on-zero 0.2.10 → 0.3.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 (242) hide show
  1. package/dist/cjs/cli.cjs +0 -0
  2. package/dist/cjs/cli.native.js +0 -0
  3. package/dist/cjs/createPermissions.native.js.map +1 -1
  4. package/dist/cjs/createSchemaFromDrizzle.cjs +30 -0
  5. package/dist/cjs/createSchemaFromDrizzle.native.js +33 -0
  6. package/dist/cjs/createSchemaFromDrizzle.native.js.map +1 -0
  7. package/dist/cjs/createUseQuery.cjs +1 -1
  8. package/dist/cjs/createUseQuery.native.js +1 -1
  9. package/dist/cjs/createZeroClient.cjs +1 -1
  10. package/dist/cjs/createZeroClient.native.js +1 -1
  11. package/dist/cjs/createZeroClient.native.js.map +1 -1
  12. package/dist/cjs/createZeroServer.native.js.map +1 -1
  13. package/dist/cjs/generate.cjs +64 -12
  14. package/dist/cjs/generate.native.js +114 -12
  15. package/dist/cjs/generate.native.js.map +1 -1
  16. package/dist/cjs/helpers/createMutators.native.js.map +1 -1
  17. package/dist/cjs/index.cjs +2 -1
  18. package/dist/cjs/index.native.js +1 -0
  19. package/dist/cjs/index.native.js.map +1 -1
  20. package/dist/cjs/mutations.cjs +1 -1
  21. package/dist/cjs/mutations.native.js +1 -1
  22. package/dist/cjs/mutations.native.js.map +1 -1
  23. package/dist/cjs/server.native.js +1 -18
  24. package/dist/cjs/server.native.js.map +1 -1
  25. package/dist/cjs/vite-plugin.cjs +1 -1
  26. package/dist/cjs/vite-plugin.native.js +1 -1
  27. package/dist/cjs/vite-plugin.native.js.map +1 -1
  28. package/dist/esm/cli.mjs +0 -0
  29. package/dist/esm/cli.native.js +0 -0
  30. package/dist/esm/createPermissions.mjs.map +1 -1
  31. package/dist/esm/createPermissions.native.js.map +1 -1
  32. package/dist/esm/createSchemaFromDrizzle.mjs +5 -0
  33. package/dist/esm/createSchemaFromDrizzle.mjs.map +1 -0
  34. package/dist/esm/createSchemaFromDrizzle.native.js +5 -0
  35. package/dist/esm/createSchemaFromDrizzle.native.js.map +1 -0
  36. package/dist/esm/createUseQuery.mjs +2 -2
  37. package/dist/esm/createUseQuery.mjs.map +1 -1
  38. package/dist/esm/createUseQuery.native.js +2 -2
  39. package/dist/esm/createUseQuery.native.js.map +1 -1
  40. package/dist/esm/createZeroClient.mjs +2 -2
  41. package/dist/esm/createZeroClient.mjs.map +1 -1
  42. package/dist/esm/createZeroClient.native.js +2 -2
  43. package/dist/esm/createZeroClient.native.js.map +1 -1
  44. package/dist/esm/createZeroServer.mjs.map +1 -1
  45. package/dist/esm/createZeroServer.native.js.map +1 -1
  46. package/dist/esm/generate.mjs +64 -13
  47. package/dist/esm/generate.mjs.map +1 -1
  48. package/dist/esm/generate.native.js +114 -13
  49. package/dist/esm/generate.native.js.map +1 -1
  50. package/dist/esm/helpers/createMutators.mjs.map +1 -1
  51. package/dist/esm/helpers/createMutators.native.js.map +1 -1
  52. package/dist/esm/index.js +19 -24
  53. package/dist/esm/index.js.map +1 -6
  54. package/dist/esm/index.mjs +1 -0
  55. package/dist/esm/index.mjs.map +1 -1
  56. package/dist/esm/index.native.js +1 -0
  57. package/dist/esm/index.native.js.map +1 -1
  58. package/dist/esm/mutations.mjs +1 -1
  59. package/dist/esm/mutations.mjs.map +1 -1
  60. package/dist/esm/mutations.native.js +1 -1
  61. package/dist/esm/mutations.native.js.map +1 -1
  62. package/dist/esm/server.native.js +1 -1
  63. package/dist/esm/server.native.js.map +1 -1
  64. package/dist/esm/vite-plugin.mjs +1 -1
  65. package/dist/esm/vite-plugin.mjs.map +1 -1
  66. package/dist/esm/vite-plugin.native.js +1 -1
  67. package/dist/esm/vite-plugin.native.js.map +1 -1
  68. package/package.json +10 -4
  69. package/readme.md +80 -54
  70. package/src/createPermissions.ts +4 -9
  71. package/src/createSchemaFromDrizzle.ts +3 -0
  72. package/src/createUseQuery.tsx +4 -4
  73. package/src/createZeroClient.tsx +14 -14
  74. package/src/createZeroServer.ts +12 -12
  75. package/src/generate.test.ts +32 -32
  76. package/src/generate.ts +208 -55
  77. package/src/helpers/batchQuery.ts +1 -1
  78. package/src/helpers/createMutators.ts +7 -6
  79. package/src/helpers/didRunPermissionCheck.ts +1 -1
  80. package/src/helpers/mutatorContext.ts +2 -2
  81. package/src/helpers/prettyFormatZeroQuery.ts +1 -1
  82. package/src/helpers/queryContext.ts +1 -1
  83. package/src/helpers/useZeroDebug.ts +2 -2
  84. package/src/index.ts +2 -0
  85. package/src/mutations.ts +33 -7
  86. package/src/queryRegistry.ts +1 -1
  87. package/src/resolveQuery.ts +3 -3
  88. package/src/run.ts +5 -5
  89. package/src/server.native.ts +3 -0
  90. package/src/serverWhere.ts +2 -2
  91. package/src/types.ts +4 -2
  92. package/src/vite-plugin.ts +2 -2
  93. package/src/where.ts +5 -5
  94. package/src/zeroRunner.ts +2 -2
  95. package/types/createPermissions.d.ts +1 -1
  96. package/types/createPermissions.d.ts.map +1 -1
  97. package/types/createSchemaFromDrizzle.d.ts +4 -0
  98. package/types/createSchemaFromDrizzle.d.ts.map +1 -0
  99. package/types/createUseQuery.d.ts.map +1 -1
  100. package/types/createZeroServer.d.ts +11 -11
  101. package/types/generate.d.ts +29 -0
  102. package/types/generate.d.ts.map +1 -1
  103. package/types/helpers/createMutators.d.ts.map +1 -1
  104. package/types/index.d.ts +1 -0
  105. package/types/index.d.ts.map +1 -1
  106. package/types/mutations.d.ts +4 -1
  107. package/types/mutations.d.ts.map +1 -1
  108. package/types/resolveQuery.d.ts +1 -1
  109. package/types/resolveQuery.d.ts.map +1 -1
  110. package/types/server.native.d.ts +1 -0
  111. package/types/server.native.d.ts.map +1 -0
  112. package/types/types.d.ts.map +1 -1
  113. package/dist/cjs/cli.js +0 -42
  114. package/dist/cjs/cli.js.map +0 -6
  115. package/dist/cjs/constants.js +0 -23
  116. package/dist/cjs/constants.js.map +0 -6
  117. package/dist/cjs/createPermissions.js +0 -92
  118. package/dist/cjs/createPermissions.js.map +0 -6
  119. package/dist/cjs/createUseQuery.js +0 -35
  120. package/dist/cjs/createUseQuery.js.map +0 -6
  121. package/dist/cjs/createZeroClient.js +0 -178
  122. package/dist/cjs/createZeroClient.js.map +0 -6
  123. package/dist/cjs/createZeroServer.js +0 -189
  124. package/dist/cjs/createZeroServer.js.map +0 -6
  125. package/dist/cjs/generate.js +0 -811
  126. package/dist/cjs/generate.js.map +0 -6
  127. package/dist/cjs/generate.test.js +0 -404
  128. package/dist/cjs/generate.test.js.map +0 -6
  129. package/dist/cjs/helpers/batchQuery.js +0 -41
  130. package/dist/cjs/helpers/batchQuery.js.map +0 -6
  131. package/dist/cjs/helpers/createMutators.js +0 -118
  132. package/dist/cjs/helpers/createMutators.js.map +0 -6
  133. package/dist/cjs/helpers/didRunPermissionCheck.js +0 -26
  134. package/dist/cjs/helpers/didRunPermissionCheck.js.map +0 -6
  135. package/dist/cjs/helpers/ensureLoggedIn.js +0 -25
  136. package/dist/cjs/helpers/ensureLoggedIn.js.map +0 -6
  137. package/dist/cjs/helpers/getAuth.js +0 -31
  138. package/dist/cjs/helpers/getAuth.js.map +0 -6
  139. package/dist/cjs/helpers/mutatorContext.js +0 -44
  140. package/dist/cjs/helpers/mutatorContext.js.map +0 -6
  141. package/dist/cjs/helpers/prettyFormatZeroQuery.js +0 -92
  142. package/dist/cjs/helpers/prettyFormatZeroQuery.js.map +0 -6
  143. package/dist/cjs/helpers/queryContext.js +0 -33
  144. package/dist/cjs/helpers/queryContext.js.map +0 -6
  145. package/dist/cjs/helpers/useZeroDebug.js +0 -49
  146. package/dist/cjs/helpers/useZeroDebug.js.map +0 -6
  147. package/dist/cjs/index.js +0 -40
  148. package/dist/cjs/index.js.map +0 -6
  149. package/dist/cjs/modelRegistry.js +0 -32
  150. package/dist/cjs/modelRegistry.js.map +0 -6
  151. package/dist/cjs/mutations.js +0 -70
  152. package/dist/cjs/mutations.js.map +0 -6
  153. package/dist/cjs/queryRegistry.js +0 -32
  154. package/dist/cjs/queryRegistry.js.map +0 -6
  155. package/dist/cjs/resolveQuery.js +0 -40
  156. package/dist/cjs/resolveQuery.js.map +0 -6
  157. package/dist/cjs/run.js +0 -42
  158. package/dist/cjs/run.js.map +0 -6
  159. package/dist/cjs/server.js +0 -15
  160. package/dist/cjs/server.js.map +0 -6
  161. package/dist/cjs/serverWhere.js +0 -24
  162. package/dist/cjs/serverWhere.js.map +0 -6
  163. package/dist/cjs/serverWhere.test.js +0 -52
  164. package/dist/cjs/serverWhere.test.js.map +0 -6
  165. package/dist/cjs/state.js +0 -48
  166. package/dist/cjs/state.js.map +0 -6
  167. package/dist/cjs/types.js +0 -14
  168. package/dist/cjs/types.js.map +0 -6
  169. package/dist/cjs/usePermission.test.js +0 -29
  170. package/dist/cjs/usePermission.test.js.map +0 -6
  171. package/dist/cjs/vite-plugin.js +0 -89
  172. package/dist/cjs/vite-plugin.js.map +0 -6
  173. package/dist/cjs/where.js +0 -50
  174. package/dist/cjs/where.js.map +0 -6
  175. package/dist/cjs/zeroRunner.js +0 -35
  176. package/dist/cjs/zeroRunner.js.map +0 -6
  177. package/dist/cjs/zql.js +0 -26
  178. package/dist/cjs/zql.js.map +0 -6
  179. package/dist/esm/cli.js +0 -44
  180. package/dist/esm/cli.js.map +0 -6
  181. package/dist/esm/constants.js +0 -7
  182. package/dist/esm/constants.js.map +0 -6
  183. package/dist/esm/createPermissions.js +0 -81
  184. package/dist/esm/createPermissions.js.map +0 -6
  185. package/dist/esm/createUseQuery.js +0 -22
  186. package/dist/esm/createUseQuery.js.map +0 -6
  187. package/dist/esm/createZeroClient.js +0 -185
  188. package/dist/esm/createZeroClient.js.map +0 -6
  189. package/dist/esm/createZeroServer.js +0 -192
  190. package/dist/esm/createZeroServer.js.map +0 -6
  191. package/dist/esm/generate.js +0 -789
  192. package/dist/esm/generate.js.map +0 -6
  193. package/dist/esm/generate.test.js +0 -408
  194. package/dist/esm/generate.test.js.map +0 -6
  195. package/dist/esm/helpers/batchQuery.js +0 -25
  196. package/dist/esm/helpers/batchQuery.js.map +0 -6
  197. package/dist/esm/helpers/createMutators.js +0 -96
  198. package/dist/esm/helpers/createMutators.js.map +0 -6
  199. package/dist/esm/helpers/didRunPermissionCheck.js +0 -10
  200. package/dist/esm/helpers/didRunPermissionCheck.js.map +0 -6
  201. package/dist/esm/helpers/ensureLoggedIn.js +0 -10
  202. package/dist/esm/helpers/ensureLoggedIn.js.map +0 -6
  203. package/dist/esm/helpers/getAuth.js +0 -17
  204. package/dist/esm/helpers/getAuth.js.map +0 -6
  205. package/dist/esm/helpers/mutatorContext.js +0 -28
  206. package/dist/esm/helpers/mutatorContext.js.map +0 -6
  207. package/dist/esm/helpers/prettyFormatZeroQuery.js +0 -76
  208. package/dist/esm/helpers/prettyFormatZeroQuery.js.map +0 -6
  209. package/dist/esm/helpers/queryContext.js +0 -17
  210. package/dist/esm/helpers/queryContext.js.map +0 -6
  211. package/dist/esm/helpers/useZeroDebug.js +0 -35
  212. package/dist/esm/helpers/useZeroDebug.js.map +0 -6
  213. package/dist/esm/modelRegistry.js +0 -16
  214. package/dist/esm/modelRegistry.js.map +0 -6
  215. package/dist/esm/mutations.js +0 -56
  216. package/dist/esm/mutations.js.map +0 -6
  217. package/dist/esm/queryRegistry.js +0 -16
  218. package/dist/esm/queryRegistry.js.map +0 -6
  219. package/dist/esm/resolveQuery.js +0 -24
  220. package/dist/esm/resolveQuery.js.map +0 -6
  221. package/dist/esm/run.js +0 -27
  222. package/dist/esm/run.js.map +0 -6
  223. package/dist/esm/server.js +0 -2
  224. package/dist/esm/server.js.map +0 -6
  225. package/dist/esm/serverWhere.js +0 -8
  226. package/dist/esm/serverWhere.js.map +0 -6
  227. package/dist/esm/serverWhere.test.js +0 -55
  228. package/dist/esm/serverWhere.test.js.map +0 -6
  229. package/dist/esm/state.js +0 -33
  230. package/dist/esm/state.js.map +0 -6
  231. package/dist/esm/types.js +0 -1
  232. package/dist/esm/types.js.map +0 -6
  233. package/dist/esm/usePermission.test.js +0 -33
  234. package/dist/esm/usePermission.test.js.map +0 -6
  235. package/dist/esm/vite-plugin.js +0 -74
  236. package/dist/esm/vite-plugin.js.map +0 -6
  237. package/dist/esm/where.js +0 -36
  238. package/dist/esm/where.js.map +0 -6
  239. package/dist/esm/zeroRunner.js +0 -19
  240. package/dist/esm/zeroRunner.js.map +0 -6
  241. package/dist/esm/zql.js +0 -10
  242. package/dist/esm/zql.js.map +0 -6
package/src/generate.ts CHANGED
@@ -53,16 +53,18 @@ function writeFileIfChanged(filePath: string, content: string): boolean {
53
53
  return true
54
54
  }
55
55
 
56
- function generateModelsFile(modelFiles: string[]) {
56
+ function generateModelsFile(modelFiles: string[], modelsDirName: string) {
57
57
  const modelNames = modelFiles.map((f) => basename(f, '.ts')).sort()
58
58
  const getImportName = (name: string) => (name === 'user' ? 'userPublic' : name)
59
59
 
60
60
  const imports = modelNames
61
- .map((name) => `import * as ${getImportName(name)} from '../models/${name}'`)
61
+ .map(
62
+ (name) => `import * as ${getImportName(name)} from '../${modelsDirName}/${name}'`,
63
+ )
62
64
  .join('\n')
63
65
 
64
66
  const sortedByImportName = [...modelNames].sort((a, b) =>
65
- getImportName(a).localeCompare(getImportName(b))
67
+ getImportName(a).localeCompare(getImportName(b)),
66
68
  )
67
69
  const modelsObj = `export const models = {\n${sortedByImportName.map((name) => ` ${getImportName(name)},`).join('\n')}\n}`
68
70
 
@@ -84,15 +86,18 @@ function generateTypesFile(modelFiles: string[]) {
84
86
  return `import type { TableInsertRow, TableUpdateRow } from 'on-zero'\nimport type * as schema from './tables'\n\n${typeExports}\n`
85
87
  }
86
88
 
87
- function generateTablesFile(modelFiles: string[]) {
89
+ function generateTablesFile(modelFiles: string[], modelsDirName: string) {
88
90
  const modelNames = modelFiles.map((f) => basename(f, '.ts')).sort()
89
91
  const getExportName = (name: string) => (name === 'user' ? 'userPublic' : name)
90
92
 
91
93
  const exports = modelNames
92
- .map((name) => `export { schema as ${getExportName(name)} } from '../models/${name}'`)
94
+ .map(
95
+ (name) =>
96
+ `export { schema as ${getExportName(name)} } from '../${modelsDirName}/${name}'`,
97
+ )
93
98
  .join('\n')
94
99
 
95
- return `// auto-generated by: on-zero generate\n// this is separate from models as otherwise you end up with circular types :/\n\n${exports}\n`
100
+ return `// auto-generated by: on-zero generate\n\n${exports}\n`
96
101
  }
97
102
 
98
103
  function generateReadmeFile() {
@@ -157,7 +162,7 @@ see the [on-zero readme](./node_modules/on-zero/README.md) for full documentatio
157
162
  }
158
163
 
159
164
  function generateGroupedQueriesFile(
160
- queries: Array<{ name: string; sourceFile: string }>
165
+ queries: Array<{ name: string; sourceFile: string }>,
161
166
  ) {
162
167
  const sortedFiles = [...new Set(queries.map((q) => q.sourceFile))].sort()
163
168
 
@@ -182,7 +187,7 @@ function generateSyncedQueriesFile(
182
187
  params: string
183
188
  valibotCode: string
184
189
  sourceFile: string
185
- }>
190
+ }>,
186
191
  ) {
187
192
  const queryByFile = new Map<string, typeof queries>()
188
193
  for (const q of queries) {
@@ -242,11 +247,7 @@ ${queriesObject}
242
247
  `
243
248
  }
244
249
 
245
- // column type in schema valibot validator
246
- type SchemaColumn = {
247
- type: 'string' | 'number' | 'boolean' | 'json' | 'enum'
248
- optional: boolean
249
- }
250
+ // used by valibot mutation validator codegen (subset of SchemaColumn below)
250
251
 
251
252
  type ExtractedMutation = {
252
253
  name: string
@@ -258,7 +259,7 @@ type ExtractedMutation = {
258
259
  function createTypeResolver(
259
260
  ts: typeof import('typescript'),
260
261
  files: Array<{ path: string; content: string }>,
261
- dir: string
262
+ dir: string,
262
263
  ) {
263
264
  // find tsconfig if it exists for path alias resolution
264
265
  const configPath = ts.findConfigFile(dir, ts.sys.fileExists, 'tsconfig.json')
@@ -278,7 +279,7 @@ function createTypeResolver(
278
279
  const parsed = ts.parseJsonConfigFileContent(
279
280
  configFile.config,
280
281
  ts.sys,
281
- dirname(configPath)
282
+ dirname(configPath),
282
283
  )
283
284
  compilerOptions = { ...compilerOptions, ...parsed.options }
284
285
  }
@@ -305,7 +306,7 @@ function createTypeResolver(
305
306
  const program = ts.createProgram(
306
307
  files.map((f) => f.path),
307
308
  compilerOptions,
308
- host
309
+ host,
309
310
  )
310
311
  const checker = program.getTypeChecker()
311
312
 
@@ -333,7 +334,7 @@ function resolveParamType(
333
334
  resolver: ReturnType<typeof createTypeResolver>,
334
335
  sourceFile: import('typescript').SourceFile,
335
336
  exportName: string,
336
- paramIndex: number
337
+ paramIndex: number,
337
338
  ): import('typescript').Type | null {
338
339
  let result: import('typescript').Type | null = null
339
340
 
@@ -362,7 +363,7 @@ function resolveParamType(
362
363
  function resolveMutationParamTypes(
363
364
  ts: typeof import('typescript'),
364
365
  resolver: ReturnType<typeof createTypeResolver>,
365
- sourceFile: import('typescript').SourceFile
366
+ sourceFile: import('typescript').SourceFile,
366
367
  ): Map<string, import('typescript').Type> {
367
368
  const resolved = new Map<string, import('typescript').Type>()
368
369
 
@@ -434,7 +435,7 @@ function extractMutationsFromModel(
434
435
  silent: boolean,
435
436
  typeToValibot: (typeString: string) => string | null,
436
437
  resolvedTypes?: Map<string, import('typescript').Type>,
437
- resolvedTypeToValibot?: (type: import('typescript').Type) => string
438
+ resolvedTypeToValibot?: (type: import('typescript').Type) => string,
438
439
  ): ModelMutations | null {
439
440
  let mutateNode: import('typescript').CallExpression | null = null
440
441
 
@@ -556,7 +557,7 @@ function extractSchemaColumns(
556
557
  ts: typeof import('typescript'),
557
558
  sourceFile: ReturnType<typeof ts.createSourceFile>,
558
559
  columns: Record<string, SchemaColumn>,
559
- primaryKeys: string[]
560
+ primaryKeys: string[],
560
561
  ) {
561
562
  // walk AST to find table(...).columns({...}).primaryKey(...)
562
563
  function visit(node: import('typescript').Node) {
@@ -602,11 +603,11 @@ function parseColumnType(initText: string): SchemaColumn {
602
603
  else if (initText.startsWith('json(') || initText.startsWith('json<')) type = 'json'
603
604
  else if (initText.startsWith('enumeration(')) type = 'enum'
604
605
 
605
- return { type, optional }
606
+ return { type, optional, customType: undefined }
606
607
  }
607
608
 
608
609
  function columnTypeToValibot(col: SchemaColumn): string {
609
- let base: string
610
+ let base = 'v.string()'
610
611
  switch (col.type) {
611
612
  case 'string':
612
613
  base = 'v.string()'
@@ -623,8 +624,6 @@ function columnTypeToValibot(col: SchemaColumn): string {
623
624
  case 'enum':
624
625
  base = 'v.string()'
625
626
  break
626
- default:
627
- base = 'v.unknown()'
628
627
  }
629
628
  return col.optional ? `v.optional(v.nullable(${base}))` : base
630
629
  }
@@ -642,7 +641,7 @@ function formatObjectKey(name: string): string {
642
641
  function schemaColumnsToValibot(
643
642
  columns: Record<string, SchemaColumn>,
644
643
  primaryKeys: string[],
645
- mode: 'insert' | 'update' | 'delete'
644
+ mode: 'insert' | 'update' | 'delete',
646
645
  ): string {
647
646
  const entries: string[] = []
648
647
 
@@ -652,7 +651,7 @@ function schemaColumnsToValibot(
652
651
  const col = columns[pk]
653
652
  if (col)
654
653
  entries.push(
655
- `${formatObjectKey(pk)}: ${columnTypeToValibot({ ...col, optional: false })}`
654
+ `${formatObjectKey(pk)}: ${columnTypeToValibot({ ...col, optional: false })}`,
656
655
  )
657
656
  }
658
657
  } else if (mode === 'update') {
@@ -661,11 +660,11 @@ function schemaColumnsToValibot(
661
660
  const isPK = primaryKeys.includes(name)
662
661
  if (isPK) {
663
662
  entries.push(
664
- `${formatObjectKey(name)}: ${columnTypeToValibot({ ...col, optional: false })}`
663
+ `${formatObjectKey(name)}: ${columnTypeToValibot({ ...col, optional: false })}`,
665
664
  )
666
665
  } else {
667
666
  entries.push(
668
- `${formatObjectKey(name)}: ${columnTypeToValibot({ ...col, optional: true })}`
667
+ `${formatObjectKey(name)}: ${columnTypeToValibot({ ...col, optional: true })}`,
669
668
  )
670
669
  }
671
670
  }
@@ -681,7 +680,7 @@ function schemaColumnsToValibot(
681
680
 
682
681
  function generateSyncedMutationsFile(modelMutations: ModelMutations[]) {
683
682
  const sorted = [...modelMutations].sort((a, b) =>
684
- a.modelName.localeCompare(b.modelName)
683
+ a.modelName.localeCompare(b.modelName),
685
684
  )
686
685
 
687
686
  const modelDefs = sorted
@@ -698,17 +697,17 @@ function generateSyncedMutationsFile(modelMutations: ModelMutations[]) {
698
697
  const customMut = model.custom.find((m) => m.name === mode)!
699
698
  if (customMut.valibotCode) {
700
699
  entries.push(
701
- ` ${mode}: ${extractValibotExpression(customMut.valibotCode)},`
700
+ ` ${mode}: ${extractValibotExpression(customMut.valibotCode)},`,
702
701
  )
703
702
  } else {
704
703
  // fall back to schema-derived
705
704
  entries.push(
706
- ` ${mode}: ${schemaColumnsToValibot(model.columns, model.primaryKeys, mode)},`
705
+ ` ${mode}: ${schemaColumnsToValibot(model.columns, model.primaryKeys, mode)},`,
707
706
  )
708
707
  }
709
708
  } else {
710
709
  entries.push(
711
- ` ${mode}: ${schemaColumnsToValibot(model.columns, model.primaryKeys, mode)},`
710
+ ` ${mode}: ${schemaColumnsToValibot(model.columns, model.primaryKeys, mode)},`,
712
711
  )
713
712
  }
714
713
  }
@@ -796,7 +795,7 @@ function tsTypeToValibot(
796
795
  ts: typeof import('typescript'),
797
796
  checker: import('typescript').TypeChecker,
798
797
  type: import('typescript').Type,
799
- seen?: Set<import('typescript').Type>
798
+ seen?: Set<import('typescript').Type>,
800
799
  ): string {
801
800
  // prevent infinite recursion on circular types
802
801
  // only track structured types (objects, intersections) — not primitives/unions
@@ -835,11 +834,14 @@ function tsTypeToValibot(
835
834
  const members = type.types
836
835
  const hasNull = members.some((t) => t.getFlags() & ts.TypeFlags.Null)
837
836
  const hasUndefined = members.some(
838
- (t) => t.getFlags() & (ts.TypeFlags.Undefined | ts.TypeFlags.Void)
837
+ (t) => t.getFlags() & (ts.TypeFlags.Undefined | ts.TypeFlags.Void),
839
838
  )
840
839
  const rest = members.filter(
841
840
  (t) =>
842
- !(t.getFlags() & (ts.TypeFlags.Null | ts.TypeFlags.Undefined | ts.TypeFlags.Void))
841
+ !(
842
+ t.getFlags() &
843
+ (ts.TypeFlags.Null | ts.TypeFlags.Undefined | ts.TypeFlags.Void)
844
+ ),
843
845
  )
844
846
  if (
845
847
  rest.length === 2 &&
@@ -950,6 +952,152 @@ function tsTypeToValibot(
950
952
  return 'v.unknown()'
951
953
  }
952
954
 
955
+ type SchemaColumn = {
956
+ type: string
957
+ optional: boolean
958
+ customType: unknown
959
+ serverName?: string
960
+ }
961
+
962
+ type SchemaTable = {
963
+ name: string
964
+ columns: Record<string, SchemaColumn>
965
+ primaryKey: string[]
966
+ }
967
+
968
+ type SchemaRelationHop = {
969
+ sourceField: string[]
970
+ destField: string[]
971
+ destSchema: string
972
+ cardinality: 'one' | 'many'
973
+ }
974
+
975
+ type DrizzleZeroSchema = {
976
+ tables: Record<string, SchemaTable>
977
+ relationships: Record<string, Record<string, SchemaRelationHop[]>>
978
+ }
979
+
980
+ function serializeColumn(col: SchemaColumn): string {
981
+ const parts: string[] = []
982
+ parts.push(`type: '${col.type}'`)
983
+ parts.push(`optional: ${col.optional}`)
984
+ parts.push(
985
+ `customType: null as unknown as ${col.type === 'json' ? 'ReadonlyJSONValue' : col.type}`,
986
+ )
987
+ if (col.serverName) {
988
+ parts.push(`serverName: '${col.serverName}'`)
989
+ }
990
+ return `{ ${parts.join(', ')} }`
991
+ }
992
+
993
+ function serializeColumnBuilder(col: SchemaColumn): string {
994
+ const zeroType =
995
+ col.type === 'string'
996
+ ? 'string'
997
+ : col.type === 'number'
998
+ ? 'number'
999
+ : col.type === 'boolean'
1000
+ ? 'boolean'
1001
+ : 'json'
1002
+ let expr = `${zeroType}()`
1003
+ if (col.serverName) {
1004
+ expr += `.from('${col.serverName}')`
1005
+ }
1006
+ if (col.optional) {
1007
+ expr += '.optional()'
1008
+ }
1009
+ return expr
1010
+ }
1011
+
1012
+ /**
1013
+ * generate a typed schema.ts from drizzle-zero output.
1014
+ * produces a file using table()/createSchema()/relationships() from @rocicorp/zero
1015
+ * so the full type system works (no `relationships: any`).
1016
+ *
1017
+ */
1018
+ export function generateDrizzleSchemaFile(schema: DrizzleZeroSchema): string {
1019
+ const lines: string[] = [
1020
+ `// auto-generated by: on-zero generate (from drizzle schema)`,
1021
+ `import { boolean, createSchema, json, number, relationships, string, table } from '@rocicorp/zero'`,
1022
+ ``,
1023
+ ]
1024
+
1025
+ const tableNames = Object.keys(schema.tables).sort()
1026
+
1027
+ // emit table consts using Zero's builder API
1028
+ for (const tableName of tableNames) {
1029
+ const t = schema.tables[tableName]!
1030
+ const colEntries = Object.entries(t.columns)
1031
+ .map(([colName, col]) => ` ${colName}: ${serializeColumnBuilder(col)},`)
1032
+ .join('\n')
1033
+ const pkArgs = t.primaryKey.map((k) => `'${k}'`).join(', ')
1034
+
1035
+ lines.push(`const ${tableName}Table = table('${t.name}')`)
1036
+ lines.push(` .columns({`)
1037
+ lines.push(colEntries)
1038
+ lines.push(` })`)
1039
+ lines.push(` .primaryKey(${pkArgs})`)
1040
+ lines.push(``)
1041
+ }
1042
+
1043
+ // emit relationship consts
1044
+ const relTableNames = Object.keys(schema.relationships).sort()
1045
+ for (const tableName of relTableNames) {
1046
+ const rels = schema.relationships[tableName]!
1047
+ const relEntries = Object.entries(rels)
1048
+ if (relEntries.length === 0) continue
1049
+
1050
+ const relBody = relEntries
1051
+ .map(([relName, hops]) => {
1052
+ // each relationship is an array of hops (usually 1, 2 for many-to-many)
1053
+ if (hops.length === 1) {
1054
+ const hop = hops[0]!
1055
+ const fn = hop.cardinality === 'one' ? 'one' : 'many'
1056
+ const sf = hop.sourceField.map((f) => `'${f}'`).join(', ')
1057
+ const df = hop.destField.map((f) => `'${f}'`).join(', ')
1058
+ return ` ${relName}: ${fn}({\n sourceField: [${sf}],\n destSchema: ${hop.destSchema}Table,\n destField: [${df}],\n })`
1059
+ }
1060
+ // many-to-many (2 hops)
1061
+ const hopCode = hops
1062
+ .map((hop) => {
1063
+ const fn = hop.cardinality === 'one' ? 'one' : 'many'
1064
+ const sf = hop.sourceField.map((f) => `'${f}'`).join(', ')
1065
+ const df = hop.destField.map((f) => `'${f}'`).join(', ')
1066
+ return `${fn}({ sourceField: [${sf}], destSchema: ${hop.destSchema}Table, destField: [${df}] })`
1067
+ })
1068
+ .join(', ')
1069
+ return ` ${relName}: [${hopCode}]`
1070
+ })
1071
+ .join(',\n')
1072
+
1073
+ lines.push(
1074
+ `const ${tableName}Relationships = relationships(${tableName}Table, ({ one, many }) => ({`,
1075
+ )
1076
+ lines.push(relBody)
1077
+ lines.push(`}))`)
1078
+ lines.push(``)
1079
+ }
1080
+
1081
+ // emit createSchema
1082
+ const tableList = tableNames.map((n) => ` ${n}Table,`).join('\n')
1083
+ const relList = relTableNames
1084
+ .filter((n) => Object.keys(schema.relationships[n]!).length > 0)
1085
+ .map((n) => ` ${n}Relationships,`)
1086
+ .join('\n')
1087
+
1088
+ lines.push(`export const schema = createSchema({`)
1089
+ lines.push(` tables: [`)
1090
+ lines.push(tableList)
1091
+ lines.push(` ],`)
1092
+ lines.push(` relationships: [`)
1093
+ lines.push(relList)
1094
+ lines.push(` ],`)
1095
+ lines.push(`})`)
1096
+ lines.push(``)
1097
+
1098
+ return lines.join('\n')
1099
+ }
1100
+
953
1101
  export interface GenerateOptions {
954
1102
  /** base data directory */
955
1103
  dir: string
@@ -975,7 +1123,11 @@ export interface GenerateResult {
975
1123
  export async function generate(options: GenerateOptions): Promise<GenerateResult> {
976
1124
  const { dir, after, silent } = options
977
1125
  const baseDir = resolve(dir)
978
- const modelsDir = resolve(baseDir, 'models')
1126
+ // support both mutations/ (new) and models/ (legacy) directories
1127
+ const mutationsDir = resolve(baseDir, 'mutations')
1128
+ const usesMutationsDir = existsSync(mutationsDir)
1129
+ const modelsDir = usesMutationsDir ? mutationsDir : resolve(baseDir, 'models')
1130
+ const modelsDirName = usesMutationsDir ? 'mutations' : 'models'
979
1131
  const generatedDir = resolve(baseDir, 'generated')
980
1132
  const queriesDir = resolve(baseDir, 'queries')
981
1133
 
@@ -990,21 +1142,21 @@ export async function generate(options: GenerateOptions): Promise<GenerateResult
990
1142
  .sort()
991
1143
 
992
1144
  const filesWithSchema = allModelFiles.filter((f) =>
993
- readFileSync(resolve(modelsDir, f), 'utf-8').includes('export const schema = table(')
1145
+ readFileSync(resolve(modelsDir, f), 'utf-8').includes('export const schema = table('),
994
1146
  )
995
1147
 
996
1148
  const writeResults = [
997
1149
  writeFileIfChanged(
998
1150
  resolve(generatedDir, 'models.ts'),
999
- generateModelsFile(allModelFiles)
1151
+ generateModelsFile(allModelFiles, modelsDirName),
1000
1152
  ),
1001
1153
  writeFileIfChanged(
1002
1154
  resolve(generatedDir, 'types.ts'),
1003
- generateTypesFile(filesWithSchema)
1155
+ generateTypesFile(filesWithSchema),
1004
1156
  ),
1005
1157
  writeFileIfChanged(
1006
1158
  resolve(generatedDir, 'tables.ts'),
1007
- generateTablesFile(filesWithSchema)
1159
+ generateTablesFile(filesWithSchema, modelsDirName),
1008
1160
  ),
1009
1161
  writeFileIfChanged(resolve(generatedDir, 'README.md'), generateReadmeFile()),
1010
1162
  ]
@@ -1063,13 +1215,13 @@ export async function generate(options: GenerateOptions): Promise<GenerateResult
1063
1215
  filePath,
1064
1216
  content,
1065
1217
  ts.ScriptTarget.Latest,
1066
- true
1218
+ true,
1067
1219
  )
1068
1220
 
1069
1221
  ts.forEachChild(sourceFile, (node) => {
1070
1222
  if (ts.isVariableStatement(node)) {
1071
1223
  const exportModifier = node.modifiers?.find(
1072
- (m) => m.kind === ts.SyntaxKind.ExportKeyword
1224
+ (m) => m.kind === ts.SyntaxKind.ExportKeyword,
1073
1225
  )
1074
1226
  if (!exportModifier) return
1075
1227
 
@@ -1100,7 +1252,7 @@ export async function generate(options: GenerateOptions): Promise<GenerateResult
1100
1252
  resolver,
1101
1253
  resolverSourceFile,
1102
1254
  name,
1103
- 0
1255
+ 0,
1104
1256
  )
1105
1257
  if (resolvedType) {
1106
1258
  valibotCode = resolver.typeToValibot(resolvedType)
@@ -1130,11 +1282,11 @@ export async function generate(options: GenerateOptions): Promise<GenerateResult
1130
1282
 
1131
1283
  const groupedChanged = writeFileIfChanged(
1132
1284
  resolve(generatedDir, 'groupedQueries.ts'),
1133
- generateGroupedQueriesFile(allQueries)
1285
+ generateGroupedQueriesFile(allQueries),
1134
1286
  )
1135
1287
  const syncedChanged = writeFileIfChanged(
1136
1288
  resolve(generatedDir, 'syncedQueries.ts'),
1137
- generateSyncedQueriesFile(allQueries)
1289
+ generateSyncedQueriesFile(allQueries),
1138
1290
  )
1139
1291
 
1140
1292
  if (groupedChanged) filesChanged++
@@ -1161,7 +1313,7 @@ export async function generate(options: GenerateOptions): Promise<GenerateResult
1161
1313
  filePath,
1162
1314
  content,
1163
1315
  ts.ScriptTarget.Latest,
1164
- true
1316
+ true,
1165
1317
  )
1166
1318
  const result = extractMutationsFromModel(
1167
1319
  ts,
@@ -1169,7 +1321,7 @@ export async function generate(options: GenerateOptions): Promise<GenerateResult
1169
1321
  content,
1170
1322
  filePath,
1171
1323
  !!silent,
1172
- typeToValibot
1324
+ typeToValibot,
1173
1325
  )
1174
1326
 
1175
1327
  if (result) {
@@ -1178,7 +1330,7 @@ export async function generate(options: GenerateOptions): Promise<GenerateResult
1178
1330
 
1179
1331
  // check if any custom mutations have unresolved types
1180
1332
  const hasUnresolved = result.custom.some(
1181
- (m) => m.paramType !== 'void' && m.paramType !== 'unknown' && !m.valibotCode
1333
+ (m) => m.paramType !== 'void' && m.paramType !== 'unknown' && !m.valibotCode,
1182
1334
  )
1183
1335
  if (hasUnresolved) {
1184
1336
  unresolvedModels.push({ baseName: fileBaseName, filePath })
@@ -1219,7 +1371,7 @@ export async function generate(options: GenerateOptions): Promise<GenerateResult
1219
1371
  const resolvedTypes = resolveMutationParamTypes(
1220
1372
  ts,
1221
1373
  modelResolver,
1222
- resolverSourceFile
1374
+ resolverSourceFile,
1223
1375
  )
1224
1376
  if (resolvedTypes.size === 0) continue
1225
1377
 
@@ -1229,7 +1381,7 @@ export async function generate(options: GenerateOptions): Promise<GenerateResult
1229
1381
  filePath,
1230
1382
  content,
1231
1383
  ts.ScriptTarget.Latest,
1232
- true
1384
+ true,
1233
1385
  )
1234
1386
  const result = extractMutationsFromModel(
1235
1387
  ts,
@@ -1239,7 +1391,7 @@ export async function generate(options: GenerateOptions): Promise<GenerateResult
1239
1391
  !!silent,
1240
1392
  typeToValibot,
1241
1393
  resolvedTypes,
1242
- modelResolver.typeToValibot
1394
+ modelResolver.typeToValibot,
1243
1395
  )
1244
1396
 
1245
1397
  if (result) {
@@ -1255,21 +1407,21 @@ export async function generate(options: GenerateOptions): Promise<GenerateResult
1255
1407
  for (const model of allModelMutations) {
1256
1408
  if (model.hasCRUD) mutationCount += 3 // insert, update, delete
1257
1409
  mutationCount += model.custom.filter(
1258
- (m) => !model.hasCRUD || !['insert', 'update', 'delete', 'upsert'].includes(m.name)
1410
+ (m) => !model.hasCRUD || !['insert', 'update', 'delete', 'upsert'].includes(m.name),
1259
1411
  ).length
1260
1412
  }
1261
1413
 
1262
1414
  if (allModelMutations.length > 0) {
1263
1415
  const mutationsChanged = writeFileIfChanged(
1264
1416
  resolve(generatedDir, 'syncedMutations.ts'),
1265
- generateSyncedMutationsFile(allModelMutations)
1417
+ generateSyncedMutationsFile(allModelMutations),
1266
1418
  )
1267
1419
  if (mutationsChanged) filesChanged++
1268
1420
  }
1269
1421
 
1270
1422
  if (filesChanged > 0 && !silent) {
1271
1423
  console.info(
1272
- `✓ ${allModelFiles.length} models (${filesWithSchema.length} schemas)${queryCount ? `, ${queryCount} queries` : ''}${mutationCount ? `, ${mutationCount} mutations` : ''}`
1424
+ `✓ ${allModelFiles.length} models (${filesWithSchema.length} schemas)${queryCount ? `, ${queryCount} queries` : ''}${mutationCount ? `, ${mutationCount} mutations` : ''}`,
1273
1425
  )
1274
1426
  }
1275
1427
 
@@ -1300,7 +1452,8 @@ export async function generate(options: GenerateOptions): Promise<GenerateResult
1300
1452
  export async function watch(options: WatchOptions) {
1301
1453
  const { dir, debounce = 1000 } = options
1302
1454
  const baseDir = resolve(dir)
1303
- const modelsDir = resolve(baseDir, 'models')
1455
+ const mutationsDir = resolve(baseDir, 'mutations')
1456
+ const modelsDir = existsSync(mutationsDir) ? mutationsDir : resolve(baseDir, 'models')
1304
1457
  const queriesDir = resolve(baseDir, 'queries')
1305
1458
  const generatedDir = resolve(baseDir, 'generated')
1306
1459
 
@@ -18,7 +18,7 @@ export function createBatchQuery(server: ServerWithQuery) {
18
18
  chunk: number
19
19
  pause?: number
20
20
  stopAfter?: number
21
- } = { chunk: 20 }
21
+ } = { chunk: 20 },
22
22
  ) {
23
23
  let hasMore = true
24
24
  let last: Item | null = null
@@ -66,6 +66,7 @@ export function createMutators<Models extends GenericModels>({
66
66
  : undefined,
67
67
  }
68
68
 
69
+ // eslint-disable-next-line typescript-eslint/return-await
69
70
  return await runWithContext(mutationContext, () => {
70
71
  // @ts-expect-error type shenanigan
71
72
  // map to our mutations() helper
@@ -76,7 +77,7 @@ export function createMutators<Models extends GenericModels>({
76
77
 
77
78
  function withDevelopmentLogging<Args extends any[]>(
78
79
  name: string,
79
- fn: (...args: Args) => Promise<void>
80
+ fn: (...args: Args) => Promise<void>,
80
81
  ) {
81
82
  if (process.env.NODE_ENV !== 'development' && !process.env.IS_TESTING) {
82
83
  return fn
@@ -127,7 +128,7 @@ export function createMutators<Models extends GenericModels>({
127
128
  fn: (...args: Args) => Promise<void>,
128
129
  // don't want this too high - zero runs mutations in order and waits for the last to finish it seems
129
130
  // so if one mutation gets stuck it will just sit there
130
- timeoutMs: number = time.ms.minutes(1)
131
+ timeoutMs: number = time.ms.minutes(1),
131
132
  ) {
132
133
  return async (...args: Args): Promise<void> => {
133
134
  const timeoutPromise = new Promise<never>((_, reject) => {
@@ -143,7 +144,7 @@ export function createMutators<Models extends GenericModels>({
143
144
  function withValidation<Args extends any[]>(
144
145
  tableName: string,
145
146
  mutatorName: string,
146
- fn: (...args: Args) => Promise<void>
147
+ fn: (...args: Args) => Promise<void>,
147
148
  ) {
148
149
  const validator = mutationValidators?.[tableName]?.[mutatorName]
149
150
 
@@ -190,9 +191,9 @@ export function createMutators<Models extends GenericModels>({
190
191
  withValidation(
191
192
  moduleName,
192
193
  name,
193
- withContext((...args: any[]) => getDynamicFn()(...args))
194
- )
195
- )
194
+ withContext((...args: any[]) => getDynamicFn()(...args)),
195
+ ),
196
+ ),
196
197
  )
197
198
  }
198
199
  }
@@ -4,7 +4,7 @@ import type { MutatorContext } from '../types'
4
4
 
5
5
  const PermissionCheckRan = globalValue(
6
6
  `on-zero:permissions-check`,
7
- () => new WeakMap<MutatorContext, boolean>()
7
+ () => new WeakMap<MutatorContext, boolean>(),
8
8
  )
9
9
 
10
10
  export const getDidRunPermissionCheck = (ctx: MutatorContext) => {
@@ -22,7 +22,7 @@ export function isInZeroMutation() {
22
22
 
23
23
  export function runWithContext<T>(
24
24
  context: MutatorContext,
25
- fn: () => T | Promise<T>
25
+ fn: () => T | Promise<T>,
26
26
  ): Promise<T> {
27
27
  return asyncContext.run(context, fn)
28
28
  }
@@ -37,7 +37,7 @@ export function getScopedAuthData(): AuthData | null | undefined {
37
37
 
38
38
  export function runWithAuthScope<T>(
39
39
  authData: AuthData | null,
40
- fn: () => T | Promise<T>
40
+ fn: () => T | Promise<T>,
41
41
  ): Promise<T> {
42
42
  return authScopeContext.run(authData, fn)
43
43
  }
@@ -4,7 +4,7 @@ import type { Query } from '@rocicorp/zero'
4
4
 
5
5
  export const prettyFormatZeroQuery = (
6
6
  query: Query<any, any, any>,
7
- mode: 'full' | 'minimal' = 'full'
7
+ mode: 'full' | 'minimal' = 'full',
8
8
  ): string => {
9
9
  const astObject = query['_completeAst']?.()
10
10
 
@@ -14,7 +14,7 @@ export function isInQueryContext() {
14
14
 
15
15
  export function runWithQueryContext<T>(
16
16
  context: { authData: AuthData | null },
17
- fn: () => T | Promise<T>
17
+ fn: () => T | Promise<T>,
18
18
  ): Promise<T> {
19
19
  return asyncContext.run(context, fn)
20
20
  }
@@ -42,7 +42,7 @@ export const useZeroDebug = (query: Query<any, any, any>, options: any, results:
42
42
  if (shouldLog) {
43
43
  if (COLLAPSED) {
44
44
  console.groupCollapsed(
45
- `${isPermissionQuery ? `👮‍♂️` : `✨`}${prettyFormatZeroQuery(query, 'minimal')}`
45
+ `${isPermissionQuery ? `👮‍♂️` : `✨`}${prettyFormatZeroQuery(query, 'minimal')}`,
46
46
  )
47
47
  console.info(id, prettyFormatZeroQuery(query, 'full'))
48
48
  console.info('cached result', results)
@@ -80,7 +80,7 @@ export const useZeroDebug = (query: Query<any, any, any>, options: any, results:
80
80
  table,
81
81
  changeCount: history.changeCount,
82
82
  recentAsts: history.asts,
83
- }
83
+ },
84
84
  )
85
85
  }
86
86
  }