on-zero 0.4.1 → 0.4.2
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.
- package/dist/cjs/generate-helpers.cjs +309 -0
- package/dist/cjs/generate-helpers.native.js +451 -0
- package/dist/cjs/generate-helpers.native.js.map +1 -0
- package/dist/cjs/generate-lite.cjs +150 -0
- package/dist/cjs/generate-lite.native.js +269 -0
- package/dist/cjs/generate-lite.native.js.map +1 -0
- package/dist/cjs/generate-lite.test.cjs +229 -0
- package/dist/cjs/generate-lite.test.native.js +234 -0
- package/dist/cjs/generate-lite.test.native.js.map +1 -0
- package/dist/cjs/generate.cjs +16 -285
- package/dist/cjs/generate.native.js +18 -432
- package/dist/cjs/generate.native.js.map +1 -1
- package/dist/esm/generate-helpers.mjs +272 -0
- package/dist/esm/generate-helpers.mjs.map +1 -0
- package/dist/esm/generate-helpers.native.js +411 -0
- package/dist/esm/generate-helpers.native.js.map +1 -0
- package/dist/esm/generate-lite.mjs +127 -0
- package/dist/esm/generate-lite.mjs.map +1 -0
- package/dist/esm/generate-lite.native.js +243 -0
- package/dist/esm/generate-lite.native.js.map +1 -0
- package/dist/esm/generate-lite.test.mjs +230 -0
- package/dist/esm/generate-lite.test.mjs.map +1 -0
- package/dist/esm/generate-lite.test.native.js +232 -0
- package/dist/esm/generate-lite.test.native.js.map +1 -0
- package/dist/esm/generate.mjs +6 -275
- package/dist/esm/generate.mjs.map +1 -1
- package/dist/esm/generate.native.js +9 -423
- package/dist/esm/generate.native.js.map +1 -1
- package/package.json +7 -2
- package/src/generate-helpers.ts +440 -0
- package/src/generate-lite.test.ts +310 -0
- package/src/generate-lite.ts +333 -0
- package/src/generate.ts +23 -415
- package/types/generate-helpers.d.ts +42 -0
- package/types/generate-helpers.d.ts.map +1 -0
- package/types/generate-lite.d.ts +40 -0
- package/types/generate-lite.d.ts.map +1 -0
- package/types/generate-lite.test.d.ts +2 -0
- package/types/generate-lite.test.d.ts.map +1 -0
- package/types/generate.d.ts +1 -6
- 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
|
+
}
|