@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.
- package/dist/{types-DqhaAjCH.mjs → entity-kinds-Cl36zL5j.mjs} +121 -205
- package/dist/entity-kinds-Cl36zL5j.mjs.map +1 -0
- package/dist/entity-kinds.d.mts +18 -0
- package/dist/entity-kinds.d.mts.map +1 -0
- package/dist/entity-kinds.mjs +2 -0
- package/dist/factories.d.mts +2 -2
- package/dist/factories.mjs +2 -1
- package/dist/factories.mjs.map +1 -1
- package/dist/index-type-validation.d.mts +1 -1
- package/dist/index-type-validation.mjs +9 -12
- package/dist/index-type-validation.mjs.map +1 -1
- package/dist/resolve-storage-table.d.mts +2 -1
- package/dist/resolve-storage-table.d.mts.map +1 -1
- package/dist/resolve-storage-table.mjs +11 -8
- package/dist/resolve-storage-table.mjs.map +1 -1
- package/dist/sql-storage-Dga0jwP2.d.mts +128 -0
- package/dist/sql-storage-Dga0jwP2.d.mts.map +1 -0
- package/dist/{sql-storage-CXf9xjAL.d.mts → storage-value-set-WnYsIFM8.d.mts} +8 -120
- package/dist/storage-value-set-WnYsIFM8.d.mts.map +1 -0
- package/dist/types-B-eiQXff.mjs +191 -0
- package/dist/types-B-eiQXff.mjs.map +1 -0
- package/dist/{types-DEnWD3xB.d.mts → types-B1N8w0I2.d.mts} +11 -62
- package/dist/types-B1N8w0I2.d.mts.map +1 -0
- package/dist/types.d.mts +4 -3
- package/dist/types.mjs +3 -2
- package/dist/validators.d.mts +75 -40
- package/dist/validators.d.mts.map +1 -1
- package/dist/validators.mjs +53 -184
- package/dist/validators.mjs.map +1 -1
- package/package.json +8 -7
- package/src/entity-kinds.ts +45 -0
- package/src/exports/entity-kinds.ts +5 -0
- package/src/exports/types.ts +2 -4
- package/src/index-type-validation.ts +2 -3
- package/src/ir/build-sql-namespace.ts +39 -32
- package/src/ir/sql-node.ts +2 -2
- package/src/ir/sql-storage.ts +22 -24
- package/src/ir/sql-unbound-namespace.ts +15 -3
- package/src/ir/storage-entry-schemas.ts +128 -0
- package/src/ir/storage-type-instance.ts +3 -3
- package/src/ir/storage-value-set.ts +6 -5
- package/src/resolve-storage-table.ts +12 -17
- package/src/types.ts +10 -10
- package/src/validators.ts +83 -225
- package/dist/sql-storage-CXf9xjAL.d.mts.map +0 -1
- package/dist/types-DEnWD3xB.d.mts.map +0 -1
- package/dist/types-DqhaAjCH.mjs.map +0 -1
- 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
|
+
}
|
package/src/exports/types.ts
CHANGED
|
@@ -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
|
|
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,
|
|
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
|
|
8
|
-
import
|
|
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:
|
|
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
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
?
|
|
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,
|
package/src/ir/sql-node.ts
CHANGED
|
@@ -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.
|
|
45
|
-
//
|
|
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.
|
package/src/ir/sql-storage.ts
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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:
|
|
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:
|
|
44
|
-
|
|
45
|
-
|
|
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
|
|
18
|
-
*
|
|
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
|
|
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: '
|
|
11
|
+
readonly kind: 'valueSet';
|
|
11
12
|
/** Ordered permitted values, codec-encoded. Declaration order is preserved. */
|
|
12
|
-
readonly values: readonly
|
|
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 (`'
|
|
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 = '
|
|
35
|
-
readonly values: readonly
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
90
|
-
TFieldInputTypes extends
|
|
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
|
|
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
|
|
170
|
+
? F extends NamespacedFieldTypeMap
|
|
171
171
|
? F
|
|
172
172
|
: Record<string, never>
|
|
173
173
|
: Record<string, never>;
|