@prisma-next/sql-runtime 0.12.0-dev.5 → 0.12.0-dev.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -4
- package/dist/{exports-CXYd2w6k.mjs → exports-tEA99Uhc.mjs} +132 -276
- package/dist/exports-tEA99Uhc.mjs.map +1 -0
- package/dist/{index-DTr7KoUs.d.mts → index-Cbsu_4Gu.d.mts} +24 -59
- package/dist/index-Cbsu_4Gu.d.mts.map +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/test/utils.d.mts +26 -14
- package/dist/test/utils.d.mts.map +1 -1
- package/dist/test/utils.mjs +34 -28
- package/dist/test/utils.mjs.map +1 -1
- package/package.json +12 -12
- package/src/codecs/ast-codec-registry.ts +25 -0
- package/src/codecs/validation.ts +2 -2
- package/src/exports/index.ts +3 -10
- package/src/sql-context.ts +5 -5
- package/dist/exports-CXYd2w6k.mjs.map +0 -1
- package/dist/index-DTr7KoUs.d.mts.map +0 -1
- package/src/marker.ts +0 -75
- package/src/sql-marker.ts +0 -143
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Execute SQL query Plans with deterministic verification, guardrails, and feedbac
|
|
|
23
23
|
- **Mutation Default Generator Composition**: Assemble execution default generators from composed target/adapter/extension contributors
|
|
24
24
|
- **No Implicit Generator Baseline**: Runtime resolves generator ids only from composed contributors (no built-in runtime fallback table)
|
|
25
25
|
- **SQL Context Creation**: Create runtime contexts with SQL contracts, adapters, and codecs
|
|
26
|
-
- **SQL Marker
|
|
26
|
+
- **SQL Marker Bootstrap**: Provide test-utility helpers for bootstrapping the marker table; marker reads and writes both go through the control adapter SPI
|
|
27
27
|
- **Codec Encoding/Decoding**: Encode parameters and decode rows using SQL codec registries
|
|
28
28
|
- **Codec Validation**: Validate that codec registries contain all required codecs
|
|
29
29
|
- **SQL Family Adapter**: Implement `RuntimeFamilyAdapter` for SQL contracts (defined in `runtime-spi.ts`)
|
|
@@ -124,12 +124,15 @@ const runtime = createRuntime({
|
|
|
124
124
|
- `validateCodecRegistryCompleteness` - Codec validation
|
|
125
125
|
- `extractCodecIds` - Extract codec IDs from a contract
|
|
126
126
|
- `validateContractCodecMappings` - Validate contract codec mappings against registry
|
|
127
|
+
- `createAstCodecRegistry` - Contract-free codec registry that resolves codecs from AST-supplied `CodecRef`s
|
|
128
|
+
- `deriveParamMetadata` - Walk a control DML AST and collect per-value codec metadata for encoding
|
|
129
|
+
- `encodeParamsWithMetadata` - Encode lowered control DML parameters through their codecs
|
|
127
130
|
|
|
128
131
|
### SQL Marker
|
|
129
132
|
|
|
130
|
-
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
+
- Test helpers in `./test/utils` bootstrap marker tables via contract-free DDL lowering
|
|
134
|
+
|
|
135
|
+
Marker reads and writes go through the control adapter SPI (`adapter.readMarker`, `adapter.initMarker`, `adapter.updateMarker`, `adapter.writeLedgerEntry`).
|
|
133
136
|
|
|
134
137
|
### Plan Lowering
|
|
135
138
|
|
|
@@ -1,23 +1,143 @@
|
|
|
1
1
|
import { AsyncIterableResult, RuntimeCore, checkAborted, checkMiddlewareCompatibility, isRuntimeError, raceAgainstAbort, runBeforeExecuteChain, runWithMiddleware, runtimeError } from "@prisma-next/framework-components/runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { canonicalizeJson } from "@prisma-next/framework-components/utils";
|
|
3
3
|
import { PreparedParamRef, collectOrderedParamRefs, isQueryAst } from "@prisma-next/sql-relational-core/ast";
|
|
4
4
|
import { ifDefined } from "@prisma-next/utils/defined";
|
|
5
5
|
import { checkContractComponentRequirements, mergeCapabilityMatrices } from "@prisma-next/framework-components/components";
|
|
6
6
|
import { createExecutionStack } from "@prisma-next/framework-components/execution";
|
|
7
|
-
import { canonicalizeJson } from "@prisma-next/framework-components/utils";
|
|
8
7
|
import { isPostgresEnumStorageEntry } from "@prisma-next/sql-contract/types";
|
|
9
8
|
import { blindCast } from "@prisma-next/utils/casts";
|
|
10
9
|
import { createSqlOperationRegistry } from "@prisma-next/sql-operations";
|
|
11
10
|
import { buildCodecDescriptorRegistry } from "@prisma-next/sql-relational-core/codec-descriptor-registry";
|
|
12
|
-
import { APP_SPACE_ID } from "@prisma-next/framework-components/control";
|
|
13
11
|
import { createSqlParamRefMutator } from "@prisma-next/sql-relational-core/middleware";
|
|
14
12
|
import { canonicalStringify } from "@prisma-next/utils/canonical-stringify";
|
|
15
13
|
import { hashContent } from "@prisma-next/utils/hash-content";
|
|
16
14
|
import { createHash } from "node:crypto";
|
|
15
|
+
//#region src/codecs/ast-codec-resolver.ts
|
|
16
|
+
/**
|
|
17
|
+
* Build an {@link AstCodecResolver} bound to a descriptor registry and a per-call instance-context factory.
|
|
18
|
+
*
|
|
19
|
+
* The instance-context factory lets callers control `name` / `usedAt` for refs the AST supplies (e.g. AST-embedded migration ops where the materialisation site is the AST node, not a contract column). The contract-walk pre-population path constructs its own contexts and invokes the resolver with those refs to seed the cache.
|
|
20
|
+
*/
|
|
21
|
+
function createAstCodecResolver(descriptors, instanceContextFor) {
|
|
22
|
+
const cache = /* @__PURE__ */ new Map();
|
|
23
|
+
return { forCodecRef(ref) {
|
|
24
|
+
const key = `${ref.codecId}:${canonicalizeJson(ref.typeParams)}`;
|
|
25
|
+
const cached = cache.get(key);
|
|
26
|
+
if (cached) return cached;
|
|
27
|
+
const descriptor = descriptors.descriptorFor(ref.codecId);
|
|
28
|
+
if (!descriptor) throw runtimeError("RUNTIME.CODEC_DESCRIPTOR_MISSING", `No codec descriptor registered for codecId '${ref.codecId}'.`, { codecId: ref.codecId });
|
|
29
|
+
const validated = validateTypeParams$1(descriptor.paramsSchema, descriptor.isParameterized && ref.typeParams === void 0 ? {
|
|
30
|
+
...ref,
|
|
31
|
+
typeParams: {}
|
|
32
|
+
} : ref);
|
|
33
|
+
const ctx = instanceContextFor(ref);
|
|
34
|
+
const codec = descriptor.factory(validated)(ctx);
|
|
35
|
+
cache.set(key, codec);
|
|
36
|
+
return codec;
|
|
37
|
+
} };
|
|
38
|
+
}
|
|
39
|
+
function validateTypeParams$1(paramsSchema, ref) {
|
|
40
|
+
const result = paramsSchema["~standard"].validate(ref.typeParams);
|
|
41
|
+
if (result instanceof Promise) throw runtimeError("RUNTIME.TYPE_PARAMS_INVALID", `paramsSchema for codec '${ref.codecId}' returned a Promise; runtime validation requires a synchronous Standard Schema validator.`, {
|
|
42
|
+
codecId: ref.codecId,
|
|
43
|
+
typeParams: ref.typeParams
|
|
44
|
+
});
|
|
45
|
+
if ("issues" in result && result.issues) {
|
|
46
|
+
const messages = result.issues.map((issue) => issue.message).join("; ");
|
|
47
|
+
throw runtimeError("RUNTIME.TYPE_PARAMS_INVALID", `Invalid typeParams for codec '${ref.codecId}': ${messages}`, {
|
|
48
|
+
codecId: ref.codecId,
|
|
49
|
+
typeParams: ref.typeParams
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return result.value;
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region src/codecs/ast-codec-registry.ts
|
|
56
|
+
/**
|
|
57
|
+
* Build a contract-free {@link ContractCodecRegistry} that resolves codecs
|
|
58
|
+
* purely from AST-supplied {@link import('@prisma-next/framework-components/codec').CodecRef}s
|
|
59
|
+
* against a target's descriptor registry.
|
|
60
|
+
*
|
|
61
|
+
* Dispatch is driven entirely by `CodecRef`s embedded in AST nodes; no
|
|
62
|
+
* contract walk is needed. `forColumn` always returns `undefined` — this
|
|
63
|
+
* registry carries no column-to-codec mappings.
|
|
64
|
+
*/
|
|
65
|
+
function createAstCodecRegistry(descriptors) {
|
|
66
|
+
const resolver = createAstCodecResolver(descriptors, (ref) => ({
|
|
67
|
+
name: ref.codecId,
|
|
68
|
+
usedAt: []
|
|
69
|
+
}));
|
|
70
|
+
return {
|
|
71
|
+
forColumn: () => void 0,
|
|
72
|
+
forCodecRef: (ref) => resolver.forCodecRef(ref)
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
//#region src/codecs/encoding.ts
|
|
77
|
+
const NO_METADATA = Object.freeze({
|
|
78
|
+
codec: void 0,
|
|
79
|
+
name: void 0
|
|
80
|
+
});
|
|
81
|
+
function deriveParamMetadata(ast) {
|
|
82
|
+
return collectOrderedParamRefs(ast).map((ref) => {
|
|
83
|
+
return {
|
|
84
|
+
codec: ref.codec,
|
|
85
|
+
name: ref.name
|
|
86
|
+
};
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
function resolveParamCodec(metadata, contractCodecs) {
|
|
90
|
+
if (metadata.codec && contractCodecs) return contractCodecs.forCodecRef(metadata.codec);
|
|
91
|
+
}
|
|
92
|
+
function paramLabel(metadata, paramIndex) {
|
|
93
|
+
return metadata.name ?? `param[${paramIndex}]`;
|
|
94
|
+
}
|
|
95
|
+
function wrapEncodeFailure(error, metadata, paramIndex, codecId) {
|
|
96
|
+
const label = paramLabel(metadata, paramIndex);
|
|
97
|
+
const wrapped = runtimeError("RUNTIME.ENCODE_FAILED", `Failed to encode parameter ${label} with codec '${codecId}': ${error instanceof Error ? error.message : String(error)}`, {
|
|
98
|
+
label,
|
|
99
|
+
codec: codecId,
|
|
100
|
+
paramIndex
|
|
101
|
+
});
|
|
102
|
+
wrapped.cause = error;
|
|
103
|
+
throw wrapped;
|
|
104
|
+
}
|
|
105
|
+
async function encodeParamValue(value, metadata, paramIndex, ctx, contractCodecs) {
|
|
106
|
+
if (value === null || value === void 0) return null;
|
|
107
|
+
const codec = resolveParamCodec(metadata, contractCodecs);
|
|
108
|
+
if (!codec) return value;
|
|
109
|
+
try {
|
|
110
|
+
return await codec.encode(value, ctx);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
if (isRuntimeError(error)) throw error;
|
|
113
|
+
wrapEncodeFailure(error, metadata, paramIndex, codec.id);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Encodes all parameters concurrently via `Promise.all`. Per parameter, sync-and async-authored codecs share the same path: `codec.encode → await → return`. Param-level failures are wrapped in `RUNTIME.ENCODE_FAILED`.
|
|
118
|
+
*
|
|
119
|
+
* When `ctx.signal` is provided:
|
|
120
|
+
*
|
|
121
|
+
* - **Already-aborted at entry** short-circuits with `RUNTIME.ABORTED` (`{ phase: 'encode' }`) before any `codec.encode` call is made — codecs can pin this with a per-call counter that stays at zero.
|
|
122
|
+
* - **Mid-flight abort** races the per-param `Promise.all` against `abortable(ctx.signal)`. The runtime returns `RUNTIME.ABORTED` promptly even if codec bodies ignore the signal; the in-flight bodies are abandoned and run to completion in the background (cooperative cancellation, see ADR 204).
|
|
123
|
+
* - Existing `RUNTIME.ENCODE_FAILED` envelopes that surface from a codec body before the runtime observes the abort pass through unchanged (no double wrap).
|
|
124
|
+
*/
|
|
125
|
+
async function encodeParams(plan, ctx, contractCodecs) {
|
|
126
|
+
return encodeParamsWithMetadata(plan.params, deriveParamMetadata(plan.ast), ctx, contractCodecs);
|
|
127
|
+
}
|
|
128
|
+
async function encodeParamsWithMetadata(values, metadata, ctx, contractCodecs) {
|
|
129
|
+
checkAborted(ctx, "encode");
|
|
130
|
+
const signal = ctx.signal;
|
|
131
|
+
if (values.length === 0) return values;
|
|
132
|
+
const tasks = values.map((value, i) => encodeParamValue(value, metadata[i] ?? NO_METADATA, i, ctx, contractCodecs));
|
|
133
|
+
const settled = await raceAgainstAbort(Promise.all(tasks), signal, "encode");
|
|
134
|
+
return Object.freeze(settled);
|
|
135
|
+
}
|
|
136
|
+
//#endregion
|
|
17
137
|
//#region src/codecs/validation.ts
|
|
18
138
|
function extractCodecIds(contract) {
|
|
19
139
|
const codecIds = /* @__PURE__ */ new Set();
|
|
20
|
-
for (const ns of Object.values(contract.storage.namespaces)) for (const table of Object.values(ns.
|
|
140
|
+
for (const ns of Object.values(contract.storage.namespaces)) for (const table of Object.values(ns.entries.table)) for (const column of Object.values(table.columns)) {
|
|
21
141
|
const codecId = column.codecId;
|
|
22
142
|
codecIds.add(codecId);
|
|
23
143
|
}
|
|
@@ -25,7 +145,7 @@ function extractCodecIds(contract) {
|
|
|
25
145
|
}
|
|
26
146
|
function extractCodecIdsFromColumns(contract) {
|
|
27
147
|
const codecIds = /* @__PURE__ */ new Map();
|
|
28
|
-
for (const ns of Object.values(contract.storage.namespaces)) for (const [tableName, table] of Object.entries(ns.
|
|
148
|
+
for (const ns of Object.values(contract.storage.namespaces)) for (const [tableName, table] of Object.entries(ns.entries.table)) for (const [columnName, column] of Object.entries(table.columns)) {
|
|
29
149
|
const codecId = column.codecId;
|
|
30
150
|
const key = `${tableName}.${columnName}`;
|
|
31
151
|
codecIds.set(key, codecId);
|
|
@@ -84,50 +204,6 @@ function lowerSqlPlan(adapter, contract, queryPlan) {
|
|
|
84
204
|
});
|
|
85
205
|
}
|
|
86
206
|
//#endregion
|
|
87
|
-
//#region src/marker.ts
|
|
88
|
-
const MetaSchema = type({ "[string]": "unknown" });
|
|
89
|
-
function parseMeta(meta) {
|
|
90
|
-
if (meta === null || meta === void 0) return {};
|
|
91
|
-
let parsed;
|
|
92
|
-
if (typeof meta === "string") try {
|
|
93
|
-
parsed = JSON.parse(meta);
|
|
94
|
-
} catch {
|
|
95
|
-
return {};
|
|
96
|
-
}
|
|
97
|
-
else parsed = meta;
|
|
98
|
-
const result = MetaSchema(parsed);
|
|
99
|
-
if (result instanceof type.errors) return {};
|
|
100
|
-
return result;
|
|
101
|
-
}
|
|
102
|
-
const ContractMarkerRowSchema = type({
|
|
103
|
-
core_hash: "string",
|
|
104
|
-
profile_hash: "string",
|
|
105
|
-
"contract_json?": "unknown | null",
|
|
106
|
-
"canonical_version?": "number | null",
|
|
107
|
-
"updated_at?": "Date | string",
|
|
108
|
-
"app_tag?": "string | null",
|
|
109
|
-
"meta?": "unknown | null",
|
|
110
|
-
invariants: type("string").array()
|
|
111
|
-
});
|
|
112
|
-
function parseContractMarkerRow(row) {
|
|
113
|
-
const result = ContractMarkerRowSchema(row);
|
|
114
|
-
if (result instanceof type.errors) {
|
|
115
|
-
const messages = result.map((p) => p.message).join("; ");
|
|
116
|
-
throw new Error(`Invalid contract marker row: ${messages}`);
|
|
117
|
-
}
|
|
118
|
-
const updatedAt = result.updated_at ? result.updated_at instanceof Date ? result.updated_at : new Date(result.updated_at) : /* @__PURE__ */ new Date();
|
|
119
|
-
return {
|
|
120
|
-
storageHash: result.core_hash,
|
|
121
|
-
profileHash: result.profile_hash,
|
|
122
|
-
contractJson: result.contract_json ?? null,
|
|
123
|
-
canonicalVersion: result.canonical_version ?? null,
|
|
124
|
-
updatedAt,
|
|
125
|
-
appTag: result.app_tag ?? null,
|
|
126
|
-
meta: parseMeta(result.meta),
|
|
127
|
-
invariants: result.invariants
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
//#endregion
|
|
131
207
|
//#region src/middleware/budgets.ts
|
|
132
208
|
function hasAggregateWithoutGroupBy(ast) {
|
|
133
209
|
if (ast.groupBy !== void 0) return false;
|
|
@@ -396,46 +472,6 @@ function lints(options) {
|
|
|
396
472
|
});
|
|
397
473
|
}
|
|
398
474
|
//#endregion
|
|
399
|
-
//#region src/codecs/ast-codec-resolver.ts
|
|
400
|
-
/**
|
|
401
|
-
* Build an {@link AstCodecResolver} bound to a descriptor registry and a per-call instance-context factory.
|
|
402
|
-
*
|
|
403
|
-
* The instance-context factory lets callers control `name` / `usedAt` for refs the AST supplies (e.g. AST-embedded migration ops where the materialisation site is the AST node, not a contract column). The contract-walk pre-population path constructs its own contexts and invokes the resolver with those refs to seed the cache.
|
|
404
|
-
*/
|
|
405
|
-
function createAstCodecResolver(descriptors, instanceContextFor) {
|
|
406
|
-
const cache = /* @__PURE__ */ new Map();
|
|
407
|
-
return { forCodecRef(ref) {
|
|
408
|
-
const key = `${ref.codecId}:${canonicalizeJson(ref.typeParams)}`;
|
|
409
|
-
const cached = cache.get(key);
|
|
410
|
-
if (cached) return cached;
|
|
411
|
-
const descriptor = descriptors.descriptorFor(ref.codecId);
|
|
412
|
-
if (!descriptor) throw runtimeError("RUNTIME.CODEC_DESCRIPTOR_MISSING", `No codec descriptor registered for codecId '${ref.codecId}'.`, { codecId: ref.codecId });
|
|
413
|
-
const validated = validateTypeParams$1(descriptor.paramsSchema, descriptor.isParameterized && ref.typeParams === void 0 ? {
|
|
414
|
-
...ref,
|
|
415
|
-
typeParams: {}
|
|
416
|
-
} : ref);
|
|
417
|
-
const ctx = instanceContextFor(ref);
|
|
418
|
-
const codec = descriptor.factory(validated)(ctx);
|
|
419
|
-
cache.set(key, codec);
|
|
420
|
-
return codec;
|
|
421
|
-
} };
|
|
422
|
-
}
|
|
423
|
-
function validateTypeParams$1(paramsSchema, ref) {
|
|
424
|
-
const result = paramsSchema["~standard"].validate(ref.typeParams);
|
|
425
|
-
if (result instanceof Promise) throw runtimeError("RUNTIME.TYPE_PARAMS_INVALID", `paramsSchema for codec '${ref.codecId}' returned a Promise; runtime validation requires a synchronous Standard Schema validator.`, {
|
|
426
|
-
codecId: ref.codecId,
|
|
427
|
-
typeParams: ref.typeParams
|
|
428
|
-
});
|
|
429
|
-
if ("issues" in result && result.issues) {
|
|
430
|
-
const messages = result.issues.map((issue) => issue.message).join("; ");
|
|
431
|
-
throw runtimeError("RUNTIME.TYPE_PARAMS_INVALID", `Invalid typeParams for codec '${ref.codecId}': ${messages}`, {
|
|
432
|
-
codecId: ref.codecId,
|
|
433
|
-
typeParams: ref.typeParams
|
|
434
|
-
});
|
|
435
|
-
}
|
|
436
|
-
return result.value;
|
|
437
|
-
}
|
|
438
|
-
//#endregion
|
|
439
475
|
//#region src/sql-context.ts
|
|
440
476
|
function documentScopedCodecTypes(contract) {
|
|
441
477
|
return blindCast(contract.storage.types);
|
|
@@ -531,7 +567,7 @@ function collectCodecDescriptors(contributors) {
|
|
|
531
567
|
}
|
|
532
568
|
function collectTypeRefSites(storage) {
|
|
533
569
|
const sites = /* @__PURE__ */ new Map();
|
|
534
|
-
for (const ns of Object.values(storage.namespaces)) for (const [tableName, table] of Object.entries(ns.
|
|
570
|
+
for (const ns of Object.values(storage.namespaces)) for (const [tableName, table] of Object.entries(ns.entries.table)) for (const [columnName, column] of Object.entries(table.columns)) {
|
|
535
571
|
if (typeof column.typeRef !== "string") continue;
|
|
536
572
|
const list = sites.get(column.typeRef);
|
|
537
573
|
const entry = {
|
|
@@ -566,7 +602,7 @@ function initializeTypeHelpers(storage, documentTypes, codecDescriptors) {
|
|
|
566
602
|
return helpers;
|
|
567
603
|
}
|
|
568
604
|
function validateColumnTypeParams(storage, codecDescriptors) {
|
|
569
|
-
for (const ns of Object.values(storage.namespaces)) for (const [tableName, table] of Object.entries(ns.
|
|
605
|
+
for (const ns of Object.values(storage.namespaces)) for (const [tableName, table] of Object.entries(ns.entries.table)) for (const [columnName, column] of Object.entries(table.columns)) if (column.typeParams) {
|
|
570
606
|
const descriptor = codecDescriptors.get(column.codecId);
|
|
571
607
|
if (descriptor) validateTypeParams(column.typeParams, descriptor, {
|
|
572
608
|
tableName,
|
|
@@ -586,7 +622,7 @@ function validateColumnTypeParams(storage, codecDescriptors) {
|
|
|
586
622
|
* Runs unconditionally from `createExecutionContext` so contract bugs fail fast at construction time instead of silently skipping affected columns in the codec registry's pre-population walk.
|
|
587
623
|
*/
|
|
588
624
|
function assertColumnCodecIntegrity(storage, codecDescriptors) {
|
|
589
|
-
for (const ns of Object.values(storage.namespaces)) for (const [tableName, table] of Object.entries(ns.
|
|
625
|
+
for (const ns of Object.values(storage.namespaces)) for (const [tableName, table] of Object.entries(ns.entries.table)) for (const columnName of Object.keys(table.columns)) {
|
|
590
626
|
const ref = codecDescriptors.codecRefForColumn(tableName, columnName);
|
|
591
627
|
if (!ref) continue;
|
|
592
628
|
const descriptor = codecDescriptors.descriptorFor(ref.codecId);
|
|
@@ -660,7 +696,7 @@ function buildContractCodecRegistry(contract, codecDescriptors) {
|
|
|
660
696
|
nameByKey.set(key, typeName);
|
|
661
697
|
}
|
|
662
698
|
}
|
|
663
|
-
for (const ns of Object.values(contract.storage.namespaces)) for (const [tableName, table] of Object.entries(ns.
|
|
699
|
+
for (const ns of Object.values(contract.storage.namespaces)) for (const [tableName, table] of Object.entries(ns.entries.table)) for (const [columnName, column] of Object.entries(table.columns)) {
|
|
664
700
|
if (column.typeRef !== void 0) continue;
|
|
665
701
|
const ref = codecDescriptors.codecRefForColumn(tableName, columnName);
|
|
666
702
|
if (!ref) continue;
|
|
@@ -684,7 +720,7 @@ function buildContractCodecRegistry(contract, codecDescriptors) {
|
|
|
684
720
|
usedAt: usedAtByKey.get(key) ?? []
|
|
685
721
|
};
|
|
686
722
|
});
|
|
687
|
-
for (const ns of Object.values(contract.storage.namespaces)) for (const [tableName, table] of Object.entries(ns.
|
|
723
|
+
for (const ns of Object.values(contract.storage.namespaces)) for (const [tableName, table] of Object.entries(ns.entries.table)) for (const columnName of Object.keys(table.columns)) {
|
|
688
724
|
const ref = codecDescriptors.codecRefForColumn(tableName, columnName);
|
|
689
725
|
if (!ref) continue;
|
|
690
726
|
resolver.forCodecRef(ref);
|
|
@@ -817,125 +853,6 @@ function createExecutionContext(options) {
|
|
|
817
853
|
};
|
|
818
854
|
}
|
|
819
855
|
//#endregion
|
|
820
|
-
//#region src/sql-marker.ts
|
|
821
|
-
const ensureSchemaStatement = {
|
|
822
|
-
sql: "create schema if not exists prisma_contract",
|
|
823
|
-
params: []
|
|
824
|
-
};
|
|
825
|
-
/**
|
|
826
|
-
* Schema for `prisma_contract.marker`. The `space text` primary key
|
|
827
|
-
* supports one row per loaded contract space (`'app'`,
|
|
828
|
-
* `'<extension-id>'`, …); brand-new databases create this shape
|
|
829
|
-
* directly. Pre-1.0 single-row markers (no `space` column) are not
|
|
830
|
-
* auto-migrated — the target-specific migration runner detects the
|
|
831
|
-
* legacy shape at boot and surfaces a structured `LEGACY_MARKER_SHAPE`
|
|
832
|
-
* failure pointing the operator at re-running `dbInit`.
|
|
833
|
-
*
|
|
834
|
-
* @see specs/framework-mechanism.spec.md § 2.
|
|
835
|
-
*/
|
|
836
|
-
const ensureTableStatement = {
|
|
837
|
-
sql: `create table if not exists prisma_contract.marker (
|
|
838
|
-
space text not null primary key default '${APP_SPACE_ID}',
|
|
839
|
-
core_hash text not null,
|
|
840
|
-
profile_hash text not null,
|
|
841
|
-
contract_json jsonb,
|
|
842
|
-
canonical_version int,
|
|
843
|
-
updated_at timestamptz not null default now(),
|
|
844
|
-
app_tag text,
|
|
845
|
-
meta jsonb not null default '{}',
|
|
846
|
-
invariants text[] not null default '{}'
|
|
847
|
-
)`,
|
|
848
|
-
params: []
|
|
849
|
-
};
|
|
850
|
-
function readContractMarker(space) {
|
|
851
|
-
return {
|
|
852
|
-
sql: `select
|
|
853
|
-
core_hash,
|
|
854
|
-
profile_hash,
|
|
855
|
-
contract_json,
|
|
856
|
-
canonical_version,
|
|
857
|
-
updated_at,
|
|
858
|
-
app_tag,
|
|
859
|
-
meta,
|
|
860
|
-
invariants
|
|
861
|
-
from prisma_contract.marker
|
|
862
|
-
where space = $1`,
|
|
863
|
-
params: [space]
|
|
864
|
-
};
|
|
865
|
-
}
|
|
866
|
-
/**
|
|
867
|
-
* Variable columns that participate in INSERT/UPDATE alongside the
|
|
868
|
-
* always-on `space = $1` and `updated_at = now()`. Each column declares
|
|
869
|
-
* its name, optional cast type, and parameter value; the placeholder
|
|
870
|
-
* (`$N`) is computed positionally below — adding or reordering a
|
|
871
|
-
* column doesn't desync indices. `invariants` only appears when the
|
|
872
|
-
* caller supplies it — see `WriteMarkerInput.invariants`.
|
|
873
|
-
*/
|
|
874
|
-
function markerColumns(input) {
|
|
875
|
-
return [
|
|
876
|
-
{
|
|
877
|
-
name: "core_hash",
|
|
878
|
-
param: input.storageHash
|
|
879
|
-
},
|
|
880
|
-
{
|
|
881
|
-
name: "profile_hash",
|
|
882
|
-
param: input.profileHash
|
|
883
|
-
},
|
|
884
|
-
{
|
|
885
|
-
name: "contract_json",
|
|
886
|
-
type: "jsonb",
|
|
887
|
-
param: input.contractJson ?? null
|
|
888
|
-
},
|
|
889
|
-
{
|
|
890
|
-
name: "canonical_version",
|
|
891
|
-
param: input.canonicalVersion ?? null
|
|
892
|
-
},
|
|
893
|
-
{
|
|
894
|
-
name: "app_tag",
|
|
895
|
-
param: input.appTag ?? null
|
|
896
|
-
},
|
|
897
|
-
{
|
|
898
|
-
name: "meta",
|
|
899
|
-
type: "jsonb",
|
|
900
|
-
param: JSON.stringify(input.meta ?? {})
|
|
901
|
-
},
|
|
902
|
-
...input.invariants !== void 0 ? [{
|
|
903
|
-
name: "invariants",
|
|
904
|
-
type: "text[]",
|
|
905
|
-
param: input.invariants
|
|
906
|
-
}] : []
|
|
907
|
-
];
|
|
908
|
-
}
|
|
909
|
-
function writeContractMarker(input) {
|
|
910
|
-
const placed = markerColumns(input).map((c, i) => ({
|
|
911
|
-
name: c.name,
|
|
912
|
-
expr: c.type ? `$${i + 2}::${c.type}` : `$${i + 2}`,
|
|
913
|
-
param: c.param
|
|
914
|
-
}));
|
|
915
|
-
const params = [input.space, ...placed.map((c) => c.param)];
|
|
916
|
-
const insertColumns = [
|
|
917
|
-
"space",
|
|
918
|
-
...placed.map((c) => c.name),
|
|
919
|
-
"updated_at"
|
|
920
|
-
].join(", ");
|
|
921
|
-
const insertValues = [
|
|
922
|
-
"$1",
|
|
923
|
-
...placed.map((c) => c.expr),
|
|
924
|
-
"now()"
|
|
925
|
-
].join(", ");
|
|
926
|
-
const setClauses = [...placed.map((c) => `${c.name} = ${c.expr}`), "updated_at = now()"].join(", ");
|
|
927
|
-
return {
|
|
928
|
-
insert: {
|
|
929
|
-
sql: `insert into prisma_contract.marker (${insertColumns}) values (${insertValues})`,
|
|
930
|
-
params
|
|
931
|
-
},
|
|
932
|
-
update: {
|
|
933
|
-
sql: `update prisma_contract.marker set ${setClauses} where space = $1`,
|
|
934
|
-
params
|
|
935
|
-
}
|
|
936
|
-
};
|
|
937
|
-
}
|
|
938
|
-
//#endregion
|
|
939
856
|
//#region src/codecs/decoding.ts
|
|
940
857
|
const WIRE_PREVIEW_LIMIT = 100;
|
|
941
858
|
const EMPTY_INCLUDE_ALIASES = /* @__PURE__ */ new Set();
|
|
@@ -1085,67 +1002,6 @@ async function decodeRow(row, decodeCtx, rowCtx) {
|
|
|
1085
1002
|
return decoded;
|
|
1086
1003
|
}
|
|
1087
1004
|
//#endregion
|
|
1088
|
-
//#region src/codecs/encoding.ts
|
|
1089
|
-
const NO_METADATA = Object.freeze({
|
|
1090
|
-
codec: void 0,
|
|
1091
|
-
name: void 0
|
|
1092
|
-
});
|
|
1093
|
-
function deriveParamMetadata(ast) {
|
|
1094
|
-
return collectOrderedParamRefs(ast).map((ref) => {
|
|
1095
|
-
return {
|
|
1096
|
-
codec: ref.codec,
|
|
1097
|
-
name: ref.name
|
|
1098
|
-
};
|
|
1099
|
-
});
|
|
1100
|
-
}
|
|
1101
|
-
function resolveParamCodec(metadata, contractCodecs) {
|
|
1102
|
-
if (metadata.codec && contractCodecs) return contractCodecs.forCodecRef(metadata.codec);
|
|
1103
|
-
}
|
|
1104
|
-
function paramLabel(metadata, paramIndex) {
|
|
1105
|
-
return metadata.name ?? `param[${paramIndex}]`;
|
|
1106
|
-
}
|
|
1107
|
-
function wrapEncodeFailure(error, metadata, paramIndex, codecId) {
|
|
1108
|
-
const label = paramLabel(metadata, paramIndex);
|
|
1109
|
-
const wrapped = runtimeError("RUNTIME.ENCODE_FAILED", `Failed to encode parameter ${label} with codec '${codecId}': ${error instanceof Error ? error.message : String(error)}`, {
|
|
1110
|
-
label,
|
|
1111
|
-
codec: codecId,
|
|
1112
|
-
paramIndex
|
|
1113
|
-
});
|
|
1114
|
-
wrapped.cause = error;
|
|
1115
|
-
throw wrapped;
|
|
1116
|
-
}
|
|
1117
|
-
async function encodeParamValue(value, metadata, paramIndex, ctx, contractCodecs) {
|
|
1118
|
-
if (value === null || value === void 0) return null;
|
|
1119
|
-
const codec = resolveParamCodec(metadata, contractCodecs);
|
|
1120
|
-
if (!codec) return value;
|
|
1121
|
-
try {
|
|
1122
|
-
return await codec.encode(value, ctx);
|
|
1123
|
-
} catch (error) {
|
|
1124
|
-
if (isRuntimeError(error)) throw error;
|
|
1125
|
-
wrapEncodeFailure(error, metadata, paramIndex, codec.id);
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
/**
|
|
1129
|
-
* Encodes all parameters concurrently via `Promise.all`. Per parameter, sync-and async-authored codecs share the same path: `codec.encode → await → return`. Param-level failures are wrapped in `RUNTIME.ENCODE_FAILED`.
|
|
1130
|
-
*
|
|
1131
|
-
* When `ctx.signal` is provided:
|
|
1132
|
-
*
|
|
1133
|
-
* - **Already-aborted at entry** short-circuits with `RUNTIME.ABORTED` (`{ phase: 'encode' }`) before any `codec.encode` call is made — codecs can pin this with a per-call counter that stays at zero.
|
|
1134
|
-
* - **Mid-flight abort** races the per-param `Promise.all` against `abortable(ctx.signal)`. The runtime returns `RUNTIME.ABORTED` promptly even if codec bodies ignore the signal; the in-flight bodies are abandoned and run to completion in the background (cooperative cancellation, see ADR 204).
|
|
1135
|
-
* - Existing `RUNTIME.ENCODE_FAILED` envelopes that surface from a codec body before the runtime observes the abort pass through unchanged (no double wrap).
|
|
1136
|
-
*/
|
|
1137
|
-
async function encodeParams(plan, ctx, contractCodecs) {
|
|
1138
|
-
return encodeParamsWithMetadata(plan.params, deriveParamMetadata(plan.ast), ctx, contractCodecs);
|
|
1139
|
-
}
|
|
1140
|
-
async function encodeParamsWithMetadata(values, metadata, ctx, contractCodecs) {
|
|
1141
|
-
checkAborted(ctx, "encode");
|
|
1142
|
-
const signal = ctx.signal;
|
|
1143
|
-
if (values.length === 0) return values;
|
|
1144
|
-
const tasks = values.map((value, i) => encodeParamValue(value, metadata[i] ?? NO_METADATA, i, ctx, contractCodecs));
|
|
1145
|
-
const settled = await raceAgainstAbort(Promise.all(tasks), signal, "encode");
|
|
1146
|
-
return Object.freeze(settled);
|
|
1147
|
-
}
|
|
1148
|
-
//#endregion
|
|
1149
1005
|
//#region src/content-hash.ts
|
|
1150
1006
|
/**
|
|
1151
1007
|
* Computes a stable content hash for a lowered SQL execution plan.
|
|
@@ -1778,6 +1634,6 @@ function createRuntime(options) {
|
|
|
1778
1634
|
});
|
|
1779
1635
|
}
|
|
1780
1636
|
//#endregion
|
|
1781
|
-
export {
|
|
1637
|
+
export { lints as a, extractCodecIds as c, deriveParamMetadata as d, encodeParamsWithMetadata as f, createSqlExecutionStack as i, validateCodecRegistryCompleteness as l, withTransaction as n, budgets as o, createAstCodecRegistry as p, createExecutionContext as r, lowerSqlPlan as s, createRuntime as t, validateContractCodecMappings as u };
|
|
1782
1638
|
|
|
1783
|
-
//# sourceMappingURL=exports-
|
|
1639
|
+
//# sourceMappingURL=exports-tEA99Uhc.mjs.map
|