@prisma-next/sql-contract-ts 0.14.0-dev.5 → 0.14.0-dev.50

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/package.json CHANGED
@@ -1,27 +1,27 @@
1
1
  {
2
2
  "name": "@prisma-next/sql-contract-ts",
3
- "version": "0.14.0-dev.5",
3
+ "version": "0.14.0-dev.50",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "description": "SQL-specific TypeScript contract authoring surface for Prisma Next",
8
8
  "dependencies": {
9
- "@prisma-next/config": "0.14.0-dev.5",
10
- "@prisma-next/contract": "0.14.0-dev.5",
11
- "@prisma-next/contract-authoring": "0.14.0-dev.5",
12
- "@prisma-next/framework-components": "0.14.0-dev.5",
13
- "@prisma-next/sql-contract": "0.14.0-dev.5",
14
- "@prisma-next/utils": "0.14.0-dev.5",
9
+ "@prisma-next/config": "0.14.0-dev.50",
10
+ "@prisma-next/contract": "0.14.0-dev.50",
11
+ "@prisma-next/contract-authoring": "0.14.0-dev.50",
12
+ "@prisma-next/framework-components": "0.14.0-dev.50",
13
+ "@prisma-next/sql-contract": "0.14.0-dev.50",
14
+ "@prisma-next/utils": "0.14.0-dev.50",
15
15
  "arktype": "^2.2.0",
16
16
  "pathe": "^2.0.3",
17
17
  "ts-toolbelt": "^9.6.0"
18
18
  },
19
19
  "devDependencies": {
20
- "@prisma-next/test-utils": "0.14.0-dev.5",
21
- "@prisma-next/tsconfig": "0.14.0-dev.5",
20
+ "@prisma-next/test-utils": "0.14.0-dev.50",
21
+ "@prisma-next/tsconfig": "0.14.0-dev.50",
22
22
  "@types/pg": "8.20.0",
23
23
  "pg": "8.21.0",
24
- "@prisma-next/tsdown": "0.14.0-dev.5",
24
+ "@prisma-next/tsdown": "0.14.0-dev.50",
25
25
  "tsdown": "0.22.1",
26
26
  "typescript": "5.9.3",
27
27
  "vitest": "4.1.8"
@@ -63,7 +63,7 @@ export function createTypeHelpersFromNamespace(
63
63
  continue;
64
64
  }
65
65
 
66
- helpers[key] = createTypeHelpersFromNamespace(value as AuthoringTypeNamespace, currentPath);
66
+ helpers[key] = createTypeHelpersFromNamespace(value, currentPath);
67
67
  }
68
68
 
69
69
  return helpers;
@@ -133,11 +133,7 @@ export function createFieldHelpersFromNamespace(
133
133
  continue;
134
134
  }
135
135
 
136
- helpers[key] = createFieldHelpersFromNamespace(
137
- value as AuthoringFieldNamespace,
138
- createLeafHelper,
139
- currentPath,
140
- );
136
+ helpers[key] = createFieldHelpersFromNamespace(value, createLeafHelper, currentPath);
141
137
  }
142
138
 
143
139
  return helpers;
@@ -33,9 +33,8 @@ import {
33
33
  } from '@prisma-next/sql-contract/index-types';
34
34
  import {
35
35
  applyFkDefaults,
36
- buildSqlNamespace,
37
36
  type CheckConstraintInput,
38
- type SqlNamespaceTablesInput,
37
+ type SqlNamespaceInput,
39
38
  SqlStorage,
40
39
  type SqlStorageInput,
41
40
  type StorageColumn,
@@ -74,10 +73,23 @@ function encodeColumnDefault(
74
73
  defaultInput: ColumnDefault,
75
74
  codecId: string,
76
75
  codecLookup?: CodecLookup,
76
+ many = false,
77
77
  ): ColumnDefault {
78
78
  if (defaultInput.kind === 'function') {
79
79
  return { kind: 'function', expression: defaultInput.expression };
80
80
  }
81
+ if (many) {
82
+ if (!Array.isArray(defaultInput.value)) {
83
+ throw new Error(
84
+ `Literal default on a list column must be an array; received ${typeof defaultInput.value}. ` +
85
+ 'A scalar default on a list field must be rejected at the authoring surface.',
86
+ );
87
+ }
88
+ return {
89
+ kind: 'literal',
90
+ value: defaultInput.value.map((element) => encodeViaCodec(element, codecId, codecLookup)),
91
+ };
92
+ }
81
93
  return {
82
94
  kind: 'literal',
83
95
  value: encodeViaCodec(defaultInput.value, codecId, codecLookup),
@@ -235,28 +247,28 @@ function buildStorageColumn(
235
247
  };
236
248
  }
237
249
 
238
- if (field.many) {
239
- return {
240
- nativeType: JSONB_NATIVE_TYPE,
241
- codecId: JSONB_CODEC_ID,
242
- nullable: field.nullable,
243
- };
244
- }
245
-
246
250
  const codecId = field.descriptor.codecId;
247
251
  const encodedDefault =
248
252
  field.default !== undefined
249
- ? encodeColumnDefault(field.default, codecId, codecLookup)
253
+ ? encodeColumnDefault(field.default, codecId, codecLookup, field.many === true)
250
254
  : undefined;
251
255
 
256
+ // `storageValueSetRef` (derived from an `enumTypeHandle`) takes precedence
257
+ // when present — the established domain-enum path. `field.descriptor.valueSet`
258
+ // is the fallback: set by an entity-ref type constructor (e.g. `pg.enum(Ref)`)
259
+ // that resolved the field's type against a value-set-deriving entity with no
260
+ // domain enum involved. A field carries at most one of the two in practice.
261
+ const valueSet = storageValueSetRef ?? field.descriptor.valueSet;
262
+
252
263
  return {
253
264
  nativeType: field.descriptor.nativeType,
254
265
  codecId,
255
266
  nullable: field.nullable,
267
+ ...(field.many ? { many: true as const } : {}),
256
268
  ...ifDefined('typeParams', field.descriptor.typeParams),
257
269
  ...ifDefined('default', encodedDefault),
258
270
  ...ifDefined('typeRef', field.descriptor.typeRef),
259
- ...ifDefined('valueSet', storageValueSetRef),
271
+ ...ifDefined('valueSet', valueSet),
260
272
  };
261
273
  }
262
274
 
@@ -308,18 +320,15 @@ function ensureUnboundNamespaceSlot(
308
320
  if (Object.hasOwn(namespaces, UNBOUND_NAMESPACE_ID)) {
309
321
  return namespaces;
310
322
  }
311
- const unboundInput: SqlNamespaceTablesInput = {
323
+ const unboundInput: SqlNamespaceInput = {
312
324
  id: UNBOUND_NAMESPACE_ID,
313
325
  entries: { table: {} },
314
326
  };
315
- const unbound = createNamespace ? createNamespace(unboundInput) : buildSqlNamespace(unboundInput);
316
- return blindCast<
317
- SqlStorageInput['namespaces'],
318
- 'createNamespace may return a target namespace concretion; the unbound slot matches SqlNamespace at runtime'
319
- >({
327
+ const unbound = createNamespace(unboundInput);
328
+ return {
320
329
  [UNBOUND_NAMESPACE_ID]: unbound,
321
330
  ...namespaces,
322
- });
331
+ };
323
332
  }
324
333
 
325
334
  export function buildSqlContractFromDefinition(
@@ -375,6 +384,7 @@ export function buildSqlContractFromDefinition(
375
384
  const fieldToColumn: Record<string, string> = {};
376
385
  const domainFields: Record<string, ContractField> = {};
377
386
  const domainFieldRefs: Record<string, DomainFieldRef> = {};
387
+ const checksForTable: CheckConstraintInput[] = [];
378
388
 
379
389
  for (const field of semanticModel.fields) {
380
390
  const executionDefaultPhases =
@@ -421,6 +431,25 @@ export function buildSqlContractFromDefinition(
421
431
  columns[field.columnName] = column;
422
432
  fieldToColumn[field.fieldName] = field.columnName;
423
433
 
434
+ // A domain enum (`storageValueSetRef`, from an `enumType()` handle) is
435
+ // stored as a plain scalar column (`text`, `int4`, …) with no native
436
+ // type of its own to enforce membership, so it needs an explicit
437
+ // CHECK — scalar or array, since a `text[]` array has no element-level
438
+ // enforcement either. A value set resolved by an entity-ref type
439
+ // constructor (`field.descriptor.valueSet`, e.g. `pg.enum(Ref)`) binds
440
+ // the column to a codec/native-type pairing that IS the storage-level
441
+ // enforcement (a Postgres native enum type, or another target's
442
+ // equivalent) — including array columns, since the target enforces
443
+ // membership on every element of a native-typed array — so no CHECK
444
+ // for those.
445
+ if (column.valueSet !== undefined && storageValueSetRef !== undefined) {
446
+ checksForTable.push({
447
+ name: `${tableName}_${field.columnName}_check`,
448
+ column: field.columnName,
449
+ valueSet: column.valueSet,
450
+ });
451
+ }
452
+
424
453
  domainFields[field.fieldName] = buildDomainField(field, column, domainValueSetRef);
425
454
 
426
455
  if (isValueObjectField(field)) {
@@ -511,15 +540,6 @@ export function buildSqlContractFromDefinition(
511
540
  // materialised onto the base `ModelNode`, so the variant builds a domain
512
541
  // model (below) but no storage table of its own.
513
542
  if (!semanticModel.sharesBaseTable) {
514
- const checksForTable: CheckConstraintInput[] = Object.entries(columns).flatMap(
515
- ([columnName, col]) => {
516
- const valueSet = col.valueSet;
517
- return valueSet === undefined
518
- ? []
519
- : [{ name: `${tableName}_${columnName}_check`, column: columnName, valueSet }];
520
- },
521
- );
522
-
523
543
  const tableInput: StorageTableInput = {
524
544
  columns,
525
545
  ...ifDefined('control', semanticModel.control),
@@ -729,25 +749,20 @@ export function buildSqlContractFromDefinition(
729
749
  }
730
750
 
731
751
  const { createNamespace } = definition;
732
- const namespaces = blindCast<
733
- SqlStorageInput['namespaces'],
734
- 'contract authoring materialises each namespace coordinate from the model set and explicit namespace list'
735
- >(
736
- Object.fromEntries(
737
- [...namespaceCoordinateIds].sort().map((id) => {
738
- const valueSetEntries = storageValueSetsByNs[id];
739
- const nsInput: SqlNamespaceTablesInput = {
740
- id,
741
- entries: {
742
- table: tablesByNamespace[id] ?? {},
743
- ...(valueSetEntries !== undefined && Object.keys(valueSetEntries).length > 0
744
- ? { valueSet: valueSetEntries }
745
- : {}),
746
- },
747
- };
748
- return [id, createNamespace ? createNamespace(nsInput) : buildSqlNamespace(nsInput)];
749
- }),
750
- ),
752
+ const namespaces: SqlStorageInput['namespaces'] = Object.fromEntries(
753
+ [...namespaceCoordinateIds].sort().map((id) => {
754
+ const valueSetEntries = storageValueSetsByNs[id];
755
+ const nsInput: SqlNamespaceInput = {
756
+ id,
757
+ entries: {
758
+ table: tablesByNamespace[id] ?? {},
759
+ ...(valueSetEntries !== undefined && Object.keys(valueSetEntries).length > 0
760
+ ? { valueSet: valueSetEntries }
761
+ : {}),
762
+ },
763
+ };
764
+ return [id, createNamespace(nsInput)];
765
+ }),
751
766
  );
752
767
  const storageWithoutHash = {
753
768
  ...(Object.keys(documentTypes).length > 0 ? { types: documentTypes } : {}),
@@ -13,9 +13,6 @@ import type {
13
13
  } from '@prisma-next/framework-components/authoring';
14
14
  import {
15
15
  assertNoCrossRegistryCollisions,
16
- isAuthoringEntityTypeDescriptor,
17
- isAuthoringFieldPresetDescriptor,
18
- isAuthoringTypeConstructorDescriptor,
19
16
  mergeAuthoringNamespaces,
20
17
  } from '@prisma-next/framework-components/authoring';
21
18
  import type {
@@ -183,7 +180,7 @@ function composeTypeNamespace(components: readonly AuthoringComponent[]): Author
183
180
  for (const component of components) {
184
181
  const ns = extractTypeNamespace(component);
185
182
  if (Object.keys(ns).length > 0) {
186
- mergeAuthoringNamespaces(merged, ns, [], isAuthoringTypeConstructorDescriptor, 'type');
183
+ mergeAuthoringNamespaces(merged, ns, [], 'typeConstructor', 'type');
187
184
  }
188
185
  }
189
186
  return merged as AuthoringTypeNamespace;
@@ -194,7 +191,7 @@ function composeFieldNamespace(components: readonly AuthoringComponent[]): Autho
194
191
  for (const component of components) {
195
192
  const ns = extractFieldNamespace(component);
196
193
  if (Object.keys(ns).length > 0) {
197
- mergeAuthoringNamespaces(merged, ns, [], isAuthoringFieldPresetDescriptor, 'field');
194
+ mergeAuthoringNamespaces(merged, ns, [], 'fieldPreset', 'field');
198
195
  }
199
196
  }
200
197
  return merged as AuthoringFieldNamespace;
@@ -207,7 +204,7 @@ function composeEntityNamespace(
207
204
  for (const component of components) {
208
205
  const ns = extractEntitiesNamespace(component);
209
206
  if (Object.keys(ns).length > 0) {
210
- mergeAuthoringNamespaces(merged, ns, [], isAuthoringEntityTypeDescriptor, 'entity');
207
+ mergeAuthoringNamespaces(merged, ns, [], 'entity', 'entity');
211
208
  }
212
209
  }
213
210
  return merged as AuthoringEntityTypeNamespace;
@@ -3,6 +3,7 @@ import type { ContractConfig } from '@prisma-next/config/config-types';
3
3
  import { applySpecifierDefaultControlPolicy } from '@prisma-next/contract/apply-specifier-default-control-policy';
4
4
  import type { Contract, ControlPolicy } from '@prisma-next/contract/types';
5
5
  import type { TargetPackRef } from '@prisma-next/framework-components/components';
6
+ import type { SqlNamespaceBase, SqlNamespaceInput } from '@prisma-next/sql-contract/types';
6
7
  import { ifDefined } from '@prisma-next/utils/defined';
7
8
  import { ok } from '@prisma-next/utils/result';
8
9
  import { extname } from 'pathe';
@@ -27,13 +28,18 @@ export interface TypeScriptContractSpecifierOptions {
27
28
  export function emptyContract(options: {
28
29
  readonly output?: string;
29
30
  readonly target: TargetPackRef<'sql', string>;
31
+ readonly createNamespace: (input: SqlNamespaceInput) => SqlNamespaceBase;
30
32
  readonly defaultControlPolicy?: ControlPolicy;
31
33
  }): ContractConfig {
32
34
  return {
33
35
  source: {
34
36
  sourceFormat: 'typescript',
35
37
  load: async () => {
36
- const built = buildSqlContractFromDefinition({ target: options.target, models: [] });
38
+ const built = buildSqlContractFromDefinition({
39
+ target: options.target,
40
+ createNamespace: options.createNamespace,
41
+ models: [],
42
+ });
37
43
  return ok(applySpecifierDefaultControlPolicy(built, options.defaultControlPolicy));
38
44
  },
39
45
  },
@@ -6,8 +6,11 @@ import type {
6
6
  FamilyPackRef,
7
7
  TargetPackRef,
8
8
  } from '@prisma-next/framework-components/components';
9
- import type { Namespace } from '@prisma-next/framework-components/ir';
10
- import type { SqlNamespaceTablesInput, StorageTypeInstance } from '@prisma-next/sql-contract/types';
9
+ import type {
10
+ SqlNamespaceBase,
11
+ SqlNamespaceInput,
12
+ StorageTypeInstance,
13
+ } from '@prisma-next/sql-contract/types';
11
14
  import { blindCast } from '@prisma-next/utils/casts';
12
15
  import { ifDefined } from '@prisma-next/utils/defined';
13
16
  import { buildSqlContractFromDefinition } from './build-contract';
@@ -68,7 +71,7 @@ type ContractDefinition<
68
71
  readonly foreignKeyDefaults?: ForeignKeyDefaults;
69
72
  readonly defaultControlPolicy?: ControlPolicy;
70
73
  readonly namespaces?: Namespaces;
71
- readonly createNamespace?: (input: SqlNamespaceTablesInput) => Namespace;
74
+ readonly createNamespace: (input: SqlNamespaceInput) => SqlNamespaceBase;
72
75
  readonly types?: Types;
73
76
  readonly models?: Models;
74
77
  readonly codecLookup?: CodecLookup;
@@ -93,7 +96,7 @@ type ContractScaffold<
93
96
  readonly foreignKeyDefaults?: ForeignKeyDefaults;
94
97
  readonly defaultControlPolicy?: ControlPolicy;
95
98
  readonly namespaces?: Namespaces;
96
- readonly createNamespace?: (input: SqlNamespaceTablesInput) => Namespace;
99
+ readonly createNamespace: (input: SqlNamespaceInput) => SqlNamespaceBase;
97
100
  readonly types?: never;
98
101
  readonly models?: never;
99
102
  readonly codecLookup?: CodecLookup;
@@ -314,7 +317,7 @@ type BoundDefinitionInput<
314
317
  readonly foreignKeyDefaults?: ForeignKeyDefaults;
315
318
  readonly defaultControlPolicy?: ControlPolicy;
316
319
  readonly namespaces?: Namespaces;
317
- readonly createNamespace?: (input: SqlNamespaceTablesInput) => Namespace;
320
+ readonly createNamespace: (input: SqlNamespaceInput) => SqlNamespaceBase;
318
321
  readonly types?: Types;
319
322
  readonly models?: Models;
320
323
  readonly codecLookup?: CodecLookup;
@@ -6,10 +6,10 @@ import type {
6
6
  import type { ForeignKeyDefaultsState } from '@prisma-next/contract-authoring';
7
7
  import type { ColumnTypeDescriptor } from '@prisma-next/framework-components/codec';
8
8
  import type { ExtensionPackRef, TargetPackRef } from '@prisma-next/framework-components/components';
9
- import type { Namespace } from '@prisma-next/framework-components/ir';
10
9
  import type {
11
10
  ReferentialAction,
12
- SqlNamespaceTablesInput,
11
+ SqlNamespaceBase,
12
+ SqlNamespaceInput,
13
13
  StorageTypeInstance,
14
14
  } from '@prisma-next/sql-contract/types';
15
15
  import type { EnumTypeHandle } from './enum-type';
@@ -175,8 +175,8 @@ export interface ContractDefinition {
175
175
  * `SqlStorage.namespaces` together with `createNamespace`.
176
176
  */
177
177
  readonly namespaces?: readonly string[];
178
- /** Target-supplied factory that materialises a `Namespace` concretion for a declared namespace coordinate. */
179
- readonly createNamespace?: (input: SqlNamespaceTablesInput) => Namespace;
178
+ /** Target-supplied factory that materialises a `SqlNamespaceBase` concretion for a declared namespace coordinate. */
179
+ readonly createNamespace: (input: SqlNamespaceInput) => SqlNamespaceBase;
180
180
  readonly models: readonly ModelNode[];
181
181
  readonly valueObjects?: readonly ValueObjectNode[];
182
182
  /**
@@ -15,8 +15,11 @@ import type {
15
15
  FamilyPackRef,
16
16
  TargetPackRef,
17
17
  } from '@prisma-next/framework-components/components';
18
- import type { Namespace } from '@prisma-next/framework-components/ir';
19
- import type { SqlNamespaceTablesInput, StorageTypeInstance } from '@prisma-next/sql-contract/types';
18
+ import type {
19
+ SqlNamespaceBase,
20
+ SqlNamespaceInput,
21
+ StorageTypeInstance,
22
+ } from '@prisma-next/sql-contract/types';
20
23
  import { blindCast } from '@prisma-next/utils/casts';
21
24
  import { ifDefined } from '@prisma-next/utils/defined';
22
25
  import type { NamedConstraintSpec } from './authoring-type-utils';
@@ -43,6 +46,7 @@ export type ScalarFieldState<
43
46
  ColumnName extends string | undefined = string | undefined,
44
47
  IdSpec extends NamedConstraintSpec | undefined = undefined,
45
48
  UniqueSpec extends NamedConstraintSpec | undefined = undefined,
49
+ Many extends boolean = false,
46
50
  > = {
47
51
  readonly kind: 'scalar';
48
52
  readonly descriptor?: (ColumnTypeDescriptor & { readonly codecId: CodecId }) | undefined;
@@ -51,6 +55,7 @@ export type ScalarFieldState<
51
55
  readonly columnName?: ColumnName | undefined;
52
56
  readonly default?: ColumnDefault | undefined;
53
57
  readonly executionDefaults?: ExecutionMutationDefaultPhases | undefined;
58
+ readonly many?: Many extends true ? true : undefined;
54
59
  } & (IdSpec extends NamedConstraintSpec ? { readonly id: IdSpec } : { readonly id?: undefined }) &
55
60
  (UniqueSpec extends NamedConstraintSpec
56
61
  ? { readonly unique: UniqueSpec }
@@ -64,6 +69,7 @@ type AnyScalarFieldState = {
64
69
  readonly columnName?: string | undefined;
65
70
  readonly default?: ColumnDefault | undefined;
66
71
  readonly executionDefaults?: ExecutionMutationDefaultPhases | undefined;
72
+ readonly many?: boolean | undefined;
67
73
  readonly id?: NamedConstraintSpec | undefined;
68
74
  readonly unique?: NamedConstraintSpec | undefined;
69
75
  };
@@ -75,7 +81,8 @@ type HasNamedConstraintId<State extends AnyScalarFieldState> =
75
81
  boolean,
76
82
  string | undefined,
77
83
  infer IdSpec,
78
- NamedConstraintSpec | undefined
84
+ NamedConstraintSpec | undefined,
85
+ boolean
79
86
  >
80
87
  ? IdSpec extends NamedConstraintSpec
81
88
  ? true
@@ -89,7 +96,8 @@ type HasNamedConstraintUnique<State extends AnyScalarFieldState> =
89
96
  boolean,
90
97
  string | undefined,
91
98
  NamedConstraintSpec | undefined,
92
- infer UniqueSpec
99
+ infer UniqueSpec,
100
+ boolean
93
101
  >
94
102
  ? UniqueSpec extends NamedConstraintSpec
95
103
  ? true
@@ -115,7 +123,8 @@ type ApplyFieldSqlSpec<
115
123
  infer Nullable,
116
124
  infer ColumnName,
117
125
  infer IdSpec,
118
- infer UniqueSpec
126
+ infer UniqueSpec,
127
+ infer Many
119
128
  >
120
129
  ? ScalarFieldState<
121
130
  CodecId,
@@ -131,9 +140,10 @@ type ApplyFieldSqlSpec<
131
140
  ? UniqueSpec extends NamedConstraintSpec
132
141
  ? NamedConstraintSpec<UniqueName>
133
142
  : UniqueSpec
134
- : UniqueSpec
143
+ : UniqueSpec,
144
+ Many
135
145
  >
136
- : never;
146
+ : AnyScalarFieldState;
137
147
 
138
148
  export type GeneratedFieldSpec = {
139
149
  readonly type: ColumnTypeDescriptor;
@@ -171,10 +181,11 @@ export class ScalarFieldBuilder<State extends AnyScalarFieldState = AnyScalarFie
171
181
  boolean,
172
182
  infer ColumnName,
173
183
  infer IdSpec,
174
- infer UniqueSpec
184
+ infer UniqueSpec,
185
+ infer Many
175
186
  >
176
- ? ScalarFieldState<CodecId, TypeRef, true, ColumnName, IdSpec, UniqueSpec>
177
- : never
187
+ ? ScalarFieldState<CodecId, TypeRef, true, ColumnName, IdSpec, UniqueSpec, Many>
188
+ : AnyScalarFieldState
178
189
  > {
179
190
  return new ScalarFieldBuilder({
180
191
  ...this.state,
@@ -185,10 +196,11 @@ export class ScalarFieldBuilder<State extends AnyScalarFieldState = AnyScalarFie
185
196
  boolean,
186
197
  infer ColumnName,
187
198
  infer IdSpec,
188
- infer UniqueSpec
199
+ infer UniqueSpec,
200
+ infer Many
189
201
  >
190
- ? ScalarFieldState<CodecId, TypeRef, true, ColumnName, IdSpec, UniqueSpec>
191
- : never);
202
+ ? ScalarFieldState<CodecId, TypeRef, true, ColumnName, IdSpec, UniqueSpec, Many>
203
+ : AnyScalarFieldState);
192
204
  }
193
205
 
194
206
  column<ColumnName extends string>(
@@ -200,10 +212,11 @@ export class ScalarFieldBuilder<State extends AnyScalarFieldState = AnyScalarFie
200
212
  infer Nullable,
201
213
  string | undefined,
202
214
  infer IdSpec,
203
- infer UniqueSpec
215
+ infer UniqueSpec,
216
+ infer Many
204
217
  >
205
- ? ScalarFieldState<CodecId, TypeRef, Nullable, ColumnName, IdSpec, UniqueSpec>
206
- : never
218
+ ? ScalarFieldState<CodecId, TypeRef, Nullable, ColumnName, IdSpec, UniqueSpec, Many>
219
+ : AnyScalarFieldState
207
220
  > {
208
221
  return new ScalarFieldBuilder({
209
222
  ...this.state,
@@ -214,10 +227,45 @@ export class ScalarFieldBuilder<State extends AnyScalarFieldState = AnyScalarFie
214
227
  infer Nullable,
215
228
  string | undefined,
216
229
  infer IdSpec,
217
- infer UniqueSpec
230
+ infer UniqueSpec,
231
+ infer Many
218
232
  >
219
- ? ScalarFieldState<CodecId, TypeRef, Nullable, ColumnName, IdSpec, UniqueSpec>
220
- : never);
233
+ ? ScalarFieldState<CodecId, TypeRef, Nullable, ColumnName, IdSpec, UniqueSpec, Many>
234
+ : AnyScalarFieldState);
235
+ }
236
+
237
+ many(): ScalarFieldBuilder<
238
+ State extends ScalarFieldState<
239
+ infer CodecId,
240
+ infer TypeRef,
241
+ infer Nullable,
242
+ infer ColumnName,
243
+ infer IdSpec,
244
+ infer UniqueSpec,
245
+ boolean
246
+ >
247
+ ? ScalarFieldState<CodecId, TypeRef, Nullable, ColumnName, IdSpec, UniqueSpec, true>
248
+ : AnyScalarFieldState
249
+ > {
250
+ return new ScalarFieldBuilder(
251
+ blindCast<
252
+ State extends ScalarFieldState<
253
+ infer CodecId,
254
+ infer TypeRef,
255
+ infer Nullable,
256
+ infer ColumnName,
257
+ infer IdSpec,
258
+ infer UniqueSpec,
259
+ boolean
260
+ >
261
+ ? ScalarFieldState<CodecId, TypeRef, Nullable, ColumnName, IdSpec, UniqueSpec, true>
262
+ : AnyScalarFieldState,
263
+ 'object spread does not narrow the generic State conditional; runtime shape is correct'
264
+ >({
265
+ ...this.state,
266
+ many: true,
267
+ }),
268
+ );
221
269
  }
222
270
 
223
271
  default(value: ColumnDefaultLiteralInputValue | ColumnDefault): ScalarFieldBuilder<State> {
@@ -243,7 +291,8 @@ export class ScalarFieldBuilder<State extends AnyScalarFieldState = AnyScalarFie
243
291
  infer Nullable,
244
292
  infer ColumnName,
245
293
  NamedConstraintSpec | undefined,
246
- infer UniqueSpec
294
+ infer UniqueSpec,
295
+ infer Many
247
296
  >
248
297
  ? ScalarFieldState<
249
298
  CodecId,
@@ -251,9 +300,10 @@ export class ScalarFieldBuilder<State extends AnyScalarFieldState = AnyScalarFie
251
300
  Nullable,
252
301
  ColumnName,
253
302
  NamedConstraintSpec<Name>,
254
- UniqueSpec
303
+ UniqueSpec,
304
+ Many
255
305
  >
256
- : never
306
+ : AnyScalarFieldState
257
307
  > {
258
308
  return new ScalarFieldBuilder({
259
309
  ...this.state,
@@ -264,7 +314,8 @@ export class ScalarFieldBuilder<State extends AnyScalarFieldState = AnyScalarFie
264
314
  infer Nullable,
265
315
  infer ColumnName,
266
316
  NamedConstraintSpec | undefined,
267
- infer UniqueSpec
317
+ infer UniqueSpec,
318
+ infer Many
268
319
  >
269
320
  ? ScalarFieldState<
270
321
  CodecId,
@@ -272,9 +323,10 @@ export class ScalarFieldBuilder<State extends AnyScalarFieldState = AnyScalarFie
272
323
  Nullable,
273
324
  ColumnName,
274
325
  NamedConstraintSpec<Name>,
275
- UniqueSpec
326
+ UniqueSpec,
327
+ Many
276
328
  >
277
- : never);
329
+ : AnyScalarFieldState);
278
330
  }
279
331
 
280
332
  unique<const Name extends string | undefined = undefined>(
@@ -286,10 +338,19 @@ export class ScalarFieldBuilder<State extends AnyScalarFieldState = AnyScalarFie
286
338
  infer Nullable,
287
339
  infer ColumnName,
288
340
  infer IdSpec,
289
- NamedConstraintSpec | undefined
341
+ NamedConstraintSpec | undefined,
342
+ infer Many
290
343
  >
291
- ? ScalarFieldState<CodecId, TypeRef, Nullable, ColumnName, IdSpec, NamedConstraintSpec<Name>>
292
- : never
344
+ ? ScalarFieldState<
345
+ CodecId,
346
+ TypeRef,
347
+ Nullable,
348
+ ColumnName,
349
+ IdSpec,
350
+ NamedConstraintSpec<Name>,
351
+ Many
352
+ >
353
+ : AnyScalarFieldState
293
354
  > {
294
355
  return new ScalarFieldBuilder({
295
356
  ...this.state,
@@ -300,10 +361,19 @@ export class ScalarFieldBuilder<State extends AnyScalarFieldState = AnyScalarFie
300
361
  infer Nullable,
301
362
  infer ColumnName,
302
363
  infer IdSpec,
303
- NamedConstraintSpec | undefined
364
+ NamedConstraintSpec | undefined,
365
+ infer Many
304
366
  >
305
- ? ScalarFieldState<CodecId, TypeRef, Nullable, ColumnName, IdSpec, NamedConstraintSpec<Name>>
306
- : never);
367
+ ? ScalarFieldState<
368
+ CodecId,
369
+ TypeRef,
370
+ Nullable,
371
+ ColumnName,
372
+ IdSpec,
373
+ NamedConstraintSpec<Name>,
374
+ Many
375
+ >
376
+ : AnyScalarFieldState);
307
377
  }
308
378
 
309
379
  sql<const Spec extends FieldSqlSpecForState<State>>(
@@ -400,11 +470,14 @@ function namedTypeField<Handle extends EnumTypeHandle>(
400
470
  function namedTypeField(typeRef: NamedStorageTypeRef): ScalarFieldBuilder {
401
471
  if (isEnumTypeHandle(typeRef)) {
402
472
  return new EnumScalarFieldBuilder(
403
- {
473
+ blindCast<
474
+ ScalarFieldState<string, typeof typeRef, false, undefined>,
475
+ 'literal object lacks explicit many; cast to the full ScalarFieldState so optional() conditional resolves Many = false'
476
+ >({
404
477
  kind: 'scalar',
405
478
  typeRef,
406
479
  nullable: false,
407
- },
480
+ }),
408
481
  typeRef,
409
482
  );
410
483
  }
@@ -1487,7 +1560,7 @@ export type ContractInput<
1487
1560
  */
1488
1561
  readonly namespaces?: readonly string[];
1489
1562
  /**
1490
- * Target-supplied factory that materialises a `Namespace` concretion
1563
+ * Target-supplied factory that materialises a `SqlNamespaceBase` concretion
1491
1564
  * for a declared namespace coordinate. The SQL family layer is
1492
1565
  * target-agnostic and cannot import concretions like
1493
1566
  * `PostgresSchema` or `SqliteUnboundDatabase`; the factory is the
@@ -1499,14 +1572,8 @@ export type ContractInput<
1499
1572
  * `StorageTable.namespaceId` referenced by a model, and the
1500
1573
  * framework `UNBOUND_NAMESPACE_ID` sentinel (always present so the
1501
1574
  * late-bound slot stays available regardless of authoring choices).
1502
- *
1503
- * When omitted, the family layer falls back to its placeholder
1504
- * `SqlUnboundNamespace` singleton for the unbound slot and rejects
1505
- * any non-unbound coordinate — single-namespace contracts authored
1506
- * before targets ship their factory stay byte-stable; multi-namespace
1507
- * contracts must pass the factory through.
1508
1575
  */
1509
- readonly createNamespace?: (input: SqlNamespaceTablesInput) => Namespace;
1576
+ readonly createNamespace: (input: SqlNamespaceInput) => SqlNamespaceBase;
1510
1577
  readonly types?: Types;
1511
1578
  readonly models?: Models;
1512
1579
  readonly codecLookup?: CodecLookup;