Implement scoped interface extents across QX, generated bindings, SDK, Orch, Camino and browser queries. Preserve native projection, aggregation, pagination and watch invalidation without synthetic persisted objects. Validate exact query grants and scope freshness on execution and replay. Add local PostgreSQL, transport, browser and code-generation coverage. C1 implementation slice only: full-text indexes and Studio search adoption (C2/C3) remain tracked in the implementation handoff. No publication or deployment.
90 lines
3.5 KiB
TypeScript
90 lines
3.5 KiB
TypeScript
import { decodeQxValue, type QxValueType } from "./bindings.js";
|
|
import type { QueryResponse } from "./camino/api_pb.js";
|
|
import type { QxObjectRef } from "./references.js";
|
|
|
|
export type QxQueryPartial<T> = T extends QxObjectRef
|
|
? T
|
|
: T extends readonly (infer Item)[]
|
|
? QxQueryPartial<Item>[]
|
|
: T extends object
|
|
? { [Key in keyof T]?: QxQueryPartial<T[Key]> }
|
|
: T;
|
|
export type QxQuerySnapshot<T> = {
|
|
runId: string;
|
|
sequence: bigint;
|
|
dataVersion: string;
|
|
bindingDigest: string;
|
|
consistency: string;
|
|
fields: { path: readonly (string | number)[]; status: "pending" | "error"; error?: string }[];
|
|
} & ({ status: "ready"; data: T } | { status: "partial"; data: QxQueryPartial<T> });
|
|
declare const queryTypes: unique symbol;
|
|
/** Generated shape evidence, not permission to run a query. */
|
|
export interface QxQueryDescriptor<
|
|
Variables,
|
|
Result,
|
|
Root extends { kind: "object" | "workspace-interface"; interface: string } = {
|
|
kind: "object" | "workspace-interface";
|
|
interface: string;
|
|
},
|
|
> {
|
|
readonly id: string;
|
|
readonly definitionDigest: string;
|
|
readonly root: Root;
|
|
readonly variables: QxValueType;
|
|
readonly output: QxValueType;
|
|
readonly watch: boolean;
|
|
readonly [queryTypes]?: (variables: Variables, result: Result) => [Variables, Result];
|
|
}
|
|
|
|
export function decodeQuerySnapshot<T>(
|
|
response: QueryResponse,
|
|
output: QxValueType,
|
|
runId: string,
|
|
sequence: bigint,
|
|
): QxQuerySnapshot<T> {
|
|
if (response.preparationToken || response.residualWindows.length || response.relationalCaptures.length)
|
|
throw new Error("QUERY_RESULT_UNFINISHED: private preparation is not a query result");
|
|
const fields = [
|
|
...response.pending.map((field) => ({ path: field.path, status: "pending" as const })),
|
|
...response.errors.map((field) => ({ path: field.path, status: "error" as const, error: field.error })),
|
|
].map((field) => ({
|
|
...field,
|
|
path: field.path.map((part) => {
|
|
if (part.part.case !== "field" && part.part.case !== "index") throw new Error("QUERY_PATCH_INVALID");
|
|
return part.part.value;
|
|
}),
|
|
}));
|
|
// Pending/error values are absent, not successful nulls of a scalar type.
|
|
const shape = structuredClone(output);
|
|
for (const field of fields) {
|
|
let cursor = shape;
|
|
for (const [index, part] of field.path.entries()) {
|
|
while (cursor.kind === "optional") cursor = cursor.value;
|
|
const last = index === field.path.length - 1;
|
|
if (typeof part === "string" && cursor.kind === "record" && cursor.fields[part]) {
|
|
if (last) cursor.fields[part] = { kind: "optional", value: cursor.fields[part]! };
|
|
else cursor = cursor.fields[part]!;
|
|
} else if (typeof part === "number" && cursor.kind === "list") cursor = cursor.value;
|
|
else throw new Error("QUERY_PATCH_INVALID");
|
|
}
|
|
}
|
|
const data = decodeQxValue(shape, response.value, {}) as Record<string, unknown>;
|
|
for (const field of fields) {
|
|
let cursor: unknown = data;
|
|
for (const [index, part] of field.path.entries()) {
|
|
if (!cursor || typeof cursor !== "object") throw new Error("QUERY_PATCH_INVALID");
|
|
if (index === field.path.length - 1) delete (cursor as Record<string | number, unknown>)[part];
|
|
else cursor = (cursor as Record<string | number, unknown>)[part];
|
|
}
|
|
}
|
|
return {
|
|
runId,
|
|
sequence,
|
|
dataVersion: response.dataVersion,
|
|
bindingDigest: response.bindingDigest,
|
|
consistency: response.consistency,
|
|
fields,
|
|
...(fields.length ? { status: "partial", data } : { status: "ready", data }),
|
|
} as QxQuerySnapshot<T>;
|
|
}
|