@prisma-next/sql-relational-core 0.3.0-dev.1 → 0.3.0-dev.10
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/README.md +1 -1
- package/dist/ast/adapter-types.d.ts +28 -0
- package/dist/ast/adapter-types.d.ts.map +1 -0
- package/dist/{query-lane-context-BhOMmb_K.d.ts → ast/codec-types.d.ts} +14 -31
- package/dist/ast/codec-types.d.ts.map +1 -0
- package/dist/ast/common.d.ts +7 -0
- package/dist/ast/common.d.ts.map +1 -0
- package/dist/ast/delete.d.ts +8 -0
- package/dist/ast/delete.d.ts.map +1 -0
- package/dist/ast/driver-types.d.ts +20 -0
- package/dist/ast/driver-types.d.ts.map +1 -0
- package/dist/ast/insert.d.ts +8 -0
- package/dist/ast/insert.d.ts.map +1 -0
- package/dist/ast/join.d.ts +6 -0
- package/dist/ast/join.d.ts.map +1 -0
- package/dist/ast/order.d.ts +6 -0
- package/dist/ast/order.d.ts.map +1 -0
- package/dist/ast/predicate.d.ts +4 -0
- package/dist/ast/predicate.d.ts.map +1 -0
- package/dist/ast/select.d.ts +18 -0
- package/dist/ast/select.d.ts.map +1 -0
- package/dist/{plan-D0OG5qzy.d.ts → ast/types.d.ts} +23 -47
- package/dist/ast/types.d.ts.map +1 -0
- package/dist/ast/update.d.ts +9 -0
- package/dist/ast/update.d.ts.map +1 -0
- package/dist/ast/util.d.ts +2 -0
- package/dist/ast/util.d.ts.map +1 -0
- package/dist/errors.d.ts +2 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/exports/ast.d.ts +14 -119
- package/dist/exports/ast.d.ts.map +1 -0
- package/dist/exports/errors.d.ts +2 -1
- package/dist/exports/errors.d.ts.map +1 -0
- package/dist/exports/guards.d.ts +2 -63
- package/dist/exports/guards.d.ts.map +1 -0
- package/dist/exports/operations-registry.d.ts +2 -13
- package/dist/exports/operations-registry.d.ts.map +1 -0
- package/dist/exports/param.d.ts +3 -14
- package/dist/exports/param.d.ts.map +1 -0
- package/dist/exports/plan.d.ts +2 -5
- package/dist/exports/plan.d.ts.map +1 -0
- package/dist/exports/query-lane-context.d.ts +2 -4
- package/dist/exports/query-lane-context.d.ts.map +1 -0
- package/dist/exports/schema.d.ts +3 -69
- package/dist/exports/schema.d.ts.map +1 -0
- package/dist/exports/types.d.ts +2 -335
- package/dist/exports/types.d.ts.map +1 -0
- package/dist/index.d.ts +9 -13
- package/dist/index.d.ts.map +1 -0
- package/dist/operations-registry.d.ts +5 -0
- package/dist/operations-registry.d.ts.map +1 -0
- package/dist/param.d.ts +4 -0
- package/dist/param.d.ts.map +1 -0
- package/dist/plan.d.ts +23 -0
- package/dist/plan.d.ts.map +1 -0
- package/dist/query-lane-context.d.ts +16 -0
- package/dist/query-lane-context.d.ts.map +1 -0
- package/dist/schema.d.ts +63 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/types.d.ts +332 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/guards.d.ts +55 -0
- package/dist/utils/guards.d.ts.map +1 -0
- package/package.json +14 -14
- package/src/ast/adapter-types.ts +36 -0
- package/src/ast/codec-types.ts +375 -0
- package/src/ast/common.ts +36 -0
- package/src/ast/delete.ts +17 -0
- package/src/ast/driver-types.ts +25 -0
- package/src/ast/insert.ts +17 -0
- package/src/ast/join.ts +54 -0
- package/src/ast/order.ts +11 -0
- package/src/ast/predicate.ts +30 -0
- package/src/ast/select.ts +39 -0
- package/src/ast/types.ts +133 -0
- package/src/ast/update.ts +19 -0
- package/src/ast/util.ts +9 -0
- package/src/errors.ts +1 -0
- package/src/exports/ast.ts +13 -0
- package/src/exports/errors.ts +1 -0
- package/src/exports/guards.ts +10 -0
- package/src/exports/operations-registry.ts +1 -0
- package/src/exports/param.ts +2 -0
- package/src/exports/plan.ts +1 -0
- package/src/exports/query-lane-context.ts +1 -0
- package/src/exports/schema.ts +6 -0
- package/src/exports/types.ts +1 -0
- package/src/index.ts +8 -0
- package/src/operations-registry.ts +237 -0
- package/src/param.ts +15 -0
- package/src/plan.ts +39 -0
- package/src/query-lane-context.ts +18 -0
- package/src/schema.ts +373 -0
- package/src/types.ts +547 -0
- package/src/utils/guards.ts +123 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import type { O } from 'ts-toolbelt';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Codec metadata for database-specific type information.
|
|
5
|
+
* Used for schema introspection and verification.
|
|
6
|
+
*/
|
|
7
|
+
export interface CodecMeta {
|
|
8
|
+
readonly db?: {
|
|
9
|
+
readonly sql?: {
|
|
10
|
+
readonly postgres?: {
|
|
11
|
+
readonly nativeType: string; // e.g. 'integer', 'text', 'vector', 'timestamp with time zone'
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Codec interface for encoding/decoding values between wire format and JavaScript types.
|
|
19
|
+
*
|
|
20
|
+
* Codecs are pure, synchronous functions with no side effects or IO.
|
|
21
|
+
* They provide deterministic conversion between database wire types and JS values.
|
|
22
|
+
*/
|
|
23
|
+
export interface Codec<Id extends string = string, TWire = unknown, TJs = unknown> {
|
|
24
|
+
/**
|
|
25
|
+
* Namespaced codec identifier in format 'namespace/name@version'
|
|
26
|
+
* Examples: 'pg/text@1', 'pg/uuid@1', 'pg/timestamptz@1'
|
|
27
|
+
*/
|
|
28
|
+
readonly id: Id;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Contract scalar type IDs that this codec can handle.
|
|
32
|
+
* Examples: ['text'], ['int4', 'float8'], ['timestamp', 'timestamptz']
|
|
33
|
+
*/
|
|
34
|
+
readonly targetTypes: readonly string[];
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Optional metadata for database-specific type information.
|
|
38
|
+
* Used for schema introspection and verification.
|
|
39
|
+
*/
|
|
40
|
+
readonly meta?: CodecMeta;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Decode a wire value (from database) to JavaScript type.
|
|
44
|
+
* Must be synchronous and pure (no side effects).
|
|
45
|
+
*/
|
|
46
|
+
decode(wire: TWire): TJs;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Encode a JavaScript value to wire format (for database).
|
|
50
|
+
* Optional - if not provided, values pass through unchanged.
|
|
51
|
+
* Must be synchronous and pure (no side effects).
|
|
52
|
+
*/
|
|
53
|
+
encode?(value: TJs): TWire;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Registry interface for codecs organized by ID and by contract scalar type.
|
|
58
|
+
*
|
|
59
|
+
* The registry allows looking up codecs by their namespaced ID or by the
|
|
60
|
+
* contract scalar types they handle. Multiple codecs may handle the same
|
|
61
|
+
* scalar type; ordering in byScalar reflects preference (adapter first,
|
|
62
|
+
* then packs, then app overrides).
|
|
63
|
+
*/
|
|
64
|
+
export interface CodecRegistry {
|
|
65
|
+
get(id: string): Codec<string> | undefined;
|
|
66
|
+
has(id: string): boolean;
|
|
67
|
+
getByScalar(scalar: string): readonly Codec<string>[];
|
|
68
|
+
getDefaultCodec(scalar: string): Codec<string> | undefined;
|
|
69
|
+
register(codec: Codec<string>): void;
|
|
70
|
+
[Symbol.iterator](): Iterator<Codec<string>>;
|
|
71
|
+
values(): IterableIterator<Codec<string>>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Implementation of CodecRegistry.
|
|
76
|
+
*/
|
|
77
|
+
class CodecRegistryImpl implements CodecRegistry {
|
|
78
|
+
private readonly _byId = new Map<string, Codec<string>>();
|
|
79
|
+
private readonly _byScalar = new Map<string, Codec<string>[]>();
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Map-like interface for codec lookup by ID.
|
|
83
|
+
* Example: registry.get('pg/text@1')
|
|
84
|
+
*/
|
|
85
|
+
get(id: string): Codec<string> | undefined {
|
|
86
|
+
return this._byId.get(id);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Check if a codec with the given ID is registered.
|
|
91
|
+
*/
|
|
92
|
+
has(id: string): boolean {
|
|
93
|
+
return this._byId.has(id);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Get all codecs that handle a given scalar type.
|
|
98
|
+
* Returns an empty frozen array if no codecs are found.
|
|
99
|
+
* Example: registry.getByScalar('text') → [codec1, codec2, ...]
|
|
100
|
+
*/
|
|
101
|
+
getByScalar(scalar: string): readonly Codec<string>[] {
|
|
102
|
+
return this._byScalar.get(scalar) ?? Object.freeze([]);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get the default codec for a scalar type (first registered codec).
|
|
107
|
+
* Returns undefined if no codec handles this scalar type.
|
|
108
|
+
*/
|
|
109
|
+
getDefaultCodec(scalar: string): Codec<string> | undefined {
|
|
110
|
+
const _codecs = this._byScalar.get(scalar);
|
|
111
|
+
return _codecs?.[0];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Register a codec in the registry.
|
|
116
|
+
* Throws an error if a codec with the same ID is already registered.
|
|
117
|
+
*
|
|
118
|
+
* @param codec - The codec to register
|
|
119
|
+
* @throws Error if a codec with the same ID already exists
|
|
120
|
+
*/
|
|
121
|
+
register(codec: Codec<string>): void {
|
|
122
|
+
if (this._byId.has(codec.id)) {
|
|
123
|
+
throw new Error(`Codec with ID '${codec.id}' is already registered`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
this._byId.set(codec.id, codec);
|
|
127
|
+
|
|
128
|
+
// Update byScalar mapping
|
|
129
|
+
for (const scalarType of codec.targetTypes) {
|
|
130
|
+
const existing = this._byScalar.get(scalarType);
|
|
131
|
+
if (existing) {
|
|
132
|
+
existing.push(codec);
|
|
133
|
+
} else {
|
|
134
|
+
this._byScalar.set(scalarType, [codec]);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Returns an iterator over all registered codecs.
|
|
141
|
+
* Useful for iterating through codecs from another registry.
|
|
142
|
+
*/
|
|
143
|
+
*[Symbol.iterator](): Iterator<Codec<string>> {
|
|
144
|
+
for (const codec of this._byId.values()) {
|
|
145
|
+
yield codec;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Returns an iterable of all registered codecs.
|
|
151
|
+
*/
|
|
152
|
+
values(): IterableIterator<Codec<string>> {
|
|
153
|
+
return this._byId.values();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Codec factory - creates a codec with typeId and encode/decode functions.
|
|
159
|
+
*/
|
|
160
|
+
export function codec<Id extends string, TWire, TJs>(config: {
|
|
161
|
+
typeId: Id;
|
|
162
|
+
targetTypes: readonly string[];
|
|
163
|
+
encode: (value: TJs) => TWire;
|
|
164
|
+
decode: (wire: TWire) => TJs;
|
|
165
|
+
meta?: CodecMeta;
|
|
166
|
+
}): Codec<Id, TWire, TJs> {
|
|
167
|
+
return {
|
|
168
|
+
id: config.typeId,
|
|
169
|
+
targetTypes: config.targetTypes,
|
|
170
|
+
...(config.meta ? { meta: config.meta } : {}),
|
|
171
|
+
encode: config.encode,
|
|
172
|
+
decode: config.decode,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Type helpers to extract codec types.
|
|
178
|
+
*/
|
|
179
|
+
export type CodecId<T> = T extends Codec<infer Id, unknown, unknown>
|
|
180
|
+
? Id
|
|
181
|
+
: T extends { readonly id: infer Id }
|
|
182
|
+
? Id
|
|
183
|
+
: never;
|
|
184
|
+
export type CodecInput<T> = T extends Codec<string, unknown, infer JsT> ? JsT : never;
|
|
185
|
+
export type CodecOutput<T> = T extends Codec<string, unknown, infer JsT> ? JsT : never;
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Type helper to extract codec types from builder instance.
|
|
189
|
+
*/
|
|
190
|
+
export type ExtractCodecTypes<
|
|
191
|
+
ScalarNames extends { readonly [K in keyof ScalarNames]: Codec<string> } = Record<never, never>,
|
|
192
|
+
> = {
|
|
193
|
+
readonly [K in keyof ScalarNames as ScalarNames[K] extends Codec<infer Id, unknown, unknown>
|
|
194
|
+
? Id
|
|
195
|
+
: never]: {
|
|
196
|
+
readonly input: CodecInput<ScalarNames[K]>;
|
|
197
|
+
readonly output: CodecOutput<ScalarNames[K]>;
|
|
198
|
+
};
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Type helper to extract data type IDs from builder instance.
|
|
203
|
+
* Uses ExtractCodecTypes which preserves literal types as keys.
|
|
204
|
+
* Since ExtractCodecTypes<Record<K, ScalarNames[K]>> has exactly one key (the Id),
|
|
205
|
+
* we extract it by creating a mapped type that uses the Id as both key and value,
|
|
206
|
+
* then extract the value type. This preserves literal types.
|
|
207
|
+
*/
|
|
208
|
+
export type ExtractDataTypes<
|
|
209
|
+
ScalarNames extends { readonly [K in keyof ScalarNames]: Codec<string> },
|
|
210
|
+
> = {
|
|
211
|
+
readonly [K in keyof ScalarNames]: {
|
|
212
|
+
readonly [Id in keyof ExtractCodecTypes<Record<K, ScalarNames[K]>>]: Id;
|
|
213
|
+
}[keyof ExtractCodecTypes<Record<K, ScalarNames[K]>>];
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Builder interface for declaring codecs.
|
|
218
|
+
*/
|
|
219
|
+
export interface CodecDefBuilder<
|
|
220
|
+
ScalarNames extends { readonly [K in keyof ScalarNames]: Codec<string> } = Record<never, never>,
|
|
221
|
+
> {
|
|
222
|
+
readonly CodecTypes: ExtractCodecTypes<ScalarNames>;
|
|
223
|
+
|
|
224
|
+
add<ScalarName extends string, CodecImpl extends Codec<string>>(
|
|
225
|
+
scalarName: ScalarName,
|
|
226
|
+
codecImpl: CodecImpl,
|
|
227
|
+
): CodecDefBuilder<
|
|
228
|
+
O.Overwrite<ScalarNames, Record<ScalarName, CodecImpl>> & Record<ScalarName, CodecImpl>
|
|
229
|
+
>;
|
|
230
|
+
|
|
231
|
+
readonly codecDefinitions: {
|
|
232
|
+
readonly [K in keyof ScalarNames]: {
|
|
233
|
+
readonly typeId: ScalarNames[K] extends Codec<infer Id extends string, unknown, unknown>
|
|
234
|
+
? Id
|
|
235
|
+
: never;
|
|
236
|
+
readonly scalar: K;
|
|
237
|
+
readonly codec: ScalarNames[K];
|
|
238
|
+
readonly input: CodecInput<ScalarNames[K]>;
|
|
239
|
+
readonly output: CodecOutput<ScalarNames[K]>;
|
|
240
|
+
readonly jsType: CodecOutput<ScalarNames[K]>;
|
|
241
|
+
};
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
readonly dataTypes: {
|
|
245
|
+
readonly [K in keyof ScalarNames]: {
|
|
246
|
+
readonly [Id in keyof ExtractCodecTypes<Record<K, ScalarNames[K]>>]: Id;
|
|
247
|
+
}[keyof ExtractCodecTypes<Record<K, ScalarNames[K]>>];
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Implementation of CodecDefBuilder.
|
|
253
|
+
*/
|
|
254
|
+
class CodecDefBuilderImpl<
|
|
255
|
+
ScalarNames extends { readonly [K in keyof ScalarNames]: Codec<string> } = Record<never, never>,
|
|
256
|
+
> implements CodecDefBuilder<ScalarNames>
|
|
257
|
+
{
|
|
258
|
+
private readonly _codecs: ScalarNames;
|
|
259
|
+
|
|
260
|
+
public readonly CodecTypes: ExtractCodecTypes<ScalarNames>;
|
|
261
|
+
public readonly dataTypes: {
|
|
262
|
+
readonly [K in keyof ScalarNames]: {
|
|
263
|
+
readonly [Id in keyof ExtractCodecTypes<Record<K, ScalarNames[K]>>]: Id;
|
|
264
|
+
}[keyof ExtractCodecTypes<Record<K, ScalarNames[K]>>];
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
constructor(codecs: ScalarNames) {
|
|
268
|
+
this._codecs = codecs;
|
|
269
|
+
|
|
270
|
+
// Populate CodecTypes from codecs
|
|
271
|
+
const codecTypes: Record<string, { readonly input: unknown; readonly output: unknown }> = {};
|
|
272
|
+
for (const [, codecImpl] of Object.entries(this._codecs)) {
|
|
273
|
+
const codecImplTyped = codecImpl as Codec<string>;
|
|
274
|
+
codecTypes[codecImplTyped.id] = {
|
|
275
|
+
input: undefined as unknown as CodecInput<typeof codecImplTyped>,
|
|
276
|
+
output: undefined as unknown as CodecOutput<typeof codecImplTyped>,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
this.CodecTypes = codecTypes as ExtractCodecTypes<ScalarNames>;
|
|
280
|
+
|
|
281
|
+
// Populate dataTypes from codecs - extract id property from each codec
|
|
282
|
+
// Build object preserving keys from ScalarNames
|
|
283
|
+
// Type assertion is safe because we know ScalarNames structure matches the return type
|
|
284
|
+
// biome-ignore lint/suspicious/noExplicitAny: dynamic codec mapping requires any
|
|
285
|
+
const dataTypes = {} as any;
|
|
286
|
+
for (const key in this._codecs) {
|
|
287
|
+
if (Object.hasOwn(this._codecs, key)) {
|
|
288
|
+
const codec = this._codecs[key] as Codec<string>;
|
|
289
|
+
dataTypes[key] = codec.id;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
this.dataTypes = dataTypes as {
|
|
293
|
+
readonly [K in keyof ScalarNames]: {
|
|
294
|
+
readonly [Id in keyof ExtractCodecTypes<Record<K, ScalarNames[K]>>]: Id;
|
|
295
|
+
}[keyof ExtractCodecTypes<Record<K, ScalarNames[K]>>];
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
add<ScalarName extends string, CodecImpl extends Codec<string>>(
|
|
300
|
+
scalarName: ScalarName,
|
|
301
|
+
codecImpl: CodecImpl,
|
|
302
|
+
): CodecDefBuilder<
|
|
303
|
+
O.Overwrite<ScalarNames, Record<ScalarName, CodecImpl>> & Record<ScalarName, CodecImpl>
|
|
304
|
+
> {
|
|
305
|
+
return new CodecDefBuilderImpl({
|
|
306
|
+
...this._codecs,
|
|
307
|
+
[scalarName]: codecImpl,
|
|
308
|
+
} as O.Overwrite<ScalarNames, Record<ScalarName, CodecImpl>> & Record<ScalarName, CodecImpl>);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Derive codecDefinitions structure.
|
|
313
|
+
*/
|
|
314
|
+
get codecDefinitions(): {
|
|
315
|
+
readonly [K in keyof ScalarNames]: {
|
|
316
|
+
readonly typeId: ScalarNames[K] extends Codec<infer Id, unknown, unknown> ? Id : never;
|
|
317
|
+
readonly scalar: K;
|
|
318
|
+
readonly codec: ScalarNames[K];
|
|
319
|
+
readonly input: CodecInput<ScalarNames[K]>;
|
|
320
|
+
readonly output: CodecOutput<ScalarNames[K]>;
|
|
321
|
+
readonly jsType: CodecOutput<ScalarNames[K]>;
|
|
322
|
+
};
|
|
323
|
+
} {
|
|
324
|
+
const result: Record<
|
|
325
|
+
string,
|
|
326
|
+
{
|
|
327
|
+
typeId: string;
|
|
328
|
+
scalar: string;
|
|
329
|
+
codec: Codec;
|
|
330
|
+
input: unknown;
|
|
331
|
+
output: unknown;
|
|
332
|
+
jsType: unknown;
|
|
333
|
+
}
|
|
334
|
+
> = {};
|
|
335
|
+
|
|
336
|
+
for (const [scalarName, codecImpl] of Object.entries(this._codecs)) {
|
|
337
|
+
const codec = codecImpl as Codec<string>;
|
|
338
|
+
result[scalarName] = {
|
|
339
|
+
typeId: codec.id,
|
|
340
|
+
scalar: scalarName,
|
|
341
|
+
codec: codec,
|
|
342
|
+
input: undefined as unknown as CodecInput<typeof codec>,
|
|
343
|
+
output: undefined as unknown as CodecOutput<typeof codec>,
|
|
344
|
+
jsType: undefined as unknown as CodecOutput<typeof codec>,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return result as {
|
|
349
|
+
readonly [K in keyof ScalarNames]: {
|
|
350
|
+
readonly typeId: ScalarNames[K] extends Codec<infer Id extends string, unknown, unknown>
|
|
351
|
+
? Id
|
|
352
|
+
: never;
|
|
353
|
+
readonly scalar: K;
|
|
354
|
+
readonly codec: ScalarNames[K];
|
|
355
|
+
readonly input: CodecInput<ScalarNames[K]>;
|
|
356
|
+
readonly output: CodecOutput<ScalarNames[K]>;
|
|
357
|
+
readonly jsType: CodecOutput<ScalarNames[K]>;
|
|
358
|
+
};
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Create a new codec registry.
|
|
365
|
+
*/
|
|
366
|
+
export function createCodecRegistry(): CodecRegistry {
|
|
367
|
+
return new CodecRegistryImpl();
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Create a new codec definition builder.
|
|
372
|
+
*/
|
|
373
|
+
export function defineCodecs(): CodecDefBuilder<Record<never, never>> {
|
|
374
|
+
return new CodecDefBuilderImpl({});
|
|
375
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ColumnRef, LiteralExpr, OperationExpr, ParamRef, TableRef } from './types';
|
|
2
|
+
import { compact } from './util';
|
|
3
|
+
|
|
4
|
+
export function createTableRef(name: string): TableRef {
|
|
5
|
+
return {
|
|
6
|
+
kind: 'table',
|
|
7
|
+
name,
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function createColumnRef(table: string, column: string): ColumnRef {
|
|
12
|
+
return {
|
|
13
|
+
kind: 'col',
|
|
14
|
+
table,
|
|
15
|
+
column,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function createParamRef(index: number, name?: string): ParamRef {
|
|
20
|
+
return compact({
|
|
21
|
+
kind: 'param',
|
|
22
|
+
index,
|
|
23
|
+
name,
|
|
24
|
+
}) as ParamRef;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function createOperationExpr(operation: OperationExpr): OperationExpr {
|
|
28
|
+
return operation;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function createLiteralExpr(value: unknown): LiteralExpr {
|
|
32
|
+
return {
|
|
33
|
+
kind: 'literal',
|
|
34
|
+
value,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { BinaryExpr, ColumnRef, DeleteAst, TableRef } from './types';
|
|
2
|
+
import { compact } from './util';
|
|
3
|
+
|
|
4
|
+
export interface CreateDeleteAstOptions {
|
|
5
|
+
readonly table: TableRef;
|
|
6
|
+
readonly where: BinaryExpr;
|
|
7
|
+
readonly returning?: ReadonlyArray<ColumnRef>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function createDeleteAst(options: CreateDeleteAstOptions): DeleteAst {
|
|
11
|
+
return compact({
|
|
12
|
+
kind: 'delete',
|
|
13
|
+
table: options.table,
|
|
14
|
+
where: options.where,
|
|
15
|
+
returning: options.returning,
|
|
16
|
+
}) as DeleteAst;
|
|
17
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface SqlExecuteRequest {
|
|
2
|
+
readonly sql: string;
|
|
3
|
+
readonly params?: readonly unknown[];
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface SqlQueryResult<Row = Record<string, unknown>> {
|
|
7
|
+
readonly rows: ReadonlyArray<Row>;
|
|
8
|
+
readonly rowCount?: number | null;
|
|
9
|
+
readonly [key: string]: unknown;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface SqlExplainResult<Row = Record<string, unknown>> {
|
|
13
|
+
readonly rows: ReadonlyArray<Row>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface SqlDriver {
|
|
17
|
+
connect(): Promise<void>;
|
|
18
|
+
execute<Row = Record<string, unknown>>(request: SqlExecuteRequest): AsyncIterable<Row>;
|
|
19
|
+
explain?(request: SqlExecuteRequest): Promise<SqlExplainResult>;
|
|
20
|
+
query<Row = Record<string, unknown>>(
|
|
21
|
+
sql: string,
|
|
22
|
+
params?: readonly unknown[],
|
|
23
|
+
): Promise<SqlQueryResult<Row>>;
|
|
24
|
+
close(): Promise<void>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ColumnRef, InsertAst, ParamRef, TableRef } from './types';
|
|
2
|
+
import { compact } from './util';
|
|
3
|
+
|
|
4
|
+
export interface CreateInsertAstOptions {
|
|
5
|
+
readonly table: TableRef;
|
|
6
|
+
readonly values: Record<string, ColumnRef | ParamRef>;
|
|
7
|
+
readonly returning?: ReadonlyArray<ColumnRef>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function createInsertAst(options: CreateInsertAstOptions): InsertAst {
|
|
11
|
+
return compact({
|
|
12
|
+
kind: 'insert',
|
|
13
|
+
table: options.table,
|
|
14
|
+
values: options.values,
|
|
15
|
+
returning: options.returning,
|
|
16
|
+
}) as InsertAst;
|
|
17
|
+
}
|
package/src/ast/join.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { planInvalid } from '@prisma-next/plan';
|
|
2
|
+
import type { AnyColumnBuilder, JoinOnBuilder, JoinOnPredicate } from '../types';
|
|
3
|
+
import { isColumnBuilder } from '../types';
|
|
4
|
+
import type { ColumnRef, JoinAst, JoinOnExpr, TableRef } from './types';
|
|
5
|
+
|
|
6
|
+
export function createJoin(
|
|
7
|
+
joinType: 'inner' | 'left' | 'right' | 'full',
|
|
8
|
+
table: TableRef,
|
|
9
|
+
on: JoinOnExpr,
|
|
10
|
+
): JoinAst {
|
|
11
|
+
return {
|
|
12
|
+
kind: 'join',
|
|
13
|
+
joinType,
|
|
14
|
+
table,
|
|
15
|
+
on,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function createJoinOnExpr(left: ColumnRef, right: ColumnRef): JoinOnExpr {
|
|
20
|
+
return {
|
|
21
|
+
kind: 'eqCol',
|
|
22
|
+
left,
|
|
23
|
+
right,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
class JoinOnBuilderImpl implements JoinOnBuilder {
|
|
28
|
+
eqCol(left: AnyColumnBuilder, right: AnyColumnBuilder): JoinOnPredicate {
|
|
29
|
+
if (!left || !isColumnBuilder(left)) {
|
|
30
|
+
throw planInvalid('Join ON left operand must be a column');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!right || !isColumnBuilder(right)) {
|
|
34
|
+
throw planInvalid('Join ON right operand must be a column');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// TypeScript can't narrow ColumnBuilder properly, so we assert
|
|
38
|
+
const leftCol = left as unknown as { table: string; column: string };
|
|
39
|
+
const rightCol = right as unknown as { table: string; column: string };
|
|
40
|
+
if (leftCol.table === rightCol.table) {
|
|
41
|
+
throw planInvalid('Self-joins are not supported in MVP');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
kind: 'join-on',
|
|
46
|
+
left: left as AnyColumnBuilder,
|
|
47
|
+
right: right as AnyColumnBuilder,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function createJoinOnBuilder(): JoinOnBuilder {
|
|
53
|
+
return new JoinOnBuilderImpl();
|
|
54
|
+
}
|
package/src/ast/order.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ColumnRef, Direction, OperationExpr } from './types';
|
|
2
|
+
|
|
3
|
+
export function createOrderByItem(
|
|
4
|
+
expr: ColumnRef | OperationExpr,
|
|
5
|
+
dir: 'asc' | 'desc',
|
|
6
|
+
): { expr: ColumnRef | OperationExpr; dir: Direction } {
|
|
7
|
+
return {
|
|
8
|
+
expr,
|
|
9
|
+
dir,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
BinaryExpr,
|
|
3
|
+
BinaryOp,
|
|
4
|
+
ColumnRef,
|
|
5
|
+
ExistsExpr,
|
|
6
|
+
OperationExpr,
|
|
7
|
+
ParamRef,
|
|
8
|
+
SelectAst,
|
|
9
|
+
} from './types';
|
|
10
|
+
|
|
11
|
+
export function createBinaryExpr(
|
|
12
|
+
op: BinaryOp,
|
|
13
|
+
left: ColumnRef | OperationExpr,
|
|
14
|
+
right: ColumnRef | ParamRef,
|
|
15
|
+
): BinaryExpr {
|
|
16
|
+
return {
|
|
17
|
+
kind: 'bin',
|
|
18
|
+
op,
|
|
19
|
+
left,
|
|
20
|
+
right,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function createExistsExpr(not: boolean, subquery: SelectAst): ExistsExpr {
|
|
25
|
+
return {
|
|
26
|
+
kind: 'exists',
|
|
27
|
+
not,
|
|
28
|
+
subquery,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
BinaryExpr,
|
|
3
|
+
ColumnRef,
|
|
4
|
+
Direction,
|
|
5
|
+
ExistsExpr,
|
|
6
|
+
IncludeAst,
|
|
7
|
+
IncludeRef,
|
|
8
|
+
JoinAst,
|
|
9
|
+
OperationExpr,
|
|
10
|
+
SelectAst,
|
|
11
|
+
TableRef,
|
|
12
|
+
} from './types';
|
|
13
|
+
import { compact } from './util';
|
|
14
|
+
|
|
15
|
+
export interface CreateSelectAstOptions {
|
|
16
|
+
readonly from: TableRef;
|
|
17
|
+
readonly joins?: ReadonlyArray<JoinAst>;
|
|
18
|
+
readonly includes?: ReadonlyArray<IncludeAst>;
|
|
19
|
+
readonly project: ReadonlyArray<{
|
|
20
|
+
alias: string;
|
|
21
|
+
expr: ColumnRef | IncludeRef | OperationExpr;
|
|
22
|
+
}>;
|
|
23
|
+
readonly where?: BinaryExpr | ExistsExpr;
|
|
24
|
+
readonly orderBy?: ReadonlyArray<{ expr: ColumnRef | OperationExpr; dir: Direction }>;
|
|
25
|
+
readonly limit?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function createSelectAst(options: CreateSelectAstOptions): SelectAst {
|
|
29
|
+
return compact({
|
|
30
|
+
kind: 'select',
|
|
31
|
+
from: options.from,
|
|
32
|
+
joins: options.joins,
|
|
33
|
+
includes: options.includes,
|
|
34
|
+
project: options.project,
|
|
35
|
+
where: options.where,
|
|
36
|
+
orderBy: options.orderBy,
|
|
37
|
+
limit: options.limit,
|
|
38
|
+
}) as SelectAst;
|
|
39
|
+
}
|