Spaces:
Running
Running
Download source/teaql-ts/src/core/request-intent.ts from teaql/browser-sqlite-playground: direct link, hf CLI and curl.
- Browser
- Download file 16.6 kB
-
https://huggingface.co/spaces/teaql/browser-sqlite-playground/resolve/main/source/teaql-ts/src/core/request-intent.ts
- Command line
-
hf download hf://spaces/teaql/browser-sqlite-playground/source/teaql-ts/src/core/request-intent.ts
-
curl -L -o request-intent.ts https://huggingface.co/spaces/teaql/browser-sqlite-playground/resolve/main/source/teaql-ts/src/core/request-intent.ts
16.6 kB
| import type { MutationQuery, SelectQuery } from './ast'; | |
| import { cloneTraceNodes, queryTraceSource, TraceNode, MutationTraceScope, mutationScopeForEntity } from './trace-chain'; | |
| import type { EntityKey, EntityRoot } from './entity-root'; | |
| import { inheritSQLLogBindings, logValueStrings, privateLogValueStrings, scrubLogText } from './log-privacy'; | |
| import type { SQLLogBindingSource } from './log-privacy'; | |
| import { snapshotQuery, queryExecutionOwner, retainQueryExecutionOwner } from './query-snapshot'; | |
| import { LoadedScalarSnapshot } from './loaded-scalar-snapshot'; | |
| const mutationSnapshots = new WeakMap<object, LoadedScalarSnapshot>(); | |
| // Only runtime-created snapshots carry provenance. No property supplied by a | |
| // JSON/builder caller can forge it, and no mutable trace stack lives on Context. | |
| const querySources = new WeakMap<object, readonly TraceNode[]>(); | |
| const graphRequests = new WeakMap<object, { session: GraphMutationSession; | |
| parent?: MutationTraceScope; localComment?: string }>(); | |
| const scopeOwners = new WeakMap<MutationTraceScope, GraphMutationSession>(); | |
| export type QueryOperation = Readonly<{ | |
| kind: 'named-count' | 'facet' | 'relation-count' | 'relation-select' | 'aggregate' | 'pagination'; | |
| /** Exact generator-supplied native method; absent for a low-level runtime operation. */ | |
| method?: string; | |
| alias?: string; | |
| relation?: string; | |
| }>; | |
| function operationSnapshot(operation: QueryOperation): QueryOperation { | |
| if (!['named-count', 'facet', 'relation-count', 'relation-select', 'aggregate', 'pagination'].includes(operation.kind) | |
| || (operation.method !== undefined && !/^[A-Za-z_][A-Za-z0-9_]*$/.test(operation.method)) | |
| || (operation.alias !== undefined && typeof operation.alias !== 'string') | |
| || (operation.relation !== undefined && typeof operation.relation !== 'string')) { | |
| throw new Error('QUERY_OPERATION_INVALID: invalid generated operation descriptor'); | |
| } | |
| return Object.freeze({ kind: operation.kind, method: operation.method, | |
| alias: operation.alias, relation: operation.relation }); | |
| } | |
| /** One native literal renderer for all count/facet aliases. Never parse SQL. */ | |
| export function queryOperationComment(operation: QueryOperation): string { | |
| const captured = operationSnapshot(operation); | |
| const literal = (value: string) => "'" + value.replace(/\\/g, '\\\\') | |
| .replace(/'/g, "\\'").replace(/\n/g, '\\n').replace(/\r/g, '\\r') | |
| .replace(/\t/g, '\\t').replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029') + "'"; | |
| if (!captured.method) return 'framework: ' + captured.kind | |
| + (captured.relation ? ' ' + captured.relation : '') | |
| + (captured.alias !== undefined ? ' ' + literal(captured.alias) : ''); | |
| return captured.method + '(' + (captured.alias !== undefined ? literal(captured.alias) | |
| : captured.kind === 'relation-select' ? '...' : '') + ')'; | |
| } | |
| const requestBindingToken = Symbol('runtime request binding'); | |
| type QueryBinding = { | |
| [requestBindingToken]: true; | |
| parent?: QueryRequest<any>; | |
| comment: string; | |
| name: string; | |
| operation?: QueryOperation; | |
| source: readonly TraceNode[]; | |
| invocation?: object; | |
| }; | |
| export type RequestKind = 'query' | 'mutation'; | |
| /** Stable, value-free request-boundary diagnostics. */ | |
| export class RequestIntentError extends Error { | |
| constructor( | |
| public readonly code: 'REQUEST_COMMENT_REQUIRED' | 'QUERY_PURPOSE_REQUIRED', | |
| public readonly field: 'comment' | 'purpose', | |
| public readonly requestKind: RequestKind, | |
| ) { | |
| super(`${code}: ${requestKind} request requires a non-blank ${field}; supply it at the request entry point`); | |
| this.name = 'RequestIntentError'; | |
| } | |
| } | |
| // Match Rust str::trim (Unicode White_Space), not JavaScript trim: NEL is | |
| // whitespace, while BOM is not. Validate but never trim the supplied text. | |
| function requireText(value: unknown, field: 'comment' | 'purpose', kind: RequestKind): string { | |
| if (typeof value !== 'string' || /^\p{White_Space}*$/u.test(value)) { | |
| throw new RequestIntentError( | |
| field === 'comment' ? 'REQUEST_COMMENT_REQUIRED' : 'QUERY_PURPOSE_REQUIRED', field, kind, | |
| ); | |
| } | |
| return value; | |
| } | |
| export class QueryIntent { | |
| readonly #comment: string; | |
| readonly #purpose: string; | |
| constructor(comment: unknown, purpose: unknown) { | |
| this.#comment = requireText(comment, 'comment', 'query'); | |
| this.#purpose = requireText(purpose, 'purpose', 'query'); | |
| Object.freeze(this); | |
| } | |
| get comment(): string { return this.#comment; } | |
| get purpose(): string { return this.#purpose; } | |
| } | |
| export class MutationIntent { | |
| readonly #comment: string; | |
| constructor(comment: unknown) { | |
| this.#comment = requireText(comment, 'comment', 'mutation'); | |
| Object.freeze(this); | |
| } | |
| get comment(): string { return this.#comment; } | |
| get auditReason(): string { return this.#comment; } | |
| readbackIntent(): QueryIntent { | |
| return new QueryIntent(this.#comment, 'verify persisted mutation result'); | |
| } | |
| } | |
| /** | |
| * Request intent is owned separately from the mutable query builder. A snapshot | |
| * retains local, non-enumerable pagination capabilities without serializing them. | |
| */ | |
| export class QueryRequest<T extends object = SelectQuery> { | |
| readonly #intent: QueryIntent; | |
| readonly #query: T; | |
| readonly #parent?: QueryRequest<any>; | |
| readonly #comment: string; | |
| readonly #name: string; | |
| readonly #operation?: QueryOperation; | |
| readonly #invocation: object; | |
| constructor(query: T, intent?: QueryIntent, binding?: QueryBinding) { | |
| const source = query as any; | |
| const previous = queryExecutionOwner(query); | |
| const previousOwner = previous instanceof QueryRequest ? previous : undefined; | |
| this.#intent = intent === undefined | |
| ? previousOwner?.intent | |
| ?? new QueryIntent(source?._comment ?? source?.commentText, source?._purpose ?? source?.purposeText) | |
| : new QueryIntent(intent?.comment, intent?.purpose); | |
| if (binding && binding[requestBindingToken] !== true) | |
| throw new Error('QUERY_REQUEST_OWNERSHIP_INVALID: request binding is runtime-owned'); | |
| const owner = previousOwner | |
| && previousOwner.intent.comment === this.#intent.comment | |
| && previousOwner.purpose === this.#intent.purpose ? previousOwner : undefined; | |
| this.#invocation = binding?.invocation ?? (binding?.parent ? binding.parent.#invocation : undefined) | |
| ?? (owner ? owner.#invocation : undefined) ?? Object.freeze({}); | |
| this.#parent = binding ? binding.parent : owner?.parentRequest; | |
| this.#comment = requireText(binding?.comment ?? owner?.comment ?? this.#intent.comment, 'comment', 'query'); | |
| this.#name = binding?.name ?? owner?.requestName ?? String(source.entity ?? 'query'); | |
| this.#operation = binding?.operation ?? owner?.operation; | |
| this.#query = snapshotQuery(query); | |
| const captured = this.#query as any; | |
| // Every request owns its local comment. Purpose is resolved from the root | |
| // request through parent ownership and is copied only onto the private SQL | |
| // execution snapshot for boundary validation. | |
| for (const [field, value] of [ | |
| ['commentText', this.comment], ['purposeText', this.purpose], | |
| ['_comment', this.comment], ['_purpose', this.purpose], | |
| ] as const) { | |
| Object.defineProperty(captured, field, { value, enumerable: !field.startsWith('_'), | |
| configurable: true, writable: false }); | |
| } | |
| querySources.set(captured, cloneTraceNodes(binding?.source ?? owner?.traceSource ?? querySources.get(query) | |
| ?? queryTraceSource(String(source.entity), this.intent.comment, this.purpose))); | |
| retainQueryExecutionOwner(captured, this); | |
| Object.freeze(this); | |
| } | |
| get intent(): QueryIntent { return this.#intent; } | |
| get query(): T { return this.#query; } | |
| get comment(): string { return this.#comment; } | |
| get purpose(): string { return this.#intent.purpose; } | |
| get traceSource(): readonly TraceNode[] { return querySources.get(this.#query)!; } | |
| get parentRequest(): QueryRequest<any> | undefined { return this.#parent; } | |
| get rootRequest(): QueryRequest<any> { | |
| let root: QueryRequest<any> = this; | |
| while (root.parentRequest) root = root.parentRequest; | |
| return root; | |
| } | |
| get requestName(): string { return this.#name; } | |
| get operation(): QueryOperation | undefined { return this.#operation; } | |
| get traceChain(): readonly TraceNode[] { | |
| const comments: TraceNode[] = []; | |
| for (let node: QueryRequest<any> | undefined = this; node; node = node.parentRequest) | |
| comments.push({ kind: 'comment', name: node.requestName, detail: node.comment }); | |
| return cloneTraceNodes([{ kind: 'purpose', name: this.rootRequest.requestName, detail: this.purpose }, | |
| ...comments.reverse()]); | |
| } | |
| /** Runtime derivation preserves this invocation's source across builder clones. */ | |
| withQuery<Q extends object>(query: Q): QueryRequest<Q> { | |
| const previous = queryExecutionOwner(query); | |
| if (previous instanceof QueryRequest && previous.#invocation !== this.#invocation) { | |
| throw new Error('QUERY_SCOPE_OWNER_MISMATCH: replacement belongs to another query invocation'); | |
| } | |
| return new QueryRequest(query, this.intent, { [requestBindingToken]: true, | |
| parent: this.parentRequest, comment: this.comment, name: this.requestName, | |
| operation: this.operation, source: this.traceSource, invocation: this.#invocation }); | |
| } | |
| /** A real operation child need not execute SQL itself; descendants retain it. */ | |
| deriveOperation<Q extends object>(query: Q, operation: QueryOperation): QueryRequest<Q> { | |
| const captured = operationSnapshot(operation); | |
| return new QueryRequest(query, this.intent, { [requestBindingToken]: true, | |
| parent: this, comment: queryOperationComment(captured), name: String((query as any).entity ?? this.requestName), | |
| operation: captured, source: this.traceSource }); | |
| } | |
| /** Local description for work created by the framework, never a root authorization. */ | |
| deriveHelper<Q extends object>(query: Q, comment: string): QueryRequest<Q> { | |
| return new QueryRequest(query, this.intent, { [requestBindingToken]: true, | |
| parent: this, comment: requireText(comment, 'comment', 'query'), | |
| name: String((query as any).entity ?? this.requestName), source: this.traceSource }); | |
| } | |
| /** Preserve the Q-operation parent and the actual nested request's own comment. */ | |
| derive<Q extends object>(query: Q, relation: string, operation?: QueryOperation): QueryRequest<Q> { | |
| if (operation) return this.deriveOperation(this.query, operation).deriveChild(query, relation); | |
| const owner = `${String((this.query as any).entity)}.${relation}`; | |
| return this.deriveChild(query, relation, undefined, owner); | |
| } | |
| /** Aggregates execute their operation; an anonymous template is not a value load. */ | |
| deriveAggregate<Q extends object>(query: Q, relation: string, operation: QueryOperation): QueryRequest<Q> { | |
| const facet = this.parentRequest?.operation?.kind === 'facet' ? this.parentRequest : undefined; | |
| const branch = facet | |
| ? facet.deriveHelper(query, 'aggregate ' + facet.operation!.relation) | |
| : this.deriveOperation(query, operation); | |
| const physical = [...this.traceSource, { kind: 'relation' as const, name: relation, | |
| detail: `${String((this.query as any).entity)}.${relation}` }]; | |
| const scoped = new QueryRequest(query, this.intent, { [requestBindingToken]: true, | |
| parent: branch.parentRequest, comment: branch.comment, name: branch.requestName, | |
| operation: branch.operation, source: physical }); | |
| const comment = (query as any)._comment ?? (query as any).commentText; | |
| return comment === undefined ? scoped : scoped.deriveChild(query); | |
| } | |
| /** Bind a framework helper or value query below an existing operation parent. */ | |
| deriveChild<Q extends object>( | |
| query: Q, | |
| relation?: string, | |
| defaultComment?: string, | |
| requestName?: string, | |
| ): QueryRequest<Q> { | |
| const source = query as any; | |
| const comment = source._comment ?? source.commentText; | |
| return new QueryRequest(query, this.intent, { [requestBindingToken]: true, | |
| parent: this, comment: comment === undefined | |
| ? (defaultComment ?? `load ${String(source.entity)}`) | |
| : requireText(comment, 'comment', 'query'), | |
| name: requestName ?? String(source.entity), source: relation === undefined ? this.traceSource : [...this.traceSource, { | |
| kind: 'relation', name: relation, detail: `${String((this.query as any).entity)}.${relation}`, | |
| }] }); | |
| } | |
| } | |
| /** A batch also needs this root envelope; child comments are not a fallback. */ | |
| export class MutationRequest<T extends object = MutationQuery> { | |
| readonly #intent: MutationIntent; | |
| readonly #mutation: T; | |
| constructor(mutation: T, intent?: MutationIntent) { | |
| this.#intent = new MutationIntent(intent === undefined ? (mutation as any)?.comment : intent?.comment); | |
| this.#mutation = { ...mutation }; | |
| Object.defineProperty(this.#mutation, 'comment', { | |
| value: this.#intent.comment, enumerable: true, writable: false, configurable: false, | |
| }); | |
| Object.freeze(this); | |
| } | |
| get intent(): MutationIntent { return this.#intent; } | |
| get mutation(): T { return this.#mutation; } | |
| get comment(): string { return this.#intent.comment; } | |
| /** @internal Generated hydration/commit provenance, never a wire field. */ | |
| withLoadedSnapshot(snapshot: LoadedScalarSnapshot): this { | |
| mutationSnapshots.set(this, new LoadedScalarSnapshot(snapshot.values())); | |
| return this; | |
| } | |
| /** @internal Does not become part of the write payload or policy input. */ | |
| loadedValues(): Readonly<Record<string, unknown>> { return mutationSnapshots.get(this)?.values() ?? {}; } | |
| /** Runtime-owned execution capability; raw mutation fields cannot forge it. */ | |
| get graphSession(): GraphMutationSession | undefined { return graphRequests.get(this)?.session; } | |
| scopeFor(key: EntityKey): MutationTraceScope { | |
| const graph = graphRequests.get(this); | |
| const scope = mutationScopeForEntity(graph?.parent, key.entity, key.id, this.comment, graph?.localComment); | |
| if (graph) scopeOwners.set(scope, graph.session); | |
| return scope; | |
| } | |
| traceFor(key: EntityKey): readonly TraceNode[] { | |
| const mutation = this.#mutation as T & { ledgerRoot?: EntityRoot; ledgerKey?: EntityKey }; | |
| const specific = mutation.ledgerRoot?.traceChain(mutation.ledgerKey ?? key); | |
| return specific?.length ? cloneTraceNodes(specific) : this.scopeFor(key).recover(); | |
| } | |
| /** Safe event projection; internal policy intent is never mutated. */ | |
| auditProjection(key: EntityKey, payload: unknown, bindings?: SQLLogBindingSource): Readonly<{ | |
| reason: string; mutationLineage: readonly TraceNode[]; | |
| }> { | |
| const secrets = [...logValueStrings(payload), ...logValueStrings((this.#mutation as any).id), | |
| ...(bindings ? privateLogValueStrings(bindings) : logValueStrings(this.loadedValues())), | |
| ...privateLogValueStrings(this.graphSession?.logBindings)]; | |
| return Object.freeze({ reason: scrubLogText(this.comment, secrets)!, | |
| mutationLineage: cloneTraceNodes(this.traceFor(key).map(node => ({ ...node, | |
| detail: scrubLogText(node.detail, secrets) }))) }); | |
| } | |
| } | |
| /** One explicit graph invocation, never a Context-owned trace stack. */ | |
| export class GraphMutationSession { | |
| readonly #intent: MutationIntent; | |
| #bindings?: SQLLogBindingSource; | |
| constructor(intent: MutationIntent) { | |
| this.#intent = new MutationIntent(intent?.comment); | |
| Object.freeze(this); | |
| } | |
| get intent(): MutationIntent { return this.#intent; } | |
| request<T extends object>(mutation: T, parent?: MutationTraceScope, | |
| localComment?: string): MutationRequest<T> { | |
| if (parent && scopeOwners.get(parent) !== this) | |
| throw new Error('GRAPH_TRACE_SCOPE_MISMATCH: parent scope belongs to another graph invocation'); | |
| const request = new MutationRequest(mutation, this.#intent); | |
| graphRequests.set(request, { session: this, parent, localComment }); | |
| return request; | |
| } | |
| /** @internal Preflight snapshots bind provenance for all siblings before SQL. */ | |
| captureLogBindings(source: SQLLogBindingSource): void { | |
| this.#bindings = inheritSQLLogBindings(source, this.#bindings); | |
| } | |
| /** @internal Never put this raw provenance on a wire or log record. */ | |
| get logBindings(): SQLLogBindingSource | undefined { return this.#bindings; } | |
| } | |
| /** Database committed; retrying this operation as a rolled-back write is unsafe. */ | |
| export class GraphCommittedError extends Error { | |
| readonly committed = true; | |
| constructor(public readonly cause: unknown) { | |
| super('GRAPH_ALREADY_COMMITTED: post-commit processing failed; do not retry as an uncommitted mutation'); | |
| this.name = 'GraphCommittedError'; | |
| } | |
| } | |