on-zero 0.16.17 → 0.16.19

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 (110) hide show
  1. package/dist/cjs/createZeroClient.cjs +44 -11
  2. package/dist/cjs/createZeroClient.native.js +54 -17
  3. package/dist/cjs/createZeroClient.native.js.map +1 -1
  4. package/dist/cjs/createZeroClient.recovery.test.cjs +49 -0
  5. package/dist/cjs/createZeroClient.recovery.test.native.js +53 -0
  6. package/dist/cjs/createZeroClient.recovery.test.native.js.map +1 -1
  7. package/dist/cjs/generate-helpers.cjs +28 -0
  8. package/dist/cjs/generate-helpers.native.js +36 -0
  9. package/dist/cjs/generate-helpers.native.js.map +1 -1
  10. package/dist/cjs/generate-layout.cjs +62 -53
  11. package/dist/cjs/generate-layout.native.js +62 -54
  12. package/dist/cjs/generate-layout.native.js.map +1 -1
  13. package/dist/cjs/generate-lite.cjs +28 -0
  14. package/dist/cjs/generate-lite.native.js +89 -33
  15. package/dist/cjs/generate-lite.native.js.map +1 -1
  16. package/dist/cjs/generate-lite.test.cjs +46 -0
  17. package/dist/cjs/generate-lite.test.native.js +46 -0
  18. package/dist/cjs/generate-lite.test.native.js.map +1 -1
  19. package/dist/cjs/generate.cjs +237 -243
  20. package/dist/cjs/generate.native.js +352 -353
  21. package/dist/cjs/generate.native.js.map +1 -1
  22. package/dist/cjs/generate.test.cjs +35 -0
  23. package/dist/cjs/generate.test.native.js +35 -0
  24. package/dist/cjs/generate.test.native.js.map +1 -1
  25. package/dist/cjs/helpers/createMutators.cjs +2 -1
  26. package/dist/cjs/helpers/createMutators.native.js +3 -1
  27. package/dist/cjs/helpers/createMutators.native.js.map +1 -1
  28. package/dist/cjs/helpers/recoverZeroClient.cjs +8 -2
  29. package/dist/cjs/helpers/recoverZeroClient.native.js +10 -2
  30. package/dist/cjs/helpers/recoverZeroClient.native.js.map +1 -1
  31. package/dist/cjs/helpers/recoverZeroClient.test.cjs +53 -0
  32. package/dist/cjs/helpers/recoverZeroClient.test.native.js +55 -0
  33. package/dist/cjs/helpers/recoverZeroClient.test.native.js.map +1 -1
  34. package/dist/cjs/native-typescript.cjs +190 -0
  35. package/dist/cjs/native-typescript.native.js +255 -0
  36. package/dist/cjs/native-typescript.native.js.map +1 -0
  37. package/dist/esm/createZeroClient.mjs +44 -11
  38. package/dist/esm/createZeroClient.mjs.map +1 -1
  39. package/dist/esm/createZeroClient.native.js +54 -17
  40. package/dist/esm/createZeroClient.native.js.map +1 -1
  41. package/dist/esm/createZeroClient.recovery.test.mjs +50 -1
  42. package/dist/esm/createZeroClient.recovery.test.mjs.map +1 -1
  43. package/dist/esm/createZeroClient.recovery.test.native.js +54 -1
  44. package/dist/esm/createZeroClient.recovery.test.native.js.map +1 -1
  45. package/dist/esm/generate-helpers.mjs +27 -1
  46. package/dist/esm/generate-helpers.mjs.map +1 -1
  47. package/dist/esm/generate-helpers.native.js +35 -1
  48. package/dist/esm/generate-helpers.native.js.map +1 -1
  49. package/dist/esm/generate-layout.mjs +52 -54
  50. package/dist/esm/generate-layout.mjs.map +1 -1
  51. package/dist/esm/generate-layout.native.js +52 -55
  52. package/dist/esm/generate-layout.native.js.map +1 -1
  53. package/dist/esm/generate-lite.mjs +29 -1
  54. package/dist/esm/generate-lite.mjs.map +1 -1
  55. package/dist/esm/generate-lite.native.js +90 -34
  56. package/dist/esm/generate-lite.native.js.map +1 -1
  57. package/dist/esm/generate-lite.test.mjs +46 -0
  58. package/dist/esm/generate-lite.test.mjs.map +1 -1
  59. package/dist/esm/generate-lite.test.native.js +46 -0
  60. package/dist/esm/generate-lite.test.native.js.map +1 -1
  61. package/dist/esm/generate.mjs +238 -244
  62. package/dist/esm/generate.mjs.map +1 -1
  63. package/dist/esm/generate.native.js +353 -354
  64. package/dist/esm/generate.native.js.map +1 -1
  65. package/dist/esm/generate.test.mjs +35 -0
  66. package/dist/esm/generate.test.mjs.map +1 -1
  67. package/dist/esm/generate.test.native.js +35 -0
  68. package/dist/esm/generate.test.native.js.map +1 -1
  69. package/dist/esm/helpers/createMutators.mjs +2 -1
  70. package/dist/esm/helpers/createMutators.mjs.map +1 -1
  71. package/dist/esm/helpers/createMutators.native.js +3 -1
  72. package/dist/esm/helpers/createMutators.native.js.map +1 -1
  73. package/dist/esm/helpers/recoverZeroClient.mjs +8 -2
  74. package/dist/esm/helpers/recoverZeroClient.mjs.map +1 -1
  75. package/dist/esm/helpers/recoverZeroClient.native.js +10 -2
  76. package/dist/esm/helpers/recoverZeroClient.native.js.map +1 -1
  77. package/dist/esm/helpers/recoverZeroClient.test.mjs +53 -0
  78. package/dist/esm/helpers/recoverZeroClient.test.mjs.map +1 -1
  79. package/dist/esm/helpers/recoverZeroClient.test.native.js +55 -0
  80. package/dist/esm/helpers/recoverZeroClient.test.native.js.map +1 -1
  81. package/dist/esm/index.native.js.map +1 -1
  82. package/dist/esm/native-typescript.mjs +164 -0
  83. package/dist/esm/native-typescript.mjs.map +1 -0
  84. package/dist/esm/native-typescript.native.js +226 -0
  85. package/dist/esm/native-typescript.native.js.map +1 -0
  86. package/package.json +4 -3
  87. package/src/createZeroClient.recovery.test.tsx +56 -1
  88. package/src/createZeroClient.tsx +54 -15
  89. package/src/generate-helpers.ts +79 -0
  90. package/src/generate-layout.ts +64 -108
  91. package/src/generate-lite.test.ts +49 -0
  92. package/src/generate-lite.ts +44 -0
  93. package/src/generate.test.ts +52 -0
  94. package/src/generate.ts +313 -366
  95. package/src/helpers/createMutators.ts +14 -3
  96. package/src/helpers/recoverZeroClient.test.ts +50 -0
  97. package/src/helpers/recoverZeroClient.ts +12 -2
  98. package/src/native-typescript.ts +176 -0
  99. package/types/createZeroClient.d.ts.map +1 -1
  100. package/types/generate-helpers.d.ts +13 -0
  101. package/types/generate-helpers.d.ts.map +1 -1
  102. package/types/generate-layout.d.ts +2 -1
  103. package/types/generate-layout.d.ts.map +1 -1
  104. package/types/generate-lite.d.ts +1 -0
  105. package/types/generate-lite.d.ts.map +1 -1
  106. package/types/generate.d.ts.map +1 -1
  107. package/types/helpers/createMutators.d.ts.map +1 -1
  108. package/types/helpers/recoverZeroClient.d.ts.map +1 -1
  109. package/types/native-typescript.d.ts +11 -0
  110. package/types/native-typescript.d.ts.map +1 -0
@@ -43,10 +43,13 @@ module.exports = __toCommonJS(generate_exports);
43
43
  var import_node_crypto = require("node:crypto");
44
44
  var import_node_fs = require("node:fs");
45
45
  var import_node_path = require("node:path");
46
+ var ts = __toESM(require("typescript/unstable/ast"), 1);
47
+ var import_async = require("typescript/unstable/async");
46
48
  var import_generate_helpers = require("./generate-helpers.cjs");
47
49
  var import_generate_layout = require("./generate-layout.cjs");
50
+ var import_native_typescript = require("./native-typescript.cjs");
48
51
  const hash = s => (0, import_node_crypto.createHash)("sha256").update(s).digest("hex");
49
- const GENERATOR_CACHE_VERSION = "6";
52
+ const GENERATOR_CACHE_VERSION = "7";
50
53
  const isGeneratorSourceFile = name => name.endsWith(".ts") && !name.endsWith(".d.ts") && !name.endsWith(".test.ts") && !name.endsWith(".spec.ts");
51
54
  function hashInputTree(sourceRoots, generatedDir) {
52
55
  const parts = [];
@@ -107,72 +110,37 @@ function saveCache() {
107
110
  }
108
111
  function writeFileIfChanged(filePath, content) {
109
112
  const contentHash = hash(content);
110
- const cachedHash = generateCache[filePath];
111
- if (cachedHash === contentHash && (0, import_node_fs.existsSync)(filePath)) {
113
+ let onDisk = null;
114
+ try {
115
+ onDisk = (0, import_node_fs.readFileSync)(filePath, "utf-8");
116
+ } catch {}
117
+ if (onDisk !== null && hash(onDisk) === contentHash) {
118
+ generateCache[filePath] = contentHash;
112
119
  return false;
113
120
  }
114
121
  (0, import_node_fs.writeFileSync)(filePath, content, "utf-8");
115
122
  generateCache[filePath] = contentHash;
116
123
  return true;
117
124
  }
118
- function createTypeResolver(ts, files, dir) {
119
- const configPath = ts.findConfigFile(dir, ts.sys.fileExists, "tsconfig.json");
120
- let compilerOptions = {
121
- target: ts.ScriptTarget.Latest,
122
- module: ts.ModuleKind.ESNext,
123
- moduleResolution: ts.ModuleResolutionKind.Bundler,
124
- strict: false,
125
- skipLibCheck: true,
126
- noEmit: true
127
- };
128
- if (configPath) {
129
- const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
130
- if (configFile.config) {
131
- const parsed = ts.parseJsonConfigFileContent(configFile.config, ts.sys, (0, import_node_path.dirname)(configPath));
132
- compilerOptions = {
133
- ...compilerOptions,
134
- ...parsed.options
135
- };
136
- }
137
- }
138
- const fileMap = /* @__PURE__ */new Map();
139
- for (const f of files) {
140
- fileMap.set(f.path, f.content);
141
- }
142
- const host = ts.createCompilerHost(compilerOptions);
143
- const originalGetSourceFile = host.getSourceFile.bind(host);
144
- host.getSourceFile = (fileName, languageVersion, onError) => {
145
- const content = fileMap.get(fileName);
146
- if (content !== void 0) {
147
- return ts.createSourceFile(fileName, content, languageVersion, true);
148
- }
149
- return originalGetSourceFile(fileName, languageVersion, onError);
150
- };
151
- host.fileExists = fileName => fileMap.has(fileName) || ts.sys.fileExists(fileName);
152
- host.readFile = fileName => fileMap.get(fileName) ?? ts.sys.readFile(fileName);
153
- const program = ts.createProgram(files.map(f => f.path), compilerOptions, host);
154
- const checker = program.getTypeChecker();
125
+ function createTypeResolver(project) {
155
126
  return {
156
- program,
157
- checker,
158
- // resolve a type annotation node to a ts.Type
159
- resolveType(node) {
127
+ project,
128
+ async resolveType(node) {
160
129
  try {
161
- return checker.getTypeFromTypeNode(node);
130
+ return (await project.checker.getTypeFromTypeNode(node)) ?? null;
162
131
  } catch {
163
132
  return null;
164
133
  }
165
134
  },
166
- // convert a resolved type to valibot code
167
- typeToValibot(type) {
168
- return tsTypeToValibot(ts, checker, type);
135
+ async typeToValibot(type) {
136
+ return tsTypeToValibot(project, type);
169
137
  }
170
138
  };
171
139
  }
172
- function resolveParamType(ts, resolver, sourceFile, exportName, paramIndex) {
173
- let result = null;
174
- ts.forEachChild(sourceFile, node => {
175
- if (result) return;
140
+ async function resolveParamType(resolver, sourceFile, exportName, paramIndex) {
141
+ let typeNode = null;
142
+ sourceFile.forEachChild(node => {
143
+ if (typeNode) return;
176
144
  if (!ts.isVariableStatement(node)) return;
177
145
  if (!node.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword)) return;
178
146
  const decl = node.declarationList.declarations[0];
@@ -181,13 +149,13 @@ function resolveParamType(ts, resolver, sourceFile, exportName, paramIndex) {
181
149
  if (decl.initializer && ts.isArrowFunction(decl.initializer)) {
182
150
  const param = decl.initializer.parameters[paramIndex];
183
151
  if (param?.type) {
184
- result = resolver.resolveType(param.type);
152
+ typeNode = param.type;
185
153
  }
186
154
  }
187
155
  });
188
- return result;
156
+ return typeNode ? resolver.resolveType(typeNode) : null;
189
157
  }
190
- function readMutationsCall(ts, call) {
158
+ function readMutationsCall(call) {
191
159
  const args = call.arguments;
192
160
  if (args.length < 2) {
193
161
  const only = args[0];
@@ -213,9 +181,9 @@ function readMutationsCall(ts, call) {
213
181
  handlersArg: handlers && ts.isObjectLiteralExpression(handlers) ? handlers : null
214
182
  };
215
183
  }
216
- function resolveMutationParamTypes(ts, resolver, sourceFile) {
217
- const resolved = /* @__PURE__ */new Map();
218
- ts.forEachChild(sourceFile, node => {
184
+ async function resolveMutationParamTypes(resolver, sourceFile) {
185
+ const nodes = /* @__PURE__ */new Map();
186
+ sourceFile.forEachChild(node => {
219
187
  if (!ts.isVariableStatement(node)) return;
220
188
  if (!node.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword)) return;
221
189
  const decl = node.declarationList.declarations[0];
@@ -224,7 +192,7 @@ function resolveMutationParamTypes(ts, resolver, sourceFile) {
224
192
  if (!decl.initializer || !ts.isCallExpression(decl.initializer)) return;
225
193
  const {
226
194
  handlersArg
227
- } = readMutationsCall(ts, decl.initializer);
195
+ } = readMutationsCall(decl.initializer);
228
196
  if (!handlersArg) return;
229
197
  for (const prop of handlersArg.properties) {
230
198
  if (!ts.isPropertyAssignment(prop) && !ts.isMethodDeclaration(prop)) continue;
@@ -242,17 +210,19 @@ function resolveMutationParamTypes(ts, resolver, sourceFile) {
242
210
  if (!params || params.length < 2) continue;
243
211
  const typeNode = params[1].type;
244
212
  if (!typeNode) continue;
245
- const expanded = resolver.resolveType(typeNode);
246
- if (expanded) {
247
- resolved.set(name, expanded);
248
- }
213
+ nodes.set(name, typeNode);
249
214
  }
250
215
  });
216
+ const resolved = /* @__PURE__ */new Map();
217
+ for (const [name, node] of nodes) {
218
+ const expanded = await resolver.resolveType(node);
219
+ if (expanded) resolved.set(name, expanded);
220
+ }
251
221
  return resolved;
252
222
  }
253
- function extractMutationsFromModel(ts, sourceFile, content, fileName, silent, typeToValibot, resolvedTypes, resolvedTypeToValibot) {
223
+ function extractMutationsFromModel(sourceFile, content, fileName, silent, typeToValibot, resolvedValibot) {
254
224
  let mutateNode = null;
255
- ts.forEachChild(sourceFile, node => {
225
+ sourceFile.forEachChild(node => {
256
226
  if (!ts.isVariableStatement(node)) return;
257
227
  if (!node.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword)) return;
258
228
  const decl = node.declarationList.declarations[0];
@@ -276,12 +246,12 @@ function extractMutationsFromModel(ts, sourceFile, content, fileName, silent, ty
276
246
  hasTable,
277
247
  crud,
278
248
  handlersArg
279
- } = readMutationsCall(ts, call);
249
+ } = readMutationsCall(call);
280
250
  const hasCRUD = hasTable && crud;
281
251
  const columns = {};
282
252
  const primaryKeys = [];
283
253
  if (hasCRUD) {
284
- extractSchemaColumns(ts, sourceFile, columns, primaryKeys);
254
+ extractSchemaColumns(sourceFile, columns, primaryKeys);
285
255
  }
286
256
  const custom = [];
287
257
  if (handlersArg) {
@@ -318,11 +288,8 @@ function extractMutationsFromModel(ts, sourceFile, content, fileName, silent, ty
318
288
  continue;
319
289
  }
320
290
  let valibotCode = typeToValibot(paramType);
321
- if (!valibotCode && resolvedTypes && resolvedTypeToValibot) {
322
- const resolvedType = resolvedTypes.get(name);
323
- if (resolvedType) {
324
- valibotCode = resolvedTypeToValibot(resolvedType);
325
- }
291
+ if (!valibotCode && resolvedValibot) {
292
+ valibotCode = resolvedValibot.get(name) ?? null;
326
293
  }
327
294
  custom.push({
328
295
  name,
@@ -339,7 +306,7 @@ function extractMutationsFromModel(ts, sourceFile, content, fileName, silent, ty
339
306
  custom
340
307
  };
341
308
  }
342
- function extractSchemaColumns(ts, sourceFile, columns, primaryKeys) {
309
+ function extractSchemaColumns(sourceFile, columns, primaryKeys) {
343
310
  function visit(node) {
344
311
  if (ts.isCallExpression(node)) {
345
312
  const text = node.expression.getText(sourceFile);
@@ -364,80 +331,75 @@ function extractSchemaColumns(ts, sourceFile, columns, primaryKeys) {
364
331
  }
365
332
  }
366
333
  }
367
- ts.forEachChild(node, visit);
334
+ node.forEachChild(visit);
368
335
  }
369
336
  visit(sourceFile);
370
337
  }
371
- function getInstantiatedPropertyType(checker, parent, name) {
372
- const resolveProperty = Reflect.get(checker, "getTypeOfPropertyOfType");
373
- if (typeof resolveProperty !== "function") return void 0;
374
- return Reflect.apply(resolveProperty, checker, [parent, name]);
375
- }
376
- function tsTypeToValibot(ts, checker, type, seen) {
377
- if (!seen) seen = /* @__PURE__ */new Set();
378
- const flags = type.getFlags();
379
- if (flags & (ts.TypeFlags.Object | ts.TypeFlags.Intersection)) {
380
- if (seen.has(type)) return "v.unknown()";
381
- seen.add(type);
382
- }
383
- const recurse = t => tsTypeToValibot(ts, checker, t, seen);
384
- if (flags & ts.TypeFlags.String) return "v.string()";
385
- if (flags & ts.TypeFlags.Number) return "v.number()";
386
- if (flags & ts.TypeFlags.Boolean) return "v.boolean()";
387
- if (flags & ts.TypeFlags.Void || flags & ts.TypeFlags.Undefined) return "v.void_()";
388
- if (flags & ts.TypeFlags.Null) return "v.null_()";
389
- if (flags & ts.TypeFlags.Any || flags & ts.TypeFlags.Unknown) return "v.unknown()";
390
- if (flags & ts.TypeFlags.Never) return "v.never()";
391
- if (flags & ts.TypeFlags.TemplateLiteral) return "v.string()";
392
- if (flags & ts.TypeFlags.Object && (checker.isArrayType?.(type) || checker.isArrayLikeType?.(type))) {
393
- const typeArgs = checker.getTypeArguments?.(type) ?? [];
394
- const elementType = typeArgs.length === 1 ? typeArgs[0] : type.getNumberIndexType?.();
395
- return `v.array(${elementType ? recurse(elementType) : "v.unknown()"})`;
396
- }
397
- if (flags & ts.TypeFlags.StringLiteral) {
338
+ async function tsTypeToValibot(project, type, seen = /* @__PURE__ */new Set()) {
339
+ const checker = project.checker;
340
+ const flags = type.flags;
341
+ if (flags & (import_async.TypeFlags.Object | import_async.TypeFlags.Intersection)) {
342
+ if (seen.has(type.id)) return "v.unknown()";
343
+ seen.add(type.id);
344
+ }
345
+ const recurse = value => tsTypeToValibot(project, value, seen);
346
+ if (flags & import_async.TypeFlags.String) return "v.string()";
347
+ if (flags & import_async.TypeFlags.Number) return "v.number()";
348
+ if (flags & import_async.TypeFlags.Boolean) return "v.boolean()";
349
+ if (flags & import_async.TypeFlags.Void || flags & import_async.TypeFlags.Undefined) return "v.void_()";
350
+ if (flags & import_async.TypeFlags.Null) return "v.null_()";
351
+ if (flags & import_async.TypeFlags.Any || flags & import_async.TypeFlags.Unknown) return "v.unknown()";
352
+ if (flags & import_async.TypeFlags.Never) return "v.never()";
353
+ if (flags & import_async.TypeFlags.TemplateLiteral) return "v.string()";
354
+ if (flags & import_async.TypeFlags.Object && (await checker.isArrayLikeType(type))) {
355
+ const typeArgs = type.isTypeReference() ? await checker.getTypeArguments(type) : [];
356
+ const indexInfo = (await checker.getIndexInfosOfType(type)).find(info => Boolean(info.keyType.flags & import_async.TypeFlags.Number));
357
+ const elementType = typeArgs.length === 1 ? typeArgs[0] : indexInfo?.valueType;
358
+ return `v.array(${elementType ? await recurse(elementType) : "v.unknown()"})`;
359
+ }
360
+ if (type.isStringLiteralType()) {
398
361
  return `v.literal(${JSON.stringify(type.value)})`;
399
362
  }
400
- if (flags & ts.TypeFlags.NumberLiteral) {
363
+ if (type.isNumberLiteralType()) {
401
364
  return `v.literal(${type.value})`;
402
365
  }
403
- if (flags & ts.TypeFlags.BooleanLiteral) {
404
- const name = type.intrinsicName;
405
- return `v.literal(${name === "true"})`;
366
+ if (type.isBooleanLiteralType()) {
367
+ return `v.literal(${type.value})`;
406
368
  }
407
- if (type.isUnion()) {
408
- const members = type.types;
409
- const hasNull = members.some(t => t.getFlags() & ts.TypeFlags.Null);
410
- const hasUndefined = members.some(t => t.getFlags() & (ts.TypeFlags.Undefined | ts.TypeFlags.Void));
411
- const rest = members.filter(t => !(t.getFlags() & (ts.TypeFlags.Null | ts.TypeFlags.Undefined | ts.TypeFlags.Void)));
412
- if (rest.length === 2 && rest.every(t => t.getFlags() & ts.TypeFlags.BooleanLiteral)) {
369
+ if (type.isUnionType()) {
370
+ const members = await type.getTypes();
371
+ const hasNull = members.some(member => member.flags & import_async.TypeFlags.Null);
372
+ const hasUndefined = members.some(member => member.flags & (import_async.TypeFlags.Undefined | import_async.TypeFlags.Void));
373
+ const rest = members.filter(member => !(member.flags & (import_async.TypeFlags.Null | import_async.TypeFlags.Undefined | import_async.TypeFlags.Void)));
374
+ if (rest.length === 2 && rest.every(member => member.flags & import_async.TypeFlags.BooleanLiteral)) {
413
375
  let inner2 = "v.boolean()";
414
376
  if (hasNull) inner2 = `v.nullable(${inner2})`;
415
377
  if (hasUndefined) inner2 = `v.optional(${inner2})`;
416
378
  return inner2;
417
379
  }
418
380
  if (rest.length === 0) return "v.unknown()";
419
- let inner = rest.length === 1 ? recurse(rest[0]) : `v.union([${rest.map(t => recurse(t)).join(", ")}])`;
381
+ let inner = rest.length === 1 ? await recurse(rest[0]) : `v.union([${(await Promise.all(rest.map(recurse))).join(", ")}])`;
420
382
  if (hasNull) inner = `v.nullable(${inner})`;
421
383
  if (hasUndefined) inner = `v.optional(${inner})`;
422
384
  return inner;
423
385
  }
424
- const resolveSymbolType = (parent, prop) => {
425
- const instantiated = getInstantiatedPropertyType(checker, parent, prop.getName());
426
- if (instantiated) return instantiated;
427
- if (prop.valueDeclaration) return checker.getTypeOfSymbolAtLocation(prop, prop.valueDeclaration);
428
- if (prop.declarations?.[0]) return checker.getTypeOfSymbolAtLocation(prop, prop.declarations[0]);
429
- return checker.getDeclaredTypeOfSymbol(prop);
386
+ const resolveSymbolType = async property => {
387
+ const symbolType = await checker.getTypeOfSymbol(property);
388
+ if (symbolType && !symbolType.isErrorType()) return symbolType;
389
+ const declaration = property.valueDeclaration ?? property.declarations[0];
390
+ const node = declaration ? await declaration.resolve(project) : void 0;
391
+ if (node) return checker.getTypeOfSymbolAtLocation(property, node);
392
+ return checker.getDeclaredTypeOfSymbol(property);
430
393
  };
431
- if (type.isIntersection()) {
432
- const props2 = type.getProperties();
433
- if (props2.length === 0) return "v.object({})";
394
+ const objectToValibot = async props => {
395
+ if (props.length === 0) return "v.object({})";
434
396
  const entries = [];
435
- for (const prop of props2) {
436
- const name = prop.getName();
397
+ for (const prop of props) {
398
+ const name = prop.name;
437
399
  if ((0, import_generate_helpers.shouldSkipObjectKey)(name)) continue;
438
- const propType = resolveSymbolType(type, prop);
439
- const isOptional = !!(prop.getFlags() & ts.SymbolFlags.Optional);
440
- let val = recurse(propType);
400
+ const propType = await resolveSymbolType(prop);
401
+ const isOptional = Boolean(prop.flags & import_async.SymbolFlags.Optional);
402
+ let val = await recurse(propType);
441
403
  if (isOptional && !val.startsWith("v.optional(")) {
442
404
  val = `v.optional(${val})`;
443
405
  }
@@ -447,47 +409,32 @@ function tsTypeToValibot(ts, checker, type, seen) {
447
409
  return `v.object({
448
410
  ${entries.join(",\n ")},
449
411
  })`;
450
- }
451
- const props = type.getProperties();
452
- if (props.length > 0 && (type.getFlags() & ts.TypeFlags.Object || type.objectFlags)) {
453
- const objectFlags = type.objectFlags ?? 0;
454
- if (objectFlags & ts.ObjectFlags.Reference) {
455
- const typeRef = type;
456
- const symbol = type.getSymbol();
457
- const name = symbol?.getName();
458
- if ((name === "Array" || name === "ReadonlyArray") && typeRef.typeArguments?.length === 1) {
459
- return `v.array(${recurse(typeRef.typeArguments[0])})`;
412
+ };
413
+ if (type.isIntersectionType()) {
414
+ return objectToValibot(await checker.getPropertiesOfType(type));
415
+ }
416
+ if (type.isTupleType()) {
417
+ const typeArgs = await checker.getTypeArguments(type);
418
+ return `v.tuple([${(await Promise.all(typeArgs.map(recurse))).join(", ")}])`;
419
+ }
420
+ if (type.isObjectType()) {
421
+ if (type.objectFlags & import_async.ObjectFlags.Reference && type.isTypeReference()) {
422
+ const symbol = await type.getSymbol();
423
+ const typeArgs = await checker.getTypeArguments(type);
424
+ if ((symbol?.name === "Array" || symbol?.name === "ReadonlyArray") && typeArgs.length === 1) {
425
+ return `v.array(${await recurse(typeArgs[0])})`;
460
426
  }
461
427
  }
462
- if (objectFlags & ts.ObjectFlags.Tuple) {
463
- const typeRef = type;
464
- const typeArgs = typeRef.typeArguments || [];
465
- return `v.tuple([${typeArgs.map(t => recurse(t)).join(", ")}])`;
428
+ const props = await checker.getPropertiesOfType(type);
429
+ if (props.length > 0) return objectToValibot(props);
430
+ }
431
+ for (const indexInfo of await checker.getIndexInfosOfType(type)) {
432
+ if (indexInfo.keyType.flags & import_async.TypeFlags.String) {
433
+ return `v.record(v.string(), ${await recurse(indexInfo.valueType)})`;
466
434
  }
467
- const entries = [];
468
- for (const prop of props) {
469
- const name = prop.getName();
470
- if ((0, import_generate_helpers.shouldSkipObjectKey)(name)) continue;
471
- const propType = resolveSymbolType(type, prop);
472
- const isOptional = !!(prop.getFlags() & ts.SymbolFlags.Optional);
473
- let val = recurse(propType);
474
- if (isOptional && !val.startsWith("v.optional(")) {
475
- val = `v.optional(${val})`;
476
- }
477
- entries.push(`${(0, import_generate_helpers.formatObjectKey)(name)}: ${val}`);
435
+ if (indexInfo.keyType.flags & import_async.TypeFlags.Number) {
436
+ return `v.record(v.number(), ${await recurse(indexInfo.valueType)})`;
478
437
  }
479
- if (entries.length === 0) return "v.object({})";
480
- return `v.object({
481
- ${entries.join(",\n ")},
482
- })`;
483
- }
484
- const stringIndex = type.getStringIndexType();
485
- if (stringIndex) {
486
- return `v.record(v.string(), ${recurse(stringIndex)})`;
487
- }
488
- const numberIndex = type.getNumberIndexType();
489
- if (numberIndex) {
490
- return `v.record(v.number(), ${recurse(numberIndex)})`;
491
438
  }
492
439
  return "v.unknown()";
493
440
  }
@@ -578,24 +525,41 @@ function dataMembershipFromLayout(layout) {
578
525
  allTables: [...new Set(layout.instances.flatMap(instance => [...instance.syncTables, ...instance.supportTables]))].sort()
579
526
  };
580
527
  }
528
+ async function loadGeneratorProject(baseDir) {
529
+ const sourcePaths = (0, import_native_typescript.collectTypeScriptSourcePaths)([baseDir, (0, import_node_path.resolve)((0, import_node_path.dirname)(baseDir), "database")]);
530
+ return (0, import_native_typescript.createNativeTypeScriptProject)(baseDir, sourcePaths);
531
+ }
581
532
  async function deriveDataMembership(options) {
582
- const ts = await import("typescript");
583
- const layout = (0, import_generate_layout.discoverDataLayout)(ts, (0, import_node_path.resolve)(options.dir), options.config ? (0, import_node_path.resolve)(options.config) : void 0);
584
- return dataMembershipFromLayout(layout);
533
+ const baseDir = (0, import_node_path.resolve)(options.dir);
534
+ const project = await loadGeneratorProject(baseDir);
535
+ try {
536
+ const layout = (0, import_generate_layout.discoverDataLayout)(project, baseDir, options.config ? (0, import_node_path.resolve)(options.config) : void 0);
537
+ return dataMembershipFromLayout(layout);
538
+ } finally {
539
+ await project.close();
540
+ }
585
541
  }
586
542
  async function generateDrizzleSchemaInputFile(options) {
587
- const ts = await import("typescript");
588
543
  const baseDir = (0, import_node_path.resolve)(options.dir);
589
- const layout = (0, import_generate_layout.discoverDataLayout)(ts, baseDir, options.config ? (0, import_node_path.resolve)(options.config) : void 0);
544
+ const project = await loadGeneratorProject(baseDir);
545
+ try {
546
+ return generateDrizzleSchemaInputFileWithProject(options, project);
547
+ } finally {
548
+ await project.close();
549
+ }
550
+ }
551
+ async function generateDrizzleSchemaInputFileWithProject(options, project) {
552
+ const baseDir = (0, import_node_path.resolve)(options.dir);
553
+ const layout = (0, import_generate_layout.discoverDataLayout)(project, baseDir, options.config ? (0, import_node_path.resolve)(options.config) : void 0);
590
554
  const tableNames = dataMembershipFromLayout(layout).allTables;
591
555
  const relationsPath = layout.metadataPaths.find(path => (0, import_node_path.basename)(path) === "relations.ts" && (0, import_node_path.dirname)(path) === (0, import_node_path.resolve)((0, import_node_path.dirname)(baseDir), "database")) ?? layout.metadataPaths.find(path => (0, import_node_path.basename)(path) === "relations.ts");
592
556
  const relationEntries = [];
593
557
  if (relationsPath) {
594
- const source = ts.createSourceFile(relationsPath, (0, import_node_fs.readFileSync)(relationsPath, "utf8"), ts.ScriptTarget.Latest, true);
558
+ const source = project.sourceFile(relationsPath);
595
559
  const included = new Set(tableNames);
596
560
  const visit = node => {
597
561
  if (!ts.isCallExpression(node) || node.expression.getText(source) !== "defineRelations") {
598
- ts.forEachChild(node, visit);
562
+ node.forEachChild(visit);
599
563
  return;
600
564
  }
601
565
  const factory = node.arguments[1];
@@ -629,6 +593,14 @@ ${relations.map(relation => ` ${relation},`).join("\n")}
629
593
  return ["// auto-generated from the on-zero data layout", `import { defineRelations } from 'drizzle-orm'`, `import * as schema from ${schemaImportPath}`, "", `export { ${tableNames.join(", ")} } from ${schemaImportPath}`, `export const relations = defineRelations(schema, (r) => ({`, ...relationEntries, `}))`, ""].join("\n");
630
594
  }
631
595
  async function generate(options) {
596
+ const project = await loadGeneratorProject((0, import_node_path.resolve)(options.dir));
597
+ try {
598
+ return await generateWithProject(options, project);
599
+ } finally {
600
+ await project.close();
601
+ }
602
+ }
603
+ async function generateWithProject(options, project) {
632
604
  const {
633
605
  dir,
634
606
  after,
@@ -644,11 +616,41 @@ async function generate(options) {
644
616
  });
645
617
  }
646
618
  loadCache();
647
- const ts = await import("typescript");
648
- const layout = (0, import_generate_layout.discoverDataLayout)(ts, baseDir, config ? (0, import_node_path.resolve)(config) : void 0);
619
+ const layout = (0, import_generate_layout.discoverDataLayout)(project, baseDir, config ? (0, import_node_path.resolve)(config) : void 0);
649
620
  const metadataHash = hash(layout.metadataPaths.map(path => `${path}\0${(0, import_node_fs.readFileSync)(path, "utf8")}`).join("\0"));
621
+ const databaseSchemaPath = (0, import_node_path.resolve)((0, import_node_path.dirname)(baseDir), "database/schema.ts");
622
+ const needsSqliteZeroSchema = (0, import_node_fs.existsSync)(databaseSchemaPath);
650
623
  const inputHash = hash(`${hashInputTree(layout.sourceRoots, generatedDir)}\0${metadataHash}`);
651
- if (!force && generateCache.__generatorVersion === GENERATOR_CACHE_VERSION && generateCache.__inputHash === inputHash && (0, import_node_fs.existsSync)((0, import_node_path.resolve)(generatedDir, "models.ts"))) {
624
+ if (!force && generateCache.__generatorVersion === GENERATOR_CACHE_VERSION && generateCache.__inputHash === inputHash &&
625
+ // the input hash deliberately skips the generated directory, so it cannot
626
+ // notice that the outputs themselves moved. existence is not correctness
627
+ // either: a checkout, merge or revert leaves every file present and stale.
628
+ // confirm the bytes on disk still match what the cache says it wrote there
629
+ // before skipping the build, or the fast path serves someone else's output.
630
+ //
631
+ // a consumer that formats generated output after this runs never hits the
632
+ // fast path again, because the cache holds the bytes written here and disk
633
+ // holds the formatted ones. soot does exactly that, `on-zero generate &&
634
+ // generate-instance-tables && oxfmt src/data/generated/`, and the number is
635
+ // already taken: 2.50s median before, 3.69s after, four warm runs each in
636
+ // one checkout with only the pin swapped, so about 1.2 seconds or 47
637
+ // percent on this step. the full three-command script went 2.65s to 3.14s
638
+ // with a spread that swallows most of it.
639
+ //
640
+ // the shape matters more than the number. this is not a cache that hits
641
+ // less often, it is a cache that never hits, so the cost does not degrade
642
+ // gracefully and grows with the generated output rather than staying at
643
+ // 1.2s. correct and slower beats fast and stale, which is why it is written
644
+ // this way, but if generate time ever becomes the complaint, record the
645
+ // hash after the consumer's formatter runs rather than weakening this.
646
+ Object.entries(generateCache).every(([cachedPath, cachedHash]) => {
647
+ if (!cachedPath.startsWith(generatedDir)) return true;
648
+ try {
649
+ return hash((0, import_node_fs.readFileSync)(cachedPath, "utf-8")) === cachedHash;
650
+ } catch {
651
+ return false;
652
+ }
653
+ }) && (0, import_node_fs.existsSync)((0, import_node_path.resolve)(generatedDir, "models.ts")) && (!needsSqliteZeroSchema || (0, import_node_fs.existsSync)((0, import_node_path.resolve)(generatedDir, "schema.ts")))) {
652
654
  let counts = {};
653
655
  try {
654
656
  counts = JSON.parse(generateCache.__counts || "{}");
@@ -677,6 +679,24 @@ async function generate(options) {
677
679
  // managed externally and should not be overwritten.
678
680
  ...(filesWithSchema.length > 0 ? [writeFileIfChanged((0, import_node_path.resolve)(generatedDir, "types.ts"), (0, import_generate_helpers.generateTypesFile)(schemaModules.map(module2 => module2.name))), writeFileIfChanged((0, import_node_path.resolve)(generatedDir, "tables.ts"), (0, import_generate_helpers.generateTablesFile)(schemaModules))] : []), writeFileIfChanged((0, import_node_path.resolve)(generatedDir, "README.md"), (0, import_generate_helpers.generateReadmeFile)())];
679
681
  let filesChanged = writeResults.filter(Boolean).length;
682
+ if (needsSqliteZeroSchema) {
683
+ const membership = dataMembershipFromLayout(layout);
684
+ const drizzleSchema = await generateDrizzleSchemaInputFileWithProject({
685
+ dir: baseDir,
686
+ schemaImportPath: "../../database/schema",
687
+ config
688
+ }, project);
689
+ const sqliteSchema = (0, import_generate_helpers.renderDrizzleZeroSqliteSchemaModule)({
690
+ importPath: "./drizzleSchema",
691
+ tableNames: membership.allTables
692
+ });
693
+ if (writeFileIfChanged((0, import_node_path.resolve)(generatedDir, "drizzleSchema.ts"), drizzleSchema)) {
694
+ filesChanged++;
695
+ }
696
+ if (writeFileIfChanged((0, import_node_path.resolve)(generatedDir, "schema.ts"), sqliteSchema)) {
697
+ filesChanged++;
698
+ }
699
+ }
680
700
  let queryCount = 0;
681
701
  let mutationCount = 0;
682
702
  const typeToValibot = paramType => {
@@ -687,23 +707,12 @@ async function generate(options) {
687
707
  }
688
708
  };
689
709
  const allQueries = [];
690
- let queryResolver = null;
691
- const getQueryResolver = () => {
692
- if (!queryResolver) {
693
- const allFiles = [...new Set(layout.namespaces.flatMap(namespace => namespace.sourcePaths))].map(path => ({
694
- path,
695
- content: (0, import_node_fs.readFileSync)(path, "utf-8")
696
- }));
697
- queryResolver = createTypeResolver(ts, allFiles, baseDir);
698
- }
699
- return queryResolver;
700
- };
701
710
  for (const namespace of layout.namespaces.filter(namespace2 => namespace2.queryPath !== null)) {
702
711
  const filePath = namespace.queryPath;
703
712
  try {
704
- const content = (0, import_node_fs.readFileSync)(filePath, "utf-8");
705
- const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
706
- ts.forEachChild(sourceFile, node => {
713
+ const sourceFile = project.sourceFile(filePath);
714
+ const candidates = [];
715
+ sourceFile.forEachChild(node => {
707
716
  if (ts.isVariableStatement(node)) {
708
717
  const exportModifier = node.modifiers?.find(m => m.kind === ts.SyntaxKind.ExportKeyword);
709
718
  if (!exportModifier) return;
@@ -718,31 +727,33 @@ async function generate(options) {
718
727
  const param = params[0];
719
728
  paramType = param.type?.getText(sourceFile) || "unknown";
720
729
  }
721
- let valibotCode = typeToValibot(paramType);
722
- if (!valibotCode && params.length > 0 && params[0].type) {
723
- const resolver = getQueryResolver();
724
- const resolverSourceFile = resolver.program.getSourceFile(filePath);
725
- if (resolverSourceFile) {
726
- const resolvedType = resolveParamType(ts, resolver, resolverSourceFile, name, 0);
727
- if (resolvedType) {
728
- valibotCode = resolver.typeToValibot(resolvedType);
729
- }
730
- }
731
- }
732
- if (valibotCode) {
733
- allQueries.push({
734
- name,
735
- params: paramType,
736
- valibotCode,
737
- sourceFile: namespace.name,
738
- importPath: (0, import_generate_layout.namespaceImportPath)(baseDir, filePath)
739
- });
740
- } else if (!silent && paramType !== "void") {
741
- console.error(`\u2717 ${name}: could not resolve type "${paramType}"`);
742
- }
730
+ candidates.push({
731
+ name,
732
+ paramType,
733
+ typeNode: params[0]?.type ?? null
734
+ });
743
735
  }
744
736
  }
745
737
  });
738
+ const resolver = createTypeResolver(project.projectForFile(filePath));
739
+ for (const candidate of candidates) {
740
+ let valibotCode = typeToValibot(candidate.paramType);
741
+ if (!valibotCode && candidate.typeNode) {
742
+ const resolvedType = await resolveParamType(resolver, sourceFile, candidate.name, 0);
743
+ if (resolvedType) valibotCode = await resolver.typeToValibot(resolvedType);
744
+ }
745
+ if (valibotCode) {
746
+ allQueries.push({
747
+ name: candidate.name,
748
+ params: candidate.paramType,
749
+ valibotCode,
750
+ sourceFile: namespace.name,
751
+ importPath: (0, import_generate_layout.namespaceImportPath)(baseDir, filePath)
752
+ });
753
+ } else if (!silent && candidate.paramType !== "void") {
754
+ console.error(`\u2717 ${candidate.name}: could not resolve type "${candidate.paramType}"`);
755
+ }
756
+ }
746
757
  } catch (err) {
747
758
  if (!silent) console.error(`Error processing ${filePath}:`, err);
748
759
  }
@@ -771,20 +782,14 @@ async function generate(options) {
771
782
  if (aggregatesChanged) filesChanged++;
772
783
  }
773
784
  const allModelMutations = [];
774
- const mutationFiles = [];
775
785
  const unresolvedModels = [];
776
786
  for (const namespace of modelNamespaces) {
777
787
  const filePath = namespace.modelPath;
778
788
  const fileBaseName = namespace.name;
779
789
  try {
780
790
  const content = (0, import_node_fs.readFileSync)(filePath, "utf-8");
781
- mutationFiles.push({
782
- path: filePath,
783
- content,
784
- baseName: fileBaseName
785
- });
786
- const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
787
- const result = extractMutationsFromModel(ts, sourceFile, content, filePath, !!silent, typeToValibot);
791
+ const sourceFile = project.sourceFile(filePath);
792
+ const result = extractMutationsFromModel(sourceFile, content, filePath, !!silent, typeToValibot);
788
793
  if (result) {
789
794
  result.modelName = fileBaseName;
790
795
  allModelMutations.push(result);
@@ -801,36 +806,20 @@ async function generate(options) {
801
806
  }
802
807
  }
803
808
  if (unresolvedModels.length > 0) {
804
- const collectTsFiles = dir2 => {
805
- const results = [];
806
- for (const entry of (0, import_node_fs.readdirSync)(dir2, {
807
- withFileTypes: true
808
- })) {
809
- const fullPath = (0, import_node_path.resolve)(dir2, entry.name);
810
- if (entry.isDirectory() && entry.name !== "node_modules") {
811
- results.push(...collectTsFiles(fullPath));
812
- } else if (entry.isFile() && isGeneratorSourceFile(entry.name)) {
813
- results.push({
814
- path: fullPath,
815
- content: (0, import_node_fs.readFileSync)(fullPath, "utf-8")
816
- });
817
- }
818
- }
819
- return results;
820
- };
821
- const allFiles = [...new Map(layout.sourceRoots.flatMap(collectTsFiles).map(file => [file.path, file])).values()];
822
- const modelResolver = createTypeResolver(ts, allFiles, baseDir);
823
809
  for (const {
824
810
  baseName,
825
811
  filePath
826
812
  } of unresolvedModels) {
827
- const resolverSourceFile = modelResolver.program.getSourceFile(filePath);
828
- if (!resolverSourceFile) continue;
829
- const resolvedTypes = resolveMutationParamTypes(ts, modelResolver, resolverSourceFile);
813
+ const sourceFile = project.sourceFile(filePath);
814
+ const modelResolver = createTypeResolver(project.projectForFile(filePath));
815
+ const resolvedTypes = await resolveMutationParamTypes(modelResolver, sourceFile);
830
816
  if (resolvedTypes.size === 0) continue;
817
+ const resolvedValibot = /* @__PURE__ */new Map();
818
+ for (const [name, type] of resolvedTypes) {
819
+ resolvedValibot.set(name, await modelResolver.typeToValibot(type));
820
+ }
831
821
  const content = (0, import_node_fs.readFileSync)(filePath, "utf-8");
832
- const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
833
- const result = extractMutationsFromModel(ts, sourceFile, content, filePath, !!silent, typeToValibot, resolvedTypes, modelResolver.typeToValibot);
822
+ const result = extractMutationsFromModel(sourceFile, content, filePath, !!silent, typeToValibot, resolvedValibot);
834
823
  if (result) {
835
824
  result.modelName = baseName;
836
825
  const idx = allModelMutations.findIndex(m => m.modelName === baseName);
@@ -908,8 +897,13 @@ ${event} ${path}`);
908
897
  }, debounce);
909
898
  };
910
899
  const databaseDir = (0, import_node_path.resolve)((0, import_node_path.dirname)(baseDir), "database");
911
- const ts = await import("typescript");
912
- const layout = (0, import_generate_layout.discoverDataLayout)(ts, baseDir, options.config ? (0, import_node_path.resolve)(options.config) : void 0);
900
+ const project = await loadGeneratorProject(baseDir);
901
+ let layout;
902
+ try {
903
+ layout = (0, import_generate_layout.discoverDataLayout)(project, baseDir, options.config ? (0, import_node_path.resolve)(options.config) : void 0);
904
+ } finally {
905
+ await project.close();
906
+ }
913
907
  const watcher = chokidar.watch([... /* @__PURE__ */new Set([...layout.sourceRoots, ...((0, import_node_fs.existsSync)(databaseDir) ? [databaseDir] : [])])], {
914
908
  persistent: true,
915
909
  ignoreInitial: true,