over-zero 0.0.32 → 0.0.34

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 (254) hide show
  1. package/cli.cjs +3 -0
  2. package/dist/cjs/cli.cjs +224 -205
  3. package/dist/cjs/cli.js +57 -40
  4. package/dist/cjs/cli.js.map +1 -1
  5. package/dist/cjs/cli.native.js +270 -248
  6. package/dist/cjs/cli.native.js.map +1 -1
  7. package/dist/cjs/createPermissions.js.map +1 -1
  8. package/dist/cjs/createPermissions.native.js.map +1 -1
  9. package/dist/cjs/createZeroClient.cjs +6 -3
  10. package/dist/cjs/createZeroClient.js +11 -4
  11. package/dist/cjs/createZeroClient.js.map +1 -1
  12. package/dist/cjs/createZeroClient.native.js +40 -5
  13. package/dist/cjs/createZeroClient.native.js.map +1 -1
  14. package/dist/cjs/createZeroServer.cjs +5 -3
  15. package/dist/cjs/createZeroServer.js +8 -3
  16. package/dist/cjs/createZeroServer.js.map +1 -1
  17. package/dist/cjs/createZeroServer.native.js +39 -3
  18. package/dist/cjs/createZeroServer.native.js.map +1 -1
  19. package/dist/cjs/helpers/batchQuery.js.map +1 -1
  20. package/dist/cjs/helpers/batchQuery.native.js.map +1 -1
  21. package/dist/cjs/helpers/createMutators.js.map +1 -1
  22. package/dist/cjs/helpers/createMutators.native.js.map +1 -1
  23. package/dist/cjs/helpers/didRunPermissionCheck.js.map +1 -1
  24. package/dist/cjs/helpers/didRunPermissionCheck.native.js.map +1 -1
  25. package/dist/cjs/helpers/ensureLoggedIn.js.map +1 -1
  26. package/dist/cjs/helpers/ensureLoggedIn.native.js.map +1 -1
  27. package/dist/cjs/helpers/getQueryOrMutatorAuthData.js.map +1 -1
  28. package/dist/cjs/helpers/getQueryOrMutatorAuthData.native.js.map +1 -1
  29. package/dist/cjs/helpers/mutatorContext.js.map +1 -1
  30. package/dist/cjs/helpers/mutatorContext.native.js.map +1 -1
  31. package/dist/cjs/helpers/prettyFormatZeroQuery.js.map +1 -1
  32. package/dist/cjs/helpers/prettyFormatZeroQuery.native.js.map +1 -1
  33. package/dist/cjs/helpers/useZeroDebug.js.map +1 -1
  34. package/dist/cjs/helpers/useZeroDebug.native.js.map +1 -1
  35. package/dist/cjs/index.cjs +1 -0
  36. package/dist/cjs/index.js +1 -0
  37. package/dist/cjs/index.js.map +1 -1
  38. package/dist/cjs/index.native.js +1 -0
  39. package/dist/cjs/index.native.js.map +1 -1
  40. package/dist/cjs/mutations.js.map +1 -1
  41. package/dist/cjs/mutations.native.js.map +1 -1
  42. package/dist/cjs/{builder.cjs → queryRegistry.cjs} +12 -10
  43. package/dist/cjs/{queryBuilder.js → queryRegistry.js} +13 -11
  44. package/dist/cjs/queryRegistry.js.map +6 -0
  45. package/dist/cjs/{queryBuilder.native.js → queryRegistry.native.js} +13 -12
  46. package/dist/cjs/queryRegistry.native.js.map +1 -0
  47. package/dist/cjs/serverWhere.js.map +1 -1
  48. package/dist/cjs/serverWhere.native.js.map +1 -1
  49. package/dist/cjs/state.cjs +1 -4
  50. package/dist/cjs/state.js +1 -4
  51. package/dist/cjs/state.js.map +1 -1
  52. package/dist/cjs/state.native.js +1 -2
  53. package/dist/cjs/state.native.js.map +1 -1
  54. package/dist/cjs/where.js.map +1 -1
  55. package/dist/cjs/where.native.js.map +1 -1
  56. package/dist/cjs/zql.js.map +1 -1
  57. package/dist/cjs/zql.native.js.map +1 -1
  58. package/dist/esm/cli.js +58 -41
  59. package/dist/esm/cli.js.map +1 -1
  60. package/dist/esm/cli.mjs +223 -204
  61. package/dist/esm/cli.mjs.map +1 -1
  62. package/dist/esm/cli.native.js +269 -247
  63. package/dist/esm/cli.native.js.map +1 -1
  64. package/dist/esm/createPermissions.js.map +1 -1
  65. package/dist/esm/createPermissions.mjs.map +1 -1
  66. package/dist/esm/createPermissions.native.js.map +1 -1
  67. package/dist/esm/createZeroClient.js +11 -3
  68. package/dist/esm/createZeroClient.js.map +1 -1
  69. package/dist/esm/createZeroClient.mjs +6 -3
  70. package/dist/esm/createZeroClient.mjs.map +1 -1
  71. package/dist/esm/createZeroClient.native.js +40 -5
  72. package/dist/esm/createZeroClient.native.js.map +1 -1
  73. package/dist/esm/createZeroServer.js +8 -3
  74. package/dist/esm/createZeroServer.js.map +1 -1
  75. package/dist/esm/createZeroServer.mjs +5 -3
  76. package/dist/esm/createZeroServer.mjs.map +1 -1
  77. package/dist/esm/createZeroServer.native.js +39 -3
  78. package/dist/esm/createZeroServer.native.js.map +1 -1
  79. package/dist/esm/helpers/batchQuery.js.map +1 -1
  80. package/dist/esm/helpers/batchQuery.mjs.map +1 -1
  81. package/dist/esm/helpers/batchQuery.native.js.map +1 -1
  82. package/dist/esm/helpers/createMutators.js.map +1 -1
  83. package/dist/esm/helpers/createMutators.mjs.map +1 -1
  84. package/dist/esm/helpers/createMutators.native.js.map +1 -1
  85. package/dist/esm/helpers/didRunPermissionCheck.js.map +1 -1
  86. package/dist/esm/helpers/didRunPermissionCheck.mjs.map +1 -1
  87. package/dist/esm/helpers/didRunPermissionCheck.native.js.map +1 -1
  88. package/dist/esm/helpers/ensureLoggedIn.js.map +1 -1
  89. package/dist/esm/helpers/ensureLoggedIn.mjs.map +1 -1
  90. package/dist/esm/helpers/ensureLoggedIn.native.js.map +1 -1
  91. package/dist/esm/helpers/getQueryOrMutatorAuthData.js.map +1 -1
  92. package/dist/esm/helpers/getQueryOrMutatorAuthData.mjs.map +1 -1
  93. package/dist/esm/helpers/getQueryOrMutatorAuthData.native.js.map +1 -1
  94. package/dist/esm/helpers/mutatorContext.js.map +1 -1
  95. package/dist/esm/helpers/mutatorContext.mjs.map +1 -1
  96. package/dist/esm/helpers/mutatorContext.native.js.map +1 -1
  97. package/dist/esm/helpers/prettyFormatZeroQuery.js.map +1 -1
  98. package/dist/esm/helpers/prettyFormatZeroQuery.mjs.map +1 -1
  99. package/dist/esm/helpers/prettyFormatZeroQuery.native.js.map +1 -1
  100. package/dist/esm/helpers/useZeroDebug.js.map +1 -1
  101. package/dist/esm/helpers/useZeroDebug.mjs.map +1 -1
  102. package/dist/esm/helpers/useZeroDebug.native.js.map +1 -1
  103. package/dist/esm/index.js +1 -0
  104. package/dist/esm/index.js.map +1 -1
  105. package/dist/esm/index.mjs +1 -0
  106. package/dist/esm/index.mjs.map +1 -1
  107. package/dist/esm/index.native.js +1 -0
  108. package/dist/esm/index.native.js.map +1 -1
  109. package/dist/esm/mutations.js.map +1 -1
  110. package/dist/esm/mutations.mjs.map +1 -1
  111. package/dist/esm/mutations.native.js.map +1 -1
  112. package/dist/esm/queryRegistry.js +12 -0
  113. package/dist/esm/queryRegistry.js.map +6 -0
  114. package/dist/esm/queryRegistry.mjs +9 -0
  115. package/dist/esm/queryRegistry.mjs.map +1 -0
  116. package/dist/esm/queryRegistry.native.js +9 -0
  117. package/dist/esm/queryRegistry.native.js.map +1 -0
  118. package/dist/esm/serverWhere.js.map +1 -1
  119. package/dist/esm/serverWhere.mjs.map +1 -1
  120. package/dist/esm/serverWhere.native.js.map +1 -1
  121. package/dist/esm/state.js +1 -4
  122. package/dist/esm/state.js.map +1 -1
  123. package/dist/esm/state.mjs +1 -4
  124. package/dist/esm/state.mjs.map +1 -1
  125. package/dist/esm/state.native.js +1 -2
  126. package/dist/esm/state.native.js.map +1 -1
  127. package/dist/esm/where.js.map +1 -1
  128. package/dist/esm/where.mjs.map +1 -1
  129. package/dist/esm/where.native.js.map +1 -1
  130. package/dist/esm/zql.js.map +1 -1
  131. package/dist/esm/zql.mjs.map +1 -1
  132. package/dist/esm/zql.native.js.map +1 -1
  133. package/package.json +4 -4
  134. package/src/cli.ts +119 -122
  135. package/src/createPermissions.ts +8 -6
  136. package/src/createZeroClient.tsx +37 -15
  137. package/src/createZeroServer.ts +30 -15
  138. package/src/helpers/batchQuery.ts +2 -1
  139. package/src/helpers/createMutators.ts +3 -1
  140. package/src/helpers/didRunPermissionCheck.ts +1 -0
  141. package/src/helpers/ensureLoggedIn.ts +3 -1
  142. package/src/helpers/getQueryOrMutatorAuthData.ts +2 -1
  143. package/src/helpers/mutatorContext.ts +1 -0
  144. package/src/helpers/prettyFormatZeroQuery.ts +2 -1
  145. package/src/helpers/useZeroDebug.ts +3 -1
  146. package/src/index.ts +1 -0
  147. package/src/mutations.ts +2 -1
  148. package/src/queryRegistry.ts +12 -0
  149. package/src/serverWhere.ts +3 -2
  150. package/src/state.ts +2 -2
  151. package/src/types.ts +10 -9
  152. package/src/where.ts +3 -1
  153. package/src/zql.ts +1 -0
  154. package/types/createPermissions.d.ts +1 -1
  155. package/types/createPermissions.d.ts.map +1 -1
  156. package/types/createZeroClient.d.ts +5 -3
  157. package/types/createZeroClient.d.ts.map +1 -1
  158. package/types/createZeroServer.d.ts +4 -3
  159. package/types/createZeroServer.d.ts.map +1 -1
  160. package/types/helpers/batchQuery.d.ts.map +1 -1
  161. package/types/helpers/createMutators.d.ts.map +1 -1
  162. package/types/helpers/didRunPermissionCheck.d.ts.map +1 -1
  163. package/types/helpers/ensureLoggedIn.d.ts.map +1 -1
  164. package/types/helpers/getQueryOrMutatorAuthData.d.ts.map +1 -1
  165. package/types/helpers/mutatorContext.d.ts.map +1 -1
  166. package/types/helpers/prettyFormatZeroQuery.d.ts.map +1 -1
  167. package/types/helpers/useZeroDebug.d.ts.map +1 -1
  168. package/types/index.d.ts +1 -0
  169. package/types/index.d.ts.map +1 -1
  170. package/types/mutations.d.ts +1 -1
  171. package/types/mutations.d.ts.map +1 -1
  172. package/types/queryRegistry.d.ts +3 -0
  173. package/types/queryRegistry.d.ts.map +1 -0
  174. package/types/serverWhere.d.ts +1 -1
  175. package/types/serverWhere.d.ts.map +1 -1
  176. package/types/state.d.ts.map +1 -1
  177. package/types/types.d.ts.map +1 -1
  178. package/types/where.d.ts +1 -1
  179. package/types/where.d.ts.map +1 -1
  180. package/types/zql.d.ts.map +1 -1
  181. package/dist/cjs/builder.js +0 -26
  182. package/dist/cjs/builder.js.map +0 -6
  183. package/dist/cjs/builder.native.js +0 -35
  184. package/dist/cjs/builder.native.js.map +0 -1
  185. package/dist/cjs/helpers/context.cjs +0 -40
  186. package/dist/cjs/helpers/context.js +0 -36
  187. package/dist/cjs/helpers/context.js.map +0 -6
  188. package/dist/cjs/helpers/context.native.js +0 -43
  189. package/dist/cjs/helpers/context.native.js.map +0 -1
  190. package/dist/cjs/helpers/queryContext.cjs +0 -40
  191. package/dist/cjs/helpers/queryContext.js +0 -36
  192. package/dist/cjs/helpers/queryContext.js.map +0 -6
  193. package/dist/cjs/helpers/queryContext.native.js +0 -43
  194. package/dist/cjs/helpers/queryContext.native.js.map +0 -1
  195. package/dist/cjs/helpers/queryOrMutatorAuthData.cjs +0 -0
  196. package/dist/cjs/helpers/queryOrMutatorAuthData.js +0 -1
  197. package/dist/cjs/helpers/queryOrMutatorAuthData.js.map +0 -6
  198. package/dist/cjs/helpers/queryOrMutatorAuthData.native.js +0 -2
  199. package/dist/cjs/helpers/queryOrMutatorAuthData.native.js.map +0 -1
  200. package/dist/cjs/helpers/queryOrMutatorContext.cjs +0 -0
  201. package/dist/cjs/helpers/queryOrMutatorContext.js +0 -1
  202. package/dist/cjs/helpers/queryOrMutatorContext.js.map +0 -6
  203. package/dist/cjs/helpers/queryOrMutatorContext.native.js +0 -2
  204. package/dist/cjs/helpers/queryOrMutatorContext.native.js.map +0 -1
  205. package/dist/cjs/query.cjs +0 -34
  206. package/dist/cjs/query.js +0 -28
  207. package/dist/cjs/query.js.map +0 -6
  208. package/dist/cjs/query.native.js +0 -39
  209. package/dist/cjs/query.native.js.map +0 -1
  210. package/dist/cjs/queryBuilder.cjs +0 -31
  211. package/dist/cjs/queryBuilder.js.map +0 -6
  212. package/dist/cjs/queryBuilder.native.js.map +0 -1
  213. package/dist/esm/builder.js +0 -10
  214. package/dist/esm/builder.js.map +0 -6
  215. package/dist/esm/builder.mjs +0 -8
  216. package/dist/esm/builder.mjs.map +0 -1
  217. package/dist/esm/builder.native.js +0 -9
  218. package/dist/esm/builder.native.js.map +0 -1
  219. package/dist/esm/helpers/context.js +0 -20
  220. package/dist/esm/helpers/context.js.map +0 -6
  221. package/dist/esm/helpers/context.mjs +0 -15
  222. package/dist/esm/helpers/context.mjs.map +0 -1
  223. package/dist/esm/helpers/context.native.js +0 -15
  224. package/dist/esm/helpers/context.native.js.map +0 -1
  225. package/dist/esm/helpers/queryContext.js +0 -20
  226. package/dist/esm/helpers/queryContext.js.map +0 -6
  227. package/dist/esm/helpers/queryContext.mjs +0 -15
  228. package/dist/esm/helpers/queryContext.mjs.map +0 -1
  229. package/dist/esm/helpers/queryContext.native.js +0 -15
  230. package/dist/esm/helpers/queryContext.native.js.map +0 -1
  231. package/dist/esm/helpers/queryOrMutatorAuthData.js +0 -1
  232. package/dist/esm/helpers/queryOrMutatorAuthData.js.map +0 -6
  233. package/dist/esm/helpers/queryOrMutatorAuthData.mjs +0 -2
  234. package/dist/esm/helpers/queryOrMutatorAuthData.mjs.map +0 -1
  235. package/dist/esm/helpers/queryOrMutatorAuthData.native.js +0 -2
  236. package/dist/esm/helpers/queryOrMutatorAuthData.native.js.map +0 -1
  237. package/dist/esm/helpers/queryOrMutatorContext.js +0 -1
  238. package/dist/esm/helpers/queryOrMutatorContext.js.map +0 -6
  239. package/dist/esm/helpers/queryOrMutatorContext.mjs +0 -2
  240. package/dist/esm/helpers/queryOrMutatorContext.mjs.map +0 -1
  241. package/dist/esm/helpers/queryOrMutatorContext.native.js +0 -2
  242. package/dist/esm/helpers/queryOrMutatorContext.native.js.map +0 -1
  243. package/dist/esm/query.js +0 -13
  244. package/dist/esm/query.js.map +0 -6
  245. package/dist/esm/query.mjs +0 -11
  246. package/dist/esm/query.mjs.map +0 -1
  247. package/dist/esm/query.native.js +0 -13
  248. package/dist/esm/query.native.js.map +0 -1
  249. package/dist/esm/queryBuilder.js +0 -10
  250. package/dist/esm/queryBuilder.js.map +0 -6
  251. package/dist/esm/queryBuilder.mjs +0 -8
  252. package/dist/esm/queryBuilder.mjs.map +0 -1
  253. package/dist/esm/queryBuilder.native.js +0 -9
  254. package/dist/esm/queryBuilder.native.js.map +0 -1
package/src/cli.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env node
2
+ import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs'
3
+ import { basename, resolve } from 'node:path'
4
+
2
5
  import { ModelToValibot } from '@sinclair/typebox-codegen/model'
3
6
  import { TypeScriptToModel } from '@sinclair/typebox-codegen/typescript'
4
7
  import { defineCommand, runMain } from 'citty'
5
- import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs'
6
- import { basename, resolve } from 'node:path'
7
8
  import * as ts from 'typescript'
8
9
 
9
10
  /**
@@ -113,56 +114,6 @@ const generateQueries = defineCommand({
113
114
  },
114
115
  })
115
116
 
116
- function generateServerQueriesFile(
117
- queries: Array<{ name: string; params: string; valibotCode: string }>
118
- ) {
119
- const imports = `import * as v from 'valibot'\nimport * as queries from './queries'\n`
120
-
121
- const validators = queries
122
- .map((q) => {
123
- // extract just the schema definition (without import and type export)
124
- const lines = q.valibotCode.split('\n').filter((l) => l.trim())
125
- // find line starting with "export const QueryParams"
126
- const schemaLineIndex = lines.findIndex((l) =>
127
- l.startsWith('export const QueryParams')
128
- )
129
- if (schemaLineIndex === -1) {
130
- return `export const ${q.name}Schema = v.void()`
131
- }
132
-
133
- // get the full schema (might be multiline)
134
- const schemaLines: string[] = []
135
- let openBraces = 0
136
- let started = false
137
-
138
- for (let i = schemaLineIndex; i < lines.length; i++) {
139
- const line = lines[i]!
140
- const cleaned = started ? line : line.replace('export const QueryParams = ', '')
141
- schemaLines.push(cleaned)
142
- started = true
143
-
144
- // count braces to know when we're done
145
- openBraces += (cleaned.match(/\{/g) || []).length
146
- openBraces -= (cleaned.match(/\}/g) || []).length
147
- openBraces += (cleaned.match(/\(/g) || []).length
148
- openBraces -= (cleaned.match(/\)/g) || []).length
149
-
150
- // done when braces are balanced and we have at least one line
151
- if (openBraces === 0 && schemaLines.length > 0) {
152
- break
153
- }
154
- }
155
-
156
- const schemaDef = schemaLines.join('\n')
157
- return `export const ${q.name}Schema = ${schemaDef}`
158
- })
159
- .join('\n\n')
160
-
161
- const queryMap = `\nexport const queryValidators = {\n${queries.map((q) => ` ${q.name}: ${q.name}Schema,`).join('\n')}\n} as const\n`
162
-
163
- return imports + '\n' + validators + '\n' + queryMap
164
- }
165
-
166
117
  const generate = defineCommand({
167
118
  meta: {
168
119
  name: 'generate',
@@ -323,14 +274,23 @@ const generate = defineCommand({
323
274
  )
324
275
 
325
276
  const allQueries = queryResults.flat()
326
- const queriesOutput = generateQueriesFile(allQueries)
327
- const queriesChanged = writeFileIfChanged(
328
- resolve(generatedDir, 'queries.ts'),
329
- queriesOutput
277
+ const groupedQueriesOutput = generateGroupedQueriesFile(allQueries)
278
+ const syncedQueriesOutput = generateSyncedQueriesFile(allQueries)
279
+
280
+ const groupedChanged = writeFileIfChanged(
281
+ resolve(generatedDir, 'groupedQueries.ts'),
282
+ groupedQueriesOutput
283
+ )
284
+ const syncedChanged = writeFileIfChanged(
285
+ resolve(generatedDir, 'syncedQueries.ts'),
286
+ syncedQueriesOutput
330
287
  )
331
288
 
332
- if (queriesChanged) {
333
- console.info(` 📝 Updated queries.ts`)
289
+ if (groupedChanged) {
290
+ console.info(` 📝 Updated groupedQueries.ts`)
291
+ }
292
+ if (syncedChanged) {
293
+ console.info(` 📝 Updated syncedQueries.ts`)
334
294
  }
335
295
 
336
296
  console.info(
@@ -445,7 +405,7 @@ function generateTablesFile(modelFiles: string[]) {
445
405
  return `// auto-generated by: over-zero generate\n// this is separate from models as otherwise you end up with circular types :/\n\n${exports}\n`
446
406
  }
447
407
 
448
- function generateQueriesFile(
408
+ function generateGroupedQueriesFile(
449
409
  queries: Array<{
450
410
  name: string
451
411
  params: string
@@ -453,82 +413,119 @@ function generateQueriesFile(
453
413
  sourceFile: string
454
414
  }>
455
415
  ) {
456
- // sort queries alphabetically
457
- const sortedQueries = [...queries].sort((a, b) => a.name.localeCompare(b.name))
416
+ // get unique source files sorted
417
+ const sortedFiles = [...new Set(queries.map((q) => q.sourceFile))].sort()
418
+
419
+ // generate re-exports
420
+ const exports = sortedFiles
421
+ .map((file) => `export * as ${file} from '../queries/${file}'`)
422
+ .join('\n')
423
+
424
+ return `/**
425
+ * auto-generated by: over-zero generate
426
+ *
427
+ * grouped query re-exports for minification-safe query identity.
428
+ * this file re-exports all query modules - while this breaks tree-shaking,
429
+ * queries are typically small and few in number even in larger apps.
430
+ */
431
+ ${exports}
432
+ `
433
+ }
458
434
 
459
- // Group queries by source file
435
+ function generateSyncedQueriesFile(
436
+ queries: Array<{
437
+ name: string
438
+ params: string
439
+ valibotCode: string
440
+ sourceFile: string
441
+ }>
442
+ ) {
443
+ // group queries by source file
460
444
  const queryByFile = new Map<string, typeof queries>()
461
- for (const q of sortedQueries) {
445
+ for (const q of queries) {
462
446
  if (!queryByFile.has(q.sourceFile)) {
463
447
  queryByFile.set(q.sourceFile, [])
464
448
  }
465
449
  queryByFile.get(q.sourceFile)!.push(q)
466
450
  }
467
451
 
468
- // Generate imports for each query file (sorted)
469
- const queryImports = Array.from(queryByFile.keys())
470
- .sort()
471
- .map((file) => `import * as ${file}Queries from '../queries/${file}'`)
472
- .join('\n')
452
+ // sort file names for consistent output
453
+ const sortedFiles = Array.from(queryByFile.keys()).sort()
473
454
 
474
- const imports = `import * as v from 'valibot'
455
+ const imports = `// auto-generated by: over-zero generate
456
+ // server-side syncedQuery wrappers with validators
475
457
  import { syncedQuery } from '@rocicorp/zero'
476
- ${queryImports}
458
+ import * as v from 'valibot'
459
+ import * as Queries from './groupedQueries'
477
460
  `
478
461
 
479
- const syncedQueries = sortedQueries
480
- .map((q) => {
481
- // extract validator schema
482
- const lines = q.valibotCode.split('\n').filter((l) => l.trim())
483
- const schemaLineIndex = lines.findIndex((l) =>
484
- l.startsWith('export const QueryParams')
485
- )
462
+ // generate grouped exports by namespace
463
+ const namespaceExports = sortedFiles
464
+ .map((file) => {
465
+ const fileQueries = queryByFile
466
+ .get(file)!
467
+ .sort((a, b) => a.name.localeCompare(b.name))
468
+
469
+ const queryDefs = fileQueries
470
+ .map((q) => {
471
+ // extract validator schema
472
+ const lines = q.valibotCode.split('\n').filter((l) => l.trim())
473
+ const schemaLineIndex = lines.findIndex((l) =>
474
+ l.startsWith('export const QueryParams')
475
+ )
486
476
 
487
- let validatorDef = 'v.void()'
488
- if (schemaLineIndex !== -1) {
489
- const schemaLines: string[] = []
490
- let openBraces = 0
491
- let started = false
492
-
493
- for (let i = schemaLineIndex; i < lines.length; i++) {
494
- const line = lines[i]!
495
- const cleaned = started ? line : line.replace('export const QueryParams = ', '')
496
- schemaLines.push(cleaned)
497
- started = true
498
-
499
- openBraces += (cleaned.match(/\{/g) || []).length
500
- openBraces -= (cleaned.match(/\}/g) || []).length
501
- openBraces += (cleaned.match(/\(/g) || []).length
502
- openBraces -= (cleaned.match(/\)/g) || []).length
503
-
504
- if (openBraces === 0 && schemaLines.length > 0) {
505
- break
477
+ let validatorDef = 'v.void()'
478
+ if (schemaLineIndex !== -1) {
479
+ const schemaLines: string[] = []
480
+ let openBraces = 0
481
+ let started = false
482
+
483
+ for (let i = schemaLineIndex; i < lines.length; i++) {
484
+ const line = lines[i]!
485
+ const cleaned = started
486
+ ? line
487
+ : line.replace('export const QueryParams = ', '')
488
+ schemaLines.push(cleaned)
489
+ started = true
490
+
491
+ openBraces += (cleaned.match(/\{/g) || []).length
492
+ openBraces -= (cleaned.match(/\}/g) || []).length
493
+ openBraces += (cleaned.match(/\(/g) || []).length
494
+ openBraces -= (cleaned.match(/\)/g) || []).length
495
+
496
+ if (openBraces === 0 && schemaLines.length > 0) {
497
+ break
498
+ }
499
+ }
500
+ validatorDef = schemaLines.join('\n')
506
501
  }
507
- }
508
- validatorDef = schemaLines.join('\n')
509
- }
510
502
 
511
- // wrap validator in v.parser(v.tuple([...]))
512
- const wrappedValidator =
513
- validatorDef === 'v.void()'
514
- ? 'v.parser(v.tuple([]))'
515
- : `v.parser(v.tuple([${validatorDef}]))`
516
-
517
- // for void queries, no arg parameter
518
- const queryFn =
519
- validatorDef === 'v.void()'
520
- ? `() => {
521
- return ${q.sourceFile}Queries.${q.name}()
522
- }`
523
- : `(arg) => {
524
- return ${q.sourceFile}Queries.${q.name}(arg)
525
- }`
526
-
527
- return `export const ${q.name} = syncedQuery('${q.name}', ${wrappedValidator}, ${queryFn})`
503
+ // wrap validator in v.parser(v.tuple([...]))
504
+ const wrappedValidator =
505
+ validatorDef === 'v.void()'
506
+ ? 'v.parser(v.tuple([]))'
507
+ : `v.parser(v.tuple([${validatorDef}]))`
508
+
509
+ // namespaced query name: file.queryName
510
+ const namespacedName = `${q.sourceFile}.${q.name}`
511
+
512
+ // for void queries, no arg parameter
513
+ const queryFn =
514
+ validatorDef === 'v.void()'
515
+ ? `() => Queries.${file}.${q.name}()`
516
+ : `(arg) => Queries.${file}.${q.name}(arg)`
517
+
518
+ return ` ${q.name}: syncedQuery('${namespacedName}', ${wrappedValidator}, ${queryFn}),`
519
+ })
520
+ .join('\n')
521
+
522
+ return `export const ${file} = {\n${queryDefs}\n}`
528
523
  })
529
524
  .join('\n\n')
530
525
 
531
- return imports + '\n' + syncedQueries + '\n'
526
+ return `${imports}
527
+ ${namespaceExports}
528
+ `
532
529
  }
533
530
 
534
531
  function generateReadmeFile() {
@@ -541,7 +538,8 @@ this folder is auto-generated by over-zero. do not edit files here directly.
541
538
  - \`models.ts\` - exports all models from ../models
542
539
  - \`types.ts\` - typescript types derived from table schemas
543
540
  - \`tables.ts\` - exports table schemas for type inference
544
- - \`queries.ts\` - synced query definitions with validators
541
+ - \`groupedQueries.ts\` - namespaced query re-exports for client setup
542
+ - \`syncedQueries.ts\` - namespaced syncedQuery wrappers for server setup
545
543
 
546
544
  ## usage guidelines
547
545
 
@@ -549,16 +547,15 @@ this folder is auto-generated by over-zero. do not edit files here directly.
549
547
 
550
548
  ### queries
551
549
 
552
- don't import queries from this folder on the client. instead, import from \`../queries/\`:
550
+ write your queries as plain functions in \`../queries/\` and import them directly:
553
551
 
554
552
  \`\`\`ts
555
- // ❌ bad - don't import from generated
556
- import { channelMessages } from '~/data/generated/queries'
557
-
558
553
  // ✅ good - import from queries
559
554
  import { channelMessages } from '~/data/queries/message'
560
555
  \`\`\`
561
556
 
557
+ the generated query files are only used internally by zero client/server setup.
558
+
562
559
  ### types
563
560
 
564
561
  you can import types from this folder, but prefer re-exporting from \`../types.ts\`:
@@ -1,15 +1,17 @@
1
+ import { ensure, EnsureError } from '@vxrn/helpers'
2
+
3
+ import { setDidRunPermissionCheck } from './helpers/didRunPermissionCheck'
4
+ import { mutatorContext } from './helpers/mutatorContext'
5
+ import { prettyFormatZeroQuery } from './helpers/prettyFormatZeroQuery'
6
+ import { getWhereTableName } from './where'
7
+
8
+ import type { AuthData, Can, TableName, Transaction, Where } from './types'
1
9
  import type {
2
10
  Condition,
3
11
  ExpressionBuilder,
4
12
  Query,
5
13
  Schema as ZeroSchema,
6
14
  } from '@rocicorp/zero'
7
- import { ensure, EnsureError } from '@vxrn/helpers'
8
- import { setDidRunPermissionCheck } from './helpers/didRunPermissionCheck'
9
- import { mutatorContext } from './helpers/mutatorContext'
10
- import { prettyFormatZeroQuery } from './helpers/prettyFormatZeroQuery'
11
- import type { AuthData, Can, TableName, Transaction, Where } from './types'
12
- import { getWhereTableName } from './where'
13
15
 
14
16
  export function createPermissions<Schema extends ZeroSchema>({
15
17
  environment,
@@ -1,34 +1,56 @@
1
- import type {
2
- HumanReadable,
3
- Query,
4
- ReadonlyJSONValue,
5
- Row,
6
- SyncedQuery,
7
- Zero,
8
- ZeroOptions,
9
- Schema as ZeroSchema,
10
- } from '@rocicorp/zero'
11
1
  import { syncedQuery } from '@rocicorp/zero'
12
2
  import { useZero, ZeroProvider, useQuery as zeroUseQuery } from '@rocicorp/zero/react'
13
3
  import { createEmitter, mapObject } from '@vxrn/helpers'
14
4
  import { createContext, use, useMemo, type ReactNode } from 'react'
5
+
15
6
  import { createPermissions } from './createPermissions'
16
7
  import { createMutators } from './helpers/createMutators'
17
8
  import { prettyFormatZeroQuery } from './helpers/prettyFormatZeroQuery'
18
9
  import { useZeroDebug } from './helpers/useZeroDebug'
10
+ import { registerQuery, getQueryName } from './queryRegistry'
19
11
  import { setAuthData, setSchema } from './state'
12
+
20
13
  import type { AuthData, GenericModels, GetZeroMutators, ZeroEvent } from './types'
14
+ import type {
15
+ HumanReadable,
16
+ Query,
17
+ ReadonlyJSONValue,
18
+ Row,
19
+ SyncedQuery,
20
+ Zero,
21
+ ZeroOptions,
22
+ Schema as ZeroSchema,
23
+ } from '@rocicorp/zero'
24
+
25
+ // grouped queries: { namespace: { queryName: queryFn } }
26
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
27
+ export type GroupedQueries = Record<string, Record<string, (...args: any[]) => any>>
21
28
 
22
29
  export function createZeroClient<
23
30
  Schema extends ZeroSchema,
24
31
  Models extends GenericModels,
25
- >({ schema, models }: { schema: Schema; models: Models }) {
32
+ >({
33
+ schema,
34
+ models,
35
+ groupedQueries,
36
+ }: {
37
+ schema: Schema
38
+ models: Models
39
+ groupedQueries: GroupedQueries
40
+ }) {
26
41
  type ZeroMutators = GetZeroMutators<Models>
27
42
  type ZeroInstance = Zero<Schema, ZeroMutators>
28
43
  type TableName = keyof ZeroInstance['query']
29
44
 
30
45
  setSchema(schema)
31
46
 
47
+ // build query registry from grouped queries
48
+ for (const [namespace, queries] of Object.entries(groupedQueries)) {
49
+ for (const [name, fn] of Object.entries(queries)) {
50
+ registerQuery(fn, `${namespace}.${name}`)
51
+ }
52
+ }
53
+
32
54
  const DisabledContext = createContext(false)
33
55
 
34
56
  const modelWritePermissions = mapObject(models, (val) => val.permissions) as {
@@ -170,7 +192,7 @@ export function createZeroClient<
170
192
  }
171
193
 
172
194
  const fn = queryOrFn
173
- const queryName = fn.name || 'anonymousQuery'
195
+ const queryName = getQueryName(fn) || fn.name || 'anonymousQuery'
174
196
 
175
197
  // Determine if this is Pattern 2 (with params) or Pattern 3 (no params)
176
198
  const hasParams =
@@ -200,7 +222,7 @@ export function createZeroClient<
200
222
  const out = zeroUseQuery(actualQuery, options)
201
223
 
202
224
  if (process.env.NODE_ENV === 'development') {
203
- // biome-ignore lint/correctness/useHookAtTopLevel: ok
225
+ // eslint-disable-next-line react-hooks/rules-of-hooks
204
226
  useZeroDebug(actualQuery, options, out)
205
227
  }
206
228
 
@@ -218,10 +240,10 @@ export function createZeroClient<
218
240
  ...props
219
241
  }: Omit<ZeroOptions<Schema, ZeroMutators>, 'schema' | 'mutators'> & {
220
242
  children: ReactNode
221
- authData?: AuthData
243
+ authData?: AuthData | null
222
244
  disable?: boolean
223
245
  }) => {
224
- const authData = authDataIn as AuthData
246
+ const authData = (authDataIn ?? null) as AuthData
225
247
 
226
248
  const mutators = useMemo(() => {
227
249
  setAuthData(authData)
@@ -1,19 +1,13 @@
1
- import type {
2
- HumanReadable,
3
- Query,
4
- ReadonlyJSONValue,
5
- SyncedQuery,
6
- Schema as ZeroSchema,
7
- } from '@rocicorp/zero'
8
- import type { TransactionProviderInput } from '@rocicorp/zero/pg'
9
1
  import { handleGetQueriesRequest, PushProcessor } from '@rocicorp/zero/pg'
10
2
  import { zeroNodePg } from '@rocicorp/zero/server/adapters/pg'
11
3
  import { assertString, randomId } from '@vxrn/helpers'
12
4
  import { Pool } from 'pg'
5
+
13
6
  import { createPermissions } from './createPermissions'
14
7
  import { createMutators } from './helpers/createMutators'
15
8
  import { isInZeroMutation, mutatorContext } from './helpers/mutatorContext'
16
9
  import { setAuthData, setSchema } from './state'
10
+
17
11
  import type {
18
12
  AsyncAction,
19
13
  AuthData,
@@ -21,21 +15,32 @@ import type {
21
15
  GetZeroMutators,
22
16
  Transaction,
23
17
  } from './types'
18
+ import type {
19
+ HumanReadable,
20
+ Query,
21
+ ReadonlyJSONValue,
22
+ SyncedQuery,
23
+ Schema as ZeroSchema,
24
+ } from '@rocicorp/zero'
25
+ import type { TransactionProviderInput } from '@rocicorp/zero/pg'
26
+
27
+ // grouped synced queries: { namespace: { queryName: SyncedQuery } }
28
+ export type SyncedQueries = Record<
29
+ string,
30
+ Record<string, SyncedQuery<any, any, any, any, any>>
31
+ >
24
32
 
25
33
  export function createZeroServer<
26
34
  Schema extends ZeroSchema,
27
35
  Models extends GenericModels,
28
36
  ServerActions extends Record<string, unknown>,
29
- Queries extends Record<string, SyncedQuery<any, any, any, any, any>> = Record<
30
- string,
31
- never
32
- >,
37
+ Queries extends SyncedQueries = Record<string, never>,
33
38
  >({
34
39
  createServerActions,
35
40
  database,
36
41
  schema,
37
42
  models,
38
- queries,
43
+ syncedQueries,
39
44
  }: {
40
45
  /**
41
46
  * The DB connection string, same as ZERO_UPSTREAM_DB
@@ -44,7 +49,7 @@ export function createZeroServer<
44
49
  schema: Schema
45
50
  models: Models
46
51
  createServerActions: () => ServerActions
47
- queries?: Queries
52
+ syncedQueries?: Queries
48
53
  }) {
49
54
  setSchema(schema)
50
55
 
@@ -107,6 +112,16 @@ export function createZeroServer<
107
112
  }
108
113
  }
109
114
 
115
+ // build flat lookup map from grouped syncedQueries: "namespace.queryName" => SyncedQuery
116
+ const queryLookup = new Map<string, SyncedQuery<any, any, any, any, any>>()
117
+ if (syncedQueries) {
118
+ for (const [namespace, queries] of Object.entries(syncedQueries)) {
119
+ for (const [queryName, syncedQuery] of Object.entries(queries)) {
120
+ queryLookup.set(`${namespace}.${queryName}`, syncedQuery)
121
+ }
122
+ }
123
+ }
124
+
110
125
  const handleQueryRequest = async ({
111
126
  authData,
112
127
  request,
@@ -115,7 +130,7 @@ export function createZeroServer<
115
130
  request: Request
116
131
  }) => {
117
132
  function getQuery(name: string, args: readonly ReadonlyJSONValue[]) {
118
- const q = queries?.[name]
133
+ const q = queryLookup.get(name)
119
134
  if (!q) {
120
135
  throw new Error(`No such query: ${name}`)
121
136
  }
@@ -1,6 +1,7 @@
1
- import type { Query, Row } from '@rocicorp/zero'
2
1
  import { sleep } from '@vxrn/helpers'
3
2
 
3
+ import type { Query, Row } from '@rocicorp/zero'
4
+
4
5
  export async function batchQuery<Q extends Query<any, any, any>, Item extends Row<Q>>(
5
6
  q: Q,
6
7
  mapper: (items: Item[]) => Promise<void>,
@@ -1,4 +1,7 @@
1
1
  import { isClient, isServer, mapObject, time } from '@vxrn/helpers'
2
+
3
+ import { runWithContext } from './mutatorContext'
4
+
2
5
  import type {
3
6
  AuthData,
4
7
  Can,
@@ -7,7 +10,6 @@ import type {
7
10
  MutatorContext,
8
11
  Transaction,
9
12
  } from '../types'
10
- import { runWithContext } from './mutatorContext'
11
13
 
12
14
  export function createMutators<Models extends GenericModels>({
13
15
  environment,
@@ -1,4 +1,5 @@
1
1
  import { globalValue } from '@vxrn/helpers'
2
+
2
3
  import type { MutatorContext } from '../types'
3
4
 
4
5
  const PermissionCheckRan = globalValue(
@@ -1,7 +1,9 @@
1
1
  import { ensure } from '@vxrn/helpers'
2
- import type { AuthData } from '../types'
2
+
3
3
  import { mutatorContext } from './mutatorContext'
4
4
 
5
+ import type { AuthData } from '../types'
6
+
5
7
  export const ensureLoggedIn = (): AuthData => {
6
8
  const { authData } = mutatorContext()
7
9
  ensure(authData, 'logged in')
@@ -1,7 +1,8 @@
1
1
  import { getAuthData } from '../state'
2
- import type { AuthData } from '../types'
3
2
  import { isInZeroMutation, mutatorContext } from './mutatorContext'
4
3
 
4
+ import type { AuthData } from '../types'
5
+
5
6
  export function getQueryOrMutatorAuthData(): AuthData | null {
6
7
  if (isInZeroMutation()) {
7
8
  return mutatorContext().authData as AuthData
@@ -1,4 +1,5 @@
1
1
  import { createAsyncContext } from '@vxrn/helpers'
2
+
2
3
  import type { MutatorContext } from '../types'
3
4
 
4
5
  const asyncContext = createAsyncContext<MutatorContext>()
@@ -1,6 +1,7 @@
1
- import type { Query } from '@rocicorp/zero'
2
1
  import { ellipsis } from '@vxrn/helpers'
3
2
 
3
+ import type { Query } from '@rocicorp/zero'
4
+
4
5
  export const prettyFormatZeroQuery = (
5
6
  query: Query<any, any, any>,
6
7
  mode: 'full' | 'minimal' = 'full'
@@ -1,8 +1,10 @@
1
- import type { Query } from '@rocicorp/zero'
2
1
  import { getCurrentComponentStack } from '@vxrn/helpers'
3
2
  import { useEffect, useId } from 'react'
3
+
4
4
  import { prettyFormatZeroQuery } from './prettyFormatZeroQuery'
5
5
 
6
+ import type { Query } from '@rocicorp/zero'
7
+
6
8
  const activeQueries = new Map<string, number>()
7
9
 
8
10
  // AST change tracking
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './createPermissions'
2
+ export * from './queryRegistry'
2
3
  export * from './helpers/batchQuery'
3
4
  export * from './helpers/createMutators'
4
5
  export * from './helpers/ensureLoggedIn'
package/src/mutations.ts CHANGED
@@ -1,5 +1,5 @@
1
- import type { TableBuilderWithColumns } from '@rocicorp/zero'
2
1
  import { getDidRunPermissionCheck } from './helpers/didRunPermissionCheck'
2
+
3
3
  import type {
4
4
  MutatorContext,
5
5
  TableInsertRow,
@@ -7,6 +7,7 @@ import type {
7
7
  TableUpdateRow,
8
8
  Where,
9
9
  } from './types'
10
+ import type { TableBuilderWithColumns } from '@rocicorp/zero'
10
11
 
11
12
  // two ways to use it:
12
13
  // - mutations({}) which doesn't add the "allowed" helper or add CRUD
@@ -0,0 +1,12 @@
1
+ // registry for query functions to their stable names
2
+ // this allows minification while preserving query identity
3
+
4
+ const queryNameRegistry = new WeakMap<Function, string>()
5
+
6
+ export function registerQuery(fn: Function, name: string) {
7
+ queryNameRegistry.set(fn, name)
8
+ }
9
+
10
+ export function getQueryName(fn: Function): string | undefined {
11
+ return queryNameRegistry.get(fn)
12
+ }
@@ -1,7 +1,8 @@
1
- import type { Condition } from '@rocicorp/zero'
2
- import type { TableName, Where } from './types'
3
1
  import { where } from './where'
4
2
 
3
+ import type { TableName, Where } from './types'
4
+ import type { Condition } from '@rocicorp/zero'
5
+
5
6
  export function serverWhere<Table extends TableName, Builder extends Where<Table>>(
6
7
  tableName: Table,
7
8
  builder: Builder