@vertz/db 0.2.14 → 0.2.16
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/dist/d1/index.d.ts +146 -11
- package/dist/d1/index.js +1 -1
- package/dist/index.d.ts +264 -209
- package/dist/index.js +246 -147
- package/dist/internals.d.ts +1 -0
- package/dist/postgres/index.d.ts +6 -0
- package/dist/postgres/index.js +1 -1
- package/dist/shared/{chunk-pxjcpnpx.js → chunk-3a9nybt2.js} +7 -2
- package/dist/shared/{chunk-pkv8w501.js → chunk-ed82s3sa.js} +8 -4
- package/dist/shared/{chunk-2gd1fqcw.js → chunk-p2x2vmg5.js} +1 -1
- package/dist/shared/{chunk-rqe0prft.js → chunk-rjry8vc2.js} +23 -0
- package/dist/shared/{chunk-ndxe1h28.js → chunk-tnaf4hbj.js} +1 -1
- package/dist/sql/index.js +1 -1
- package/dist/sqlite/index.d.ts +146 -11
- package/dist/sqlite/index.js +1 -1
- package/package.json +3 -3
package/dist/postgres/index.d.ts
CHANGED
|
@@ -29,6 +29,12 @@ interface DbDriver {
|
|
|
29
29
|
rowsAffected: number;
|
|
30
30
|
}>;
|
|
31
31
|
/**
|
|
32
|
+
* Execute a callback within a database transaction.
|
|
33
|
+
* The callback receives a transaction-scoped QueryFn.
|
|
34
|
+
* Optional — not all drivers support transactions (e.g., D1).
|
|
35
|
+
*/
|
|
36
|
+
beginTransaction?<T>(fn: (txQueryFn: QueryFn) => Promise<T>): Promise<T>;
|
|
37
|
+
/**
|
|
32
38
|
* Close the database connection.
|
|
33
39
|
*/
|
|
34
40
|
close(): Promise<void>;
|
package/dist/postgres/index.js
CHANGED
|
@@ -160,7 +160,9 @@ function buildFilterClauses(filter, paramOffset, overrides, dialect) {
|
|
|
160
160
|
continue;
|
|
161
161
|
}
|
|
162
162
|
const columnRef = resolveColumnRef(key, overrides, dialect);
|
|
163
|
-
if (
|
|
163
|
+
if (value === null) {
|
|
164
|
+
clauses.push(`${columnRef} IS NULL`);
|
|
165
|
+
} else if (isOperatorObject(value)) {
|
|
164
166
|
const result = buildOperatorCondition(columnRef, value, idx, dialect);
|
|
165
167
|
clauses.push(...result.clauses);
|
|
166
168
|
allParams.push(...result.params);
|
|
@@ -372,7 +374,10 @@ function buildSelect(options, dialect = options.dialect ?? defaultPostgresDialec
|
|
|
372
374
|
parts.push(`WHERE ${whereClauses.join(" AND ")}`);
|
|
373
375
|
}
|
|
374
376
|
if (options.orderBy) {
|
|
375
|
-
const orderClauses = Object.entries(options.orderBy).map(([col, dir]) =>
|
|
377
|
+
const orderClauses = Object.entries(options.orderBy).map(([col, dir]) => {
|
|
378
|
+
const safeDir = dir.toUpperCase() === "DESC" ? "DESC" : "ASC";
|
|
379
|
+
return `"${camelToSnake(col, casingOverrides)}" ${safeDir}`;
|
|
380
|
+
});
|
|
376
381
|
if (orderClauses.length > 0) {
|
|
377
382
|
parts.push(`ORDER BY ${orderClauses.join(", ")}`);
|
|
378
383
|
}
|
|
@@ -122,10 +122,14 @@ function buildWhereClause(where, columns) {
|
|
|
122
122
|
const clauses = [];
|
|
123
123
|
const params = [];
|
|
124
124
|
for (const [key, value] of Object.entries(where)) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
125
|
+
if (value === null) {
|
|
126
|
+
clauses.push(`${key} IS NULL`);
|
|
127
|
+
} else {
|
|
128
|
+
clauses.push(`${key} = ?`);
|
|
129
|
+
const colMeta = columns[key]?._meta;
|
|
130
|
+
const convertedValue = convertValueForSql(value, colMeta?.sqlType);
|
|
131
|
+
params.push(convertedValue);
|
|
132
|
+
}
|
|
129
133
|
}
|
|
130
134
|
return { clauses, params };
|
|
131
135
|
}
|
|
@@ -63,6 +63,29 @@ function createPostgresDriver(url, pool) {
|
|
|
63
63
|
const result = await queryFn(sql2, params ?? []);
|
|
64
64
|
return { rowsAffected: result.rowCount };
|
|
65
65
|
},
|
|
66
|
+
async beginTransaction(fn) {
|
|
67
|
+
return sql.begin(async (txSql) => {
|
|
68
|
+
const txQueryFn = async (sqlStr, params) => {
|
|
69
|
+
try {
|
|
70
|
+
const result = await txSql.unsafe(sqlStr, params);
|
|
71
|
+
const rows = result.map((row) => {
|
|
72
|
+
const mapped = {};
|
|
73
|
+
for (const [key, value] of Object.entries(row)) {
|
|
74
|
+
mapped[key] = coerceValue(value);
|
|
75
|
+
}
|
|
76
|
+
return mapped;
|
|
77
|
+
});
|
|
78
|
+
return {
|
|
79
|
+
rows,
|
|
80
|
+
rowCount: result.count ?? rows.length
|
|
81
|
+
};
|
|
82
|
+
} catch (error) {
|
|
83
|
+
adaptPostgresError(error);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
return fn(txQueryFn);
|
|
87
|
+
});
|
|
88
|
+
},
|
|
66
89
|
async close() {
|
|
67
90
|
await sql.end();
|
|
68
91
|
},
|
package/dist/sql/index.js
CHANGED
package/dist/sqlite/index.d.ts
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* Query executor — wraps raw SQL execution with error mapping.
|
|
3
|
+
*
|
|
4
|
+
* Takes a query function (from the database driver) and wraps it to:
|
|
5
|
+
* 1. Execute parameterized SQL
|
|
6
|
+
* 2. Map PG errors to typed DbError subclasses
|
|
7
|
+
* 3. Return typed QueryResult
|
|
8
|
+
*/
|
|
9
|
+
interface ExecutorResult<T> {
|
|
10
|
+
readonly rows: readonly T[];
|
|
11
|
+
readonly rowCount: number;
|
|
12
|
+
}
|
|
13
|
+
type QueryFn = <T>(sql: string, params: readonly unknown[]) => Promise<ExecutorResult<T>>;
|
|
14
|
+
/**
|
|
2
15
|
* Database driver interface.
|
|
3
16
|
*
|
|
4
17
|
* Provides a unified interface for different database backends
|
|
@@ -16,6 +29,12 @@ interface DbDriver {
|
|
|
16
29
|
rowsAffected: number;
|
|
17
30
|
}>;
|
|
18
31
|
/**
|
|
32
|
+
* Execute a callback within a database transaction.
|
|
33
|
+
* The callback receives a transaction-scoped QueryFn.
|
|
34
|
+
* Optional — not all drivers support transactions (e.g., D1).
|
|
35
|
+
*/
|
|
36
|
+
beginTransaction?<T>(fn: (txQueryFn: QueryFn) => Promise<T>): Promise<T>;
|
|
37
|
+
/**
|
|
19
38
|
* Close the database connection.
|
|
20
39
|
*/
|
|
21
40
|
close(): Promise<void>;
|
|
@@ -54,6 +73,7 @@ interface ColumnBuilder<
|
|
|
54
73
|
readonly _meta: TMeta;
|
|
55
74
|
primary(options?: {
|
|
56
75
|
generate?: "cuid" | "uuid" | "nanoid";
|
|
76
|
+
generated?: boolean;
|
|
57
77
|
}): ColumnBuilder<TType, Omit<TMeta, "primary" | "hasDefault" | "generate"> & {
|
|
58
78
|
readonly primary: true;
|
|
59
79
|
readonly hasDefault: true;
|
|
@@ -84,6 +104,20 @@ interface ColumnBuilder<
|
|
|
84
104
|
}>;
|
|
85
105
|
}
|
|
86
106
|
type InferColumnType<C> = C extends ColumnBuilder<infer T, ColumnMetadata> ? T : never;
|
|
107
|
+
interface ThroughDef<TJoin extends TableDef<ColumnRecord> = TableDef<ColumnRecord>> {
|
|
108
|
+
readonly table: () => TJoin;
|
|
109
|
+
readonly thisKey: string;
|
|
110
|
+
readonly thatKey: string;
|
|
111
|
+
}
|
|
112
|
+
interface RelationDef<
|
|
113
|
+
TTarget extends TableDef<ColumnRecord> = TableDef<ColumnRecord>,
|
|
114
|
+
TType extends "one" | "many" = "one" | "many"
|
|
115
|
+
> {
|
|
116
|
+
readonly _type: TType;
|
|
117
|
+
readonly _target: () => TTarget;
|
|
118
|
+
readonly _foreignKey: string | null;
|
|
119
|
+
readonly _through: ThroughDef | null;
|
|
120
|
+
}
|
|
87
121
|
type IndexType = "btree" | "hash" | "gin" | "gist" | "brin";
|
|
88
122
|
interface IndexDef {
|
|
89
123
|
readonly columns: readonly string[];
|
|
@@ -172,28 +206,129 @@ interface TableDef<TColumns extends ColumnRecord = ColumnRecord> {
|
|
|
172
206
|
/** Mark this table as shared / cross-tenant. */
|
|
173
207
|
shared(): TableDef<TColumns>;
|
|
174
208
|
}
|
|
209
|
+
/** Operators available for comparable types (number, string, Date, bigint). */
|
|
210
|
+
interface ComparisonOperators<T> {
|
|
211
|
+
readonly eq?: T;
|
|
212
|
+
readonly ne?: T;
|
|
213
|
+
readonly gt?: T;
|
|
214
|
+
readonly gte?: T;
|
|
215
|
+
readonly lt?: T;
|
|
216
|
+
readonly lte?: T;
|
|
217
|
+
readonly in?: readonly T[];
|
|
218
|
+
readonly notIn?: readonly T[];
|
|
219
|
+
}
|
|
220
|
+
/** Additional operators for string columns. */
|
|
221
|
+
interface StringOperators {
|
|
222
|
+
readonly contains?: string;
|
|
223
|
+
readonly startsWith?: string;
|
|
224
|
+
readonly endsWith?: string;
|
|
225
|
+
}
|
|
226
|
+
/** The `isNull` operator — only available for nullable columns. */
|
|
227
|
+
interface NullOperator {
|
|
228
|
+
readonly isNull?: boolean;
|
|
229
|
+
}
|
|
175
230
|
/**
|
|
176
|
-
*
|
|
231
|
+
* Resolves the filter operators for a single column based on its inferred type
|
|
232
|
+
* and nullable metadata.
|
|
177
233
|
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
234
|
+
* - All types get comparison + in/notIn
|
|
235
|
+
* - String types additionally get contains, startsWith, endsWith
|
|
236
|
+
* - Nullable columns additionally get isNull
|
|
237
|
+
*
|
|
238
|
+
* Uses [T] extends [string] to prevent union distribution -- ensures that a
|
|
239
|
+
* union like 'admin' | 'editor' keeps the full union in each operator slot.
|
|
180
240
|
*/
|
|
181
|
-
|
|
241
|
+
type ColumnFilterOperators<
|
|
242
|
+
TType,
|
|
243
|
+
TNullable extends boolean
|
|
244
|
+
> = ([TType] extends [string] ? ComparisonOperators<TType> & StringOperators : ComparisonOperators<TType>) & (TNullable extends true ? NullOperator : unknown);
|
|
245
|
+
/** Determine whether a column is nullable from its metadata. */
|
|
246
|
+
type IsNullable<C> = C extends ColumnBuilder<unknown, infer M> ? M extends {
|
|
247
|
+
readonly nullable: true;
|
|
248
|
+
} ? true : false : false;
|
|
249
|
+
/**
|
|
250
|
+
* FilterType<TColumns> — typed where clause.
|
|
251
|
+
*
|
|
252
|
+
* Each key maps to either:
|
|
253
|
+
* - A direct value (shorthand for `{ eq: value }`)
|
|
254
|
+
* - An object with typed filter operators
|
|
255
|
+
*/
|
|
256
|
+
type FilterType<TColumns extends ColumnRecord> = { [K in keyof TColumns]? : InferColumnType<TColumns[K]> | ColumnFilterOperators<InferColumnType<TColumns[K]>, IsNullable<TColumns[K]>> };
|
|
257
|
+
type OrderByType<TColumns extends ColumnRecord> = { [K in keyof TColumns]? : "asc" | "desc" };
|
|
258
|
+
/** Relations record — maps relation names to RelationDef. */
|
|
259
|
+
type RelationsRecord = Record<string, RelationDef>;
|
|
260
|
+
/**
|
|
261
|
+
* The shape of include options for a given relations record.
|
|
262
|
+
* Each relation can be:
|
|
263
|
+
* - `true` — include with default fields
|
|
264
|
+
* - An object with `select`, `where`, `orderBy`, `limit` constrained to target table columns
|
|
265
|
+
*/
|
|
266
|
+
type IncludeOption<TRelations extends RelationsRecord> = { [K in keyof TRelations]? : true | (RelationTarget<TRelations[K]> extends TableDef<infer TCols> ? {
|
|
267
|
+
select?: { [C in keyof TCols]? : true };
|
|
268
|
+
where?: FilterType<TCols>;
|
|
269
|
+
orderBy?: OrderByType<TCols>;
|
|
270
|
+
limit?: number;
|
|
271
|
+
/** Nested includes — untyped until full model registry is threaded through. */
|
|
272
|
+
include?: Record<string, unknown>;
|
|
273
|
+
} : never) };
|
|
274
|
+
/** Extract the target table from a RelationDef. */
|
|
275
|
+
type RelationTarget<R> = R extends RelationDef<infer TTarget, "one" | "many"> ? TTarget : never;
|
|
276
|
+
/** A model entry in the database registry, pairing a table with its relations. */
|
|
277
|
+
interface ModelEntry<
|
|
278
|
+
TTable extends TableDef<ColumnRecord> = TableDef<ColumnRecord>,
|
|
279
|
+
TRelations extends RelationsRecord = RelationsRecord
|
|
280
|
+
> {
|
|
281
|
+
readonly table: TTable;
|
|
282
|
+
readonly relations: TRelations;
|
|
283
|
+
}
|
|
284
|
+
/** A single include entry with optional query constraints. */
|
|
285
|
+
interface AdapterIncludeEntry {
|
|
286
|
+
select?: Record<string, true>;
|
|
182
287
|
where?: Record<string, unknown>;
|
|
183
288
|
orderBy?: Record<string, "asc" | "desc">;
|
|
184
289
|
limit?: number;
|
|
290
|
+
include?: Record<string, unknown>;
|
|
291
|
+
}
|
|
292
|
+
/** Include specification: maps relation names to `true` or structured entries. */
|
|
293
|
+
type AdapterIncludeSpec = Record<string, true | AdapterIncludeEntry>;
|
|
294
|
+
/**
|
|
295
|
+
* Resolves the where clause type for a given entry.
|
|
296
|
+
* When TEntry is the default (unparameterized), falls back to Record<string, unknown>.
|
|
297
|
+
*/
|
|
298
|
+
type ResolveWhere<TEntry extends ModelEntry> = TEntry extends ModelEntry<infer TTable> ? TTable extends TableDef<infer TCols> ? [ColumnRecord] extends [TCols] ? Record<string, unknown> : FilterType<TCols> : Record<string, unknown> : Record<string, unknown>;
|
|
299
|
+
/**
|
|
300
|
+
* Resolves the orderBy type for a given entry.
|
|
301
|
+
* When TEntry is the default (unparameterized), falls back to Record<string, 'asc' | 'desc'>.
|
|
302
|
+
*/
|
|
303
|
+
type ResolveOrderBy<TEntry extends ModelEntry> = TEntry extends ModelEntry<infer TTable> ? TTable extends TableDef<infer TCols> ? [ColumnRecord] extends [TCols] ? Record<string, "asc" | "desc"> : OrderByType<TCols> : Record<string, "asc" | "desc"> : Record<string, "asc" | "desc">;
|
|
304
|
+
/**
|
|
305
|
+
* Resolves the include type for a given entry.
|
|
306
|
+
* When TEntry is the default (unparameterized), falls back to AdapterIncludeSpec.
|
|
307
|
+
*/
|
|
308
|
+
type ResolveInclude<TEntry extends ModelEntry> = TEntry extends ModelEntry<TableDef<ColumnRecord>, infer TRels> ? [Record<string, never>] extends [TRels] ? AdapterIncludeSpec : IncludeOption<TRels> : AdapterIncludeSpec;
|
|
309
|
+
interface ListOptions<TEntry extends ModelEntry = ModelEntry> {
|
|
310
|
+
where?: ResolveWhere<TEntry>;
|
|
311
|
+
orderBy?: ResolveOrderBy<TEntry>;
|
|
312
|
+
limit?: number;
|
|
185
313
|
/** Cursor-based pagination: fetch records after this ID. */
|
|
186
314
|
after?: string;
|
|
315
|
+
/** Relation include specification for relation loading. */
|
|
316
|
+
include?: ResolveInclude<TEntry>;
|
|
317
|
+
}
|
|
318
|
+
/** Options for get-by-id operations. */
|
|
319
|
+
interface GetOptions<TEntry extends ModelEntry = ModelEntry> {
|
|
320
|
+
/** Relation include specification for relation loading. */
|
|
321
|
+
include?: ResolveInclude<TEntry>;
|
|
187
322
|
}
|
|
188
|
-
interface EntityDbAdapter {
|
|
189
|
-
get(id: string): Promise<
|
|
190
|
-
list(options?: ListOptions): Promise<{
|
|
191
|
-
data:
|
|
323
|
+
interface EntityDbAdapter<TEntry extends ModelEntry = ModelEntry> {
|
|
324
|
+
get(id: string, options?: GetOptions<TEntry>): Promise<TEntry["table"]["$response"] | null>;
|
|
325
|
+
list(options?: ListOptions<TEntry>): Promise<{
|
|
326
|
+
data: TEntry["table"]["$response"][];
|
|
192
327
|
total: number;
|
|
193
328
|
}>;
|
|
194
|
-
create(data:
|
|
195
|
-
update(id: string, data:
|
|
196
|
-
delete(id: string): Promise<
|
|
329
|
+
create(data: TEntry["table"]["$create_input"]): Promise<TEntry["table"]["$response"]>;
|
|
330
|
+
update(id: string, data: TEntry["table"]["$update_input"]): Promise<TEntry["table"]["$response"]>;
|
|
331
|
+
delete(id: string): Promise<TEntry["table"]["$response"] | null>;
|
|
197
332
|
}
|
|
198
333
|
interface SqliteAdapterOptions<T extends ColumnRecord> {
|
|
199
334
|
/** The table schema definition */
|
package/dist/sqlite/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vertz/db",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.16",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Database layer for Vertz — typed queries, migrations, codegen",
|
|
@@ -81,8 +81,8 @@
|
|
|
81
81
|
"node": ">=22"
|
|
82
82
|
},
|
|
83
83
|
"dependencies": {
|
|
84
|
-
"@vertz/errors": "^0.2.
|
|
85
|
-
"@vertz/schema": "^0.2.
|
|
84
|
+
"@vertz/errors": "^0.2.15",
|
|
85
|
+
"@vertz/schema": "^0.2.15",
|
|
86
86
|
"@paralleldrive/cuid2": "^3.3.0",
|
|
87
87
|
"nanoid": "^5.1.5",
|
|
88
88
|
"uuid": "^13.0.0"
|