on-zero 0.9.7 → 0.10.0

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 (49) hide show
  1. package/dist/cjs/generate-helpers.test.cjs +0 -38
  2. package/dist/cjs/generate-helpers.test.native.js +0 -38
  3. package/dist/cjs/generate-helpers.test.native.js.map +1 -1
  4. package/dist/cjs/generate-lite.test.cjs +0 -522
  5. package/dist/cjs/generate-lite.test.native.js +0 -525
  6. package/dist/cjs/generate-lite.test.native.js.map +1 -1
  7. package/dist/cjs/generate.test.cjs +0 -691
  8. package/dist/cjs/generate.test.native.js +0 -691
  9. package/dist/cjs/generate.test.native.js.map +1 -1
  10. package/dist/cjs/mutations.test.cjs +0 -11
  11. package/dist/cjs/mutations.test.native.js +0 -18
  12. package/dist/cjs/mutations.test.native.js.map +1 -1
  13. package/dist/cjs/server.test.cjs +0 -93
  14. package/dist/cjs/server.test.native.js +0 -101
  15. package/dist/cjs/server.test.native.js.map +1 -1
  16. package/dist/cjs/serverWhere.test.cjs +0 -45
  17. package/dist/cjs/serverWhere.test.native.js +0 -51
  18. package/dist/cjs/serverWhere.test.native.js.map +1 -1
  19. package/dist/esm/generate-helpers.test.mjs +1 -39
  20. package/dist/esm/generate-helpers.test.mjs.map +1 -1
  21. package/dist/esm/generate-helpers.test.native.js +1 -39
  22. package/dist/esm/generate-helpers.test.native.js.map +1 -1
  23. package/dist/esm/generate-lite.test.mjs +0 -522
  24. package/dist/esm/generate-lite.test.mjs.map +1 -1
  25. package/dist/esm/generate-lite.test.native.js +0 -525
  26. package/dist/esm/generate-lite.test.native.js.map +1 -1
  27. package/dist/esm/generate.test.mjs +2 -668
  28. package/dist/esm/generate.test.mjs.map +1 -1
  29. package/dist/esm/generate.test.native.js +2 -668
  30. package/dist/esm/generate.test.native.js.map +1 -1
  31. package/dist/esm/mutations.test.mjs +0 -11
  32. package/dist/esm/mutations.test.mjs.map +1 -1
  33. package/dist/esm/mutations.test.native.js +0 -18
  34. package/dist/esm/mutations.test.native.js.map +1 -1
  35. package/dist/esm/server.test.mjs +0 -93
  36. package/dist/esm/server.test.mjs.map +1 -1
  37. package/dist/esm/server.test.native.js +0 -101
  38. package/dist/esm/server.test.native.js.map +1 -1
  39. package/dist/esm/serverWhere.test.mjs +0 -45
  40. package/dist/esm/serverWhere.test.mjs.map +1 -1
  41. package/dist/esm/serverWhere.test.native.js +0 -51
  42. package/dist/esm/serverWhere.test.native.js.map +1 -1
  43. package/package.json +1 -1
  44. package/src/generate-helpers.test.ts +0 -40
  45. package/src/generate-lite.test.ts +0 -586
  46. package/src/generate.test.ts +0 -844
  47. package/src/mutations.test.ts +0 -21
  48. package/src/server.test.ts +0 -75
  49. package/src/serverWhere.test.ts +0 -42
@@ -1,10 +1,9 @@
1
- import { existsSync, mkdirSync, readFileSync, rmSync, symlinkSync, writeFileSync as writeFile } from "fs";
1
+ import { existsSync, mkdirSync, rmSync, writeFileSync as writeFile } from "fs";
2
2
  import { tmpdir } from "os";
3
3
  import { dirname, join } from "path";
4
4
  import { runInNewContext } from "vm";
5
- import * as zero from "@rocicorp/zero";
6
5
  import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
7
- import { deriveDataMembership, generate, generateDrizzleSchemaFile, generateDrizzleSchemaInputFile } from "./generate.native.js";
6
+ import { deriveDataMembership, generate, generateDrizzleSchemaInputFile } from "./generate.native.js";
8
7
  var testDir = join(tmpdir(), "on-zero-test-" + Date.now());
9
8
  function writeFileSync(path, content) {
10
9
  mkdirSync(dirname(path), {
@@ -25,105 +24,6 @@ afterEach(function () {
25
24
  });
26
25
  });
27
26
  describe("generate", function () {
28
- test("generates models.ts, types.ts, tables.ts from model files", async function () {
29
- writeFileSync(join(testDir, "post/mutations.ts"), `
30
- import { table, string, boolean } from 'on-zero'
31
-
32
- export const schema = table('post', {
33
- id: string(),
34
- title: string(),
35
- published: boolean(),
36
- })
37
- `);
38
- writeFileSync(join(testDir, "comment/mutations.ts"), `
39
- import { table, string } from 'on-zero'
40
-
41
- export const schema = table('comment', {
42
- id: string(),
43
- postId: string(),
44
- body: string(),
45
- })
46
- `);
47
- writeFileSync(join(testDir, "post/post.test.ts"), `throw new Error('test files must not be generated as models')`);
48
- writeFileSync(join(testDir, "comment/comment.spec.ts"), `throw new Error('spec files must not be generated as queries')`);
49
- var result = await generate({
50
- dir: testDir,
51
- silent: true
52
- });
53
- expect(result.modelCount).toBe(2);
54
- expect(result.schemaCount).toBe(2);
55
- expect(result.filesChanged).toBeGreaterThan(0);
56
- expect(existsSync(join(testDir, "generated/models.ts"))).toBe(true);
57
- expect(existsSync(join(testDir, "generated/types.ts"))).toBe(true);
58
- expect(existsSync(join(testDir, "generated/tables.ts"))).toBe(true);
59
- var modelsContent = readFileSync(join(testDir, "generated/models.ts"), "utf-8");
60
- expect(modelsContent).toContain("import * as comment from '../comment/mutations'");
61
- expect(modelsContent).toContain("import * as post from '../post/mutations'");
62
- expect(modelsContent).not.toContain("post.test");
63
- expect(modelsContent).toContain("export const models = {");
64
- var typesContent = readFileSync(join(testDir, "generated/types.ts"), "utf-8");
65
- expect(typesContent).toContain("export type Post = TableInsertRow<typeof schema.post>");
66
- expect(typesContent).toContain("export type Comment = TableInsertRow<typeof schema.comment>");
67
- var tablesContent = readFileSync(join(testDir, "generated/tables.ts"), "utf-8");
68
- expect(tablesContent).toContain("export { schema as post } from '../post/mutations'");
69
- expect(tablesContent).toContain("export { schema as comment } from '../comment/mutations'");
70
- });
71
- test("generates query validators from query files", async function () {
72
- writeFileSync(join(testDir, "post/mutations.ts"), `export const schema = table('post', { id: string() })`);
73
- writeFileSync(join(testDir, "post/queries.ts"), `
74
- import { zero } from '../zero'
75
-
76
- export const allPosts = () => zero.query.post
77
-
78
- export const postById = ({ id }: { id: string }) => zero.query.post.where('id', id)
79
-
80
- export const postsByAuthor = ({ authorId, limit }: { authorId: string; limit?: number }) =>
81
- zero.query.post.where('authorId', authorId).limit(limit ?? 10)
82
- `);
83
- var result = await generate({
84
- dir: testDir,
85
- silent: true
86
- });
87
- expect(result.queryCount).toBe(3);
88
- expect(existsSync(join(testDir, "generated/groupedQueries.ts"))).toBe(true);
89
- expect(existsSync(join(testDir, "generated/syncedQueries.ts"))).toBe(true);
90
- var groupedContent = readFileSync(join(testDir, "generated/groupedQueries.ts"), "utf-8");
91
- expect(groupedContent).toContain("import * as postSource from '../post/queries'");
92
- expect(groupedContent).toContain("postById: postSource.postById");
93
- var syncedContent = readFileSync(join(testDir, "generated/syncedQueries.ts"), "utf-8");
94
- expect(syncedContent).toContain("allPosts: defineQuery");
95
- expect(syncedContent).toContain("postById: defineQuery");
96
- expect(syncedContent).toContain("postsByAuthor: defineQuery");
97
- expect(syncedContent).toContain("v.object");
98
- });
99
- test("skips permission exports in queries", async function () {
100
- writeFileSync(join(testDir, "post/mutations.ts"), `export const schema = table('post', { id: string() })`);
101
- writeFileSync(join(testDir, "post/queries.ts"), `
102
- export const permission = () => ({ canRead: true })
103
- export const allPosts = () => zero.query.post
104
- `);
105
- var result = await generate({
106
- dir: testDir,
107
- silent: true
108
- });
109
- expect(result.queryCount).toBe(1);
110
- var syncedContent = readFileSync(join(testDir, "generated/syncedQueries.ts"), "utf-8");
111
- expect(syncedContent).toContain("allPosts");
112
- expect(syncedContent).not.toContain("permission:");
113
- });
114
- test("aliases user import without changing the model key", async function () {
115
- writeFileSync(join(testDir, "user/mutations.ts"), `export const schema = table('user', { id: string(), name: string() })`);
116
- await generate({
117
- dir: testDir,
118
- silent: true
119
- });
120
- var modelsContent = readFileSync(join(testDir, "generated/models.ts"), "utf-8");
121
- expect(modelsContent).toContain("import * as userPublic from '../user/mutations'");
122
- expect(modelsContent).toContain("user: userPublic,");
123
- expect(modelsContent).not.toContain("\n userPublic,");
124
- var typesContent = readFileSync(join(testDir, "generated/types.ts"), "utf-8");
125
- expect(typesContent).toContain("typeof schema.userPublic");
126
- });
127
27
  test("runs after command when files change", async function () {
128
28
  writeFileSync(join(testDir, "post/mutations.ts"), `export const schema = table('post', { id: string() })`);
129
29
  var markerFile = join(testDir, "after-ran");
@@ -169,51 +69,6 @@ describe("instance layout", function () {
169
69
  var dataDir = function dataDir2() {
170
70
  return join(testDir, "src/data");
171
71
  };
172
- test("discovers file and folder namespaces and emits related sync closure", async function () {
173
- writeFileSync(join(dataDir(), "control/reaction.ts"), `export const allReactions = () => zql.reaction`);
174
- writeFileSync(join(dataDir(), "on-zero.config.ts"), `export default defineConfig({ instances: { control: {}, project: { scope: 'projectId' } } })`);
175
- writeFileSync(join(dataDir(), "project/message/queries.ts"), `
176
- const unused = () => zql.message.related('notARealRelation')
177
- const withComments = () => zql.message.related('comments', (q) => q.related('author'))
178
- export const messages = () => withComments()
179
- `);
180
- writeFileSync(join(dataDir(), "project/message/mutations.ts"), `export const schema = table('message').columns({ id: string(), projectId: string() })`);
181
- writeFileSync(join(dataDir(), "project/message/helpers.ts"), `export const privateHelper = () => 'ignored'`);
182
- writeFileSync(join(testDir, "src/database/relations.ts"), `
183
- export const relations = defineRelations(schema, (r) => ({
184
- message: { comments: r.many.comment({}) },
185
- comment: { author: r.one.userPublic({}) },
186
- }))
187
- `);
188
- writeFileSync(join(testDir, "src/database/schema.ts"), `
189
- export const comment = sqliteTable('comment', { id: text(), projectId: text() })
190
- export const userPublic = sqliteTable('user_public', { id: text(), projectId: text() })
191
- `);
192
- var membership = await deriveDataMembership({
193
- dir: dataDir()
194
- });
195
- expect(membership.allTables).toEqual(["comment", "message", "reaction", "userPublic"]);
196
- expect(membership.instances.project).toEqual({
197
- tables: ["message"],
198
- syncTables: ["comment", "message", "userPublic"],
199
- supportTables: [],
200
- scope: "projectId"
201
- });
202
- await generate({
203
- dir: dataDir(),
204
- silent: true
205
- });
206
- var grouped = readFileSync(join(dataDir(), "generated/groupedQueries.ts"), "utf8");
207
- expect(grouped).toContain("from '../control/reaction'");
208
- expect(grouped).toContain("from '../project/message/queries'");
209
- expect(grouped).not.toContain("privateHelper");
210
- var manifest = readFileSync(join(dataDir(), "generated/instances.ts"), "utf8");
211
- expect(manifest).toContain("control: {");
212
- expect(manifest).toContain("project: {");
213
- expect(manifest).toContain('tables: ["message"]');
214
- expect(manifest).toContain('syncTables: ["comment","message","userPublic"]');
215
- expect(manifest).toContain(`defaultVisibility: (value: string) => ({ column: "projectId", value })`);
216
- });
217
72
  test("derives query membership from the returned query root", async function () {
218
73
  writeFileSync(join(dataDir(), "category/mutations.ts"), `export const schema = table('category').columns({ id: string() })`);
219
74
  writeFileSync(join(dataDir(), "dashboard/queries.ts"), `export const monthSummary = () => zql.category.related('expenses')`);
@@ -432,42 +287,6 @@ export const messages = () => zql.message.related('author')
432
287
  silent: true
433
288
  })).rejects.toThrow(/namespace 'message'.*instances 'control' and 'project'/);
434
289
  });
435
- test("resolves configured instance directories relative to the config file", async function () {
436
- writeFileSync(join(dataDir(), "on-zero.config.ts"), `export default defineConfig({ instances: { control: { dir: './planes/control-data', supportTables: ['audit'] }, project: { dir: '../project-data', scope: 'projectId' } } })`);
437
- writeFileSync(join(dataDir(), "planes/control-data/account.ts"), `export const accounts = () => zql.account`);
438
- writeFileSync(join(testDir, "src/project-data/message.ts"), `export const schema = table('message').columns({ id: string(), projectId: string() })`);
439
- await expect(deriveDataMembership({
440
- dir: dataDir(),
441
- config: join(dataDir(), "on-zero.config.ts")
442
- })).resolves.toEqual({
443
- instances: {
444
- control: {
445
- tables: ["account"],
446
- syncTables: ["account"],
447
- supportTables: ["audit"],
448
- scope: null
449
- },
450
- project: {
451
- tables: ["message"],
452
- syncTables: ["message"],
453
- supportTables: [],
454
- scope: "projectId"
455
- }
456
- },
457
- allTables: ["account", "audit", "message"]
458
- });
459
- await expect(generate({
460
- dir: dataDir(),
461
- config: join(dataDir(), "on-zero.config.ts"),
462
- silent: true
463
- })).resolves.toMatchObject({
464
- modelCount: 1,
465
- schemaCount: 1
466
- });
467
- var models = readFileSync(join(dataDir(), "generated/models.ts"), "utf8");
468
- expect(models).toContain("from '../../project-data/message'");
469
- expect(models).not.toContain("account");
470
- });
471
290
  test("rejects missing configured instance directories", async function () {
472
291
  writeFileSync(join(dataDir(), "on-zero.config.ts"), `export default defineConfig({ instances: { project: { dir: './missing' } } })`);
473
292
  await expect(deriveDataMembership({
@@ -512,253 +331,6 @@ export const messages = () => zql.message.related('author')
512
331
  });
513
332
  });
514
333
  describe("mutations", function () {
515
- test("generates validators for inline mutation param types", async function () {
516
- writeFileSync(join(testDir, "post/mutations.ts"), `
517
- import { table, string } from 'on-zero'
518
- import { mutations, serverWhere } from 'on-zero'
519
-
520
- export const schema = table('post').columns({
521
- id: string(),
522
- title: string(),
523
- }).primaryKey('id')
524
-
525
- const perm = serverWhere('post', () => true)
526
-
527
- export const mutate = mutations(schema, perm, {
528
- archive: async ({ tx }, { id, reason }: { id: string; reason: string }) => {
529
- await tx.mutate.post.update({ id, archived: true })
530
- },
531
- })
532
- `);
533
- var result = await generate({
534
- dir: testDir,
535
- silent: true
536
- });
537
- expect(result.mutationCount).toBeGreaterThan(0);
538
- expect(existsSync(join(testDir, "generated/syncedMutations.ts"))).toBe(true);
539
- var content = readFileSync(join(testDir, "generated/syncedMutations.ts"), "utf-8");
540
- expect(content).toContain("archive");
541
- expect(content).toContain("v.object");
542
- expect(content).toContain("v.string()");
543
- });
544
- test("generates CRUD validators from schema columns", async function () {
545
- writeFileSync(join(testDir, "task/mutations.ts"), `
546
- import { table, string, number, boolean } from 'on-zero'
547
- import { mutations, serverWhere } from 'on-zero'
548
-
549
- export const schema = table('task').columns({
550
- id: string(),
551
- title: string(),
552
- priority: number(),
553
- done: boolean(),
554
- note: string().optional(),
555
- }).primaryKey('id')
556
-
557
- const perm = serverWhere('task', () => true)
558
-
559
- export const mutate = mutations(schema, perm)
560
- `);
561
- var result = await generate({
562
- dir: testDir,
563
- silent: true
564
- });
565
- var content = readFileSync(join(testDir, "generated/syncedMutations.ts"), "utf-8");
566
- expect(content).toContain("insert:");
567
- expect(content).toMatch(/insert:.*v\.object/);
568
- expect(content).toContain("update:");
569
- expect(content).toContain("delete:");
570
- });
571
- test("treats models without export const mutate as empty mutations", async function () {
572
- writeFileSync(join(testDir, "readonly/mutations.ts"), `
573
- import { table, string } from 'on-zero'
574
-
575
- export const schema = table('readonly').columns({
576
- id: string(),
577
- name: string(),
578
- }).primaryKey('id')
579
- `);
580
- var result = await generate({
581
- dir: testDir,
582
- silent: true
583
- });
584
- expect(result.mutationCount).toBe(0);
585
- var content = readFileSync(join(testDir, "generated/syncedMutations.ts"), "utf-8");
586
- expect(content).toContain("readonly: {");
587
- });
588
- test("extracts custom mutations from bare mutations({})", async function () {
589
- writeFileSync(join(testDir, "admin/mutations.ts"), `
590
- import { mutations } from 'on-zero'
591
-
592
- export const mutate = mutations({
593
- reset: async ({ tx }, { targetId }: { targetId: string }) => {
594
- await tx.mutate.admin.delete({ id: targetId })
595
- },
596
- })
597
- `);
598
- var result = await generate({
599
- dir: testDir,
600
- silent: true
601
- });
602
- var content = readFileSync(join(testDir, "generated/syncedMutations.ts"), "utf-8");
603
- expect(content).toContain("reset");
604
- expect(content).toContain("v.string()");
605
- });
606
- test("generates validators for string-model multiline mutation params", async function () {
607
- writeFileSync(join(testDir, "agentEvent/mutations.ts"), `
608
- import { mutations, serverWhere } from 'on-zero'
609
-
610
- const perm = serverWhere('agentEvent', () => true)
611
-
612
- export const mutate = mutations('agentEvent', perm, {
613
- claimReviewLease: async (
614
- { tx },
615
- props: {
616
- id: string
617
- projectId: string
618
- agentId: string
619
- userId: string
620
- taskId: string
621
- reviewKey: string
622
- runnerId: string
623
- createdAt: number
624
- },
625
- ) => {
626
- await tx.mutate.agentEvent.insert(props)
627
- },
628
- })
629
- `);
630
- await generate({
631
- dir: testDir,
632
- silent: true
633
- });
634
- var content = readFileSync(join(testDir, "generated/syncedMutations.ts"), "utf-8");
635
- expect(content).toContain("claimReviewLease");
636
- expect(content).toContain("projectId: v.string()");
637
- expect(content).toContain("createdAt: v.number()");
638
- expect(content).not.toContain("claimReviewLease: v.object({})");
639
- });
640
- test("generates validators for object intersections", async function () {
641
- writeFileSync(join(testDir, "agent/mutations.ts"), `
642
- import { mutations, serverWhere } from 'on-zero'
643
-
644
- const perm = serverWhere('agent', () => true)
645
-
646
- export const mutate = mutations('agent', perm, {
647
- update: async (
648
- { tx },
649
- props: {
650
- id: string
651
- } & {
652
- status?: string
653
- currentTaskId?: string
654
- [key: string]: unknown
655
- },
656
- ) => {
657
- await tx.mutate.agent.update(props)
658
- },
659
- })
660
- `);
661
- await generate({
662
- dir: testDir,
663
- silent: true
664
- });
665
- var content = readFileSync(join(testDir, "generated/syncedMutations.ts"), "utf-8");
666
- expect(content).toContain("update");
667
- expect(content).toContain("id: v.string()");
668
- expect(content).toContain("status: v.optional(v.string())");
669
- expect(content).toContain("currentTaskId: v.optional(v.string())");
670
- expect(content).not.toContain("update: v.object({})");
671
- expect(content).not.toContain("[key");
672
- });
673
- test("uses v.unknown for untyped mutation payloads", async function () {
674
- writeFileSync(join(testDir, "project/mutations.ts"), `
675
- import { mutations, serverWhere } from 'on-zero'
676
-
677
- const perm = serverWhere('project', () => true)
678
-
679
- export const mutate = mutations('project', perm, {
680
- insert: async ({ tx }, project) => {
681
- await tx.mutate.project.insert(project)
682
- },
683
- })
684
- `);
685
- await generate({
686
- dir: testDir,
687
- silent: true
688
- });
689
- var content = readFileSync(join(testDir, "generated/syncedMutations.ts"), "utf-8");
690
- expect(content).toContain("insert: v.unknown()");
691
- expect(content).not.toContain("insert: v.void_()");
692
- });
693
- test("handles mutations with only context param (void)", async function () {
694
- writeFileSync(join(testDir, "user/mutations.ts"), `
695
- import { table, string } from 'on-zero'
696
- import { mutations, serverWhere } from 'on-zero'
697
-
698
- export const schema = table('user').columns({
699
- id: string(),
700
- name: string(),
701
- }).primaryKey('id')
702
-
703
- const perm = serverWhere('user', () => true)
704
-
705
- export const mutate = mutations(schema, perm, {
706
- finishOnboarding: async ({ tx }) => {
707
- // no second param
708
- },
709
- })
710
- `);
711
- var result = await generate({
712
- dir: testDir,
713
- silent: true
714
- });
715
- var content = readFileSync(join(testDir, "generated/syncedMutations.ts"), "utf-8");
716
- expect(content).toContain("finishOnboarding");
717
- });
718
- test("handles primitive param type", async function () {
719
- writeFileSync(join(testDir, "user/mutations.ts"), `
720
- import { table, string } from 'on-zero'
721
- import { mutations, serverWhere } from 'on-zero'
722
-
723
- export const schema = table('user').columns({
724
- id: string(),
725
- name: string(),
726
- }).primaryKey('id')
727
-
728
- const perm = serverWhere('user', () => true)
729
-
730
- export const mutate = mutations(schema, perm, {
731
- completeSignup: async ({ tx }, userId: string) => {
732
- await tx.mutate.user.update({ id: userId })
733
- },
734
- })
735
- `);
736
- var result = await generate({
737
- dir: testDir,
738
- silent: true
739
- });
740
- var content = readFileSync(join(testDir, "generated/syncedMutations.ts"), "utf-8");
741
- expect(content).toContain("completeSignup");
742
- expect(content).toContain("v.string()");
743
- });
744
- test("handles array param type", async function () {
745
- writeFileSync(join(testDir, "batch/mutations.ts"), `
746
- import { mutations } from 'on-zero'
747
-
748
- export const mutate = mutations({
749
- bulkDelete: async ({ tx }, ids: Array<{ id: string }>) => {
750
- for (const { id } of ids) await tx.mutate.batch.delete({ id })
751
- },
752
- })
753
- `);
754
- var result = await generate({
755
- dir: testDir,
756
- silent: true
757
- });
758
- var content = readFileSync(join(testDir, "generated/syncedMutations.ts"), "utf-8");
759
- expect(content).toContain("bulkDelete");
760
- expect(content).toContain("v.array");
761
- });
762
334
  test("populates mutationCount and caching works", async function () {
763
335
  writeFileSync(join(testDir, "item/mutations.ts"), `
764
336
  import { table, string } from 'on-zero'
@@ -790,242 +362,4 @@ export const mutate = mutations(schema, perm, {
790
362
  expect(second.mutationCount).toBe(first.mutationCount);
791
363
  });
792
364
  });
793
- describe("type resolution", function () {
794
- test("resolves imported type references for mutations", async function () {
795
- writeFileSync(join(testDir, "post/types.ts"), `
796
- export type ArchiveParams = {
797
- id: string
798
- reason: string
799
- archived: boolean
800
- }
801
- `);
802
- writeFileSync(join(testDir, "post/mutations.ts"), `
803
- import { table, string, boolean } from 'on-zero'
804
- import { mutations } from 'on-zero'
805
- import type { ArchiveParams } from './types'
806
-
807
- export const mutate = mutations({
808
- archive: async ({ tx }, params: ArchiveParams) => {
809
- await tx.mutate.post.update(params)
810
- },
811
- })
812
- `);
813
- var result = await generate({
814
- dir: testDir,
815
- silent: true
816
- });
817
- var content = readFileSync(join(testDir, "generated/syncedMutations.ts"), "utf-8");
818
- expect(content).toContain("v.string()");
819
- expect(content).toContain("v.boolean()");
820
- expect(content).toContain("reason");
821
- expect(content).toContain("archived");
822
- });
823
- test("resolves utility types like Pick", async function () {
824
- writeFileSync(join(testDir, "item/types.ts"), `
825
- export type Item = {
826
- id: string
827
- name: string
828
- description: string
829
- count: number
830
- }
831
- `);
832
- writeFileSync(join(testDir, "item/mutations.ts"), `
833
- import { table, string, number } from 'on-zero'
834
- import { mutations, serverWhere } from 'on-zero'
835
- import type { Item } from './types'
836
-
837
- export const schema = table('item').columns({
838
- id: string(),
839
- name: string(),
840
- description: string(),
841
- count: number(),
842
- }).primaryKey('id')
843
-
844
- const perm = serverWhere('item', () => true)
845
-
846
- export const mutate = mutations(schema, perm, {
847
- rename: async ({ tx }, updates: Pick<Item, 'id' | 'name'>) => {
848
- await tx.mutate.item.update(updates)
849
- },
850
- })
851
- `);
852
- var result = await generate({
853
- dir: testDir,
854
- silent: true
855
- });
856
- var content = readFileSync(join(testDir, "generated/syncedMutations.ts"), "utf-8");
857
- expect(content).toContain("id");
858
- expect(content).toContain("name");
859
- expect(content).not.toMatch(/rename:[\s\S]*description/);
860
- expect(content).not.toMatch(/rename:[\s\S]*count/);
861
- });
862
- test("resolves instantiated Zero row fields through Partial and Omit", async function () {
863
- symlinkSync(join(import.meta.dirname, "../node_modules"), join(testDir, "node_modules"), "dir");
864
- writeFileSync(join(testDir, "item/types.ts"), `
865
- import type { Row } from '@rocicorp/zero'
866
- import { drizzleZeroConfig } from 'drizzle-zero-sqlite'
867
- import { sqliteTable, text } from 'drizzle-orm/sqlite-core'
868
-
869
- const item = sqliteTable('item', {
870
- id: text().primaryKey(),
871
- name: text().notNull(),
872
- description: text().notNull(),
873
- })
874
- const schema = drizzleZeroConfig({ item })
875
-
876
- export type Item = Row<(typeof schema)['tables']['item']>
877
- `);
878
- writeFileSync(join(testDir, "item/mutations.ts"), `
879
- import { mutations } from 'on-zero'
880
- import type { Item } from './types'
881
-
882
- type ItemUpdate = { id: string } & Partial<Omit<Item, 'id'>>
883
-
884
- export const mutate = mutations('item', {
885
- update: async ({ tx }, props: ItemUpdate) => {
886
- await tx.mutate.item.update(props)
887
- },
888
- })
889
- `);
890
- await generate({
891
- dir: testDir,
892
- silent: true
893
- });
894
- var content = readFileSync(join(testDir, "generated/syncedMutations.ts"), "utf-8");
895
- expect(content).toContain("name: v.optional(v.string())");
896
- expect(content).toContain("description: v.optional(v.string())");
897
- expect(content).not.toContain("name: v.optional(v.unknown())");
898
- expect(content).not.toContain("description: v.optional(v.unknown())");
899
- });
900
- test("skips symbol-keyed properties when resolving imported mutation param types", async function () {
901
- writeFileSync(join(testDir, "item/types.ts"), `
902
- export type WeirdParams = {
903
- id: string
904
- [Symbol.iterator]?: () => Iterator<string>
905
- }
906
- `);
907
- writeFileSync(join(testDir, "item/mutations.ts"), `
908
- import { mutations } from 'on-zero'
909
- import type { WeirdParams } from './types'
910
-
911
- export const mutate = mutations({
912
- run: async ({ tx }, params: WeirdParams) => {
913
- await tx.mutate.item.delete({ id: params.id })
914
- },
915
- })
916
- `);
917
- await generate({
918
- dir: testDir,
919
- silent: true
920
- });
921
- var content = readFileSync(join(testDir, "generated/syncedMutations.ts"), "utf-8");
922
- expect(content).toContain("run");
923
- expect(content).toContain("id: v.string()");
924
- expect(content).not.toContain("__@iterator");
925
- });
926
- test("resolves imported types in query params", async function () {
927
- writeFileSync(join(testDir, "post/mutations.ts"), `export const schema = table('post', { id: string() })`);
928
- writeFileSync(join(testDir, "post/types.ts"), `
929
- export type PostFilter = {
930
- authorId: string
931
- published: boolean
932
- }
933
- `);
934
- writeFileSync(join(testDir, "post/queries.ts"), `
935
- import type { PostFilter } from './types'
936
-
937
- export const filteredPosts = (filter: PostFilter) => zero.query.post
938
- `);
939
- var result = await generate({
940
- dir: testDir,
941
- silent: true
942
- });
943
- var content = readFileSync(join(testDir, "generated/syncedQueries.ts"), "utf-8");
944
- expect(content).toContain("filteredPosts");
945
- expect(content).toContain("v.object");
946
- expect(content).toContain("authorId");
947
- expect(content).toContain("v.boolean()");
948
- });
949
- });
950
- describe("generateDrizzleSchemaFile", function () {
951
- test("preserves table server names and executable two-hop relationships", function () {
952
- var schema = {
953
- tables: {
954
- users: {
955
- name: "users",
956
- serverName: "user_records",
957
- primaryKey: ["id"],
958
- columns: {
959
- id: {
960
- type: "string",
961
- optional: false,
962
- customType: null
963
- }
964
- }
965
- },
966
- groups: {
967
- name: "groups",
968
- primaryKey: ["id"],
969
- columns: {
970
- id: {
971
- type: "string",
972
- optional: false,
973
- customType: null
974
- }
975
- }
976
- },
977
- memberships: {
978
- name: "memberships",
979
- primaryKey: ["userId", "groupId"],
980
- columns: {
981
- userId: {
982
- type: "string",
983
- optional: false,
984
- customType: null
985
- },
986
- groupId: {
987
- type: "string",
988
- optional: false,
989
- customType: null
990
- }
991
- }
992
- }
993
- },
994
- relationships: {
995
- users: {
996
- groups: [{
997
- sourceField: ["id"],
998
- destField: ["userId"],
999
- destSchema: "memberships",
1000
- cardinality: "many"
1001
- }, {
1002
- sourceField: ["groupId"],
1003
- destField: ["id"],
1004
- destSchema: "groups",
1005
- cardinality: "many"
1006
- }]
1007
- }
1008
- }
1009
- };
1010
- var source = generateDrizzleSchemaFile(schema);
1011
- var executableSource = source.replace("import { boolean, createSchema, json, number, relationships, string, table } from '@rocicorp/zero'", "").replace("export const schema =", "globalThis.generatedSchema =");
1012
- var context = {
1013
- ...zero
1014
- };
1015
- runInNewContext(executableSource, context);
1016
- expect(source).toContain('const usersTable = table("users").from("user_records")');
1017
- expect(context.generatedSchema).toMatchObject({
1018
- tables: {
1019
- users: {
1020
- serverName: "user_records"
1021
- }
1022
- },
1023
- relationships: {
1024
- users: {
1025
- groups: schema.relationships.users.groups
1026
- }
1027
- }
1028
- });
1029
- });
1030
- });
1031
365
  //# sourceMappingURL=generate.test.native.js.map