@prisma-next/sql-contract-ts 0.14.0-dev.6 → 0.14.0-dev.60

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.
@@ -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';
@@ -15,6 +18,7 @@ import {
15
18
  type ComposedAuthoringHelpers,
16
19
  createComposedAuthoringHelpers,
17
20
  } from './composed-authoring-helpers';
21
+ import type { PackEntitiesInput } from './contract-definition';
18
22
  import {
19
23
  type ContractInput,
20
24
  type ContractModelBuilder,
@@ -68,11 +72,12 @@ type ContractDefinition<
68
72
  readonly foreignKeyDefaults?: ForeignKeyDefaults;
69
73
  readonly defaultControlPolicy?: ControlPolicy;
70
74
  readonly namespaces?: Namespaces;
71
- readonly createNamespace?: (input: SqlNamespaceTablesInput) => Namespace;
75
+ readonly createNamespace: (input: SqlNamespaceInput) => SqlNamespaceBase;
72
76
  readonly types?: Types;
73
77
  readonly models?: Models;
74
78
  readonly codecLookup?: CodecLookup;
75
79
  readonly enums?: Enums;
80
+ readonly packEntities?: PackEntitiesInput;
76
81
  };
77
82
 
78
83
  type ContractScaffold<
@@ -93,11 +98,12 @@ type ContractScaffold<
93
98
  readonly foreignKeyDefaults?: ForeignKeyDefaults;
94
99
  readonly defaultControlPolicy?: ControlPolicy;
95
100
  readonly namespaces?: Namespaces;
96
- readonly createNamespace?: (input: SqlNamespaceTablesInput) => Namespace;
101
+ readonly createNamespace: (input: SqlNamespaceInput) => SqlNamespaceBase;
97
102
  readonly types?: never;
98
103
  readonly models?: never;
99
104
  readonly codecLookup?: CodecLookup;
100
105
  readonly enums?: Enums;
106
+ readonly packEntities?: PackEntitiesInput;
101
107
  };
102
108
 
103
109
  type ContractFactory<
@@ -111,6 +117,7 @@ type ContractFactory<
111
117
  readonly types?: Types;
112
118
  readonly models?: Models;
113
119
  readonly enums?: Enums;
120
+ readonly packEntities?: PackEntitiesInput;
114
121
  };
115
122
 
116
123
  function validateTargetPackRef(
@@ -314,11 +321,12 @@ type BoundDefinitionInput<
314
321
  readonly foreignKeyDefaults?: ForeignKeyDefaults;
315
322
  readonly defaultControlPolicy?: ControlPolicy;
316
323
  readonly namespaces?: Namespaces;
317
- readonly createNamespace?: (input: SqlNamespaceTablesInput) => Namespace;
324
+ readonly createNamespace: (input: SqlNamespaceInput) => SqlNamespaceBase;
318
325
  readonly types?: Types;
319
326
  readonly models?: Models;
320
327
  readonly codecLookup?: CodecLookup;
321
328
  readonly enums?: Record<string, EnumTypeHandle>;
329
+ readonly packEntities?: PackEntitiesInput;
322
330
  };
323
331
 
324
332
  // A bare `Record<string, EnumTypeHandle>` (no literal keys) is the widened
@@ -342,6 +350,60 @@ type WithFamilyTarget<
342
350
  T extends TargetPackRef<'sql', string>,
343
351
  > = Input & { readonly family: F; readonly target: T };
344
352
 
353
+ // Deep-merges packEntities authored on the scaffold definition with those
354
+ // returned from the factory callback — three levels deep (namespace, kind,
355
+ // name), unlike `enums`' flat shallow merge, since a factory-declared
356
+ // namespace/kind must not silently drop a scaffold-declared sibling entry
357
+ // under the same namespace or kind. A same-name key present on both sides is
358
+ // only merged when it is the identical entity instance; two *different*
359
+ // entities of the same namespace/kind/name is a collision (the emitted
360
+ // entry could reflect only one), rejected here the same way
361
+ // `mergeCollectedPackEntities` in `build-contract.ts` rejects it.
362
+ function mergePackEntityNames(
363
+ namespaceId: string,
364
+ kind: string,
365
+ a: Readonly<Record<string, unknown>> | undefined,
366
+ b: Readonly<Record<string, unknown>> | undefined,
367
+ ): Readonly<Record<string, unknown>> {
368
+ if (a !== undefined && b !== undefined) {
369
+ for (const [name, entity] of Object.entries(b)) {
370
+ const existing = a[name];
371
+ if (existing !== undefined && existing !== entity) {
372
+ throw new Error(
373
+ `defineContract: two different "${kind}" entities named "${name}" in namespace "${namespaceId}" — a factory-returned pack entity conflicts with a scaffold-declared one; pack-entity names must be unique per namespace.`,
374
+ );
375
+ }
376
+ }
377
+ }
378
+ return { ...(a ?? {}), ...(b ?? {}) };
379
+ }
380
+
381
+ function mergePackEntityKinds(
382
+ namespaceId: string,
383
+ a: Readonly<Record<string, Readonly<Record<string, unknown>>>> | undefined,
384
+ b: Readonly<Record<string, Readonly<Record<string, unknown>>>> | undefined,
385
+ ): Readonly<Record<string, Readonly<Record<string, unknown>>>> {
386
+ const kinds = new Set([...Object.keys(a ?? {}), ...Object.keys(b ?? {})]);
387
+ const result: Record<string, Readonly<Record<string, unknown>>> = {};
388
+ for (const kind of kinds) {
389
+ result[kind] = mergePackEntityNames(namespaceId, kind, a?.[kind], b?.[kind]);
390
+ }
391
+ return result;
392
+ }
393
+
394
+ function mergePackEntities(
395
+ a: PackEntitiesInput | undefined,
396
+ b: PackEntitiesInput | undefined,
397
+ ): PackEntitiesInput | undefined {
398
+ if (a === undefined && b === undefined) return undefined;
399
+ const namespaces = new Set([...Object.keys(a ?? {}), ...Object.keys(b ?? {})]);
400
+ const result: Record<string, Readonly<Record<string, Readonly<Record<string, unknown>>>>> = {};
401
+ for (const namespaceId of namespaces) {
402
+ result[namespaceId] = mergePackEntityKinds(namespaceId, a?.[namespaceId], b?.[namespaceId]);
403
+ }
404
+ return result;
405
+ }
406
+
345
407
  /**
346
408
  * Shared builder that assembles a SqlContract with pre-bound family and target.
347
409
  * Extension wrappers keep their own public overloads and delegate their impl body here;
@@ -387,6 +449,7 @@ export function buildBoundContract<
387
449
  readonly types?: Record<string, StorageTypeInstance>;
388
450
  readonly models?: Record<string, ModelLike>;
389
451
  readonly enums?: Record<string, EnumTypeHandle>;
452
+ readonly packEntities?: PackEntitiesInput;
390
453
  },
391
454
  >(
392
455
  family: F,
@@ -412,6 +475,7 @@ export function buildBoundContract(
412
475
  readonly types?: Record<string, StorageTypeInstance>;
413
476
  readonly models?: Record<string, ModelLike>;
414
477
  readonly enums?: Record<string, EnumTypeHandle>;
478
+ readonly packEntities?: PackEntitiesInput;
415
479
  })
416
480
  | undefined,
417
481
  ) {
@@ -426,11 +490,13 @@ export function buildBoundContract(
426
490
  }),
427
491
  );
428
492
  const mergedEnums = { ...(definition.enums ?? {}), ...built.enums };
493
+ const mergedPackEntities = mergePackEntities(definition.packEntities, built.packEntities);
429
494
  return buildContractFromDsl({
430
495
  ...full,
431
496
  ...ifDefined('types', built.types),
432
497
  ...ifDefined('models', built.models),
433
498
  ...ifDefined('enums', Object.keys(mergedEnums).length > 0 ? mergedEnums : undefined),
499
+ ...ifDefined('packEntities', mergedPackEntities),
434
500
  });
435
501
  }
436
502
 
@@ -6,16 +6,30 @@ 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';
16
16
 
17
17
  export type { ExecutionMutationDefaultPhases };
18
18
 
19
+ /**
20
+ * An author-declared, namespace-scoped pack entity attachment: namespace id →
21
+ * entity kind (the discriminator the target/extension pack registered its
22
+ * `AuthoringContributions.entityTypes` descriptor under, e.g. `native_enum`)
23
+ * → entity name → the lowered entity instance. Generic on purpose — neither
24
+ * the framework nor `contract-ts` names a specific entity kind here; the
25
+ * shape mirrors `SqlNamespaceInput.entries` (`entries.<kind>[name]`), just
26
+ * namespace-nested so an author can target any declared namespace (default
27
+ * or named), not only the contract's default namespace.
28
+ */
29
+ export type PackEntitiesInput = Readonly<
30
+ Record<string, Readonly<Record<string, Readonly<Record<string, unknown>>>>>
31
+ >;
32
+
19
33
  export interface FieldNode {
20
34
  readonly fieldName: string;
21
35
  readonly columnName: string;
@@ -175,8 +189,8 @@ export interface ContractDefinition {
175
189
  * `SqlStorage.namespaces` together with `createNamespace`.
176
190
  */
177
191
  readonly namespaces?: readonly string[];
178
- /** Target-supplied factory that materialises a `Namespace` concretion for a declared namespace coordinate. */
179
- readonly createNamespace?: (input: SqlNamespaceTablesInput) => Namespace;
192
+ /** Target-supplied factory that materialises a `SqlNamespaceBase` concretion for a declared namespace coordinate. */
193
+ readonly createNamespace: (input: SqlNamespaceInput) => SqlNamespaceBase;
180
194
  readonly models: readonly ModelNode[];
181
195
  readonly valueObjects?: readonly ValueObjectNode[];
182
196
  /**
@@ -185,4 +199,13 @@ export interface ContractDefinition {
185
199
  * default namespace.
186
200
  */
187
201
  readonly enums?: Record<string, EnumTypeHandle>;
202
+ /**
203
+ * Author-declared pack entities, keyed by namespace then entity kind then
204
+ * name. Each entity lowers into `storage.namespaces[ns].entries.<kind>`;
205
+ * when the registered entity-type descriptor's factory output implements
206
+ * the `SqlValueSetDerivingEntityTypeOutput.deriveValueSet` hook, the
207
+ * derived value-set also folds into `entries.valueSet`, mirroring how
208
+ * `enums` flows there.
209
+ */
210
+ readonly packEntities?: PackEntitiesInput;
188
211
  }
@@ -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
232
+ >
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
218
246
  >
219
- ? ScalarFieldState<CodecId, TypeRef, Nullable, ColumnName, IdSpec, UniqueSpec>
220
- : never);
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;
@@ -1516,6 +1583,15 @@ export type ContractInput<
1516
1583
  * default namespace. Fields reference the enum via `field.namedType(handle)`.
1517
1584
  */
1518
1585
  readonly enums?: Record<string, import('./enum-type').EnumTypeHandle>;
1586
+ /**
1587
+ * Author-declared pack entities, keyed by namespace id then entity kind
1588
+ * then name — e.g. `{ auth: { native_enum: { AalLevel: <entity> } } }`.
1589
+ * Each entity lowers into `storage.namespaces[ns].entries.<kind>`; when its
1590
+ * registered entity-type descriptor derives a value-set, that also folds
1591
+ * into `entries.valueSet`, mirroring how `enums` flows there. Generic on
1592
+ * purpose — neither this type nor the assembler names a specific kind.
1593
+ */
1594
+ readonly packEntities?: import('./contract-definition').PackEntitiesInput;
1519
1595
  };
1520
1596
 
1521
1597
  export function model<
@@ -727,6 +727,7 @@ function resolveModelNode(
727
727
  columnName,
728
728
  descriptor,
729
729
  nullable: fieldState.nullable,
730
+ ...(fieldState.many === true ? { many: true } : {}),
730
731
  ...(fieldState.default ? { default: fieldState.default } : {}),
731
732
  ...(fieldState.executionDefaults ? { executionDefaults: fieldState.executionDefaults } : {}),
732
733
  ...(enumHandle !== undefined ? { enumTypeHandle: enumHandle } : {}),
@@ -877,10 +878,13 @@ export function buildContractDefinition(definition: ContractInput): ContractDefi
877
878
  ? { storageTypes: collection.storageTypes }
878
879
  : {}),
879
880
  ...(definition.namespaces ? { namespaces: definition.namespaces } : {}),
880
- ...(definition.createNamespace ? { createNamespace: definition.createNamespace } : {}),
881
+ createNamespace: definition.createNamespace,
881
882
  ...(definition.enums && Object.keys(definition.enums).length > 0
882
883
  ? { enums: definition.enums }
883
884
  : {}),
885
+ ...(definition.packEntities && Object.keys(definition.packEntities).length > 0
886
+ ? { packEntities: definition.packEntities }
887
+ : {}),
884
888
  models,
885
889
  };
886
890
  }