@prisma-next/sql-contract 0.9.0 → 0.10.0
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/dist/factories.d.mts +3 -2
- package/dist/factories.d.mts.map +1 -1
- package/dist/factories.mjs +13 -6
- package/dist/factories.mjs.map +1 -1
- package/dist/index-type-validation.d.mts +1 -1
- package/dist/index-type-validation.mjs +11 -8
- package/dist/index-type-validation.mjs.map +1 -1
- package/dist/{types-B0lbr9cb.d.mts → types-FVBrwvCz.d.mts} +64 -51
- package/dist/types-FVBrwvCz.d.mts.map +1 -0
- package/dist/{types-iqFGDcJp.mjs → types-L8p7B1dP.mjs} +76 -65
- package/dist/types-L8p7B1dP.mjs.map +1 -0
- package/dist/types.d.mts +2 -2
- package/dist/types.mjs +2 -2
- package/dist/validators.d.mts +3 -3
- package/dist/validators.d.mts.map +1 -1
- package/dist/validators.mjs +82 -73
- package/dist/validators.mjs.map +1 -1
- package/package.json +6 -6
- package/src/exports/types.ts +4 -3
- package/src/factories.ts +9 -6
- package/src/index-type-validation.ts +27 -24
- package/src/ir/foreign-key-reference.ts +29 -0
- package/src/ir/foreign-key.ts +21 -14
- package/src/ir/sql-storage.ts +107 -41
- package/src/ir/{sql-unspecified-namespace.ts → sql-unbound-namespace.ts} +11 -7
- package/src/ir/storage-table.ts +3 -5
- package/src/types.ts +5 -4
- package/src/validators.ts +104 -84
- package/dist/types-B0lbr9cb.d.mts.map +0 -1
- package/dist/types-iqFGDcJp.mjs.map +0 -1
- package/src/ir/foreign-key-references.ts +0 -26
package/src/ir/sql-storage.ts
CHANGED
|
@@ -2,15 +2,16 @@ import type { StorageHashBase } from '@prisma-next/contract/types';
|
|
|
2
2
|
import {
|
|
3
3
|
freezeNode,
|
|
4
4
|
type Namespace,
|
|
5
|
+
NamespaceBase,
|
|
5
6
|
type Storage,
|
|
6
|
-
|
|
7
|
+
UNBOUND_NAMESPACE_ID,
|
|
7
8
|
} from '@prisma-next/framework-components/ir';
|
|
8
9
|
import {
|
|
9
10
|
isPostgresEnumStorageEntry,
|
|
10
11
|
type PostgresEnumStorageEntry,
|
|
11
12
|
} from './postgres-enum-storage-entry';
|
|
12
13
|
import { SqlNode } from './sql-node';
|
|
13
|
-
import {
|
|
14
|
+
import { SqlUnboundNamespace } from './sql-unbound-namespace';
|
|
14
15
|
import { StorageTable, type StorageTableInput } from './storage-table';
|
|
15
16
|
import {
|
|
16
17
|
isStorageTypeInstance,
|
|
@@ -19,50 +20,104 @@ import {
|
|
|
19
20
|
} from './storage-type-instance';
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
|
-
* Polymorphic value type for `SqlStorage.types` entries
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* `PostgresEnumStorageEntry` (with `kind: 'postgres-enum'`) are the
|
|
27
|
-
* two variants the SQL family ships today. The construction side also
|
|
28
|
-
* accepts {@link StorageTypeInstanceInput} so callers can pass raw
|
|
29
|
-
* codec triples; the constructor stamps the discriminator.
|
|
23
|
+
* Polymorphic value type for document-scoped `SqlStorage.types` entries
|
|
24
|
+
* (codec aliases / parameterised native type registrations). Postgres
|
|
25
|
+
* native enum registrations live under
|
|
26
|
+
* `storage.namespaces[namespaceId].types` instead.
|
|
30
27
|
*/
|
|
31
28
|
export type SqlStorageTypeEntry =
|
|
32
29
|
| StorageTypeInstance
|
|
33
|
-
|
|
|
34
|
-
|
|
|
30
|
+
| StorageTypeInstanceInput
|
|
31
|
+
| PostgresEnumStorageEntry;
|
|
35
32
|
|
|
36
33
|
const DEFAULT_NAMESPACES: Readonly<Record<string, Namespace>> = Object.freeze({
|
|
37
|
-
[
|
|
34
|
+
[UNBOUND_NAMESPACE_ID]: SqlUnboundNamespace.instance,
|
|
38
35
|
});
|
|
39
36
|
|
|
37
|
+
export interface SqlNamespaceTablesInput {
|
|
38
|
+
readonly id: string;
|
|
39
|
+
readonly tables?: Record<string, StorageTable | StorageTableInput>;
|
|
40
|
+
readonly types?: Record<string, PostgresEnumStorageEntry>;
|
|
41
|
+
}
|
|
42
|
+
|
|
40
43
|
export interface SqlStorageInput<THash extends string = string> {
|
|
41
44
|
readonly storageHash: StorageHashBase<THash>;
|
|
42
|
-
readonly tables: Record<string, StorageTable | StorageTableInput>;
|
|
43
45
|
readonly types?: Record<string, SqlStorageTypeEntry>;
|
|
44
|
-
readonly namespaces?: Readonly<Record<string, Namespace>>;
|
|
46
|
+
readonly namespaces?: Readonly<Record<string, Namespace | SqlNamespaceTablesInput>>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
class SqlNamespacePayload extends NamespaceBase {
|
|
50
|
+
declare readonly kind?: string;
|
|
51
|
+
declare readonly types?: Readonly<Record<string, PostgresEnumStorageEntry>>;
|
|
52
|
+
|
|
53
|
+
readonly id: string;
|
|
54
|
+
readonly tables: Readonly<Record<string, StorageTable>>;
|
|
55
|
+
|
|
56
|
+
constructor(input: SqlNamespaceTablesInput) {
|
|
57
|
+
super();
|
|
58
|
+
this.id = input.id;
|
|
59
|
+
this.tables = Object.freeze(
|
|
60
|
+
Object.fromEntries(
|
|
61
|
+
Object.entries(input.tables ?? {}).map(([name, t]) => [
|
|
62
|
+
name,
|
|
63
|
+
t instanceof StorageTable ? t : new StorageTable(t),
|
|
64
|
+
]),
|
|
65
|
+
),
|
|
66
|
+
);
|
|
67
|
+
if (input.types !== undefined && Object.keys(input.types).length > 0) {
|
|
68
|
+
Object.defineProperty(this, 'types', {
|
|
69
|
+
value: Object.freeze({ ...input.types }),
|
|
70
|
+
writable: false,
|
|
71
|
+
enumerable: true,
|
|
72
|
+
configurable: false,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
Object.defineProperty(this, 'kind', {
|
|
76
|
+
value: 'sql-namespace',
|
|
77
|
+
writable: false,
|
|
78
|
+
enumerable: false,
|
|
79
|
+
configurable: true,
|
|
80
|
+
});
|
|
81
|
+
freezeNode(this);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function normaliseNamespaceEntry(
|
|
86
|
+
nsKey: string,
|
|
87
|
+
ns: Namespace | SqlNamespaceTablesInput,
|
|
88
|
+
): Namespace {
|
|
89
|
+
if (ns instanceof NamespaceBase) {
|
|
90
|
+
return ns;
|
|
91
|
+
}
|
|
92
|
+
const input = ns as SqlNamespaceTablesInput; // JSON namespace payloads match SqlNamespaceTablesInput before SqlNamespacePayload materialises StorageTable instances.
|
|
93
|
+
const tableCount = Object.keys(input.tables ?? {}).length;
|
|
94
|
+
const typeCount = Object.keys(input.types ?? {}).length;
|
|
95
|
+
if (nsKey === UNBOUND_NAMESPACE_ID && tableCount === 0 && typeCount === 0) {
|
|
96
|
+
return SqlUnboundNamespace.instance;
|
|
97
|
+
}
|
|
98
|
+
return new SqlNamespacePayload(input);
|
|
45
99
|
}
|
|
46
100
|
|
|
47
101
|
/**
|
|
48
102
|
* SQL Contract IR root node for the `storage` field.
|
|
49
103
|
*
|
|
50
104
|
* Single concrete family-shared class — both Postgres and SQLite
|
|
51
|
-
* consume this
|
|
105
|
+
* consume this class today. Per-target storage subclasses are
|
|
52
106
|
* introduced when each target's namespace shape earns its
|
|
53
107
|
* target-specific concretion (target-specific derived fields,
|
|
54
108
|
* target-specific storage extensions).
|
|
55
109
|
*
|
|
56
110
|
* Honours the framework `Storage` interface: every SQL IR carries a
|
|
57
111
|
* `namespaces` map keyed by namespace id. The default singleton
|
|
58
|
-
* (`{ [
|
|
112
|
+
* (`{ [UNBOUND_NAMESPACE_ID]: SqlUnboundNamespace.instance }`)
|
|
59
113
|
* binds every contract authored before per-target namespace concretions
|
|
60
|
-
* land; per-target namespace classes (`PostgresSchema.
|
|
61
|
-
* `
|
|
114
|
+
* land; per-target namespace classes (`PostgresSchema.unbound`,
|
|
115
|
+
* `SqliteUnboundDatabase.instance`) earn their slots when each
|
|
62
116
|
* target's namespace shape lands.
|
|
63
117
|
*
|
|
64
|
-
* The constructor normalises
|
|
65
|
-
*
|
|
118
|
+
* The constructor normalises optional `types` into class instances and
|
|
119
|
+
* materialises plain namespace envelope objects into `Namespace` class
|
|
120
|
+
* instances so downstream walks see a uniform AST.
|
|
66
121
|
* `types` is polymorphic per Decision 18 Option B: codec-triple inputs
|
|
67
122
|
* are stamped with `kind: 'codec-instance'`; class-instance kinds
|
|
68
123
|
* (e.g. Postgres-enum entries satisfying `PostgresEnumStorageEntry`)
|
|
@@ -71,31 +126,42 @@ export interface SqlStorageInput<THash extends string = string> {
|
|
|
71
126
|
* responsibility (so the family base does not import target-specific
|
|
72
127
|
* subclasses).
|
|
73
128
|
*/
|
|
129
|
+
// SQL concretions always store `StorageTable`-shaped values in `tables`.
|
|
130
|
+
// `tables` is a SQL-family idiom — the framework `Namespace` contract no
|
|
131
|
+
// longer mandates this field; Mongo namespaces carry `collections`
|
|
132
|
+
// instead. The `__unbound__` slot uses the same narrowing as every other
|
|
133
|
+
// SQL namespace; the wider `Record<string, object>` on `StorageTable` is
|
|
134
|
+
// only there so emitted `contract.d.ts` table literals (which lack the
|
|
135
|
+
// runtime `kind` discriminator on `StorageTable`) structurally satisfy
|
|
136
|
+
// the slot without a class-instance check.
|
|
137
|
+
export type SqlNamespace = Namespace & {
|
|
138
|
+
readonly tables: Readonly<Record<string, StorageTable>>;
|
|
139
|
+
readonly types?: Readonly<Record<string, PostgresEnumStorageEntry>>;
|
|
140
|
+
};
|
|
141
|
+
|
|
74
142
|
export class SqlStorage<THash extends string = string> extends SqlNode implements Storage {
|
|
75
143
|
readonly storageHash: StorageHashBase<THash>;
|
|
76
|
-
readonly
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
// today (codec triples + Postgres-enum structural entries). Each
|
|
80
|
-
// variant extends the framework `StorageType` alphabet; the SQL
|
|
81
|
-
// narrowing keeps cross-domain layering clean — SQL-family consumers
|
|
82
|
-
// dispatch via `isStorageTypeInstance` / `isPostgresEnumStorageEntry`
|
|
83
|
-
// type guards rather than importing the target's concrete IR class
|
|
84
|
-
// (cross-domain rule: SQL may not import `target-*`).
|
|
144
|
+
readonly namespaces: Readonly<Record<string, SqlNamespace>> & {
|
|
145
|
+
readonly __unbound__: SqlNamespace;
|
|
146
|
+
};
|
|
85
147
|
declare readonly types?: Readonly<Record<string, StorageTypeInstance | PostgresEnumStorageEntry>>;
|
|
86
148
|
|
|
87
149
|
constructor(input: SqlStorageInput<THash>) {
|
|
88
150
|
super();
|
|
89
151
|
this.storageHash = input.storageHash;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
),
|
|
152
|
+
const inputNamespaces = input.namespaces ?? DEFAULT_NAMESPACES;
|
|
153
|
+
const normalised: Record<string, SqlNamespace> = Object.fromEntries(
|
|
154
|
+
Object.entries(inputNamespaces).map(([nsKey, ns]) => [
|
|
155
|
+
nsKey,
|
|
156
|
+
normaliseNamespaceEntry(nsKey, ns) as SqlNamespace,
|
|
157
|
+
]),
|
|
97
158
|
);
|
|
98
|
-
|
|
159
|
+
if (!normalised[UNBOUND_NAMESPACE_ID]) {
|
|
160
|
+
normalised[UNBOUND_NAMESPACE_ID] = SqlUnboundNamespace.instance as SqlNamespace;
|
|
161
|
+
}
|
|
162
|
+
this.namespaces = Object.freeze(normalised) as Readonly<Record<string, SqlNamespace>> & {
|
|
163
|
+
readonly __unbound__: SqlNamespace;
|
|
164
|
+
};
|
|
99
165
|
if (input.types !== undefined) {
|
|
100
166
|
this.types = Object.freeze(
|
|
101
167
|
Object.fromEntries(
|
|
@@ -108,9 +174,9 @@ export class SqlStorage<THash extends string = string> extends SqlNode implement
|
|
|
108
174
|
}
|
|
109
175
|
|
|
110
176
|
/**
|
|
111
|
-
* Strict polymorphic-slot dispatch for `SqlStorage.types` entries
|
|
112
|
-
*
|
|
113
|
-
*
|
|
177
|
+
* Strict polymorphic-slot dispatch for `SqlStorage.types` entries.
|
|
178
|
+
* Every entry must carry a recognised `kind` discriminator — either
|
|
179
|
+
* `'codec-instance'` (codec triple, family-shared) or
|
|
114
180
|
* `'postgres-enum'` (target-specific IR class). Untagged or
|
|
115
181
|
* unrecognised inputs throw a diagnostic naming the entry and its
|
|
116
182
|
* `kind`, so format drift surfaces loudly at the deserializer
|
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
import {
|
|
2
2
|
freezeNode,
|
|
3
3
|
NamespaceBase,
|
|
4
|
-
|
|
4
|
+
UNBOUND_NAMESPACE_ID,
|
|
5
5
|
} from '@prisma-next/framework-components/ir';
|
|
6
|
+
import type { StorageTable } from './storage-table';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
|
-
* Family-layer placeholder for the SQL
|
|
9
|
+
* Family-layer placeholder for the SQL unbound-namespace singleton —
|
|
10
|
+
* the late-bound slot whose binding the target resolves at connection
|
|
11
|
+
* time rather than at authoring time.
|
|
9
12
|
*
|
|
10
13
|
* SQL contracts honour the framework `Storage.namespaces` invariant from
|
|
11
14
|
* the moment they appear in the IR. Today `SqlStorage` is family-shared
|
|
12
15
|
* (Postgres + SQLite consume the same class); a per-target namespace
|
|
13
|
-
* concretion (`PostgresSchema.
|
|
16
|
+
* concretion (`PostgresSchema.unbound`, `SqliteUnboundDatabase.instance`)
|
|
14
17
|
* earns its existence when each target's namespace shape lands. Until
|
|
15
18
|
* then the family ships a single placeholder singleton so the JSON
|
|
16
19
|
* envelope and runtime walk are honest at every layer.
|
|
17
20
|
*
|
|
18
21
|
* The `kind` discriminator is installed as a non-enumerable own property
|
|
19
|
-
* so the JSON envelope reads `{ "id": "
|
|
22
|
+
* so the JSON envelope reads `{ "id": "__unbound__" }` — symmetric
|
|
20
23
|
* with the family-level non-enumerable `kind` on `SqlNode` and bounded
|
|
21
24
|
* to the minimum data the framework `Namespace` interface promises.
|
|
22
25
|
*
|
|
@@ -32,10 +35,11 @@ import {
|
|
|
32
35
|
* fields safely, lift `freezeNode` to a leaf-class `seal()` hook each
|
|
33
36
|
* leaf calls explicitly at the end of its own constructor.
|
|
34
37
|
*/
|
|
35
|
-
export class
|
|
36
|
-
static readonly instance:
|
|
38
|
+
export class SqlUnboundNamespace extends NamespaceBase {
|
|
39
|
+
static readonly instance: SqlUnboundNamespace = new SqlUnboundNamespace();
|
|
37
40
|
|
|
38
|
-
readonly id =
|
|
41
|
+
readonly id = UNBOUND_NAMESPACE_ID;
|
|
42
|
+
readonly tables: Readonly<Record<string, StorageTable>> = Object.freeze({});
|
|
39
43
|
declare readonly kind?: string;
|
|
40
44
|
|
|
41
45
|
private constructor() {
|
package/src/ir/storage-table.ts
CHANGED
|
@@ -15,7 +15,8 @@ export interface StorageTableInput {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
* SQL Contract IR node for a single table entry in
|
|
18
|
+
* SQL Contract IR node for a single table entry in a namespace's
|
|
19
|
+
* `tables` map.
|
|
19
20
|
*
|
|
20
21
|
* The constructor normalises nested IR-class fields (columns, primary
|
|
21
22
|
* key, uniques, indexes, foreign keys) into the appropriate class
|
|
@@ -23,10 +24,7 @@ export interface StorageTableInput {
|
|
|
23
24
|
* the input was a JSON literal or an already-constructed class.
|
|
24
25
|
*
|
|
25
26
|
* The table's `name` is not on the class — tables are keyed by name in
|
|
26
|
-
* the parent `
|
|
27
|
-
* A future namespace-aware milestone will add a `namespaceId` field
|
|
28
|
-
* when namespace-keyed storage lands; today's single-namespace shape
|
|
29
|
-
* needs neither field.
|
|
27
|
+
* the parent namespace's `tables: Record<string, StorageTable>` map.
|
|
30
28
|
*/
|
|
31
29
|
export class StorageTable extends SqlNode {
|
|
32
30
|
readonly columns: Readonly<Record<string, StorageColumn>>;
|
package/src/types.ts
CHANGED
|
@@ -7,9 +7,9 @@ export {
|
|
|
7
7
|
type ReferentialAction,
|
|
8
8
|
} from './ir/foreign-key';
|
|
9
9
|
export {
|
|
10
|
-
|
|
11
|
-
type
|
|
12
|
-
} from './ir/foreign-key-
|
|
10
|
+
ForeignKeyReference,
|
|
11
|
+
type ForeignKeyReferenceInput,
|
|
12
|
+
} from './ir/foreign-key-reference';
|
|
13
13
|
export {
|
|
14
14
|
isPostgresEnumStorageEntry,
|
|
15
15
|
POSTGRES_ENUM_KIND,
|
|
@@ -19,11 +19,12 @@ export { PrimaryKey, type PrimaryKeyInput } from './ir/primary-key';
|
|
|
19
19
|
export { Index, type IndexInput } from './ir/sql-index';
|
|
20
20
|
export { SqlNode } from './ir/sql-node';
|
|
21
21
|
export {
|
|
22
|
+
type SqlNamespaceTablesInput,
|
|
22
23
|
SqlStorage,
|
|
23
24
|
type SqlStorageInput,
|
|
24
25
|
type SqlStorageTypeEntry,
|
|
25
26
|
} from './ir/sql-storage';
|
|
26
|
-
export {
|
|
27
|
+
export { SqlUnboundNamespace } from './ir/sql-unbound-namespace';
|
|
27
28
|
export { StorageColumn, type StorageColumnInput } from './ir/storage-column';
|
|
28
29
|
export { StorageTable, type StorageTableInput } from './ir/storage-table';
|
|
29
30
|
export {
|