@smonn/ids 0.15.0 → 1.0.0-rc.1
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 +3 -3
- package/dist/{adapter-types-7wWdELSh.mjs → adapter-types-CjzFNDcJ.mjs} +7 -2
- package/dist/adapter-types-CjzFNDcJ.mjs.map +1 -0
- package/dist/cli.mjs +30 -22
- package/dist/cli.mjs.map +1 -1
- package/dist/drizzle.d.mts +202 -3
- package/dist/drizzle.d.mts.map +1 -1
- package/dist/drizzle.mjs +245 -5
- package/dist/drizzle.mjs.map +1 -1
- package/dist/express.d.mts +44 -2
- package/dist/express.d.mts.map +1 -1
- package/dist/express.mjs +60 -2
- package/dist/express.mjs.map +1 -1
- package/dist/fastify.d.mts +49 -2
- package/dist/fastify.d.mts.map +1 -1
- package/dist/fastify.mjs +61 -2
- package/dist/fastify.mjs.map +1 -1
- package/dist/hono.d.mts +44 -2
- package/dist/hono.d.mts.map +1 -1
- package/dist/hono.mjs +54 -2
- package/dist/hono.mjs.map +1 -1
- package/dist/kysely.d.mts +103 -2
- package/dist/kysely.d.mts.map +1 -1
- package/dist/kysely.mjs +105 -2
- package/dist/kysely.mjs.map +1 -1
- package/dist/mikro-orm.d.mts +81 -3
- package/dist/mikro-orm.d.mts.map +1 -1
- package/dist/mikro-orm.mjs +86 -4
- package/dist/mikro-orm.mjs.map +1 -1
- package/dist/nestjs.mjs +1 -1
- package/dist/{opaque-COAcIIY4.mjs → opaque-Dle3CmSE.mjs} +18 -10
- package/dist/opaque-Dle3CmSE.mjs.map +1 -0
- package/dist/opaque.d.mts +16 -10
- package/dist/opaque.d.mts.map +1 -1
- package/dist/opaque.mjs +1 -1
- package/dist/prisma.d.mts +135 -3
- package/dist/prisma.d.mts.map +1 -1
- package/dist/prisma.mjs +141 -3
- package/dist/prisma.mjs.map +1 -1
- package/dist/typeorm.d.mts +84 -1
- package/dist/typeorm.d.mts.map +1 -1
- package/dist/typeorm.mjs +87 -2
- package/dist/typeorm.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/adapter-types-7wWdELSh.mjs.map +0 -1
- package/dist/opaque-COAcIIY4.mjs.map +0 -1
package/dist/prisma.d.mts
CHANGED
|
@@ -1,9 +1,29 @@
|
|
|
1
1
|
import { t as Id } from "./types-hGBnCpJj.mjs";
|
|
2
2
|
import { n as IdColumnCodec } from "./adapter-types-Bia_w9sg.mjs";
|
|
3
3
|
import { n as IdsErrorCode, r as isIdsError, t as IdsError } from "./error-CifcKKOG.mjs";
|
|
4
|
+
import { ModelQueryOptionsCb } from "@prisma/client/runtime/library";
|
|
4
5
|
|
|
5
6
|
//#region src/adapters/prisma.d.ts
|
|
6
7
|
/**
|
|
8
|
+
* Extension of {@link IdColumnCodec} that also exposes synchronous `generate()`.
|
|
9
|
+
* Required by {@link idField} so that {@link IdTransform.defaultQuery} can produce
|
|
10
|
+
* IDs at write time. Every full codec variant (Timestamp, Reverse Timestamp) satisfies
|
|
11
|
+
* this; async-generate codecs (Opaque, Signed, Wrapped, Digest) do not and are
|
|
12
|
+
* therefore unsupported by `defaultQuery`.
|
|
13
|
+
*/
|
|
14
|
+
type IdGeneratingCodec<Brand extends string> = IdColumnCodec<Brand> & {
|
|
15
|
+
generate(): Id<Brand>;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* The per-model object returned by {@link IdTransform.defaultQuery}, suitable for
|
|
19
|
+
* the model-level value inside a Prisma `$extends({ query: { modelName: … } })` block.
|
|
20
|
+
* Structurally identical to `{ [operation: string]: ModelQueryOptionsCb }` from
|
|
21
|
+
* `@prisma/client/runtime/library`.
|
|
22
|
+
*/
|
|
23
|
+
type IdQueryField = {
|
|
24
|
+
[operation: string]: ModelQueryOptionsCb;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
7
27
|
* Typed `$extends` result-component field definition produced by
|
|
8
28
|
* {@link IdTransform.computeField} — a `{ needs, compute }` pair whose `compute`
|
|
9
29
|
* return type is statically `Id<Brand>`, so the extended-client model field is
|
|
@@ -14,6 +34,15 @@ type IdComputeField<Brand extends string> = {
|
|
|
14
34
|
compute: (model: Record<string, unknown>) => Id<Brand>;
|
|
15
35
|
};
|
|
16
36
|
/**
|
|
37
|
+
* Typed `$extends` result-component field definition produced by
|
|
38
|
+
* {@link IdTransform.computeNullableField} — like {@link IdComputeField} but
|
|
39
|
+
* `compute` returns `Id<Brand> | null` for nullable columns.
|
|
40
|
+
*/
|
|
41
|
+
type NullableIdComputeField<Brand extends string> = {
|
|
42
|
+
needs: Record<string, boolean>;
|
|
43
|
+
compute: (model: Record<string, unknown>) => Id<Brand> | null;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
17
46
|
* Read/write transform pair and `$extends` result-component factory for
|
|
18
47
|
* integrating `Id<Brand>` with Prisma extensions.
|
|
19
48
|
*/
|
|
@@ -25,6 +54,11 @@ type IdTransform<Brand extends string> = {
|
|
|
25
54
|
*/
|
|
26
55
|
read(value: unknown): Id<Brand>;
|
|
27
56
|
/**
|
|
57
|
+
* Nullable read transform: returns `null` when `value` is `null` or `undefined`;
|
|
58
|
+
* otherwise delegates to {@link read}. Use for optional foreign keys.
|
|
59
|
+
*/
|
|
60
|
+
readNullable(value: unknown): Id<Brand> | null;
|
|
61
|
+
/**
|
|
28
62
|
* Write transform: passes `Id<Brand>` through as its canonical string form.
|
|
29
63
|
* `Id<Brand>` is already the canonical string, so this is an identity function
|
|
30
64
|
* at runtime.
|
|
@@ -51,16 +85,61 @@ type IdTransform<Brand extends string> = {
|
|
|
51
85
|
* ```
|
|
52
86
|
*/
|
|
53
87
|
computeField(fieldName: string): IdComputeField<Brand>;
|
|
88
|
+
/**
|
|
89
|
+
* Creates a `$extends` query-component model slice that auto-generates
|
|
90
|
+
* `Id<Brand>` values for `create`, `createMany`, and `upsert` operations
|
|
91
|
+
* when the field is absent, `undefined`, or `null` in `args.data` (or
|
|
92
|
+
* `args.create` for upsert). Explicitly supplied values are always passed
|
|
93
|
+
* through unchanged.
|
|
94
|
+
*
|
|
95
|
+
* @param fieldName - The model field to auto-generate (e.g. `"id"`).
|
|
96
|
+
* @returns An {@link IdQueryField} suitable for the model-level value inside
|
|
97
|
+
* a Prisma `$extends({ query: { modelName: … } })` block.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```ts
|
|
101
|
+
* const xprisma = prisma.$extends({
|
|
102
|
+
* query: { user: userIdField.defaultQuery("id") },
|
|
103
|
+
* result: { user: { id: userIdField.computeField("id") } },
|
|
104
|
+
* });
|
|
105
|
+
* // id is auto-filled on create, and typed as Id<"usr"> on read
|
|
106
|
+
* await xprisma.user.create({ data: { name: "Alice" } });
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
defaultQuery(fieldName: string): IdQueryField;
|
|
110
|
+
/**
|
|
111
|
+
* Like {@link computeField} but for nullable columns — `compute` returns
|
|
112
|
+
* `Id<Brand> | null` instead of `Id<Brand>`.
|
|
113
|
+
*
|
|
114
|
+
* @param fieldName - The nullable model field to read from.
|
|
115
|
+
* @returns A {@link NullableIdComputeField} whose `compute` returns `Id<Brand> | null`.
|
|
116
|
+
*/
|
|
117
|
+
computeNullableField(fieldName: string): NullableIdComputeField<Brand>;
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* The read/nullable-read/write surface returned by {@link nullableIdField} —
|
|
121
|
+
* mirrors the nullable methods of {@link IdTransform} but omits `defaultQuery`,
|
|
122
|
+
* `read`, and `computeField` since nullable FK columns neither auto-generate IDs
|
|
123
|
+
* nor require a non-null read path at the top level.
|
|
124
|
+
*/
|
|
125
|
+
type NullableIdTransform<Brand extends string> = {
|
|
126
|
+
readNullable(value: unknown): Id<Brand> | null;
|
|
127
|
+
write(value: Id<Brand>): string;
|
|
128
|
+
computeNullableField(fieldName: string): NullableIdComputeField<Brand>;
|
|
54
129
|
};
|
|
55
130
|
/**
|
|
56
131
|
* Creates a read/write transform pair for use with Prisma's `$extends` extension model.
|
|
57
132
|
*
|
|
58
|
-
*
|
|
133
|
+
* Requires a codec variant that exposes a synchronous `generate()` in addition to `safeParse` — see {@link IdGeneratingCodec}. Only the **Timestamp codec** and **Reverse Timestamp codec** qualify; Opaque, Signed, Wrapped, and Digest codecs cannot be passed to `idField()`.
|
|
59
134
|
*
|
|
60
135
|
* Use `computeField(fieldName)` to produce a typed `$extends` result-component
|
|
61
136
|
* field definition — the brand is carried through Prisma's type machinery
|
|
62
137
|
* automatically and no per-call-site cast is required.
|
|
63
138
|
*
|
|
139
|
+
* For codecs that do not expose a synchronous `generate()` (Opaque Timestamp,
|
|
140
|
+
* Signed Timestamp, Wrapped key, Digest), use {@link idFieldReadOnly} instead —
|
|
141
|
+
* it accepts any {@link IdColumnCodec} and omits `defaultQuery`.
|
|
142
|
+
*
|
|
64
143
|
* @example
|
|
65
144
|
* ```ts
|
|
66
145
|
* import { idField } from "@smonn/ids/prisma";
|
|
@@ -77,7 +156,60 @@ type IdTransform<Brand extends string> = {
|
|
|
77
156
|
* // xprisma.user.findUnique(…).id is typed as Id<"usr"> — no cast required
|
|
78
157
|
* ```
|
|
79
158
|
*/
|
|
80
|
-
declare function idField<Brand extends string>(codec:
|
|
159
|
+
declare function idField<Brand extends string>(codec: IdGeneratingCodec<Brand>): IdTransform<Brand>;
|
|
160
|
+
/**
|
|
161
|
+
* Standalone nullable counterpart of {@link idField} for Prisma adapter symmetry
|
|
162
|
+
* with the other ORM adapters ({@link nullableIdColumn} in Drizzle/Kysely,
|
|
163
|
+
* `nullableIdType` in MikroORM, `nullableIdTransformer` in TypeORM).
|
|
164
|
+
*
|
|
165
|
+
* Accepts any {@link IdColumnCodec} — no synchronous `generate()` required,
|
|
166
|
+
* because nullable FK columns do not auto-generate IDs.
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```ts
|
|
170
|
+
* import { nullableIdField } from "@smonn/ids/prisma";
|
|
171
|
+
* import { createTimestampId } from "@smonn/ids";
|
|
172
|
+
*
|
|
173
|
+
* const usr = createTimestampId("usr");
|
|
174
|
+
* const authorIdField = nullableIdField(usr);
|
|
175
|
+
*
|
|
176
|
+
* const xprisma = prisma.$extends({
|
|
177
|
+
* result: {
|
|
178
|
+
* post: { authorId: authorIdField.computeNullableField("authorId") },
|
|
179
|
+
* },
|
|
180
|
+
* });
|
|
181
|
+
* // xprisma.post.findUnique(…).authorId is typed as Id<"usr"> | null
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
184
|
+
declare function nullableIdField<Brand extends string>(codec: IdColumnCodec<Brand>): NullableIdTransform<Brand>;
|
|
185
|
+
/**
|
|
186
|
+
* Read-only sibling of {@link idField} for codec variants that do not expose a
|
|
187
|
+
* synchronous `generate()` — Opaque Timestamp, Signed Timestamp, Wrapped key,
|
|
188
|
+
* and Digest codecs all qualify.
|
|
189
|
+
*
|
|
190
|
+
* Accepts any {@link IdColumnCodec} (the wider constraint that only requires
|
|
191
|
+
* `safeParse`) and returns the full read/transform surface of {@link IdTransform}
|
|
192
|
+
* **minus `defaultQuery`**. Because `defaultQuery` is the only method that calls
|
|
193
|
+
* `generate()`, callers who only need the read path are not forced to provide a
|
|
194
|
+
* synchronous generator.
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```ts
|
|
198
|
+
* import { idFieldReadOnly } from "@smonn/ids/prisma";
|
|
199
|
+
* import { createOpaqueTimestampId } from "@smonn/ids/opaque";
|
|
200
|
+
*
|
|
201
|
+
* const inv = createOpaqueTimestampId("inv", { key });
|
|
202
|
+
* const invoiceIdField = idFieldReadOnly(inv);
|
|
203
|
+
*
|
|
204
|
+
* const xprisma = prisma.$extends({
|
|
205
|
+
* result: {
|
|
206
|
+
* invoice: { id: invoiceIdField.computeField("id") },
|
|
207
|
+
* },
|
|
208
|
+
* });
|
|
209
|
+
* // xprisma.invoice.findUnique(…).id is typed as Id<"inv"> — no cast required
|
|
210
|
+
* ```
|
|
211
|
+
*/
|
|
212
|
+
declare function idFieldReadOnly<Brand extends string>(codec: IdColumnCodec<Brand>): Omit<IdTransform<Brand>, "defaultQuery">;
|
|
81
213
|
//#endregion
|
|
82
|
-
export { type IdColumnCodec, IdComputeField, IdTransform, IdsError, type IdsErrorCode, idField, isIdsError };
|
|
214
|
+
export { type IdColumnCodec, IdComputeField, IdGeneratingCodec, IdQueryField, IdTransform, IdsError, type IdsErrorCode, NullableIdComputeField, NullableIdTransform, idField, idFieldReadOnly, isIdsError, nullableIdField };
|
|
83
215
|
//# sourceMappingURL=prisma.d.mts.map
|
package/dist/prisma.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prisma.d.mts","names":[],"sources":["../src/adapters/prisma.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"prisma.d.mts","names":[],"sources":["../src/adapters/prisma.ts"],"mappings":";;;;;;AAgBA;;;;;;;AAAA,KAAY,iBAAA,yBAA0C,aAAA,CAAc,KAAA;EAClE,QAAA,IAAY,EAAA,CAAG,KAAA;AAAA;;;;;;;KASL,YAAA;EAAA,CAAsC,SAAA,WAAA,mBAAA;AAAA;;;AAAA;AAQlD;;;KAAY,cAAA;EACV,KAAA,EAAO,MAAA;EACP,OAAA,GAAU,KAAA,EAAO,MAAA,sBAA4B,EAAA,CAAG,KAAA;AAAA;;;;;;KAQtC,sBAAA;EACV,KAAA,EAAO,MAAA;EACP,OAAA,GAAU,KAAA,EAAO,MAAA,sBAA4B,EAAA,CAAG,KAAA;AAAA;;;AAVA;AAQlD;KASY,WAAA;;;;;;EAMV,IAAA,CAAK,KAAA,YAAiB,EAAA,CAAG,KAAA;EAboB;;;;EAkB7C,YAAA,CAAa,KAAA,YAAiB,EAAA,CAAG,KAAA;;;;;;AAlBe;AAOlD;EAmBE,KAAA,CAAM,KAAA,EAAO,EAAA,CAAG,KAAA;;;;;;;;;;;;;;;;;;;EAmBhB,YAAA,CAAa,SAAA,WAAoB,cAAA,CAAe,KAAA;;;;;;;;;;;;;;;;;;;;;AA8BgB;EARhE,YAAA,CAAa,SAAA,WAAoB,YAAA;EAiBvB;;;;;;;EATV,oBAAA,CAAqB,SAAA,WAAoB,sBAAA,CAAuB,KAAA;AAAA;;;;;;;KAStD,mBAAA;EACV,YAAA,CAAa,KAAA,YAAiB,EAAA,CAAG,KAAA;EACjC,KAAA,CAAM,KAAA,EAAO,EAAA,CAAG,KAAA;EAChB,oBAAA,CAAqB,SAAA,WAAoB,sBAAA,CAAuB,KAAA;AAAA;;;;;AAAA;AAgClE;;;;;;;;;;;;;;;;AAA4F;AA4F5F;;;;;;;iBA5FgB,OAAA,uBAA8B,KAAA,EAAO,iBAAA,CAAkB,KAAA,IAAS,WAAA,CAAY,KAAA;;;;;;;;;AA8FrE;AA6CvB;;;;;;;;;;;;;;;iBA/CgB,eAAA,uBACd,KAAA,EAAO,aAAA,CAAc,KAAA,IACpB,mBAAA,CAAoB,KAAA;;;AA+CH;;;;;;;;;;;;;;;;;;;;;;;;;iBAFJ,eAAA,uBACd,KAAA,EAAO,aAAA,CAAc,KAAA,IACpB,IAAA,CAAK,WAAA,CAAY,KAAA"}
|
package/dist/prisma.mjs
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { n as isIdsError, t as IdsError } from "./error-Cp5qYZcv.mjs";
|
|
2
|
-
import { t as readIdColumn } from "./adapter-types-
|
|
2
|
+
import { n as readIdColumnNullable, t as readIdColumn } from "./adapter-types-CjzFNDcJ.mjs";
|
|
3
3
|
//#region src/adapters/prisma.ts
|
|
4
4
|
/**
|
|
5
5
|
* Creates a read/write transform pair for use with Prisma's `$extends` extension model.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
7
|
+
* Requires a codec variant that exposes a synchronous `generate()` in addition to `safeParse` — see {@link IdGeneratingCodec}. Only the **Timestamp codec** and **Reverse Timestamp codec** qualify; Opaque, Signed, Wrapped, and Digest codecs cannot be passed to `idField()`.
|
|
8
8
|
*
|
|
9
9
|
* Use `computeField(fieldName)` to produce a typed `$extends` result-component
|
|
10
10
|
* field definition — the brand is carried through Prisma's type machinery
|
|
11
11
|
* automatically and no per-call-site cast is required.
|
|
12
12
|
*
|
|
13
|
+
* For codecs that do not expose a synchronous `generate()` (Opaque Timestamp,
|
|
14
|
+
* Signed Timestamp, Wrapped key, Digest), use {@link idFieldReadOnly} instead —
|
|
15
|
+
* it accepts any {@link IdColumnCodec} and omits `defaultQuery`.
|
|
16
|
+
*
|
|
13
17
|
* @example
|
|
14
18
|
* ```ts
|
|
15
19
|
* import { idField } from "@smonn/ids/prisma";
|
|
@@ -27,10 +31,138 @@ import { t as readIdColumn } from "./adapter-types-7wWdELSh.mjs";
|
|
|
27
31
|
* ```
|
|
28
32
|
*/
|
|
29
33
|
function idField(codec) {
|
|
34
|
+
const { generate } = codec;
|
|
35
|
+
return {
|
|
36
|
+
read(value) {
|
|
37
|
+
return readIdColumn(codec, value);
|
|
38
|
+
},
|
|
39
|
+
readNullable(value) {
|
|
40
|
+
return readIdColumnNullable(codec, value);
|
|
41
|
+
},
|
|
42
|
+
write(value) {
|
|
43
|
+
return value;
|
|
44
|
+
},
|
|
45
|
+
computeField(fieldName) {
|
|
46
|
+
return {
|
|
47
|
+
needs: { [fieldName]: true },
|
|
48
|
+
compute: (model) => readIdColumn(codec, model[fieldName])
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
defaultQuery(fieldName) {
|
|
52
|
+
function injectIfAbsent(data) {
|
|
53
|
+
if (data[fieldName] == null) return {
|
|
54
|
+
...data,
|
|
55
|
+
[fieldName]: generate()
|
|
56
|
+
};
|
|
57
|
+
return data;
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
async create({ args, query }) {
|
|
61
|
+
const data = args.data;
|
|
62
|
+
return query(data != null ? {
|
|
63
|
+
...args,
|
|
64
|
+
data: injectIfAbsent(data)
|
|
65
|
+
} : args);
|
|
66
|
+
},
|
|
67
|
+
async createMany({ args, query }) {
|
|
68
|
+
const data = args.data;
|
|
69
|
+
return query(Array.isArray(data) ? {
|
|
70
|
+
...args,
|
|
71
|
+
data: data.map((item) => injectIfAbsent(item))
|
|
72
|
+
} : args);
|
|
73
|
+
},
|
|
74
|
+
async upsert({ args, query }) {
|
|
75
|
+
const createData = args.create;
|
|
76
|
+
return query(createData != null ? {
|
|
77
|
+
...args,
|
|
78
|
+
create: injectIfAbsent(createData)
|
|
79
|
+
} : args);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
computeNullableField(fieldName) {
|
|
84
|
+
return {
|
|
85
|
+
needs: { [fieldName]: true },
|
|
86
|
+
compute: (model) => readIdColumnNullable(codec, model[fieldName])
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Standalone nullable counterpart of {@link idField} for Prisma adapter symmetry
|
|
93
|
+
* with the other ORM adapters ({@link nullableIdColumn} in Drizzle/Kysely,
|
|
94
|
+
* `nullableIdType` in MikroORM, `nullableIdTransformer` in TypeORM).
|
|
95
|
+
*
|
|
96
|
+
* Accepts any {@link IdColumnCodec} — no synchronous `generate()` required,
|
|
97
|
+
* because nullable FK columns do not auto-generate IDs.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```ts
|
|
101
|
+
* import { nullableIdField } from "@smonn/ids/prisma";
|
|
102
|
+
* import { createTimestampId } from "@smonn/ids";
|
|
103
|
+
*
|
|
104
|
+
* const usr = createTimestampId("usr");
|
|
105
|
+
* const authorIdField = nullableIdField(usr);
|
|
106
|
+
*
|
|
107
|
+
* const xprisma = prisma.$extends({
|
|
108
|
+
* result: {
|
|
109
|
+
* post: { authorId: authorIdField.computeNullableField("authorId") },
|
|
110
|
+
* },
|
|
111
|
+
* });
|
|
112
|
+
* // xprisma.post.findUnique(…).authorId is typed as Id<"usr"> | null
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
function nullableIdField(codec) {
|
|
116
|
+
return {
|
|
117
|
+
readNullable(value) {
|
|
118
|
+
return readIdColumnNullable(codec, value);
|
|
119
|
+
},
|
|
120
|
+
write(value) {
|
|
121
|
+
return value;
|
|
122
|
+
},
|
|
123
|
+
computeNullableField(fieldName) {
|
|
124
|
+
return {
|
|
125
|
+
needs: { [fieldName]: true },
|
|
126
|
+
compute: (model) => readIdColumnNullable(codec, model[fieldName])
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Read-only sibling of {@link idField} for codec variants that do not expose a
|
|
133
|
+
* synchronous `generate()` — Opaque Timestamp, Signed Timestamp, Wrapped key,
|
|
134
|
+
* and Digest codecs all qualify.
|
|
135
|
+
*
|
|
136
|
+
* Accepts any {@link IdColumnCodec} (the wider constraint that only requires
|
|
137
|
+
* `safeParse`) and returns the full read/transform surface of {@link IdTransform}
|
|
138
|
+
* **minus `defaultQuery`**. Because `defaultQuery` is the only method that calls
|
|
139
|
+
* `generate()`, callers who only need the read path are not forced to provide a
|
|
140
|
+
* synchronous generator.
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```ts
|
|
144
|
+
* import { idFieldReadOnly } from "@smonn/ids/prisma";
|
|
145
|
+
* import { createOpaqueTimestampId } from "@smonn/ids/opaque";
|
|
146
|
+
*
|
|
147
|
+
* const inv = createOpaqueTimestampId("inv", { key });
|
|
148
|
+
* const invoiceIdField = idFieldReadOnly(inv);
|
|
149
|
+
*
|
|
150
|
+
* const xprisma = prisma.$extends({
|
|
151
|
+
* result: {
|
|
152
|
+
* invoice: { id: invoiceIdField.computeField("id") },
|
|
153
|
+
* },
|
|
154
|
+
* });
|
|
155
|
+
* // xprisma.invoice.findUnique(…).id is typed as Id<"inv"> — no cast required
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
function idFieldReadOnly(codec) {
|
|
30
159
|
return {
|
|
31
160
|
read(value) {
|
|
32
161
|
return readIdColumn(codec, value);
|
|
33
162
|
},
|
|
163
|
+
readNullable(value) {
|
|
164
|
+
return readIdColumnNullable(codec, value);
|
|
165
|
+
},
|
|
34
166
|
write(value) {
|
|
35
167
|
return value;
|
|
36
168
|
},
|
|
@@ -39,10 +171,16 @@ function idField(codec) {
|
|
|
39
171
|
needs: { [fieldName]: true },
|
|
40
172
|
compute: (model) => readIdColumn(codec, model[fieldName])
|
|
41
173
|
};
|
|
174
|
+
},
|
|
175
|
+
computeNullableField(fieldName) {
|
|
176
|
+
return {
|
|
177
|
+
needs: { [fieldName]: true },
|
|
178
|
+
compute: (model) => readIdColumnNullable(codec, model[fieldName])
|
|
179
|
+
};
|
|
42
180
|
}
|
|
43
181
|
};
|
|
44
182
|
}
|
|
45
183
|
//#endregion
|
|
46
|
-
export { IdsError, idField, isIdsError };
|
|
184
|
+
export { IdsError, idField, idFieldReadOnly, isIdsError, nullableIdField };
|
|
47
185
|
|
|
48
186
|
//# sourceMappingURL=prisma.mjs.map
|
package/dist/prisma.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prisma.mjs","names":[],"sources":["../src/adapters/prisma.ts"],"sourcesContent":["import { readIdColumn, type IdColumnCodec } from \"./adapter-types.js\";\nimport type { Id } from \"../types.js\";\n\n/** {@link IdsError} class, {@link isIdsError} type guard, and {@link IdsErrorCode} union — re-exported from `\"@smonn/ids\"` for convenience. */\nexport { IdsError, isIdsError, type IdsErrorCode } from \"../error.js\";\n\nexport type { IdColumnCodec };\n\n/**\n * Typed `$extends` result-component field definition produced by\n * {@link IdTransform.computeField} — a `{ needs, compute }` pair whose `compute`\n * return type is statically `Id<Brand>`, so the extended-client model field is\n * typed correctly without a per-call-site cast.\n */\nexport type IdComputeField<Brand extends string> = {\n needs: Record<string, boolean>;\n compute: (model: Record<string, unknown>) => Id<Brand>;\n};\n\n/**\n * Read/write transform pair and `$extends` result-component factory for\n * integrating `Id<Brand>` with Prisma extensions.\n */\nexport type IdTransform<Brand extends string> = {\n /**\n * Read transform: validates the raw database value via `safeParse` and returns\n * `Id<Brand>`. Throws if the value is missing, malformed, or belongs to a\n * different brand.\n */\n read(value: unknown): Id<Brand>;\n /**\n * Write transform: passes `Id<Brand>` through as its canonical string form.\n * `Id<Brand>` is already the canonical string, so this is an identity function\n * at runtime.\n *\n * Use in a Prisma `$extends` query component or explicit `data` mapping.\n */\n write(value: Id<Brand>): string;\n /**\n * Creates a typed `$extends` result-component field definition that carries\n * `Id<Brand>` through Prisma's type machinery without a per-call-site cast.\n *\n * @param fieldName - The model field to read from (e.g. `\"id\"`).\n * @returns An {@link IdComputeField} whose `compute` return type is statically\n * `Id<Brand>`, so the extended-client model field is typed correctly.\n *\n * @example\n * ```ts\n * const xprisma = prisma.$extends({\n * result: {\n * user: { id: userIdField.computeField(\"id\") },\n * },\n * });\n * // xprisma.user.findUnique(…).id is typed as Id<\"usr\"> — no cast required\n * ```\n */\n computeField(fieldName: string): IdComputeField<Brand>;\n};\n\n/**\n * Creates a read/write transform pair for use with Prisma's `$extends` extension model.\n *\n * Works with any codec variant exposing `safeParse`.\n *\n * Use `computeField(fieldName)` to produce a typed `$extends` result-component\n * field definition — the brand is carried through Prisma's type machinery\n * automatically and no per-call-site cast is required.\n *\n * @example\n * ```ts\n * import { idField } from \"@smonn/ids/prisma\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n * const userIdField = idField(usr);\n *\n * const xprisma = prisma.$extends({\n * result: {\n * user: { id: userIdField.computeField(\"id\") },\n * },\n * });\n * // xprisma.user.findUnique(…).id is typed as Id<\"usr\"> — no cast required\n * ```\n */\nexport function idField<Brand extends string>(codec: IdColumnCodec<Brand>): IdTransform<Brand> {\n return {\n read(value: unknown): Id<Brand> {\n return readIdColumn(codec, value);\n },\n write(value: Id<Brand>): string {\n return value;\n },\n computeField(fieldName: string) {\n return {\n needs: { [fieldName]: true },\n // Prisma's $extends types `compute` as returning `any` in its constraint\n // type (DynamicResultExtensionArgs). Returning a pre-built object with an\n // explicit Id<Brand> return type on `compute` causes TypeScript to infer\n // the brand through the `& R` intersection in $extends — encapsulating\n // the single necessary cast here rather than pushing it to every call site.\n compute: (model: Record<string, unknown>): Id<Brand> =>\n readIdColumn(codec, model[fieldName]),\n };\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoFA,SAAgB,QAA8B,OAAiD;CAC7F,OAAO;EACL,KAAK,OAA2B;GAC9B,OAAO,aAAa,OAAO,KAAK;EAClC;EACA,MAAM,OAA0B;GAC9B,OAAO;EACT;EACA,aAAa,WAAmB;GAC9B,OAAO;IACL,OAAO,GAAG,YAAY,KAAK;IAM3B,UAAU,UACR,aAAa,OAAO,MAAM,UAAU;GACxC;EACF;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"prisma.mjs","names":[],"sources":["../src/adapters/prisma.ts"],"sourcesContent":["import type { ModelQueryOptionsCb, ModelQueryOptionsCbArgs } from \"@prisma/client/runtime/library\";\nimport { readIdColumn, readIdColumnNullable, type IdColumnCodec } from \"./adapter-types.js\";\nimport type { Id } from \"../types.js\";\n\n/** {@link IdsError} class, {@link isIdsError} type guard, and {@link IdsErrorCode} union — re-exported from `\"@smonn/ids\"` for convenience. */\nexport { IdsError, isIdsError, type IdsErrorCode } from \"../error.js\";\n\nexport type { IdColumnCodec };\n\n/**\n * Extension of {@link IdColumnCodec} that also exposes synchronous `generate()`.\n * Required by {@link idField} so that {@link IdTransform.defaultQuery} can produce\n * IDs at write time. Every full codec variant (Timestamp, Reverse Timestamp) satisfies\n * this; async-generate codecs (Opaque, Signed, Wrapped, Digest) do not and are\n * therefore unsupported by `defaultQuery`.\n */\nexport type IdGeneratingCodec<Brand extends string> = IdColumnCodec<Brand> & {\n generate(): Id<Brand>;\n};\n\n/**\n * The per-model object returned by {@link IdTransform.defaultQuery}, suitable for\n * the model-level value inside a Prisma `$extends({ query: { modelName: … } })` block.\n * Structurally identical to `{ [operation: string]: ModelQueryOptionsCb }` from\n * `@prisma/client/runtime/library`.\n */\nexport type IdQueryField = { [operation: string]: ModelQueryOptionsCb };\n\n/**\n * Typed `$extends` result-component field definition produced by\n * {@link IdTransform.computeField} — a `{ needs, compute }` pair whose `compute`\n * return type is statically `Id<Brand>`, so the extended-client model field is\n * typed correctly without a per-call-site cast.\n */\nexport type IdComputeField<Brand extends string> = {\n needs: Record<string, boolean>;\n compute: (model: Record<string, unknown>) => Id<Brand>;\n};\n\n/**\n * Typed `$extends` result-component field definition produced by\n * {@link IdTransform.computeNullableField} — like {@link IdComputeField} but\n * `compute` returns `Id<Brand> | null` for nullable columns.\n */\nexport type NullableIdComputeField<Brand extends string> = {\n needs: Record<string, boolean>;\n compute: (model: Record<string, unknown>) => Id<Brand> | null;\n};\n\n/**\n * Read/write transform pair and `$extends` result-component factory for\n * integrating `Id<Brand>` with Prisma extensions.\n */\nexport type IdTransform<Brand extends string> = {\n /**\n * Read transform: validates the raw database value via `safeParse` and returns\n * `Id<Brand>`. Throws if the value is missing, malformed, or belongs to a\n * different brand.\n */\n read(value: unknown): Id<Brand>;\n /**\n * Nullable read transform: returns `null` when `value` is `null` or `undefined`;\n * otherwise delegates to {@link read}. Use for optional foreign keys.\n */\n readNullable(value: unknown): Id<Brand> | null;\n /**\n * Write transform: passes `Id<Brand>` through as its canonical string form.\n * `Id<Brand>` is already the canonical string, so this is an identity function\n * at runtime.\n *\n * Use in a Prisma `$extends` query component or explicit `data` mapping.\n */\n write(value: Id<Brand>): string;\n /**\n * Creates a typed `$extends` result-component field definition that carries\n * `Id<Brand>` through Prisma's type machinery without a per-call-site cast.\n *\n * @param fieldName - The model field to read from (e.g. `\"id\"`).\n * @returns An {@link IdComputeField} whose `compute` return type is statically\n * `Id<Brand>`, so the extended-client model field is typed correctly.\n *\n * @example\n * ```ts\n * const xprisma = prisma.$extends({\n * result: {\n * user: { id: userIdField.computeField(\"id\") },\n * },\n * });\n * // xprisma.user.findUnique(…).id is typed as Id<\"usr\"> — no cast required\n * ```\n */\n computeField(fieldName: string): IdComputeField<Brand>;\n /**\n * Creates a `$extends` query-component model slice that auto-generates\n * `Id<Brand>` values for `create`, `createMany`, and `upsert` operations\n * when the field is absent, `undefined`, or `null` in `args.data` (or\n * `args.create` for upsert). Explicitly supplied values are always passed\n * through unchanged.\n *\n * @param fieldName - The model field to auto-generate (e.g. `\"id\"`).\n * @returns An {@link IdQueryField} suitable for the model-level value inside\n * a Prisma `$extends({ query: { modelName: … } })` block.\n *\n * @example\n * ```ts\n * const xprisma = prisma.$extends({\n * query: { user: userIdField.defaultQuery(\"id\") },\n * result: { user: { id: userIdField.computeField(\"id\") } },\n * });\n * // id is auto-filled on create, and typed as Id<\"usr\"> on read\n * await xprisma.user.create({ data: { name: \"Alice\" } });\n * ```\n */\n defaultQuery(fieldName: string): IdQueryField;\n /**\n * Like {@link computeField} but for nullable columns — `compute` returns\n * `Id<Brand> | null` instead of `Id<Brand>`.\n *\n * @param fieldName - The nullable model field to read from.\n * @returns A {@link NullableIdComputeField} whose `compute` returns `Id<Brand> | null`.\n */\n computeNullableField(fieldName: string): NullableIdComputeField<Brand>;\n};\n\n/**\n * The read/nullable-read/write surface returned by {@link nullableIdField} —\n * mirrors the nullable methods of {@link IdTransform} but omits `defaultQuery`,\n * `read`, and `computeField` since nullable FK columns neither auto-generate IDs\n * nor require a non-null read path at the top level.\n */\nexport type NullableIdTransform<Brand extends string> = {\n readNullable(value: unknown): Id<Brand> | null;\n write(value: Id<Brand>): string;\n computeNullableField(fieldName: string): NullableIdComputeField<Brand>;\n};\n\n/**\n * Creates a read/write transform pair for use with Prisma's `$extends` extension model.\n *\n * Requires a codec variant that exposes a synchronous `generate()` in addition to `safeParse` — see {@link IdGeneratingCodec}. Only the **Timestamp codec** and **Reverse Timestamp codec** qualify; Opaque, Signed, Wrapped, and Digest codecs cannot be passed to `idField()`.\n *\n * Use `computeField(fieldName)` to produce a typed `$extends` result-component\n * field definition — the brand is carried through Prisma's type machinery\n * automatically and no per-call-site cast is required.\n *\n * For codecs that do not expose a synchronous `generate()` (Opaque Timestamp,\n * Signed Timestamp, Wrapped key, Digest), use {@link idFieldReadOnly} instead —\n * it accepts any {@link IdColumnCodec} and omits `defaultQuery`.\n *\n * @example\n * ```ts\n * import { idField } from \"@smonn/ids/prisma\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n * const userIdField = idField(usr);\n *\n * const xprisma = prisma.$extends({\n * result: {\n * user: { id: userIdField.computeField(\"id\") },\n * },\n * });\n * // xprisma.user.findUnique(…).id is typed as Id<\"usr\"> — no cast required\n * ```\n */\nexport function idField<Brand extends string>(codec: IdGeneratingCodec<Brand>): IdTransform<Brand> {\n const { generate } = codec;\n return {\n read(value: unknown): Id<Brand> {\n return readIdColumn(codec, value);\n },\n readNullable(value: unknown): Id<Brand> | null {\n return readIdColumnNullable(codec, value);\n },\n write(value: Id<Brand>): string {\n return value;\n },\n computeField(fieldName: string) {\n return {\n needs: { [fieldName]: true },\n // Prisma's $extends types `compute` as returning `any` in its constraint\n // type (DynamicResultExtensionArgs). Returning a pre-built object with an\n // explicit Id<Brand> return type on `compute` causes TypeScript to infer\n // the brand through the `& R` intersection in $extends — encapsulating\n // the single necessary cast here rather than pushing it to every call site.\n compute: (model: Record<string, unknown>): Id<Brand> =>\n readIdColumn(codec, model[fieldName]),\n };\n },\n defaultQuery(fieldName: string): IdQueryField {\n function injectIfAbsent(data: Record<string, unknown>): Record<string, unknown> {\n if (data[fieldName] == null) {\n return { ...data, [fieldName]: generate() };\n }\n return data;\n }\n\n type QueryArg = Parameters<ModelQueryOptionsCbArgs[\"query\"]>[0];\n\n return {\n async create({ args, query }) {\n const data = args.data as Record<string, unknown> | null | undefined;\n const nextArgs =\n data != null ? ({ ...args, data: injectIfAbsent(data) } as unknown as QueryArg) : args;\n return query(nextArgs);\n },\n async createMany({ args, query }) {\n const data = args.data as Array<Record<string, unknown>> | null | undefined;\n const nextArgs = Array.isArray(data)\n ? ({ ...args, data: data.map((item) => injectIfAbsent(item)) } as unknown as QueryArg)\n : args;\n return query(nextArgs);\n },\n async upsert({ args, query }) {\n const createData = args.create as Record<string, unknown> | null | undefined;\n const nextArgs =\n createData != null\n ? ({ ...args, create: injectIfAbsent(createData) } as unknown as QueryArg)\n : args;\n return query(nextArgs);\n },\n };\n },\n computeNullableField(fieldName: string) {\n return {\n needs: { [fieldName]: true },\n compute: (model: Record<string, unknown>): Id<Brand> | null =>\n readIdColumnNullable(codec, model[fieldName]),\n };\n },\n };\n}\n\n/**\n * Standalone nullable counterpart of {@link idField} for Prisma adapter symmetry\n * with the other ORM adapters ({@link nullableIdColumn} in Drizzle/Kysely,\n * `nullableIdType` in MikroORM, `nullableIdTransformer` in TypeORM).\n *\n * Accepts any {@link IdColumnCodec} — no synchronous `generate()` required,\n * because nullable FK columns do not auto-generate IDs.\n *\n * @example\n * ```ts\n * import { nullableIdField } from \"@smonn/ids/prisma\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n * const authorIdField = nullableIdField(usr);\n *\n * const xprisma = prisma.$extends({\n * result: {\n * post: { authorId: authorIdField.computeNullableField(\"authorId\") },\n * },\n * });\n * // xprisma.post.findUnique(…).authorId is typed as Id<\"usr\"> | null\n * ```\n */\nexport function nullableIdField<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n): NullableIdTransform<Brand> {\n return {\n readNullable(value: unknown): Id<Brand> | null {\n return readIdColumnNullable(codec, value);\n },\n write(value: Id<Brand>): string {\n return value;\n },\n computeNullableField(fieldName: string) {\n return {\n needs: { [fieldName]: true },\n compute: (model: Record<string, unknown>): Id<Brand> | null =>\n readIdColumnNullable(codec, model[fieldName]),\n };\n },\n };\n}\n\n/**\n * Read-only sibling of {@link idField} for codec variants that do not expose a\n * synchronous `generate()` — Opaque Timestamp, Signed Timestamp, Wrapped key,\n * and Digest codecs all qualify.\n *\n * Accepts any {@link IdColumnCodec} (the wider constraint that only requires\n * `safeParse`) and returns the full read/transform surface of {@link IdTransform}\n * **minus `defaultQuery`**. Because `defaultQuery` is the only method that calls\n * `generate()`, callers who only need the read path are not forced to provide a\n * synchronous generator.\n *\n * @example\n * ```ts\n * import { idFieldReadOnly } from \"@smonn/ids/prisma\";\n * import { createOpaqueTimestampId } from \"@smonn/ids/opaque\";\n *\n * const inv = createOpaqueTimestampId(\"inv\", { key });\n * const invoiceIdField = idFieldReadOnly(inv);\n *\n * const xprisma = prisma.$extends({\n * result: {\n * invoice: { id: invoiceIdField.computeField(\"id\") },\n * },\n * });\n * // xprisma.invoice.findUnique(…).id is typed as Id<\"inv\"> — no cast required\n * ```\n */\nexport function idFieldReadOnly<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n): Omit<IdTransform<Brand>, \"defaultQuery\"> {\n return {\n read(value: unknown): Id<Brand> {\n return readIdColumn(codec, value);\n },\n readNullable(value: unknown): Id<Brand> | null {\n return readIdColumnNullable(codec, value);\n },\n write(value: Id<Brand>): string {\n return value;\n },\n computeField(fieldName: string) {\n return {\n needs: { [fieldName]: true },\n compute: (model: Record<string, unknown>): Id<Brand> =>\n readIdColumn(codec, model[fieldName]),\n };\n },\n computeNullableField(fieldName: string) {\n return {\n needs: { [fieldName]: true },\n compute: (model: Record<string, unknown>): Id<Brand> | null =>\n readIdColumnNullable(codec, model[fieldName]),\n };\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqKA,SAAgB,QAA8B,OAAqD;CACjG,MAAM,EAAE,aAAa;CACrB,OAAO;EACL,KAAK,OAA2B;GAC9B,OAAO,aAAa,OAAO,KAAK;EAClC;EACA,aAAa,OAAkC;GAC7C,OAAO,qBAAqB,OAAO,KAAK;EAC1C;EACA,MAAM,OAA0B;GAC9B,OAAO;EACT;EACA,aAAa,WAAmB;GAC9B,OAAO;IACL,OAAO,GAAG,YAAY,KAAK;IAM3B,UAAU,UACR,aAAa,OAAO,MAAM,UAAU;GACxC;EACF;EACA,aAAa,WAAiC;GAC5C,SAAS,eAAe,MAAwD;IAC9E,IAAI,KAAK,cAAc,MACrB,OAAO;KAAE,GAAG;MAAO,YAAY,SAAS;IAAE;IAE5C,OAAO;GACT;GAIA,OAAO;IACL,MAAM,OAAO,EAAE,MAAM,SAAS;KAC5B,MAAM,OAAO,KAAK;KAGlB,OAAO,MADL,QAAQ,OAAQ;MAAE,GAAG;MAAM,MAAM,eAAe,IAAI;KAAE,IAA4B,IAC/D;IACvB;IACA,MAAM,WAAW,EAAE,MAAM,SAAS;KAChC,MAAM,OAAO,KAAK;KAIlB,OAAO,MAHU,MAAM,QAAQ,IAAI,IAC9B;MAAE,GAAG;MAAM,MAAM,KAAK,KAAK,SAAS,eAAe,IAAI,CAAC;KAAE,IAC3D,IACiB;IACvB;IACA,MAAM,OAAO,EAAE,MAAM,SAAS;KAC5B,MAAM,aAAa,KAAK;KAKxB,OAAO,MAHL,cAAc,OACT;MAAE,GAAG;MAAM,QAAQ,eAAe,UAAU;KAAE,IAC/C,IACe;IACvB;GACF;EACF;EACA,qBAAqB,WAAmB;GACtC,OAAO;IACL,OAAO,GAAG,YAAY,KAAK;IAC3B,UAAU,UACR,qBAAqB,OAAO,MAAM,UAAU;GAChD;EACF;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,SAAgB,gBACd,OAC4B;CAC5B,OAAO;EACL,aAAa,OAAkC;GAC7C,OAAO,qBAAqB,OAAO,KAAK;EAC1C;EACA,MAAM,OAA0B;GAC9B,OAAO;EACT;EACA,qBAAqB,WAAmB;GACtC,OAAO;IACL,OAAO,GAAG,YAAY,KAAK;IAC3B,UAAU,UACR,qBAAqB,OAAO,MAAM,UAAU;GAChD;EACF;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,SAAgB,gBACd,OAC0C;CAC1C,OAAO;EACL,KAAK,OAA2B;GAC9B,OAAO,aAAa,OAAO,KAAK;EAClC;EACA,aAAa,OAAkC;GAC7C,OAAO,qBAAqB,OAAO,KAAK;EAC1C;EACA,MAAM,OAA0B;GAC9B,OAAO;EACT;EACA,aAAa,WAAmB;GAC9B,OAAO;IACL,OAAO,GAAG,YAAY,KAAK;IAC3B,UAAU,UACR,aAAa,OAAO,MAAM,UAAU;GACxC;EACF;EACA,qBAAqB,WAAmB;GACtC,OAAO;IACL,OAAO,GAAG,YAAY,KAAK;IAC3B,UAAU,UACR,qBAAqB,OAAO,MAAM,UAAU;GAChD;EACF;CACF;AACF"}
|
package/dist/typeorm.d.mts
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
|
+
import { t as Id } from "./types-hGBnCpJj.mjs";
|
|
1
2
|
import { n as IdColumnCodec } from "./adapter-types-Bia_w9sg.mjs";
|
|
2
3
|
import { n as IdsErrorCode, r as isIdsError, t as IdsError } from "./error-CifcKKOG.mjs";
|
|
3
4
|
import { ValueTransformer } from "typeorm";
|
|
4
5
|
|
|
5
6
|
//#region src/adapters/typeorm.d.ts
|
|
6
7
|
/**
|
|
8
|
+
* Extension of {@link IdColumnCodec} that also exposes synchronous `generate()`.
|
|
9
|
+
* Required by {@link beforeInsertHook} so that the hook can produce IDs at insert
|
|
10
|
+
* time. Every full codec variant (Timestamp, Reverse Timestamp) satisfies this;
|
|
11
|
+
* async-generate codecs (Opaque, Signed, Wrapped, Digest) do not and are therefore
|
|
12
|
+
* unsupported by `beforeInsertHook`.
|
|
13
|
+
*/
|
|
14
|
+
type IdGeneratingCodec<Brand extends string> = IdColumnCodec<Brand> & {
|
|
15
|
+
generate(): Id<Brand>;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
7
18
|
* TypeORM column transformer for `Id<Brand>`.
|
|
8
19
|
*
|
|
9
20
|
* Returns a `ValueTransformer` object suitable for use in a TypeORM `@Column`
|
|
@@ -36,6 +47,78 @@ import { ValueTransformer } from "typeorm";
|
|
|
36
47
|
* ```
|
|
37
48
|
*/
|
|
38
49
|
declare function idTransformer<Brand extends string>(codec: IdColumnCodec<Brand>): ValueTransformer;
|
|
50
|
+
/**
|
|
51
|
+
* Returns a function suitable for use inside a TypeORM `@BeforeInsert()` lifecycle
|
|
52
|
+
* hook that auto-generates an `Id<Brand>` for `fieldName` when the field is absent
|
|
53
|
+
* (`null` or `undefined`) on the entity at insert time. If the field already has a
|
|
54
|
+
* value it is left unchanged.
|
|
55
|
+
*
|
|
56
|
+
* Requires a codec variant that exposes a synchronous `generate()` — see
|
|
57
|
+
* {@link IdGeneratingCodec}. Only the **Timestamp codec** and **Reverse Timestamp
|
|
58
|
+
* codec** qualify; passing an async-generate codec (Opaque, Signed, Wrapped, Digest)
|
|
59
|
+
* is a **compile-time type error**.
|
|
60
|
+
*
|
|
61
|
+
* Pair with {@link idTransformer} on the same column: `idTransformer` handles the
|
|
62
|
+
* read/write path; `beforeInsertHook` handles generation.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* import { idTransformer, beforeInsertHook } from "@smonn/ids/typeorm";
|
|
67
|
+
* import { createTimestampId } from "@smonn/ids";
|
|
68
|
+
* import type { Id } from "@smonn/ids";
|
|
69
|
+
* import { BeforeInsert, Column, Entity } from "typeorm";
|
|
70
|
+
*
|
|
71
|
+
* const usr = createTimestampId("usr");
|
|
72
|
+
* const fillUserId = beforeInsertHook("id", usr);
|
|
73
|
+
*
|
|
74
|
+
* @Entity()
|
|
75
|
+
* class User {
|
|
76
|
+
* @Column({ type: "text", transformer: idTransformer(usr) })
|
|
77
|
+
* id!: Id<"usr">;
|
|
78
|
+
*
|
|
79
|
+
* @BeforeInsert()
|
|
80
|
+
* generateId() {
|
|
81
|
+
* fillUserId(this);
|
|
82
|
+
* }
|
|
83
|
+
* }
|
|
84
|
+
* ```
|
|
85
|
+
*
|
|
86
|
+
* @remarks
|
|
87
|
+
* **Gating rule:** `beforeInsertHook` requires a synchronous `generate()` codec
|
|
88
|
+
* (`IdGeneratingCodec`). Async-generate codecs — Opaque Timestamp, Signed Timestamp,
|
|
89
|
+
* Wrapped key, and Digest — do not satisfy `IdGeneratingCodec` and cannot be passed
|
|
90
|
+
* here. TypeORM `@BeforeInsert` can be async, but synchronous generation keeps the
|
|
91
|
+
* hook ergonomic and matches the full Prisma parity shape.
|
|
92
|
+
*
|
|
93
|
+
* **Read/write path:** Use {@link idTransformer} on the column for database
|
|
94
|
+
* read/write transforms. `beforeInsertHook` only handles the generation step — it
|
|
95
|
+
* does not replace the transformer.
|
|
96
|
+
*/
|
|
97
|
+
declare function beforeInsertHook<Brand extends string>(fieldName: string, codec: IdGeneratingCodec<Brand>): (entity: Record<string, unknown>) => void;
|
|
98
|
+
/**
|
|
99
|
+
* TypeORM column transformer for a **nullable** `Id<Brand>` column.
|
|
100
|
+
*
|
|
101
|
+
* Behaves like {@link idTransformer} but `from` returns `null` for `null` /
|
|
102
|
+
* `undefined` database values and `to` passes `null` / `undefined` through
|
|
103
|
+
* unchanged. Use for optional foreign keys.
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```ts
|
|
107
|
+
* import { nullableIdTransformer } from "@smonn/ids/typeorm";
|
|
108
|
+
* import { createTimestampId } from "@smonn/ids";
|
|
109
|
+
* import type { Id } from "@smonn/ids";
|
|
110
|
+
* import { Column, Entity } from "typeorm";
|
|
111
|
+
*
|
|
112
|
+
* const usr = createTimestampId("usr");
|
|
113
|
+
*
|
|
114
|
+
* @Entity()
|
|
115
|
+
* class Post {
|
|
116
|
+
* @Column({ type: "text", nullable: true, transformer: nullableIdTransformer(usr) })
|
|
117
|
+
* authorId!: Id<"usr"> | null;
|
|
118
|
+
* }
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
declare function nullableIdTransformer<Brand extends string>(codec: IdColumnCodec<Brand>): ValueTransformer;
|
|
39
122
|
//#endregion
|
|
40
|
-
export { type IdColumnCodec, IdsError, type IdsErrorCode, idTransformer, isIdsError };
|
|
123
|
+
export { type IdColumnCodec, IdGeneratingCodec, IdsError, type IdsErrorCode, beforeInsertHook, idTransformer, isIdsError, nullableIdTransformer };
|
|
41
124
|
//# sourceMappingURL=typeorm.d.mts.map
|
package/dist/typeorm.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typeorm.d.mts","names":[],"sources":["../src/adapters/typeorm.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"typeorm.d.mts","names":[],"sources":["../src/adapters/typeorm.ts"],"mappings":";;;;;;AAgBA;;;;;;;AAAA,KAAY,iBAAA,yBAA0C,aAAA,CAAc,KAAA;EAClE,QAAA,IAAY,EAAA,CAAG,KAAA;AAAA;;;;;;;AAAA;AAmCjB;;;;;;;;;;;;;;AAAkF;AA0DlF;;;;;;;;;;iBA1DgB,aAAA,uBAAoC,KAAA,EAAO,aAAA,CAAc,KAAA,IAAS,gBAAA;;;;;;AA6DtE;AA+BZ;;;;;;;;;;;;;;AAEG;;;;;;;;;;;;;;;;;;;;;;;;;;;iBApCa,gBAAA,uBACd,SAAA,UACA,KAAA,EAAO,iBAAA,CAAkB,KAAA,KACvB,MAAA,EAAQ,MAAA;;;;;;;;;;;;;;;;;;;;;;;;iBA+BI,qBAAA,uBACd,KAAA,EAAO,aAAA,CAAc,KAAA,IACpB,gBAAA"}
|
package/dist/typeorm.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as isIdsError, t as IdsError } from "./error-Cp5qYZcv.mjs";
|
|
2
|
-
import { t as readIdColumn } from "./adapter-types-
|
|
2
|
+
import { n as readIdColumnNullable, t as readIdColumn } from "./adapter-types-CjzFNDcJ.mjs";
|
|
3
3
|
//#region src/adapters/typeorm.ts
|
|
4
4
|
/**
|
|
5
5
|
* TypeORM column transformer for `Id<Brand>`.
|
|
@@ -43,7 +43,92 @@ function idTransformer(codec) {
|
|
|
43
43
|
}
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Returns a function suitable for use inside a TypeORM `@BeforeInsert()` lifecycle
|
|
48
|
+
* hook that auto-generates an `Id<Brand>` for `fieldName` when the field is absent
|
|
49
|
+
* (`null` or `undefined`) on the entity at insert time. If the field already has a
|
|
50
|
+
* value it is left unchanged.
|
|
51
|
+
*
|
|
52
|
+
* Requires a codec variant that exposes a synchronous `generate()` — see
|
|
53
|
+
* {@link IdGeneratingCodec}. Only the **Timestamp codec** and **Reverse Timestamp
|
|
54
|
+
* codec** qualify; passing an async-generate codec (Opaque, Signed, Wrapped, Digest)
|
|
55
|
+
* is a **compile-time type error**.
|
|
56
|
+
*
|
|
57
|
+
* Pair with {@link idTransformer} on the same column: `idTransformer` handles the
|
|
58
|
+
* read/write path; `beforeInsertHook` handles generation.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* import { idTransformer, beforeInsertHook } from "@smonn/ids/typeorm";
|
|
63
|
+
* import { createTimestampId } from "@smonn/ids";
|
|
64
|
+
* import type { Id } from "@smonn/ids";
|
|
65
|
+
* import { BeforeInsert, Column, Entity } from "typeorm";
|
|
66
|
+
*
|
|
67
|
+
* const usr = createTimestampId("usr");
|
|
68
|
+
* const fillUserId = beforeInsertHook("id", usr);
|
|
69
|
+
*
|
|
70
|
+
* @Entity()
|
|
71
|
+
* class User {
|
|
72
|
+
* @Column({ type: "text", transformer: idTransformer(usr) })
|
|
73
|
+
* id!: Id<"usr">;
|
|
74
|
+
*
|
|
75
|
+
* @BeforeInsert()
|
|
76
|
+
* generateId() {
|
|
77
|
+
* fillUserId(this);
|
|
78
|
+
* }
|
|
79
|
+
* }
|
|
80
|
+
* ```
|
|
81
|
+
*
|
|
82
|
+
* @remarks
|
|
83
|
+
* **Gating rule:** `beforeInsertHook` requires a synchronous `generate()` codec
|
|
84
|
+
* (`IdGeneratingCodec`). Async-generate codecs — Opaque Timestamp, Signed Timestamp,
|
|
85
|
+
* Wrapped key, and Digest — do not satisfy `IdGeneratingCodec` and cannot be passed
|
|
86
|
+
* here. TypeORM `@BeforeInsert` can be async, but synchronous generation keeps the
|
|
87
|
+
* hook ergonomic and matches the full Prisma parity shape.
|
|
88
|
+
*
|
|
89
|
+
* **Read/write path:** Use {@link idTransformer} on the column for database
|
|
90
|
+
* read/write transforms. `beforeInsertHook` only handles the generation step — it
|
|
91
|
+
* does not replace the transformer.
|
|
92
|
+
*/
|
|
93
|
+
function beforeInsertHook(fieldName, codec) {
|
|
94
|
+
return function(entity) {
|
|
95
|
+
if (entity[fieldName] == null) entity[fieldName] = codec.generate();
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* TypeORM column transformer for a **nullable** `Id<Brand>` column.
|
|
100
|
+
*
|
|
101
|
+
* Behaves like {@link idTransformer} but `from` returns `null` for `null` /
|
|
102
|
+
* `undefined` database values and `to` passes `null` / `undefined` through
|
|
103
|
+
* unchanged. Use for optional foreign keys.
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```ts
|
|
107
|
+
* import { nullableIdTransformer } from "@smonn/ids/typeorm";
|
|
108
|
+
* import { createTimestampId } from "@smonn/ids";
|
|
109
|
+
* import type { Id } from "@smonn/ids";
|
|
110
|
+
* import { Column, Entity } from "typeorm";
|
|
111
|
+
*
|
|
112
|
+
* const usr = createTimestampId("usr");
|
|
113
|
+
*
|
|
114
|
+
* @Entity()
|
|
115
|
+
* class Post {
|
|
116
|
+
* @Column({ type: "text", nullable: true, transformer: nullableIdTransformer(usr) })
|
|
117
|
+
* authorId!: Id<"usr"> | null;
|
|
118
|
+
* }
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
function nullableIdTransformer(codec) {
|
|
122
|
+
return {
|
|
123
|
+
to(value) {
|
|
124
|
+
return value;
|
|
125
|
+
},
|
|
126
|
+
from(value) {
|
|
127
|
+
return readIdColumnNullable(codec, value);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
46
131
|
//#endregion
|
|
47
|
-
export { IdsError, idTransformer, isIdsError };
|
|
132
|
+
export { IdsError, beforeInsertHook, idTransformer, isIdsError, nullableIdTransformer };
|
|
48
133
|
|
|
49
134
|
//# sourceMappingURL=typeorm.mjs.map
|
package/dist/typeorm.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typeorm.mjs","names":[],"sources":["../src/adapters/typeorm.ts"],"sourcesContent":["import type { ValueTransformer } from \"typeorm\";\nimport { readIdColumn, type IdColumnCodec } from \"./adapter-types.js\";\nimport type { Id } from \"../types.js\";\n\n/** {@link IdsError} class, {@link isIdsError} type guard, and {@link IdsErrorCode} union — re-exported from `\"@smonn/ids\"` for convenience. */\nexport { IdsError, isIdsError, type IdsErrorCode } from \"../error.js\";\n\nexport type { IdColumnCodec };\n\n/**\n * TypeORM column transformer for `Id<Brand>`.\n *\n * Returns a `ValueTransformer` object suitable for use in a TypeORM `@Column`\n * decorator's `transformer` option.\n *\n * **Write path (`to`):** passes the `Id<Brand>` directly to the database — it is\n * already the canonical string form.\n *\n * **Read path (`from`):** normalises the raw database value via `codec.safeParse()`.\n * Throws `IdsError` with code `\"invalid_id\"` if the value does not parse as a valid\n * `Id<Brand>`.\n *\n * **TypeORM branding caveat:** TypeORM cannot brand a generated entity field type at\n * the schema level. Annotate the entity field explicitly: `id!: Id<\"usr\">`.\n *\n * @example\n * ```ts\n * import { idTransformer } from \"@smonn/ids/typeorm\";\n * import { createTimestampId } from \"@smonn/ids\";\n * import type { Id } from \"@smonn/ids\";\n * import { Column, Entity } from \"typeorm\";\n *\n * const usr = createTimestampId(\"usr\");\n *\n * @Entity()\n * class User {\n * @Column({ type: \"text\", transformer: idTransformer(usr) })\n * id!: Id<\"usr\">;\n * }\n * ```\n */\nexport function idTransformer<Brand extends string>(codec: IdColumnCodec<Brand>): ValueTransformer {\n return {\n to(value: Id<Brand>): string {\n return value;\n },\n from(value: unknown): Id<Brand> {\n return readIdColumn(codec, value);\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"typeorm.mjs","names":[],"sources":["../src/adapters/typeorm.ts"],"sourcesContent":["import type { ValueTransformer } from \"typeorm\";\nimport { readIdColumn, readIdColumnNullable, type IdColumnCodec } from \"./adapter-types.js\";\nimport type { Id } from \"../types.js\";\n\n/** {@link IdsError} class, {@link isIdsError} type guard, and {@link IdsErrorCode} union — re-exported from `\"@smonn/ids\"` for convenience. */\nexport { IdsError, isIdsError, type IdsErrorCode } from \"../error.js\";\n\nexport type { IdColumnCodec };\n\n/**\n * Extension of {@link IdColumnCodec} that also exposes synchronous `generate()`.\n * Required by {@link beforeInsertHook} so that the hook can produce IDs at insert\n * time. Every full codec variant (Timestamp, Reverse Timestamp) satisfies this;\n * async-generate codecs (Opaque, Signed, Wrapped, Digest) do not and are therefore\n * unsupported by `beforeInsertHook`.\n */\nexport type IdGeneratingCodec<Brand extends string> = IdColumnCodec<Brand> & {\n generate(): Id<Brand>;\n};\n\n/**\n * TypeORM column transformer for `Id<Brand>`.\n *\n * Returns a `ValueTransformer` object suitable for use in a TypeORM `@Column`\n * decorator's `transformer` option.\n *\n * **Write path (`to`):** passes the `Id<Brand>` directly to the database — it is\n * already the canonical string form.\n *\n * **Read path (`from`):** normalises the raw database value via `codec.safeParse()`.\n * Throws `IdsError` with code `\"invalid_id\"` if the value does not parse as a valid\n * `Id<Brand>`.\n *\n * **TypeORM branding caveat:** TypeORM cannot brand a generated entity field type at\n * the schema level. Annotate the entity field explicitly: `id!: Id<\"usr\">`.\n *\n * @example\n * ```ts\n * import { idTransformer } from \"@smonn/ids/typeorm\";\n * import { createTimestampId } from \"@smonn/ids\";\n * import type { Id } from \"@smonn/ids\";\n * import { Column, Entity } from \"typeorm\";\n *\n * const usr = createTimestampId(\"usr\");\n *\n * @Entity()\n * class User {\n * @Column({ type: \"text\", transformer: idTransformer(usr) })\n * id!: Id<\"usr\">;\n * }\n * ```\n */\nexport function idTransformer<Brand extends string>(codec: IdColumnCodec<Brand>): ValueTransformer {\n return {\n to(value: Id<Brand>): string {\n return value;\n },\n from(value: unknown): Id<Brand> {\n return readIdColumn(codec, value);\n },\n };\n}\n\n/**\n * Returns a function suitable for use inside a TypeORM `@BeforeInsert()` lifecycle\n * hook that auto-generates an `Id<Brand>` for `fieldName` when the field is absent\n * (`null` or `undefined`) on the entity at insert time. If the field already has a\n * value it is left unchanged.\n *\n * Requires a codec variant that exposes a synchronous `generate()` — see\n * {@link IdGeneratingCodec}. Only the **Timestamp codec** and **Reverse Timestamp\n * codec** qualify; passing an async-generate codec (Opaque, Signed, Wrapped, Digest)\n * is a **compile-time type error**.\n *\n * Pair with {@link idTransformer} on the same column: `idTransformer` handles the\n * read/write path; `beforeInsertHook` handles generation.\n *\n * @example\n * ```ts\n * import { idTransformer, beforeInsertHook } from \"@smonn/ids/typeorm\";\n * import { createTimestampId } from \"@smonn/ids\";\n * import type { Id } from \"@smonn/ids\";\n * import { BeforeInsert, Column, Entity } from \"typeorm\";\n *\n * const usr = createTimestampId(\"usr\");\n * const fillUserId = beforeInsertHook(\"id\", usr);\n *\n * @Entity()\n * class User {\n * @Column({ type: \"text\", transformer: idTransformer(usr) })\n * id!: Id<\"usr\">;\n *\n * @BeforeInsert()\n * generateId() {\n * fillUserId(this);\n * }\n * }\n * ```\n *\n * @remarks\n * **Gating rule:** `beforeInsertHook` requires a synchronous `generate()` codec\n * (`IdGeneratingCodec`). Async-generate codecs — Opaque Timestamp, Signed Timestamp,\n * Wrapped key, and Digest — do not satisfy `IdGeneratingCodec` and cannot be passed\n * here. TypeORM `@BeforeInsert` can be async, but synchronous generation keeps the\n * hook ergonomic and matches the full Prisma parity shape.\n *\n * **Read/write path:** Use {@link idTransformer} on the column for database\n * read/write transforms. `beforeInsertHook` only handles the generation step — it\n * does not replace the transformer.\n */\nexport function beforeInsertHook<Brand extends string>(\n fieldName: string,\n codec: IdGeneratingCodec<Brand>,\n): (entity: Record<string, unknown>) => void {\n return function (entity: Record<string, unknown>): void {\n if (entity[fieldName] == null) {\n entity[fieldName] = codec.generate();\n }\n };\n}\n\n/**\n * TypeORM column transformer for a **nullable** `Id<Brand>` column.\n *\n * Behaves like {@link idTransformer} but `from` returns `null` for `null` /\n * `undefined` database values and `to` passes `null` / `undefined` through\n * unchanged. Use for optional foreign keys.\n *\n * @example\n * ```ts\n * import { nullableIdTransformer } from \"@smonn/ids/typeorm\";\n * import { createTimestampId } from \"@smonn/ids\";\n * import type { Id } from \"@smonn/ids\";\n * import { Column, Entity } from \"typeorm\";\n *\n * const usr = createTimestampId(\"usr\");\n *\n * @Entity()\n * class Post {\n * @Column({ type: \"text\", nullable: true, transformer: nullableIdTransformer(usr) })\n * authorId!: Id<\"usr\"> | null;\n * }\n * ```\n */\nexport function nullableIdTransformer<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n): ValueTransformer {\n return {\n to(value: Id<Brand> | null | undefined): string | null | undefined {\n return value;\n },\n from(value: unknown): Id<Brand> | null {\n return readIdColumnNullable(codec, value);\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDA,SAAgB,cAAoC,OAA+C;CACjG,OAAO;EACL,GAAG,OAA0B;GAC3B,OAAO;EACT;EACA,KAAK,OAA2B;GAC9B,OAAO,aAAa,OAAO,KAAK;EAClC;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiDA,SAAgB,iBACd,WACA,OAC2C;CAC3C,OAAO,SAAU,QAAuC;EACtD,IAAI,OAAO,cAAc,MACvB,OAAO,aAAa,MAAM,SAAS;CAEvC;AACF;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,SAAgB,sBACd,OACkB;CAClB,OAAO;EACL,GAAG,OAAgE;GACjE,OAAO;EACT;EACA,KAAK,OAAkC;GACrC,OAAO,qBAAqB,OAAO,KAAK;EAC1C;CACF;AACF"}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"adapter-types-7wWdELSh.mjs","names":[],"sources":["../src/adapters/adapter-types.ts"],"sourcesContent":["import { IdsError } from \"../error.js\";\nimport type { Id, ParseError, ParseResult } from \"../types.js\";\n\n/** Discriminated failure value passed to `onError` and emitted to the framework's error handler. */\nexport type IdParamFailure =\n | { readonly reason: \"brand_mismatch\"; readonly status: number }\n | { readonly reason: \"malformed\"; readonly status: number };\n\n/** Minimum structural type required by web and ORM adapters. Any codec variant satisfies this — all expose `safeParse`. Adapters only ever call `safeParse` — never key-dependent methods like `extractTimestamp`, `wrap`, or `unwrap`. */\nexport type IdCodec<Brand extends string> = {\n safeParse(value: unknown): ParseResult<Brand>;\n};\n\n/** Re-exported from ORM adapter subpaths (`@smonn/ids/drizzle`, `@smonn/ids/prisma`, `@smonn/ids/kysely`) under the public name; structurally identical to {@link IdCodec}. */\nexport type IdColumnCodec<Brand extends string> = IdCodec<Brand>;\n\n/** Parses `value` as `Id<Brand>` via `codec.safeParse`; throws `IdsError(\"invalid_id\")` on failure. Shared read helper for ORM adapters. */\nexport function readIdColumn<Brand extends string>(\n codec: IdCodec<Brand>,\n value: unknown,\n): Id<Brand> {\n const result = codec.safeParse(value);\n if (!result.ok) {\n throw new IdsError(\"invalid_id\", `invalid ID from database: ${result.error}`, {\n cause: result.error,\n });\n }\n return result.id;\n}\n\n/**\n * Maps a `ParseError` to `{ reason, status }` for web adapter failure handling.\n *\n * - `invalid_prefix` → `brand_mismatch` / default 404\n * - anything else → `malformed` / default 400\n * - `options.status[reason]` overrides the default for that reason\n */\nexport function resolveIdParamFailure(\n error: ParseError,\n options?: { status?: { brand_mismatch?: number; malformed?: number } },\n): IdParamFailure {\n const reason = error === \"invalid_prefix\" ? (\"brand_mismatch\" as const) : (\"malformed\" as const);\n const defaultStatus = reason === \"brand_mismatch\" ? 404 : 400;\n const status = options?.status?.[reason] ?? defaultStatus;\n return { reason, status };\n}\n"],"mappings":";;;AAiBA,SAAgB,aACd,OACA,OACW;CACX,MAAM,SAAS,MAAM,UAAU,KAAK;CACpC,IAAI,CAAC,OAAO,IACV,MAAM,IAAI,SAAS,cAAc,6BAA6B,OAAO,SAAS,EAC5E,OAAO,OAAO,MAChB,CAAC;CAEH,OAAO,OAAO;AAChB;;;;;;;;AASA,SAAgB,sBACd,OACA,SACgB;CAChB,MAAM,SAAS,UAAU,mBAAoB,mBAA8B;CAC3E,MAAM,gBAAgB,WAAW,mBAAmB,MAAM;CAE1D,OAAO;EAAE;EAAQ,QADF,SAAS,SAAS,WAAW;CACpB;AAC1B"}
|