@smonn/ids 1.0.0-rc.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 +1 -1
- package/dist/drizzle.d.mts +116 -1
- package/dist/drizzle.d.mts.map +1 -1
- package/dist/drizzle.mjs +131 -1
- package/dist/drizzle.mjs.map +1 -1
- package/dist/kysely.d.mts +31 -1
- package/dist/kysely.d.mts.map +1 -1
- package/dist/kysely.mjs +22 -1
- package/dist/kysely.mjs.map +1 -1
- package/dist/mikro-orm.d.mts +40 -1
- package/dist/mikro-orm.d.mts.map +1 -1
- package/dist/mikro-orm.mjs +33 -1
- package/dist/mikro-orm.mjs.map +1 -1
- package/dist/prisma.d.mts +37 -1
- package/dist/prisma.d.mts.map +1 -1
- package/dist/prisma.mjs +41 -1
- package/dist/prisma.mjs.map +1 -1
- package/dist/typeorm.d.mts +60 -1
- package/dist/typeorm.d.mts.map +1 -1
- package/dist/typeorm.mjs +53 -1
- package/dist/typeorm.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -62,7 +62,7 @@ Try them all live in the [playground](https://ids.smonn.se/playground/).
|
|
|
62
62
|
Framework and ORM adapters ship as optional subpath exports (each requires its own peer dependency):
|
|
63
63
|
|
|
64
64
|
- **HTTP params:** [Hono](https://ids.smonn.se/adapters/hono/), [Express](https://ids.smonn.se/adapters/express/), [Fastify](https://ids.smonn.se/adapters/fastify/) — `idParam`, `idQuery` middleware; [NestJS](https://ids.smonn.se/adapters/nestjs/) — `ParseIdPipe`
|
|
65
|
-
- **ORM columns:** [Drizzle](https://ids.smonn.se/adapters/drizzle/) — `idColumn`, `idColumnMysql`, `idColumnSqlite`, [Kysely](https://ids.smonn.se/adapters/kysely/) — `idPlugin` / `idColumn`, [MikroORM](https://ids.smonn.se/adapters/mikro-orm/) — `idType`, [Prisma](https://ids.smonn.se/adapters/prisma/) — `idField`, [TypeORM](https://ids.smonn.se/adapters/typeorm/) — `idTransformer`
|
|
65
|
+
- **ORM columns:** [Drizzle](https://ids.smonn.se/adapters/drizzle/) — `idColumn`, `idColumnMysql`, `idColumnSqlite`, `nullableIdColumn`, [Kysely](https://ids.smonn.se/adapters/kysely/) — `idPlugin` / `idColumn`, `nullableIdColumn`, [MikroORM](https://ids.smonn.se/adapters/mikro-orm/) — `idType`, `nullableIdType`, `idField`, [Prisma](https://ids.smonn.se/adapters/prisma/) — `idField`, `nullableIdField`, [TypeORM](https://ids.smonn.se/adapters/typeorm/) — `idTransformer`, `nullableIdTransformer`
|
|
66
66
|
- **GraphQL:** [GraphQL](https://ids.smonn.se/adapters/graphql/) — `idScalar` custom scalar
|
|
67
67
|
- **CLI:** brand-agnostic `inspect` / `generate` / `keygen` — `npx @smonn/ids --help` ([docs](https://ids.smonn.se/cli/))
|
|
68
68
|
|
package/dist/drizzle.d.mts
CHANGED
|
@@ -4,9 +4,20 @@ import { n as IdsErrorCode, r as isIdsError, t as IdsError } from "./error-CifcK
|
|
|
4
4
|
import { ConvertCustomConfig, PgCustomColumnBuilder } from "drizzle-orm/pg-core";
|
|
5
5
|
import { ConvertCustomConfig as ConvertCustomConfig$1, MySqlCustomColumnBuilder } from "drizzle-orm/mysql-core";
|
|
6
6
|
import { ConvertCustomConfig as ConvertCustomConfig$2, SQLiteCustomColumnBuilder } from "drizzle-orm/sqlite-core";
|
|
7
|
+
import { HasDefault, HasRuntimeDefault } from "drizzle-orm/column-builder";
|
|
7
8
|
|
|
8
9
|
//#region src/adapters/drizzle.d.ts
|
|
9
10
|
/**
|
|
11
|
+
* Extension of {@link IdColumnCodec} that also exposes synchronous `generate()`.
|
|
12
|
+
* Required by the `generatedIdColumn` family so that Drizzle's `.$defaultFn` can
|
|
13
|
+
* produce IDs at write time without explicit call-site wiring. Only the **Timestamp
|
|
14
|
+
* codec** and **Reverse Timestamp codec** satisfy this; async-generate codecs
|
|
15
|
+
* (Opaque, Signed, Wrapped, Digest) do not and are a compile-time error.
|
|
16
|
+
*/
|
|
17
|
+
type IdGeneratingCodec<Brand extends string> = IdColumnCodec<Brand> & {
|
|
18
|
+
generate(): Id<Brand>;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
10
21
|
* Drizzle custom column type that stores an `Id<Brand>` as a canonical SQL string value in PostgreSQL.
|
|
11
22
|
*
|
|
12
23
|
* **Write path:** passes the `Id<Brand>` directly to the driver — it is already
|
|
@@ -113,6 +124,110 @@ declare function nullableIdColumn<Brand extends string>(codec: IdColumnCodec<Bra
|
|
|
113
124
|
data: Id<Brand> | null;
|
|
114
125
|
driverData: string | null;
|
|
115
126
|
}>>;
|
|
127
|
+
/**
|
|
128
|
+
* Drizzle custom column type that stores an `Id<Brand>` as a canonical SQL string value
|
|
129
|
+
* in PostgreSQL, with a client-side `.$defaultFn` so inserts that omit the field receive
|
|
130
|
+
* a freshly generated ID automatically.
|
|
131
|
+
*
|
|
132
|
+
* **Write path:** `.$defaultFn(() => codec.generate())` is wired — if the field is absent
|
|
133
|
+
* on insert, Drizzle calls `codec.generate()` to produce a new `Id<Brand>`.
|
|
134
|
+
*
|
|
135
|
+
* **Read path:** normalises the raw DB string via `codec.safeParse()`, not strict `is()`.
|
|
136
|
+
* Throws `IdsError("invalid_id")` if the value from the database does not parse as a
|
|
137
|
+
* valid `Id<Brand>`.
|
|
138
|
+
*
|
|
139
|
+
* Requires a codec that exposes synchronous `generate()` — see {@link IdGeneratingCodec}.
|
|
140
|
+
* Only the **Timestamp codec** and **Reverse Timestamp codec** qualify; Opaque, Signed,
|
|
141
|
+
* Wrapped, and Digest codecs are a compile-time error.
|
|
142
|
+
*
|
|
143
|
+
* @param codec - The brand-scoped codec used to generate and parse values.
|
|
144
|
+
* @param options - Optional column configuration.
|
|
145
|
+
* @param options.columnType - SQL column type to use (default: `"text"`). Pass
|
|
146
|
+
* `"varchar(30)"` or `"char(26)"` to match an existing DDL or index strategy.
|
|
147
|
+
* The value is passed through verbatim — no validation is performed.
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```ts
|
|
151
|
+
* import { generatedIdColumn } from "@smonn/ids/drizzle";
|
|
152
|
+
* import { createTimestampId } from "@smonn/ids";
|
|
153
|
+
*
|
|
154
|
+
* const usr = createTimestampId("usr");
|
|
155
|
+
* export const users = pgTable("users", { id: generatedIdColumn(usr).primaryKey() });
|
|
156
|
+
* // id is auto-filled on insert — no hand-supplied id needed
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
declare function generatedIdColumn<Brand extends string>(codec: IdGeneratingCodec<Brand>, options?: {
|
|
160
|
+
columnType?: string;
|
|
161
|
+
}): HasRuntimeDefault<HasDefault<PgCustomColumnBuilder<ConvertCustomConfig<"", {
|
|
162
|
+
data: Id<Brand>;
|
|
163
|
+
driverData: string;
|
|
164
|
+
}>>>>;
|
|
165
|
+
/**
|
|
166
|
+
* Drizzle custom column type that stores an `Id<Brand>` as a canonical `text` value
|
|
167
|
+
* in MySQL, with a client-side `.$defaultFn` so inserts that omit the field receive
|
|
168
|
+
* a freshly generated ID automatically.
|
|
169
|
+
*
|
|
170
|
+
* **Write path:** `.$defaultFn(() => codec.generate())` is wired — if the field is absent
|
|
171
|
+
* on insert, Drizzle calls `codec.generate()` to produce a new `Id<Brand>`.
|
|
172
|
+
*
|
|
173
|
+
* **Read path:** normalises the raw DB string via `codec.safeParse()`, not strict `is()`.
|
|
174
|
+
* Throws `IdsError("invalid_id")` if the value from the database does not parse as a
|
|
175
|
+
* valid `Id<Brand>`.
|
|
176
|
+
*
|
|
177
|
+
* Requires a codec that exposes synchronous `generate()` — see {@link IdGeneratingCodec}.
|
|
178
|
+
* Only the **Timestamp codec** and **Reverse Timestamp codec** qualify; Opaque, Signed,
|
|
179
|
+
* Wrapped, and Digest codecs are a compile-time error.
|
|
180
|
+
*
|
|
181
|
+
* @param codec - The brand-scoped codec used to generate and parse values.
|
|
182
|
+
* Column type is always `text` and cannot be overridden.
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```ts
|
|
186
|
+
* import { generatedIdColumnMysql } from "@smonn/ids/drizzle";
|
|
187
|
+
* import { createTimestampId } from "@smonn/ids";
|
|
188
|
+
*
|
|
189
|
+
* const usr = createTimestampId("usr");
|
|
190
|
+
* export const users = mysqlTable("users", { id: generatedIdColumnMysql(usr).primaryKey() });
|
|
191
|
+
* // id is auto-filled on insert — no hand-supplied id needed
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
declare function generatedIdColumnMysql<Brand extends string>(codec: IdGeneratingCodec<Brand>): HasRuntimeDefault<HasDefault<MySqlCustomColumnBuilder<ConvertCustomConfig$1<"", {
|
|
195
|
+
data: Id<Brand>;
|
|
196
|
+
driverData: string;
|
|
197
|
+
}>>>>;
|
|
198
|
+
/**
|
|
199
|
+
* Drizzle custom column type that stores an `Id<Brand>` as a canonical `text` value
|
|
200
|
+
* in SQLite, with a client-side `.$defaultFn` so inserts that omit the field receive
|
|
201
|
+
* a freshly generated ID automatically.
|
|
202
|
+
*
|
|
203
|
+
* **Write path:** `.$defaultFn(() => codec.generate())` is wired — if the field is absent
|
|
204
|
+
* on insert, Drizzle calls `codec.generate()` to produce a new `Id<Brand>`.
|
|
205
|
+
*
|
|
206
|
+
* **Read path:** normalises the raw DB string via `codec.safeParse()`, not strict `is()`.
|
|
207
|
+
* Throws `IdsError("invalid_id")` if the value from the database does not parse as a
|
|
208
|
+
* valid `Id<Brand>`.
|
|
209
|
+
*
|
|
210
|
+
* Requires a codec that exposes synchronous `generate()` — see {@link IdGeneratingCodec}.
|
|
211
|
+
* Only the **Timestamp codec** and **Reverse Timestamp codec** qualify; Opaque, Signed,
|
|
212
|
+
* Wrapped, and Digest codecs are a compile-time error.
|
|
213
|
+
*
|
|
214
|
+
* @param codec - The brand-scoped codec used to generate and parse values.
|
|
215
|
+
* Column type is always `text` and cannot be overridden.
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* ```ts
|
|
219
|
+
* import { generatedIdColumnSqlite } from "@smonn/ids/drizzle";
|
|
220
|
+
* import { createTimestampId } from "@smonn/ids";
|
|
221
|
+
*
|
|
222
|
+
* const usr = createTimestampId("usr");
|
|
223
|
+
* export const users = sqliteTable("users", { id: generatedIdColumnSqlite(usr).primaryKey() });
|
|
224
|
+
* // id is auto-filled on insert — no hand-supplied id needed
|
|
225
|
+
* ```
|
|
226
|
+
*/
|
|
227
|
+
declare function generatedIdColumnSqlite<Brand extends string>(codec: IdGeneratingCodec<Brand>): HasRuntimeDefault<HasDefault<SQLiteCustomColumnBuilder<ConvertCustomConfig$2<"", {
|
|
228
|
+
data: Id<Brand>;
|
|
229
|
+
driverData: string;
|
|
230
|
+
}>>>>;
|
|
116
231
|
//#endregion
|
|
117
|
-
export { type IdColumnCodec, IdsError, type IdsErrorCode, idColumn, idColumnMysql, idColumnSqlite, isIdsError, nullableIdColumn };
|
|
232
|
+
export { type IdColumnCodec, IdGeneratingCodec, IdsError, type IdsErrorCode, generatedIdColumn, generatedIdColumnMysql, generatedIdColumnSqlite, idColumn, idColumnMysql, idColumnSqlite, isIdsError, nullableIdColumn };
|
|
118
233
|
//# sourceMappingURL=drizzle.d.mts.map
|
package/dist/drizzle.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drizzle.d.mts","names":[],"sources":["../src/adapters/drizzle.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"drizzle.d.mts","names":[],"sources":["../src/adapters/drizzle.ts"],"mappings":";;;;;;;;;AA+BA;;;;;;;AAAA,KAAY,iBAAA,yBAA0C,aAAA,CAAc,KAAA;EAClE,QAAA,IAAY,EAAA,CAAG,KAAA;AAAA;;;;;;;AAAA;AAgCjB;;;;;;;;;;;;;;;;;;;;;;iBAAgB,QAAA,uBACd,KAAA,EAAO,aAAA,CAAc,KAAA,GACrB,OAAA;EAAY,UAAA;AAAA,IACX,qBAAA,CAAsB,mBAAA;EAA0B,IAAA,EAAM,EAAA,CAAG,KAAA;EAAQ,UAAA;AAAA;;;;;;;;;;;;;;;;;;;;AAqCQ;iBAF5D,aAAA,uBACd,KAAA,EAAO,aAAA,CAAc,KAAA,IACpB,wBAAA,CAAyB,qBAAA;EAA+B,IAAA,EAAM,EAAA,CAAG,KAAA;EAAQ,UAAA;AAAA;;;;;;;;;;;;;;;;;;;AAqCzB;AAmCnD;iBAtCgB,cAAA,uBACd,KAAA,EAAO,aAAA,CAAc,KAAA,IACpB,yBAAA,CACD,qBAAA;EAAgC,IAAA,EAAM,EAAA,CAAG,KAAA;EAAQ,UAAA;AAAA;;;;;;;;;;;;;;;;;;AAsCC;AA+CpD;;iBAlDgB,gBAAA,uBACd,KAAA,EAAO,aAAA,CAAc,KAAA,IACpB,qBAAA,CACD,mBAAA;EAA0B,IAAA,EAAM,EAAA,CAAG,KAAA;EAAe,UAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;AAoDiB;AA8CrE;;;;;;;;;iBAnDgB,iBAAA,uBACd,KAAA,EAAO,iBAAA,CAAkB,KAAA,GACzB,OAAA;EAAY,UAAA;AAAA,IACX,iBAAA,CACD,UAAA,CACE,qBAAA,CAAsB,mBAAA;EAA0B,IAAA,EAAM,EAAA,CAAG,KAAA;EAAQ,UAAA;AAAA;;;;;;;;;;;;AAkDQ;AA6C7E;;;;;;;;;;;;;;;;;iBAjDgB,sBAAA,uBACd,KAAA,EAAO,iBAAA,CAAkB,KAAA,IACxB,iBAAA,CACD,UAAA,CACE,wBAAA,CAAyB,qBAAA;EAA+B,IAAA,EAAM,EAAA,CAAG,KAAA;EAAQ,UAAA;AAAA;;;;;;AAkDtB;;;;;;;;;;;;;;;;;;;;;;;;iBALvC,uBAAA,uBACd,KAAA,EAAO,iBAAA,CAAkB,KAAA,IACxB,iBAAA,CACD,UAAA,CACE,yBAAA,CACE,qBAAA;EAAgC,IAAA,EAAM,EAAA,CAAG,KAAA;EAAQ,UAAA;AAAA"}
|
package/dist/drizzle.mjs
CHANGED
|
@@ -146,7 +146,137 @@ function nullableIdColumn(codec) {
|
|
|
146
146
|
}
|
|
147
147
|
})();
|
|
148
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Drizzle custom column type that stores an `Id<Brand>` as a canonical SQL string value
|
|
151
|
+
* in PostgreSQL, with a client-side `.$defaultFn` so inserts that omit the field receive
|
|
152
|
+
* a freshly generated ID automatically.
|
|
153
|
+
*
|
|
154
|
+
* **Write path:** `.$defaultFn(() => codec.generate())` is wired — if the field is absent
|
|
155
|
+
* on insert, Drizzle calls `codec.generate()` to produce a new `Id<Brand>`.
|
|
156
|
+
*
|
|
157
|
+
* **Read path:** normalises the raw DB string via `codec.safeParse()`, not strict `is()`.
|
|
158
|
+
* Throws `IdsError("invalid_id")` if the value from the database does not parse as a
|
|
159
|
+
* valid `Id<Brand>`.
|
|
160
|
+
*
|
|
161
|
+
* Requires a codec that exposes synchronous `generate()` — see {@link IdGeneratingCodec}.
|
|
162
|
+
* Only the **Timestamp codec** and **Reverse Timestamp codec** qualify; Opaque, Signed,
|
|
163
|
+
* Wrapped, and Digest codecs are a compile-time error.
|
|
164
|
+
*
|
|
165
|
+
* @param codec - The brand-scoped codec used to generate and parse values.
|
|
166
|
+
* @param options - Optional column configuration.
|
|
167
|
+
* @param options.columnType - SQL column type to use (default: `"text"`). Pass
|
|
168
|
+
* `"varchar(30)"` or `"char(26)"` to match an existing DDL or index strategy.
|
|
169
|
+
* The value is passed through verbatim — no validation is performed.
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```ts
|
|
173
|
+
* import { generatedIdColumn } from "@smonn/ids/drizzle";
|
|
174
|
+
* import { createTimestampId } from "@smonn/ids";
|
|
175
|
+
*
|
|
176
|
+
* const usr = createTimestampId("usr");
|
|
177
|
+
* export const users = pgTable("users", { id: generatedIdColumn(usr).primaryKey() });
|
|
178
|
+
* // id is auto-filled on insert — no hand-supplied id needed
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
function generatedIdColumn(codec, options) {
|
|
182
|
+
const columnType = options?.columnType ?? "text";
|
|
183
|
+
return customType({
|
|
184
|
+
dataType() {
|
|
185
|
+
return columnType;
|
|
186
|
+
},
|
|
187
|
+
toDriver(value) {
|
|
188
|
+
return value;
|
|
189
|
+
},
|
|
190
|
+
fromDriver(value) {
|
|
191
|
+
return readIdColumn(codec, value);
|
|
192
|
+
}
|
|
193
|
+
})().$defaultFn(() => codec.generate());
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Drizzle custom column type that stores an `Id<Brand>` as a canonical `text` value
|
|
197
|
+
* in MySQL, with a client-side `.$defaultFn` so inserts that omit the field receive
|
|
198
|
+
* a freshly generated ID automatically.
|
|
199
|
+
*
|
|
200
|
+
* **Write path:** `.$defaultFn(() => codec.generate())` is wired — if the field is absent
|
|
201
|
+
* on insert, Drizzle calls `codec.generate()` to produce a new `Id<Brand>`.
|
|
202
|
+
*
|
|
203
|
+
* **Read path:** normalises the raw DB string via `codec.safeParse()`, not strict `is()`.
|
|
204
|
+
* Throws `IdsError("invalid_id")` if the value from the database does not parse as a
|
|
205
|
+
* valid `Id<Brand>`.
|
|
206
|
+
*
|
|
207
|
+
* Requires a codec that exposes synchronous `generate()` — see {@link IdGeneratingCodec}.
|
|
208
|
+
* Only the **Timestamp codec** and **Reverse Timestamp codec** qualify; Opaque, Signed,
|
|
209
|
+
* Wrapped, and Digest codecs are a compile-time error.
|
|
210
|
+
*
|
|
211
|
+
* @param codec - The brand-scoped codec used to generate and parse values.
|
|
212
|
+
* Column type is always `text` and cannot be overridden.
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* ```ts
|
|
216
|
+
* import { generatedIdColumnMysql } from "@smonn/ids/drizzle";
|
|
217
|
+
* import { createTimestampId } from "@smonn/ids";
|
|
218
|
+
*
|
|
219
|
+
* const usr = createTimestampId("usr");
|
|
220
|
+
* export const users = mysqlTable("users", { id: generatedIdColumnMysql(usr).primaryKey() });
|
|
221
|
+
* // id is auto-filled on insert — no hand-supplied id needed
|
|
222
|
+
* ```
|
|
223
|
+
*/
|
|
224
|
+
function generatedIdColumnMysql(codec) {
|
|
225
|
+
return customType$1({
|
|
226
|
+
dataType() {
|
|
227
|
+
return "text";
|
|
228
|
+
},
|
|
229
|
+
toDriver(value) {
|
|
230
|
+
return value;
|
|
231
|
+
},
|
|
232
|
+
fromDriver(value) {
|
|
233
|
+
return readIdColumn(codec, value);
|
|
234
|
+
}
|
|
235
|
+
})().$defaultFn(() => codec.generate());
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Drizzle custom column type that stores an `Id<Brand>` as a canonical `text` value
|
|
239
|
+
* in SQLite, with a client-side `.$defaultFn` so inserts that omit the field receive
|
|
240
|
+
* a freshly generated ID automatically.
|
|
241
|
+
*
|
|
242
|
+
* **Write path:** `.$defaultFn(() => codec.generate())` is wired — if the field is absent
|
|
243
|
+
* on insert, Drizzle calls `codec.generate()` to produce a new `Id<Brand>`.
|
|
244
|
+
*
|
|
245
|
+
* **Read path:** normalises the raw DB string via `codec.safeParse()`, not strict `is()`.
|
|
246
|
+
* Throws `IdsError("invalid_id")` if the value from the database does not parse as a
|
|
247
|
+
* valid `Id<Brand>`.
|
|
248
|
+
*
|
|
249
|
+
* Requires a codec that exposes synchronous `generate()` — see {@link IdGeneratingCodec}.
|
|
250
|
+
* Only the **Timestamp codec** and **Reverse Timestamp codec** qualify; Opaque, Signed,
|
|
251
|
+
* Wrapped, and Digest codecs are a compile-time error.
|
|
252
|
+
*
|
|
253
|
+
* @param codec - The brand-scoped codec used to generate and parse values.
|
|
254
|
+
* Column type is always `text` and cannot be overridden.
|
|
255
|
+
*
|
|
256
|
+
* @example
|
|
257
|
+
* ```ts
|
|
258
|
+
* import { generatedIdColumnSqlite } from "@smonn/ids/drizzle";
|
|
259
|
+
* import { createTimestampId } from "@smonn/ids";
|
|
260
|
+
*
|
|
261
|
+
* const usr = createTimestampId("usr");
|
|
262
|
+
* export const users = sqliteTable("users", { id: generatedIdColumnSqlite(usr).primaryKey() });
|
|
263
|
+
* // id is auto-filled on insert — no hand-supplied id needed
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
function generatedIdColumnSqlite(codec) {
|
|
267
|
+
return customType$2({
|
|
268
|
+
dataType() {
|
|
269
|
+
return "text";
|
|
270
|
+
},
|
|
271
|
+
toDriver(value) {
|
|
272
|
+
return value;
|
|
273
|
+
},
|
|
274
|
+
fromDriver(value) {
|
|
275
|
+
return readIdColumn(codec, value);
|
|
276
|
+
}
|
|
277
|
+
})().$defaultFn(() => codec.generate());
|
|
278
|
+
}
|
|
149
279
|
//#endregion
|
|
150
|
-
export { IdsError, idColumn, idColumnMysql, idColumnSqlite, isIdsError, nullableIdColumn };
|
|
280
|
+
export { IdsError, generatedIdColumn, generatedIdColumnMysql, generatedIdColumnSqlite, idColumn, idColumnMysql, idColumnSqlite, isIdsError, nullableIdColumn };
|
|
151
281
|
|
|
152
282
|
//# sourceMappingURL=drizzle.mjs.map
|
package/dist/drizzle.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drizzle.mjs","names":["customTypeMysql","customTypeSqlite"],"sources":["../src/adapters/drizzle.ts"],"sourcesContent":["import {\n customType,\n type ConvertCustomConfig,\n type PgCustomColumnBuilder,\n} from \"drizzle-orm/pg-core\";\nimport {\n customType as customTypeMysql,\n type ConvertCustomConfig as ConvertCustomConfigMysql,\n type MySqlCustomColumnBuilder,\n} from \"drizzle-orm/mysql-core\";\nimport {\n customType as customTypeSqlite,\n type ConvertCustomConfig as ConvertCustomConfigSqlite,\n type SQLiteCustomColumnBuilder,\n} from \"drizzle-orm/sqlite-core\";\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 * Drizzle custom column type that stores an `Id<Brand>` as a canonical SQL string value in PostgreSQL.\n *\n * **Write path:** passes the `Id<Brand>` directly to the driver — it is already\n * the canonical string form.\n *\n * **Read path:** normalises the raw DB string via `codec.safeParse()`, not strict\n * `is()`. Data at rest should already be canonical per ADR-0003, but `safeParse`\n * is a safe boundary in case stale non-canonical values exist. Throws if the\n * value from the database does not parse as a valid `Id<Brand>`.\n *\n * @param codec - The brand-scoped codec used to parse values read from the database.\n * @param options - Optional column configuration.\n * @param options.columnType - SQL column type to use (default: `\"text\"`). Pass\n * `\"varchar(30)\"` or `\"char(26)\"` to match an existing DDL or index strategy.\n * The value is passed through verbatim — no validation is performed.\n *\n * @example\n * ```ts\n * import { idColumn } from \"@smonn/ids/drizzle\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n * // default: text column\n * export const users = pgTable(\"users\", { id: idColumn(usr).primaryKey() });\n * // explicit varchar column\n * export const orgs = pgTable(\"orgs\", { id: idColumn(usr, { columnType: \"varchar(30)\" }).primaryKey() });\n * ```\n */\nexport function idColumn<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n options?: { columnType?: string },\n): PgCustomColumnBuilder<ConvertCustomConfig<\"\", { data: Id<Brand>; driverData: string }>> {\n const columnType = options?.columnType ?? \"text\";\n return customType<{ data: Id<Brand>; driverData: string }>({\n dataType() {\n return columnType;\n },\n toDriver(value: Id<Brand>): string {\n return value;\n },\n fromDriver(value: string): Id<Brand> {\n return readIdColumn(codec, value);\n },\n })();\n}\n\n/**\n * Drizzle custom column type that stores an `Id<Brand>` as a canonical `text` value in MySQL.\n *\n * **Write path:** passes the `Id<Brand>` directly to the driver — it is already\n * the canonical string form.\n *\n * **Read path:** normalises the raw DB string via `codec.safeParse()`, not strict\n * `is()`. Throws `IdsError(\"invalid_id\")` if the value from the database does not\n * parse as a valid `Id<Brand>`.\n *\n * @example\n * ```ts\n * import { idColumnMysql } from \"@smonn/ids/drizzle\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n * export const users = mysqlTable(\"users\", { id: idColumnMysql(usr).primaryKey() });\n * // users.id is Id<\"usr\"> end-to-end\n * ```\n */\nexport function idColumnMysql<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n): MySqlCustomColumnBuilder<ConvertCustomConfigMysql<\"\", { data: Id<Brand>; driverData: string }>> {\n return customTypeMysql<{ data: Id<Brand>; driverData: string }>({\n dataType() {\n return \"text\";\n },\n toDriver(value: Id<Brand>): string {\n return value;\n },\n fromDriver(value: string): Id<Brand> {\n return readIdColumn(codec, value);\n },\n })();\n}\n\n/**\n * Drizzle custom column type that stores an `Id<Brand>` as a canonical `text` value in SQLite.\n *\n * **Write path:** passes the `Id<Brand>` directly to the driver — it is already\n * the canonical string form.\n *\n * **Read path:** normalises the raw DB string via `codec.safeParse()`, not strict\n * `is()`. Throws `IdsError(\"invalid_id\")` if the value from the database does not\n * parse as a valid `Id<Brand>`.\n *\n * @example\n * ```ts\n * import { idColumnSqlite } from \"@smonn/ids/drizzle\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n * export const users = sqliteTable(\"users\", { id: idColumnSqlite(usr).primaryKey() });\n * // users.id is Id<\"usr\"> end-to-end\n * ```\n */\nexport function idColumnSqlite<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n): SQLiteCustomColumnBuilder<\n ConvertCustomConfigSqlite<\"\", { data: Id<Brand>; driverData: string }>\n> {\n return customTypeSqlite<{ data: Id<Brand>; driverData: string }>({\n dataType() {\n return \"text\";\n },\n toDriver(value: Id<Brand>): string {\n return value;\n },\n fromDriver(value: string): Id<Brand> {\n return readIdColumn(codec, value);\n },\n })();\n}\n\n/**\n * Drizzle custom column type for a **nullable** `Id<Brand>` column.\n *\n * Behaves identically to {@link idColumn} except that `null` and `undefined`\n * driver values are passed through as `null` rather than throwing. Use for\n * optional foreign keys, `LEFT JOIN` results, and any column that is\n * legitimately absent.\n *\n * @example\n * ```ts\n * import { nullableIdColumn } from \"@smonn/ids/drizzle\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n * export const posts = pgTable(\"posts\", {\n * authorId: nullableIdColumn(usr),\n * });\n * // posts.authorId is Id<\"usr\"> | null end-to-end\n * ```\n */\nexport function nullableIdColumn<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n): PgCustomColumnBuilder<\n ConvertCustomConfig<\"\", { data: Id<Brand> | null; driverData: string | null }>\n> {\n return customType<{ data: Id<Brand> | null; driverData: string | null }>({\n dataType() {\n return \"text\";\n },\n toDriver(value: Id<Brand> | null): string | null {\n return value;\n },\n fromDriver(value: string | null): Id<Brand> | null {\n return readIdColumnNullable(codec, value);\n },\n })();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDA,SAAgB,SACd,OACA,SACyF;CACzF,MAAM,aAAa,SAAS,cAAc;CAC1C,OAAO,WAAoD;EACzD,WAAW;GACT,OAAO;EACT;EACA,SAAS,OAA0B;GACjC,OAAO;EACT;EACA,WAAW,OAA0B;GACnC,OAAO,aAAa,OAAO,KAAK;EAClC;CACF,CAAC,CAAC,CAAC;AACL;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,cACd,OACiG;CACjG,OAAOA,aAAyD;EAC9D,WAAW;GACT,OAAO;EACT;EACA,SAAS,OAA0B;GACjC,OAAO;EACT;EACA,WAAW,OAA0B;GACnC,OAAO,aAAa,OAAO,KAAK;EAClC;CACF,CAAC,CAAC,CAAC;AACL;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,eACd,OAGA;CACA,OAAOC,aAA0D;EAC/D,WAAW;GACT,OAAO;EACT;EACA,SAAS,OAA0B;GACjC,OAAO;EACT;EACA,WAAW,OAA0B;GACnC,OAAO,aAAa,OAAO,KAAK;EAClC;CACF,CAAC,CAAC,CAAC;AACL;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,iBACd,OAGA;CACA,OAAO,WAAkE;EACvE,WAAW;GACT,OAAO;EACT;EACA,SAAS,OAAwC;GAC/C,OAAO;EACT;EACA,WAAW,OAAwC;GACjD,OAAO,qBAAqB,OAAO,KAAK;EAC1C;CACF,CAAC,CAAC,CAAC;AACL"}
|
|
1
|
+
{"version":3,"file":"drizzle.mjs","names":["customTypeMysql","customTypeSqlite"],"sources":["../src/adapters/drizzle.ts"],"sourcesContent":["import {\n customType,\n type ConvertCustomConfig,\n type PgCustomColumnBuilder,\n} from \"drizzle-orm/pg-core\";\nimport {\n customType as customTypeMysql,\n type ConvertCustomConfig as ConvertCustomConfigMysql,\n type MySqlCustomColumnBuilder,\n} from \"drizzle-orm/mysql-core\";\nimport {\n customType as customTypeSqlite,\n type ConvertCustomConfig as ConvertCustomConfigSqlite,\n type SQLiteCustomColumnBuilder,\n} from \"drizzle-orm/sqlite-core\";\nimport type { HasDefault, HasRuntimeDefault } from \"drizzle-orm/column-builder\";\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 the `generatedIdColumn` family so that Drizzle's `.$defaultFn` can\n * produce IDs at write time without explicit call-site wiring. Only the **Timestamp\n * codec** and **Reverse Timestamp codec** satisfy this; async-generate codecs\n * (Opaque, Signed, Wrapped, Digest) do not and are a compile-time error.\n */\nexport type IdGeneratingCodec<Brand extends string> = IdColumnCodec<Brand> & {\n generate(): Id<Brand>;\n};\n\n/**\n * Drizzle custom column type that stores an `Id<Brand>` as a canonical SQL string value in PostgreSQL.\n *\n * **Write path:** passes the `Id<Brand>` directly to the driver — it is already\n * the canonical string form.\n *\n * **Read path:** normalises the raw DB string via `codec.safeParse()`, not strict\n * `is()`. Data at rest should already be canonical per ADR-0003, but `safeParse`\n * is a safe boundary in case stale non-canonical values exist. Throws if the\n * value from the database does not parse as a valid `Id<Brand>`.\n *\n * @param codec - The brand-scoped codec used to parse values read from the database.\n * @param options - Optional column configuration.\n * @param options.columnType - SQL column type to use (default: `\"text\"`). Pass\n * `\"varchar(30)\"` or `\"char(26)\"` to match an existing DDL or index strategy.\n * The value is passed through verbatim — no validation is performed.\n *\n * @example\n * ```ts\n * import { idColumn } from \"@smonn/ids/drizzle\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n * // default: text column\n * export const users = pgTable(\"users\", { id: idColumn(usr).primaryKey() });\n * // explicit varchar column\n * export const orgs = pgTable(\"orgs\", { id: idColumn(usr, { columnType: \"varchar(30)\" }).primaryKey() });\n * ```\n */\nexport function idColumn<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n options?: { columnType?: string },\n): PgCustomColumnBuilder<ConvertCustomConfig<\"\", { data: Id<Brand>; driverData: string }>> {\n const columnType = options?.columnType ?? \"text\";\n return customType<{ data: Id<Brand>; driverData: string }>({\n dataType() {\n return columnType;\n },\n toDriver(value: Id<Brand>): string {\n return value;\n },\n fromDriver(value: string): Id<Brand> {\n return readIdColumn(codec, value);\n },\n })();\n}\n\n/**\n * Drizzle custom column type that stores an `Id<Brand>` as a canonical `text` value in MySQL.\n *\n * **Write path:** passes the `Id<Brand>` directly to the driver — it is already\n * the canonical string form.\n *\n * **Read path:** normalises the raw DB string via `codec.safeParse()`, not strict\n * `is()`. Throws `IdsError(\"invalid_id\")` if the value from the database does not\n * parse as a valid `Id<Brand>`.\n *\n * @example\n * ```ts\n * import { idColumnMysql } from \"@smonn/ids/drizzle\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n * export const users = mysqlTable(\"users\", { id: idColumnMysql(usr).primaryKey() });\n * // users.id is Id<\"usr\"> end-to-end\n * ```\n */\nexport function idColumnMysql<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n): MySqlCustomColumnBuilder<ConvertCustomConfigMysql<\"\", { data: Id<Brand>; driverData: string }>> {\n return customTypeMysql<{ data: Id<Brand>; driverData: string }>({\n dataType() {\n return \"text\";\n },\n toDriver(value: Id<Brand>): string {\n return value;\n },\n fromDriver(value: string): Id<Brand> {\n return readIdColumn(codec, value);\n },\n })();\n}\n\n/**\n * Drizzle custom column type that stores an `Id<Brand>` as a canonical `text` value in SQLite.\n *\n * **Write path:** passes the `Id<Brand>` directly to the driver — it is already\n * the canonical string form.\n *\n * **Read path:** normalises the raw DB string via `codec.safeParse()`, not strict\n * `is()`. Throws `IdsError(\"invalid_id\")` if the value from the database does not\n * parse as a valid `Id<Brand>`.\n *\n * @example\n * ```ts\n * import { idColumnSqlite } from \"@smonn/ids/drizzle\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n * export const users = sqliteTable(\"users\", { id: idColumnSqlite(usr).primaryKey() });\n * // users.id is Id<\"usr\"> end-to-end\n * ```\n */\nexport function idColumnSqlite<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n): SQLiteCustomColumnBuilder<\n ConvertCustomConfigSqlite<\"\", { data: Id<Brand>; driverData: string }>\n> {\n return customTypeSqlite<{ data: Id<Brand>; driverData: string }>({\n dataType() {\n return \"text\";\n },\n toDriver(value: Id<Brand>): string {\n return value;\n },\n fromDriver(value: string): Id<Brand> {\n return readIdColumn(codec, value);\n },\n })();\n}\n\n/**\n * Drizzle custom column type for a **nullable** `Id<Brand>` column.\n *\n * Behaves identically to {@link idColumn} except that `null` and `undefined`\n * driver values are passed through as `null` rather than throwing. Use for\n * optional foreign keys, `LEFT JOIN` results, and any column that is\n * legitimately absent.\n *\n * @example\n * ```ts\n * import { nullableIdColumn } from \"@smonn/ids/drizzle\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n * export const posts = pgTable(\"posts\", {\n * authorId: nullableIdColumn(usr),\n * });\n * // posts.authorId is Id<\"usr\"> | null end-to-end\n * ```\n */\nexport function nullableIdColumn<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n): PgCustomColumnBuilder<\n ConvertCustomConfig<\"\", { data: Id<Brand> | null; driverData: string | null }>\n> {\n return customType<{ data: Id<Brand> | null; driverData: string | null }>({\n dataType() {\n return \"text\";\n },\n toDriver(value: Id<Brand> | null): string | null {\n return value;\n },\n fromDriver(value: string | null): Id<Brand> | null {\n return readIdColumnNullable(codec, value);\n },\n })();\n}\n\n/**\n * Drizzle custom column type that stores an `Id<Brand>` as a canonical SQL string value\n * in PostgreSQL, with a client-side `.$defaultFn` so inserts that omit the field receive\n * a freshly generated ID automatically.\n *\n * **Write path:** `.$defaultFn(() => codec.generate())` is wired — if the field is absent\n * on insert, Drizzle calls `codec.generate()` to produce a new `Id<Brand>`.\n *\n * **Read path:** normalises the raw DB string via `codec.safeParse()`, not strict `is()`.\n * Throws `IdsError(\"invalid_id\")` if the value from the database does not parse as a\n * valid `Id<Brand>`.\n *\n * Requires a codec that exposes synchronous `generate()` — see {@link IdGeneratingCodec}.\n * Only the **Timestamp codec** and **Reverse Timestamp codec** qualify; Opaque, Signed,\n * Wrapped, and Digest codecs are a compile-time error.\n *\n * @param codec - The brand-scoped codec used to generate and parse values.\n * @param options - Optional column configuration.\n * @param options.columnType - SQL column type to use (default: `\"text\"`). Pass\n * `\"varchar(30)\"` or `\"char(26)\"` to match an existing DDL or index strategy.\n * The value is passed through verbatim — no validation is performed.\n *\n * @example\n * ```ts\n * import { generatedIdColumn } from \"@smonn/ids/drizzle\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n * export const users = pgTable(\"users\", { id: generatedIdColumn(usr).primaryKey() });\n * // id is auto-filled on insert — no hand-supplied id needed\n * ```\n */\nexport function generatedIdColumn<Brand extends string>(\n codec: IdGeneratingCodec<Brand>,\n options?: { columnType?: string },\n): HasRuntimeDefault<\n HasDefault<\n PgCustomColumnBuilder<ConvertCustomConfig<\"\", { data: Id<Brand>; driverData: string }>>\n >\n> {\n const columnType = options?.columnType ?? \"text\";\n return customType<{ data: Id<Brand>; driverData: string }>({\n dataType() {\n return columnType;\n },\n toDriver(value: Id<Brand>): string {\n return value;\n },\n fromDriver(value: string): Id<Brand> {\n return readIdColumn(codec, value);\n },\n })().$defaultFn(() => codec.generate());\n}\n\n/**\n * Drizzle custom column type that stores an `Id<Brand>` as a canonical `text` value\n * in MySQL, with a client-side `.$defaultFn` so inserts that omit the field receive\n * a freshly generated ID automatically.\n *\n * **Write path:** `.$defaultFn(() => codec.generate())` is wired — if the field is absent\n * on insert, Drizzle calls `codec.generate()` to produce a new `Id<Brand>`.\n *\n * **Read path:** normalises the raw DB string via `codec.safeParse()`, not strict `is()`.\n * Throws `IdsError(\"invalid_id\")` if the value from the database does not parse as a\n * valid `Id<Brand>`.\n *\n * Requires a codec that exposes synchronous `generate()` — see {@link IdGeneratingCodec}.\n * Only the **Timestamp codec** and **Reverse Timestamp codec** qualify; Opaque, Signed,\n * Wrapped, and Digest codecs are a compile-time error.\n *\n * @param codec - The brand-scoped codec used to generate and parse values.\n * Column type is always `text` and cannot be overridden.\n *\n * @example\n * ```ts\n * import { generatedIdColumnMysql } from \"@smonn/ids/drizzle\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n * export const users = mysqlTable(\"users\", { id: generatedIdColumnMysql(usr).primaryKey() });\n * // id is auto-filled on insert — no hand-supplied id needed\n * ```\n */\nexport function generatedIdColumnMysql<Brand extends string>(\n codec: IdGeneratingCodec<Brand>,\n): HasRuntimeDefault<\n HasDefault<\n MySqlCustomColumnBuilder<ConvertCustomConfigMysql<\"\", { data: Id<Brand>; driverData: string }>>\n >\n> {\n return customTypeMysql<{ data: Id<Brand>; driverData: string }>({\n dataType() {\n return \"text\";\n },\n toDriver(value: Id<Brand>): string {\n return value;\n },\n fromDriver(value: string): Id<Brand> {\n return readIdColumn(codec, value);\n },\n })().$defaultFn(() => codec.generate());\n}\n\n/**\n * Drizzle custom column type that stores an `Id<Brand>` as a canonical `text` value\n * in SQLite, with a client-side `.$defaultFn` so inserts that omit the field receive\n * a freshly generated ID automatically.\n *\n * **Write path:** `.$defaultFn(() => codec.generate())` is wired — if the field is absent\n * on insert, Drizzle calls `codec.generate()` to produce a new `Id<Brand>`.\n *\n * **Read path:** normalises the raw DB string via `codec.safeParse()`, not strict `is()`.\n * Throws `IdsError(\"invalid_id\")` if the value from the database does not parse as a\n * valid `Id<Brand>`.\n *\n * Requires a codec that exposes synchronous `generate()` — see {@link IdGeneratingCodec}.\n * Only the **Timestamp codec** and **Reverse Timestamp codec** qualify; Opaque, Signed,\n * Wrapped, and Digest codecs are a compile-time error.\n *\n * @param codec - The brand-scoped codec used to generate and parse values.\n * Column type is always `text` and cannot be overridden.\n *\n * @example\n * ```ts\n * import { generatedIdColumnSqlite } from \"@smonn/ids/drizzle\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n * export const users = sqliteTable(\"users\", { id: generatedIdColumnSqlite(usr).primaryKey() });\n * // id is auto-filled on insert — no hand-supplied id needed\n * ```\n */\nexport function generatedIdColumnSqlite<Brand extends string>(\n codec: IdGeneratingCodec<Brand>,\n): HasRuntimeDefault<\n HasDefault<\n SQLiteCustomColumnBuilder<\n ConvertCustomConfigSqlite<\"\", { data: Id<Brand>; driverData: string }>\n >\n >\n> {\n return customTypeSqlite<{ data: Id<Brand>; driverData: string }>({\n dataType() {\n return \"text\";\n },\n toDriver(value: Id<Brand>): string {\n return value;\n },\n fromDriver(value: string): Id<Brand> {\n return readIdColumn(codec, value);\n },\n })().$defaultFn(() => codec.generate());\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgEA,SAAgB,SACd,OACA,SACyF;CACzF,MAAM,aAAa,SAAS,cAAc;CAC1C,OAAO,WAAoD;EACzD,WAAW;GACT,OAAO;EACT;EACA,SAAS,OAA0B;GACjC,OAAO;EACT;EACA,WAAW,OAA0B;GACnC,OAAO,aAAa,OAAO,KAAK;EAClC;CACF,CAAC,CAAC,CAAC;AACL;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,cACd,OACiG;CACjG,OAAOA,aAAyD;EAC9D,WAAW;GACT,OAAO;EACT;EACA,SAAS,OAA0B;GACjC,OAAO;EACT;EACA,WAAW,OAA0B;GACnC,OAAO,aAAa,OAAO,KAAK;EAClC;CACF,CAAC,CAAC,CAAC;AACL;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,eACd,OAGA;CACA,OAAOC,aAA0D;EAC/D,WAAW;GACT,OAAO;EACT;EACA,SAAS,OAA0B;GACjC,OAAO;EACT;EACA,WAAW,OAA0B;GACnC,OAAO,aAAa,OAAO,KAAK;EAClC;CACF,CAAC,CAAC,CAAC;AACL;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,iBACd,OAGA;CACA,OAAO,WAAkE;EACvE,WAAW;GACT,OAAO;EACT;EACA,SAAS,OAAwC;GAC/C,OAAO;EACT;EACA,WAAW,OAAwC;GACjD,OAAO,qBAAqB,OAAO,KAAK;EAC1C;CACF,CAAC,CAAC,CAAC;AACL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,SAAgB,kBACd,OACA,SAKA;CACA,MAAM,aAAa,SAAS,cAAc;CAC1C,OAAO,WAAoD;EACzD,WAAW;GACT,OAAO;EACT;EACA,SAAS,OAA0B;GACjC,OAAO;EACT;EACA,WAAW,OAA0B;GACnC,OAAO,aAAa,OAAO,KAAK;EAClC;CACF,CAAC,CAAC,CAAC,CAAC,CAAC,iBAAiB,MAAM,SAAS,CAAC;AACxC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,SAAgB,uBACd,OAKA;CACA,OAAOD,aAAyD;EAC9D,WAAW;GACT,OAAO;EACT;EACA,SAAS,OAA0B;GACjC,OAAO;EACT;EACA,WAAW,OAA0B;GACnC,OAAO,aAAa,OAAO,KAAK;EAClC;CACF,CAAC,CAAC,CAAC,CAAC,CAAC,iBAAiB,MAAM,SAAS,CAAC;AACxC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,SAAgB,wBACd,OAOA;CACA,OAAOC,aAA0D;EAC/D,WAAW;GACT,OAAO;EACT;EACA,SAAS,OAA0B;GACjC,OAAO;EACT;EACA,WAAW,OAA0B;GACnC,OAAO,aAAa,OAAO,KAAK;EAClC;CACF,CAAC,CAAC,CAAC,CAAC,CAAC,iBAAiB,MAAM,SAAS,CAAC;AACxC"}
|
package/dist/kysely.d.mts
CHANGED
|
@@ -5,6 +5,17 @@ import { ColumnType, KyselyPlugin } from "kysely";
|
|
|
5
5
|
|
|
6
6
|
//#region src/adapters/kysely.d.ts
|
|
7
7
|
/**
|
|
8
|
+
* Extension of {@link IdColumnCodec} that also exposes synchronous `generate()`.
|
|
9
|
+
* Required by {@link insertId} so that the insert-time call site can produce a
|
|
10
|
+
* fresh `Id<Brand>` with a compile-time constraint enforcing codec capability.
|
|
11
|
+
* Only the **Timestamp codec** and **Reverse Timestamp codec** satisfy this;
|
|
12
|
+
* async-generate codecs (Opaque, Signed, Wrapped, Digest) do not and are
|
|
13
|
+
* therefore rejected at the TypeScript level.
|
|
14
|
+
*/
|
|
15
|
+
type IdGeneratingCodec<Brand extends string> = IdColumnCodec<Brand> & {
|
|
16
|
+
generate(): Id<Brand>;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
8
19
|
* Kysely column type mapping for `Id<Brand>`.
|
|
9
20
|
*
|
|
10
21
|
* Use this in your Kysely `Database` interface to type a column as `Id<Brand>` at
|
|
@@ -70,6 +81,25 @@ declare function idColumn<Brand extends string>(codec: IdColumnCodec<Brand>): {
|
|
|
70
81
|
fromDriver(value: string): Id<Brand>;
|
|
71
82
|
};
|
|
72
83
|
/**
|
|
84
|
+
* Generates a fresh `Id<Brand>` for use at a Kysely insert call site.
|
|
85
|
+
*
|
|
86
|
+
* Requires a codec variant that exposes a synchronous `generate()` — see
|
|
87
|
+
* {@link IdGeneratingCodec}. Only the **Timestamp codec** and **Reverse
|
|
88
|
+
* Timestamp codec** qualify; Opaque, Signed, Wrapped, and Digest codecs
|
|
89
|
+
* are rejected at compile time.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```ts
|
|
93
|
+
* import { insertId } from "@smonn/ids/kysely";
|
|
94
|
+
* import { createTimestampId } from "@smonn/ids";
|
|
95
|
+
*
|
|
96
|
+
* const usr = createTimestampId("usr");
|
|
97
|
+
*
|
|
98
|
+
* await db.insertInto("users").values({ id: insertId(usr), name: "Alice" }).execute();
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
declare function insertId<Brand extends string>(codec: IdGeneratingCodec<Brand>): Id<Brand>;
|
|
102
|
+
/**
|
|
73
103
|
* Kysely plugin that automatically transforms result columns using the provided codec map.
|
|
74
104
|
*
|
|
75
105
|
* Keys in `map` are plain column names (`"id"`) or `"table.column"` qualified names
|
|
@@ -123,5 +153,5 @@ declare function nullableIdColumn<Brand extends string>(codec: IdColumnCodec<Bra
|
|
|
123
153
|
fromDriver(value: string | null): Id<Brand> | null;
|
|
124
154
|
};
|
|
125
155
|
//#endregion
|
|
126
|
-
export { type IdColumnCodec, IdColumnType, IdsError, type IdsErrorCode, NullableIdColumnType, idColumn, idPlugin, isIdsError, nullableIdColumn };
|
|
156
|
+
export { type IdColumnCodec, IdColumnType, IdGeneratingCodec, IdsError, type IdsErrorCode, NullableIdColumnType, idColumn, idPlugin, insertId, isIdsError, nullableIdColumn };
|
|
127
157
|
//# sourceMappingURL=kysely.d.mts.map
|
package/dist/kysely.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kysely.d.mts","names":[],"sources":["../src/adapters/kysely.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"kysely.d.mts","names":[],"sources":["../src/adapters/kysely.ts"],"mappings":";;;;;;AAuBA;;;;;;;;AAAA,KAAY,iBAAA,yBAA0C,aAAA,CAAc,KAAA;EAClE,QAAA,IAAY,EAAA,CAAG,KAAA;AAAA;;;;;;AAAA;AAoBjB;;;;;;;;;;;KAAY,YAAA,yBAAqC,UAAA,CAAW,EAAA,CAAG,KAAA,GAAQ,EAAA,CAAG,KAAA,GAAQ,EAAA,CAAG,KAAA;;;;;;;;;;AAAA;AAmBrF;;;;;;;KAAY,oBAAA,yBAA6C,UAAA,CACvD,EAAA,CAAG,KAAA,UACH,EAAA,CAAG,KAAA,UACH,EAAA,CAAG,KAAA;;;;;;;;;;;;;;AAAA;AA4BL;;;;;;;;;;;iBAAgB,QAAA,uBACd,KAAA,EAAO,aAAA,CAAc,KAAA;EAErB,QAAA,CAAS,KAAA,EAAO,EAAA,CAAG,KAAA;EACnB,UAAA,CAAW,KAAA,WAAgB,EAAA,CAAG,KAAA;AAAA;;;;;;;;;;AAAA;AA8BhC;;;;;;;;iBAAgB,QAAA,uBAA+B,KAAA,EAAO,iBAAA,CAAkB,KAAA,IAAS,EAAA,CAAG,KAAA;;;;;;;;AAAA;AAiCpF;;;;;;;;;;;;;AAAsE;AAqDtE;;;;;;;iBArDgB,QAAA,CAAS,GAAA,EAAK,MAAA,SAAe,aAAA,YAAyB,YAAA;;;;;;;;;;;;;;;;;AAyD/B;;;iBAJvB,gBAAA,uBACd,KAAA,EAAO,aAAA,CAAc,KAAA;EAErB,QAAA,CAAS,KAAA,EAAO,EAAA,CAAG,KAAA;EACnB,UAAA,CAAW,KAAA,kBAAuB,EAAA,CAAG,KAAA;AAAA"}
|
package/dist/kysely.mjs
CHANGED
|
@@ -37,6 +37,27 @@ function idColumn(codec) {
|
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
39
|
/**
|
|
40
|
+
* Generates a fresh `Id<Brand>` for use at a Kysely insert call site.
|
|
41
|
+
*
|
|
42
|
+
* Requires a codec variant that exposes a synchronous `generate()` — see
|
|
43
|
+
* {@link IdGeneratingCodec}. Only the **Timestamp codec** and **Reverse
|
|
44
|
+
* Timestamp codec** qualify; Opaque, Signed, Wrapped, and Digest codecs
|
|
45
|
+
* are rejected at compile time.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* import { insertId } from "@smonn/ids/kysely";
|
|
50
|
+
* import { createTimestampId } from "@smonn/ids";
|
|
51
|
+
*
|
|
52
|
+
* const usr = createTimestampId("usr");
|
|
53
|
+
*
|
|
54
|
+
* await db.insertInto("users").values({ id: insertId(usr), name: "Alice" }).execute();
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
function insertId(codec) {
|
|
58
|
+
return codec.generate();
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
40
61
|
* Kysely plugin that automatically transforms result columns using the provided codec map.
|
|
41
62
|
*
|
|
42
63
|
* Keys in `map` are plain column names (`"id"`) or `"table.column"` qualified names
|
|
@@ -119,6 +140,6 @@ function nullableIdColumn(codec) {
|
|
|
119
140
|
};
|
|
120
141
|
}
|
|
121
142
|
//#endregion
|
|
122
|
-
export { IdsError, idColumn, idPlugin, isIdsError, nullableIdColumn };
|
|
143
|
+
export { IdsError, idColumn, idPlugin, insertId, isIdsError, nullableIdColumn };
|
|
123
144
|
|
|
124
145
|
//# sourceMappingURL=kysely.mjs.map
|
package/dist/kysely.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kysely.mjs","names":[],"sources":["../src/adapters/kysely.ts"],"sourcesContent":["import type {\n ColumnType,\n KyselyPlugin,\n PluginTransformQueryArgs,\n PluginTransformResultArgs,\n QueryResult,\n UnknownRow,\n} from \"kysely\";\nimport { readIdColumn, readIdColumnNullable, type IdColumnCodec } from \"./adapter-types.js\";\nimport type { Id } from \"../types.js\";\n\nexport type { IdColumnCodec } from \"./adapter-types.js\";\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\n/**\n * Kysely column type mapping for `Id<Brand>`.\n *\n * Use this in your Kysely `Database` interface to type a column as `Id<Brand>` at\n * the TypeScript level. Pair it with `idColumn(codec)` for runtime read/write\n * transformation.\n *\n * @example\n * ```ts\n * import type { IdColumnType } from \"@smonn/ids/kysely\";\n * import type { Id } from \"@smonn/ids\";\n *\n * interface Database {\n * users: { id: IdColumnType<\"usr\"> };\n * }\n * ```\n */\nexport type IdColumnType<Brand extends string> = ColumnType<Id<Brand>, Id<Brand>, Id<Brand>>;\n\n/**\n * Kysely column type mapping for a nullable `Id<Brand>` column.\n *\n * Use in your Kysely `Database` interface for optional foreign keys or any\n * column that can be `NULL`. Pair with `nullableIdColumn(codec)` for runtime\n * transformation.\n *\n * @example\n * ```ts\n * import type { NullableIdColumnType } from \"@smonn/ids/kysely\";\n * import type { Id } from \"@smonn/ids\";\n *\n * interface Database {\n * posts: { authorId: NullableIdColumnType<\"usr\"> };\n * }\n * ```\n */\nexport type NullableIdColumnType<Brand extends string> = ColumnType<\n Id<Brand> | null,\n Id<Brand> | null,\n Id<Brand> | null\n>;\n\n/**\n * Kysely column adapter bound to a codec.\n *\n * Returns an object with `fromDriver` / `toDriver` helpers that mirror the read/write\n * contract of the Drizzle adapter — same error message, same strictness (safeParse on\n * read, identity on write).\n *\n * **Write path:** passes the `Id<Brand>` directly to the driver — it is already\n * the canonical string form.\n *\n * **Read path:** normalises the raw DB string via `codec.safeParse()`. Throws if\n * the value does not parse as a valid `Id<Brand>`.\n *\n * @example\n * ```ts\n * import { idColumn } from \"@smonn/ids/kysely\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n * const usrCol = idColumn(usr);\n *\n * // In a query result handler:\n * const id = usrCol.fromDriver(row.id);\n * ```\n */\nexport function idColumn<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n): {\n toDriver(value: Id<Brand>): string;\n fromDriver(value: string): Id<Brand>;\n} {\n return {\n toDriver(value: Id<Brand>): string {\n return value;\n },\n fromDriver(value: string): Id<Brand> {\n return readIdColumn(codec, value);\n },\n };\n}\n\n/**\n * Kysely plugin that automatically transforms result columns using the provided codec map.\n *\n * Keys in `map` are plain column names (`\"id\"`) or `\"table.column\"` qualified names\n * (`\"users.id\"`). Plain names match any result column with that name; qualified names\n * match by the column-name part (after the last `.`). If both a plain key and a qualified\n * key resolve to the same column name, the qualified key takes precedence.\n *\n * `transformResult` calls `readIdColumn(codec, rawValue)` for each matched column, returning\n * a branded `Id<Brand>` on success and throwing `IdsError(\"invalid_id\")` on parse failure.\n * `transformQuery` is a no-op identity pass-through.\n *\n * @example\n * ```ts\n * import { idPlugin } from \"@smonn/ids/kysely\";\n * import { createTimestampId } from \"@smonn/ids\";\n * import { Kysely } from \"kysely\";\n *\n * const usr = createTimestampId(\"usr\");\n *\n * const db = new Kysely<Database>({\n * // ...\n * plugins: [idPlugin({ \"users.id\": usr })],\n * });\n *\n * // result.id is automatically validated and branded as Id<\"usr\">\n * const row = await db.selectFrom(\"users\").selectAll().executeTakeFirstOrThrow();\n * ```\n */\nexport function idPlugin(map: Record<string, IdColumnCodec<string>>): KyselyPlugin {\n // Build a lookup keyed by the bare column name.\n // Plain keys (\"id\") are added first; qualified keys (\"users.id\") are added second\n // so they override the plain key when both resolve to the same column name.\n const lookup = new Map<string, IdColumnCodec<string>>();\n for (const [key, codec] of Object.entries(map)) {\n if (!key.includes(\".\")) {\n lookup.set(key, codec);\n }\n }\n for (const [key, codec] of Object.entries(map)) {\n if (key.includes(\".\")) {\n lookup.set(key.slice(key.lastIndexOf(\".\") + 1), codec);\n }\n }\n\n return {\n transformQuery(args: PluginTransformQueryArgs) {\n return args.node;\n },\n async transformResult(args: PluginTransformResultArgs): Promise<QueryResult<UnknownRow>> {\n const rows = args.result.rows.map((row) => {\n const newRow: Record<string, unknown> = {};\n for (const [colName, value] of Object.entries(row)) {\n const codec = lookup.get(colName);\n newRow[colName] = codec !== undefined ? readIdColumn(codec, value) : value;\n }\n return newRow;\n });\n return { ...args.result, rows };\n },\n };\n}\n\n/**\n * Kysely column adapter for a **nullable** `Id<Brand>` column.\n *\n * Behaves like {@link idColumn} but `fromDriver` returns `null` for `null` /\n * `undefined` driver values and `toDriver` passes `null` through unchanged.\n * Use for optional foreign keys and `LEFT JOIN` results.\n *\n * @example\n * ```ts\n * import { nullableIdColumn } from \"@smonn/ids/kysely\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n * const authorCol = nullableIdColumn(usr);\n *\n * // In a query result handler:\n * const authorId = authorCol.fromDriver(row.author_id); // Id<\"usr\"> | null\n * ```\n */\nexport function nullableIdColumn<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n): {\n toDriver(value: Id<Brand> | null): string | null;\n fromDriver(value: string | null): Id<Brand> | null;\n} {\n return {\n toDriver(value: Id<Brand> | null): string | null {\n return value;\n },\n fromDriver(value: string | null): Id<Brand> | null {\n return readIdColumnNullable(codec, value);\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"kysely.mjs","names":[],"sources":["../src/adapters/kysely.ts"],"sourcesContent":["import type {\n ColumnType,\n KyselyPlugin,\n PluginTransformQueryArgs,\n PluginTransformResultArgs,\n QueryResult,\n UnknownRow,\n} from \"kysely\";\nimport { readIdColumn, readIdColumnNullable, type IdColumnCodec } from \"./adapter-types.js\";\nimport type { Id } from \"../types.js\";\n\nexport type { IdColumnCodec } from \"./adapter-types.js\";\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\n/**\n * Extension of {@link IdColumnCodec} that also exposes synchronous `generate()`.\n * Required by {@link insertId} so that the insert-time call site can produce a\n * fresh `Id<Brand>` with a compile-time constraint enforcing codec capability.\n * Only the **Timestamp codec** and **Reverse Timestamp codec** satisfy this;\n * async-generate codecs (Opaque, Signed, Wrapped, Digest) do not and are\n * therefore rejected at the TypeScript level.\n */\nexport type IdGeneratingCodec<Brand extends string> = IdColumnCodec<Brand> & {\n generate(): Id<Brand>;\n};\n\n/**\n * Kysely column type mapping for `Id<Brand>`.\n *\n * Use this in your Kysely `Database` interface to type a column as `Id<Brand>` at\n * the TypeScript level. Pair it with `idColumn(codec)` for runtime read/write\n * transformation.\n *\n * @example\n * ```ts\n * import type { IdColumnType } from \"@smonn/ids/kysely\";\n * import type { Id } from \"@smonn/ids\";\n *\n * interface Database {\n * users: { id: IdColumnType<\"usr\"> };\n * }\n * ```\n */\nexport type IdColumnType<Brand extends string> = ColumnType<Id<Brand>, Id<Brand>, Id<Brand>>;\n\n/**\n * Kysely column type mapping for a nullable `Id<Brand>` column.\n *\n * Use in your Kysely `Database` interface for optional foreign keys or any\n * column that can be `NULL`. Pair with `nullableIdColumn(codec)` for runtime\n * transformation.\n *\n * @example\n * ```ts\n * import type { NullableIdColumnType } from \"@smonn/ids/kysely\";\n * import type { Id } from \"@smonn/ids\";\n *\n * interface Database {\n * posts: { authorId: NullableIdColumnType<\"usr\"> };\n * }\n * ```\n */\nexport type NullableIdColumnType<Brand extends string> = ColumnType<\n Id<Brand> | null,\n Id<Brand> | null,\n Id<Brand> | null\n>;\n\n/**\n * Kysely column adapter bound to a codec.\n *\n * Returns an object with `fromDriver` / `toDriver` helpers that mirror the read/write\n * contract of the Drizzle adapter — same error message, same strictness (safeParse on\n * read, identity on write).\n *\n * **Write path:** passes the `Id<Brand>` directly to the driver — it is already\n * the canonical string form.\n *\n * **Read path:** normalises the raw DB string via `codec.safeParse()`. Throws if\n * the value does not parse as a valid `Id<Brand>`.\n *\n * @example\n * ```ts\n * import { idColumn } from \"@smonn/ids/kysely\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n * const usrCol = idColumn(usr);\n *\n * // In a query result handler:\n * const id = usrCol.fromDriver(row.id);\n * ```\n */\nexport function idColumn<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n): {\n toDriver(value: Id<Brand>): string;\n fromDriver(value: string): Id<Brand>;\n} {\n return {\n toDriver(value: Id<Brand>): string {\n return value;\n },\n fromDriver(value: string): Id<Brand> {\n return readIdColumn(codec, value);\n },\n };\n}\n\n/**\n * Generates a fresh `Id<Brand>` for use at a Kysely insert call site.\n *\n * Requires a codec variant that exposes a synchronous `generate()` — see\n * {@link IdGeneratingCodec}. Only the **Timestamp codec** and **Reverse\n * Timestamp codec** qualify; Opaque, Signed, Wrapped, and Digest codecs\n * are rejected at compile time.\n *\n * @example\n * ```ts\n * import { insertId } from \"@smonn/ids/kysely\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n *\n * await db.insertInto(\"users\").values({ id: insertId(usr), name: \"Alice\" }).execute();\n * ```\n */\nexport function insertId<Brand extends string>(codec: IdGeneratingCodec<Brand>): Id<Brand> {\n return codec.generate();\n}\n\n/**\n * Kysely plugin that automatically transforms result columns using the provided codec map.\n *\n * Keys in `map` are plain column names (`\"id\"`) or `\"table.column\"` qualified names\n * (`\"users.id\"`). Plain names match any result column with that name; qualified names\n * match by the column-name part (after the last `.`). If both a plain key and a qualified\n * key resolve to the same column name, the qualified key takes precedence.\n *\n * `transformResult` calls `readIdColumn(codec, rawValue)` for each matched column, returning\n * a branded `Id<Brand>` on success and throwing `IdsError(\"invalid_id\")` on parse failure.\n * `transformQuery` is a no-op identity pass-through.\n *\n * @example\n * ```ts\n * import { idPlugin } from \"@smonn/ids/kysely\";\n * import { createTimestampId } from \"@smonn/ids\";\n * import { Kysely } from \"kysely\";\n *\n * const usr = createTimestampId(\"usr\");\n *\n * const db = new Kysely<Database>({\n * // ...\n * plugins: [idPlugin({ \"users.id\": usr })],\n * });\n *\n * // result.id is automatically validated and branded as Id<\"usr\">\n * const row = await db.selectFrom(\"users\").selectAll().executeTakeFirstOrThrow();\n * ```\n */\nexport function idPlugin(map: Record<string, IdColumnCodec<string>>): KyselyPlugin {\n // Build a lookup keyed by the bare column name.\n // Plain keys (\"id\") are added first; qualified keys (\"users.id\") are added second\n // so they override the plain key when both resolve to the same column name.\n const lookup = new Map<string, IdColumnCodec<string>>();\n for (const [key, codec] of Object.entries(map)) {\n if (!key.includes(\".\")) {\n lookup.set(key, codec);\n }\n }\n for (const [key, codec] of Object.entries(map)) {\n if (key.includes(\".\")) {\n lookup.set(key.slice(key.lastIndexOf(\".\") + 1), codec);\n }\n }\n\n return {\n transformQuery(args: PluginTransformQueryArgs) {\n return args.node;\n },\n async transformResult(args: PluginTransformResultArgs): Promise<QueryResult<UnknownRow>> {\n const rows = args.result.rows.map((row) => {\n const newRow: Record<string, unknown> = {};\n for (const [colName, value] of Object.entries(row)) {\n const codec = lookup.get(colName);\n newRow[colName] = codec !== undefined ? readIdColumn(codec, value) : value;\n }\n return newRow;\n });\n return { ...args.result, rows };\n },\n };\n}\n\n/**\n * Kysely column adapter for a **nullable** `Id<Brand>` column.\n *\n * Behaves like {@link idColumn} but `fromDriver` returns `null` for `null` /\n * `undefined` driver values and `toDriver` passes `null` through unchanged.\n * Use for optional foreign keys and `LEFT JOIN` results.\n *\n * @example\n * ```ts\n * import { nullableIdColumn } from \"@smonn/ids/kysely\";\n * import { createTimestampId } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n * const authorCol = nullableIdColumn(usr);\n *\n * // In a query result handler:\n * const authorId = authorCol.fromDriver(row.author_id); // Id<\"usr\"> | null\n * ```\n */\nexport function nullableIdColumn<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n): {\n toDriver(value: Id<Brand> | null): string | null;\n fromDriver(value: string | null): Id<Brand> | null;\n} {\n return {\n toDriver(value: Id<Brand> | null): string | null {\n return value;\n },\n fromDriver(value: string | null): Id<Brand> | null {\n return readIdColumnNullable(codec, value);\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8FA,SAAgB,SACd,OAIA;CACA,OAAO;EACL,SAAS,OAA0B;GACjC,OAAO;EACT;EACA,WAAW,OAA0B;GACnC,OAAO,aAAa,OAAO,KAAK;EAClC;CACF;AACF;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,SAA+B,OAA4C;CACzF,OAAO,MAAM,SAAS;AACxB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,SAAgB,SAAS,KAA0D;CAIjF,MAAM,yBAAS,IAAI,IAAmC;CACtD,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,GAAG,GAC3C,IAAI,CAAC,IAAI,SAAS,GAAG,GACnB,OAAO,IAAI,KAAK,KAAK;CAGzB,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,GAAG,GAC3C,IAAI,IAAI,SAAS,GAAG,GAClB,OAAO,IAAI,IAAI,MAAM,IAAI,YAAY,GAAG,IAAI,CAAC,GAAG,KAAK;CAIzD,OAAO;EACL,eAAe,MAAgC;GAC7C,OAAO,KAAK;EACd;EACA,MAAM,gBAAgB,MAAmE;GACvF,MAAM,OAAO,KAAK,OAAO,KAAK,KAAK,QAAQ;IACzC,MAAM,SAAkC,CAAC;IACzC,KAAK,MAAM,CAAC,SAAS,UAAU,OAAO,QAAQ,GAAG,GAAG;KAClD,MAAM,QAAQ,OAAO,IAAI,OAAO;KAChC,OAAO,WAAW,UAAU,KAAA,IAAY,aAAa,OAAO,KAAK,IAAI;IACvE;IACA,OAAO;GACT,CAAC;GACD,OAAO;IAAE,GAAG,KAAK;IAAQ;GAAK;EAChC;CACF;AACF;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,iBACd,OAIA;CACA,OAAO;EACL,SAAS,OAAwC;GAC/C,OAAO;EACT;EACA,WAAW,OAAwC;GACjD,OAAO,qBAAqB,OAAO,KAAK;EAC1C;CACF;AACF"}
|
package/dist/mikro-orm.d.mts
CHANGED
|
@@ -5,6 +5,45 @@ import { Type } from "@mikro-orm/core";
|
|
|
5
5
|
|
|
6
6
|
//#region src/adapters/mikro-orm.d.ts
|
|
7
7
|
/**
|
|
8
|
+
* Extension of {@link IdColumnCodec} that also exposes synchronous `generate()`.
|
|
9
|
+
* Required by {@link idField} so that the MikroORM `onCreate` hook can produce
|
|
10
|
+
* IDs at persist time. Only the **Timestamp codec** and **Reverse Timestamp codec**
|
|
11
|
+
* satisfy this; async-generate codecs (Opaque, Signed, Wrapped, Digest) do not.
|
|
12
|
+
*/
|
|
13
|
+
type IdGeneratingCodec<Brand extends string> = IdColumnCodec<Brand> & {
|
|
14
|
+
generate(): Id<Brand>;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Returns a MikroORM property option object that wires `codec.generate()` into the
|
|
18
|
+
* `onCreate` lifecycle hook, so the field auto-fills on first persist.
|
|
19
|
+
*
|
|
20
|
+
* Requires a codec variant that exposes a synchronous `generate()` —
|
|
21
|
+
* see {@link IdGeneratingCodec}. Only the **Timestamp codec** and **Reverse
|
|
22
|
+
* Timestamp codec** qualify; Opaque, Signed, Wrapped, and Digest codecs cannot
|
|
23
|
+
* be passed here.
|
|
24
|
+
*
|
|
25
|
+
* Pass the result as options to `@PrimaryKey()` (the typical case for auto-generated primary keys) or any other MikroORM property decorator:
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* import { PrimaryKey } from "@mikro-orm/core";
|
|
30
|
+
* import { idField } from "@smonn/ids/mikro-orm";
|
|
31
|
+
* import { createTimestampId } from "@smonn/ids";
|
|
32
|
+
* import type { Id } from "@smonn/ids";
|
|
33
|
+
*
|
|
34
|
+
* const usr = createTimestampId("usr");
|
|
35
|
+
*
|
|
36
|
+
* class User {
|
|
37
|
+
* @PrimaryKey(idField(usr))
|
|
38
|
+
* id!: Id<"usr">;
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
declare function idField<Brand extends string>(codec: IdGeneratingCodec<Brand>): {
|
|
43
|
+
type: new () => Type<Id<Brand>, string>;
|
|
44
|
+
onCreate: () => Id<Brand>;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
8
47
|
* Factory that returns a MikroORM `Type` subclass bound to a codec.
|
|
9
48
|
*
|
|
10
49
|
* **Write path** (`convertToDatabaseValue`): passes the `Id<Brand>` through
|
|
@@ -72,5 +111,5 @@ declare function idType<Brand extends string>(codec: IdColumnCodec<Brand>, optio
|
|
|
72
111
|
*/
|
|
73
112
|
declare function nullableIdType<Brand extends string>(codec: IdColumnCodec<Brand>): new () => Type<Id<Brand> | null, string | null>;
|
|
74
113
|
//#endregion
|
|
75
|
-
export { type IdColumnCodec, IdsError, type IdsErrorCode, idType, isIdsError, nullableIdType };
|
|
114
|
+
export { type IdColumnCodec, IdGeneratingCodec, IdsError, type IdsErrorCode, idField, idType, isIdsError, nullableIdType };
|
|
76
115
|
//# sourceMappingURL=mikro-orm.d.mts.map
|
package/dist/mikro-orm.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mikro-orm.d.mts","names":[],"sources":["../src/adapters/mikro-orm.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"mikro-orm.d.mts","names":[],"sources":["../src/adapters/mikro-orm.ts"],"mappings":";;;;;;AAeA;;;;;;AAAA,KAAY,iBAAA,yBAA0C,aAAA,CAAc,KAAA;EAClE,QAAA,IAAY,EAAA,CAAG,KAAA;AAAA;;;;;;;;AAAA;AA6BjB;;;;;;;;;;;;;;;;;;iBAAgB,OAAA,uBACd,KAAA,EAAO,iBAAA,CAAkB,KAAA;EAEzB,IAAA,YAAgB,IAAA,CAAK,EAAA,CAAG,KAAA;EACxB,QAAA,QAAgB,EAAA,CAAG,KAAA;AAAA;;;AAAA;AAiDrB;;;;;;;;;;;;;;;;;;;;AAGqB;AAqCrB;;;;;;;;;;;;;;;;;iBAxCgB,MAAA,uBACd,KAAA,EAAO,aAAA,CAAc,KAAA,GACrB,OAAA;EAAY,UAAA;AAAA,cACD,IAAA,CAAK,EAAA,CAAG,KAAA;;;;;;;;;;;;;;;;;;;;;;;iBAqCL,cAAA,uBACd,KAAA,EAAO,aAAA,CAAc,KAAA,cACV,IAAA,CAAK,EAAA,CAAG,KAAA"}
|
package/dist/mikro-orm.mjs
CHANGED
|
@@ -3,6 +3,38 @@ import { n as readIdColumnNullable, t as readIdColumn } from "./adapter-types-Cj
|
|
|
3
3
|
import { Type } from "@mikro-orm/core";
|
|
4
4
|
//#region src/adapters/mikro-orm.ts
|
|
5
5
|
/**
|
|
6
|
+
* Returns a MikroORM property option object that wires `codec.generate()` into the
|
|
7
|
+
* `onCreate` lifecycle hook, so the field auto-fills on first persist.
|
|
8
|
+
*
|
|
9
|
+
* Requires a codec variant that exposes a synchronous `generate()` —
|
|
10
|
+
* see {@link IdGeneratingCodec}. Only the **Timestamp codec** and **Reverse
|
|
11
|
+
* Timestamp codec** qualify; Opaque, Signed, Wrapped, and Digest codecs cannot
|
|
12
|
+
* be passed here.
|
|
13
|
+
*
|
|
14
|
+
* Pass the result as options to `@PrimaryKey()` (the typical case for auto-generated primary keys) or any other MikroORM property decorator:
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { PrimaryKey } from "@mikro-orm/core";
|
|
19
|
+
* import { idField } from "@smonn/ids/mikro-orm";
|
|
20
|
+
* import { createTimestampId } from "@smonn/ids";
|
|
21
|
+
* import type { Id } from "@smonn/ids";
|
|
22
|
+
*
|
|
23
|
+
* const usr = createTimestampId("usr");
|
|
24
|
+
*
|
|
25
|
+
* class User {
|
|
26
|
+
* @PrimaryKey(idField(usr))
|
|
27
|
+
* id!: Id<"usr">;
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
function idField(codec) {
|
|
32
|
+
return {
|
|
33
|
+
type: idType(codec),
|
|
34
|
+
onCreate: () => codec.generate()
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
6
38
|
* Factory that returns a MikroORM `Type` subclass bound to a codec.
|
|
7
39
|
*
|
|
8
40
|
* **Write path** (`convertToDatabaseValue`): passes the `Id<Brand>` through
|
|
@@ -93,6 +125,6 @@ function nullableIdType(codec) {
|
|
|
93
125
|
};
|
|
94
126
|
}
|
|
95
127
|
//#endregion
|
|
96
|
-
export { IdsError, idType, isIdsError, nullableIdType };
|
|
128
|
+
export { IdsError, idField, idType, isIdsError, nullableIdType };
|
|
97
129
|
|
|
98
130
|
//# sourceMappingURL=mikro-orm.mjs.map
|
package/dist/mikro-orm.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mikro-orm.mjs","names":[],"sources":["../src/adapters/mikro-orm.ts"],"sourcesContent":["import { Type } from \"@mikro-orm/core\";\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 * Factory that returns a MikroORM `Type` subclass bound to a codec.\n *\n * **Write path** (`convertToDatabaseValue`): passes the `Id<Brand>` through\n * unchanged — it is already the canonical string form.\n *\n * **Read path** (`convertToJSValue`): normalises the raw DB value via\n * `codec.safeParse()`. Throws `IdsError(\"invalid_id\")` if the stored value\n * does not parse as a valid `Id<Brand>`.\n *\n * **Column type** (`getColumnType`): returns `\"text\"` by default, or the\n * `options.columnType` override when provided.\n *\n * @param codec - The brand-scoped codec used to parse values read from the database.\n * @param options - Optional column configuration.\n * @param options.columnType - SQL column type to use (default: `\"text\"`). Pass\n * `\"varchar(30)\"` or `\"char(26)\"` to match an existing DDL or index strategy.\n * The value is passed through verbatim — no validation is performed.\n *\n * @example\n * ```ts\n * import { PrimaryKey } from \"@mikro-orm/core\";\n * import { idType } from \"@smonn/ids/mikro-orm\";\n * import { createTimestampId } from \"@smonn/ids\";\n * import type { Id } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n *\n * // default: text column\n * class User {\n * @PrimaryKey({ type: idType(usr) })\n * id!: Id<\"usr\">;\n * }\n *\n * // explicit varchar column\n * class Org {\n * @PrimaryKey({ type: idType(usr, { columnType: \"varchar(30)\" }) })\n * id!: Id<\"usr\">;\n * }\n * ```\n */\nexport function idType<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n options?: { columnType?: string },\n): new () => Type<Id<Brand>, string> {\n const columnType = options?.columnType ?? \"text\";\n return class extends Type<Id<Brand>, string> {\n override convertToDatabaseValue(value: Id<Brand>): string {\n return value;\n }\n override convertToJSValue(value: string): Id<Brand> {\n return readIdColumn(codec, value);\n }\n override getColumnType(): string {\n return columnType;\n }\n };\n}\n\n/**\n * Factory that returns a MikroORM `Type` subclass for a **nullable** `Id<Brand>` column.\n *\n * Behaves like {@link idType} but `convertToJSValue` returns `null` for `null` /\n * `undefined` database values and `convertToDatabaseValue` passes `null` through\n * unchanged. Use for optional foreign keys.\n *\n * @example\n * ```ts\n * import { Property } from \"@mikro-orm/core\";\n * import { nullableIdType } from \"@smonn/ids/mikro-orm\";\n * import { createTimestampId } from \"@smonn/ids\";\n * import type { Id } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n *\n * class Post {\n * @Property({ type: nullableIdType(usr), nullable: true })\n * authorId!: Id<\"usr\"> | null;\n * }\n * ```\n */\nexport function nullableIdType<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n): new () => Type<Id<Brand> | null, string | null> {\n return class extends Type<Id<Brand> | null, string | null> {\n override convertToDatabaseValue(value: Id<Brand> | null): string | null {\n return value;\n }\n override convertToJSValue(value: string | null): Id<Brand> | null {\n return readIdColumnNullable(codec, value);\n }\n override getColumnType(): string {\n return \"text\";\n }\n };\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"mikro-orm.mjs","names":[],"sources":["../src/adapters/mikro-orm.ts"],"sourcesContent":["import { Type } from \"@mikro-orm/core\";\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 the MikroORM `onCreate` hook can produce\n * IDs at persist time. Only the **Timestamp codec** and **Reverse Timestamp codec**\n * satisfy this; async-generate codecs (Opaque, Signed, Wrapped, Digest) do not.\n */\nexport type IdGeneratingCodec<Brand extends string> = IdColumnCodec<Brand> & {\n generate(): Id<Brand>;\n};\n\n/**\n * Returns a MikroORM property option object that wires `codec.generate()` into the\n * `onCreate` lifecycle hook, so the field auto-fills on first persist.\n *\n * Requires a codec variant that exposes a synchronous `generate()` —\n * see {@link IdGeneratingCodec}. Only the **Timestamp codec** and **Reverse\n * Timestamp codec** qualify; Opaque, Signed, Wrapped, and Digest codecs cannot\n * be passed here.\n *\n * Pass the result as options to `@PrimaryKey()` (the typical case for auto-generated primary keys) or any other MikroORM property decorator:\n *\n * @example\n * ```ts\n * import { PrimaryKey } from \"@mikro-orm/core\";\n * import { idField } from \"@smonn/ids/mikro-orm\";\n * import { createTimestampId } from \"@smonn/ids\";\n * import type { Id } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n *\n * class User {\n * @PrimaryKey(idField(usr))\n * id!: Id<\"usr\">;\n * }\n * ```\n */\nexport function idField<Brand extends string>(\n codec: IdGeneratingCodec<Brand>,\n): {\n type: new () => Type<Id<Brand>, string>;\n onCreate: () => Id<Brand>;\n} {\n return {\n type: idType(codec),\n onCreate: () => codec.generate(),\n };\n}\n\n/**\n * Factory that returns a MikroORM `Type` subclass bound to a codec.\n *\n * **Write path** (`convertToDatabaseValue`): passes the `Id<Brand>` through\n * unchanged — it is already the canonical string form.\n *\n * **Read path** (`convertToJSValue`): normalises the raw DB value via\n * `codec.safeParse()`. Throws `IdsError(\"invalid_id\")` if the stored value\n * does not parse as a valid `Id<Brand>`.\n *\n * **Column type** (`getColumnType`): returns `\"text\"` by default, or the\n * `options.columnType` override when provided.\n *\n * @param codec - The brand-scoped codec used to parse values read from the database.\n * @param options - Optional column configuration.\n * @param options.columnType - SQL column type to use (default: `\"text\"`). Pass\n * `\"varchar(30)\"` or `\"char(26)\"` to match an existing DDL or index strategy.\n * The value is passed through verbatim — no validation is performed.\n *\n * @example\n * ```ts\n * import { PrimaryKey } from \"@mikro-orm/core\";\n * import { idType } from \"@smonn/ids/mikro-orm\";\n * import { createTimestampId } from \"@smonn/ids\";\n * import type { Id } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n *\n * // default: text column\n * class User {\n * @PrimaryKey({ type: idType(usr) })\n * id!: Id<\"usr\">;\n * }\n *\n * // explicit varchar column\n * class Org {\n * @PrimaryKey({ type: idType(usr, { columnType: \"varchar(30)\" }) })\n * id!: Id<\"usr\">;\n * }\n * ```\n */\nexport function idType<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n options?: { columnType?: string },\n): new () => Type<Id<Brand>, string> {\n const columnType = options?.columnType ?? \"text\";\n return class extends Type<Id<Brand>, string> {\n override convertToDatabaseValue(value: Id<Brand>): string {\n return value;\n }\n override convertToJSValue(value: string): Id<Brand> {\n return readIdColumn(codec, value);\n }\n override getColumnType(): string {\n return columnType;\n }\n };\n}\n\n/**\n * Factory that returns a MikroORM `Type` subclass for a **nullable** `Id<Brand>` column.\n *\n * Behaves like {@link idType} but `convertToJSValue` returns `null` for `null` /\n * `undefined` database values and `convertToDatabaseValue` passes `null` through\n * unchanged. Use for optional foreign keys.\n *\n * @example\n * ```ts\n * import { Property } from \"@mikro-orm/core\";\n * import { nullableIdType } from \"@smonn/ids/mikro-orm\";\n * import { createTimestampId } from \"@smonn/ids\";\n * import type { Id } from \"@smonn/ids\";\n *\n * const usr = createTimestampId(\"usr\");\n *\n * class Post {\n * @Property({ type: nullableIdType(usr), nullable: true })\n * authorId!: Id<\"usr\"> | null;\n * }\n * ```\n */\nexport function nullableIdType<Brand extends string>(\n codec: IdColumnCodec<Brand>,\n): new () => Type<Id<Brand> | null, string | null> {\n return class extends Type<Id<Brand> | null, string | null> {\n override convertToDatabaseValue(value: Id<Brand> | null): string | null {\n return value;\n }\n override convertToJSValue(value: string | null): Id<Brand> | null {\n return readIdColumnNullable(codec, value);\n }\n override getColumnType(): string {\n return \"text\";\n }\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CA,SAAgB,QACd,OAIA;CACA,OAAO;EACL,MAAM,OAAO,KAAK;EAClB,gBAAgB,MAAM,SAAS;CACjC;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,SAAgB,OACd,OACA,SACmC;CACnC,MAAM,aAAa,SAAS,cAAc;CAC1C,OAAO,cAAc,KAAwB;EAC3C,uBAAgC,OAA0B;GACxD,OAAO;EACT;EACA,iBAA0B,OAA0B;GAClD,OAAO,aAAa,OAAO,KAAK;EAClC;EACA,gBAAiC;GAC/B,OAAO;EACT;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,eACd,OACiD;CACjD,OAAO,cAAc,KAAsC;EACzD,uBAAgC,OAAwC;GACtE,OAAO;EACT;EACA,iBAA0B,OAAwC;GAChE,OAAO,qBAAqB,OAAO,KAAK;EAC1C;EACA,gBAAiC;GAC/B,OAAO;EACT;CACF;AACF"}
|
package/dist/prisma.d.mts
CHANGED
|
@@ -117,6 +117,17 @@ type IdTransform<Brand extends string> = {
|
|
|
117
117
|
computeNullableField(fieldName: string): NullableIdComputeField<Brand>;
|
|
118
118
|
};
|
|
119
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>;
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
120
131
|
* Creates a read/write transform pair for use with Prisma's `$extends` extension model.
|
|
121
132
|
*
|
|
122
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()`.
|
|
@@ -147,6 +158,31 @@ type IdTransform<Brand extends string> = {
|
|
|
147
158
|
*/
|
|
148
159
|
declare function idField<Brand extends string>(codec: IdGeneratingCodec<Brand>): IdTransform<Brand>;
|
|
149
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
|
+
/**
|
|
150
186
|
* Read-only sibling of {@link idField} for codec variants that do not expose a
|
|
151
187
|
* synchronous `generate()` — Opaque Timestamp, Signed Timestamp, Wrapped key,
|
|
152
188
|
* and Digest codecs all qualify.
|
|
@@ -175,5 +211,5 @@ declare function idField<Brand extends string>(codec: IdGeneratingCodec<Brand>):
|
|
|
175
211
|
*/
|
|
176
212
|
declare function idFieldReadOnly<Brand extends string>(codec: IdColumnCodec<Brand>): Omit<IdTransform<Brand>, "defaultQuery">;
|
|
177
213
|
//#endregion
|
|
178
|
-
export { type IdColumnCodec, IdComputeField, IdGeneratingCodec, IdQueryField, IdTransform, IdsError, type IdsErrorCode, NullableIdComputeField, idField, idFieldReadOnly, isIdsError };
|
|
214
|
+
export { type IdColumnCodec, IdComputeField, IdGeneratingCodec, IdQueryField, IdTransform, IdsError, type IdsErrorCode, NullableIdComputeField, NullableIdTransform, idField, idFieldReadOnly, isIdsError, nullableIdField };
|
|
179
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":";;;;;;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;
|
|
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
|
@@ -89,6 +89,46 @@ function idField(codec) {
|
|
|
89
89
|
};
|
|
90
90
|
}
|
|
91
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
|
+
/**
|
|
92
132
|
* Read-only sibling of {@link idField} for codec variants that do not expose a
|
|
93
133
|
* synchronous `generate()` — Opaque Timestamp, Signed Timestamp, Wrapped key,
|
|
94
134
|
* and Digest codecs all qualify.
|
|
@@ -141,6 +181,6 @@ function idFieldReadOnly(codec) {
|
|
|
141
181
|
};
|
|
142
182
|
}
|
|
143
183
|
//#endregion
|
|
144
|
-
export { IdsError, idField, idFieldReadOnly, isIdsError };
|
|
184
|
+
export { IdsError, idField, idFieldReadOnly, isIdsError, nullableIdField };
|
|
145
185
|
|
|
146
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 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 * 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 * 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyJA,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;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"}
|
|
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`
|
|
@@ -37,6 +48,54 @@ import { ValueTransformer } from "typeorm";
|
|
|
37
48
|
*/
|
|
38
49
|
declare function idTransformer<Brand extends string>(codec: IdColumnCodec<Brand>): ValueTransformer;
|
|
39
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
|
+
/**
|
|
40
99
|
* TypeORM column transformer for a **nullable** `Id<Brand>` column.
|
|
41
100
|
*
|
|
42
101
|
* Behaves like {@link idTransformer} but `from` returns `null` for `null` /
|
|
@@ -61,5 +120,5 @@ declare function idTransformer<Brand extends string>(codec: IdColumnCodec<Brand>
|
|
|
61
120
|
*/
|
|
62
121
|
declare function nullableIdTransformer<Brand extends string>(codec: IdColumnCodec<Brand>): ValueTransformer;
|
|
63
122
|
//#endregion
|
|
64
|
-
export { type IdColumnCodec, IdsError, type IdsErrorCode, idTransformer, isIdsError, nullableIdTransformer };
|
|
123
|
+
export { type IdColumnCodec, IdGeneratingCodec, IdsError, type IdsErrorCode, beforeInsertHook, idTransformer, isIdsError, nullableIdTransformer };
|
|
65
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
|
@@ -44,6 +44,58 @@ function idTransformer(codec) {
|
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
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
|
+
/**
|
|
47
99
|
* TypeORM column transformer for a **nullable** `Id<Brand>` column.
|
|
48
100
|
*
|
|
49
101
|
* Behaves like {@link idTransformer} but `from` returns `null` for `null` /
|
|
@@ -77,6 +129,6 @@ function nullableIdTransformer(codec) {
|
|
|
77
129
|
};
|
|
78
130
|
}
|
|
79
131
|
//#endregion
|
|
80
|
-
export { IdsError, idTransformer, isIdsError, nullableIdTransformer };
|
|
132
|
+
export { IdsError, beforeInsertHook, idTransformer, isIdsError, nullableIdTransformer };
|
|
81
133
|
|
|
82
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, 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 * 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 * 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
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"}
|