on-zero 0.4.1 → 0.4.3

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 (41) hide show
  1. package/dist/cjs/generate-helpers.cjs +309 -0
  2. package/dist/cjs/generate-helpers.native.js +451 -0
  3. package/dist/cjs/generate-helpers.native.js.map +1 -0
  4. package/dist/cjs/generate-lite.cjs +150 -0
  5. package/dist/cjs/generate-lite.native.js +269 -0
  6. package/dist/cjs/generate-lite.native.js.map +1 -0
  7. package/dist/cjs/generate-lite.test.cjs +229 -0
  8. package/dist/cjs/generate-lite.test.native.js +234 -0
  9. package/dist/cjs/generate-lite.test.native.js.map +1 -0
  10. package/dist/cjs/generate.cjs +16 -285
  11. package/dist/cjs/generate.native.js +18 -432
  12. package/dist/cjs/generate.native.js.map +1 -1
  13. package/dist/esm/generate-helpers.mjs +272 -0
  14. package/dist/esm/generate-helpers.mjs.map +1 -0
  15. package/dist/esm/generate-helpers.native.js +411 -0
  16. package/dist/esm/generate-helpers.native.js.map +1 -0
  17. package/dist/esm/generate-lite.mjs +127 -0
  18. package/dist/esm/generate-lite.mjs.map +1 -0
  19. package/dist/esm/generate-lite.native.js +243 -0
  20. package/dist/esm/generate-lite.native.js.map +1 -0
  21. package/dist/esm/generate-lite.test.mjs +230 -0
  22. package/dist/esm/generate-lite.test.mjs.map +1 -0
  23. package/dist/esm/generate-lite.test.native.js +232 -0
  24. package/dist/esm/generate-lite.test.native.js.map +1 -0
  25. package/dist/esm/generate.mjs +6 -275
  26. package/dist/esm/generate.mjs.map +1 -1
  27. package/dist/esm/generate.native.js +9 -423
  28. package/dist/esm/generate.native.js.map +1 -1
  29. package/package.json +7 -2
  30. package/src/generate-helpers.ts +440 -0
  31. package/src/generate-lite.test.ts +310 -0
  32. package/src/generate-lite.ts +333 -0
  33. package/src/generate.ts +23 -415
  34. package/types/generate-helpers.d.ts +42 -0
  35. package/types/generate-helpers.d.ts.map +1 -0
  36. package/types/generate-lite.d.ts +40 -0
  37. package/types/generate-lite.d.ts.map +1 -0
  38. package/types/generate-lite.test.d.ts +2 -0
  39. package/types/generate-lite.test.d.ts.map +1 -0
  40. package/types/generate.d.ts +1 -6
  41. package/types/generate.d.ts.map +1 -1
@@ -0,0 +1,309 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all) __defProp(target, name, {
7
+ get: all[name],
8
+ enumerable: !0
9
+ });
10
+ },
11
+ __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from == "object" || typeof from == "function") for (let key of __getOwnPropNames(from)) !__hasOwnProp.call(to, key) && key !== except && __defProp(to, key, {
13
+ get: () => from[key],
14
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
+ });
16
+ return to;
17
+ };
18
+ var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
19
+ value: !0
20
+ }), mod);
21
+ var generate_helpers_exports = {};
22
+ __export(generate_helpers_exports, {
23
+ columnTypeToValibot: () => columnTypeToValibot,
24
+ extractValibotExpression: () => extractValibotExpression,
25
+ formatObjectKey: () => formatObjectKey,
26
+ generateGroupedQueriesFile: () => generateGroupedQueriesFile,
27
+ generateModelsFile: () => generateModelsFile,
28
+ generateReadmeFile: () => generateReadmeFile,
29
+ generateSyncedMutationsFile: () => generateSyncedMutationsFile,
30
+ generateSyncedQueriesFile: () => generateSyncedQueriesFile,
31
+ generateTablesFile: () => generateTablesFile,
32
+ generateTypesFile: () => generateTypesFile,
33
+ getModelImportName: () => getModelImportName,
34
+ parseColumnType: () => parseColumnType,
35
+ parseTypeString: () => parseTypeString,
36
+ schemaColumnsToValibot: () => schemaColumnsToValibot,
37
+ shouldSkipObjectKey: () => shouldSkipObjectKey
38
+ });
39
+ module.exports = __toCommonJS(generate_helpers_exports);
40
+ function shouldSkipObjectKey(name) {
41
+ return name.startsWith("__@");
42
+ }
43
+ function formatObjectKey(name) {
44
+ return /^[$A-Z_a-z][$\w]*$/.test(name) ? name : JSON.stringify(name);
45
+ }
46
+ function getModelImportName(name) {
47
+ return name === "user" ? "userPublic" : name;
48
+ }
49
+ function parseTypeString(type) {
50
+ if (type = type.trim(), type === "string") return "v.string()";
51
+ if (type === "number") return "v.number()";
52
+ if (type === "boolean") return "v.boolean()";
53
+ if (type === "void" || type === "undefined") return "v.void_()";
54
+ if (type === "null") return "v.null_()";
55
+ if (type === "any" || type === "unknown") return "v.unknown()";
56
+ if (type.startsWith("{") && type.endsWith("}")) {
57
+ const inner = type.slice(1, -1).trim();
58
+ if (!inner) return "v.object({})";
59
+ const normalized = inner.replace(/\n/g, "; ").replace(/;\s*;/g, ";"),
60
+ entries = [];
61
+ for (const part of normalized.split(";")) {
62
+ const trimmed = part.trim().replace(/,\s*$/, "");
63
+ if (!trimmed) continue;
64
+ const match = trimmed.match(/^(?:readonly\s+)?(\w+)(\?)?:\s*(.+)$/);
65
+ if (!match) continue;
66
+ const [, name, opt, typeStr] = match,
67
+ parsed = parseTypeString(typeStr.trim());
68
+ if (!parsed) return null;
69
+ let val = parsed;
70
+ opt && (val = `v.optional(${val})`), entries.push(`${formatObjectKey(name)}: ${val}`);
71
+ }
72
+ return entries.length === 0 ? "v.object({})" : `v.object({
73
+ ${entries.join(`,
74
+ `)},
75
+ })`;
76
+ }
77
+ if (type.endsWith("[]")) {
78
+ const inner = parseTypeString(type.slice(0, -2).trim());
79
+ return inner ? `v.array(${inner})` : null;
80
+ }
81
+ return null;
82
+ }
83
+ function generateModelsFile(modelNames, modelsDirName) {
84
+ const sorted = [...modelNames].sort(),
85
+ imports = sorted.map(name => `import * as ${getModelImportName(name)} from '../${modelsDirName}/${name}'`).join(`
86
+ `),
87
+ modelsObj = `export const models = {
88
+ ${[...sorted].sort((a, b) => getModelImportName(a).localeCompare(getModelImportName(b))).map(name => ` ${getModelImportName(name)},`).join(`
89
+ `)}
90
+ }`;
91
+ return `// auto-generated by: on-zero generate
92
+ ${imports}
93
+
94
+ ${modelsObj}
95
+ `;
96
+ }
97
+ function generateTypesFile(modelNames) {
98
+ return `import type { TableInsertRow, TableUpdateRow } from 'on-zero'
99
+ import type * as schema from './tables'
100
+
101
+ ${[...modelNames].sort().map(name => {
102
+ const pascalName = name.charAt(0).toUpperCase() + name.slice(1),
103
+ schemaName = getModelImportName(name);
104
+ return `export type ${pascalName} = TableInsertRow<typeof schema.${schemaName}>
105
+ export type ${pascalName}Update = TableUpdateRow<typeof schema.${schemaName}>`;
106
+ }).join(`
107
+
108
+ `)}
109
+ `;
110
+ }
111
+ function generateTablesFile(modelNames, modelsDirName) {
112
+ return `// auto-generated by: on-zero generate
113
+
114
+ ${[...modelNames].sort().map(name => `export { schema as ${getModelImportName(name)} } from '../${modelsDirName}/${name}'`).join(`
115
+ `)}
116
+ `;
117
+ }
118
+ function generateReadmeFile() {
119
+ return `# generated
120
+
121
+ this folder is auto-generated by on-zero. do not edit files here directly.
122
+
123
+ ## what's generated
124
+
125
+ - \`models.ts\` - exports all models from ../models
126
+ - \`types.ts\` - typescript types derived from table schemas
127
+ - \`tables.ts\` - exports table schemas for type inference
128
+ - \`groupedQueries.ts\` - namespaced query re-exports for client setup
129
+ - \`syncedQueries.ts\` - namespaced syncedQuery wrappers for server setup
130
+ - \`syncedMutations.ts\` - valibot validators for mutation args (server auto-validation)
131
+
132
+ ## usage guidelines
133
+
134
+ **do not import generated files outside of the data folder.**
135
+
136
+ ### queries
137
+
138
+ write your queries as plain functions in \`../queries/\` and import them directly:
139
+
140
+ \`\`\`ts
141
+ // \u2705 good - import from queries
142
+ import { channelMessages } from '~/data/queries/message'
143
+ \`\`\`
144
+
145
+ the generated query files are only used internally by zero client/server setup.
146
+
147
+ ### types
148
+
149
+ you can import types from this folder, but prefer re-exporting from \`../types.ts\`:
150
+
151
+ \`\`\`ts
152
+ // \u274C okay but not preferred
153
+ import type { Message } from '~/data/generated/types'
154
+
155
+ // \u2705 better - re-export from types.ts
156
+ import type { Message } from '~/data/types'
157
+ \`\`\`
158
+
159
+ ## regeneration
160
+
161
+ files are regenerated when you run:
162
+
163
+ \`\`\`bash
164
+ bun on-zero generate
165
+ \`\`\`
166
+
167
+ or in watch mode:
168
+
169
+ \`\`\`bash
170
+ bun on-zero generate --watch
171
+ \`\`\`
172
+
173
+ ## more info
174
+
175
+ see the [on-zero readme](./node_modules/on-zero/README.md) for full documentation.
176
+ `;
177
+ }
178
+ function generateGroupedQueriesFile(queries) {
179
+ return `/**
180
+ * auto-generated by: on-zero generate
181
+ *
182
+ * grouped query re-exports for minification-safe query identity.
183
+ * this file re-exports all query modules - while this breaks tree-shaking,
184
+ * queries are typically small and few in number even in larger apps.
185
+ */
186
+ ${[...new Set(queries.map(q => q.sourceFile))].sort().map(file => `export * as ${file} from '../queries/${file}'`).join(`
187
+ `)}
188
+ `;
189
+ }
190
+ function generateSyncedQueriesFile(queries) {
191
+ const queryByFile = /* @__PURE__ */new Map();
192
+ for (const q of queries) queryByFile.has(q.sourceFile) || queryByFile.set(q.sourceFile, []), queryByFile.get(q.sourceFile).push(q);
193
+ const sortedFiles = Array.from(queryByFile.keys()).sort(),
194
+ imports = `// auto-generated by: on-zero generate
195
+ // server-side query definitions with validators
196
+ import { defineQuery, defineQueries } from '@rocicorp/zero'
197
+ import * as v from 'valibot'
198
+ import * as Queries from './groupedQueries'
199
+ `,
200
+ namespaceDefs = sortedFiles.map(file => {
201
+ const queryDefs = queryByFile.get(file).sort((a, b) => a.name.localeCompare(b.name)).map(q => {
202
+ const validatorDef = q.valibotCode.trim();
203
+ if (q.params === "void" || !validatorDef) return ` ${q.name}: defineQuery(() => Queries.${file}.${q.name}()),`;
204
+ const indentedValidator = validatorDef.split(`
205
+ `).map((line, i) => i === 0 ? line : ` ${line}`).join(`
206
+ `);
207
+ return ` ${q.name}: defineQuery(
208
+ ${indentedValidator},
209
+ ({ args }) => Queries.${file}.${q.name}(args)
210
+ ),`;
211
+ }).join(`
212
+ `);
213
+ return `const ${file} = {
214
+ ${queryDefs}
215
+ }`;
216
+ }).join(`
217
+
218
+ `),
219
+ queriesObject = sortedFiles.map(file => ` ${file},`).join(`
220
+ `);
221
+ return `${imports}
222
+ ${namespaceDefs}
223
+
224
+ export const queries = defineQueries({
225
+ ${queriesObject}
226
+ })
227
+ `;
228
+ }
229
+ function columnTypeToValibot(col) {
230
+ let base = "v.string()";
231
+ switch (col.type) {
232
+ case "string":
233
+ base = "v.string()";
234
+ break;
235
+ case "number":
236
+ base = "v.number()";
237
+ break;
238
+ case "boolean":
239
+ base = "v.boolean()";
240
+ break;
241
+ case "json":
242
+ base = "v.unknown()";
243
+ break;
244
+ case "enum":
245
+ base = "v.string()";
246
+ break;
247
+ }
248
+ return col.optional ? `v.optional(v.nullable(${base}))` : base;
249
+ }
250
+ function schemaColumnsToValibot(columns, primaryKeys, mode) {
251
+ const entries = [];
252
+ if (mode === "delete") for (const pk of primaryKeys) {
253
+ const col = columns[pk];
254
+ col && entries.push(`${formatObjectKey(pk)}: ${columnTypeToValibot({
255
+ ...col,
256
+ optional: !1
257
+ })}`);
258
+ } else if (mode === "update") for (const [name, col] of Object.entries(columns)) primaryKeys.includes(name) ? entries.push(`${formatObjectKey(name)}: ${columnTypeToValibot({
259
+ ...col,
260
+ optional: !1
261
+ })}`) : entries.push(`${formatObjectKey(name)}: ${columnTypeToValibot({
262
+ ...col,
263
+ optional: !0
264
+ })}`);else for (const [name, col] of Object.entries(columns)) entries.push(`${formatObjectKey(name)}: ${columnTypeToValibot(col)}`);
265
+ return `v.object({
266
+ ${entries.join(`,
267
+ `)},
268
+ })`;
269
+ }
270
+ function extractValibotExpression(valibotCode) {
271
+ return valibotCode.trim() || "v.unknown()";
272
+ }
273
+ function parseColumnType(initText) {
274
+ const optional = initText.includes(".optional()");
275
+ let type = "string";
276
+ return initText.startsWith("number(") ? type = "number" : initText.startsWith("boolean(") ? type = "boolean" : initText.startsWith("json(") || initText.startsWith("json<") ? type = "json" : initText.startsWith("enumeration(") && (type = "enum"), {
277
+ type,
278
+ optional,
279
+ customType: void 0
280
+ };
281
+ }
282
+ function generateSyncedMutationsFile(modelMutations) {
283
+ return `// auto-generated by: on-zero generate
284
+ // mutation validators derived from model schemas and handler types
285
+ import * as v from 'valibot'
286
+
287
+ export const mutationValidators = {
288
+ ${[...modelMutations].sort((a, b) => a.modelName.localeCompare(b.modelName)).map(model => {
289
+ const entries = [];
290
+ if (model.hasCRUD && Object.keys(model.columns).length > 0) for (const mode of ["insert", "update", "delete"]) if (model.custom.some(m => m.name === mode)) {
291
+ const customMut = model.custom.find(m => m.name === mode);
292
+ customMut.valibotCode ? entries.push(` ${mode}: ${extractValibotExpression(customMut.valibotCode)},`) : entries.push(` ${mode}: ${schemaColumnsToValibot(model.columns, model.primaryKeys, mode)},`);
293
+ } else entries.push(` ${mode}: ${schemaColumnsToValibot(model.columns, model.primaryKeys, mode)},`);
294
+ for (const mut of model.custom) if (!(model.hasCRUD && ["insert", "update", "delete", "upsert"].includes(mut.name))) {
295
+ if (mut.paramType === "void" || !mut.valibotCode) {
296
+ entries.push(` ${mut.name}: v.void_(),`);
297
+ continue;
298
+ }
299
+ entries.push(` ${mut.name}: ${extractValibotExpression(mut.valibotCode)},`);
300
+ }
301
+ return ` ${model.modelName}: {
302
+ ${entries.join(`
303
+ `)}
304
+ },`;
305
+ }).join(`
306
+ `)}
307
+ }
308
+ `;
309
+ }