@prisma-next/sql-contract-ts 0.14.0-dev.22 → 0.14.0-dev.24
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/contract-builder.d.mts +1 -138
- package/dist/contract-builder.d.mts.map +1 -1
- package/dist/contract-builder.mjs +1 -66
- package/dist/contract-builder.mjs.map +1 -1
- package/package.json +10 -10
- package/src/enum-type.ts +14 -306
package/package.json
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/sql-contract-ts",
|
|
3
|
-
"version": "0.14.0-dev.
|
|
3
|
+
"version": "0.14.0-dev.24",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"description": "SQL-specific TypeScript contract authoring surface for Prisma Next",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@prisma-next/config": "0.14.0-dev.
|
|
10
|
-
"@prisma-next/contract": "0.14.0-dev.
|
|
11
|
-
"@prisma-next/contract-authoring": "0.14.0-dev.
|
|
12
|
-
"@prisma-next/framework-components": "0.14.0-dev.
|
|
13
|
-
"@prisma-next/sql-contract": "0.14.0-dev.
|
|
14
|
-
"@prisma-next/utils": "0.14.0-dev.
|
|
9
|
+
"@prisma-next/config": "0.14.0-dev.24",
|
|
10
|
+
"@prisma-next/contract": "0.14.0-dev.24",
|
|
11
|
+
"@prisma-next/contract-authoring": "0.14.0-dev.24",
|
|
12
|
+
"@prisma-next/framework-components": "0.14.0-dev.24",
|
|
13
|
+
"@prisma-next/sql-contract": "0.14.0-dev.24",
|
|
14
|
+
"@prisma-next/utils": "0.14.0-dev.24",
|
|
15
15
|
"arktype": "^2.2.0",
|
|
16
16
|
"pathe": "^2.0.3",
|
|
17
17
|
"ts-toolbelt": "^9.6.0"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"@prisma-next/test-utils": "0.14.0-dev.
|
|
21
|
-
"@prisma-next/tsconfig": "0.14.0-dev.
|
|
20
|
+
"@prisma-next/test-utils": "0.14.0-dev.24",
|
|
21
|
+
"@prisma-next/tsconfig": "0.14.0-dev.24",
|
|
22
22
|
"@types/pg": "8.20.0",
|
|
23
23
|
"pg": "8.21.0",
|
|
24
|
-
"@prisma-next/tsdown": "0.14.0-dev.
|
|
24
|
+
"@prisma-next/tsdown": "0.14.0-dev.24",
|
|
25
25
|
"tsdown": "0.22.1",
|
|
26
26
|
"typescript": "5.9.3",
|
|
27
27
|
"vitest": "4.1.8"
|
package/src/enum-type.ts
CHANGED
|
@@ -1,306 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
readonly name: Name;
|
|
16
|
-
readonly value: Value;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Declare an enum member. The `value` defaults to `name` when omitted. The
|
|
21
|
-
* value is an unconstrained literal here; `enumType` constrains it against the
|
|
22
|
-
* codec's input type. Both generics are preserved as literals so downstream
|
|
23
|
-
* `enumType` carries the value union in its type; the value is serialized to its
|
|
24
|
-
* codec string form only at lowering.
|
|
25
|
-
*/
|
|
26
|
-
export function member<const Name extends string>(name: Name): EnumMember<Name, Name>;
|
|
27
|
-
export function member<const Name extends string, const Value>(
|
|
28
|
-
name: Name,
|
|
29
|
-
value: Value,
|
|
30
|
-
): EnumMember<Name, Value>;
|
|
31
|
-
export function member<const Name extends string, const Value = Name>(
|
|
32
|
-
name: Name,
|
|
33
|
-
value?: Value,
|
|
34
|
-
): EnumMember<Name, Value> {
|
|
35
|
-
return {
|
|
36
|
-
name,
|
|
37
|
-
value: blindCast<
|
|
38
|
-
Value,
|
|
39
|
-
'overload signatures enforce Value=Name when value is omitted; default generic Value=Name makes this safe'
|
|
40
|
-
>(value ?? name),
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// ---------------------------------------------------------------------------
|
|
45
|
-
// Internal types for inferring the literal tuple from the members spread
|
|
46
|
-
// ---------------------------------------------------------------------------
|
|
47
|
-
|
|
48
|
-
type MembersToValues<Members extends readonly EnumMember<string, unknown>[]> = {
|
|
49
|
-
readonly [K in keyof Members]: Members[K] extends EnumMember<string, infer V> ? V : never;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
type MembersToNames<Members extends readonly EnumMember<string, unknown>[]> = {
|
|
53
|
-
readonly [K in keyof Members]: Members[K] extends EnumMember<infer N, unknown> ? N : never;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
type MembersAccessorMap<Members extends readonly EnumMember<string, unknown>[]> = {
|
|
57
|
-
readonly [M in Members[number] as M['name']]: M['value'];
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
// ---------------------------------------------------------------------------
|
|
61
|
-
// EnumTypeHandle — the authoring handle returned by enumType()
|
|
62
|
-
// ---------------------------------------------------------------------------
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Internal brand that identifies an EnumTypeHandle in the lowering pipeline.
|
|
66
|
-
* Not exported — callers only interact with `EnumTypeHandle`.
|
|
67
|
-
*/
|
|
68
|
-
export const ENUM_TYPE_HANDLE_BRAND = Symbol('EnumTypeHandle');
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Authoring handle returned by `enumType()`. Carries:
|
|
72
|
-
*
|
|
73
|
-
* - The ordered literal value tuple (`.values`) and name tuple (`.names`)
|
|
74
|
-
* so downstream type-tests can assert literal preservation.
|
|
75
|
-
* - A namespaced member accessor map (`.members`) to avoid collisions with
|
|
76
|
-
* `.values` / `.has` / `.nameOf` / `.ordinalOf`.
|
|
77
|
-
* - Runtime helpers `.has()`, `.nameOf()`, `.ordinalOf()`.
|
|
78
|
-
* - Internal metadata (`enumName`, `codecId`, `nativeType`,
|
|
79
|
-
* `enumMembers`) for the lowering pipeline.
|
|
80
|
-
*
|
|
81
|
-
* The type is generic over the ordered value tuple so callers that assign
|
|
82
|
-
* `const Role = enumType(...)` retain the literal tuple on `.values`.
|
|
83
|
-
*/
|
|
84
|
-
export interface EnumTypeHandle<
|
|
85
|
-
Name extends string = string,
|
|
86
|
-
Values extends readonly unknown[] = readonly unknown[],
|
|
87
|
-
Names extends readonly string[] = readonly string[],
|
|
88
|
-
MembersMap extends Record<string, unknown> = Record<string, unknown>,
|
|
89
|
-
> {
|
|
90
|
-
/** Internal brand for lowering-pipeline detection. */
|
|
91
|
-
readonly [ENUM_TYPE_HANDLE_BRAND]: true;
|
|
92
|
-
|
|
93
|
-
/** The enum's declared name (used as the key in domain `enum` / storage `valueSet`). */
|
|
94
|
-
readonly enumName: Name;
|
|
95
|
-
|
|
96
|
-
/** codecId from the codec passed to `enumType`. */
|
|
97
|
-
readonly codecId: string;
|
|
98
|
-
|
|
99
|
-
/** nativeType from the codec passed to `enumType`. */
|
|
100
|
-
readonly nativeType: string;
|
|
101
|
-
|
|
102
|
-
/** Ordered member list for lowering (name + value pairs). */
|
|
103
|
-
readonly enumMembers: readonly { readonly name: string; readonly value: Values[number] }[];
|
|
104
|
-
|
|
105
|
-
/** Ordered literal value tuple. Declaration order is preserved. */
|
|
106
|
-
readonly values: Values;
|
|
107
|
-
|
|
108
|
-
/** Ordered literal name tuple. Declaration order is preserved. */
|
|
109
|
-
readonly names: Names;
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Namespaced accessor map: `Role.members.User === 'user'`.
|
|
113
|
-
* Namespaced under `.members` to avoid collisions with `.values` / `.has`.
|
|
114
|
-
*/
|
|
115
|
-
readonly members: MembersMap;
|
|
116
|
-
|
|
117
|
-
/** Returns `true` if `v` is a declared member value. */
|
|
118
|
-
has(v: Values[number]): boolean;
|
|
119
|
-
|
|
120
|
-
/** Returns the member name for a value, or `undefined` if not found. */
|
|
121
|
-
nameOf(v: Values[number]): string | undefined;
|
|
122
|
-
|
|
123
|
-
/** Returns the zero-based declaration index of a value, or `-1` if not found. */
|
|
124
|
-
ordinalOf(v: Values[number]): number;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// ---------------------------------------------------------------------------
|
|
128
|
-
// enumType()
|
|
129
|
-
// ---------------------------------------------------------------------------
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* A codec typemap: codecId → `{ input, output }`, the same shape the query
|
|
133
|
-
* lanes consume (e.g. `{ 'pg/text@1': { input: string }, 'pg/int4@1': { input: number } }`).
|
|
134
|
-
* The bound `enumType` wrappers supply the target pack's typemap; the core
|
|
135
|
-
* defaults to an empty map (no codec is known), so member values stay
|
|
136
|
-
* unconstrained.
|
|
137
|
-
*/
|
|
138
|
-
export type CodecTypeMap = Record<string, { readonly input?: unknown }>;
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* The application input type the codec dictates for an enum's member values:
|
|
142
|
-
* looks `Codec['codecId']` up in the supplied codec typemap. When the codecId
|
|
143
|
-
* isn't in the map (the core's empty default, or an unknown codec) the input is
|
|
144
|
-
* unconstrained, so any member-value literal is accepted and inferred verbatim.
|
|
145
|
-
*/
|
|
146
|
-
export type CodecInput<
|
|
147
|
-
CodecTypes extends CodecTypeMap,
|
|
148
|
-
Codec extends { readonly codecId: string },
|
|
149
|
-
> = Codec['codecId'] extends keyof CodecTypes
|
|
150
|
-
? CodecTypes[Codec['codecId']] extends { readonly input: infer In }
|
|
151
|
-
? In
|
|
152
|
-
: unknown
|
|
153
|
-
: unknown;
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Declare a domain enum for use in TS-authoring contracts.
|
|
157
|
-
*
|
|
158
|
-
* - The codec is an explicit required argument — the `codecId` and
|
|
159
|
-
* `nativeType` are taken from the passed `ColumnTypeDescriptor` (e.g.
|
|
160
|
-
* `{ codecId: 'pg/text@1', nativeType: 'text' }` from a field preset
|
|
161
|
-
* output or a direct inline object).
|
|
162
|
-
* - `const` generics on the members spread preserve the ordered literal
|
|
163
|
-
* value tuple so `Role.values` is `readonly ['user','admin']`, not
|
|
164
|
-
* `string[]`.
|
|
165
|
-
* - Well-formedness assertions at construction: non-empty member list;
|
|
166
|
-
* unique names; unique values.
|
|
167
|
-
*
|
|
168
|
-
* The returned handle wires into `field.namedType(handle)` to set
|
|
169
|
-
* `valueSet` refs on both the domain field and the storage column.
|
|
170
|
-
*
|
|
171
|
-
* @example
|
|
172
|
-
* ```ts
|
|
173
|
-
* const Role = enumType('Role', { codecId: 'pg/text@1', nativeType: 'text' },
|
|
174
|
-
* member('User', 'user'),
|
|
175
|
-
* member('Admin', 'admin'),
|
|
176
|
-
* );
|
|
177
|
-
* // Role.values → readonly ['user', 'admin']
|
|
178
|
-
* // Role.members.User → 'user'
|
|
179
|
-
* ```
|
|
180
|
-
*/
|
|
181
|
-
export function enumType<
|
|
182
|
-
CodecTypes extends CodecTypeMap = Record<string, never>,
|
|
183
|
-
const Name extends string = string,
|
|
184
|
-
const Codec extends Pick<ColumnTypeDescriptor, 'codecId' | 'nativeType'> = Pick<
|
|
185
|
-
ColumnTypeDescriptor,
|
|
186
|
-
'codecId' | 'nativeType'
|
|
187
|
-
>,
|
|
188
|
-
const Members extends readonly [
|
|
189
|
-
EnumMember<string, CodecInput<CodecTypes, Codec>>,
|
|
190
|
-
...EnumMember<string, CodecInput<CodecTypes, Codec>>[],
|
|
191
|
-
] = readonly [EnumMember<string, CodecInput<CodecTypes, Codec>>],
|
|
192
|
-
>(
|
|
193
|
-
name: Name,
|
|
194
|
-
codec: Codec,
|
|
195
|
-
...members: Members
|
|
196
|
-
): EnumTypeHandle<
|
|
197
|
-
Name,
|
|
198
|
-
MembersToValues<[...Members]>,
|
|
199
|
-
MembersToNames<[...Members]>,
|
|
200
|
-
MembersAccessorMap<[...Members]>
|
|
201
|
-
>;
|
|
202
|
-
export function enumType(
|
|
203
|
-
name: string,
|
|
204
|
-
codec: Pick<ColumnTypeDescriptor, 'codecId' | 'nativeType'>,
|
|
205
|
-
...members: EnumMember<string, unknown>[]
|
|
206
|
-
): EnumTypeHandle;
|
|
207
|
-
export function enumType(
|
|
208
|
-
name: string,
|
|
209
|
-
codec: Pick<ColumnTypeDescriptor, 'codecId' | 'nativeType'>,
|
|
210
|
-
...members: EnumMember<string, unknown>[]
|
|
211
|
-
): EnumTypeHandle {
|
|
212
|
-
if (members.length === 0) {
|
|
213
|
-
throw new Error(`enumType("${name}"): must have at least one member.`);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const seenNames = new Set<string>();
|
|
217
|
-
const seenValues = new Set<string>();
|
|
218
|
-
for (const m of members) {
|
|
219
|
-
if (seenNames.has(m.name)) {
|
|
220
|
-
throw new Error(
|
|
221
|
-
`enumType("${name}"): duplicate member name "${m.name}". Member names must be unique.`,
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
seenNames.add(m.name);
|
|
225
|
-
|
|
226
|
-
const loweredValue = String(m.value);
|
|
227
|
-
if (seenValues.has(loweredValue)) {
|
|
228
|
-
throw new Error(
|
|
229
|
-
`enumType("${name}"): duplicate member value "${loweredValue}". Member values must be unique.`,
|
|
230
|
-
);
|
|
231
|
-
}
|
|
232
|
-
seenValues.add(loweredValue);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
const values = Object.freeze(members.map((m) => m.value));
|
|
236
|
-
const names = Object.freeze(members.map((m) => m.name));
|
|
237
|
-
const enumMembers = Object.freeze(members.map((m) => ({ name: m.name, value: m.value })));
|
|
238
|
-
|
|
239
|
-
const membersAccessor = Object.freeze(Object.fromEntries(members.map((m) => [m.name, m.value])));
|
|
240
|
-
|
|
241
|
-
const valueSet = new Set(values);
|
|
242
|
-
const valueToName = new Map(members.map((m) => [m.value, m.name]));
|
|
243
|
-
const valueToOrdinal = new Map(values.map((v, i) => [v, i]));
|
|
244
|
-
|
|
245
|
-
return {
|
|
246
|
-
[ENUM_TYPE_HANDLE_BRAND]: true,
|
|
247
|
-
enumName: name,
|
|
248
|
-
codecId: codec.codecId,
|
|
249
|
-
nativeType: codec.nativeType,
|
|
250
|
-
enumMembers,
|
|
251
|
-
values,
|
|
252
|
-
names,
|
|
253
|
-
members: membersAccessor,
|
|
254
|
-
has: (v: unknown) => valueSet.has(v),
|
|
255
|
-
nameOf: (v: unknown) => valueToName.get(v),
|
|
256
|
-
ordinalOf: (v: unknown) => valueToOrdinal.get(v) ?? -1,
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* The signature of an `enumType` whose codec typemap is already bound — the
|
|
262
|
-
* shape a target-bound wrapper (e.g. `@prisma-next/postgres/contract-builder`)
|
|
263
|
-
* exposes. The member values are constrained to the codec's input type drawn
|
|
264
|
-
* from `CodecTypes` (so a `pg/text@1` codec rejects numeric members, etc.),
|
|
265
|
-
* while `Name`, `Codec`, and the member tuple still infer from the call.
|
|
266
|
-
*/
|
|
267
|
-
export type BoundEnumType<CodecTypes extends CodecTypeMap> = <
|
|
268
|
-
const Name extends string,
|
|
269
|
-
const Codec extends Pick<ColumnTypeDescriptor, 'codecId' | 'nativeType'>,
|
|
270
|
-
const Members extends readonly [
|
|
271
|
-
EnumMember<string, CodecInput<CodecTypes, Codec>>,
|
|
272
|
-
...EnumMember<string, CodecInput<CodecTypes, Codec>>[],
|
|
273
|
-
],
|
|
274
|
-
>(
|
|
275
|
-
name: Name,
|
|
276
|
-
codec: Codec,
|
|
277
|
-
...members: Members
|
|
278
|
-
) => EnumTypeHandle<
|
|
279
|
-
Name,
|
|
280
|
-
MembersToValues<[...Members]>,
|
|
281
|
-
MembersToNames<[...Members]>,
|
|
282
|
-
MembersAccessorMap<[...Members]>
|
|
283
|
-
>;
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Bind `enumType` to a target's codec typemap. The returned function is the
|
|
287
|
-
* same runtime `enumType`, retyped so member values are constrained to the
|
|
288
|
-
* codec's input type. Target packages call this with their pack's
|
|
289
|
-
* `ExtractCodecTypesFromPack<Pack>` to expose a codec-aware `enumType`.
|
|
290
|
-
*/
|
|
291
|
-
export function bindEnumType<CodecTypes extends CodecTypeMap>(): BoundEnumType<CodecTypes> {
|
|
292
|
-
return enumType;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Returns true when the value is an `EnumTypeHandle` produced by
|
|
297
|
-
* `enumType()`. Used in the lowering pipeline to detect enum handles
|
|
298
|
-
* in field state without importing the BRAND symbol at every call site.
|
|
299
|
-
*/
|
|
300
|
-
export function isEnumTypeHandle(value: unknown): value is EnumTypeHandle {
|
|
301
|
-
return (
|
|
302
|
-
typeof value === 'object' &&
|
|
303
|
-
value !== null &&
|
|
304
|
-
Reflect.get(value, ENUM_TYPE_HANDLE_BRAND) === true
|
|
305
|
-
);
|
|
306
|
-
}
|
|
1
|
+
export type {
|
|
2
|
+
BoundEnumType,
|
|
3
|
+
CodecInput,
|
|
4
|
+
CodecTypeMap,
|
|
5
|
+
EnumMember,
|
|
6
|
+
EnumTypeHandle,
|
|
7
|
+
} from '@prisma-next/contract-authoring';
|
|
8
|
+
export {
|
|
9
|
+
bindEnumType,
|
|
10
|
+
ENUM_TYPE_HANDLE_BRAND,
|
|
11
|
+
enumType,
|
|
12
|
+
isEnumTypeHandle,
|
|
13
|
+
member,
|
|
14
|
+
} from '@prisma-next/contract-authoring';
|