@fragno-dev/db 0.1.12 → 0.1.13
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/.turbo/turbo-build.log +27 -27
- package/CHANGELOG.md +6 -0
- package/dist/adapters/drizzle/drizzle-adapter.d.ts.map +1 -1
- package/dist/adapters/drizzle/drizzle-adapter.js +1 -1
- package/dist/adapters/drizzle/drizzle-query.d.ts.map +1 -1
- package/dist/adapters/drizzle/drizzle-query.js +4 -0
- package/dist/adapters/drizzle/drizzle-query.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-uow-compiler.js +2 -1
- package/dist/adapters/drizzle/drizzle-uow-compiler.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-uow-decoder.js +25 -1
- package/dist/adapters/drizzle/drizzle-uow-decoder.js.map +1 -1
- package/dist/adapters/drizzle/generate.js +1 -1
- package/dist/adapters/kysely/kysely-adapter.d.ts +1 -1
- package/dist/adapters/kysely/kysely-query.d.ts.map +1 -1
- package/dist/adapters/kysely/kysely-query.js +29 -1
- package/dist/adapters/kysely/kysely-query.js.map +1 -1
- package/dist/adapters/kysely/kysely-uow-compiler.js +2 -1
- package/dist/adapters/kysely/kysely-uow-compiler.js.map +1 -1
- package/dist/adapters/kysely/migration/execute-base.js +1 -1
- package/dist/mod.d.ts +2 -1
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +2 -1
- package/dist/mod.js.map +1 -1
- package/dist/query/cursor.d.ts +67 -32
- package/dist/query/cursor.d.ts.map +1 -1
- package/dist/query/cursor.js +84 -31
- package/dist/query/cursor.js.map +1 -1
- package/dist/query/query.d.ts +5 -0
- package/dist/query/query.d.ts.map +1 -1
- package/dist/query/unit-of-work.d.ts +14 -4
- package/dist/query/unit-of-work.d.ts.map +1 -1
- package/dist/query/unit-of-work.js +52 -9
- package/dist/query/unit-of-work.js.map +1 -1
- package/package.json +3 -3
- package/src/adapters/drizzle/drizzle-adapter-pglite.test.ts +72 -5
- package/src/adapters/drizzle/drizzle-adapter-sqlite.test.ts +6 -4
- package/src/adapters/drizzle/drizzle-query.ts +9 -0
- package/src/adapters/drizzle/drizzle-uow-compiler.test.ts +19 -3
- package/src/adapters/drizzle/drizzle-uow-compiler.ts +3 -2
- package/src/adapters/drizzle/drizzle-uow-decoder.ts +40 -1
- package/src/adapters/kysely/kysely-adapter-pglite.test.ts +102 -4
- package/src/adapters/kysely/kysely-query.ts +50 -1
- package/src/adapters/kysely/kysely-uow-compiler.test.ts +19 -3
- package/src/adapters/kysely/kysely-uow-compiler.ts +3 -2
- package/src/mod.ts +6 -1
- package/src/query/cursor.test.ts +113 -68
- package/src/query/cursor.ts +127 -36
- package/src/query/query.ts +19 -0
- package/src/query/unit-of-work.ts +133 -15
|
@@ -4,6 +4,8 @@ import type { Condition, ConditionBuilder } from "./condition-builder";
|
|
|
4
4
|
import type { SelectClause, TableToInsertValues, TableToUpdateValues, SelectResult } from "./query";
|
|
5
5
|
import { buildCondition } from "./condition-builder";
|
|
6
6
|
import type { CompiledJoin } from "./orm/orm";
|
|
7
|
+
import type { CursorResult } from "./cursor";
|
|
8
|
+
import { Cursor } from "./cursor";
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* Builder for updateMany operations that supports both whereIndex and set chaining
|
|
@@ -112,11 +114,11 @@ type FindOptions<
|
|
|
112
114
|
/**
|
|
113
115
|
* Cursor for pagination - continue after this cursor
|
|
114
116
|
*/
|
|
115
|
-
after?: string;
|
|
117
|
+
after?: Cursor | string;
|
|
116
118
|
/**
|
|
117
119
|
* Cursor for pagination - continue before this cursor
|
|
118
120
|
*/
|
|
119
|
-
before?: string;
|
|
121
|
+
before?: Cursor | string;
|
|
120
122
|
/**
|
|
121
123
|
* Number of results per page
|
|
122
124
|
*/
|
|
@@ -144,6 +146,7 @@ export type RetrievalOperation<
|
|
|
144
146
|
table: TTable;
|
|
145
147
|
indexName: string;
|
|
146
148
|
options: FindOptions<TTable, SelectClause<TTable>>;
|
|
149
|
+
withCursor?: boolean;
|
|
147
150
|
}
|
|
148
151
|
| {
|
|
149
152
|
type: "count";
|
|
@@ -262,12 +265,13 @@ export class FindBuilder<
|
|
|
262
265
|
indexName: string;
|
|
263
266
|
direction: "asc" | "desc";
|
|
264
267
|
};
|
|
265
|
-
#afterCursor?: string;
|
|
266
|
-
#beforeCursor?: string;
|
|
268
|
+
#afterCursor?: Cursor | string;
|
|
269
|
+
#beforeCursor?: Cursor | string;
|
|
267
270
|
#pageSizeValue?: number;
|
|
268
271
|
#selectClause?: TSelect;
|
|
269
272
|
#joinClause?: (jb: IndexedJoinBuilder<TTable, {}>) => IndexedJoinBuilder<TTable, TJoinOut>;
|
|
270
273
|
#countMode = false;
|
|
274
|
+
#cursorMetadata?: Cursor;
|
|
271
275
|
|
|
272
276
|
constructor(tableName: string, table: TTable) {
|
|
273
277
|
this.#tableName = tableName;
|
|
@@ -357,17 +361,27 @@ export class FindBuilder<
|
|
|
357
361
|
|
|
358
362
|
/**
|
|
359
363
|
* Set cursor to continue pagination after this point (forward pagination)
|
|
364
|
+
* If a Cursor object is provided, its metadata will be used to set defaults for
|
|
365
|
+
* index, orderByIndex, and pageSize (if not explicitly set)
|
|
360
366
|
*/
|
|
361
|
-
after(cursor: string): this {
|
|
367
|
+
after(cursor: Cursor | string): this {
|
|
362
368
|
this.#afterCursor = cursor;
|
|
369
|
+
if (cursor instanceof Cursor) {
|
|
370
|
+
this.#cursorMetadata = cursor;
|
|
371
|
+
}
|
|
363
372
|
return this;
|
|
364
373
|
}
|
|
365
374
|
|
|
366
375
|
/**
|
|
367
376
|
* Set cursor to continue pagination before this point (backward pagination)
|
|
377
|
+
* If a Cursor object is provided, its metadata will be used to set defaults for
|
|
378
|
+
* index, orderByIndex, and pageSize (if not explicitly set)
|
|
368
379
|
*/
|
|
369
|
-
before(cursor: string): this {
|
|
380
|
+
before(cursor: Cursor | string): this {
|
|
370
381
|
this.#beforeCursor = cursor;
|
|
382
|
+
if (cursor instanceof Cursor) {
|
|
383
|
+
this.#cursorMetadata = cursor;
|
|
384
|
+
}
|
|
371
385
|
return this;
|
|
372
386
|
}
|
|
373
387
|
|
|
@@ -400,7 +414,47 @@ export class FindBuilder<
|
|
|
400
414
|
indexName: string;
|
|
401
415
|
options: Pick<FindOptions<TTable>, "where" | "useIndex">;
|
|
402
416
|
} {
|
|
403
|
-
if
|
|
417
|
+
// Apply cursor metadata as defaults if available and not explicitly set
|
|
418
|
+
let indexName = this.#indexName;
|
|
419
|
+
let orderByIndex = this.#orderByIndexClause;
|
|
420
|
+
let pageSize = this.#pageSizeValue;
|
|
421
|
+
|
|
422
|
+
if (this.#cursorMetadata) {
|
|
423
|
+
// Use cursor metadata as defaults
|
|
424
|
+
if (!indexName) {
|
|
425
|
+
indexName = this.#cursorMetadata.indexName;
|
|
426
|
+
}
|
|
427
|
+
if (!orderByIndex) {
|
|
428
|
+
orderByIndex = {
|
|
429
|
+
indexName: this.#cursorMetadata.indexName,
|
|
430
|
+
direction: this.#cursorMetadata.orderDirection,
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
if (pageSize === undefined) {
|
|
434
|
+
pageSize = this.#cursorMetadata.pageSize;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Validate that explicit params match cursor params
|
|
438
|
+
if (indexName && indexName !== this.#cursorMetadata.indexName) {
|
|
439
|
+
throw new Error(
|
|
440
|
+
`Index mismatch: builder specifies "${indexName}" but cursor specifies "${this.#cursorMetadata.indexName}"`,
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
if (
|
|
444
|
+
orderByIndex &&
|
|
445
|
+
(orderByIndex.indexName !== this.#cursorMetadata.indexName ||
|
|
446
|
+
orderByIndex.direction !== this.#cursorMetadata.orderDirection)
|
|
447
|
+
) {
|
|
448
|
+
throw new Error(`Order mismatch: builder and cursor specify different ordering`);
|
|
449
|
+
}
|
|
450
|
+
if (pageSize !== undefined && pageSize !== this.#cursorMetadata.pageSize) {
|
|
451
|
+
throw new Error(
|
|
452
|
+
`Page size mismatch: builder specifies ${pageSize} but cursor specifies ${this.#cursorMetadata.pageSize}`,
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (!indexName) {
|
|
404
458
|
throw new Error(
|
|
405
459
|
`Must specify an index using .whereIndex() before finalizing find operation on table "${this.#tableName}"`,
|
|
406
460
|
);
|
|
@@ -410,9 +464,9 @@ export class FindBuilder<
|
|
|
410
464
|
if (this.#countMode) {
|
|
411
465
|
return {
|
|
412
466
|
type: "count",
|
|
413
|
-
indexName
|
|
467
|
+
indexName,
|
|
414
468
|
options: {
|
|
415
|
-
useIndex:
|
|
469
|
+
useIndex: indexName,
|
|
416
470
|
where: this.#whereClause,
|
|
417
471
|
},
|
|
418
472
|
};
|
|
@@ -424,18 +478,24 @@ export class FindBuilder<
|
|
|
424
478
|
compiledJoins = buildJoinIndexed(this.#table, this.#joinClause);
|
|
425
479
|
}
|
|
426
480
|
|
|
481
|
+
// Convert Cursor objects to strings for after/before
|
|
482
|
+
const afterCursor =
|
|
483
|
+
this.#afterCursor instanceof Cursor ? this.#afterCursor.encode() : this.#afterCursor;
|
|
484
|
+
const beforeCursor =
|
|
485
|
+
this.#beforeCursor instanceof Cursor ? this.#beforeCursor.encode() : this.#beforeCursor;
|
|
486
|
+
|
|
427
487
|
const options: FindOptions<TTable, TSelect> = {
|
|
428
|
-
useIndex:
|
|
488
|
+
useIndex: indexName,
|
|
429
489
|
select: this.#selectClause,
|
|
430
490
|
where: this.#whereClause,
|
|
431
|
-
orderByIndex
|
|
432
|
-
after:
|
|
433
|
-
before:
|
|
434
|
-
pageSize
|
|
491
|
+
orderByIndex,
|
|
492
|
+
after: afterCursor,
|
|
493
|
+
before: beforeCursor,
|
|
494
|
+
pageSize,
|
|
435
495
|
joins: compiledJoins,
|
|
436
496
|
};
|
|
437
497
|
|
|
438
|
-
return { type: "find", indexName
|
|
498
|
+
return { type: "find", indexName, options };
|
|
439
499
|
}
|
|
440
500
|
}
|
|
441
501
|
|
|
@@ -983,6 +1043,64 @@ export class UnitOfWork<
|
|
|
983
1043
|
>;
|
|
984
1044
|
}
|
|
985
1045
|
|
|
1046
|
+
/**
|
|
1047
|
+
* Add a find operation with cursor metadata (retrieval phase only)
|
|
1048
|
+
*/
|
|
1049
|
+
findWithCursor<
|
|
1050
|
+
TTableName extends keyof TSchema["tables"] & string,
|
|
1051
|
+
TSelect extends SelectClause<TSchema["tables"][TTableName]> = true,
|
|
1052
|
+
TJoinOut = {},
|
|
1053
|
+
>(
|
|
1054
|
+
tableName: TTableName,
|
|
1055
|
+
builderFn: (
|
|
1056
|
+
// We omit "build" because we don't want to expose it to the user
|
|
1057
|
+
builder: Omit<FindBuilder<TSchema["tables"][TTableName]>, "build">,
|
|
1058
|
+
) => Omit<FindBuilder<TSchema["tables"][TTableName], TSelect, TJoinOut>, "build"> | void,
|
|
1059
|
+
): UnitOfWork<
|
|
1060
|
+
TSchema,
|
|
1061
|
+
[
|
|
1062
|
+
...TRetrievalResults,
|
|
1063
|
+
CursorResult<SelectResult<TSchema["tables"][TTableName], TJoinOut, TSelect>>,
|
|
1064
|
+
],
|
|
1065
|
+
TRawInput
|
|
1066
|
+
> {
|
|
1067
|
+
if (this.#state !== "building-retrieval") {
|
|
1068
|
+
throw new Error(
|
|
1069
|
+
`findWithCursor() can only be called during retrieval phase. Current state: ${this.#state}`,
|
|
1070
|
+
);
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
const table = this.#schema.tables[tableName];
|
|
1074
|
+
if (!table) {
|
|
1075
|
+
throw new Error(`Table ${tableName} not found in schema`);
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
// Create builder and pass to callback
|
|
1079
|
+
const builder = new FindBuilder(tableName, table as TSchema["tables"][TTableName]);
|
|
1080
|
+
builderFn(builder);
|
|
1081
|
+
const { indexName, options, type } = builder.build();
|
|
1082
|
+
|
|
1083
|
+
this.#retrievalOps.push({
|
|
1084
|
+
type,
|
|
1085
|
+
// Safe: we know the table is part of the schema from the findWithCursor() method
|
|
1086
|
+
table: table as TSchema["tables"][TTableName],
|
|
1087
|
+
indexName,
|
|
1088
|
+
// Safe: we're storing the options for later compilation
|
|
1089
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1090
|
+
options: options as any,
|
|
1091
|
+
withCursor: true,
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1094
|
+
return this as unknown as UnitOfWork<
|
|
1095
|
+
TSchema,
|
|
1096
|
+
[
|
|
1097
|
+
...TRetrievalResults,
|
|
1098
|
+
CursorResult<SelectResult<TSchema["tables"][TTableName], TJoinOut, TSelect>>,
|
|
1099
|
+
],
|
|
1100
|
+
TRawInput
|
|
1101
|
+
>;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
986
1104
|
/**
|
|
987
1105
|
* Add a create operation (mutation phase only)
|
|
988
1106
|
* Returns a FragnoId with the external ID that can be used immediately in subsequent operations
|