@prisma-next/sql-relational-core 0.5.0-dev.7 → 0.5.0-dev.71
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 +65 -1
- package/dist/{errors-D3xmG4h-.mjs → errors-Cb03_kgU.mjs} +2 -2
- package/dist/{errors-D3xmG4h-.mjs.map → errors-Cb03_kgU.mjs.map} +1 -1
- package/dist/{errors-ChY_dHam.d.mts → errors-DCg-36g-.d.mts} +2 -2
- package/dist/errors-DCg-36g-.d.mts.map +1 -0
- package/dist/exports/ast.d.mts +154 -85
- package/dist/exports/ast.d.mts.map +1 -1
- package/dist/exports/ast.mjs +229 -1289
- package/dist/exports/ast.mjs.map +1 -1
- package/dist/exports/codec-descriptor-registry.d.mts +17 -0
- package/dist/exports/codec-descriptor-registry.d.mts.map +1 -0
- package/dist/exports/codec-descriptor-registry.mjs +40 -0
- package/dist/exports/codec-descriptor-registry.mjs.map +1 -0
- package/dist/exports/errors.d.mts +1 -4
- package/dist/exports/errors.mjs +2 -3
- package/dist/exports/expression.d.mts +84 -0
- package/dist/exports/expression.d.mts.map +1 -0
- package/dist/exports/expression.mjs +62 -0
- package/dist/exports/expression.mjs.map +1 -0
- package/dist/exports/plan.d.mts +3 -2
- package/dist/exports/plan.mjs +1 -17
- package/dist/exports/query-lane-context.d.mts +2 -3
- package/dist/exports/query-lane-context.mjs +1 -1
- package/dist/exports/types.d.mts +3 -4
- package/dist/exports/types.mjs +1 -1
- package/dist/index.d.mts +10 -11
- package/dist/index.mjs +6 -5
- package/dist/plan-CZ6CFuSX.d.mts +24 -0
- package/dist/plan-CZ6CFuSX.d.mts.map +1 -0
- package/dist/query-lane-context-DaimN0zf.d.mts +174 -0
- package/dist/query-lane-context-DaimN0zf.d.mts.map +1 -0
- package/dist/sql-execution-plan-DgcD75jn.d.mts +32 -0
- package/dist/sql-execution-plan-DgcD75jn.d.mts.map +1 -0
- package/dist/types-BnLrX_Vr.d.mts +23 -0
- package/dist/types-BnLrX_Vr.d.mts.map +1 -0
- package/dist/{types-k9pir8XY.d.mts → types-Czw4j_wY.d.mts} +18 -25
- package/dist/types-Czw4j_wY.d.mts.map +1 -0
- package/dist/{types-C3Hg-CVz.d.mts → types-hwPoFZX2.d.mts} +36 -23
- package/dist/types-hwPoFZX2.d.mts.map +1 -0
- package/dist/types-vA5134SY.mjs +1072 -0
- package/dist/types-vA5134SY.mjs.map +1 -0
- package/package.json +16 -12
- package/src/ast/adapter-types.ts +6 -14
- package/src/ast/codec-types.ts +90 -372
- package/src/ast/sql-codec-helpers.ts +79 -0
- package/src/ast/sql-codecs.ts +285 -125
- package/src/ast/types.ts +170 -175
- package/src/ast/util.ts +23 -0
- package/src/ast/validate-param-refs.ts +39 -0
- package/src/codec-descriptor-registry.ts +52 -0
- package/src/exports/ast.ts +2 -0
- package/src/exports/codec-descriptor-registry.ts +1 -0
- package/src/exports/expression.ts +1 -0
- package/src/exports/plan.ts +1 -0
- package/src/exports/types.ts +1 -0
- package/src/expression.ts +134 -0
- package/src/index.ts +1 -0
- package/src/plan.ts +11 -30
- package/src/query-lane-context.ts +35 -55
- package/src/runtime-scope.ts +20 -0
- package/src/sql-execution-plan.ts +28 -0
- package/src/types.ts +9 -22
- package/dist/codec-types-DcEITed4.d.mts +0 -144
- package/dist/codec-types-DcEITed4.d.mts.map +0 -1
- package/dist/errors-ChY_dHam.d.mts.map +0 -1
- package/dist/exports/plan.mjs.map +0 -1
- package/dist/plan-Cs65hb-E.d.mts +0 -28
- package/dist/plan-Cs65hb-E.d.mts.map +0 -1
- package/dist/query-lane-context-UlR8vOkd.d.mts +0 -89
- package/dist/query-lane-context-UlR8vOkd.d.mts.map +0 -1
- package/dist/types-C3Hg-CVz.d.mts.map +0 -1
- package/dist/types-k9pir8XY.d.mts.map +0 -1
package/src/ast/codec-types.ts
CHANGED
|
@@ -1,39 +1,51 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import type {
|
|
2
|
+
Codec as BaseCodec,
|
|
3
|
+
CodecCallContext,
|
|
4
|
+
CodecDescriptor,
|
|
5
|
+
CodecInstanceContext,
|
|
6
|
+
CodecTrait,
|
|
7
|
+
} from '@prisma-next/framework-components/codec';
|
|
8
|
+
|
|
9
|
+
export type {
|
|
10
|
+
CodecCallContext,
|
|
11
|
+
CodecDescriptor,
|
|
12
|
+
CodecTrait,
|
|
13
|
+
} from '@prisma-next/framework-components/codec';
|
|
8
14
|
|
|
9
15
|
/**
|
|
10
|
-
*
|
|
11
|
-
* Shared between adapter (compile-time) and runtime layers to avoid duplication.
|
|
16
|
+
* SQL-family addressing of a single column. The decode site populates a `SqlColumnRef` whenever it can resolve the cell to a single underlying `(table, column)` (the typical case for projected columns from a single-table source); cells the runtime cannot resolve (aggregate aliases, include aggregate fields, computed projections without a simple ref) get `column = undefined`.
|
|
12
17
|
*
|
|
13
|
-
*
|
|
14
|
-
* @template THelper - The type returned by the optional `init` hook
|
|
18
|
+
* The shape is a structural projection of the runtime's `ColumnRef` so the SQL decode site can reuse the resolution it already performs for `RUNTIME.DECODE_FAILED` envelope construction without allocating twice per cell.
|
|
15
19
|
*/
|
|
16
|
-
export interface
|
|
17
|
-
|
|
18
|
-
readonly
|
|
20
|
+
export interface SqlColumnRef {
|
|
21
|
+
readonly table: string;
|
|
22
|
+
readonly name: string;
|
|
23
|
+
}
|
|
19
24
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
/**
|
|
26
|
+
* SQL-family per-call context. Extends the framework {@link CodecCallContext} (which carries `signal` only) with `column?: SqlColumnRef`, populated on **decode** call sites that can resolve a single underlying column ref. Encode call sites currently leave `column` undefined (encode-time column context is the middleware's domain).
|
|
27
|
+
*
|
|
28
|
+
* SQL codec authors writing codec methods observe this type via {@link SqlCodec}. The framework codec dispatch surface (and Mongo) sees only the base `CodecCallContext`.
|
|
29
|
+
*/
|
|
30
|
+
export interface SqlCodecCallContext extends CodecCallContext {
|
|
31
|
+
readonly column?: SqlColumnRef;
|
|
32
|
+
}
|
|
25
33
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
/**
|
|
35
|
+
* SQL-family per-instance context. Extends the framework {@link CodecInstanceContext} (`name` only) with `usedAt`, the set of `(table, column)` pairs the resolved codec serves.
|
|
36
|
+
*
|
|
37
|
+
* - For `typeRef` columns sharing one named `storage.types` instance, the array lists every referencing column — a column-scoped stateful codec (e.g. encryption) can derive aggregated per-instance state across all the columns sharing the named instance.
|
|
38
|
+
* - For inline-`typeParams` columns, the array has exactly one entry — the column that owns the inline params.
|
|
39
|
+
* - For shared non-parameterized codecs, the array carries one representative entry (the column that triggered materialization); the codec is shared across every column with that codec id, so the `usedAt` is informational only.
|
|
40
|
+
*
|
|
41
|
+
* SQL extensions consuming `usedAt` (e.g. column-scoped state derivation) type their factory parameter as `SqlCodecInstanceContext`. Extensions that don't read `usedAt` type their factory parameter as the family-agnostic {@link CodecInstanceContext} — a `SqlCodecInstanceContext` is structurally assignable to the base.
|
|
42
|
+
*/
|
|
43
|
+
export interface SqlCodecInstanceContext extends CodecInstanceContext {
|
|
44
|
+
readonly usedAt: ReadonlyArray<{ readonly table: string; readonly column: string }>;
|
|
32
45
|
}
|
|
33
46
|
|
|
34
47
|
/**
|
|
35
|
-
* Codec metadata for database-specific type information.
|
|
36
|
-
* Used for schema introspection and verification.
|
|
48
|
+
* Codec metadata for database-specific type information. Used for schema introspection and verification.
|
|
37
49
|
*/
|
|
38
50
|
export interface CodecMeta {
|
|
39
51
|
readonly db?: {
|
|
@@ -46,379 +58,85 @@ export interface CodecMeta {
|
|
|
46
58
|
}
|
|
47
59
|
|
|
48
60
|
/**
|
|
49
|
-
* SQL codec
|
|
61
|
+
* SQL codec — extends the framework codec base by narrowing the per-call context to the SQL-family {@link SqlCodecCallContext} (adds `column?: SqlColumnRef`). TypeScript treats method-syntax declarations bivariantly, so the SQL narrowing is structurally compatible with the framework {@link BaseCodec} super-interface.
|
|
62
|
+
*
|
|
63
|
+
* Codec-id-keyed static metadata (`traits`, `targetTypes`, `meta`, `paramsSchema`, `renderOutputType`) lives on the unified {@link import('@prisma-next/framework-components/codec').CodecDescriptor} — the codec instance itself only carries `id` plus the four conversion methods.
|
|
50
64
|
*
|
|
51
|
-
*
|
|
52
|
-
* They provide deterministic conversion between database wire types and JS values,
|
|
53
|
-
* and between JS values and contract JSON.
|
|
65
|
+
* See `Codec` in `@prisma-next/framework-components/codec` for the codec contract that this interface extends.
|
|
54
66
|
*/
|
|
55
67
|
export interface Codec<
|
|
56
68
|
Id extends string = string,
|
|
57
69
|
TTraits extends readonly CodecTrait[] = readonly CodecTrait[],
|
|
58
70
|
TWire = unknown,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
readonly meta?: CodecMeta;
|
|
64
|
-
readonly paramsSchema?: Type<TParams>;
|
|
65
|
-
readonly init?: (params: TParams) => THelper;
|
|
71
|
+
TInput = unknown,
|
|
72
|
+
> extends BaseCodec<Id, TTraits, TWire, TInput> {
|
|
73
|
+
encode(value: TInput, ctx: SqlCodecCallContext): Promise<TWire>;
|
|
74
|
+
decode(wire: TWire, ctx: SqlCodecCallContext): Promise<TInput>;
|
|
66
75
|
}
|
|
67
76
|
|
|
68
77
|
/**
|
|
69
|
-
*
|
|
78
|
+
* Contract-bound codec registry.
|
|
70
79
|
*
|
|
71
|
-
* The
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*/
|
|
76
|
-
export interface CodecRegistry {
|
|
77
|
-
get(id: string): Codec<string> | undefined;
|
|
78
|
-
has(id: string): boolean;
|
|
79
|
-
getByScalar(scalar: string): readonly Codec<string>[];
|
|
80
|
-
getDefaultCodec(scalar: string): Codec<string> | undefined;
|
|
81
|
-
register(codec: Codec<string>): void;
|
|
82
|
-
/** Returns true if the codec with this ID has the given trait. */
|
|
83
|
-
hasTrait(codecId: string, trait: CodecTrait): boolean;
|
|
84
|
-
/** Returns all traits for a codec, or an empty array if not found. */
|
|
85
|
-
traitsOf(codecId: string): readonly CodecTrait[];
|
|
86
|
-
[Symbol.iterator](): Iterator<Codec<string>>;
|
|
87
|
-
values(): IterableIterator<Codec<string>>;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Implementation of CodecRegistry.
|
|
80
|
+
* The dispatch interface for encode/decode at runtime: built once at `ExecutionContext` construction time by walking the contract's `storage.tables[].columns[]` and resolving each column through its descriptor's factory (per-instance for parameterized columns; the cached shared codec for non-parameterized columns). The dispatch path calls `forColumn(table, column).encode/decode(...)` and doesn't know whether the codec
|
|
81
|
+
* is parameterized.
|
|
82
|
+
*
|
|
83
|
+
* `forCodecId(codecId)` is the refs-less fallback. Every column-bound `ParamRef` carries `refs: { table; column }` and the builder-pipeline `validateParamRefRefs` pass enforces refs on every parameterized `ParamRef` before encode runs, so this fallback is only reached for non-parameterized codec ids.
|
|
92
84
|
*/
|
|
93
|
-
|
|
94
|
-
private readonly _byId = new Map<string, Codec<string>>();
|
|
95
|
-
private readonly _byScalar = new Map<string, Codec<string>[]>();
|
|
96
|
-
|
|
85
|
+
export interface ContractCodecRegistry {
|
|
97
86
|
/**
|
|
98
|
-
*
|
|
99
|
-
* Example: registry.get('pg/text@1')
|
|
87
|
+
* Resolve the codec for `(table, column)`. Returns the per-instance parameterized codec for parameterized columns, the shared codec for non-parameterized columns, or `undefined` if the column is unknown or the codec isn't registered.
|
|
100
88
|
*/
|
|
101
|
-
|
|
102
|
-
return this._byId.get(id);
|
|
103
|
-
}
|
|
89
|
+
forColumn(table: string, column: string): Codec | undefined;
|
|
104
90
|
|
|
105
91
|
/**
|
|
106
|
-
*
|
|
92
|
+
* Resolve a codec by id. For non-parameterized codecs this returns the canonical shared instance materialized once at context construction; for parameterized codecs it returns the column-resolved instance when a single column declares the codec id, or the `factory(undefined)` representative when the descriptor's factory is parameter-tolerant. Used by refs-less call sites; the validator pass guarantees the call site's
|
|
93
|
+
* `codecId` is non-parameterized (or parameter-tolerant) at this boundary.
|
|
107
94
|
*/
|
|
108
|
-
|
|
109
|
-
return this._byId.has(id);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Get all codecs that handle a given scalar type.
|
|
114
|
-
* Returns an empty frozen array if no codecs are found.
|
|
115
|
-
* Example: registry.getByScalar('text') → [codec1, codec2, ...]
|
|
116
|
-
*/
|
|
117
|
-
getByScalar(scalar: string): readonly Codec<string>[] {
|
|
118
|
-
return this._byScalar.get(scalar) ?? Object.freeze([]);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Get the default codec for a scalar type (first registered codec).
|
|
123
|
-
* Returns undefined if no codec handles this scalar type.
|
|
124
|
-
*/
|
|
125
|
-
getDefaultCodec(scalar: string): Codec<string> | undefined {
|
|
126
|
-
const _codecs = this._byScalar.get(scalar);
|
|
127
|
-
return _codecs?.[0];
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Register a codec in the registry.
|
|
132
|
-
* Throws an error if a codec with the same ID is already registered.
|
|
133
|
-
*
|
|
134
|
-
* @param codec - The codec to register
|
|
135
|
-
* @throws Error if a codec with the same ID already exists
|
|
136
|
-
*/
|
|
137
|
-
register(codec: Codec<string>): void {
|
|
138
|
-
if (this._byId.has(codec.id)) {
|
|
139
|
-
throw new Error(`Codec with ID '${codec.id}' is already registered`);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
this._byId.set(codec.id, codec);
|
|
143
|
-
|
|
144
|
-
// Update byScalar mapping
|
|
145
|
-
for (const scalarType of codec.targetTypes) {
|
|
146
|
-
const existing = this._byScalar.get(scalarType);
|
|
147
|
-
if (existing) {
|
|
148
|
-
existing.push(codec);
|
|
149
|
-
} else {
|
|
150
|
-
this._byScalar.set(scalarType, [codec]);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
hasTrait(codecId: string, trait: CodecTrait): boolean {
|
|
156
|
-
const codec = this._byId.get(codecId);
|
|
157
|
-
return codec?.traits?.includes(trait) ?? false;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
traitsOf(codecId: string): readonly CodecTrait[] {
|
|
161
|
-
return this._byId.get(codecId)?.traits ?? [];
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Returns an iterator over all registered codecs.
|
|
166
|
-
* Useful for iterating through codecs from another registry.
|
|
167
|
-
*/
|
|
168
|
-
*[Symbol.iterator](): Iterator<Codec<string>> {
|
|
169
|
-
for (const codec of this._byId.values()) {
|
|
170
|
-
yield codec;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Returns an iterable of all registered codecs.
|
|
176
|
-
*/
|
|
177
|
-
values(): IterableIterator<Codec<string>> {
|
|
178
|
-
return this._byId.values();
|
|
179
|
-
}
|
|
95
|
+
forCodecId(codecId: string): Codec | undefined;
|
|
180
96
|
}
|
|
181
97
|
|
|
182
98
|
/**
|
|
183
|
-
*
|
|
184
|
-
*
|
|
99
|
+
* Variance-erased descriptor type used for heterogeneous storage in collection containers and on the unified contributor `codecs:` slot. The descriptor's `factory` and `renderOutputType` are contravariant in `P`, so descriptors with different params shapes are not in a subtype relationship; collecting them into one container needs an explicit variance erasure rather than `CodecDescriptor<unknown>` (which is the
|
|
100
|
+
* narrowest, not the widest, of the family).
|
|
185
101
|
*/
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
const TTraits extends readonly CodecTrait[],
|
|
189
|
-
TWire,
|
|
190
|
-
TJs,
|
|
191
|
-
TParams = Record<string, unknown>,
|
|
192
|
-
THelper = unknown,
|
|
193
|
-
>(config: {
|
|
194
|
-
typeId: Id;
|
|
195
|
-
targetTypes: readonly string[];
|
|
196
|
-
encode: (value: TJs) => TWire;
|
|
197
|
-
decode: (wire: TWire) => TJs;
|
|
198
|
-
encodeJson?: (value: TJs) => JsonValue;
|
|
199
|
-
decodeJson?: (json: JsonValue) => TJs;
|
|
200
|
-
meta?: CodecMeta;
|
|
201
|
-
paramsSchema?: Type<TParams>;
|
|
202
|
-
init?: (params: TParams) => THelper;
|
|
203
|
-
traits?: TTraits;
|
|
204
|
-
renderOutputType?: (typeParams: Record<string, unknown>) => string | undefined;
|
|
205
|
-
}): Codec<Id, TTraits, TWire, TJs, TParams, THelper> {
|
|
206
|
-
const identity = (v: unknown) => v;
|
|
207
|
-
return {
|
|
208
|
-
id: config.typeId,
|
|
209
|
-
targetTypes: config.targetTypes,
|
|
210
|
-
...ifDefined('meta', config.meta),
|
|
211
|
-
...ifDefined('paramsSchema', config.paramsSchema),
|
|
212
|
-
...ifDefined('init', config.init),
|
|
213
|
-
...ifDefined(
|
|
214
|
-
'traits',
|
|
215
|
-
config.traits ? (Object.freeze([...config.traits]) as TTraits) : undefined,
|
|
216
|
-
),
|
|
217
|
-
...ifDefined('renderOutputType', config.renderOutputType),
|
|
218
|
-
encode: config.encode,
|
|
219
|
-
decode: config.decode,
|
|
220
|
-
encodeJson: (config.encodeJson ?? identity) as (value: TJs) => JsonValue,
|
|
221
|
-
decodeJson: (config.decodeJson ?? identity) as (json: JsonValue) => TJs,
|
|
222
|
-
};
|
|
223
|
-
}
|
|
102
|
+
// biome-ignore lint/suspicious/noExplicitAny: descriptor variance erasure — `P` is contravariant on the factory and renderOutputType slots, so heterogeneous descriptor storage cannot use `unknown`.
|
|
103
|
+
export type AnyCodecDescriptor = CodecDescriptor<any>;
|
|
224
104
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
*/
|
|
228
|
-
export type CodecId<T> =
|
|
229
|
-
T extends Codec<infer Id> ? Id : T extends { readonly id: infer Id } ? Id : never;
|
|
230
|
-
export type CodecInput<T> =
|
|
231
|
-
T extends Codec<string, readonly CodecTrait[], unknown, infer JsT> ? JsT : never;
|
|
232
|
-
export type CodecOutput<T> =
|
|
233
|
-
T extends Codec<string, readonly CodecTrait[], unknown, infer JsT> ? JsT : never;
|
|
234
|
-
export type CodecTraits<T> =
|
|
235
|
-
T extends Codec<string, infer TTraits> ? TTraits[number] & CodecTrait : never;
|
|
105
|
+
type DescriptorResolvedCodec<D> =
|
|
106
|
+
D extends CodecDescriptor<infer _P> ? ReturnType<ReturnType<D['factory']>> : never;
|
|
236
107
|
|
|
237
|
-
|
|
238
|
-
* Type helper to extract codec types from builder instance.
|
|
239
|
-
*/
|
|
240
|
-
export type ExtractCodecTypes<
|
|
241
|
-
ScalarNames extends { readonly [K in keyof ScalarNames]: Codec<string> } = Record<never, never>,
|
|
242
|
-
> = {
|
|
243
|
-
readonly [K in keyof ScalarNames as ScalarNames[K] extends Codec<infer Id> ? Id : never]: {
|
|
244
|
-
readonly input: CodecInput<ScalarNames[K]>;
|
|
245
|
-
readonly output: CodecOutput<ScalarNames[K]>;
|
|
246
|
-
readonly traits: CodecTraits<ScalarNames[K]>;
|
|
247
|
-
};
|
|
248
|
-
};
|
|
108
|
+
export type DescriptorCodecId<D> = D extends AnyCodecDescriptor ? D['codecId'] : never;
|
|
249
109
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
* we extract it by creating a mapped type that uses the Id as both key and value,
|
|
255
|
-
* then extract the value type. This preserves literal types.
|
|
256
|
-
*/
|
|
257
|
-
export type ExtractDataTypes<
|
|
258
|
-
ScalarNames extends { readonly [K in keyof ScalarNames]: Codec<string> },
|
|
259
|
-
> = {
|
|
260
|
-
readonly [K in keyof ScalarNames]: {
|
|
261
|
-
readonly [Id in keyof ExtractCodecTypes<Record<K, ScalarNames[K]>>]: Id;
|
|
262
|
-
}[keyof ExtractCodecTypes<Record<K, ScalarNames[K]>>];
|
|
263
|
-
};
|
|
110
|
+
export type DescriptorCodecInput<D> =
|
|
111
|
+
DescriptorResolvedCodec<D> extends BaseCodec<string, readonly CodecTrait[], unknown, infer In>
|
|
112
|
+
? In
|
|
113
|
+
: never;
|
|
264
114
|
|
|
265
115
|
/**
|
|
266
|
-
*
|
|
116
|
+
* Resolve the trait union for a descriptor `D`.
|
|
117
|
+
*
|
|
118
|
+
* Reads `traits` directly off the descriptor — concrete descriptor classes declare `override readonly traits = [...] as const`, which preserves the literal trait tuple at the descriptor type. Reading from the resolved codec instance (`CodecImpl<…, TTraits, …>`) would lose the literal because `Codec` carries `TTraits` only on its optional phantom slot (`readonly __codecTraits?: TTraits`); codecs extending `CodecImpl`
|
|
119
|
+
* have no required structural site that pins `TTraits`, so a descriptor-keyed extractor reading from the codec instance would widen to the broad `CodecTrait` union.
|
|
267
120
|
*/
|
|
268
|
-
export
|
|
269
|
-
|
|
270
|
-
> {
|
|
271
|
-
readonly CodecTypes: ExtractCodecTypes<ScalarNames>;
|
|
272
|
-
|
|
273
|
-
add<ScalarName extends string, CodecImpl extends Codec<string>>(
|
|
274
|
-
scalarName: ScalarName,
|
|
275
|
-
codecImpl: CodecImpl,
|
|
276
|
-
): CodecDefBuilder<
|
|
277
|
-
O.Overwrite<ScalarNames, Record<ScalarName, CodecImpl>> & Record<ScalarName, CodecImpl>
|
|
278
|
-
>;
|
|
279
|
-
|
|
280
|
-
readonly codecDefinitions: {
|
|
281
|
-
readonly [K in keyof ScalarNames]: {
|
|
282
|
-
readonly typeId: ScalarNames[K] extends Codec<infer Id extends string> ? Id : never;
|
|
283
|
-
readonly scalar: K;
|
|
284
|
-
readonly codec: ScalarNames[K];
|
|
285
|
-
readonly input: CodecInput<ScalarNames[K]>;
|
|
286
|
-
readonly output: CodecOutput<ScalarNames[K]>;
|
|
287
|
-
readonly jsType: CodecOutput<ScalarNames[K]>;
|
|
288
|
-
};
|
|
289
|
-
};
|
|
290
|
-
|
|
291
|
-
readonly dataTypes: {
|
|
292
|
-
readonly [K in keyof ScalarNames]: {
|
|
293
|
-
readonly [Id in keyof ExtractCodecTypes<Record<K, ScalarNames[K]>>]: Id;
|
|
294
|
-
}[keyof ExtractCodecTypes<Record<K, ScalarNames[K]>>];
|
|
295
|
-
};
|
|
121
|
+
export type DescriptorCodecTraits<D> = D extends {
|
|
122
|
+
readonly traits: infer TTraits extends readonly CodecTrait[];
|
|
296
123
|
}
|
|
124
|
+
? TTraits[number] & CodecTrait
|
|
125
|
+
: never;
|
|
297
126
|
|
|
298
127
|
/**
|
|
299
|
-
*
|
|
128
|
+
* Project a record of {@link AnyCodecDescriptor}s keyed by scalar name onto the codec-id-keyed `CodecTypes` shape consumed by emit and no-emit type pipelines (`{ readonly [codecId]: { input; output; traits } }`).
|
|
129
|
+
*
|
|
130
|
+
* Canonical extractor for the descriptor-keyed type pipeline; the legacy instance-keyed extractor and its `mkCodec`-bound builder retired alongside the carrier deletion.
|
|
300
131
|
*/
|
|
301
|
-
|
|
302
|
-
ScalarNames extends {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
readonly [K
|
|
310
|
-
readonly [Id in keyof ExtractCodecTypes<Record<K, ScalarNames[K]>>]: Id;
|
|
311
|
-
}[keyof ExtractCodecTypes<Record<K, ScalarNames[K]>>];
|
|
132
|
+
export type ExtractCodecTypes<
|
|
133
|
+
ScalarNames extends {
|
|
134
|
+
readonly [K in keyof ScalarNames]: AnyCodecDescriptor;
|
|
135
|
+
} = Record<never, never>,
|
|
136
|
+
> = {
|
|
137
|
+
readonly [K in keyof ScalarNames as DescriptorCodecId<ScalarNames[K]>]: {
|
|
138
|
+
readonly input: DescriptorCodecInput<ScalarNames[K]>;
|
|
139
|
+
readonly output: DescriptorCodecInput<ScalarNames[K]>;
|
|
140
|
+
readonly traits: DescriptorCodecTraits<ScalarNames[K]>;
|
|
312
141
|
};
|
|
313
|
-
|
|
314
|
-
constructor(codecs: ScalarNames) {
|
|
315
|
-
this._codecs = codecs;
|
|
316
|
-
|
|
317
|
-
// Populate CodecTypes from codecs
|
|
318
|
-
const codecTypes: Record<
|
|
319
|
-
string,
|
|
320
|
-
{ readonly input: unknown; readonly output: unknown; readonly traits: unknown }
|
|
321
|
-
> = {};
|
|
322
|
-
for (const [, codecImpl] of Object.entries(this._codecs)) {
|
|
323
|
-
const codecImplTyped = codecImpl as Codec<string>;
|
|
324
|
-
codecTypes[codecImplTyped.id] = {
|
|
325
|
-
input: undefined as unknown as CodecInput<typeof codecImplTyped>,
|
|
326
|
-
output: undefined as unknown as CodecOutput<typeof codecImplTyped>,
|
|
327
|
-
traits: undefined as unknown as CodecTraits<typeof codecImplTyped>,
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
this.CodecTypes = codecTypes as ExtractCodecTypes<ScalarNames>;
|
|
331
|
-
|
|
332
|
-
// Populate dataTypes from codecs - extract id property from each codec
|
|
333
|
-
// Build object preserving keys from ScalarNames
|
|
334
|
-
// Type assertion is safe because we know ScalarNames structure matches the return type
|
|
335
|
-
// biome-ignore lint/suspicious/noExplicitAny: dynamic codec mapping requires any
|
|
336
|
-
const dataTypes = {} as any;
|
|
337
|
-
for (const key in this._codecs) {
|
|
338
|
-
if (Object.hasOwn(this._codecs, key)) {
|
|
339
|
-
const codec = this._codecs[key] as Codec<string>;
|
|
340
|
-
dataTypes[key] = codec.id;
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
this.dataTypes = dataTypes as {
|
|
344
|
-
readonly [K in keyof ScalarNames]: {
|
|
345
|
-
readonly [Id in keyof ExtractCodecTypes<Record<K, ScalarNames[K]>>]: Id;
|
|
346
|
-
}[keyof ExtractCodecTypes<Record<K, ScalarNames[K]>>];
|
|
347
|
-
};
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
add<ScalarName extends string, CodecImpl extends Codec<string>>(
|
|
351
|
-
scalarName: ScalarName,
|
|
352
|
-
codecImpl: CodecImpl,
|
|
353
|
-
): CodecDefBuilder<
|
|
354
|
-
O.Overwrite<ScalarNames, Record<ScalarName, CodecImpl>> & Record<ScalarName, CodecImpl>
|
|
355
|
-
> {
|
|
356
|
-
return new CodecDefBuilderImpl({
|
|
357
|
-
...this._codecs,
|
|
358
|
-
[scalarName]: codecImpl,
|
|
359
|
-
} as O.Overwrite<ScalarNames, Record<ScalarName, CodecImpl>> & Record<ScalarName, CodecImpl>);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
/**
|
|
363
|
-
* Derive codecDefinitions structure.
|
|
364
|
-
*/
|
|
365
|
-
get codecDefinitions(): {
|
|
366
|
-
readonly [K in keyof ScalarNames]: {
|
|
367
|
-
readonly typeId: ScalarNames[K] extends Codec<infer Id> ? Id : never;
|
|
368
|
-
readonly scalar: K;
|
|
369
|
-
readonly codec: ScalarNames[K];
|
|
370
|
-
readonly input: CodecInput<ScalarNames[K]>;
|
|
371
|
-
readonly output: CodecOutput<ScalarNames[K]>;
|
|
372
|
-
readonly jsType: CodecOutput<ScalarNames[K]>;
|
|
373
|
-
};
|
|
374
|
-
} {
|
|
375
|
-
const result: Record<
|
|
376
|
-
string,
|
|
377
|
-
{
|
|
378
|
-
typeId: string;
|
|
379
|
-
scalar: string;
|
|
380
|
-
codec: Codec;
|
|
381
|
-
input: unknown;
|
|
382
|
-
output: unknown;
|
|
383
|
-
jsType: unknown;
|
|
384
|
-
}
|
|
385
|
-
> = {};
|
|
386
|
-
|
|
387
|
-
for (const [scalarName, codecImpl] of Object.entries(this._codecs)) {
|
|
388
|
-
const codec = codecImpl as Codec<string>;
|
|
389
|
-
result[scalarName] = {
|
|
390
|
-
typeId: codec.id,
|
|
391
|
-
scalar: scalarName,
|
|
392
|
-
codec: codec,
|
|
393
|
-
input: undefined as unknown as CodecInput<typeof codec>,
|
|
394
|
-
output: undefined as unknown as CodecOutput<typeof codec>,
|
|
395
|
-
jsType: undefined as unknown as CodecOutput<typeof codec>,
|
|
396
|
-
};
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
return result as {
|
|
400
|
-
readonly [K in keyof ScalarNames]: {
|
|
401
|
-
readonly typeId: ScalarNames[K] extends Codec<infer Id extends string> ? Id : never;
|
|
402
|
-
readonly scalar: K;
|
|
403
|
-
readonly codec: ScalarNames[K];
|
|
404
|
-
readonly input: CodecInput<ScalarNames[K]>;
|
|
405
|
-
readonly output: CodecOutput<ScalarNames[K]>;
|
|
406
|
-
readonly jsType: CodecOutput<ScalarNames[K]>;
|
|
407
|
-
};
|
|
408
|
-
};
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
/**
|
|
413
|
-
* Create a new codec registry.
|
|
414
|
-
*/
|
|
415
|
-
export function createCodecRegistry(): CodecRegistry {
|
|
416
|
-
return new CodecRegistryImpl();
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
/**
|
|
420
|
-
* Create a new codec definition builder.
|
|
421
|
-
*/
|
|
422
|
-
export function defineCodecs(): CodecDefBuilder<Record<never, never>> {
|
|
423
|
-
return new CodecDefBuilderImpl({});
|
|
424
|
-
}
|
|
142
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared encode/decode/render constants and codec id literals for the six SQL base codecs (`sql/char@1`, `sql/varchar@1`, `sql/int@1`, `sql/float@1`, `sql/text@1`, `sql/timestamp@1`).
|
|
3
|
+
*
|
|
4
|
+
* The codec implementations live in `sql-codecs.ts` (TML-2357). This module retains only the conversion helpers + emit-path renderers the codec methods compose with — keeping a single source of truth for non-trivial conversions while the codec methods provide the framework-required `Promise<…>` boundary.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { JsonValue } from '@prisma-next/contract/types';
|
|
8
|
+
|
|
9
|
+
export const SQL_CHAR_CODEC_ID = 'sql/char@1' as const;
|
|
10
|
+
export const SQL_VARCHAR_CODEC_ID = 'sql/varchar@1' as const;
|
|
11
|
+
export const SQL_INT_CODEC_ID = 'sql/int@1' as const;
|
|
12
|
+
export const SQL_FLOAT_CODEC_ID = 'sql/float@1' as const;
|
|
13
|
+
export const SQL_TEXT_CODEC_ID = 'sql/text@1' as const;
|
|
14
|
+
export const SQL_TIMESTAMP_CODEC_ID = 'sql/timestamp@1' as const;
|
|
15
|
+
|
|
16
|
+
export const sqlCharEncode = (value: string): string => value;
|
|
17
|
+
export const sqlCharDecode = (wire: string): string => wire.trimEnd();
|
|
18
|
+
export const sqlCharRenderOutputType = (typeParams: { readonly length?: number }) => {
|
|
19
|
+
const length = typeParams.length;
|
|
20
|
+
if (length === undefined) return undefined;
|
|
21
|
+
if (typeof length !== 'number' || !Number.isFinite(length) || !Number.isInteger(length)) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
`renderOutputType: expected integer "length" in typeParams for Char, got ${String(length)}`,
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
return `Char<${length}>`;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const sqlVarcharEncode = (value: string): string => value;
|
|
30
|
+
export const sqlVarcharDecode = (wire: string): string => wire;
|
|
31
|
+
export const sqlVarcharRenderOutputType = (typeParams: { readonly length?: number }) => {
|
|
32
|
+
const length = typeParams.length;
|
|
33
|
+
if (length === undefined) return undefined;
|
|
34
|
+
if (typeof length !== 'number' || !Number.isFinite(length) || !Number.isInteger(length)) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`renderOutputType: expected integer "length" in typeParams for Varchar, got ${String(length)}`,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
return `Varchar<${length}>`;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const sqlIntEncode = (value: number): number => value;
|
|
43
|
+
export const sqlIntDecode = (wire: number): number => wire;
|
|
44
|
+
|
|
45
|
+
export const sqlFloatEncode = (value: number): number => value;
|
|
46
|
+
export const sqlFloatDecode = (wire: number): number => wire;
|
|
47
|
+
|
|
48
|
+
export const sqlTextEncode = (value: string): string => value;
|
|
49
|
+
export const sqlTextDecode = (wire: string): string => wire;
|
|
50
|
+
|
|
51
|
+
export const sqlTimestampEncode = (value: Date): Date => value;
|
|
52
|
+
export const sqlTimestampDecode = (wire: Date): Date => wire;
|
|
53
|
+
export const sqlTimestampEncodeJson = (value: Date): JsonValue => value.toISOString();
|
|
54
|
+
export const sqlTimestampDecodeJson = (json: JsonValue): Date => {
|
|
55
|
+
if (typeof json !== 'string') {
|
|
56
|
+
throw new Error(`Expected ISO date string for sql/timestamp@1, got ${typeof json}`);
|
|
57
|
+
}
|
|
58
|
+
const date = new Date(json);
|
|
59
|
+
if (Number.isNaN(date.getTime())) {
|
|
60
|
+
throw new Error(`Invalid ISO date string for sql/timestamp@1: ${json}`);
|
|
61
|
+
}
|
|
62
|
+
return date;
|
|
63
|
+
};
|
|
64
|
+
export const sqlTimestampRenderOutputType = (typeParams: { readonly precision?: number }) => {
|
|
65
|
+
const precision = typeParams.precision;
|
|
66
|
+
if (precision === undefined) {
|
|
67
|
+
return 'Timestamp';
|
|
68
|
+
}
|
|
69
|
+
if (
|
|
70
|
+
typeof precision !== 'number' ||
|
|
71
|
+
!Number.isFinite(precision) ||
|
|
72
|
+
!Number.isInteger(precision)
|
|
73
|
+
) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`renderOutputType: expected integer "precision" in typeParams for Timestamp, got ${String(precision)}`,
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
return `Timestamp<${precision}>`;
|
|
79
|
+
};
|