@prisma-next/sql-runtime 0.6.0-dev.6 → 0.6.0-dev.7

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.
@@ -1,9 +1,10 @@
1
1
  import { AsyncIterableResult, RuntimeCore, checkAborted, checkMiddlewareCompatibility, isRuntimeError, raceAgainstAbort, runWithMiddleware, runtimeError } from "@prisma-next/framework-components/runtime";
2
2
  import { type } from "arktype";
3
- import { collectOrderedParamRefs, isQueryAst, validateParamRefRefs } from "@prisma-next/sql-relational-core/ast";
3
+ import { collectOrderedParamRefs, isQueryAst } from "@prisma-next/sql-relational-core/ast";
4
4
  import { ifDefined } from "@prisma-next/utils/defined";
5
5
  import { checkContractComponentRequirements } 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";
7
8
  import { createSqlOperationRegistry } from "@prisma-next/sql-operations";
8
9
  import { buildCodecDescriptorRegistry } from "@prisma-next/sql-relational-core/codec-descriptor-registry";
9
10
  import { APP_SPACE_ID } from "@prisma-next/framework-components/control";
@@ -388,6 +389,46 @@ function lints(options) {
388
389
  });
389
390
  }
390
391
  //#endregion
392
+ //#region src/codecs/ast-codec-resolver.ts
393
+ /**
394
+ * Build an {@link AstCodecResolver} bound to a descriptor registry and a per-call instance-context factory.
395
+ *
396
+ * 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.
397
+ */
398
+ function createAstCodecResolver(descriptors, instanceContextFor) {
399
+ const cache = /* @__PURE__ */ new Map();
400
+ return { forCodecRef(ref) {
401
+ const key = `${ref.codecId}:${canonicalizeJson(ref.typeParams)}`;
402
+ const cached = cache.get(key);
403
+ if (cached) return cached;
404
+ const descriptor = descriptors.descriptorFor(ref.codecId);
405
+ if (!descriptor) throw runtimeError("RUNTIME.CODEC_DESCRIPTOR_MISSING", `No codec descriptor registered for codecId '${ref.codecId}'.`, { codecId: ref.codecId });
406
+ const validated = validateTypeParams$1(descriptor.paramsSchema, descriptor.isParameterized && ref.typeParams === void 0 ? {
407
+ ...ref,
408
+ typeParams: {}
409
+ } : ref);
410
+ const ctx = instanceContextFor(ref);
411
+ const codec = descriptor.factory(validated)(ctx);
412
+ cache.set(key, codec);
413
+ return codec;
414
+ } };
415
+ }
416
+ function validateTypeParams$1(paramsSchema, ref) {
417
+ const result = paramsSchema["~standard"].validate(ref.typeParams);
418
+ 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.`, {
419
+ codecId: ref.codecId,
420
+ typeParams: ref.typeParams
421
+ });
422
+ if ("issues" in result && result.issues) {
423
+ const messages = result.issues.map((issue) => issue.message).join("; ");
424
+ throw runtimeError("RUNTIME.TYPE_PARAMS_INVALID", `Invalid typeParams for codec '${ref.codecId}': ${messages}`, {
425
+ codecId: ref.codecId,
426
+ typeParams: ref.typeParams
427
+ });
428
+ }
429
+ return result.value;
430
+ }
431
+ //#endregion
391
432
  //#region src/sql-context.ts
392
433
  function createSqlExecutionStack(options) {
393
434
  return createExecutionStack({
@@ -502,93 +543,124 @@ function validateColumnTypeParams(storage, codecDescriptors) {
502
543
  });
503
544
  }
504
545
  }
505
- function isResolvedCodec(candidate) {
506
- return candidate !== null && typeof candidate === "object" && "id" in candidate && "decode" in candidate;
507
- }
508
546
  /**
509
- * Walk the contract's `storage.tables[].columns[]` and resolve each column to a `Codec` through the unified descriptor map. Per-instance behavior:
547
+ * Build-time contract-integrity check: every `(table, column)` resolves to a {@link CodecRef} whose `codecId` is registered and whose `typeParams` presence matches the descriptor's `isParameterized` flag.
548
+ *
549
+ * Surfaces three classes of malformed contract that AST-bound codec resolution would otherwise mask silently:
510
550
  *
511
- * - **typeRef columns**: reuse the resolved codec materialized once by `initializeTypeHelpers` for the `storage.types` entry. Multiple columns sharing one typeRef share one codec instance.
512
- * - **inline-typeParams columns**: call `descriptor.factory(typeParams) (ctx)` once per column (per-column anonymous instance).
513
- * - **non-parameterized columns**: call `descriptor.factory()(ctx)` once. The synthesized descriptor's factory is constant — every call returns the same shared codec instance — so columns sharing a non-parameterized codec id share one resolved codec without explicit caching.
551
+ * - column references a codecId no contributor registered `RUNTIME.CODEC_DESCRIPTOR_MISSING`.
552
+ * - parameterized codec, no `typeParams` (legacy "tolerate refs without params" shape) → `RUNTIME.CODEC_PARAMETERIZATION_MISMATCH`.
553
+ * - non-parameterized codec, `typeParams` supplied `RUNTIME.CODEC_PARAMETERIZATION_MISMATCH`.
514
554
  *
515
- * Codec-registry-unification spec § AC-4: every column resolves through one descriptor map without branching on parameterization. JSON-Schema validation, when required, lives inside the resolved codec's `decode` body (see `arktype-json`'s `ArktypeJsonCodecClass`); the framework no longer maintains a parallel validator registry.
555
+ * 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.
516
556
  */
517
- function buildContractCodecRegistry(contract, codecDescriptors, types, parameterizedDescriptors) {
518
- const byColumn = /* @__PURE__ */ new Map();
519
- const byCodecId = /* @__PURE__ */ new Map();
520
- const ambiguousCodecIds = /* @__PURE__ */ new Set();
521
- for (const descriptor of codecDescriptors.values()) {
522
- if (descriptor.isParameterized) continue;
523
- const ctx = {
524
- name: `<shared:${descriptor.codecId}>`,
525
- usedAt: []
526
- };
527
- const voidFactory = descriptor.factory;
528
- byCodecId.set(descriptor.codecId, voidFactory(void 0)(ctx));
557
+ function assertColumnCodecIntegrity(storage, codecDescriptors) {
558
+ for (const [tableName, table] of Object.entries(storage.tables)) for (const columnName of Object.keys(table.columns)) {
559
+ const ref = codecDescriptors.codecRefForColumn(tableName, columnName);
560
+ if (!ref) continue;
561
+ const descriptor = codecDescriptors.descriptorFor(ref.codecId);
562
+ 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.`, {
563
+ table: tableName,
564
+ column: columnName,
565
+ codecId: ref.codecId
566
+ });
567
+ if (descriptor.isParameterized && ref.typeParams === void 0) {
568
+ const probe = descriptor.paramsSchema["~standard"].validate({});
569
+ if (probe instanceof Promise) {
570
+ probe.catch(() => {});
571
+ throw runtimeError("RUNTIME.TYPE_PARAMS_INVALID", `Column '${tableName}.${columnName}' uses parameterized codec '${ref.codecId}' whose paramsSchema returned a Promise; paramsSchema must be a synchronous Standard Schema validator. Return a value/issues result directly instead of a Promise.`, {
572
+ table: tableName,
573
+ column: columnName,
574
+ codecId: ref.codecId
575
+ });
576
+ }
577
+ if ("issues" in probe && !!probe.issues) throw runtimeError("RUNTIME.CODEC_PARAMETERIZATION_MISMATCH", `Column '${tableName}.${columnName}' uses parameterized codec '${ref.codecId}' but no typeParams are supplied. Provide typeParams on the column, or use a typeRef pointing at a storage.types entry that carries them.`, {
578
+ table: tableName,
579
+ column: columnName,
580
+ codecId: ref.codecId,
581
+ expected: "parameterized",
582
+ actual: "no typeParams"
583
+ });
584
+ }
585
+ 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.`, {
586
+ table: tableName,
587
+ column: columnName,
588
+ codecId: ref.codecId,
589
+ expected: "non-parameterized",
590
+ actual: "has typeParams"
591
+ });
529
592
  }
530
- const parameterizedRepresentatives = /* @__PURE__ */ new Map();
531
- for (const descriptor of codecDescriptors.values()) {
532
- if (!descriptor.isParameterized) continue;
533
- const ctx = {
534
- name: `<shared:${descriptor.codecId}>`,
535
- usedAt: []
536
- };
537
- const factory = descriptor.factory.bind(descriptor);
538
- try {
539
- parameterizedRepresentatives.set(descriptor.codecId, factory(void 0)(ctx));
540
- } catch {}
593
+ }
594
+ /**
595
+ * Build a {@link ContractCodecRegistry} that resolves codecs exclusively through the `forCodecRef` content-keyed cache.
596
+ *
597
+ * One pre-population pass walks `storage.types` and `storage.tables[].columns[]` to seed the resolver's per-ref instance context with the *aggregated* `usedAt` set for each canonical `(codecId, typeParams)` key. The same codec materialised through `forColumn` or `forCodecRef` is therefore one instance with one `SqlCodecInstanceContext` — stateful codecs reading `usedAt` see the full column set regardless of which surface the caller used.
598
+ *
599
+ * Per-key instance-name policy:
600
+ *
601
+ * - typeRef-shared columns use the `storage.types[name]` name.
602
+ * - inline-`typeParams` columns use `<col:Table.column>` (the first column observed at that key; additional columns sharing the key extend `usedAt`).
603
+ * - non-parameterized codec ids use `<codec:codecId>`, aggregating every column on that codec id into one `usedAt` set.
604
+ * - ad-hoc refs the contract walk did not pre-populate (e.g. AST-supplied refs from deserialised migration ops) fall back to the canonical cache key `${codecId}:${canonicalizeJson(typeParams)}` — the only structurally honest identity for an ad-hoc ref, distinct per `(codecId, typeParams)`.
605
+ *
606
+ * 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.
607
+ *
608
+ * `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.
609
+ */
610
+ function buildContractCodecRegistry(contract, codecDescriptors) {
611
+ const refKeyOf = (ref) => `${ref.codecId}:${canonicalizeJson(ref.typeParams)}`;
612
+ const usedAtByKey = /* @__PURE__ */ new Map();
613
+ const nameByKey = /* @__PURE__ */ new Map();
614
+ const typeRefSites = collectTypeRefSites(contract.storage);
615
+ for (const [typeName, typeInstance] of Object.entries(contract.storage.types ?? {})) {
616
+ const key = refKeyOf({
617
+ codecId: typeInstance.codecId,
618
+ typeParams: typeInstance.typeParams
619
+ });
620
+ const sites = typeRefSites.get(typeName) ?? [];
621
+ const existing = usedAtByKey.get(key);
622
+ if (existing) existing.push(...sites);
623
+ else {
624
+ usedAtByKey.set(key, [...sites]);
625
+ nameByKey.set(key, typeName);
626
+ }
541
627
  }
542
628
  for (const [tableName, table] of Object.entries(contract.storage.tables)) for (const [columnName, column] of Object.entries(table.columns)) {
543
- const columnKey = `${tableName}.${columnName}`;
544
- const descriptor = codecDescriptors.descriptorFor(column.codecId);
545
- let resolvedCodec;
546
- if (descriptor) {
547
- const isParameterized = parameterizedDescriptors.has(column.codecId);
548
- if (column.typeRef) {
549
- const helper = types[column.typeRef];
550
- if (isResolvedCodec(helper)) resolvedCodec = helper;
551
- } else if (column.typeParams && isParameterized) {
552
- const parameterizedDescriptor = parameterizedDescriptors.get(column.codecId);
553
- if (parameterizedDescriptor) {
554
- const validatedParams = validateTypeParams(column.typeParams, parameterizedDescriptor, {
555
- tableName,
556
- columnName
557
- });
558
- const ctx = {
559
- name: `<col:${tableName}.${columnName}>`,
560
- usedAt: [{
561
- table: tableName,
562
- column: columnName
563
- }]
564
- };
565
- resolvedCodec = parameterizedDescriptor.factory(validatedParams)(ctx);
566
- }
567
- } else if (!isParameterized) {
568
- const ctx = {
569
- name: `<col:${tableName}.${columnName}>`,
570
- usedAt: [{
571
- table: tableName,
572
- column: columnName
573
- }]
574
- };
575
- resolvedCodec = descriptor.factory.bind(descriptor)(void 0)(ctx);
576
- }
577
- }
578
- if (resolvedCodec) {
579
- byColumn.set(columnKey, resolvedCodec);
580
- const existing = byCodecId.get(column.codecId);
581
- if (existing === void 0) byCodecId.set(column.codecId, resolvedCodec);
582
- else if (existing !== resolvedCodec && parameterizedDescriptors.has(column.codecId)) ambiguousCodecIds.add(column.codecId);
629
+ if (column.typeRef !== void 0) continue;
630
+ const ref = codecDescriptors.codecRefForColumn(tableName, columnName);
631
+ if (!ref) continue;
632
+ const key = refKeyOf(ref);
633
+ const site = {
634
+ table: tableName,
635
+ column: columnName
636
+ };
637
+ const existing = usedAtByKey.get(key);
638
+ if (existing) existing.push(site);
639
+ else {
640
+ usedAtByKey.set(key, [site]);
641
+ const name = ref.typeParams !== void 0 ? `<col:${tableName}.${columnName}>` : `<codec:${ref.codecId}>`;
642
+ nameByKey.set(key, name);
583
643
  }
584
644
  }
645
+ const resolver = createAstCodecResolver(codecDescriptors, (ref) => {
646
+ const key = refKeyOf(ref);
647
+ return {
648
+ name: nameByKey.get(key) ?? key,
649
+ usedAt: usedAtByKey.get(key) ?? []
650
+ };
651
+ });
652
+ for (const [tableName, table] of Object.entries(contract.storage.tables)) for (const columnName of Object.keys(table.columns)) {
653
+ const ref = codecDescriptors.codecRefForColumn(tableName, columnName);
654
+ if (!ref) continue;
655
+ resolver.forCodecRef(ref);
656
+ }
585
657
  return {
586
658
  forColumn(table, column) {
587
- return byColumn.get(`${table}.${column}`);
659
+ const ref = codecDescriptors.codecRefForColumn(table, column);
660
+ return ref ? resolver.forCodecRef(ref) : void 0;
588
661
  },
589
- forCodecId(codecId) {
590
- if (ambiguousCodecIds.has(codecId)) throw runtimeError("RUNTIME.TYPE_PARAMS_INVALID", `Codec '${codecId}' resolves to multiple parameterized instances; column-aware dispatch is required.`, { codecId });
591
- return byCodecId.get(codecId) ?? parameterizedRepresentatives.get(codecId);
662
+ forCodecRef(ref) {
663
+ return resolver.forCodecRef(ref);
592
664
  }
593
665
  };
594
666
  }
@@ -683,14 +755,15 @@ function createExecutionContext(options) {
683
755
  const ops = contributor.queryOperations?.() ?? {};
684
756
  for (const [name, op] of Object.entries(ops)) queryOperationRegistry.register(name, op);
685
757
  }
686
- const codecDescriptors = buildCodecDescriptorRegistry(allCodecDescriptors);
758
+ const codecDescriptors = buildCodecDescriptorRegistry(allCodecDescriptors, contract.storage);
759
+ assertColumnCodecIntegrity(contract.storage, codecDescriptors);
687
760
  const mutationDefaultGeneratorRegistry = collectMutationDefaultGenerators(contributors);
688
761
  assertMutationDefaultGeneratorsAvailable(contract, mutationDefaultGeneratorRegistry);
689
762
  if (parameterizedCodecDescriptors.size > 0) validateColumnTypeParams(contract.storage, parameterizedCodecDescriptors);
690
763
  const types = initializeTypeHelpers(contract.storage, parameterizedCodecDescriptors);
691
764
  return {
692
765
  contract,
693
- contractCodecs: buildContractCodecRegistry(contract, codecDescriptors, types, parameterizedCodecDescriptors),
766
+ contractCodecs: buildContractCodecRegistry(contract, codecDescriptors),
694
767
  codecDescriptors,
695
768
  queryOperations: queryOperationRegistry,
696
769
  types,
@@ -817,33 +890,6 @@ function writeContractMarker(input) {
817
890
  };
818
891
  }
819
892
  //#endregion
820
- //#region src/codecs/alias-resolver.ts
821
- /**
822
- * Build a map from query-local table aliases to their underlying source table names.
823
- *
824
- * Self-joins like `db.sql.post.as('p1').innerJoin(db.sql.post.as('p2'), …)` produce `ColumnRef`s whose `table` is the alias (`p1`, `p2`) — the SQL renderer needs the alias for `SELECT p1.id, …`. Codec dispatch keys `byColumn` by the underlying source table, so aliases must be resolved back to the source name for `forColumn(...)` to hit. Tables that already use their canonical name (no alias) are also entered so a single
825
- * lookup works for both shapes.
826
- */
827
- function buildAliasMap(ast) {
828
- const aliases = /* @__PURE__ */ new Map();
829
- const recordSource = (source) => {
830
- if (source.kind === "table-source") {
831
- const key = source.alias ?? source.name;
832
- aliases.set(key, source.name);
833
- } else aliases.set(source.alias, source.alias);
834
- };
835
- if (ast.kind === "select") {
836
- recordSource(ast.from);
837
- for (const join of ast.joins ?? []) recordSource(join.source);
838
- } else if (ast.kind === "raw-sql") {} else recordSource(ast.table);
839
- return aliases;
840
- }
841
- function makeAliasResolver(ast) {
842
- if (!ast) return (alias) => alias;
843
- const map = buildAliasMap(ast);
844
- return (alias) => map.get(alias) ?? alias;
845
- }
846
- //#endregion
847
893
  //#region src/codecs/decoding.ts
848
894
  const WIRE_PREVIEW_LIMIT = 100;
849
895
  const EMPTY_INCLUDE_ALIASES = /* @__PURE__ */ new Set();
@@ -855,30 +901,8 @@ function projectionListFromAst(ast) {
855
901
  if (ast.kind === "raw-sql") return;
856
902
  return ast.returning;
857
903
  }
858
- /**
859
- * Resolve the per-cell codec for a projection item.
860
- *
861
- * When a `(table, column)` ref is available — either implicit on a `column-ref` expression or carried explicitly via `item.refs` for column-bound non-`column-ref` projections — prefer `contractCodecs.forColumn(table, column)`: that returns the per-instance codec materialized from the descriptor's factory for that column, encoding any per-instance state (typeParams like vector length, schema validators, etc.).
862
- *
863
- * The wrong-instance risk for parameterized codecs is closed off structurally:
864
- *
865
- * 1. `buildContractCodecRegistry` pre-populates `byCodecId` with one canonical instance per non-parameterized descriptor; parameterized descriptors are intentionally absent. 2. `forCodecId` rejects ambiguous parameterized fallbacks (`ambiguousCodecIds`). 3. The non-ambiguous parameterized case stores the column-correct per-instance codec under `byCodecId`, so the fall-through still resolves to the right instance.
866
- *
867
- * The `forCodecId` fallback otherwise covers projections that are *not* column-bound (computed projections, raw SQL aliases) but still carry a `codecId` (ADR 205 stamps every `ProjectionItem` with the producer's codec id).
868
- *
869
- * Codec-registry-unification spec § AC-4 / AC-5.
870
- */
871
- function resolveProjectionCodec(item, contractCodecs, aliasResolver) {
872
- if (contractCodecs) {
873
- if (item.expr.kind === "column-ref") {
874
- const byColumn = contractCodecs.forColumn(aliasResolver(item.expr.table), item.expr.column);
875
- if (byColumn && (item.codecId === void 0 || byColumn.id === item.codecId)) return byColumn;
876
- } else if (item.refs) {
877
- const byColumn = contractCodecs.forColumn(aliasResolver(item.refs.table), item.refs.column);
878
- if (byColumn && (item.codecId === void 0 || byColumn.id === item.codecId)) return byColumn;
879
- }
880
- }
881
- if (item.codecId) return contractCodecs?.forCodecId(item.codecId);
904
+ function resolveProjectionCodec(item, contractCodecs) {
905
+ if (item.codec && contractCodecs) return contractCodecs.forCodecRef(item.codec);
882
906
  }
883
907
  function buildDecodeContext(plan, contractCodecs) {
884
908
  if (!isAstBackedPlan(plan)) return {
@@ -898,19 +922,14 @@ function buildDecodeContext(plan, contractCodecs) {
898
922
  const codecs = /* @__PURE__ */ new Map();
899
923
  const columnRefs = /* @__PURE__ */ new Map();
900
924
  const includeAliases = /* @__PURE__ */ new Set();
901
- const aliasResolver = makeAliasResolver(plan.ast);
902
925
  for (const item of projection) {
903
926
  aliases.push(item.alias);
904
- const codec = resolveProjectionCodec(item, contractCodecs, aliasResolver);
927
+ const codec = resolveProjectionCodec(item, contractCodecs);
905
928
  if (codec) codecs.set(item.alias, codec);
906
929
  if (item.expr.kind === "column-ref") columnRefs.set(item.alias, {
907
- table: aliasResolver(item.expr.table),
930
+ table: item.expr.table,
908
931
  column: item.expr.column
909
932
  });
910
- else if (item.refs) columnRefs.set(item.alias, {
911
- table: aliasResolver(item.refs.table),
912
- column: item.refs.column
913
- });
914
933
  else if (item.expr.kind === "subquery" || item.expr.kind === "json-array-agg") includeAliases.add(item.alias);
915
934
  }
916
935
  return {
@@ -1035,29 +1054,11 @@ async function decodeRow(row, plan, rowCtx, contractCodecs) {
1035
1054
  //#endregion
1036
1055
  //#region src/codecs/encoding.ts
1037
1056
  const NO_METADATA = Object.freeze({
1038
- codecId: void 0,
1039
- name: void 0,
1040
- refs: void 0
1057
+ codec: void 0,
1058
+ name: void 0
1041
1059
  });
1042
- /**
1043
- * Resolve the codec for an outgoing param.
1044
- *
1045
- * Column-aware dispatch: when `metadata.refs` is populated by a column-bound construction site, prefer `contractCodecs.forColumn(refs.table, refs.column)` — that returns the per-instance codec the contract walk materialized for the `(table, column)` pair, encoding the column's typeParams (e.g. `vector(1024)` vs. `vector(1536)`).
1046
- *
1047
- * On a column-lookup miss the resolver falls through to `forCodecId`. The wrong-instance risk for parameterized codecs is closed off structurally:
1048
- *
1049
- * 1. `buildContractCodecRegistry` pre-populates `byCodecId` with one canonical instance per non-parameterized descriptor; parameterized descriptors are intentionally absent from this pre-population. 2. `forCodecId` rejects ambiguous parameterized fallbacks (`ambiguousCodecIds`) — if the contract walk resolved more than one distinct instance under a single parameterized id, the call throws rather than binding to
1050
- * whichever landed first. 3. For the non-ambiguous parameterized case (a single column with that id), `byCodecId` stores the column-correct per-instance codec, so the fall-through still resolves to the right instance.
1051
- *
1052
- * Refs-less fallback: ParamRefs constructed outside a column-bound site (literals, transient builder state) carry a non-parameterized `codecId` whose dispatch is ambiguity-free. The validator pass (`validateParamRefRefs`) already enforced refs on every parameterized ParamRef before encode runs.
1053
- */
1054
- function resolveParamCodec(metadata, contractCodecs, aliasResolver) {
1055
- if (!metadata.codecId) return void 0;
1056
- if (metadata.refs && contractCodecs) {
1057
- const byColumn = contractCodecs.forColumn(aliasResolver(metadata.refs.table), metadata.refs.column);
1058
- if (byColumn && byColumn.id === metadata.codecId) return byColumn;
1059
- }
1060
- return contractCodecs?.forCodecId(metadata.codecId);
1060
+ function resolveParamCodec(metadata, contractCodecs) {
1061
+ if (metadata.codec && contractCodecs) return contractCodecs.forCodecRef(metadata.codec);
1061
1062
  }
1062
1063
  function paramLabel(metadata, paramIndex) {
1063
1064
  return metadata.name ?? `param[${paramIndex}]`;
@@ -1072,9 +1073,9 @@ function wrapEncodeFailure(error, metadata, paramIndex, codecId) {
1072
1073
  wrapped.cause = error;
1073
1074
  throw wrapped;
1074
1075
  }
1075
- async function encodeParamValue(value, metadata, paramIndex, ctx, contractCodecs, aliasResolver) {
1076
+ async function encodeParamValue(value, metadata, paramIndex, ctx, contractCodecs) {
1076
1077
  if (value === null || value === void 0) return null;
1077
- const codec = resolveParamCodec(metadata, contractCodecs, aliasResolver);
1078
+ const codec = resolveParamCodec(metadata, contractCodecs);
1078
1079
  if (!codec) return value;
1079
1080
  try {
1080
1081
  return await codec.encode(value, ctx);
@@ -1103,15 +1104,13 @@ async function encodeParams(plan, ctx, contractCodecs) {
1103
1104
  for (let i = 0; i < paramCount && i < refs.length; i++) {
1104
1105
  const ref = refs[i];
1105
1106
  if (ref) metadata[i] = {
1106
- codecId: ref.codecId,
1107
- name: ref.name,
1108
- refs: ref.refs
1107
+ codec: ref.codec,
1108
+ name: ref.name
1109
1109
  };
1110
1110
  }
1111
1111
  }
1112
- const aliasResolver = makeAliasResolver(plan.ast);
1113
1112
  const tasks = new Array(paramCount);
1114
- for (let i = 0; i < paramCount; i++) tasks[i] = encodeParamValue(plan.params[i], metadata[i] ?? NO_METADATA, i, ctx, contractCodecs, aliasResolver);
1113
+ for (let i = 0; i < paramCount; i++) tasks[i] = encodeParamValue(plan.params[i], metadata[i] ?? NO_METADATA, i, ctx, contractCodecs);
1115
1114
  const settled = await raceAgainstAbort(Promise.all(tasks), signal, "encode");
1116
1115
  return Object.freeze(settled);
1117
1116
  }
@@ -1272,7 +1271,6 @@ var SqlRuntimeImpl = class extends RuntimeCore {
1272
1271
  * `ctx: SqlCodecCallContext` is forwarded to `encodeParams` so per-query cancellation reaches every codec body during parameter encoding. The framework abstract typed this as `CodecCallContext`; the SQL family narrows it to the SQL-specific extension. SQL params do not populate `ctx.column` — encode-side column metadata is the middleware's domain.
1273
1272
  */
1274
1273
  async lower(plan, ctx) {
1275
- validateParamRefRefs(plan.ast, this.codecDescriptors);
1276
1274
  const lowered = lowerSqlPlan(this.adapter, this.contract, plan);
1277
1275
  return Object.freeze({
1278
1276
  ...lowered,
@@ -1319,13 +1317,11 @@ var SqlRuntimeImpl = class extends RuntimeCore {
1319
1317
  const generator = async function* () {
1320
1318
  checkAborted(codecCtx, "stream");
1321
1319
  let exec;
1322
- if (isExecutionPlan(plan)) {
1323
- if (plan.ast) validateParamRefRefs(plan.ast, self.codecDescriptors);
1324
- exec = Object.freeze({
1325
- ...plan,
1326
- params: await encodeParams(plan, codecCtx, self.contractCodecs)
1327
- });
1328
- } else exec = await self.lower(await self.runBeforeCompile(plan), codecCtx);
1320
+ if (isExecutionPlan(plan)) exec = Object.freeze({
1321
+ ...plan,
1322
+ params: await encodeParams(plan, codecCtx, self.contractCodecs)
1323
+ });
1324
+ else exec = await self.lower(await self.runBeforeCompile(plan), codecCtx);
1329
1325
  self.familyAdapter.validatePlan(exec, self.contract);
1330
1326
  self._telemetry = null;
1331
1327
  if (!self.startupVerified && self.verify.mode === "startup") await self.verifyMarker();
@@ -1514,4 +1510,4 @@ function createRuntime(options) {
1514
1510
  //#endregion
1515
1511
  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 };
1516
1512
 
1517
- //# sourceMappingURL=exports-B_kfmMPM.mjs.map
1513
+ //# sourceMappingURL=exports-BeaTZIiJ.mjs.map