on-zero 0.6.11 → 0.6.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.
Files changed (131) hide show
  1. package/dist/cjs/createZeroSQLiteServer.cjs +319 -0
  2. package/dist/cjs/createZeroSQLiteServer.native.js +440 -0
  3. package/dist/cjs/createZeroSQLiteServer.native.js.map +1 -0
  4. package/dist/cjs/createZeroSQLiteServer.test.cjs +429 -0
  5. package/dist/cjs/createZeroSQLiteServer.test.native.js +466 -0
  6. package/dist/cjs/createZeroSQLiteServer.test.native.js.map +1 -0
  7. package/dist/cjs/createZeroServer.cjs +20 -234
  8. package/dist/cjs/createZeroServer.native.js +24 -250
  9. package/dist/cjs/createZeroServer.native.js.map +1 -1
  10. package/dist/cjs/createZeroServer.test.cjs +35 -1
  11. package/dist/cjs/createZeroServer.test.native.js +38 -1
  12. package/dist/cjs/createZeroServer.test.native.js.map +1 -1
  13. package/dist/cjs/createZeroServerCore.cjs +362 -0
  14. package/dist/cjs/createZeroServerCore.native.js +389 -0
  15. package/dist/cjs/createZeroServerCore.native.js.map +1 -0
  16. package/dist/cjs/generate-lite.cjs +1 -0
  17. package/dist/cjs/generate-lite.native.js +1 -0
  18. package/dist/cjs/generate-lite.native.js.map +1 -1
  19. package/dist/cjs/generate-lite.test.cjs +5 -1
  20. package/dist/cjs/generate-lite.test.native.js +5 -1
  21. package/dist/cjs/generate-lite.test.native.js.map +1 -1
  22. package/dist/cjs/generate.cjs +20 -11
  23. package/dist/cjs/generate.native.js +29 -21
  24. package/dist/cjs/generate.native.js.map +1 -1
  25. package/dist/cjs/generate.test.cjs +110 -0
  26. package/dist/cjs/generate.test.native.js +110 -0
  27. package/dist/cjs/generate.test.native.js.map +1 -1
  28. package/dist/cjs/httpPull/transport.test.cjs +44 -0
  29. package/dist/cjs/httpPull/transport.test.native.js +53 -0
  30. package/dist/cjs/httpPull/transport.test.native.js.map +1 -1
  31. package/dist/cjs/httpPullTransport.cjs +4 -1
  32. package/dist/cjs/httpPullTransport.native.js +5 -2
  33. package/dist/cjs/httpPullTransport.native.js.map +1 -1
  34. package/dist/cjs/server.cjs +2 -1
  35. package/dist/cjs/syncHost.cjs +164 -0
  36. package/dist/cjs/syncHost.native.js +371 -0
  37. package/dist/cjs/syncHost.native.js.map +1 -0
  38. package/dist/cjs/syncHost.test.cjs +229 -0
  39. package/dist/cjs/syncHost.test.native.js +278 -0
  40. package/dist/cjs/syncHost.test.native.js.map +1 -0
  41. package/dist/esm/createZeroSQLiteServer.mjs +293 -0
  42. package/dist/esm/createZeroSQLiteServer.mjs.map +1 -0
  43. package/dist/esm/createZeroSQLiteServer.native.js +411 -0
  44. package/dist/esm/createZeroSQLiteServer.native.js.map +1 -0
  45. package/dist/esm/createZeroSQLiteServer.test.mjs +430 -0
  46. package/dist/esm/createZeroSQLiteServer.test.mjs.map +1 -0
  47. package/dist/esm/createZeroSQLiteServer.test.native.js +464 -0
  48. package/dist/esm/createZeroSQLiteServer.test.native.js.map +1 -0
  49. package/dist/esm/createZeroServer.mjs +17 -231
  50. package/dist/esm/createZeroServer.mjs.map +1 -1
  51. package/dist/esm/createZeroServer.native.js +21 -247
  52. package/dist/esm/createZeroServer.native.js.map +1 -1
  53. package/dist/esm/createZeroServer.test.mjs +35 -1
  54. package/dist/esm/createZeroServer.test.mjs.map +1 -1
  55. package/dist/esm/createZeroServer.test.native.js +38 -1
  56. package/dist/esm/createZeroServer.test.native.js.map +1 -1
  57. package/dist/esm/createZeroServerCore.mjs +336 -0
  58. package/dist/esm/createZeroServerCore.mjs.map +1 -0
  59. package/dist/esm/createZeroServerCore.native.js +360 -0
  60. package/dist/esm/createZeroServerCore.native.js.map +1 -0
  61. package/dist/esm/generate-lite.mjs +1 -0
  62. package/dist/esm/generate-lite.mjs.map +1 -1
  63. package/dist/esm/generate-lite.native.js +1 -0
  64. package/dist/esm/generate-lite.native.js.map +1 -1
  65. package/dist/esm/generate-lite.test.mjs +5 -1
  66. package/dist/esm/generate-lite.test.mjs.map +1 -1
  67. package/dist/esm/generate-lite.test.native.js +5 -1
  68. package/dist/esm/generate-lite.test.native.js.map +1 -1
  69. package/dist/esm/generate.mjs +20 -11
  70. package/dist/esm/generate.mjs.map +1 -1
  71. package/dist/esm/generate.native.js +29 -21
  72. package/dist/esm/generate.native.js.map +1 -1
  73. package/dist/esm/generate.test.mjs +87 -1
  74. package/dist/esm/generate.test.mjs.map +1 -1
  75. package/dist/esm/generate.test.native.js +87 -1
  76. package/dist/esm/generate.test.native.js.map +1 -1
  77. package/dist/esm/httpPull/transport.test.mjs +44 -0
  78. package/dist/esm/httpPull/transport.test.mjs.map +1 -1
  79. package/dist/esm/httpPull/transport.test.native.js +53 -0
  80. package/dist/esm/httpPull/transport.test.native.js.map +1 -1
  81. package/dist/esm/httpPullTransport.mjs +4 -1
  82. package/dist/esm/httpPullTransport.mjs.map +1 -1
  83. package/dist/esm/httpPullTransport.native.js +5 -2
  84. package/dist/esm/httpPullTransport.native.js.map +1 -1
  85. package/dist/esm/server.mjs +1 -0
  86. package/dist/esm/server.mjs.map +1 -1
  87. package/dist/esm/syncHost.mjs +138 -0
  88. package/dist/esm/syncHost.mjs.map +1 -0
  89. package/dist/esm/syncHost.native.js +342 -0
  90. package/dist/esm/syncHost.native.js.map +1 -0
  91. package/dist/esm/syncHost.test.mjs +206 -0
  92. package/dist/esm/syncHost.test.mjs.map +1 -0
  93. package/dist/esm/syncHost.test.native.js +252 -0
  94. package/dist/esm/syncHost.test.native.js.map +1 -0
  95. package/package.json +19 -3
  96. package/readme.md +26 -0
  97. package/src/createZeroSQLiteServer.test.ts +461 -0
  98. package/src/createZeroSQLiteServer.ts +523 -0
  99. package/src/createZeroServer.test.ts +39 -1
  100. package/src/createZeroServer.ts +35 -428
  101. package/src/createZeroServerCore.ts +557 -0
  102. package/src/generate-lite.test.ts +4 -0
  103. package/src/generate-lite.ts +1 -0
  104. package/src/generate.test.ts +87 -1
  105. package/src/generate.ts +29 -20
  106. package/src/httpPull/transport.test.ts +47 -0
  107. package/src/httpPullTransport.ts +15 -1
  108. package/src/server.ts +1 -0
  109. package/src/syncHost.test.ts +216 -0
  110. package/src/syncHost.ts +255 -0
  111. package/test-fixtures/server-return-types.ts +50 -0
  112. package/test-fixtures/tsconfig.json +13 -0
  113. package/types/createZeroSQLiteServer.d.ts +37 -0
  114. package/types/createZeroSQLiteServer.d.ts.map +1 -0
  115. package/types/createZeroSQLiteServer.test.d.ts +2 -0
  116. package/types/createZeroSQLiteServer.test.d.ts.map +1 -0
  117. package/types/createZeroServer.d.ts +11 -293
  118. package/types/createZeroServer.d.ts.map +1 -1
  119. package/types/createZeroServerCore.d.ts +310 -0
  120. package/types/createZeroServerCore.d.ts.map +1 -0
  121. package/types/generate-lite.d.ts.map +1 -1
  122. package/types/generate.d.ts +2 -1
  123. package/types/generate.d.ts.map +1 -1
  124. package/types/httpPullTransport.d.ts +2 -0
  125. package/types/httpPullTransport.d.ts.map +1 -1
  126. package/types/server.d.ts +1 -0
  127. package/types/server.d.ts.map +1 -1
  128. package/types/syncHost.d.ts +51 -0
  129. package/types/syncHost.d.ts.map +1 -0
  130. package/types/syncHost.test.d.ts +2 -0
  131. package/types/syncHost.test.d.ts.map +1 -0
@@ -0,0 +1,461 @@
1
+ import { DatabaseSync, type SQLInputValue } from 'node:sqlite'
2
+
3
+ import { boolean, createSchema, json, relationships, string, table } from '@rocicorp/zero'
4
+ import { asQueryInternals } from '@rocicorp/zero/bindings'
5
+ import { afterEach, beforeEach, describe, expect, test } from 'vitest'
6
+
7
+ import { createZeroSQLiteServer } from './createZeroSQLiteServer'
8
+ import { zeroHttpFixtureSchema } from './httpPull/fixtureSchema'
9
+
10
+ import type {
11
+ SQLiteTransactionExecutor,
12
+ SQLiteTransactionProvider,
13
+ } from './createZeroSQLiteServer'
14
+
15
+ const note = table('note')
16
+ .from('note_records')
17
+ .columns({
18
+ id: string().from('note_id'),
19
+ done: boolean(),
20
+ data: json<{ label: string }>().optional(),
21
+ })
22
+ .primaryKey('id')
23
+
24
+ const sqliteTypesSchema = createSchema({
25
+ tables: [note],
26
+ relationships: [],
27
+ enableLegacyQueries: true,
28
+ })
29
+
30
+ const person = table('person').columns({ id: string() }).primaryKey('id')
31
+ const membership = table('membership')
32
+ .columns({ personId: string(), teamId: string() })
33
+ .primaryKey('personId', 'teamId')
34
+ const team = table('team')
35
+ .from('team_records')
36
+ .columns({
37
+ id: string().from('team_id'),
38
+ active: boolean().from('is_active'),
39
+ metadata: json<{ label: string }>().from('metadata_json'),
40
+ })
41
+ .primaryKey('id')
42
+ const twoHopSchema = createSchema({
43
+ tables: [person, membership, team],
44
+ relationships: [
45
+ relationships(person, ({ many }) => ({
46
+ teams: many(
47
+ {
48
+ sourceField: ['id'],
49
+ destSchema: membership,
50
+ destField: ['personId'],
51
+ },
52
+ {
53
+ sourceField: ['teamId'],
54
+ destSchema: team,
55
+ destField: ['id'],
56
+ },
57
+ ),
58
+ })),
59
+ ],
60
+ })
61
+
62
+ function pushRequest(id: number, name: string, args: unknown) {
63
+ return new Request('http://localhost/push?schema=zero_0&appID=zero', {
64
+ method: 'POST',
65
+ headers: { 'content-type': 'application/json' },
66
+ body: JSON.stringify({
67
+ clientGroupID: 'cg1',
68
+ mutations: [
69
+ {
70
+ type: 'custom',
71
+ id,
72
+ clientID: 'client-a',
73
+ name,
74
+ args: [args],
75
+ timestamp: 1,
76
+ },
77
+ ],
78
+ pushVersion: 1,
79
+ timestamp: 1,
80
+ requestID: 'req-' + id,
81
+ }),
82
+ })
83
+ }
84
+
85
+ function cleanupRequest() {
86
+ return new Request('http://localhost/push?schema=zero_0&appID=zero', {
87
+ method: 'POST',
88
+ headers: { 'content-type': 'application/json' },
89
+ body: JSON.stringify({
90
+ clientGroupID: 'cg1',
91
+ mutations: [
92
+ {
93
+ type: 'custom',
94
+ id: 2,
95
+ clientID: 'client-a',
96
+ name: '_zero_cleanupResults',
97
+ args: [
98
+ {
99
+ clientGroupID: 'cg1',
100
+ clientID: 'client-a',
101
+ upToMutationID: 1,
102
+ },
103
+ ],
104
+ timestamp: 1,
105
+ },
106
+ ],
107
+ pushVersion: 1,
108
+ timestamp: 1,
109
+ requestID: 'cleanup',
110
+ }),
111
+ })
112
+ }
113
+
114
+ describe('createZeroSQLiteServer', () => {
115
+ let db: DatabaseSync
116
+ let retryNextTransaction: boolean
117
+ let transactionProvider: SQLiteTransactionProvider
118
+
119
+ beforeEach(() => {
120
+ db = new DatabaseSync(':memory:')
121
+ db.exec(
122
+ [
123
+ 'CREATE TABLE project (id TEXT PRIMARY KEY, ownerId TEXT NOT NULL, name TEXT NOT NULL);',
124
+ 'CREATE TABLE note_records (note_id TEXT PRIMARY KEY, done INTEGER NOT NULL, data TEXT);',
125
+ ].join('\n'),
126
+ )
127
+
128
+ retryNextTransaction = false
129
+ const executor: SQLiteTransactionExecutor = {
130
+ async exec(sql, params = []) {
131
+ db.prepare(sql).run(...(params as SQLInputValue[]))
132
+ },
133
+ async query<Row extends Record<string, unknown>>(sql, params = []) {
134
+ return db.prepare(sql).all(...(params as SQLInputValue[])) as Row[]
135
+ },
136
+ async queryAst<Result>(ast, format) {
137
+ let rows: Record<string, unknown>[]
138
+ if (ast.table === 'project') {
139
+ rows = db.prepare('SELECT * FROM project ORDER BY id').all()
140
+ } else if (ast.table === 'note') {
141
+ rows = db
142
+ .prepare('SELECT note_id, done, data FROM note_records ORDER BY note_id')
143
+ .all()
144
+ .map((row) => ({
145
+ id: row.note_id,
146
+ done: row.done !== 0,
147
+ data: typeof row.data === 'string' ? JSON.parse(row.data) : row.data,
148
+ }))
149
+ } else if (ast.table === 'person') {
150
+ rows = [
151
+ {
152
+ id: 'p1',
153
+ teams: [
154
+ {
155
+ id: 't1',
156
+ active: true,
157
+ metadata: { label: 'core' },
158
+ },
159
+ ],
160
+ },
161
+ ]
162
+ } else {
163
+ throw new Error('unexpected table ' + ast.table)
164
+ }
165
+ return (format.singular ? rows[0] : rows) as Result
166
+ },
167
+ }
168
+ const run = async <R>(callback: (tx: SQLiteTransactionExecutor) => Promise<R>) => {
169
+ db.exec('BEGIN')
170
+ try {
171
+ const value = await callback(executor)
172
+ db.exec('COMMIT')
173
+ return value
174
+ } catch (error) {
175
+ db.exec('ROLLBACK')
176
+ throw error
177
+ }
178
+ }
179
+ transactionProvider = async <R>(
180
+ callback: (tx: SQLiteTransactionExecutor) => Promise<R>,
181
+ ) => {
182
+ if (retryNextTransaction) {
183
+ retryNextTransaction = false
184
+ db.exec('BEGIN')
185
+ await callback(executor)
186
+ db.exec('ROLLBACK')
187
+ }
188
+ return run(callback)
189
+ }
190
+ })
191
+
192
+ afterEach(() => {
193
+ db.close()
194
+ })
195
+
196
+ test('commits CRUD, the mutation ledger, and one retry-safe async task', async () => {
197
+ let effectRuns = 0
198
+ const server = createZeroSQLiteServer({
199
+ schema: zeroHttpFixtureSchema,
200
+ models: {
201
+ project: {
202
+ mutate: {
203
+ create: async ({ tx, server }, value: Record<string, unknown>) => {
204
+ await tx.mutate.project.insert(value as never)
205
+ server?.asyncTasks.push(async () => {})
206
+ expect(server?.asyncTasks).toHaveLength(1)
207
+ server?.asyncTasks.splice(0, 1, async () => {})
208
+ server!.asyncTasks[0] = async () => {
209
+ effectRuns++
210
+ }
211
+ },
212
+ },
213
+ },
214
+ },
215
+ createServerActions: () => ({}),
216
+ transactionProvider,
217
+ })
218
+
219
+ retryNextTransaction = true
220
+ const result = await server.handleMutationRequest({
221
+ authData: null,
222
+ request: pushRequest(1, 'project.create', {
223
+ id: 'p1',
224
+ ownerId: 'u1',
225
+ name: 'first',
226
+ }),
227
+ skipAsyncTasks: true,
228
+ })
229
+
230
+ expect((result.response as any).mutations[0].result).toEqual({})
231
+ expect(db.prepare('SELECT * FROM project').all()).toEqual([
232
+ { id: 'p1', ownerId: 'u1', name: 'first' },
233
+ ])
234
+ expect(db.prepare('SELECT * FROM zero_0_clients').all()).toEqual([
235
+ { clientGroupID: 'cg1', clientID: 'client-a', lastMutationID: 1 },
236
+ ])
237
+ expect(result.asyncTasks).toHaveLength(1)
238
+ await result.asyncTasks[0]!()
239
+ expect(effectRuns).toBe(1)
240
+ })
241
+
242
+ test('rolls back row writes and persists an application error with its LMID', async () => {
243
+ const server = createZeroSQLiteServer({
244
+ schema: zeroHttpFixtureSchema,
245
+ models: {
246
+ project: {
247
+ mutate: {
248
+ fail: async ({ tx }, value: Record<string, unknown>) => {
249
+ await tx.mutate.project.insert(value as never)
250
+ throw new Error('rejected')
251
+ },
252
+ },
253
+ },
254
+ },
255
+ createServerActions: () => ({}),
256
+ transactionProvider,
257
+ })
258
+
259
+ const result = await server.handleMutationRequest({
260
+ authData: null,
261
+ request: pushRequest(1, 'project.fail', {
262
+ id: 'p1',
263
+ ownerId: 'u1',
264
+ name: 'first',
265
+ }),
266
+ })
267
+
268
+ expect((result.response as any).mutations[0].result).toMatchObject({
269
+ error: 'app',
270
+ message: 'rejected',
271
+ })
272
+ expect(db.prepare('SELECT * FROM project').all()).toEqual([])
273
+ expect(db.prepare('SELECT lastMutationID FROM zero_0_clients').get()).toEqual({
274
+ lastMutationID: 1,
275
+ })
276
+ const stored = db.prepare('SELECT result FROM zero_0_mutations').get() as {
277
+ result: string
278
+ }
279
+ expect(JSON.parse(stored.result)).toMatchObject({
280
+ error: 'app',
281
+ message: 'rejected',
282
+ })
283
+ })
284
+
285
+ test('recovers duplicate results and deletes them through the cleanup mutation', async () => {
286
+ const server = createZeroSQLiteServer({
287
+ schema: zeroHttpFixtureSchema,
288
+ models: {
289
+ project: {
290
+ mutate: {
291
+ create: async ({ tx }, value: Record<string, unknown>) => {
292
+ await tx.mutate.project.insert(value as never)
293
+ },
294
+ },
295
+ },
296
+ },
297
+ createServerActions: () => ({}),
298
+ transactionProvider,
299
+ })
300
+ const value = {
301
+ id: 'p1',
302
+ ownerId: 'u1',
303
+ name: 'first',
304
+ }
305
+
306
+ await server.handleMutationRequest({
307
+ authData: null,
308
+ request: pushRequest(1, 'project.create', value),
309
+ })
310
+ const duplicate = await server.handleMutationRequest({
311
+ authData: null,
312
+ request: pushRequest(1, 'project.create', value),
313
+ })
314
+
315
+ expect((duplicate.response as any).mutations[0].result).toMatchObject({
316
+ error: 'alreadyProcessed',
317
+ })
318
+ expect(db.prepare('SELECT lastMutationID FROM zero_0_clients').get()).toEqual({
319
+ lastMutationID: 1,
320
+ })
321
+ expect(db.prepare('SELECT COUNT(*) AS count FROM zero_0_mutations').get()).toEqual({
322
+ count: 1,
323
+ })
324
+
325
+ await server.handleMutationRequest({
326
+ authData: null,
327
+ request: cleanupRequest(),
328
+ })
329
+ expect(db.prepare('SELECT COUNT(*) AS count FROM zero_0_mutations').get()).toEqual({
330
+ count: 0,
331
+ })
332
+ })
333
+
334
+ test('supports direct CRUD, server names, value codecs, and query AST execution', async () => {
335
+ const server = createZeroSQLiteServer({
336
+ schema: sqliteTypesSchema,
337
+ models: {
338
+ note: {
339
+ mutate: {
340
+ insert: async ({ tx }, value: Record<string, unknown>) => {
341
+ await tx.mutate.note.insert(value as never)
342
+ },
343
+ upsert: async ({ tx }, value: Record<string, unknown>) => {
344
+ await tx.mutate.note.upsert(value as never)
345
+ },
346
+ update: async ({ tx }, value: Record<string, unknown>) => {
347
+ await tx.mutate.note.update(value as never)
348
+ },
349
+ delete: async ({ tx }, value: Record<string, unknown>) => {
350
+ await tx.mutate.note.delete(value as never)
351
+ },
352
+ },
353
+ },
354
+ },
355
+ createServerActions: () => ({}),
356
+ transactionProvider,
357
+ })
358
+
359
+ await server.mutate.note.insert({
360
+ id: 'n1',
361
+ done: false,
362
+ data: { label: 'first' },
363
+ })
364
+ await server.mutate.note.upsert({
365
+ id: 'n1',
366
+ done: true,
367
+ data: { label: 'second' },
368
+ })
369
+ await server.mutate.note.update({
370
+ id: 'n1',
371
+ done: false,
372
+ })
373
+
374
+ expect(db.prepare('SELECT * FROM note_records').get()).toEqual({
375
+ note_id: 'n1',
376
+ done: 0,
377
+ data: JSON.stringify({ label: 'second' }),
378
+ })
379
+ await expect(server.query((q) => q.note)).resolves.toEqual([
380
+ {
381
+ id: 'n1',
382
+ done: false,
383
+ data: { label: 'second' },
384
+ },
385
+ ])
386
+ await server.transaction(async (tx) => {
387
+ if (tx.location !== 'server') throw new Error('expected server transaction')
388
+ await expect((tx.query as any).note.one().run()).resolves.toEqual({
389
+ id: 'n1',
390
+ done: false,
391
+ data: { label: 'second' },
392
+ })
393
+ await expect(
394
+ tx.dbTransaction.query('SELECT * FROM note_records', []),
395
+ ).resolves.toEqual([
396
+ {
397
+ note_id: 'n1',
398
+ done: 0,
399
+ data: JSON.stringify({ label: 'second' }),
400
+ },
401
+ ])
402
+ })
403
+
404
+ await server.mutate.note.delete({ id: 'n1' })
405
+ expect(db.prepare('SELECT * FROM note_records').all()).toEqual([])
406
+ })
407
+
408
+ test('decodes destination rows through a hidden two-hop junction', async () => {
409
+ const server = createZeroSQLiteServer({
410
+ schema: twoHopSchema,
411
+ models: {},
412
+ createServerActions: () => ({}),
413
+ transactionProvider,
414
+ })
415
+
416
+ await expect(server.query((q: any) => q.person.related('teams'))).resolves.toEqual([
417
+ {
418
+ id: 'p1',
419
+ teams: [
420
+ {
421
+ id: 't1',
422
+ active: true,
423
+ metadata: { label: 'core' },
424
+ },
425
+ ],
426
+ },
427
+ ])
428
+ })
429
+
430
+ test('accepts a hydrated singular query result from the transaction executor', async () => {
431
+ const hydrated = {
432
+ id: 'n1',
433
+ done: false,
434
+ data: { label: 'already decoded' },
435
+ }
436
+ let receivedQueryName: string | undefined
437
+ const hydratedExecutor = {
438
+ async exec() {},
439
+ async query() {
440
+ return []
441
+ },
442
+ async queryAst(_ast: unknown, _format: unknown, queryName?: string) {
443
+ receivedQueryName = queryName
444
+ return hydrated
445
+ },
446
+ } as unknown as SQLiteTransactionExecutor
447
+ const hydratedProvider: SQLiteTransactionProvider = async (callback) =>
448
+ callback(hydratedExecutor)
449
+ const server = createZeroSQLiteServer({
450
+ schema: sqliteTypesSchema,
451
+ models: {},
452
+ createServerActions: () => ({}),
453
+ transactionProvider: hydratedProvider,
454
+ })
455
+
456
+ await expect(
457
+ server.query((q) => asQueryInternals(q.note.one()).nameAndArgs('noteByID', ['n1'])),
458
+ ).resolves.toEqual(hydrated)
459
+ expect(receivedQueryName).toBe('noteByID')
460
+ })
461
+ })