@prisma-next/sql-runtime 0.12.0 → 0.13.0-dev.10

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 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 Management**: Provide SQL statements for reading/writing contract markers
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
- - `readContractMarker`, `writeContractMarker` - SQL marker statements
131
- - `ensureSchemaStatement`, `ensureTableStatement` - DDL statements for marker table setup
132
- - `SqlStatement` - SQL statement type
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 { type } from "arktype";
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.tables)) for (const column of Object.values(table.columns)) {
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.tables)) for (const [columnName, column] of Object.entries(table.columns)) {
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.tables)) for (const [columnName, column] of Object.entries(table.columns)) {
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 = {
@@ -556,7 +592,7 @@ function initializeTypeHelpers(storage, documentTypes, codecDescriptors) {
556
592
  helpers[typeName] = typeInstance;
557
593
  continue;
558
594
  }
559
- const validatedParams = validateTypeParams(typeParams, descriptor, { typeName });
595
+ const validatedParams = validateTypeParams(typeParams ?? {}, descriptor, { typeName });
560
596
  const ctx = {
561
597
  name: typeName,
562
598
  usedAt: typeRefSites.get(typeName) ?? []
@@ -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.tables)) for (const [columnName, column] of Object.entries(table.columns)) if (column.typeParams) {
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,8 +622,8 @@ 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.tables)) for (const columnName of Object.keys(table.columns)) {
590
- const ref = codecDescriptors.codecRefForColumn(tableName, columnName);
625
+ for (const [namespaceId, ns] of Object.entries(storage.namespaces)) for (const [tableName, table] of Object.entries(ns.entries.table)) for (const columnName of Object.keys(table.columns)) {
626
+ const ref = codecDescriptors.codecRefForColumn(namespaceId, tableName, columnName);
591
627
  if (!ref) continue;
592
628
  const descriptor = codecDescriptors.descriptorFor(ref.codecId);
593
629
  if (!descriptor) throw runtimeError("RUNTIME.CODEC_DESCRIPTOR_MISSING", `Column '${tableName}.${columnName}' references codec '${ref.codecId}' but no contributor registered a codec descriptor for that codecId. Add the extension pack that owns the codec to the runtime stack.`, {
@@ -613,7 +649,9 @@ function assertColumnCodecIntegrity(storage, codecDescriptors) {
613
649
  actual: "no typeParams"
614
650
  });
615
651
  }
616
- if (!descriptor.isParameterized && ref.typeParams !== void 0) throw runtimeError("RUNTIME.CODEC_PARAMETERIZATION_MISMATCH", `Column '${tableName}.${columnName}' supplies typeParams to non-parameterized codec '${ref.codecId}'. Remove the typeParams or switch to a parameterized codec id.`, {
652
+ const refTypeParams = ref.typeParams;
653
+ const refHasTypeParamKeys = refTypeParams !== void 0 && refTypeParams !== null && typeof refTypeParams === "object" && !Array.isArray(refTypeParams) && Object.keys(refTypeParams).length > 0;
654
+ if (!descriptor.isParameterized && refHasTypeParamKeys) throw runtimeError("RUNTIME.CODEC_PARAMETERIZATION_MISMATCH", `Column '${tableName}.${columnName}' supplies typeParams to non-parameterized codec '${ref.codecId}'. Remove the typeParams or switch to a parameterized codec id.`, {
617
655
  table: tableName,
618
656
  column: columnName,
619
657
  codecId: ref.codecId,
@@ -636,7 +674,7 @@ function assertColumnCodecIntegrity(storage, codecDescriptors) {
636
674
  *
637
675
  * Contract integrity is enforced upstream by {@link assertColumnCodecIntegrity}: every column must reference a registered `codecId` whose `descriptor.isParameterized` flag matches the presence of `typeParams` (via `codecRefForColumn`). The pre-population walk and `forColumn` therefore make no defensive checks — malformed columns fail fast at `createExecutionContext` construction with `RUNTIME.CODEC_DESCRIPTOR_MISSING` or `RUNTIME.CODEC_PARAMETERIZATION_MISMATCH` rather than being silently skipped here.
638
676
  *
639
- * `forColumn(t, c)` is a thin delegate over `forCodecRef(codecRefForColumn(t, c))`; encode/decode hot paths read the resolver directly via `forCodecRef`. The only `undefined` `forColumn` returns is the legitimate "no such column in the contract" case.
677
+ * `forColumn(ns, t, c)` is a thin delegate over `forCodecRef(codecRefForColumn(ns, t, c))`; encode/decode hot paths read the resolver directly via `forCodecRef`. The only `undefined` `forColumn` returns is the legitimate "no such column in the contract" case.
640
678
  */
641
679
  function buildContractCodecRegistry(contract, codecDescriptors) {
642
680
  const refKeyOf = (ref) => `${ref.codecId}:${canonicalizeJson(ref.typeParams)}`;
@@ -645,13 +683,15 @@ function buildContractCodecRegistry(contract, codecDescriptors) {
645
683
  const typeRefSites = collectTypeRefSites(contract.storage);
646
684
  for (const [typeName, typeInstance] of Object.entries(documentScopedCodecTypes(contract) ?? {})) {
647
685
  const enumView = readEnumViewIfApplicable(typeInstance);
648
- const key = refKeyOf(enumView ? {
686
+ const instanceTypeParams = enumView ? enumView.typeParams : typeInstance.typeParams;
687
+ const hasParamKeys = instanceTypeParams !== void 0 && Object.keys(instanceTypeParams).length > 0;
688
+ const key = refKeyOf(enumView ? hasParamKeys ? {
649
689
  codecId: enumView.codecId,
650
- typeParams: enumView.typeParams
651
- } : {
690
+ typeParams: instanceTypeParams
691
+ } : { codecId: enumView.codecId } : hasParamKeys ? {
652
692
  codecId: typeInstance.codecId,
653
- typeParams: typeInstance.typeParams
654
- });
693
+ typeParams: instanceTypeParams
694
+ } : { codecId: typeInstance.codecId });
655
695
  const sites = typeRefSites.get(typeName) ?? [];
656
696
  const existing = usedAtByKey.get(key);
657
697
  if (existing) existing.push(...sites);
@@ -660,9 +700,9 @@ function buildContractCodecRegistry(contract, codecDescriptors) {
660
700
  nameByKey.set(key, typeName);
661
701
  }
662
702
  }
663
- for (const ns of Object.values(contract.storage.namespaces)) for (const [tableName, table] of Object.entries(ns.tables)) for (const [columnName, column] of Object.entries(table.columns)) {
703
+ for (const [namespaceId, ns] of Object.entries(contract.storage.namespaces)) for (const [tableName, table] of Object.entries(ns.entries.table)) for (const [columnName, column] of Object.entries(table.columns)) {
664
704
  if (column.typeRef !== void 0) continue;
665
- const ref = codecDescriptors.codecRefForColumn(tableName, columnName);
705
+ const ref = codecDescriptors.codecRefForColumn(namespaceId, tableName, columnName);
666
706
  if (!ref) continue;
667
707
  const key = refKeyOf(ref);
668
708
  const site = {
@@ -684,14 +724,14 @@ function buildContractCodecRegistry(contract, codecDescriptors) {
684
724
  usedAt: usedAtByKey.get(key) ?? []
685
725
  };
686
726
  });
687
- for (const ns of Object.values(contract.storage.namespaces)) for (const [tableName, table] of Object.entries(ns.tables)) for (const columnName of Object.keys(table.columns)) {
688
- const ref = codecDescriptors.codecRefForColumn(tableName, columnName);
727
+ for (const [namespaceId, ns] of Object.entries(contract.storage.namespaces)) for (const [tableName, table] of Object.entries(ns.entries.table)) for (const columnName of Object.keys(table.columns)) {
728
+ const ref = codecDescriptors.codecRefForColumn(namespaceId, tableName, columnName);
689
729
  if (!ref) continue;
690
730
  resolver.forCodecRef(ref);
691
731
  }
692
732
  return {
693
- forColumn(table, column) {
694
- const ref = codecDescriptors.codecRefForColumn(table, column);
733
+ forColumn(namespaceId, table, column) {
734
+ const ref = codecDescriptors.codecRefForColumn(namespaceId, table, column);
695
735
  return ref ? resolver.forCodecRef(ref) : void 0;
696
736
  },
697
737
  forCodecRef(ref) {
@@ -817,125 +857,6 @@ function createExecutionContext(options) {
817
857
  };
818
858
  }
819
859
  //#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
860
  //#region src/codecs/decoding.ts
940
861
  const WIRE_PREVIEW_LIMIT = 100;
941
862
  const EMPTY_INCLUDE_ALIASES = /* @__PURE__ */ new Set();
@@ -1085,67 +1006,6 @@ async function decodeRow(row, decodeCtx, rowCtx) {
1085
1006
  return decoded;
1086
1007
  }
1087
1008
  //#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
1009
  //#region src/content-hash.ts
1150
1010
  /**
1151
1011
  * Computes a stable content hash for a lowered SQL execution plan.
@@ -1698,31 +1558,24 @@ async function withTransaction(runtime, fn) {
1698
1558
  const connection = await runtime.connection();
1699
1559
  const transaction = await connection.transaction();
1700
1560
  let invalidated = false;
1561
+ async function* guardedStream(inner) {
1562
+ if (invalidated) throw transactionClosedError();
1563
+ for await (const row of inner) {
1564
+ yield row;
1565
+ if (invalidated) throw transactionClosedError();
1566
+ }
1567
+ }
1701
1568
  const txContext = {
1702
1569
  get invalidated() {
1703
1570
  return invalidated;
1704
1571
  },
1705
1572
  execute(plan, options) {
1706
1573
  if (invalidated) throw transactionClosedError();
1707
- const inner = transaction.execute(plan, options);
1708
- const guarded = async function* () {
1709
- for await (const row of inner) {
1710
- if (invalidated) throw transactionClosedError();
1711
- yield row;
1712
- }
1713
- };
1714
- return new AsyncIterableResult(guarded());
1574
+ return new AsyncIterableResult(guardedStream(transaction.execute(plan, options)));
1715
1575
  },
1716
1576
  executePrepared(ps, params, options) {
1717
1577
  if (invalidated) throw transactionClosedError();
1718
- const inner = transaction.executePrepared(ps, params, options);
1719
- const guarded = async function* () {
1720
- for await (const row of inner) {
1721
- if (invalidated) throw transactionClosedError();
1722
- yield row;
1723
- }
1724
- };
1725
- return new AsyncIterableResult(guarded());
1578
+ return new AsyncIterableResult(guardedStream(transaction.executePrepared(ps, params, options)));
1726
1579
  }
1727
1580
  };
1728
1581
  let connectionDisposed = false;
@@ -1778,6 +1631,6 @@ function createRuntime(options) {
1778
1631
  });
1779
1632
  }
1780
1633
  //#endregion
1781
- export { ensureTableStatement as a, createExecutionContext as c, budgets as d, parseContractMarkerRow as f, validateContractCodecMappings as g, validateCodecRegistryCompleteness as h, ensureSchemaStatement as i, createSqlExecutionStack as l, extractCodecIds as m, withTransaction as n, readContractMarker as o, lowerSqlPlan as p, APP_SPACE_ID as r, writeContractMarker as s, createRuntime as t, lints as u };
1634
+ 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
1635
 
1783
- //# sourceMappingURL=exports-CXYd2w6k.mjs.map
1636
+ //# sourceMappingURL=exports-DDqF-xmg.mjs.map