@fragno-dev/db 0.1.10 → 0.1.12
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 +40 -37
- package/CHANGELOG.md +19 -0
- package/dist/adapters/drizzle/drizzle-query.d.ts +1 -0
- package/dist/adapters/drizzle/drizzle-query.d.ts.map +1 -1
- package/dist/adapters/drizzle/drizzle-query.js +41 -38
- package/dist/adapters/drizzle/drizzle-query.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-uow-compiler.d.ts +2 -0
- package/dist/adapters/drizzle/drizzle-uow-compiler.d.ts.map +1 -1
- package/dist/adapters/drizzle/drizzle-uow-compiler.js +13 -1
- package/dist/adapters/drizzle/drizzle-uow-compiler.js.map +1 -1
- package/dist/adapters/drizzle/shared.d.ts +1 -0
- package/dist/adapters/kysely/kysely-adapter.d.ts +3 -2
- package/dist/adapters/kysely/kysely-adapter.d.ts.map +1 -1
- package/dist/adapters/kysely/kysely-adapter.js.map +1 -1
- package/dist/adapters/kysely/kysely-query-builder.js +23 -12
- package/dist/adapters/kysely/kysely-query-builder.js.map +1 -1
- package/dist/adapters/kysely/kysely-query.d.ts +22 -0
- package/dist/adapters/kysely/kysely-query.d.ts.map +1 -0
- package/dist/adapters/kysely/kysely-query.js +72 -50
- package/dist/adapters/kysely/kysely-query.js.map +1 -1
- package/dist/adapters/kysely/kysely-uow-executor.js +2 -2
- package/dist/adapters/kysely/kysely-uow-executor.js.map +1 -1
- package/dist/migration-engine/generation-engine.d.ts +1 -1
- package/dist/migration-engine/generation-engine.d.ts.map +1 -1
- package/dist/migration-engine/generation-engine.js.map +1 -1
- package/dist/mod.d.ts +5 -5
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js.map +1 -1
- package/dist/query/query.d.ts +24 -8
- package/dist/query/query.d.ts.map +1 -1
- package/dist/query/result-transform.js +17 -5
- package/dist/query/result-transform.js.map +1 -1
- package/dist/query/unit-of-work.d.ts +5 -4
- package/dist/query/unit-of-work.d.ts.map +1 -1
- package/dist/query/unit-of-work.js +2 -3
- package/dist/query/unit-of-work.js.map +1 -1
- package/dist/schema/serialize.js +2 -0
- package/dist/schema/serialize.js.map +1 -1
- package/package.json +2 -2
- package/src/adapters/drizzle/drizzle-adapter-pglite.test.ts +170 -50
- package/src/adapters/drizzle/drizzle-adapter-sqlite.test.ts +89 -35
- package/src/adapters/drizzle/drizzle-query.test.ts +56 -6
- package/src/adapters/drizzle/drizzle-query.ts +68 -63
- package/src/adapters/drizzle/drizzle-uow-compiler.test.ts +63 -3
- package/src/adapters/drizzle/drizzle-uow-compiler.ts +27 -2
- package/src/adapters/kysely/kysely-adapter-pglite.test.ts +88 -0
- package/src/adapters/kysely/kysely-adapter.ts +6 -3
- package/src/adapters/kysely/kysely-query-builder.ts +35 -11
- package/src/adapters/kysely/kysely-query.test.ts +498 -0
- package/src/adapters/kysely/kysely-query.ts +137 -82
- package/src/adapters/kysely/kysely-uow-compiler.test.ts +66 -0
- package/src/adapters/kysely/kysely-uow-executor.ts +5 -9
- package/src/migration-engine/generation-engine.ts +2 -1
- package/src/mod.ts +6 -6
- package/src/query/query-type.test.ts +34 -14
- package/src/query/query.ts +77 -36
- package/src/query/result-transform.test.ts +5 -5
- package/src/query/result-transform.ts +29 -11
- package/src/query/unit-of-work.ts +8 -11
- package/src/schema/serialize.test.ts +223 -0
- package/src/schema/serialize.ts +16 -0
|
@@ -38,6 +38,10 @@ describe("DrizzleAdapter PGLite", () => {
|
|
|
38
38
|
.addColumn("user_id", referenceColumn())
|
|
39
39
|
.addColumn("title", column("string"))
|
|
40
40
|
.addColumn("content", column("string"))
|
|
41
|
+
.addColumn(
|
|
42
|
+
"created_at",
|
|
43
|
+
column("timestamp").defaultTo((b) => b.now()),
|
|
44
|
+
)
|
|
41
45
|
.createIndex("posts_user_idx", ["user_id"]);
|
|
42
46
|
})
|
|
43
47
|
.addTable("comments", (t) => {
|
|
@@ -116,7 +120,8 @@ describe("DrizzleAdapter PGLite", () => {
|
|
|
116
120
|
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
117
121
|
|
|
118
122
|
// Create initial user using UOW
|
|
119
|
-
const createUow = queryEngine.createUnitOfWork("create-user")
|
|
123
|
+
const createUow = queryEngine.createUnitOfWork("create-user");
|
|
124
|
+
createUow.create("users", {
|
|
120
125
|
name: "Alice",
|
|
121
126
|
age: 25,
|
|
122
127
|
});
|
|
@@ -201,12 +206,11 @@ describe("DrizzleAdapter PGLite", () => {
|
|
|
201
206
|
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
202
207
|
|
|
203
208
|
// Create some users
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
.executeMutations();
|
|
209
|
+
const createUow = queryEngine.createUnitOfWork("create-users");
|
|
210
|
+
createUow.create("users", { name: "User1", age: 20 });
|
|
211
|
+
createUow.create("users", { name: "User2", age: 30 });
|
|
212
|
+
createUow.create("users", { name: "User3", age: 40 });
|
|
213
|
+
await createUow.executeMutations();
|
|
210
214
|
|
|
211
215
|
// Count all users
|
|
212
216
|
const [totalCount] = await queryEngine
|
|
@@ -221,13 +225,12 @@ describe("DrizzleAdapter PGLite", () => {
|
|
|
221
225
|
it("should support cursor-based pagination", async () => {
|
|
222
226
|
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
223
227
|
|
|
224
|
-
const createUow = queryEngine
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
.create("users", { name: "Page User E", age: 60 });
|
|
228
|
+
const createUow = queryEngine.createUnitOfWork("create-users");
|
|
229
|
+
createUow.create("users", { name: "Page User A", age: 20 });
|
|
230
|
+
createUow.create("users", { name: "Page User B", age: 30 });
|
|
231
|
+
createUow.create("users", { name: "Page User C", age: 40 });
|
|
232
|
+
createUow.create("users", { name: "Page User D", age: 50 });
|
|
233
|
+
createUow.create("users", { name: "Page User E", age: 60 });
|
|
231
234
|
|
|
232
235
|
await createUow.executeMutations();
|
|
233
236
|
|
|
@@ -269,9 +272,8 @@ describe("DrizzleAdapter PGLite", () => {
|
|
|
269
272
|
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
270
273
|
const queries: DrizzleCompiledQuery[] = [];
|
|
271
274
|
|
|
272
|
-
const createUow = queryEngine
|
|
273
|
-
|
|
274
|
-
.create("users", { name: "Email User", age: 20 });
|
|
275
|
+
const createUow = queryEngine.createUnitOfWork("create-users");
|
|
276
|
+
createUow.create("users", { name: "Email User", age: 20 });
|
|
275
277
|
|
|
276
278
|
await createUow.executeMutations();
|
|
277
279
|
|
|
@@ -282,7 +284,8 @@ describe("DrizzleAdapter PGLite", () => {
|
|
|
282
284
|
.executeRetrieve();
|
|
283
285
|
|
|
284
286
|
// Create an email for testing joins
|
|
285
|
-
const createEmailUow = queryEngine.createUnitOfWork("create-test-email")
|
|
287
|
+
const createEmailUow = queryEngine.createUnitOfWork("create-test-email");
|
|
288
|
+
createEmailUow.create("emails", {
|
|
286
289
|
user_id: existingUser.id,
|
|
287
290
|
email: "test@example.com",
|
|
288
291
|
is_primary: true,
|
|
@@ -330,9 +333,8 @@ describe("DrizzleAdapter PGLite", () => {
|
|
|
330
333
|
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
331
334
|
|
|
332
335
|
// Create a user first
|
|
333
|
-
const createUserUow = queryEngine
|
|
334
|
-
|
|
335
|
-
.create("users", { name: "External ID Test User", age: 35 });
|
|
336
|
+
const createUserUow = queryEngine.createUnitOfWork("create-user-for-external-id");
|
|
337
|
+
createUserUow.create("users", { name: "External ID Test User", age: 35 });
|
|
336
338
|
await createUserUow.executeMutations();
|
|
337
339
|
|
|
338
340
|
// Get the user
|
|
@@ -344,13 +346,12 @@ describe("DrizzleAdapter PGLite", () => {
|
|
|
344
346
|
.executeRetrieve();
|
|
345
347
|
|
|
346
348
|
// Create an email using just the external id string (not the full id object)
|
|
347
|
-
const createEmailUow = queryEngine
|
|
348
|
-
|
|
349
|
-
.
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
});
|
|
349
|
+
const createEmailUow = queryEngine.createUnitOfWork("create-email-with-external-id");
|
|
350
|
+
createEmailUow.create("emails", {
|
|
351
|
+
user_id: user.id.externalId,
|
|
352
|
+
email: "external-id-test@example.com",
|
|
353
|
+
is_primary: false,
|
|
354
|
+
});
|
|
354
355
|
|
|
355
356
|
const { success } = await createEmailUow.executeMutations();
|
|
356
357
|
expect(success).toBe(true);
|
|
@@ -385,9 +386,8 @@ describe("DrizzleAdapter PGLite", () => {
|
|
|
385
386
|
const queries: DrizzleCompiledQuery[] = [];
|
|
386
387
|
|
|
387
388
|
// Create a user (author)
|
|
388
|
-
const createAuthorUow = queryEngine
|
|
389
|
-
|
|
390
|
-
.create("users", { name: "Blog Author", age: 30 });
|
|
389
|
+
const createAuthorUow = queryEngine.createUnitOfWork("create-author");
|
|
390
|
+
createAuthorUow.create("users", { name: "Blog Author", age: 30 });
|
|
391
391
|
await createAuthorUow.executeMutations();
|
|
392
392
|
|
|
393
393
|
// Get the author
|
|
@@ -397,7 +397,8 @@ describe("DrizzleAdapter PGLite", () => {
|
|
|
397
397
|
.executeRetrieve();
|
|
398
398
|
|
|
399
399
|
// Create a post by the author
|
|
400
|
-
const createPostUow = queryEngine.createUnitOfWork("create-post")
|
|
400
|
+
const createPostUow = queryEngine.createUnitOfWork("create-post");
|
|
401
|
+
createPostUow.create("posts", {
|
|
401
402
|
user_id: author.id,
|
|
402
403
|
title: "My First Post",
|
|
403
404
|
content: "This is the content of my first post",
|
|
@@ -408,9 +409,8 @@ describe("DrizzleAdapter PGLite", () => {
|
|
|
408
409
|
const [[post]] = await queryEngine.createUnitOfWork("get-post").find("posts").executeRetrieve();
|
|
409
410
|
|
|
410
411
|
// Create a commenter
|
|
411
|
-
const createCommenterUow = queryEngine
|
|
412
|
-
|
|
413
|
-
.create("users", { name: "Commenter User", age: 25 });
|
|
412
|
+
const createCommenterUow = queryEngine.createUnitOfWork("create-commenter");
|
|
413
|
+
createCommenterUow.create("users", { name: "Commenter User", age: 25 });
|
|
414
414
|
await createCommenterUow.executeMutations();
|
|
415
415
|
|
|
416
416
|
// Get the commenter
|
|
@@ -420,7 +420,8 @@ describe("DrizzleAdapter PGLite", () => {
|
|
|
420
420
|
.executeRetrieve();
|
|
421
421
|
|
|
422
422
|
// Create a comment on the post
|
|
423
|
-
const createCommentUow = queryEngine.createUnitOfWork("create-comment")
|
|
423
|
+
const createCommentUow = queryEngine.createUnitOfWork("create-comment");
|
|
424
|
+
createCommentUow.create("comments", {
|
|
424
425
|
post_id: post.id,
|
|
425
426
|
user_id: commenter.id,
|
|
426
427
|
text: "Great post!",
|
|
@@ -564,24 +565,22 @@ describe("DrizzleAdapter PGLite", () => {
|
|
|
564
565
|
const queryEngine = adapter.createQueryEngine(authSchema, "test-namespace");
|
|
565
566
|
|
|
566
567
|
// Create a user
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
.
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
.executeMutations();
|
|
568
|
+
const createUserUow = queryEngine.createUnitOfWork("create-user");
|
|
569
|
+
createUserUow.create("user", {
|
|
570
|
+
email: "test@example.com",
|
|
571
|
+
passwordHash: "hash",
|
|
572
|
+
});
|
|
573
|
+
await createUserUow.executeMutations();
|
|
574
574
|
|
|
575
575
|
// Create a session
|
|
576
576
|
const [[user]] = await queryEngine.createUnitOfWork("get-user").find("user").executeRetrieve();
|
|
577
577
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
.
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
.executeMutations();
|
|
578
|
+
const createSessionUow = queryEngine.createUnitOfWork("create-session");
|
|
579
|
+
createSessionUow.create("session", {
|
|
580
|
+
userId: user.id,
|
|
581
|
+
expiresAt: new Date("2025-12-31"),
|
|
582
|
+
});
|
|
583
|
+
await createSessionUow.executeMutations();
|
|
585
584
|
|
|
586
585
|
// Get session
|
|
587
586
|
const [[session]] = await queryEngine
|
|
@@ -609,4 +608,125 @@ describe("DrizzleAdapter PGLite", () => {
|
|
|
609
608
|
|
|
610
609
|
await cleanup();
|
|
611
610
|
});
|
|
611
|
+
|
|
612
|
+
it("should handle timestamps and timezones correctly", async () => {
|
|
613
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
614
|
+
|
|
615
|
+
// Create a user
|
|
616
|
+
const createUserUow = queryEngine.createUnitOfWork("create-user-for-timestamp");
|
|
617
|
+
createUserUow.create("users", { name: "Timestamp User", age: 28 });
|
|
618
|
+
await createUserUow.executeMutations();
|
|
619
|
+
|
|
620
|
+
const [[user]] = await queryEngine
|
|
621
|
+
.createUnitOfWork("get-user-for-timestamp")
|
|
622
|
+
.find("users", (b) => b.whereIndex("name_idx", (eb) => eb("name", "=", "Timestamp User")))
|
|
623
|
+
.executeRetrieve();
|
|
624
|
+
|
|
625
|
+
// Create a post with database-generated timestamp (defaultTo(b => b.now()))
|
|
626
|
+
const createPostUow = queryEngine.createUnitOfWork("create-post-with-timestamp");
|
|
627
|
+
createPostUow.create("posts", {
|
|
628
|
+
user_id: user.id,
|
|
629
|
+
title: "Timestamp Test Post",
|
|
630
|
+
content: "Testing timestamp handling",
|
|
631
|
+
});
|
|
632
|
+
await createPostUow.executeMutations();
|
|
633
|
+
|
|
634
|
+
const createdPostIds = createPostUow.getCreatedIds();
|
|
635
|
+
expect(createdPostIds).toHaveLength(1);
|
|
636
|
+
const postId = createdPostIds[0];
|
|
637
|
+
|
|
638
|
+
// Retrieve the specific post we just created by its ID
|
|
639
|
+
const [[post]] = await queryEngine
|
|
640
|
+
.createUnitOfWork("get-post-with-timestamp")
|
|
641
|
+
.find("posts", (b) => b.whereIndex("primary", (eb) => eb("id", "=", postId)))
|
|
642
|
+
.executeRetrieve();
|
|
643
|
+
|
|
644
|
+
// Verify created_at is a Date
|
|
645
|
+
expect(post.created_at).toBeInstanceOf(Date);
|
|
646
|
+
|
|
647
|
+
// Verify the timestamp is a valid date (not too far in the past or future)
|
|
648
|
+
const now = Date.now();
|
|
649
|
+
const createdTime = post.created_at.getTime();
|
|
650
|
+
expect(createdTime).toBeGreaterThan(now - 24 * 60 * 60 * 1000); // Within last 24 hours
|
|
651
|
+
expect(createdTime).toBeLessThan(now + 24 * 60 * 60 * 1000); // Not more than 24 hours in future
|
|
652
|
+
|
|
653
|
+
// Verify we can compare timestamps
|
|
654
|
+
expect(post.created_at.getTime()).toBeGreaterThan(0);
|
|
655
|
+
|
|
656
|
+
// Test that the Date object has the correct methods
|
|
657
|
+
expect(typeof post.created_at.toISOString).toBe("function");
|
|
658
|
+
expect(typeof post.created_at.getTime).toBe("function");
|
|
659
|
+
expect(typeof post.created_at.getTimezoneOffset).toBe("function");
|
|
660
|
+
|
|
661
|
+
// Verify the date can be serialized and deserialized
|
|
662
|
+
const isoString = post.created_at.toISOString();
|
|
663
|
+
expect(typeof isoString).toBe("string");
|
|
664
|
+
expect(new Date(isoString).getTime()).toBe(post.created_at.getTime());
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
it("should create user and post in same transaction using returned ID", async () => {
|
|
668
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
669
|
+
const queries: DrizzleCompiledQuery[] = [];
|
|
670
|
+
|
|
671
|
+
// Create UOW and create both user and post in same transaction
|
|
672
|
+
const uow = queryEngine.createUnitOfWork("create-user-and-post", {
|
|
673
|
+
onQuery: (query) => queries.push(query),
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
// Create user and capture the returned ID
|
|
677
|
+
const userId = uow.create("users", {
|
|
678
|
+
name: "UOW Test User",
|
|
679
|
+
age: 35,
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
// Use the returned FragnoId directly to create a post in the same transaction
|
|
683
|
+
// The compiler will extract externalId and generate a subquery to lookup the internal ID
|
|
684
|
+
const postId = uow.create("posts", {
|
|
685
|
+
user_id: userId,
|
|
686
|
+
title: "UOW Test Post",
|
|
687
|
+
content: "This post was created in the same transaction as the user",
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
// Execute all mutations in a single transaction
|
|
691
|
+
const { success } = await uow.executeMutations();
|
|
692
|
+
expect(success).toBe(true);
|
|
693
|
+
|
|
694
|
+
// Verify both records were created
|
|
695
|
+
const userIdStr = userId.toString();
|
|
696
|
+
const postIdStr = postId.toString();
|
|
697
|
+
|
|
698
|
+
const [[user]] = await queryEngine
|
|
699
|
+
.createUnitOfWork("verify-user")
|
|
700
|
+
.find("users", (b) => b.whereIndex("primary", (eb) => eb("id", "=", userIdStr)))
|
|
701
|
+
.executeRetrieve();
|
|
702
|
+
|
|
703
|
+
expect(user.name).toBe("UOW Test User");
|
|
704
|
+
expect(user.age).toBe(35);
|
|
705
|
+
|
|
706
|
+
const [[post]] = await queryEngine
|
|
707
|
+
.createUnitOfWork("verify-post")
|
|
708
|
+
.find("posts", (b) => b.whereIndex("primary", (eb) => eb("id", "=", postIdStr)))
|
|
709
|
+
.executeRetrieve();
|
|
710
|
+
|
|
711
|
+
expect(post.title).toBe("UOW Test Post");
|
|
712
|
+
expect(post.content).toBe("This post was created in the same transaction as the user");
|
|
713
|
+
|
|
714
|
+
// Verify the foreign key relationship is correct
|
|
715
|
+
expect(post.user_id.internalId).toBe(user.id.internalId);
|
|
716
|
+
|
|
717
|
+
const [insertUserQuery, insertPostQuery] = queries;
|
|
718
|
+
expect(insertUserQuery.sql).toMatchInlineSnapshot(
|
|
719
|
+
`"insert into "users_namespace" ("id", "name", "age", "_internalId", "_version") values ($1, $2, $3, default, default)"`,
|
|
720
|
+
);
|
|
721
|
+
expect(insertUserQuery.params).toEqual([userId.externalId, "UOW Test User", 35]);
|
|
722
|
+
expect(insertPostQuery.sql).toMatchInlineSnapshot(
|
|
723
|
+
`"insert into "posts_namespace" ("id", "user_id", "title", "content", "created_at", "_internalId", "_version") values ($1, (select "_internalId" from "users_namespace" where "id" = $2 limit 1), $3, $4, default, default, default)"`,
|
|
724
|
+
);
|
|
725
|
+
expect(insertPostQuery.params).toEqual([
|
|
726
|
+
postId.externalId,
|
|
727
|
+
userId.externalId,
|
|
728
|
+
"UOW Test Post",
|
|
729
|
+
"This post was created in the same transaction as the user",
|
|
730
|
+
]);
|
|
731
|
+
});
|
|
612
732
|
});
|
|
@@ -118,16 +118,15 @@ describe("DrizzleAdapter SQLite", () => {
|
|
|
118
118
|
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
119
119
|
|
|
120
120
|
// Create two users at once using UOW
|
|
121
|
-
const createUow = queryEngine
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
});
|
|
121
|
+
const createUow = queryEngine.createUnitOfWork("create-users");
|
|
122
|
+
createUow.create("users", {
|
|
123
|
+
name: "Alice",
|
|
124
|
+
age: 25,
|
|
125
|
+
});
|
|
126
|
+
createUow.create("users", {
|
|
127
|
+
name: "Bob",
|
|
128
|
+
age: 30,
|
|
129
|
+
});
|
|
131
130
|
|
|
132
131
|
expectTypeOf(createUow.find).parameter(0).toEqualTypeOf<keyof typeof testSchema.tables>();
|
|
133
132
|
|
|
@@ -232,12 +231,11 @@ describe("DrizzleAdapter SQLite", () => {
|
|
|
232
231
|
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
233
232
|
|
|
234
233
|
// Create some users
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
.executeMutations();
|
|
234
|
+
const createUow = queryEngine.createUnitOfWork("create-users");
|
|
235
|
+
createUow.create("users", { name: "User1", age: 20 });
|
|
236
|
+
createUow.create("users", { name: "User2", age: 30 });
|
|
237
|
+
createUow.create("users", { name: "User3", age: 40 });
|
|
238
|
+
await createUow.executeMutations();
|
|
241
239
|
|
|
242
240
|
// Count all users
|
|
243
241
|
const [totalCount] = await queryEngine
|
|
@@ -253,13 +251,12 @@ describe("DrizzleAdapter SQLite", () => {
|
|
|
253
251
|
it("should support cursor-based pagination", async () => {
|
|
254
252
|
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
255
253
|
|
|
256
|
-
const createUow = queryEngine
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
.create("users", { name: "Page User E", age: 60 });
|
|
254
|
+
const createUow = queryEngine.createUnitOfWork("create-users");
|
|
255
|
+
createUow.create("users", { name: "Page User A", age: 20 });
|
|
256
|
+
createUow.create("users", { name: "Page User B", age: 30 });
|
|
257
|
+
createUow.create("users", { name: "Page User C", age: 40 });
|
|
258
|
+
createUow.create("users", { name: "Page User D", age: 50 });
|
|
259
|
+
createUow.create("users", { name: "Page User E", age: 60 });
|
|
263
260
|
|
|
264
261
|
await createUow.executeMutations();
|
|
265
262
|
|
|
@@ -302,9 +299,8 @@ describe("DrizzleAdapter SQLite", () => {
|
|
|
302
299
|
it("should support joins", async () => {
|
|
303
300
|
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
304
301
|
|
|
305
|
-
const createUow = queryEngine
|
|
306
|
-
|
|
307
|
-
.create("users", { name: "Email User", age: 20 });
|
|
302
|
+
const createUow = queryEngine.createUnitOfWork("create-users");
|
|
303
|
+
createUow.create("users", { name: "Email User", age: 20 });
|
|
308
304
|
|
|
309
305
|
const { success } = await createUow.executeMutations();
|
|
310
306
|
expect(success).toBe(true);
|
|
@@ -321,7 +317,8 @@ describe("DrizzleAdapter SQLite", () => {
|
|
|
321
317
|
expect(createdUser.name).toBe("Email User");
|
|
322
318
|
|
|
323
319
|
// Create an email for testing joins
|
|
324
|
-
const createEmailUow = queryEngine.createUnitOfWork("create-test-email")
|
|
320
|
+
const createEmailUow = queryEngine.createUnitOfWork("create-test-email");
|
|
321
|
+
createEmailUow.create("emails", {
|
|
325
322
|
user_id: createdUser.id,
|
|
326
323
|
email: "test@example.com",
|
|
327
324
|
is_primary: true,
|
|
@@ -364,9 +361,8 @@ describe("DrizzleAdapter SQLite", () => {
|
|
|
364
361
|
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
365
362
|
|
|
366
363
|
// Create a user (author)
|
|
367
|
-
const createAuthorUow = queryEngine
|
|
368
|
-
|
|
369
|
-
.create("users", { name: "Blog Author", age: 30 });
|
|
364
|
+
const createAuthorUow = queryEngine.createUnitOfWork("create-author");
|
|
365
|
+
createAuthorUow.create("users", { name: "Blog Author", age: 30 });
|
|
370
366
|
await createAuthorUow.executeMutations();
|
|
371
367
|
|
|
372
368
|
// Fetch the created author to get the proper ID
|
|
@@ -376,7 +372,8 @@ describe("DrizzleAdapter SQLite", () => {
|
|
|
376
372
|
.executeRetrieve();
|
|
377
373
|
|
|
378
374
|
// Create a post by the author
|
|
379
|
-
const createPostUow = queryEngine.createUnitOfWork("create-post")
|
|
375
|
+
const createPostUow = queryEngine.createUnitOfWork("create-post");
|
|
376
|
+
createPostUow.create("posts", {
|
|
380
377
|
user_id: author.id,
|
|
381
378
|
title: "My First Post",
|
|
382
379
|
content: "This is the content of my first post",
|
|
@@ -390,9 +387,8 @@ describe("DrizzleAdapter SQLite", () => {
|
|
|
390
387
|
.executeRetrieve();
|
|
391
388
|
|
|
392
389
|
// Create a commenter
|
|
393
|
-
const createCommenterUow = queryEngine
|
|
394
|
-
|
|
395
|
-
.create("users", { name: "Commenter User", age: 25 });
|
|
390
|
+
const createCommenterUow = queryEngine.createUnitOfWork("create-commenter");
|
|
391
|
+
createCommenterUow.create("users", { name: "Commenter User", age: 25 });
|
|
396
392
|
await createCommenterUow.executeMutations();
|
|
397
393
|
|
|
398
394
|
// Fetch the created commenter to get the proper ID
|
|
@@ -402,7 +398,8 @@ describe("DrizzleAdapter SQLite", () => {
|
|
|
402
398
|
.executeRetrieve();
|
|
403
399
|
|
|
404
400
|
// Create a comment on the post
|
|
405
|
-
const createCommentUow = queryEngine.createUnitOfWork("create-comment")
|
|
401
|
+
const createCommentUow = queryEngine.createUnitOfWork("create-comment");
|
|
402
|
+
createCommentUow.create("comments", {
|
|
406
403
|
post_id: post.id,
|
|
407
404
|
user_id: commenter.id,
|
|
408
405
|
text: "Great post!",
|
|
@@ -580,4 +577,61 @@ describe("DrizzleAdapter SQLite", () => {
|
|
|
580
577
|
age: 60,
|
|
581
578
|
});
|
|
582
579
|
});
|
|
580
|
+
|
|
581
|
+
it("should handle timestamps and timezones correctly", async () => {
|
|
582
|
+
const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
|
|
583
|
+
|
|
584
|
+
// Create a user
|
|
585
|
+
const createUserUow = queryEngine.createUnitOfWork("create-user-for-timestamp");
|
|
586
|
+
createUserUow.create("users", { name: "Timestamp User", age: 28 });
|
|
587
|
+
await createUserUow.executeMutations();
|
|
588
|
+
|
|
589
|
+
const [[user]] = await queryEngine
|
|
590
|
+
.createUnitOfWork("get-user-for-timestamp")
|
|
591
|
+
.find("users", (b) => b.whereIndex("name_idx", (eb) => eb("name", "=", "Timestamp User")))
|
|
592
|
+
.executeRetrieve();
|
|
593
|
+
|
|
594
|
+
// Create a post (note: SQLite schema doesn't have created_at column)
|
|
595
|
+
const createPostUow = queryEngine.createUnitOfWork("create-post-for-timestamp");
|
|
596
|
+
createPostUow.create("posts", {
|
|
597
|
+
user_id: user.id,
|
|
598
|
+
title: "Timestamp Test Post",
|
|
599
|
+
content: "Testing timestamp handling",
|
|
600
|
+
});
|
|
601
|
+
await createPostUow.executeMutations();
|
|
602
|
+
|
|
603
|
+
// Retrieve the post
|
|
604
|
+
const [[post]] = await queryEngine
|
|
605
|
+
.createUnitOfWork("get-post-for-timestamp")
|
|
606
|
+
.find("posts", (b) => b.whereIndex("posts_user_idx", (eb) => eb("user_id", "=", user.id)))
|
|
607
|
+
.executeRetrieve();
|
|
608
|
+
|
|
609
|
+
expect(post).toBeDefined();
|
|
610
|
+
expect(post.title).toBe("Timestamp Test Post");
|
|
611
|
+
|
|
612
|
+
// Test general Date handling (SQLite stores timestamps as integers)
|
|
613
|
+
const now = new Date();
|
|
614
|
+
expect(now).toBeInstanceOf(Date);
|
|
615
|
+
expect(typeof now.getTime).toBe("function");
|
|
616
|
+
expect(typeof now.toISOString).toBe("function");
|
|
617
|
+
|
|
618
|
+
// Verify date serialization/deserialization works
|
|
619
|
+
const isoString = now.toISOString();
|
|
620
|
+
expect(typeof isoString).toBe("string");
|
|
621
|
+
expect(new Date(isoString).getTime()).toBe(now.getTime());
|
|
622
|
+
|
|
623
|
+
// Test timezone preservation
|
|
624
|
+
const specificDate = new Date("2024-06-15T14:30:00Z");
|
|
625
|
+
expect(specificDate.toISOString()).toBe("2024-06-15T14:30:00.000Z");
|
|
626
|
+
|
|
627
|
+
// Verify SQLite numeric timestamp conversion
|
|
628
|
+
const timestamp = Date.now();
|
|
629
|
+
const dateFromTimestamp = new Date(timestamp);
|
|
630
|
+
expect(dateFromTimestamp.getTime()).toBe(timestamp);
|
|
631
|
+
|
|
632
|
+
// Verify that dates from different timezones are handled correctly
|
|
633
|
+
const localDate = new Date("2024-06-15T14:30:00");
|
|
634
|
+
expect(localDate).toBeInstanceOf(Date);
|
|
635
|
+
expect(typeof localDate.getTimezoneOffset()).toBe("number");
|
|
636
|
+
});
|
|
583
637
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { drizzle } from "drizzle-orm/pglite";
|
|
2
|
-
import { beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
3
|
-
import { column, idColumn, referenceColumn, schema } from "../../schema/create";
|
|
2
|
+
import { beforeAll, beforeEach, describe, expect, expectTypeOf, it } from "vitest";
|
|
3
|
+
import { column, FragnoId, idColumn, referenceColumn, schema } from "../../schema/create";
|
|
4
4
|
import type { DBType } from "./shared";
|
|
5
5
|
import { writeAndLoadSchema } from "./test-utils";
|
|
6
6
|
import { fromDrizzle } from "./drizzle-query";
|
|
@@ -134,10 +134,14 @@ describe("drizzle-query", () => {
|
|
|
134
134
|
it("should find with select subset of columns", async () => {
|
|
135
135
|
const someExternalId = "some-external-id";
|
|
136
136
|
|
|
137
|
-
await orm.findFirst("user", (b) =>
|
|
137
|
+
const res = await orm.findFirst("user", (b) =>
|
|
138
138
|
b.whereIndex("primary", (eb) => eb("id", "=", someExternalId)).select(["id", "email"]),
|
|
139
139
|
);
|
|
140
140
|
|
|
141
|
+
if (res) {
|
|
142
|
+
expectTypeOf(res.email).toEqualTypeOf<string>();
|
|
143
|
+
}
|
|
144
|
+
|
|
141
145
|
const [query] = queries;
|
|
142
146
|
expect(query.sql).toMatchInlineSnapshot(
|
|
143
147
|
`"select "id", "email", "_internalId", "_version" from "user" "user" where "user"."id" = $1 limit $2"`,
|
|
@@ -178,7 +182,7 @@ describe("drizzle-query", () => {
|
|
|
178
182
|
|
|
179
183
|
const [query] = queries;
|
|
180
184
|
expect(query.sql).toMatchInlineSnapshot(
|
|
181
|
-
`"select "id", "userId", "expiresAt", "createdAt", "_internalId", "_version" from "session" "session" where "session"."userId" = $1"`,
|
|
185
|
+
`"select "id", "userId", "expiresAt", "createdAt", "_internalId", "_version" from "session" "session" where "session"."userId" = (select "_internalId" from "user" where "id" = $1 limit 1)"`,
|
|
182
186
|
);
|
|
183
187
|
expect(query.params).toEqual([userId]);
|
|
184
188
|
});
|
|
@@ -194,7 +198,7 @@ describe("drizzle-query", () => {
|
|
|
194
198
|
});
|
|
195
199
|
|
|
196
200
|
it("should find with select subset", async () => {
|
|
197
|
-
await orm.find("user", (b) => b.whereIndex("primary").select(["id", "email"]));
|
|
201
|
+
const _res = await orm.find("user", (b) => b.whereIndex("primary").select(["id", "email"]));
|
|
198
202
|
|
|
199
203
|
const [query] = queries;
|
|
200
204
|
expect(query.sql).toMatchInlineSnapshot(
|
|
@@ -331,6 +335,33 @@ describe("drizzle-query", () => {
|
|
|
331
335
|
);
|
|
332
336
|
expect(query.params).toEqual([newExpiresAt.toISOString(), sessionId]);
|
|
333
337
|
});
|
|
338
|
+
|
|
339
|
+
it("should update with version check using FragnoId", async () => {
|
|
340
|
+
const userId = FragnoId.fromExternal("user-123", 5);
|
|
341
|
+
|
|
342
|
+
await orm.update("user", userId, (b) =>
|
|
343
|
+
b
|
|
344
|
+
.set({
|
|
345
|
+
email: "checked@example.com",
|
|
346
|
+
})
|
|
347
|
+
.check(),
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
// Verify the SQL query includes version check in WHERE clause
|
|
351
|
+
const [query] = queries;
|
|
352
|
+
expect(query.sql).toMatchInlineSnapshot(
|
|
353
|
+
`"update "user" set "email" = $1, "_version" = COALESCE(_version, 0) + 1 where ("user"."id" = $2 and "user"."_version" = $3)"`,
|
|
354
|
+
);
|
|
355
|
+
expect(query.params).toEqual(["checked@example.com", "user-123", 5]);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
it("should throw when trying to check() with string ID", async () => {
|
|
359
|
+
await expect(
|
|
360
|
+
orm.update("user", "user-123", (b) => b.set({ email: "test@example.com" }).check()),
|
|
361
|
+
).rejects.toThrow(
|
|
362
|
+
'Cannot use check() with a string ID on table "user". Version checking requires a FragnoId with version information.',
|
|
363
|
+
);
|
|
364
|
+
});
|
|
334
365
|
});
|
|
335
366
|
|
|
336
367
|
describe("updateMany", () => {
|
|
@@ -378,6 +409,25 @@ describe("drizzle-query", () => {
|
|
|
378
409
|
expect(query.sql).toMatchInlineSnapshot(`"delete from "session" where "session"."id" = $1"`);
|
|
379
410
|
expect(query.params).toEqual([sessionId]);
|
|
380
411
|
});
|
|
412
|
+
|
|
413
|
+
it("should delete with version check using FragnoId", async () => {
|
|
414
|
+
const userId = FragnoId.fromExternal("user-789", 3);
|
|
415
|
+
|
|
416
|
+
await orm.delete("user", userId, (b) => b.check());
|
|
417
|
+
|
|
418
|
+
// Verify the SQL query includes version check in WHERE clause
|
|
419
|
+
const [query] = queries;
|
|
420
|
+
expect(query.sql).toMatchInlineSnapshot(
|
|
421
|
+
`"delete from "user" where ("user"."id" = $1 and "user"."_version" = $2)"`,
|
|
422
|
+
);
|
|
423
|
+
expect(query.params).toEqual(["user-789", 3]);
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
it("should throw when trying to check() with string ID on delete", async () => {
|
|
427
|
+
await expect(orm.delete("user", "user-123", (b) => b.check())).rejects.toThrow(
|
|
428
|
+
'Cannot use check() with a string ID on table "user". Version checking requires a FragnoId with version information.',
|
|
429
|
+
);
|
|
430
|
+
});
|
|
381
431
|
});
|
|
382
432
|
|
|
383
433
|
describe("deleteMany", () => {
|
|
@@ -394,7 +444,7 @@ describe("drizzle-query", () => {
|
|
|
394
444
|
// Verify the find query that's executed first
|
|
395
445
|
const findQuery = queries[0];
|
|
396
446
|
expect(findQuery.sql).toMatchInlineSnapshot(
|
|
397
|
-
`"select "id", "userId", "expiresAt", "createdAt", "_internalId", "_version" from "session" "session" where "session"."userId" = $1"`,
|
|
447
|
+
`"select "id", "userId", "expiresAt", "createdAt", "_internalId", "_version" from "session" "session" where "session"."userId" = (select "_internalId" from "user" where "id" = $1 limit 1)"`,
|
|
398
448
|
);
|
|
399
449
|
expect(findQuery.params).toEqual([userId]);
|
|
400
450
|
|