@prisma-next/sql-contract 0.13.0-dev.4 → 0.13.0-dev.40

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.
Files changed (48) hide show
  1. package/dist/{types-DqhaAjCH.mjs → entity-kinds-Cl36zL5j.mjs} +121 -205
  2. package/dist/entity-kinds-Cl36zL5j.mjs.map +1 -0
  3. package/dist/entity-kinds.d.mts +18 -0
  4. package/dist/entity-kinds.d.mts.map +1 -0
  5. package/dist/entity-kinds.mjs +2 -0
  6. package/dist/factories.d.mts +2 -2
  7. package/dist/factories.mjs +2 -1
  8. package/dist/factories.mjs.map +1 -1
  9. package/dist/index-type-validation.d.mts +1 -1
  10. package/dist/index-type-validation.mjs +9 -12
  11. package/dist/index-type-validation.mjs.map +1 -1
  12. package/dist/resolve-storage-table.d.mts +2 -1
  13. package/dist/resolve-storage-table.d.mts.map +1 -1
  14. package/dist/resolve-storage-table.mjs +11 -8
  15. package/dist/resolve-storage-table.mjs.map +1 -1
  16. package/dist/sql-storage-Dga0jwP2.d.mts +128 -0
  17. package/dist/sql-storage-Dga0jwP2.d.mts.map +1 -0
  18. package/dist/{sql-storage-CXf9xjAL.d.mts → storage-value-set-WnYsIFM8.d.mts} +8 -120
  19. package/dist/storage-value-set-WnYsIFM8.d.mts.map +1 -0
  20. package/dist/types-B-eiQXff.mjs +191 -0
  21. package/dist/types-B-eiQXff.mjs.map +1 -0
  22. package/dist/{types-DEnWD3xB.d.mts → types-B1N8w0I2.d.mts} +11 -62
  23. package/dist/types-B1N8w0I2.d.mts.map +1 -0
  24. package/dist/types.d.mts +4 -3
  25. package/dist/types.mjs +3 -2
  26. package/dist/validators.d.mts +75 -40
  27. package/dist/validators.d.mts.map +1 -1
  28. package/dist/validators.mjs +53 -184
  29. package/dist/validators.mjs.map +1 -1
  30. package/package.json +8 -7
  31. package/src/entity-kinds.ts +45 -0
  32. package/src/exports/entity-kinds.ts +5 -0
  33. package/src/exports/types.ts +2 -4
  34. package/src/index-type-validation.ts +2 -3
  35. package/src/ir/build-sql-namespace.ts +39 -32
  36. package/src/ir/sql-node.ts +2 -2
  37. package/src/ir/sql-storage.ts +22 -24
  38. package/src/ir/sql-unbound-namespace.ts +15 -3
  39. package/src/ir/storage-entry-schemas.ts +128 -0
  40. package/src/ir/storage-type-instance.ts +3 -3
  41. package/src/ir/storage-value-set.ts +6 -5
  42. package/src/resolve-storage-table.ts +12 -17
  43. package/src/types.ts +10 -10
  44. package/src/validators.ts +83 -225
  45. package/dist/sql-storage-CXf9xjAL.d.mts.map +0 -1
  46. package/dist/types-DEnWD3xB.d.mts.map +0 -1
  47. package/dist/types-DqhaAjCH.mjs.map +0 -1
  48. package/src/ir/postgres-enum-storage-entry.ts +0 -57
@@ -0,0 +1,45 @@
1
+ import type {
2
+ AnyEntityKindDescriptor,
3
+ EntityKindDescriptor,
4
+ } from '@prisma-next/framework-components/ir';
5
+ import { StorageTableSchema, StorageValueSetSchema } from './ir/storage-entry-schemas';
6
+ import { StorageTable, type StorageTableInput } from './ir/storage-table';
7
+ import { StorageValueSet, type StorageValueSetInput } from './ir/storage-value-set';
8
+
9
+ export const tableEntityKind: EntityKindDescriptor<StorageTableInput, StorageTable> = {
10
+ kind: 'table',
11
+ schema: StorageTableSchema,
12
+ construct: (input) => new StorageTable(input),
13
+ };
14
+
15
+ export const valueSetEntityKind: EntityKindDescriptor<StorageValueSetInput, StorageValueSet> = {
16
+ kind: 'valueSet',
17
+ schema: StorageValueSetSchema,
18
+ construct: (input) => new StorageValueSet(input),
19
+ };
20
+
21
+ /**
22
+ * Assembles the `kind → descriptor` registry for SQL namespaces: the built-in
23
+ * `table` and `valueSet` kinds plus any target `packKinds`. This builds the
24
+ * lookup table — it does not touch contract data. `hydrateNamespaceEntities`
25
+ * later consumes this registry to turn a namespace's raw entries into IR
26
+ * instances, and `createSqlContractSchema` derives validation from the same
27
+ * registry. Throws on a duplicate kind.
28
+ */
29
+ export function composeSqlEntityKinds(
30
+ packKinds: readonly AnyEntityKindDescriptor[] = [],
31
+ ): ReadonlyMap<string, AnyEntityKindDescriptor> {
32
+ const kinds = new Map<string, AnyEntityKindDescriptor>([
33
+ ['table', tableEntityKind],
34
+ ['valueSet', valueSetEntityKind],
35
+ ]);
36
+ for (const descriptor of packKinds) {
37
+ if (kinds.has(descriptor.kind)) {
38
+ throw new Error(
39
+ `composeSqlEntityKinds: duplicate entity kind "${descriptor.kind}" — each kind may be registered only once`,
40
+ );
41
+ }
42
+ kinds.set(descriptor.kind, descriptor);
43
+ }
44
+ return kinds;
45
+ }
@@ -0,0 +1,5 @@
1
+ export {
2
+ composeSqlEntityKinds,
3
+ tableEntityKind,
4
+ valueSetEntityKind,
5
+ } from '../entity-kinds';
@@ -13,7 +13,6 @@ export type {
13
13
  ForeignKeyOptions,
14
14
  ForeignKeyReferenceInput,
15
15
  IndexInput,
16
- PostgresEnumStorageEntry,
17
16
  PrimaryKeyInput,
18
17
  QueryOperationReturn,
19
18
  QueryOperationSelfSpec,
@@ -25,6 +24,8 @@ export type {
25
24
  SqlControlDriverInstance,
26
25
  SqlModelFieldStorage,
27
26
  SqlModelStorage,
27
+ SqlNamespace,
28
+ SqlNamespaceEntries,
28
29
  SqlNamespaceTablesInput,
29
30
  SqlQueryOperationTypes,
30
31
  SqlStorageInput,
@@ -49,9 +50,7 @@ export {
49
50
  ForeignKey,
50
51
  ForeignKeyReference,
51
52
  Index,
52
- isPostgresEnumStorageEntry,
53
53
  isStorageTypeInstance,
54
- POSTGRES_ENUM_KIND,
55
54
  PrimaryKey,
56
55
  SqlNode,
57
56
  SqlStorage,
@@ -59,7 +58,6 @@ export {
59
58
  StorageColumn,
60
59
  StorageTable,
61
60
  StorageValueSet,
62
- storageTableAt,
63
61
  toStorageTypeInstance,
64
62
  UniqueConstraint,
65
63
  } from '../types';
@@ -2,15 +2,14 @@ import { ContractValidationError } from '@prisma-next/contract/contract-validati
2
2
  import type { Contract } from '@prisma-next/contract/types';
3
3
  import { type } from 'arktype';
4
4
  import type { IndexTypeRegistry } from './index-types';
5
- import type { SqlStorage, StorageTable } from './types';
5
+ import type { SqlStorage } from './types';
6
6
 
7
7
  export function validateIndexTypes(
8
8
  contract: Contract<SqlStorage>,
9
9
  indexTypeRegistry: IndexTypeRegistry,
10
10
  ): void {
11
11
  for (const [namespaceId, ns] of Object.entries(contract.storage.namespaces)) {
12
- for (const [tableName, rawTable] of Object.entries(ns.entries.table)) {
13
- const table = rawTable as StorageTable;
12
+ for (const [tableName, table] of Object.entries(ns.entries.table ?? {})) {
14
13
  for (const index of table.indexes) {
15
14
  if (index.type === undefined && index.options !== undefined) {
16
15
  throw new ContractValidationError(
@@ -1,14 +1,16 @@
1
1
  import {
2
2
  freezeNode,
3
+ hydrateNamespaceEntities,
3
4
  type Namespace,
4
5
  NamespaceBase,
5
6
  UNBOUND_NAMESPACE_ID,
6
7
  } from '@prisma-next/framework-components/ir';
7
- import { blindCast, castAs } from '@prisma-next/utils/casts';
8
- import type { SqlNamespace, SqlNamespaceTablesInput } from './sql-storage';
8
+ import { blindCast } from '@prisma-next/utils/casts';
9
+ import { composeSqlEntityKinds } from '../entity-kinds';
10
+ import type { SqlNamespace, SqlNamespaceEntries, SqlNamespaceTablesInput } from './sql-storage';
9
11
  import { SqlUnboundNamespace } from './sql-unbound-namespace';
10
- import { StorageTable } from './storage-table';
11
- import { StorageValueSet } from './storage-value-set';
12
+ import type { StorageTable } from './storage-table';
13
+ import type { StorageValueSet } from './storage-value-set';
12
14
 
13
15
  const SQL_NAMESPACE_KIND = 'sql-namespace' as const;
14
16
 
@@ -27,39 +29,39 @@ class SqlBoundNamespace extends NamespaceBase {
27
29
  declare readonly kind: string;
28
30
 
29
31
  readonly id: string;
30
- readonly entries: Readonly<{
31
- readonly table: Readonly<Record<string, StorageTable>>;
32
- readonly valueSet?: Readonly<Record<string, StorageValueSet>>;
33
- }>;
32
+ readonly entries: SqlNamespaceEntries;
34
33
 
35
34
  static fromTablesInput(input: SqlNamespaceTablesInput): SqlNamespace {
36
- const tableCount = Object.keys(input.entries.table).length;
37
- const hasValueSets =
38
- input.entries.valueSet !== undefined && Object.keys(input.entries.valueSet).length > 0;
39
- if (input.id === UNBOUND_NAMESPACE_ID && tableCount === 0 && !hasValueSets) {
40
- return castAs<SqlNamespace>(SqlUnboundNamespace.instance);
35
+ const tableKind = input.entries['table'];
36
+ const tableCount = tableKind !== undefined ? Object.keys(tableKind).length : 0;
37
+ const valueSetKind = input.entries['valueSet'];
38
+ const hasValueSets = valueSetKind !== undefined && Object.keys(valueSetKind).length > 0;
39
+ const hasUnknownKinds = Object.keys(input.entries).some(
40
+ (kind) => kind !== 'table' && kind !== 'valueSet',
41
+ );
42
+ if (
43
+ input.id === UNBOUND_NAMESPACE_ID &&
44
+ tableCount === 0 &&
45
+ !hasValueSets &&
46
+ !hasUnknownKinds
47
+ ) {
48
+ return SqlUnboundNamespace.instance;
41
49
  }
42
- return castAs<SqlNamespace>(new SqlBoundNamespace(input));
50
+ return new SqlBoundNamespace(input);
43
51
  }
44
52
 
45
53
  private constructor(input: SqlNamespaceTablesInput) {
46
54
  super();
47
55
  this.id = input.id;
48
- const table = Object.freeze(
49
- Object.fromEntries(
50
- Object.entries(input.entries.table).map(([k, v]) => [k, new StorageTable(v)]),
51
- ),
56
+
57
+ const dispatched = hydrateNamespaceEntities(input.entries, composeSqlEntityKinds(), 'carry');
58
+
59
+ this.entries = Object.freeze(
60
+ blindCast<
61
+ SqlNamespaceEntries,
62
+ 'composeSqlEntityKinds() supplies table→StorageTable and valueSet→StorageValueSet descriptors, so this open-dict result holds exactly the typed members SqlNamespaceEntries declares; the descriptor Map erases those per-kind Node types from the return.'
63
+ >(dispatched),
52
64
  );
53
- if (input.entries.valueSet !== undefined) {
54
- const valueSet = Object.freeze(
55
- Object.fromEntries(
56
- Object.entries(input.entries.valueSet).map(([k, v]) => [k, new StorageValueSet(v)]),
57
- ),
58
- );
59
- this.entries = Object.freeze({ table, valueSet });
60
- } else {
61
- this.entries = Object.freeze({ table });
62
- }
63
65
  Object.defineProperty(this, 'kind', {
64
66
  value: SQL_NAMESPACE_KIND,
65
67
  writable: false,
@@ -69,6 +71,14 @@ class SqlBoundNamespace extends NamespaceBase {
69
71
  freezeNode(this);
70
72
  }
71
73
 
74
+ get table(): Readonly<Record<string, StorageTable>> {
75
+ return this.entries.table ?? Object.freeze({});
76
+ }
77
+
78
+ get valueSet(): Readonly<Record<string, StorageValueSet>> | undefined {
79
+ return this.entries.valueSet;
80
+ }
81
+
72
82
  qualifyTable(tableName: string): string {
73
83
  if (this.id === UNBOUND_NAMESPACE_ID) {
74
84
  return `"${tableName}"`;
@@ -88,10 +98,7 @@ export function buildSqlNamespaceMap(
88
98
  Object.entries(namespaces).map(([nsKey, ns]) => [
89
99
  nsKey,
90
100
  isMaterializedSqlNamespace(ns)
91
- ? blindCast<
92
- SqlNamespace,
93
- 'a materialised SQL-family namespace entry in a namespace map is a SqlNamespace'
94
- >(ns)
101
+ ? ns
95
102
  : SqlBoundNamespace.fromTablesInput(
96
103
  blindCast<
97
104
  SqlNamespaceTablesInput,
@@ -41,8 +41,8 @@ export abstract class SqlNode extends IRNodeBase {
41
41
  value: 'sql',
42
42
  writable: false,
43
43
  enumerable: false,
44
- // configurable so per-leaf subclasses (e.g. PostgresEnumType in
45
- // target-postgres) can override `kind` with their narrower
44
+ // configurable so per-leaf subclasses (e.g. StorageValueSet)
45
+ // can override `kind` with their narrower
46
46
  // enumerable literal via a class-field initializer. SqlNode
47
47
  // itself never needs to mutate the property again, so
48
48
  // configurability has no surface impact at this layer.
@@ -1,14 +1,14 @@
1
1
  import type { StorageHashBase } from '@prisma-next/contract/types';
2
2
  import { freezeNode, type Namespace, type Storage } from '@prisma-next/framework-components/ir';
3
3
  import { SqlNode } from './sql-node';
4
- import type { StorageTable, StorageTableInput } from './storage-table';
4
+ import type { StorageTable } from './storage-table';
5
5
  import {
6
6
  isStorageTypeInstance,
7
7
  type StorageTypeInstance,
8
8
  type StorageTypeInstanceInput,
9
9
  toStorageTypeInstance,
10
10
  } from './storage-type-instance';
11
- import type { StorageValueSet, StorageValueSetInput } from './storage-value-set';
11
+ import type { StorageValueSet } from './storage-value-set';
12
12
 
13
13
  /**
14
14
  * Polymorphic value type for document-scoped `SqlStorage.types` entries
@@ -21,10 +21,7 @@ export type SqlStorageTypeEntry = StorageTypeInstance | StorageTypeInstanceInput
21
21
 
22
22
  export interface SqlNamespaceTablesInput {
23
23
  readonly id: string;
24
- readonly entries: {
25
- readonly table: Record<string, StorageTable | StorageTableInput>;
26
- readonly valueSet?: Record<string, StorageValueSet | StorageValueSetInput>;
27
- };
24
+ readonly entries: Readonly<Record<string, Readonly<Record<string, unknown>>>>;
28
25
  }
29
26
 
30
27
  export interface SqlStorageInput<THash extends string = string> {
@@ -54,17 +51,26 @@ export interface SqlStorageInput<THash extends string = string> {
54
51
  * the per-target serializer's responsibility (so the family base does
55
52
  * not import target-specific subclasses).
56
53
  */
57
- // SQL concretions store `StorageTable` values under `entries.table`.
58
- // Mongo namespaces carry `entries.collection` instead. The wider
59
- // `Record<string, object>` on `StorageTable` is only there so emitted
60
- // `contract.d.ts` table literals (which lack the runtime `kind`
61
- // discriminator on `StorageTable`) structurally satisfy the slot without
62
- // a class-instance check.
54
+ /**
55
+ * The typed `entries` shape for SQL family namespaces. The open dictionary
56
+ * is intersected with optional known-kind maps so that `ns.entries.table`
57
+ * and `ns.entries.valueSet` resolve without a cast, while unknown pack-
58
+ * contributed kinds remain valid (the `Record` part allows any string key).
59
+ */
60
+ export type SqlNamespaceEntries = Readonly<Record<string, Readonly<Record<string, unknown>>>> & {
61
+ readonly table?: Readonly<Record<string, StorageTable>>;
62
+ readonly valueSet?: Readonly<Record<string, StorageValueSet>>;
63
+ };
64
+
65
+ /**
66
+ * SQL family namespace. `entries` is the open ADR 224 dictionary —
67
+ * `entries[entityKind][entityName]` addresses any entity. Emitted
68
+ * contract literals satisfy this structurally (no prototype getters
69
+ * needed). For typed access to specific kinds, use the class getters
70
+ * on the concretion or `ns.entries.table` / `ns.entries.valueSet` directly.
71
+ */
63
72
  export type SqlNamespace = Namespace & {
64
- readonly entries: Readonly<{
65
- readonly table: Readonly<Record<string, StorageTable>>;
66
- readonly valueSet?: Readonly<Record<string, StorageValueSet>>;
67
- }>;
73
+ readonly entries: SqlNamespaceEntries;
68
74
  /**
69
75
  * Render a dialect-qualified table reference for runtime SQL emission.
70
76
  * Present on materialised target concretions (`PostgresSchema`,
@@ -94,14 +100,6 @@ export class SqlStorage<THash extends string = string> extends SqlNode implement
94
100
  }
95
101
  }
96
102
 
97
- export function storageTableAt(
98
- storage: SqlStorage,
99
- namespaceId: string,
100
- tableName: string,
101
- ): StorageTable | undefined {
102
- return storage.namespaces[namespaceId]?.entries.table[tableName];
103
- }
104
-
105
103
  /**
106
104
  * Strict polymorphic-slot dispatch for `SqlStorage.types` entries.
107
105
  * Every entry must carry a `kind: 'codec-instance'` discriminator or
@@ -3,6 +3,8 @@ import {
3
3
  NamespaceBase,
4
4
  UNBOUND_NAMESPACE_ID,
5
5
  } from '@prisma-next/framework-components/ir';
6
+ import { blindCast } from '@prisma-next/utils/casts';
7
+ import type { SqlNamespaceEntries } from './sql-storage';
6
8
  import type { StorageTable } from './storage-table';
7
9
 
8
10
  /**
@@ -40,9 +42,12 @@ export class SqlUnboundNamespace extends NamespaceBase {
40
42
  static readonly instance: SqlUnboundNamespace = new SqlUnboundNamespace();
41
43
 
42
44
  readonly id = UNBOUND_NAMESPACE_ID;
43
- readonly entries: Readonly<{
44
- readonly table: Readonly<Record<string, StorageTable>>;
45
- }> = Object.freeze({ table: Object.freeze({}) });
45
+ readonly entries: SqlNamespaceEntries = Object.freeze({
46
+ table: blindCast<
47
+ Readonly<Record<string, StorageTable>>,
48
+ 'empty frozen map is a valid Readonly<Record<string, StorageTable>>'
49
+ >(Object.freeze({})),
50
+ });
46
51
  declare readonly kind: string;
47
52
 
48
53
  private constructor() {
@@ -56,6 +61,13 @@ export class SqlUnboundNamespace extends NamespaceBase {
56
61
  freezeNode(this);
57
62
  }
58
63
 
64
+ get table(): Readonly<Record<string, StorageTable>> {
65
+ return blindCast<
66
+ Readonly<Record<string, StorageTable>>,
67
+ 'entries[table] holds only StorageTable by construction'
68
+ >(this.entries['table']);
69
+ }
70
+
59
71
  qualifyTable(tableName: string): string {
60
72
  return `"${tableName}"`;
61
73
  }
@@ -0,0 +1,128 @@
1
+ import { type Type, type } from 'arktype';
2
+ import type { ForeignKeyInput, ReferentialAction } from './foreign-key';
3
+ import type { ForeignKeyReferenceInput } from './foreign-key-reference';
4
+ import type { PrimaryKeyInput } from './primary-key';
5
+ import type { UniqueConstraintInput } from './unique-constraint';
6
+
7
+ type ColumnDefaultLiteral = {
8
+ readonly kind: 'literal';
9
+ readonly value: string | number | boolean | Record<string, unknown> | unknown[] | null;
10
+ };
11
+ type ColumnDefaultFunction = { readonly kind: 'function'; readonly expression: string };
12
+
13
+ const literalKindSchema = type("'literal'");
14
+ const functionKindSchema = type("'function'");
15
+ const ControlPolicySchema = type("'managed' | 'tolerated' | 'external' | 'observed'");
16
+
17
+ export const ColumnDefaultLiteralSchema = type.declare<ColumnDefaultLiteral>().type({
18
+ kind: literalKindSchema,
19
+ value: 'string | number | boolean | null | unknown[] | Record<string, unknown>',
20
+ });
21
+
22
+ export const ColumnDefaultFunctionSchema = type.declare<ColumnDefaultFunction>().type({
23
+ kind: functionKindSchema,
24
+ expression: 'string',
25
+ });
26
+
27
+ export const ColumnDefaultSchema = ColumnDefaultLiteralSchema.or(ColumnDefaultFunctionSchema);
28
+
29
+ const StorageValueSetRefSchema = type({
30
+ plane: "'storage'",
31
+ namespaceId: 'string',
32
+ entityKind: "'valueSet'",
33
+ entityName: 'string',
34
+ 'spaceId?': 'string',
35
+ });
36
+
37
+ const StorageColumnSchema = type({
38
+ '+': 'reject',
39
+ nativeType: 'string',
40
+ codecId: 'string',
41
+ nullable: 'boolean',
42
+ 'typeParams?': 'Record<string, unknown>',
43
+ 'typeRef?': 'string',
44
+ 'default?': ColumnDefaultSchema,
45
+ 'control?': ControlPolicySchema,
46
+ 'valueSet?': StorageValueSetRefSchema,
47
+ }).narrow((col, ctx) => {
48
+ if (col.typeParams !== undefined && col.typeRef !== undefined) {
49
+ return ctx.mustBe('a column with either typeParams or typeRef, not both');
50
+ }
51
+ return true;
52
+ });
53
+
54
+ /**
55
+ * Storage value-set entry under `storage.namespaces[id].entries.valueSet[name]`.
56
+ * Carries a `kind: 'valueSet'` discriminator (enumerable, survives JSON) and an
57
+ * ordered `values` array of codec-encoded permitted values.
58
+ */
59
+ export const StorageValueSetSchema = type({
60
+ kind: "'valueSet'",
61
+ values: type('string | number | boolean | null | unknown[] | Record<string, unknown>')
62
+ .array()
63
+ .readonly(),
64
+ });
65
+
66
+ const PrimaryKeySchema = type.declare<PrimaryKeyInput>().type({
67
+ columns: type.string.array().readonly(),
68
+ 'name?': 'string',
69
+ });
70
+
71
+ const UniqueConstraintSchema = type.declare<UniqueConstraintInput>().type({
72
+ columns: type.string.array().readonly(),
73
+ 'name?': 'string',
74
+ });
75
+
76
+ export const IndexSchema = type({
77
+ columns: type.string.array().readonly(),
78
+ 'name?': 'string',
79
+ 'type?': 'string',
80
+ 'options?': 'Record<string, unknown>',
81
+ });
82
+
83
+ export const ForeignKeyReferenceSchema = type({
84
+ '+': 'reject',
85
+ namespaceId: 'string',
86
+ tableName: 'string',
87
+ columns: type.string.array().readonly(),
88
+ 'spaceId?': 'string',
89
+ }) satisfies Type<ForeignKeyReferenceInput>;
90
+
91
+ export const ForeignKeySourceSchema = type({
92
+ '+': 'reject',
93
+ namespaceId: 'string',
94
+ tableName: 'string',
95
+ columns: type.string.array().readonly(),
96
+ }) satisfies Type<ForeignKeyReferenceInput>;
97
+
98
+ export const ReferentialActionSchema = type
99
+ .declare<ReferentialAction>()
100
+ .type("'noAction' | 'restrict' | 'cascade' | 'setNull' | 'setDefault'");
101
+
102
+ export const ForeignKeySchema = type.declare<ForeignKeyInput>().type({
103
+ source: ForeignKeySourceSchema,
104
+ target: ForeignKeyReferenceSchema,
105
+ 'name?': 'string',
106
+ 'onDelete?': ReferentialActionSchema,
107
+ 'onUpdate?': ReferentialActionSchema,
108
+ constraint: 'boolean',
109
+ index: 'boolean',
110
+ });
111
+
112
+ export const CheckConstraintSchema = type({
113
+ '+': 'reject',
114
+ name: 'string',
115
+ column: 'string',
116
+ valueSet: StorageValueSetRefSchema,
117
+ });
118
+
119
+ export const StorageTableSchema = type({
120
+ '+': 'reject',
121
+ columns: type({ '[string]': StorageColumnSchema }),
122
+ 'primaryKey?': PrimaryKeySchema,
123
+ uniques: UniqueConstraintSchema.array().readonly(),
124
+ indexes: IndexSchema.array().readonly(),
125
+ foreignKeys: ForeignKeySchema.array().readonly(),
126
+ 'control?': ControlPolicySchema,
127
+ 'checks?': CheckConstraintSchema.array().readonly(),
128
+ });
@@ -14,8 +14,8 @@ export const CODEC_INSTANCE_KIND = 'codec-instance' as const;
14
14
  * in `SqlStorage.types`. These are plain object literals — there is no
15
15
  * runtime IR class, the JSON envelope round-trips through the slot
16
16
  * unchanged. The `kind: 'codec-instance'` discriminator is the dispatch
17
- * key that distinguishes codec-typed entries from class-instance entries
18
- * (e.g. `PostgresEnumType`) sharing the polymorphic slot.
17
+ * key that distinguishes codec-typed entries from any class-instance
18
+ * kinds a target pack contributes to the polymorphic slot.
19
19
  */
20
20
  export interface StorageTypeInstance extends StorageType {
21
21
  readonly kind: typeof CODEC_INSTANCE_KIND;
@@ -54,7 +54,7 @@ export function toStorageTypeInstance(input: StorageTypeInstanceInput): StorageT
54
54
  /**
55
55
  * Type-guard for codec-typed entries on the polymorphic
56
56
  * `SqlStorage.types` slot. Distinguishes `StorageTypeInstance` from
57
- * class-instance kinds (e.g. `PostgresEnumType`).
57
+ * any class-instance kinds a target pack contributes.
58
58
  */
59
59
  export function isStorageTypeInstance(value: unknown): value is StorageTypeInstance {
60
60
  if (typeof value !== 'object' || value === null) return false;
@@ -1,3 +1,4 @@
1
+ import type { JsonValue } from '@prisma-next/contract/types';
1
2
  import { freezeNode } from '@prisma-next/framework-components/ir';
2
3
  import { SqlNode } from './sql-node';
3
4
 
@@ -7,9 +8,9 @@ import { SqlNode } from './sql-node';
7
8
  * walker can hand a validated literal straight to `new`.
8
9
  */
9
10
  export interface StorageValueSetInput {
10
- readonly kind: 'value-set';
11
+ readonly kind: 'valueSet';
11
12
  /** Ordered permitted values, codec-encoded. Declaration order is preserved. */
12
- readonly values: readonly string[];
13
+ readonly values: readonly JsonValue[];
13
14
  }
14
15
 
15
16
  /**
@@ -21,7 +22,7 @@ export interface StorageValueSetInput {
21
22
  * column that references it already holds the codec; the value-set holds
22
23
  * only the permitted values.
23
24
  *
24
- * The node's `kind` is enumerable (`'value-set'`) so the JSON envelope
25
+ * The node's `kind` is enumerable (`'valueSet'`) so the JSON envelope
25
26
  * carries the discriminator and the serializer hydration walker can
26
27
  * dispatch on it. This follows the per-leaf enumerable-kind convention
27
28
  * established in the SQL-node comment (future polymorphic dispatch on
@@ -31,8 +32,8 @@ export interface StorageValueSetInput {
31
32
  * the parent namespace's `valueSet: Record<string, StorageValueSet>` map.
32
33
  */
33
34
  export class StorageValueSet extends SqlNode {
34
- override readonly kind = 'value-set' as const;
35
- readonly values: readonly string[];
35
+ override readonly kind = 'valueSet' as const;
36
+ readonly values: readonly JsonValue[];
36
37
 
37
38
  constructor(input: StorageValueSetInput) {
38
39
  super();
@@ -1,4 +1,5 @@
1
- import type { SqlNamespace, SqlStorage } from './ir/sql-storage';
1
+ import { entityAt } from '@prisma-next/framework-components/ir';
2
+ import type { SqlStorage } from './ir/sql-storage';
2
3
  import type { StorageTable } from './ir/storage-table';
3
4
 
4
5
  export interface ResolvedStorageTable {
@@ -6,20 +7,6 @@ export interface ResolvedStorageTable {
6
7
  readonly table: StorageTable;
7
8
  }
8
9
 
9
- function tableInNamespace(
10
- namespace: SqlNamespace | undefined,
11
- tableName: string,
12
- ): StorageTable | undefined {
13
- if (namespace === undefined) {
14
- return undefined;
15
- }
16
- const tables = namespace.entries.table;
17
- if (!Object.hasOwn(tables, tableName)) {
18
- return undefined;
19
- }
20
- return tables[tableName];
21
- }
22
-
23
10
  /**
24
11
  * Resolve a bare storage table name to its namespace coordinate and table IR.
25
12
  *
@@ -35,13 +22,21 @@ export function resolveStorageTable(
35
22
  namespaceId?: string,
36
23
  ): ResolvedStorageTable | undefined {
37
24
  if (namespaceId !== undefined) {
38
- const table = tableInNamespace(storage.namespaces[namespaceId], tableName);
25
+ const table = entityAt<StorageTable>(storage, {
26
+ namespaceId,
27
+ entityKind: 'table',
28
+ entityName: tableName,
29
+ });
39
30
  return table === undefined ? undefined : { namespaceId, table };
40
31
  }
41
32
 
42
33
  const matches: ResolvedStorageTable[] = [];
43
34
  for (const candidateNamespaceId of Object.keys(storage.namespaces)) {
44
- const table = tableInNamespace(storage.namespaces[candidateNamespaceId], tableName);
35
+ const table = entityAt<StorageTable>(storage, {
36
+ namespaceId: candidateNamespaceId,
37
+ entityKind: 'table',
38
+ entityName: tableName,
39
+ });
45
40
  if (table !== undefined) {
46
41
  matches.push({ namespaceId: candidateNamespaceId, table });
47
42
  }
package/src/types.ts CHANGED
@@ -21,20 +21,16 @@ export {
21
21
  ForeignKeyReference,
22
22
  type ForeignKeyReferenceInput,
23
23
  } from './ir/foreign-key-reference';
24
- export {
25
- isPostgresEnumStorageEntry,
26
- POSTGRES_ENUM_KIND,
27
- type PostgresEnumStorageEntry,
28
- } from './ir/postgres-enum-storage-entry';
29
24
  export { PrimaryKey, type PrimaryKeyInput } from './ir/primary-key';
30
25
  export { Index, type IndexInput } from './ir/sql-index';
31
26
  export { SqlNode } from './ir/sql-node';
32
27
  export {
28
+ type SqlNamespace,
29
+ type SqlNamespaceEntries,
33
30
  type SqlNamespaceTablesInput,
34
31
  SqlStorage,
35
32
  type SqlStorageInput,
36
33
  type SqlStorageTypeEntry,
37
- storageTableAt,
38
34
  } from './ir/sql-storage';
39
35
  export { SqlUnboundNamespace } from './ir/sql-unbound-namespace';
40
36
  export { StorageColumn, type StorageColumnInput } from './ir/storage-column';
@@ -83,11 +79,15 @@ export function applyFkDefaults(
83
79
  };
84
80
  }
85
81
 
82
+ // Field-type maps nested by namespace coordinate: `[namespaceId][model][field]`.
83
+ // Shared by the output and input field-type maps and their extractors.
84
+ export type NamespacedFieldTypeMap = Record<string, Record<string, Record<string, unknown>>>;
85
+
86
86
  export type TypeMaps<
87
87
  TCodecTypes extends Record<string, { output: unknown }> = Record<string, never>,
88
88
  TQueryOperationTypes extends Record<string, unknown> = Record<string, never>,
89
- TFieldOutputTypes extends Record<string, Record<string, unknown>> = Record<string, never>,
90
- TFieldInputTypes extends Record<string, Record<string, unknown>> = Record<string, never>,
89
+ TFieldOutputTypes extends NamespacedFieldTypeMap = Record<string, never>,
90
+ TFieldInputTypes extends NamespacedFieldTypeMap = Record<string, never>,
91
91
  > = {
92
92
  readonly codecTypes: TCodecTypes;
93
93
  readonly queryOperationTypes: TQueryOperationTypes;
@@ -159,7 +159,7 @@ export type ExtractTypeMapsFromContract<T> = TypeMapsPhantomKey extends keyof T
159
159
  export type FieldOutputTypesOf<T> = [T] extends [never]
160
160
  ? Record<string, never>
161
161
  : T extends { readonly fieldOutputTypes: infer F }
162
- ? F extends Record<string, Record<string, unknown>>
162
+ ? F extends NamespacedFieldTypeMap
163
163
  ? F
164
164
  : Record<string, never>
165
165
  : Record<string, never>;
@@ -167,7 +167,7 @@ export type FieldOutputTypesOf<T> = [T] extends [never]
167
167
  export type FieldInputTypesOf<T> = [T] extends [never]
168
168
  ? Record<string, never>
169
169
  : T extends { readonly fieldInputTypes: infer F }
170
- ? F extends Record<string, Record<string, unknown>>
170
+ ? F extends NamespacedFieldTypeMap
171
171
  ? F
172
172
  : Record<string, never>
173
173
  : Record<string, never>;