prisma-ltree 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +11 -11
  2. package/dist/{codec-types-ECy3aUGO.d.mts → codec-types-Ba5cQ3bc.d.mts} +19 -19
  3. package/dist/codec-types-Ba5cQ3bc.d.mts.map +1 -0
  4. package/dist/codec-types.d.mts +1 -1
  5. package/dist/column-types.d.mts.map +1 -1
  6. package/dist/column-types.mjs +3 -9
  7. package/dist/column-types.mjs.map +1 -1
  8. package/dist/constants-Dfknw-h5.mjs +9 -0
  9. package/dist/constants-Dfknw-h5.mjs.map +1 -0
  10. package/dist/control.d.mts.map +1 -1
  11. package/dist/control.mjs +250 -27
  12. package/dist/control.mjs.map +1 -1
  13. package/dist/{descriptor-meta-D2gu1NKP.mjs → descriptor-meta-Cc6tmP54.mjs} +106 -62
  14. package/dist/descriptor-meta-Cc6tmP54.mjs.map +1 -0
  15. package/dist/pack.d.mts +4 -14
  16. package/dist/pack.d.mts.map +1 -1
  17. package/dist/pack.mjs +1 -1
  18. package/dist/runtime.mjs +2 -1
  19. package/dist/runtime.mjs.map +1 -1
  20. package/package.json +11 -10
  21. package/src/contract.d.ts +109 -24
  22. package/src/contract.json +28 -5
  23. package/src/contract.ts +22 -10
  24. package/src/core/authoring.ts +2 -3
  25. package/src/core/codecs-validation.ts +31 -0
  26. package/src/core/codecs.ts +39 -76
  27. package/src/core/contract-space-constants.ts +11 -2
  28. package/src/core/data-types.ts +75 -0
  29. package/src/core/descriptor-meta.ts +12 -28
  30. package/src/exports/column-types.ts +0 -3
  31. package/src/exports/control.ts +36 -2
  32. package/src/exports/runtime.ts +1 -0
  33. package/dist/codec-types-ECy3aUGO.d.mts.map +0 -1
  34. package/dist/contract-space-constants-Da9EC668.mjs +0 -15
  35. package/dist/contract-space-constants-Da9EC668.mjs.map +0 -1
  36. package/dist/descriptor-meta-D2gu1NKP.mjs.map +0 -1
package/src/contract.d.ts CHANGED
@@ -1,46 +1,36 @@
1
1
  // ⚠️ GENERATED FILE - DO NOT EDIT
2
2
  // This file is automatically generated by 'prisma contract emit'.
3
3
  // To regenerate, run: prisma contract emit
4
- import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma/orm-postgres/adapter/operation-types';
5
4
  import type {
6
- Bit,
7
- Char,
8
5
  CodecTypes as PgTypes,
9
- Interval,
10
- JsonValue,
11
- Numeric,
12
- Time,
13
- TimeString,
14
- Timestamp,
15
- TimestampString,
16
- Timestamptz,
17
- TimestamptzString,
18
- Timetz,
19
- VarBit,
20
- Varchar,
6
+ ParameterizedOutputTypesForTypeParams as PgParameterizedOutputTypes,
21
7
  } from '@prisma/orm-postgres/target/codec-types';
8
+ import type { QueryOperationTypes as PgTargetQueryOps } from '@prisma/orm-postgres/target/operation-types';
22
9
 
23
10
  import type {
11
+ ColumnTypesFromStorage,
24
12
  ContractWithTypeMaps,
13
+ RelationKeys,
25
14
  TypeMaps as TypeMapsType,
26
15
  } from '@prisma/orm-postgres/family-contract/types';
27
16
  import type {
28
17
  Contract as ContractType,
29
18
  ExecutionHashBase,
19
+ FieldTypesFromDomain,
30
20
  NamespaceId,
31
21
  ProfileHashBase,
32
22
  StorageHashBase,
33
23
  } from '@prisma/orm-postgres/contract/types';
34
24
 
35
25
  export type StorageHash =
36
- StorageHashBase<'ca70941530ea2af306b38e95be66ab1deac79357bcfafb48302d07c8f2c9aa92'>;
26
+ StorageHashBase<'6db18d99f118ef4e7fd359cf8bc472b9335bf3edeeefa8a15ee11eaf7742f9fb'>;
37
27
  export type ExecutionHash = ExecutionHashBase<string>;
38
28
  export type ProfileHash =
39
29
  ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>;
40
30
 
41
31
  export type CodecTypes = PgTypes;
42
32
  export type LaneCodecTypes = CodecTypes;
43
- export type QueryOperationTypes = PgAdapterQueryOps<CodecTypes>;
33
+ export type QueryOperationTypes = PgTargetQueryOps<CodecTypes>;
44
34
  export type AggregateTypes = {
45
35
  readonly avg: {
46
36
  readonly byCodec: {
@@ -52,6 +42,10 @@ export type AggregateTypes = {
52
42
  readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true };
53
43
  readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true };
54
44
  readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true };
45
+ readonly 'pg/interval-string@1': {
46
+ readonly output: 'pg/interval-string@1';
47
+ readonly nullable: true;
48
+ };
55
49
  readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true };
56
50
  readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true };
57
51
  readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true };
@@ -101,8 +95,14 @@ export type AggregateTypes = {
101
95
  readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true };
102
96
  readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true };
103
97
  readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true };
98
+ readonly 'pg/interval-string@1': {
99
+ readonly output: 'pg/interval-string@1';
100
+ readonly nullable: true;
101
+ };
104
102
  readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true };
103
+ readonly 'pg/money@1': { readonly output: 'pg/money@1'; readonly nullable: true };
105
104
  readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true };
105
+ readonly 'pg/oid@1': { readonly output: 'pg/oid@1'; readonly nullable: true };
106
106
  readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true };
107
107
  readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true };
108
108
  readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true };
@@ -110,6 +110,14 @@ export type AggregateTypes = {
110
110
  readonly output: 'pg/time-temporal@1';
111
111
  readonly nullable: true;
112
112
  };
113
+ readonly 'pg/timestamp-date@1': {
114
+ readonly output: 'pg/timestamp-date@1';
115
+ readonly nullable: true;
116
+ };
117
+ readonly 'pg/timestamp-instant@1': {
118
+ readonly output: 'pg/timestamp-instant@1';
119
+ readonly nullable: true;
120
+ };
113
121
  readonly 'pg/timestamp-string@1': {
114
122
  readonly output: 'pg/timestamp-string@1';
115
123
  readonly nullable: true;
@@ -118,6 +126,10 @@ export type AggregateTypes = {
118
126
  readonly output: 'pg/timestamp-temporal@1';
119
127
  readonly nullable: true;
120
128
  };
129
+ readonly 'pg/timestamptz-date@1': {
130
+ readonly output: 'pg/timestamptz-date@1';
131
+ readonly nullable: true;
132
+ };
121
133
  readonly 'pg/timestamptz-string@1': {
122
134
  readonly output: 'pg/timestamptz-string@1';
123
135
  readonly nullable: true;
@@ -157,8 +169,14 @@ export type AggregateTypes = {
157
169
  readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true };
158
170
  readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true };
159
171
  readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true };
172
+ readonly 'pg/interval-string@1': {
173
+ readonly output: 'pg/interval-string@1';
174
+ readonly nullable: true;
175
+ };
160
176
  readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true };
177
+ readonly 'pg/money@1': { readonly output: 'pg/money@1'; readonly nullable: true };
161
178
  readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true };
179
+ readonly 'pg/oid@1': { readonly output: 'pg/oid@1'; readonly nullable: true };
162
180
  readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true };
163
181
  readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true };
164
182
  readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true };
@@ -166,6 +184,14 @@ export type AggregateTypes = {
166
184
  readonly output: 'pg/time-temporal@1';
167
185
  readonly nullable: true;
168
186
  };
187
+ readonly 'pg/timestamp-date@1': {
188
+ readonly output: 'pg/timestamp-date@1';
189
+ readonly nullable: true;
190
+ };
191
+ readonly 'pg/timestamp-instant@1': {
192
+ readonly output: 'pg/timestamp-instant@1';
193
+ readonly nullable: true;
194
+ };
169
195
  readonly 'pg/timestamp-string@1': {
170
196
  readonly output: 'pg/timestamp-string@1';
171
197
  readonly nullable: true;
@@ -174,6 +200,10 @@ export type AggregateTypes = {
174
200
  readonly output: 'pg/timestamp-temporal@1';
175
201
  readonly nullable: true;
176
202
  };
203
+ readonly 'pg/timestamptz-date@1': {
204
+ readonly output: 'pg/timestamptz-date@1';
205
+ readonly nullable: true;
206
+ };
177
207
  readonly 'pg/timestamptz-string@1': {
178
208
  readonly output: 'pg/timestamptz-string@1';
179
209
  readonly nullable: true;
@@ -205,7 +235,12 @@ export type AggregateTypes = {
205
235
  readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true };
206
236
  readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true };
207
237
  readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true };
238
+ readonly 'pg/interval-string@1': {
239
+ readonly output: 'pg/interval-string@1';
240
+ readonly nullable: true;
241
+ };
208
242
  readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true };
243
+ readonly 'pg/money@1': { readonly output: 'pg/money@1'; readonly nullable: true };
209
244
  readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true };
210
245
  readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true };
211
246
  readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true };
@@ -237,11 +272,47 @@ type DefaultLiteralValue<CodecId extends string, Encoded> = CodecId extends keyo
237
272
  ? Encoded
238
273
  : CodecTypes[CodecId]['json']
239
274
  : Encoded;
275
+ type ParameterizedOutputTypeFunctions = PgParameterizedOutputTypes;
276
+
277
+ type FieldTypeOverrides = {};
278
+ export type FieldOutputTypes = FieldTypesFromDomain<
279
+ ContractBase['domain']['namespaces'],
280
+ CodecTypes,
281
+ ParameterizedOutputTypeFunctions,
282
+ FieldTypeOverrides,
283
+ 'output',
284
+ ContractBase['storage']['namespaces']
285
+ >;
286
+ export type FieldInputTypes = FieldTypesFromDomain<
287
+ ContractBase['domain']['namespaces'],
288
+ CodecTypes,
289
+ ParameterizedOutputTypeFunctions,
290
+ FieldTypeOverrides,
291
+ 'input',
292
+ ContractBase['storage']['namespaces']
293
+ >;
294
+ type ColumnTypeOverrides = {};
295
+ export type StorageColumnTypes = ColumnTypesFromStorage<
296
+ ContractBase['storage'],
297
+ CodecTypes,
298
+ ParameterizedOutputTypeFunctions,
299
+ ColumnTypeOverrides,
300
+ 'output'
301
+ >;
302
+ export type StorageColumnInputTypes = ColumnTypesFromStorage<
303
+ ContractBase['storage'],
304
+ CodecTypes,
305
+ ParameterizedOutputTypeFunctions,
306
+ ColumnTypeOverrides,
307
+ 'input'
308
+ >;
309
+
310
+ export namespace Models {}
311
+
312
+ export declare const models: {
313
+ public: {};
314
+ };
240
315
 
241
- export type FieldOutputTypes = { readonly public: Record<string, never> };
242
- export type FieldInputTypes = { readonly public: Record<string, never> };
243
- export type StorageColumnTypes = { readonly public: {} };
244
- export type StorageColumnInputTypes = { readonly public: {} };
245
316
  export type TypeMaps = TypeMapsType<
246
317
  CodecTypes,
247
318
  QueryOperationTypes,
@@ -255,6 +326,11 @@ export type TypeMaps = TypeMapsType<
255
326
  type ContractBase = Omit<
256
327
  ContractType<{
257
328
  readonly namespaces: {
329
+ readonly __unbound__: {
330
+ readonly id: '__unbound__';
331
+ readonly kind: 'postgres-unbound-schema';
332
+ readonly entries: { readonly table: {} };
333
+ };
258
334
  readonly public: {
259
335
  readonly id: 'public';
260
336
  readonly kind: 'postgres-schema';
@@ -265,13 +341,13 @@ type ContractBase = Omit<
265
341
  readonly ltree: {
266
342
  readonly kind: 'codec-instance';
267
343
  readonly codecId: 'pg/ltree@1';
268
- readonly nativeType: 'ltree';
344
+ readonly dataType: 'ltree/ltree';
269
345
  readonly typeParams: Record<string, never>;
270
346
  };
271
347
  readonly ltreeArray: {
272
348
  readonly kind: 'codec-instance';
273
349
  readonly codecId: 'pg/ltree-array@1';
274
- readonly nativeType: 'ltree[]';
350
+ readonly dataType: 'ltree/ltree-array';
275
351
  readonly typeParams: Record<string, never>;
276
352
  };
277
353
  };
@@ -292,6 +368,8 @@ type ContractBase = Omit<
292
368
  readonly capabilities: {
293
369
  readonly postgres: {
294
370
  readonly distinctOn: true;
371
+ readonly forKeyShare: true;
372
+ readonly forNoKeyUpdate: true;
295
373
  readonly jsonAgg: true;
296
374
  readonly lateral: true;
297
375
  readonly limit: true;
@@ -302,14 +380,21 @@ type ContractBase = Omit<
302
380
  readonly checkConstraint: true;
303
381
  readonly defaultInInsert: true;
304
382
  readonly enums: true;
383
+ readonly forShare: true;
384
+ readonly forUpdate: true;
385
+ readonly insertOnConflictSkip: true;
386
+ readonly insertOnConflictTarget: true;
387
+ readonly insertOnConflictWithoutTarget: true;
305
388
  readonly lateral: true;
389
+ readonly lockNowait: true;
390
+ readonly lockOf: true;
391
+ readonly lockSkipLocked: true;
306
392
  readonly returning: true;
307
393
  readonly scalarList: true;
308
394
  };
309
395
  };
310
396
  readonly extensions: {};
311
397
  readonly meta: {};
312
-
313
398
  readonly profileHash: ProfileHash;
314
399
  };
315
400
 
package/src/contract.json CHANGED
@@ -13,6 +13,19 @@
13
13
  },
14
14
  "storage": {
15
15
  "namespaces": {
16
+ "__unbound__": {
17
+ "entries": {
18
+ "extension": {
19
+ "ltree": {
20
+ "kind": "extension",
21
+ "name": "ltree"
22
+ }
23
+ },
24
+ "table": {}
25
+ },
26
+ "id": "__unbound__",
27
+ "kind": "postgres-unbound-schema"
28
+ },
16
29
  "public": {
17
30
  "entries": {
18
31
  "table": {}
@@ -21,23 +34,25 @@
21
34
  "kind": "postgres-schema"
22
35
  }
23
36
  },
24
- "storageHash": "ca70941530ea2af306b38e95be66ab1deac79357bcfafb48302d07c8f2c9aa92",
37
+ "storageHash": "6db18d99f118ef4e7fd359cf8bc472b9335bf3edeeefa8a15ee11eaf7742f9fb",
25
38
  "types": {
26
39
  "ltree": {
27
40
  "codecId": "pg/ltree@1",
28
- "kind": "codec-instance",
29
- "nativeType": "ltree"
41
+ "dataType": "ltree/ltree",
42
+ "kind": "codec-instance"
30
43
  },
31
44
  "ltreeArray": {
32
45
  "codecId": "pg/ltree-array@1",
33
- "kind": "codec-instance",
34
- "nativeType": "ltree[]"
46
+ "dataType": "ltree/ltree-array",
47
+ "kind": "codec-instance"
35
48
  }
36
49
  }
37
50
  },
38
51
  "capabilities": {
39
52
  "postgres": {
40
53
  "distinctOn": true,
54
+ "forKeyShare": true,
55
+ "forNoKeyUpdate": true,
41
56
  "jsonAgg": true,
42
57
  "lateral": true,
43
58
  "limit": true,
@@ -48,7 +63,15 @@
48
63
  "checkConstraint": true,
49
64
  "defaultInInsert": true,
50
65
  "enums": true,
66
+ "forShare": true,
67
+ "forUpdate": true,
68
+ "insertOnConflictSkip": true,
69
+ "insertOnConflictTarget": true,
70
+ "insertOnConflictWithoutTarget": true,
51
71
  "lateral": true,
72
+ "lockNowait": true,
73
+ "lockOf": true,
74
+ "lockSkipLocked": true,
52
75
  "returning": true,
53
76
  "scalarList": true
54
77
  }
package/src/contract.ts CHANGED
@@ -1,27 +1,39 @@
1
- import { defineContract } from "@prisma/orm-postgres/contract-builder";
1
+ import { assembleDataTypes } from "@prisma/orm-framework/components/codec";
2
+ import { defineContract, extension, unbound } from "@prisma/orm-postgres/contract-builder";
3
+ import { assemblePostgresCodecRegistryWithBuiltins } from "@prisma/orm-target-postgres/target/codecs";
4
+ import { postgresDataTypes } from "@prisma/orm-target-postgres/target/data-types";
5
+ import type { Temporal } from "temporal-spec";
2
6
  import { LTREE_ARRAY_CODEC_ID, LTREE_CODEC_ID } from "./core/constants";
3
- import {
4
- LTREE_ARRAY_STORAGE_TYPE,
5
- LTREE_ARRAY_NATIVE_TYPE,
6
- LTREE_NATIVE_TYPE,
7
- } from "./core/contract-space-constants";
7
+ import { LTREE_ARRAY_STORAGE_TYPE, LTREE_NATIVE_TYPE } from "./core/contract-space-constants";
8
+ import { codecDescriptors } from "./core/codecs";
9
+ import { ltreeDataTypes } from "./core/data-types";
8
10
 
9
- export const contract = defineContract({}, () => ({
10
- types: {
11
+ export type { Temporal };
12
+
13
+ const dataTypeLookup = assembleDataTypes([
14
+ { id: "postgres", dataTypes: postgresDataTypes },
15
+ { id: "ltree", dataTypes: ltreeDataTypes },
16
+ ]).lookup;
17
+ const codecLookup = assemblePostgresCodecRegistryWithBuiltins(
18
+ [{ types: { codecTypes: { codecDescriptors: [...codecDescriptors] } } }],
19
+ dataTypeLookup,
20
+ );
21
+
22
+ export const contract = defineContract({ codecLookup, dataTypeLookup }, () => ({
23
+ databaseTypes: {
11
24
  [LTREE_NATIVE_TYPE]: {
12
25
  kind: "codec-instance",
13
26
  codecId: LTREE_CODEC_ID,
14
- nativeType: LTREE_NATIVE_TYPE,
15
27
  typeParams: {},
16
28
  },
17
29
  [LTREE_ARRAY_STORAGE_TYPE]: {
18
30
  kind: "codec-instance",
19
31
  codecId: LTREE_ARRAY_CODEC_ID,
20
- nativeType: LTREE_ARRAY_NATIVE_TYPE,
21
32
  typeParams: {},
22
33
  },
23
34
  },
24
35
  models: {},
36
+ entities: [extension("ltree", { namespace: unbound })],
25
37
  }));
26
38
 
27
39
  export default contract;
@@ -1,21 +1,20 @@
1
1
  import type { AuthoringTypeNamespace } from "@prisma/orm-framework/components/authoring";
2
2
  import { LTREE_ARRAY_CODEC_ID, LTREE_CODEC_ID } from "./constants";
3
- import { LTREE_ARRAY_NATIVE_TYPE, LTREE_NATIVE_TYPE } from "./contract-space-constants";
4
3
 
5
4
  export const ltreeAuthoringTypes = {
6
5
  ltree: {
7
6
  Ltree: {
8
7
  kind: "typeConstructor",
8
+ inferred: true,
9
9
  output: {
10
10
  codecId: LTREE_CODEC_ID,
11
- nativeType: LTREE_NATIVE_TYPE,
12
11
  },
13
12
  },
14
13
  LtreeArray: {
15
14
  kind: "typeConstructor",
15
+ inferred: true,
16
16
  output: {
17
17
  codecId: LTREE_ARRAY_CODEC_ID,
18
- nativeType: LTREE_ARRAY_NATIVE_TYPE,
19
18
  },
20
19
  },
21
20
  },
@@ -0,0 +1,31 @@
1
+ import { LTREE_MAX_LABEL_LENGTH, LTREE_MAX_LABELS } from "./constants";
2
+
3
+ const LABEL_PATTERN = /^[A-Za-z0-9_-]+$/;
4
+
5
+ export function assertValidLtree(value: unknown): asserts value is string {
6
+ if (typeof value !== "string") {
7
+ throw new Error("ltree value must be a string");
8
+ }
9
+ if (value.length === 0) {
10
+ throw new Error("ltree value must not be empty");
11
+ }
12
+ const labels = value.split(".");
13
+ if (labels.length > LTREE_MAX_LABELS) {
14
+ throw new Error(`ltree path exceeds max labels: got ${labels.length}, max ${LTREE_MAX_LABELS}`);
15
+ }
16
+ for (const label of labels) {
17
+ if (label.length === 0) {
18
+ throw new Error(`ltree label must not be empty in path "${value}"`);
19
+ }
20
+ if (label.length > LTREE_MAX_LABEL_LENGTH) {
21
+ throw new Error(
22
+ `ltree label exceeds max length: got ${label.length}, max ${LTREE_MAX_LABEL_LENGTH}`,
23
+ );
24
+ }
25
+ if (!LABEL_PATTERN.test(label)) {
26
+ throw new Error(
27
+ `ltree label "${label}" contains invalid characters; allowed: alphanumeric, underscore, hyphen`,
28
+ );
29
+ }
30
+ }
31
+ }
@@ -1,56 +1,24 @@
1
- import type { JsonValue } from "@prisma/orm-framework/contract/types";
2
1
  import {
2
+ type AnyCodecDescriptor,
3
3
  type CodecCallContext,
4
4
  CodecImpl,
5
5
  type CodecInstanceContext,
6
6
  type ColumnHelperFor,
7
7
  type ColumnHelperForStrict,
8
8
  column,
9
- voidParamsSchema,
9
+ type DataTypeValue,
10
10
  } from "@prisma/orm-framework/components/codec";
11
11
  import type { ExtractCodecTypes, ProjectionExpr } from "@prisma/orm-family-sql/relational-core/ast";
12
+ import { CastExpr } from "@prisma/orm-family-sql/relational-core/ast";
12
13
  import {
13
14
  definePostgresCodecs,
14
15
  PostgresCodecDescriptor,
15
16
  } from "@prisma/orm-target-postgres/target/codec-descriptor";
16
- import type { StandardSchemaV1 } from "@standard-schema/spec";
17
- import {
18
- LTREE_ARRAY_CODEC_ID,
19
- LTREE_CODEC_ID,
20
- LTREE_MAX_LABEL_LENGTH,
21
- LTREE_MAX_LABELS,
22
- } from "./constants";
23
- import { LTREE_ARRAY_NATIVE_TYPE, LTREE_NATIVE_TYPE } from "./contract-space-constants";
24
-
25
- const LABEL_PATTERN = /^[A-Za-z0-9_-]+$/;
17
+ import { assertValidLtree } from "./codecs-validation";
18
+ import { LTREE_ARRAY_CODEC_ID, LTREE_CODEC_ID } from "./constants";
19
+ import { ltreeArrayDataType, ltreeDataType } from "./data-types";
26
20
 
27
- export function assertValidLtree(value: unknown): asserts value is string {
28
- if (typeof value !== "string") {
29
- throw new Error("ltree value must be a string");
30
- }
31
- if (value.length === 0) {
32
- throw new Error("ltree value must not be empty");
33
- }
34
- const labels = value.split(".");
35
- if (labels.length > LTREE_MAX_LABELS) {
36
- throw new Error(`ltree path exceeds max labels: got ${labels.length}, max ${LTREE_MAX_LABELS}`);
37
- }
38
- for (const label of labels) {
39
- if (label.length === 0) {
40
- throw new Error(`ltree label must not be empty in path "${value}"`);
41
- }
42
- if (label.length > LTREE_MAX_LABEL_LENGTH) {
43
- throw new Error(
44
- `ltree label exceeds max length: got ${label.length}, max ${LTREE_MAX_LABEL_LENGTH}`,
45
- );
46
- }
47
- if (!LABEL_PATTERN.test(label)) {
48
- throw new Error(
49
- `ltree label "${label}" contains invalid characters; allowed: alphanumeric, underscore, hyphen`,
50
- );
51
- }
52
- }
53
- }
21
+ export { assertValidLtree } from "./codecs-validation";
54
22
 
55
23
  export class LtreeCodec extends CodecImpl<
56
24
  typeof LTREE_CODEC_ID,
@@ -58,38 +26,39 @@ export class LtreeCodec extends CodecImpl<
58
26
  string,
59
27
  string
60
28
  > {
61
- async encode(value: string, _ctx: CodecCallContext): Promise<string> {
29
+ constructor(descriptor: AnyCodecDescriptor) {
30
+ super(descriptor, ltreeDataType);
31
+ }
32
+
33
+ async toWire(value: string, _ctx: CodecCallContext): Promise<string> {
62
34
  assertValidLtree(value);
63
35
  return value;
64
36
  }
65
37
 
66
- async decode(wire: string, _ctx: CodecCallContext): Promise<string> {
38
+ async fromWire(wire: string, _ctx: CodecCallContext): Promise<string> {
67
39
  return wire;
68
40
  }
69
41
 
70
- encodeJson(value: string): JsonValue {
42
+ toDataTypeValue(value: string): DataTypeValue {
71
43
  assertValidLtree(value);
72
- return value;
44
+ return this.dataTypeValueOf(value);
73
45
  }
74
46
 
75
- decodeJson(json: JsonValue): string {
76
- assertValidLtree(json);
77
- return json;
47
+ fromDataTypeValue(value: DataTypeValue): string {
48
+ return value.value as string;
78
49
  }
79
50
  }
80
51
 
81
52
  export class LtreeDescriptor extends PostgresCodecDescriptor<void> {
82
- protected override nativeType(): string {
83
- return LTREE_NATIVE_TYPE;
84
- }
53
+ override readonly jsonType = "string" as const;
85
54
  protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr {
86
- // ltree's text form is already its canonical JSON string.
87
- return expression;
55
+ // JSON projections read through fromWire; cast so the wire value is text.
56
+ return CastExpr.as(expression, "text");
88
57
  }
58
+ override readonly dataType = ltreeDataType.id;
89
59
  override readonly codecId = LTREE_CODEC_ID;
90
60
  override readonly traits = ["equality", "order"] as const;
91
- override readonly targetTypes = [LTREE_NATIVE_TYPE] as const;
92
- override readonly paramsSchema: StandardSchemaV1<void> = voidParamsSchema;
61
+ override readonly paramsSchema = undefined;
93
62
  override renderOutputType(): string {
94
63
  return "string";
95
64
  }
@@ -101,8 +70,7 @@ export class LtreeDescriptor extends PostgresCodecDescriptor<void> {
101
70
 
102
71
  export const ltreeDescriptor = new LtreeDescriptor();
103
72
 
104
- export const ltree = () =>
105
- column(ltreeDescriptor.factory(), ltreeDescriptor.codecId, undefined, LTREE_NATIVE_TYPE);
73
+ export const ltree = () => column(ltreeDescriptor.factory(), ltreeDescriptor.codecId, undefined);
106
74
 
107
75
  ltree satisfies ColumnHelperFor<LtreeDescriptor>;
108
76
  ltree satisfies ColumnHelperForStrict<LtreeDescriptor>;
@@ -113,47 +81,47 @@ export class LtreeArrayCodec extends CodecImpl<
113
81
  readonly string[],
114
82
  readonly string[]
115
83
  > {
116
- async encode(value: readonly string[], _ctx: CodecCallContext): Promise<readonly string[]> {
84
+ constructor(descriptor: AnyCodecDescriptor) {
85
+ super(descriptor, ltreeArrayDataType);
86
+ }
87
+
88
+ async toWire(value: readonly string[], _ctx: CodecCallContext): Promise<readonly string[]> {
117
89
  for (const entry of value) {
118
90
  assertValidLtree(entry);
119
91
  }
120
92
  return value;
121
93
  }
122
94
 
123
- async decode(wire: readonly string[], _ctx: CodecCallContext): Promise<readonly string[]> {
95
+ async fromWire(wire: readonly string[], _ctx: CodecCallContext): Promise<readonly string[]> {
124
96
  return wire;
125
97
  }
126
98
 
127
- encodeJson(value: readonly string[]): JsonValue {
99
+ toDataTypeValue(value: readonly string[]): DataTypeValue {
128
100
  for (const entry of value) {
129
101
  assertValidLtree(entry);
130
102
  }
131
- return [...value];
103
+ return this.dataTypeValueOf([...value]);
132
104
  }
133
105
 
134
- decodeJson(json: JsonValue): readonly string[] {
106
+ fromDataTypeValue(value: DataTypeValue): readonly string[] {
107
+ const json = value.value;
135
108
  if (!Array.isArray(json)) {
136
109
  return [];
137
110
  }
138
- return json.map((entry) => {
139
- assertValidLtree(entry);
140
- return String(entry);
141
- });
111
+ return json.map((entry) => String(entry));
142
112
  }
143
113
  }
144
114
 
145
115
  export class LtreeArrayDescriptor extends PostgresCodecDescriptor<void> {
146
- protected override nativeType(): string {
147
- return LTREE_ARRAY_NATIVE_TYPE;
148
- }
116
+ override readonly jsonType = "array" as const;
149
117
  protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr {
150
- // ltree[] text form projects as a JSON string array already.
118
+ // ltree[] already projects as a JSON string array for fromWire.
151
119
  return expression;
152
120
  }
121
+ override readonly dataType = ltreeArrayDataType.id;
153
122
  override readonly codecId = LTREE_ARRAY_CODEC_ID;
154
123
  override readonly traits = ["equality"] as const;
155
- override readonly targetTypes = [LTREE_ARRAY_NATIVE_TYPE] as const;
156
- override readonly paramsSchema: StandardSchemaV1<void> = voidParamsSchema;
124
+ override readonly paramsSchema = undefined;
157
125
  override renderOutputType(): string {
158
126
  return "readonly string[]";
159
127
  }
@@ -166,12 +134,7 @@ export class LtreeArrayDescriptor extends PostgresCodecDescriptor<void> {
166
134
  export const ltreeArrayDescriptor = new LtreeArrayDescriptor();
167
135
 
168
136
  export const ltreeArray = () =>
169
- column(
170
- ltreeArrayDescriptor.factory(),
171
- ltreeArrayDescriptor.codecId,
172
- undefined,
173
- LTREE_ARRAY_NATIVE_TYPE,
174
- );
137
+ column(ltreeArrayDescriptor.factory(), ltreeArrayDescriptor.codecId, undefined);
175
138
 
176
139
  ltreeArray satisfies ColumnHelperFor<LtreeArrayDescriptor>;
177
140
  ltreeArray satisfies ColumnHelperForStrict<LtreeArrayDescriptor>;
@@ -2,13 +2,22 @@ export const LTREE_SPACE_ID = "ltree" as const;
2
2
 
3
3
  export const LTREE_NATIVE_TYPE = "ltree" as const;
4
4
 
5
- export const LTREE_ARRAY_NATIVE_TYPE = "ltree[]" as const;
6
-
7
5
  /** Contract storage.types key — must be a valid identifier for contract emit. */
8
6
  export const LTREE_ARRAY_STORAGE_TYPE = "ltreeArray" as const;
9
7
 
10
8
  export const LTREE_BASELINE_MIGRATION_NAME = "20260619T2142_install_ltree" as const;
11
9
 
10
+ export const LTREE_DECLARE_EXTENSION_MIGRATION_NAME =
11
+ "20261111T1905_declare_ltree_extension" as const;
12
+
13
+ export const LTREE_CREATE_EXTENSION_MIGRATION_NAME =
14
+ "20261111T1904_create_ltree_extension" as const;
15
+
16
+ export const LTREE_SEARCH_PATH_MIGRATION_NAME = "20261111T1908_find_ltree_on_search_path" as const;
17
+
18
+ export const LTREE_SEARCH_PATH_INSTALL_MIGRATION_NAME =
19
+ "20261111T1908_install_ltree_on_search_path" as const;
20
+
12
21
  export const LTREE_INVARIANTS = {
13
22
  installLtree: "ltree:install-ltree-v1",
14
23
  } as const;