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,35 +1,9 @@
1
- var __create = Object.create;
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __getProtoOf = Object.getPrototypeOf;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __copyProps = (to, from, except, desc) => {
8
- if (from && typeof from === "object" || typeof from === "function") {
9
- for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
10
- get: () => from[key],
11
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
12
- });
13
- }
14
- return to;
15
- };
16
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
- // If the importer is in node compatibility mode or this is not an ESM
18
- // file that has been converted to a CommonJS file using a Babel-
19
- // compatible transform (i.e. "__esModule" has not been set), then set
20
- // "default" to the CommonJS "module.exports" for node compatibility.
21
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
22
- value: mod,
23
- enumerable: true
24
- }) : target, mod));
25
1
  var import_node_fs = require("node:fs");
26
2
  var import_node_os = require("node:os");
27
3
  var import_node_path = require("node:path");
28
4
  var import_node_vm = require("node:vm");
29
- var zero = __toESM(require("@rocicorp/zero"), 1);
30
5
  var import_vitest = require("vitest");
31
6
  var import_generate = require("./generate.cjs");
32
- const import_meta = {};
33
7
  const testDir = (0, import_node_path.join)((0, import_node_os.tmpdir)(), "on-zero-test-" + Date.now());
34
8
  function writeFileSync(path, content) {
35
9
  (0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(path), {
@@ -50,105 +24,6 @@ function writeFileSync(path, content) {
50
24
  });
51
25
  });
52
26
  (0, import_vitest.describe)("generate", () => {
53
- (0, import_vitest.test)("generates models.ts, types.ts, tables.ts from model files", async () => {
54
- writeFileSync((0, import_node_path.join)(testDir, "post/mutations.ts"), `
55
- import { table, string, boolean } from 'on-zero'
56
-
57
- export const schema = table('post', {
58
- id: string(),
59
- title: string(),
60
- published: boolean(),
61
- })
62
- `);
63
- writeFileSync((0, import_node_path.join)(testDir, "comment/mutations.ts"), `
64
- import { table, string } from 'on-zero'
65
-
66
- export const schema = table('comment', {
67
- id: string(),
68
- postId: string(),
69
- body: string(),
70
- })
71
- `);
72
- writeFileSync((0, import_node_path.join)(testDir, "post/post.test.ts"), `throw new Error('test files must not be generated as models')`);
73
- writeFileSync((0, import_node_path.join)(testDir, "comment/comment.spec.ts"), `throw new Error('spec files must not be generated as queries')`);
74
- const result = await (0, import_generate.generate)({
75
- dir: testDir,
76
- silent: true
77
- });
78
- (0, import_vitest.expect)(result.modelCount).toBe(2);
79
- (0, import_vitest.expect)(result.schemaCount).toBe(2);
80
- (0, import_vitest.expect)(result.filesChanged).toBeGreaterThan(0);
81
- (0, import_vitest.expect)((0, import_node_fs.existsSync)((0, import_node_path.join)(testDir, "generated/models.ts"))).toBe(true);
82
- (0, import_vitest.expect)((0, import_node_fs.existsSync)((0, import_node_path.join)(testDir, "generated/types.ts"))).toBe(true);
83
- (0, import_vitest.expect)((0, import_node_fs.existsSync)((0, import_node_path.join)(testDir, "generated/tables.ts"))).toBe(true);
84
- const modelsContent = (0, import_node_fs.readFileSync)((0, import_node_path.join)(testDir, "generated/models.ts"), "utf-8");
85
- (0, import_vitest.expect)(modelsContent).toContain("import * as comment from '../comment/mutations'");
86
- (0, import_vitest.expect)(modelsContent).toContain("import * as post from '../post/mutations'");
87
- (0, import_vitest.expect)(modelsContent).not.toContain("post.test");
88
- (0, import_vitest.expect)(modelsContent).toContain("export const models = {");
89
- const typesContent = (0, import_node_fs.readFileSync)((0, import_node_path.join)(testDir, "generated/types.ts"), "utf-8");
90
- (0, import_vitest.expect)(typesContent).toContain("export type Post = TableInsertRow<typeof schema.post>");
91
- (0, import_vitest.expect)(typesContent).toContain("export type Comment = TableInsertRow<typeof schema.comment>");
92
- const tablesContent = (0, import_node_fs.readFileSync)((0, import_node_path.join)(testDir, "generated/tables.ts"), "utf-8");
93
- (0, import_vitest.expect)(tablesContent).toContain("export { schema as post } from '../post/mutations'");
94
- (0, import_vitest.expect)(tablesContent).toContain("export { schema as comment } from '../comment/mutations'");
95
- });
96
- (0, import_vitest.test)("generates query validators from query files", async () => {
97
- writeFileSync((0, import_node_path.join)(testDir, "post/mutations.ts"), `export const schema = table('post', { id: string() })`);
98
- writeFileSync((0, import_node_path.join)(testDir, "post/queries.ts"), `
99
- import { zero } from '../zero'
100
-
101
- export const allPosts = () => zero.query.post
102
-
103
- export const postById = ({ id }: { id: string }) => zero.query.post.where('id', id)
104
-
105
- export const postsByAuthor = ({ authorId, limit }: { authorId: string; limit?: number }) =>
106
- zero.query.post.where('authorId', authorId).limit(limit ?? 10)
107
- `);
108
- const result = await (0, import_generate.generate)({
109
- dir: testDir,
110
- silent: true
111
- });
112
- (0, import_vitest.expect)(result.queryCount).toBe(3);
113
- (0, import_vitest.expect)((0, import_node_fs.existsSync)((0, import_node_path.join)(testDir, "generated/groupedQueries.ts"))).toBe(true);
114
- (0, import_vitest.expect)((0, import_node_fs.existsSync)((0, import_node_path.join)(testDir, "generated/syncedQueries.ts"))).toBe(true);
115
- const groupedContent = (0, import_node_fs.readFileSync)((0, import_node_path.join)(testDir, "generated/groupedQueries.ts"), "utf-8");
116
- (0, import_vitest.expect)(groupedContent).toContain("import * as postSource from '../post/queries'");
117
- (0, import_vitest.expect)(groupedContent).toContain("postById: postSource.postById");
118
- const syncedContent = (0, import_node_fs.readFileSync)((0, import_node_path.join)(testDir, "generated/syncedQueries.ts"), "utf-8");
119
- (0, import_vitest.expect)(syncedContent).toContain("allPosts: defineQuery");
120
- (0, import_vitest.expect)(syncedContent).toContain("postById: defineQuery");
121
- (0, import_vitest.expect)(syncedContent).toContain("postsByAuthor: defineQuery");
122
- (0, import_vitest.expect)(syncedContent).toContain("v.object");
123
- });
124
- (0, import_vitest.test)("skips permission exports in queries", async () => {
125
- writeFileSync((0, import_node_path.join)(testDir, "post/mutations.ts"), `export const schema = table('post', { id: string() })`);
126
- writeFileSync((0, import_node_path.join)(testDir, "post/queries.ts"), `
127
- export const permission = () => ({ canRead: true })
128
- export const allPosts = () => zero.query.post
129
- `);
130
- const result = await (0, import_generate.generate)({
131
- dir: testDir,
132
- silent: true
133
- });
134
- (0, import_vitest.expect)(result.queryCount).toBe(1);
135
- const syncedContent = (0, import_node_fs.readFileSync)((0, import_node_path.join)(testDir, "generated/syncedQueries.ts"), "utf-8");
136
- (0, import_vitest.expect)(syncedContent).toContain("allPosts");
137
- (0, import_vitest.expect)(syncedContent).not.toContain("permission:");
138
- });
139
- (0, import_vitest.test)("aliases user import without changing the model key", async () => {
140
- writeFileSync((0, import_node_path.join)(testDir, "user/mutations.ts"), `export const schema = table('user', { id: string(), name: string() })`);
141
- await (0, import_generate.generate)({
142
- dir: testDir,
143
- silent: true
144
- });
145
- const modelsContent = (0, import_node_fs.readFileSync)((0, import_node_path.join)(testDir, "generated/models.ts"), "utf-8");
146
- (0, import_vitest.expect)(modelsContent).toContain("import * as userPublic from '../user/mutations'");
147
- (0, import_vitest.expect)(modelsContent).toContain("user: userPublic,");
148
- (0, import_vitest.expect)(modelsContent).not.toContain("\n userPublic,");
149
- const typesContent = (0, import_node_fs.readFileSync)((0, import_node_path.join)(testDir, "generated/types.ts"), "utf-8");
150
- (0, import_vitest.expect)(typesContent).toContain("typeof schema.userPublic");
151
- });
152
27
  (0, import_vitest.test)("runs after command when files change", async () => {
153
28
  writeFileSync((0, import_node_path.join)(testDir, "post/mutations.ts"), `export const schema = table('post', { id: string() })`);
154
29
  const markerFile = (0, import_node_path.join)(testDir, "after-ran");
@@ -192,51 +67,6 @@ export const allPosts = () => zero.query.post
192
67
  });
193
68
  (0, import_vitest.describe)("instance layout", () => {
194
69
  const dataDir = () => (0, import_node_path.join)(testDir, "src/data");
195
- (0, import_vitest.test)("discovers file and folder namespaces and emits related sync closure", async () => {
196
- writeFileSync((0, import_node_path.join)(dataDir(), "control/reaction.ts"), `export const allReactions = () => zql.reaction`);
197
- writeFileSync((0, import_node_path.join)(dataDir(), "on-zero.config.ts"), `export default defineConfig({ instances: { control: {}, project: { scope: 'projectId' } } })`);
198
- writeFileSync((0, import_node_path.join)(dataDir(), "project/message/queries.ts"), `
199
- const unused = () => zql.message.related('notARealRelation')
200
- const withComments = () => zql.message.related('comments', (q) => q.related('author'))
201
- export const messages = () => withComments()
202
- `);
203
- writeFileSync((0, import_node_path.join)(dataDir(), "project/message/mutations.ts"), `export const schema = table('message').columns({ id: string(), projectId: string() })`);
204
- writeFileSync((0, import_node_path.join)(dataDir(), "project/message/helpers.ts"), `export const privateHelper = () => 'ignored'`);
205
- writeFileSync((0, import_node_path.join)(testDir, "src/database/relations.ts"), `
206
- export const relations = defineRelations(schema, (r) => ({
207
- message: { comments: r.many.comment({}) },
208
- comment: { author: r.one.userPublic({}) },
209
- }))
210
- `);
211
- writeFileSync((0, import_node_path.join)(testDir, "src/database/schema.ts"), `
212
- export const comment = sqliteTable('comment', { id: text(), projectId: text() })
213
- export const userPublic = sqliteTable('user_public', { id: text(), projectId: text() })
214
- `);
215
- const membership = await (0, import_generate.deriveDataMembership)({
216
- dir: dataDir()
217
- });
218
- (0, import_vitest.expect)(membership.allTables).toEqual(["comment", "message", "reaction", "userPublic"]);
219
- (0, import_vitest.expect)(membership.instances.project).toEqual({
220
- tables: ["message"],
221
- syncTables: ["comment", "message", "userPublic"],
222
- supportTables: [],
223
- scope: "projectId"
224
- });
225
- await (0, import_generate.generate)({
226
- dir: dataDir(),
227
- silent: true
228
- });
229
- const grouped = (0, import_node_fs.readFileSync)((0, import_node_path.join)(dataDir(), "generated/groupedQueries.ts"), "utf8");
230
- (0, import_vitest.expect)(grouped).toContain("from '../control/reaction'");
231
- (0, import_vitest.expect)(grouped).toContain("from '../project/message/queries'");
232
- (0, import_vitest.expect)(grouped).not.toContain("privateHelper");
233
- const manifest = (0, import_node_fs.readFileSync)((0, import_node_path.join)(dataDir(), "generated/instances.ts"), "utf8");
234
- (0, import_vitest.expect)(manifest).toContain("control: {");
235
- (0, import_vitest.expect)(manifest).toContain("project: {");
236
- (0, import_vitest.expect)(manifest).toContain('tables: ["message"]');
237
- (0, import_vitest.expect)(manifest).toContain('syncTables: ["comment","message","userPublic"]');
238
- (0, import_vitest.expect)(manifest).toContain(`defaultVisibility: (value: string) => ({ column: "projectId", value })`);
239
- });
240
70
  (0, import_vitest.test)("derives query membership from the returned query root", async () => {
241
71
  writeFileSync((0, import_node_path.join)(dataDir(), "category/mutations.ts"), `export const schema = table('category').columns({ id: string() })`);
242
72
  writeFileSync((0, import_node_path.join)(dataDir(), "dashboard/queries.ts"), `export const monthSummary = () => zql.category.related('expenses')`);
@@ -445,42 +275,6 @@ export const messages = () => zql.message.related('author')
445
275
  silent: true
446
276
  })).rejects.toThrow(/namespace 'message'.*instances 'control' and 'project'/);
447
277
  });
448
- (0, import_vitest.test)("resolves configured instance directories relative to the config file", async () => {
449
- writeFileSync((0, import_node_path.join)(dataDir(), "on-zero.config.ts"), `export default defineConfig({ instances: { control: { dir: './planes/control-data', supportTables: ['audit'] }, project: { dir: '../project-data', scope: 'projectId' } } })`);
450
- writeFileSync((0, import_node_path.join)(dataDir(), "planes/control-data/account.ts"), `export const accounts = () => zql.account`);
451
- writeFileSync((0, import_node_path.join)(testDir, "src/project-data/message.ts"), `export const schema = table('message').columns({ id: string(), projectId: string() })`);
452
- await (0, import_vitest.expect)((0, import_generate.deriveDataMembership)({
453
- dir: dataDir(),
454
- config: (0, import_node_path.join)(dataDir(), "on-zero.config.ts")
455
- })).resolves.toEqual({
456
- instances: {
457
- control: {
458
- tables: ["account"],
459
- syncTables: ["account"],
460
- supportTables: ["audit"],
461
- scope: null
462
- },
463
- project: {
464
- tables: ["message"],
465
- syncTables: ["message"],
466
- supportTables: [],
467
- scope: "projectId"
468
- }
469
- },
470
- allTables: ["account", "audit", "message"]
471
- });
472
- await (0, import_vitest.expect)((0, import_generate.generate)({
473
- dir: dataDir(),
474
- config: (0, import_node_path.join)(dataDir(), "on-zero.config.ts"),
475
- silent: true
476
- })).resolves.toMatchObject({
477
- modelCount: 1,
478
- schemaCount: 1
479
- });
480
- const models = (0, import_node_fs.readFileSync)((0, import_node_path.join)(dataDir(), "generated/models.ts"), "utf8");
481
- (0, import_vitest.expect)(models).toContain("from '../../project-data/message'");
482
- (0, import_vitest.expect)(models).not.toContain("account");
483
- });
484
278
  (0, import_vitest.test)("rejects missing configured instance directories", async () => {
485
279
  writeFileSync((0, import_node_path.join)(dataDir(), "on-zero.config.ts"), `export default defineConfig({ instances: { project: { dir: './missing' } } })`);
486
280
  await (0, import_vitest.expect)((0, import_generate.deriveDataMembership)({
@@ -525,253 +319,6 @@ export const messages = () => zql.message.related('author')
525
319
  });
526
320
  });
527
321
  (0, import_vitest.describe)("mutations", () => {
528
- (0, import_vitest.test)("generates validators for inline mutation param types", async () => {
529
- writeFileSync((0, import_node_path.join)(testDir, "post/mutations.ts"), `
530
- import { table, string } from 'on-zero'
531
- import { mutations, serverWhere } from 'on-zero'
532
-
533
- export const schema = table('post').columns({
534
- id: string(),
535
- title: string(),
536
- }).primaryKey('id')
537
-
538
- const perm = serverWhere('post', () => true)
539
-
540
- export const mutate = mutations(schema, perm, {
541
- archive: async ({ tx }, { id, reason }: { id: string; reason: string }) => {
542
- await tx.mutate.post.update({ id, archived: true })
543
- },
544
- })
545
- `);
546
- const result = await (0, import_generate.generate)({
547
- dir: testDir,
548
- silent: true
549
- });
550
- (0, import_vitest.expect)(result.mutationCount).toBeGreaterThan(0);
551
- (0, import_vitest.expect)((0, import_node_fs.existsSync)((0, import_node_path.join)(testDir, "generated/syncedMutations.ts"))).toBe(true);
552
- const content = (0, import_node_fs.readFileSync)((0, import_node_path.join)(testDir, "generated/syncedMutations.ts"), "utf-8");
553
- (0, import_vitest.expect)(content).toContain("archive");
554
- (0, import_vitest.expect)(content).toContain("v.object");
555
- (0, import_vitest.expect)(content).toContain("v.string()");
556
- });
557
- (0, import_vitest.test)("generates CRUD validators from schema columns", async () => {
558
- writeFileSync((0, import_node_path.join)(testDir, "task/mutations.ts"), `
559
- import { table, string, number, boolean } from 'on-zero'
560
- import { mutations, serverWhere } from 'on-zero'
561
-
562
- export const schema = table('task').columns({
563
- id: string(),
564
- title: string(),
565
- priority: number(),
566
- done: boolean(),
567
- note: string().optional(),
568
- }).primaryKey('id')
569
-
570
- const perm = serverWhere('task', () => true)
571
-
572
- export const mutate = mutations(schema, perm)
573
- `);
574
- const result = await (0, import_generate.generate)({
575
- dir: testDir,
576
- silent: true
577
- });
578
- const content = (0, import_node_fs.readFileSync)((0, import_node_path.join)(testDir, "generated/syncedMutations.ts"), "utf-8");
579
- (0, import_vitest.expect)(content).toContain("insert:");
580
- (0, import_vitest.expect)(content).toMatch(/insert:.*v\.object/);
581
- (0, import_vitest.expect)(content).toContain("update:");
582
- (0, import_vitest.expect)(content).toContain("delete:");
583
- });
584
- (0, import_vitest.test)("treats models without export const mutate as empty mutations", async () => {
585
- writeFileSync((0, import_node_path.join)(testDir, "readonly/mutations.ts"), `
586
- import { table, string } from 'on-zero'
587
-
588
- export const schema = table('readonly').columns({
589
- id: string(),
590
- name: string(),
591
- }).primaryKey('id')
592
- `);
593
- const result = await (0, import_generate.generate)({
594
- dir: testDir,
595
- silent: true
596
- });
597
- (0, import_vitest.expect)(result.mutationCount).toBe(0);
598
- const content = (0, import_node_fs.readFileSync)((0, import_node_path.join)(testDir, "generated/syncedMutations.ts"), "utf-8");
599
- (0, import_vitest.expect)(content).toContain("readonly: {");
600
- });
601
- (0, import_vitest.test)("extracts custom mutations from bare mutations({})", async () => {
602
- writeFileSync((0, import_node_path.join)(testDir, "admin/mutations.ts"), `
603
- import { mutations } from 'on-zero'
604
-
605
- export const mutate = mutations({
606
- reset: async ({ tx }, { targetId }: { targetId: string }) => {
607
- await tx.mutate.admin.delete({ id: targetId })
608
- },
609
- })
610
- `);
611
- const result = await (0, import_generate.generate)({
612
- dir: testDir,
613
- silent: true
614
- });
615
- const content = (0, import_node_fs.readFileSync)((0, import_node_path.join)(testDir, "generated/syncedMutations.ts"), "utf-8");
616
- (0, import_vitest.expect)(content).toContain("reset");
617
- (0, import_vitest.expect)(content).toContain("v.string()");
618
- });
619
- (0, import_vitest.test)("generates validators for string-model multiline mutation params", async () => {
620
- writeFileSync((0, import_node_path.join)(testDir, "agentEvent/mutations.ts"), `
621
- import { mutations, serverWhere } from 'on-zero'
622
-
623
- const perm = serverWhere('agentEvent', () => true)
624
-
625
- export const mutate = mutations('agentEvent', perm, {
626
- claimReviewLease: async (
627
- { tx },
628
- props: {
629
- id: string
630
- projectId: string
631
- agentId: string
632
- userId: string
633
- taskId: string
634
- reviewKey: string
635
- runnerId: string
636
- createdAt: number
637
- },
638
- ) => {
639
- await tx.mutate.agentEvent.insert(props)
640
- },
641
- })
642
- `);
643
- await (0, import_generate.generate)({
644
- dir: testDir,
645
- silent: true
646
- });
647
- const content = (0, import_node_fs.readFileSync)((0, import_node_path.join)(testDir, "generated/syncedMutations.ts"), "utf-8");
648
- (0, import_vitest.expect)(content).toContain("claimReviewLease");
649
- (0, import_vitest.expect)(content).toContain("projectId: v.string()");
650
- (0, import_vitest.expect)(content).toContain("createdAt: v.number()");
651
- (0, import_vitest.expect)(content).not.toContain("claimReviewLease: v.object({})");
652
- });
653
- (0, import_vitest.test)("generates validators for object intersections", async () => {
654
- writeFileSync((0, import_node_path.join)(testDir, "agent/mutations.ts"), `
655
- import { mutations, serverWhere } from 'on-zero'
656
-
657
- const perm = serverWhere('agent', () => true)
658
-
659
- export const mutate = mutations('agent', perm, {
660
- update: async (
661
- { tx },
662
- props: {
663
- id: string
664
- } & {
665
- status?: string
666
- currentTaskId?: string
667
- [key: string]: unknown
668
- },
669
- ) => {
670
- await tx.mutate.agent.update(props)
671
- },
672
- })
673
- `);
674
- await (0, import_generate.generate)({
675
- dir: testDir,
676
- silent: true
677
- });
678
- const content = (0, import_node_fs.readFileSync)((0, import_node_path.join)(testDir, "generated/syncedMutations.ts"), "utf-8");
679
- (0, import_vitest.expect)(content).toContain("update");
680
- (0, import_vitest.expect)(content).toContain("id: v.string()");
681
- (0, import_vitest.expect)(content).toContain("status: v.optional(v.string())");
682
- (0, import_vitest.expect)(content).toContain("currentTaskId: v.optional(v.string())");
683
- (0, import_vitest.expect)(content).not.toContain("update: v.object({})");
684
- (0, import_vitest.expect)(content).not.toContain("[key");
685
- });
686
- (0, import_vitest.test)("uses v.unknown for untyped mutation payloads", async () => {
687
- writeFileSync((0, import_node_path.join)(testDir, "project/mutations.ts"), `
688
- import { mutations, serverWhere } from 'on-zero'
689
-
690
- const perm = serverWhere('project', () => true)
691
-
692
- export const mutate = mutations('project', perm, {
693
- insert: async ({ tx }, project) => {
694
- await tx.mutate.project.insert(project)
695
- },
696
- })
697
- `);
698
- await (0, import_generate.generate)({
699
- dir: testDir,
700
- silent: true
701
- });
702
- const content = (0, import_node_fs.readFileSync)((0, import_node_path.join)(testDir, "generated/syncedMutations.ts"), "utf-8");
703
- (0, import_vitest.expect)(content).toContain("insert: v.unknown()");
704
- (0, import_vitest.expect)(content).not.toContain("insert: v.void_()");
705
- });
706
- (0, import_vitest.test)("handles mutations with only context param (void)", async () => {
707
- writeFileSync((0, import_node_path.join)(testDir, "user/mutations.ts"), `
708
- import { table, string } from 'on-zero'
709
- import { mutations, serverWhere } from 'on-zero'
710
-
711
- export const schema = table('user').columns({
712
- id: string(),
713
- name: string(),
714
- }).primaryKey('id')
715
-
716
- const perm = serverWhere('user', () => true)
717
-
718
- export const mutate = mutations(schema, perm, {
719
- finishOnboarding: async ({ tx }) => {
720
- // no second param
721
- },
722
- })
723
- `);
724
- const result = await (0, import_generate.generate)({
725
- dir: testDir,
726
- silent: true
727
- });
728
- const content = (0, import_node_fs.readFileSync)((0, import_node_path.join)(testDir, "generated/syncedMutations.ts"), "utf-8");
729
- (0, import_vitest.expect)(content).toContain("finishOnboarding");
730
- });
731
- (0, import_vitest.test)("handles primitive param type", async () => {
732
- writeFileSync((0, import_node_path.join)(testDir, "user/mutations.ts"), `
733
- import { table, string } from 'on-zero'
734
- import { mutations, serverWhere } from 'on-zero'
735
-
736
- export const schema = table('user').columns({
737
- id: string(),
738
- name: string(),
739
- }).primaryKey('id')
740
-
741
- const perm = serverWhere('user', () => true)
742
-
743
- export const mutate = mutations(schema, perm, {
744
- completeSignup: async ({ tx }, userId: string) => {
745
- await tx.mutate.user.update({ id: userId })
746
- },
747
- })
748
- `);
749
- const result = await (0, import_generate.generate)({
750
- dir: testDir,
751
- silent: true
752
- });
753
- const content = (0, import_node_fs.readFileSync)((0, import_node_path.join)(testDir, "generated/syncedMutations.ts"), "utf-8");
754
- (0, import_vitest.expect)(content).toContain("completeSignup");
755
- (0, import_vitest.expect)(content).toContain("v.string()");
756
- });
757
- (0, import_vitest.test)("handles array param type", async () => {
758
- writeFileSync((0, import_node_path.join)(testDir, "batch/mutations.ts"), `
759
- import { mutations } from 'on-zero'
760
-
761
- export const mutate = mutations({
762
- bulkDelete: async ({ tx }, ids: Array<{ id: string }>) => {
763
- for (const { id } of ids) await tx.mutate.batch.delete({ id })
764
- },
765
- })
766
- `);
767
- const result = await (0, import_generate.generate)({
768
- dir: testDir,
769
- silent: true
770
- });
771
- const content = (0, import_node_fs.readFileSync)((0, import_node_path.join)(testDir, "generated/syncedMutations.ts"), "utf-8");
772
- (0, import_vitest.expect)(content).toContain("bulkDelete");
773
- (0, import_vitest.expect)(content).toContain("v.array");
774
- });
775
322
  (0, import_vitest.test)("populates mutationCount and caching works", async () => {
776
323
  writeFileSync((0, import_node_path.join)(testDir, "item/mutations.ts"), `
777
324
  import { table, string } from 'on-zero'
@@ -802,242 +349,4 @@ export const mutate = mutations(schema, perm, {
802
349
  (0, import_vitest.expect)(second.filesChanged).toBe(0);
803
350
  (0, import_vitest.expect)(second.mutationCount).toBe(first.mutationCount);
804
351
  });
805
- });
806
- (0, import_vitest.describe)("type resolution", () => {
807
- (0, import_vitest.test)("resolves imported type references for mutations", async () => {
808
- writeFileSync((0, import_node_path.join)(testDir, "post/types.ts"), `
809
- export type ArchiveParams = {
810
- id: string
811
- reason: string
812
- archived: boolean
813
- }
814
- `);
815
- writeFileSync((0, import_node_path.join)(testDir, "post/mutations.ts"), `
816
- import { table, string, boolean } from 'on-zero'
817
- import { mutations } from 'on-zero'
818
- import type { ArchiveParams } from './types'
819
-
820
- export const mutate = mutations({
821
- archive: async ({ tx }, params: ArchiveParams) => {
822
- await tx.mutate.post.update(params)
823
- },
824
- })
825
- `);
826
- const result = await (0, import_generate.generate)({
827
- dir: testDir,
828
- silent: true
829
- });
830
- const content = (0, import_node_fs.readFileSync)((0, import_node_path.join)(testDir, "generated/syncedMutations.ts"), "utf-8");
831
- (0, import_vitest.expect)(content).toContain("v.string()");
832
- (0, import_vitest.expect)(content).toContain("v.boolean()");
833
- (0, import_vitest.expect)(content).toContain("reason");
834
- (0, import_vitest.expect)(content).toContain("archived");
835
- });
836
- (0, import_vitest.test)("resolves utility types like Pick", async () => {
837
- writeFileSync((0, import_node_path.join)(testDir, "item/types.ts"), `
838
- export type Item = {
839
- id: string
840
- name: string
841
- description: string
842
- count: number
843
- }
844
- `);
845
- writeFileSync((0, import_node_path.join)(testDir, "item/mutations.ts"), `
846
- import { table, string, number } from 'on-zero'
847
- import { mutations, serverWhere } from 'on-zero'
848
- import type { Item } from './types'
849
-
850
- export const schema = table('item').columns({
851
- id: string(),
852
- name: string(),
853
- description: string(),
854
- count: number(),
855
- }).primaryKey('id')
856
-
857
- const perm = serverWhere('item', () => true)
858
-
859
- export const mutate = mutations(schema, perm, {
860
- rename: async ({ tx }, updates: Pick<Item, 'id' | 'name'>) => {
861
- await tx.mutate.item.update(updates)
862
- },
863
- })
864
- `);
865
- const result = await (0, import_generate.generate)({
866
- dir: testDir,
867
- silent: true
868
- });
869
- const content = (0, import_node_fs.readFileSync)((0, import_node_path.join)(testDir, "generated/syncedMutations.ts"), "utf-8");
870
- (0, import_vitest.expect)(content).toContain("id");
871
- (0, import_vitest.expect)(content).toContain("name");
872
- (0, import_vitest.expect)(content).not.toMatch(/rename:[\s\S]*description/);
873
- (0, import_vitest.expect)(content).not.toMatch(/rename:[\s\S]*count/);
874
- });
875
- (0, import_vitest.test)("resolves instantiated Zero row fields through Partial and Omit", async () => {
876
- (0, import_node_fs.symlinkSync)((0, import_node_path.join)(import_meta.dirname, "../node_modules"), (0, import_node_path.join)(testDir, "node_modules"), "dir");
877
- writeFileSync((0, import_node_path.join)(testDir, "item/types.ts"), `
878
- import type { Row } from '@rocicorp/zero'
879
- import { drizzleZeroConfig } from 'drizzle-zero-sqlite'
880
- import { sqliteTable, text } from 'drizzle-orm/sqlite-core'
881
-
882
- const item = sqliteTable('item', {
883
- id: text().primaryKey(),
884
- name: text().notNull(),
885
- description: text().notNull(),
886
- })
887
- const schema = drizzleZeroConfig({ item })
888
-
889
- export type Item = Row<(typeof schema)['tables']['item']>
890
- `);
891
- writeFileSync((0, import_node_path.join)(testDir, "item/mutations.ts"), `
892
- import { mutations } from 'on-zero'
893
- import type { Item } from './types'
894
-
895
- type ItemUpdate = { id: string } & Partial<Omit<Item, 'id'>>
896
-
897
- export const mutate = mutations('item', {
898
- update: async ({ tx }, props: ItemUpdate) => {
899
- await tx.mutate.item.update(props)
900
- },
901
- })
902
- `);
903
- await (0, import_generate.generate)({
904
- dir: testDir,
905
- silent: true
906
- });
907
- const content = (0, import_node_fs.readFileSync)((0, import_node_path.join)(testDir, "generated/syncedMutations.ts"), "utf-8");
908
- (0, import_vitest.expect)(content).toContain("name: v.optional(v.string())");
909
- (0, import_vitest.expect)(content).toContain("description: v.optional(v.string())");
910
- (0, import_vitest.expect)(content).not.toContain("name: v.optional(v.unknown())");
911
- (0, import_vitest.expect)(content).not.toContain("description: v.optional(v.unknown())");
912
- });
913
- (0, import_vitest.test)("skips symbol-keyed properties when resolving imported mutation param types", async () => {
914
- writeFileSync((0, import_node_path.join)(testDir, "item/types.ts"), `
915
- export type WeirdParams = {
916
- id: string
917
- [Symbol.iterator]?: () => Iterator<string>
918
- }
919
- `);
920
- writeFileSync((0, import_node_path.join)(testDir, "item/mutations.ts"), `
921
- import { mutations } from 'on-zero'
922
- import type { WeirdParams } from './types'
923
-
924
- export const mutate = mutations({
925
- run: async ({ tx }, params: WeirdParams) => {
926
- await tx.mutate.item.delete({ id: params.id })
927
- },
928
- })
929
- `);
930
- await (0, import_generate.generate)({
931
- dir: testDir,
932
- silent: true
933
- });
934
- const content = (0, import_node_fs.readFileSync)((0, import_node_path.join)(testDir, "generated/syncedMutations.ts"), "utf-8");
935
- (0, import_vitest.expect)(content).toContain("run");
936
- (0, import_vitest.expect)(content).toContain("id: v.string()");
937
- (0, import_vitest.expect)(content).not.toContain("__@iterator");
938
- });
939
- (0, import_vitest.test)("resolves imported types in query params", async () => {
940
- writeFileSync((0, import_node_path.join)(testDir, "post/mutations.ts"), `export const schema = table('post', { id: string() })`);
941
- writeFileSync((0, import_node_path.join)(testDir, "post/types.ts"), `
942
- export type PostFilter = {
943
- authorId: string
944
- published: boolean
945
- }
946
- `);
947
- writeFileSync((0, import_node_path.join)(testDir, "post/queries.ts"), `
948
- import type { PostFilter } from './types'
949
-
950
- export const filteredPosts = (filter: PostFilter) => zero.query.post
951
- `);
952
- const result = await (0, import_generate.generate)({
953
- dir: testDir,
954
- silent: true
955
- });
956
- const content = (0, import_node_fs.readFileSync)((0, import_node_path.join)(testDir, "generated/syncedQueries.ts"), "utf-8");
957
- (0, import_vitest.expect)(content).toContain("filteredPosts");
958
- (0, import_vitest.expect)(content).toContain("v.object");
959
- (0, import_vitest.expect)(content).toContain("authorId");
960
- (0, import_vitest.expect)(content).toContain("v.boolean()");
961
- });
962
- });
963
- (0, import_vitest.describe)("generateDrizzleSchemaFile", () => {
964
- (0, import_vitest.test)("preserves table server names and executable two-hop relationships", () => {
965
- const schema = {
966
- tables: {
967
- users: {
968
- name: "users",
969
- serverName: "user_records",
970
- primaryKey: ["id"],
971
- columns: {
972
- id: {
973
- type: "string",
974
- optional: false,
975
- customType: null
976
- }
977
- }
978
- },
979
- groups: {
980
- name: "groups",
981
- primaryKey: ["id"],
982
- columns: {
983
- id: {
984
- type: "string",
985
- optional: false,
986
- customType: null
987
- }
988
- }
989
- },
990
- memberships: {
991
- name: "memberships",
992
- primaryKey: ["userId", "groupId"],
993
- columns: {
994
- userId: {
995
- type: "string",
996
- optional: false,
997
- customType: null
998
- },
999
- groupId: {
1000
- type: "string",
1001
- optional: false,
1002
- customType: null
1003
- }
1004
- }
1005
- }
1006
- },
1007
- relationships: {
1008
- users: {
1009
- groups: [{
1010
- sourceField: ["id"],
1011
- destField: ["userId"],
1012
- destSchema: "memberships",
1013
- cardinality: "many"
1014
- }, {
1015
- sourceField: ["groupId"],
1016
- destField: ["id"],
1017
- destSchema: "groups",
1018
- cardinality: "many"
1019
- }]
1020
- }
1021
- }
1022
- };
1023
- const source = (0, import_generate.generateDrizzleSchemaFile)(schema);
1024
- const executableSource = source.replace("import { boolean, createSchema, json, number, relationships, string, table } from '@rocicorp/zero'", "").replace("export const schema =", "globalThis.generatedSchema =");
1025
- const context = {
1026
- ...zero
1027
- };
1028
- (0, import_node_vm.runInNewContext)(executableSource, context);
1029
- (0, import_vitest.expect)(source).toContain('const usersTable = table("users").from("user_records")');
1030
- (0, import_vitest.expect)(context.generatedSchema).toMatchObject({
1031
- tables: {
1032
- users: {
1033
- serverName: "user_records"
1034
- }
1035
- },
1036
- relationships: {
1037
- users: {
1038
- groups: schema.relationships.users.groups
1039
- }
1040
- }
1041
- });
1042
- });
1043
352
  });