@rebasepro/server-postgresql 0.0.1-canary.4d4fb3e → 0.0.1-canary.ca2cb6e

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.
Files changed (136) hide show
  1. package/dist/common/src/collections/CollectionRegistry.d.ts +8 -0
  2. package/dist/common/src/util/entities.d.ts +22 -0
  3. package/dist/common/src/util/relations.d.ts +14 -4
  4. package/dist/common/src/util/resolutions.d.ts +1 -1
  5. package/dist/index.es.js +1254 -591
  6. package/dist/index.es.js.map +1 -1
  7. package/dist/index.umd.js +1254 -591
  8. package/dist/index.umd.js.map +1 -1
  9. package/dist/server-postgresql/src/PostgresBackendDriver.d.ts +17 -29
  10. package/dist/server-postgresql/src/auth/services.d.ts +7 -3
  11. package/dist/server-postgresql/src/collections/PostgresCollectionRegistry.d.ts +1 -1
  12. package/dist/server-postgresql/src/connection.d.ts +34 -1
  13. package/dist/server-postgresql/src/data-transformer.d.ts +26 -4
  14. package/dist/server-postgresql/src/databasePoolManager.d.ts +2 -2
  15. package/dist/server-postgresql/src/schema/auth-schema.d.ts +139 -38
  16. package/dist/server-postgresql/src/schema/doctor-cli.d.ts +2 -0
  17. package/dist/server-postgresql/src/schema/doctor.d.ts +43 -0
  18. package/dist/server-postgresql/src/schema/generate-drizzle-schema-logic.d.ts +1 -1
  19. package/dist/server-postgresql/src/schema/test-schema.d.ts +24 -0
  20. package/dist/server-postgresql/src/services/EntityFetchService.d.ts +22 -8
  21. package/dist/server-postgresql/src/services/EntityPersistService.d.ts +1 -1
  22. package/dist/server-postgresql/src/services/RelationService.d.ts +11 -5
  23. package/dist/server-postgresql/src/services/entity-helpers.d.ts +16 -2
  24. package/dist/server-postgresql/src/services/entityService.d.ts +8 -6
  25. package/dist/server-postgresql/src/services/realtimeService.d.ts +2 -0
  26. package/dist/server-postgresql/src/utils/drizzle-conditions.d.ts +2 -2
  27. package/dist/types/src/controllers/auth.d.ts +2 -0
  28. package/dist/types/src/controllers/client.d.ts +119 -7
  29. package/dist/types/src/controllers/collection_registry.d.ts +4 -3
  30. package/dist/types/src/controllers/customization_controller.d.ts +7 -1
  31. package/dist/types/src/controllers/data.d.ts +34 -7
  32. package/dist/types/src/controllers/data_driver.d.ts +20 -28
  33. package/dist/types/src/controllers/database_admin.d.ts +2 -2
  34. package/dist/types/src/controllers/email.d.ts +34 -0
  35. package/dist/types/src/controllers/index.d.ts +1 -0
  36. package/dist/types/src/controllers/local_config_persistence.d.ts +4 -4
  37. package/dist/types/src/controllers/navigation.d.ts +5 -5
  38. package/dist/types/src/controllers/registry.d.ts +6 -3
  39. package/dist/types/src/controllers/side_entity_controller.d.ts +7 -6
  40. package/dist/types/src/controllers/storage.d.ts +24 -26
  41. package/dist/types/src/rebase_context.d.ts +8 -4
  42. package/dist/types/src/types/backend.d.ts +4 -1
  43. package/dist/types/src/types/builders.d.ts +5 -4
  44. package/dist/types/src/types/chips.d.ts +1 -1
  45. package/dist/types/src/types/collections.d.ts +169 -125
  46. package/dist/types/src/types/cron.d.ts +102 -0
  47. package/dist/types/src/types/data_source.d.ts +1 -1
  48. package/dist/types/src/types/entity_actions.d.ts +8 -8
  49. package/dist/types/src/types/entity_callbacks.d.ts +15 -15
  50. package/dist/types/src/types/entity_link_builder.d.ts +1 -1
  51. package/dist/types/src/types/entity_overrides.d.ts +2 -1
  52. package/dist/types/src/types/entity_views.d.ts +8 -8
  53. package/dist/types/src/types/export_import.d.ts +3 -3
  54. package/dist/types/src/types/index.d.ts +1 -0
  55. package/dist/types/src/types/plugins.d.ts +72 -18
  56. package/dist/types/src/types/properties.d.ts +118 -33
  57. package/dist/types/src/types/relations.d.ts +1 -1
  58. package/dist/types/src/types/slots.d.ts +30 -6
  59. package/dist/types/src/types/translations.d.ts +44 -0
  60. package/dist/types/src/types/user_management_delegate.d.ts +1 -0
  61. package/drizzle-test/0000_woozy_junta.sql +6 -0
  62. package/drizzle-test/0001_youthful_arachne.sql +1 -0
  63. package/drizzle-test/0002_lively_dragon_lord.sql +2 -0
  64. package/drizzle-test/0003_mean_king_cobra.sql +2 -0
  65. package/drizzle-test/meta/0000_snapshot.json +47 -0
  66. package/drizzle-test/meta/0001_snapshot.json +48 -0
  67. package/drizzle-test/meta/0002_snapshot.json +38 -0
  68. package/drizzle-test/meta/0003_snapshot.json +48 -0
  69. package/drizzle-test/meta/_journal.json +34 -0
  70. package/drizzle-test-out/0000_tan_trauma.sql +6 -0
  71. package/drizzle-test-out/0001_rapid_drax.sql +1 -0
  72. package/drizzle-test-out/meta/0000_snapshot.json +44 -0
  73. package/drizzle-test-out/meta/0001_snapshot.json +54 -0
  74. package/drizzle-test-out/meta/_journal.json +20 -0
  75. package/drizzle.test.config.ts +10 -0
  76. package/package.json +88 -89
  77. package/scratch.ts +41 -0
  78. package/src/PostgresBackendDriver.ts +63 -79
  79. package/src/PostgresBootstrapper.ts +7 -8
  80. package/src/auth/ensure-tables.ts +158 -86
  81. package/src/auth/services.ts +109 -50
  82. package/src/cli.ts +259 -16
  83. package/src/collections/PostgresCollectionRegistry.ts +6 -6
  84. package/src/connection.ts +70 -48
  85. package/src/data-transformer.ts +155 -116
  86. package/src/databasePoolManager.ts +6 -5
  87. package/src/history/HistoryService.ts +3 -12
  88. package/src/interfaces.ts +3 -3
  89. package/src/schema/auth-schema.ts +26 -3
  90. package/src/schema/doctor-cli.ts +47 -0
  91. package/src/schema/doctor.ts +595 -0
  92. package/src/schema/generate-drizzle-schema-logic.ts +204 -57
  93. package/src/schema/generate-drizzle-schema.ts +6 -6
  94. package/src/schema/test-schema.ts +11 -0
  95. package/src/services/BranchService.ts +5 -5
  96. package/src/services/EntityFetchService.ts +317 -188
  97. package/src/services/EntityPersistService.ts +15 -17
  98. package/src/services/RelationService.ts +299 -37
  99. package/src/services/entity-helpers.ts +39 -13
  100. package/src/services/entityService.ts +11 -9
  101. package/src/services/realtimeService.ts +58 -29
  102. package/src/utils/drizzle-conditions.ts +25 -24
  103. package/src/websocket.ts +52 -21
  104. package/test/auth-services.test.ts +131 -39
  105. package/test/batch-many-to-many-regression.test.ts +573 -0
  106. package/test/branchService.test.ts +22 -12
  107. package/test/data-transformer-hardening.test.ts +417 -0
  108. package/test/data-transformer.test.ts +175 -0
  109. package/test/doctor.test.ts +182 -0
  110. package/test/entityService.errors.test.ts +31 -16
  111. package/test/entityService.relations.test.ts +155 -59
  112. package/test/entityService.subcollection-search.test.ts +107 -57
  113. package/test/entityService.test.ts +105 -47
  114. package/test/generate-drizzle-schema.test.ts +262 -69
  115. package/test/historyService.test.ts +31 -16
  116. package/test/n-plus-one-regression.test.ts +314 -0
  117. package/test/postgresDataDriver.test.ts +260 -168
  118. package/test/realtimeService.test.ts +70 -39
  119. package/test/relation-pipeline-gaps.test.ts +637 -0
  120. package/test/relations.test.ts +492 -39
  121. package/test-drizzle-bug.ts +18 -0
  122. package/test-drizzle-out/0000_cultured_freak.sql +7 -0
  123. package/test-drizzle-out/0001_tiresome_professor_monster.sql +1 -0
  124. package/test-drizzle-out/meta/0000_snapshot.json +55 -0
  125. package/test-drizzle-out/meta/0001_snapshot.json +63 -0
  126. package/test-drizzle-out/meta/_journal.json +20 -0
  127. package/test-drizzle-prompt.sh +2 -0
  128. package/test-policy-prompt.sh +3 -0
  129. package/test-programmatic.ts +30 -0
  130. package/test-programmatic2.ts +59 -0
  131. package/test-schema-no-policies.ts +12 -0
  132. package/test_drizzle_mock.js +2 -2
  133. package/test_find_changed.mjs +3 -1
  134. package/test_hash.js +14 -0
  135. package/tsconfig.json +1 -1
  136. package/vite.config.ts +5 -5
@@ -4,8 +4,10 @@ import { users, refreshTokens, passwordResetTokens, User } from "../src/schema/a
4
4
 
5
5
  // Mock the drizzle-orm functions
6
6
  jest.mock("drizzle-orm", () => ({
7
- eq: jest.fn((field, value) => ({ field, value, type: "eq" })),
8
- sql: jest.fn((strings: TemplateStringsArray, ...values: any[]) => ({
7
+ eq: jest.fn((field, value) => ({ field,
8
+ value,
9
+ type: "eq" })),
10
+ sql: jest.fn((strings: TemplateStringsArray, ...values: unknown[]) => ({
9
11
  strings,
10
12
  values,
11
13
  type: "sql"
@@ -14,7 +16,7 @@ jest.mock("drizzle-orm", () => ({
14
16
  }));
15
17
 
16
18
  describe("Auth Services", () => {
17
- let db: jest.Mocked<NodePgDatabase<any>>;
19
+ let db: jest.Mocked<NodePgDatabase<Record<string, unknown>>>;
18
20
  let mockInsertValues: jest.Mock;
19
21
  let mockInsertReturning: jest.Mock;
20
22
  let mockSelectFrom: jest.Mock;
@@ -28,9 +30,10 @@ describe("Auth Services", () => {
28
30
  beforeEach(() => {
29
31
  // Create chainable mocks
30
32
  mockInsertReturning = jest.fn().mockResolvedValue([]);
31
- mockInsertValues = jest.fn().mockReturnValue({
33
+ mockInsertValues = jest.fn().mockReturnValue({
32
34
  returning: mockInsertReturning,
33
- onConflictDoUpdate: jest.fn().mockReturnValue({ returning: mockInsertReturning })
35
+ onConflictDoUpdate: jest.fn().mockReturnValue({ returning: mockInsertReturning }),
36
+ onConflictDoNothing: jest.fn().mockReturnValue({ returning: mockInsertReturning })
34
37
  });
35
38
 
36
39
  mockSelectWhere = jest.fn().mockResolvedValue([]);
@@ -52,7 +55,7 @@ describe("Auth Services", () => {
52
55
  update: jest.fn().mockReturnValue({ set: mockUpdateSet }),
53
56
  delete: jest.fn().mockReturnValue({ where: mockDeleteWhere }),
54
57
  execute: mockExecute
55
- } as any;
58
+ } as unknown as jest.Mocked<NodePgDatabase<Record<string, unknown>>>;
56
59
  });
57
60
 
58
61
  describe("UserService", () => {
@@ -66,10 +69,12 @@ describe("Auth Services", () => {
66
69
  it("should create a user and return it", async () => {
67
70
  const newUser = {
68
71
  email: "test@example.com",
69
- displayName: "Test User",
70
- provider: "email"
72
+ displayName: "Test User"
71
73
  };
72
- const createdUser = { id: "user-123", ...newUser, createdAt: new Date(), updatedAt: new Date() };
74
+ const createdUser = { id: "user-123",
75
+ ...newUser,
76
+ createdAt: new Date(),
77
+ updatedAt: new Date() };
73
78
  mockInsertReturning.mockResolvedValueOnce([createdUser]);
74
79
 
75
80
  const result = await userService.createUser(newUser);
@@ -82,7 +87,8 @@ describe("Auth Services", () => {
82
87
 
83
88
  describe("getUserById", () => {
84
89
  it("should return user when found", async () => {
85
- const mockUser = { id: "user-123", email: "test@example.com" };
90
+ const mockUser = { id: "user-123",
91
+ email: "test@example.com" };
86
92
  mockSelectWhere.mockResolvedValueOnce([mockUser]);
87
93
 
88
94
  const result = await userService.getUserById("user-123");
@@ -102,7 +108,8 @@ describe("Auth Services", () => {
102
108
 
103
109
  describe("getUserByEmail", () => {
104
110
  it("should return user when found by email", async () => {
105
- const mockUser = { id: "user-123", email: "test@example.com" };
111
+ const mockUser = { id: "user-123",
112
+ email: "test@example.com" };
106
113
  mockSelectWhere.mockResolvedValueOnce([mockUser]);
107
114
 
108
115
  const result = await userService.getUserByEmail("test@example.com");
@@ -120,20 +127,40 @@ describe("Auth Services", () => {
120
127
  });
121
128
  });
122
129
 
123
- describe("getUserByGoogleId", () => {
124
- it("should return user when found by Google ID", async () => {
125
- const mockUser = { id: "user-123", googleId: "google-abc" };
126
- mockSelectWhere.mockResolvedValueOnce([mockUser]);
130
+ describe("getUserByIdentity", () => {
131
+ it("should execute sql for identity lookup", async () => {
132
+ const mockUser = { id: "user-123" };
133
+ // execute mock instead of select
134
+ mockExecute.mockResolvedValueOnce({ rows: [mockUser] });
127
135
 
128
- const result = await userService.getUserByGoogleId("google-abc");
136
+ const result = await userService.getUserByIdentity("google", "google-abc");
129
137
 
130
- expect(result).toEqual(mockUser);
138
+ expect(mockExecute).toHaveBeenCalled();
139
+ });
140
+ });
141
+
142
+ describe("getUserIdentities", () => {
143
+ it("should fetch user identities", async () => {
144
+ mockExecute.mockResolvedValueOnce({ rows: [] });
145
+
146
+ const result = await userService.getUserIdentities("user-123");
147
+
148
+ expect(mockExecute).toHaveBeenCalled();
149
+ });
150
+ });
151
+
152
+ describe("linkUserIdentity", () => {
153
+ it("should insert user identity", async () => {
154
+ await userService.linkUserIdentity("user-123", "google", "123", { email: "test@test.com" });
155
+ expect(db.insert).toHaveBeenCalled();
131
156
  });
132
157
  });
133
158
 
134
159
  describe("updateUser", () => {
135
160
  it("should update user and return updated record", async () => {
136
- const updatedUser = { id: "user-123", email: "test@example.com", displayName: "Updated Name" };
161
+ const updatedUser = { id: "user-123",
162
+ email: "test@example.com",
163
+ displayName: "Updated Name" };
137
164
  mockUpdateReturning.mockResolvedValueOnce([updatedUser]);
138
165
 
139
166
  const result = await userService.updateUser("user-123", { displayName: "Updated Name" });
@@ -167,8 +194,10 @@ describe("Auth Services", () => {
167
194
  describe("listUsers", () => {
168
195
  it("should return all users", async () => {
169
196
  const mockUsers = [
170
- { id: "user-1", email: "user1@example.com" },
171
- { id: "user-2", email: "user2@example.com" }
197
+ { id: "user-1",
198
+ email: "user1@example.com" },
199
+ { id: "user-2",
200
+ email: "user2@example.com" }
172
201
  ];
173
202
  mockSelectFrom.mockReturnValueOnce(Promise.resolve(mockUsers));
174
203
 
@@ -235,7 +264,8 @@ describe("Auth Services", () => {
235
264
 
236
265
  describe("getUserByVerificationToken", () => {
237
266
  it("should find user by verification token", async () => {
238
- const mockUser = { id: "user-123", email: "test@example.com" };
267
+ const mockUser = { id: "user-123",
268
+ email: "test@example.com" };
239
269
  mockSelectWhere.mockResolvedValueOnce([mockUser]);
240
270
 
241
271
  const result = await userService.getUserByVerificationToken("token-abc");
@@ -248,8 +278,18 @@ describe("Auth Services", () => {
248
278
  it("should return roles for user", async () => {
249
279
  mockExecute.mockResolvedValueOnce({
250
280
  rows: [
251
- { id: "admin", name: "Admin", is_admin: true, default_permissions: null, collection_permissions: null, config: null },
252
- { id: "editor", name: "Editor", is_admin: false, default_permissions: { edit: true }, collection_permissions: null, config: null }
281
+ { id: "admin",
282
+ name: "Admin",
283
+ is_admin: true,
284
+ default_permissions: null,
285
+ collection_permissions: null,
286
+ config: null },
287
+ { id: "editor",
288
+ name: "Editor",
289
+ is_admin: false,
290
+ default_permissions: { edit: true },
291
+ collection_permissions: null,
292
+ config: null }
253
293
  ]
254
294
  });
255
295
 
@@ -271,7 +311,12 @@ describe("Auth Services", () => {
271
311
  it("should return role IDs for user", async () => {
272
312
  mockExecute.mockResolvedValueOnce({
273
313
  rows: [
274
- { id: "admin", name: "Admin", is_admin: true, default_permissions: null, collection_permissions: null, config: null }
314
+ { id: "admin",
315
+ name: "Admin",
316
+ is_admin: true,
317
+ default_permissions: null,
318
+ collection_permissions: null,
319
+ config: null }
275
320
  ]
276
321
  });
277
322
 
@@ -308,17 +353,28 @@ describe("Auth Services", () => {
308
353
 
309
354
  describe("getUserWithRoles", () => {
310
355
  it("should return user with roles", async () => {
311
- const mockUser = { id: "user-123", email: "test@example.com" };
356
+ const mockUser = { id: "user-123",
357
+ email: "test@example.com" };
312
358
  mockSelectWhere.mockResolvedValueOnce([mockUser]);
313
359
  mockExecute.mockResolvedValueOnce({
314
- rows: [{ id: "admin", name: "Admin", is_admin: true, default_permissions: null, collection_permissions: null, config: null }]
360
+ rows: [{ id: "admin",
361
+ name: "Admin",
362
+ is_admin: true,
363
+ default_permissions: null,
364
+ collection_permissions: null,
365
+ config: null }]
315
366
  });
316
367
 
317
368
  const result = await userService.getUserWithRoles("user-123");
318
369
 
319
370
  expect(result).toEqual({
320
371
  user: mockUser,
321
- roles: [{ id: "admin", name: "Admin", isAdmin: true, defaultPermissions: null, collectionPermissions: null, config: null }]
372
+ roles: [{ id: "admin",
373
+ name: "Admin",
374
+ isAdmin: true,
375
+ defaultPermissions: null,
376
+ collectionPermissions: null,
377
+ config: null }]
322
378
  });
323
379
  });
324
380
 
@@ -342,7 +398,12 @@ describe("Auth Services", () => {
342
398
  describe("getRoleById", () => {
343
399
  it("should return role when found", async () => {
344
400
  mockExecute.mockResolvedValueOnce({
345
- rows: [{ id: "admin", name: "Admin", is_admin: true, default_permissions: null, collection_permissions: null, config: null }]
401
+ rows: [{ id: "admin",
402
+ name: "Admin",
403
+ is_admin: true,
404
+ default_permissions: null,
405
+ collection_permissions: null,
406
+ config: null }]
346
407
  });
347
408
 
348
409
  const result = await roleService.getRoleById("admin");
@@ -370,8 +431,18 @@ describe("Auth Services", () => {
370
431
  it("should return all roles", async () => {
371
432
  mockExecute.mockResolvedValueOnce({
372
433
  rows: [
373
- { id: "admin", name: "Admin", is_admin: true, default_permissions: null, collection_permissions: null, config: null },
374
- { id: "editor", name: "Editor", is_admin: false, default_permissions: null, collection_permissions: null, config: null }
434
+ { id: "admin",
435
+ name: "Admin",
436
+ is_admin: true,
437
+ default_permissions: null,
438
+ collection_permissions: null,
439
+ config: null },
440
+ { id: "editor",
441
+ name: "Editor",
442
+ is_admin: false,
443
+ default_permissions: null,
444
+ collection_permissions: null,
445
+ config: null }
375
446
  ]
376
447
  });
377
448
 
@@ -384,7 +455,12 @@ describe("Auth Services", () => {
384
455
  describe("createRole", () => {
385
456
  it("should create a role", async () => {
386
457
  mockExecute.mockResolvedValueOnce({
387
- rows: [{ id: "custom", name: "Custom Role", is_admin: false, default_permissions: null, collection_permissions: null, config: null }]
458
+ rows: [{ id: "custom",
459
+ name: "Custom Role",
460
+ is_admin: false,
461
+ default_permissions: null,
462
+ collection_permissions: null,
463
+ config: null }]
388
464
  });
389
465
 
390
466
  const role = await roleService.createRole({
@@ -402,9 +478,19 @@ describe("Auth Services", () => {
402
478
  describe("updateRole", () => {
403
479
  it("should update a role", async () => {
404
480
  mockExecute
405
- .mockResolvedValueOnce({ rows: [{ id: "admin", name: "Admin", is_admin: true, default_permissions: null, collection_permissions: null, config: null }] })
481
+ .mockResolvedValueOnce({ rows: [{ id: "admin",
482
+ name: "Admin",
483
+ is_admin: true,
484
+ default_permissions: null,
485
+ collection_permissions: null,
486
+ config: null }] })
406
487
  .mockResolvedValueOnce({ rows: [] })
407
- .mockResolvedValueOnce({ rows: [{ id: "admin", name: "Super Admin", is_admin: true, default_permissions: null, collection_permissions: null, config: null }] });
488
+ .mockResolvedValueOnce({ rows: [{ id: "admin",
489
+ name: "Super Admin",
490
+ is_admin: true,
491
+ default_permissions: null,
492
+ collection_permissions: null,
493
+ config: null }] });
408
494
 
409
495
  const result = await roleService.updateRole("admin", { name: "Super Admin" });
410
496
 
@@ -456,11 +542,13 @@ describe("Auth Services", () => {
456
542
  describe("findByHash", () => {
457
543
  it("should find token by hash", async () => {
458
544
  const expiresAt = new Date();
459
- mockSelectWhere.mockResolvedValueOnce([{ userId: "user-123", expiresAt }]);
545
+ mockSelectWhere.mockResolvedValueOnce([{ userId: "user-123",
546
+ expiresAt }]);
460
547
 
461
548
  const result = await refreshTokenService.findByHash("token-hash");
462
549
 
463
- expect(result).toEqual({ userId: "user-123", expiresAt });
550
+ expect(result).toEqual({ userId: "user-123",
551
+ expiresAt });
464
552
  });
465
553
 
466
554
  it("should return null when token not found", async () => {
@@ -512,14 +600,17 @@ describe("Auth Services", () => {
512
600
  describe("findValidByHash", () => {
513
601
  it("should find valid token", async () => {
514
602
  const expiresAt = new Date(Date.now() + 60 * 60 * 1000);
515
- mockSelectWhere.mockResolvedValueOnce([{ userId: "user-123", expiresAt }]);
603
+ mockSelectWhere.mockResolvedValueOnce([{ userId: "user-123",
604
+ expiresAt }]);
516
605
  mockExecute.mockResolvedValueOnce({
517
- rows: [{ user_id: "user-123", expires_at: expiresAt }]
606
+ rows: [{ user_id: "user-123",
607
+ expires_at: expiresAt }]
518
608
  });
519
609
 
520
610
  const result = await passwordResetTokenService.findValidByHash("token-hash");
521
611
 
522
- expect(result).toEqual({ userId: "user-123", expiresAt });
612
+ expect(result).toEqual({ userId: "user-123",
613
+ expiresAt });
523
614
  });
524
615
 
525
616
  it("should return null when token not found", async () => {
@@ -532,7 +623,8 @@ describe("Auth Services", () => {
532
623
 
533
624
  it("should return null when token is expired or used", async () => {
534
625
  const expiresAt = new Date(Date.now() + 60 * 60 * 1000);
535
- mockSelectWhere.mockResolvedValueOnce([{ userId: "user-123", expiresAt }]);
626
+ mockSelectWhere.mockResolvedValueOnce([{ userId: "user-123",
627
+ expiresAt }]);
536
628
  mockExecute.mockResolvedValueOnce({ rows: [] }); // No valid token found
537
629
 
538
630
  const result = await passwordResetTokenService.findValidByHash("token-hash");