@async/db 0.2.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 (398) hide show
  1. package/CHANGELOG.md +167 -0
  2. package/README.md +431 -0
  3. package/SPEC.md +1429 -0
  4. package/db.config.example.mjs +128 -0
  5. package/dist/cli/args.d.ts +8 -0
  6. package/dist/cli/args.js +16 -0
  7. package/dist/cli/commands/create.d.ts +3 -0
  8. package/dist/cli/commands/create.js +13 -0
  9. package/dist/cli/commands/doctor.d.ts +3 -0
  10. package/dist/cli/commands/doctor.js +31 -0
  11. package/dist/cli/commands/generate.d.ts +6 -0
  12. package/dist/cli/commands/generate.js +24 -0
  13. package/dist/cli/commands/operations.d.ts +12 -0
  14. package/dist/cli/commands/operations.js +61 -0
  15. package/dist/cli/commands/schema.d.ts +11 -0
  16. package/dist/cli/commands/schema.js +1086 -0
  17. package/dist/cli/commands/serve.d.ts +9 -0
  18. package/dist/cli/commands/serve.js +18 -0
  19. package/dist/cli/commands/sync.d.ts +3 -0
  20. package/dist/cli/commands/sync.js +11 -0
  21. package/dist/cli/commands/types.d.ts +7 -0
  22. package/dist/cli/commands/types.js +37 -0
  23. package/dist/cli/commands/viewer.d.ts +6 -0
  24. package/dist/cli/commands/viewer.js +29 -0
  25. package/dist/cli/index.d.ts +2 -0
  26. package/dist/cli/index.js +108 -0
  27. package/dist/cli/output.d.ts +25 -0
  28. package/dist/cli/output.js +149 -0
  29. package/dist/cli/schema-prompt.d.ts +20 -0
  30. package/dist/cli/schema-prompt.js +66 -0
  31. package/dist/cli.d.ts +2 -0
  32. package/dist/cli.js +3 -0
  33. package/dist/client-cache.d.ts +105 -0
  34. package/dist/client-cache.js +916 -0
  35. package/dist/client.d.ts +64 -0
  36. package/dist/client.js +405 -0
  37. package/dist/config-public.d.ts +1 -0
  38. package/dist/config-public.js +1 -0
  39. package/dist/config.d.ts +54 -0
  40. package/dist/config.js +2 -0
  41. package/dist/csv.d.ts +1 -0
  42. package/dist/csv.js +1 -0
  43. package/dist/db.d.ts +3 -0
  44. package/dist/db.js +3 -0
  45. package/dist/doctor.d.ts +1 -0
  46. package/dist/doctor.js +1 -0
  47. package/dist/errors.d.ts +1 -0
  48. package/dist/errors.js +1 -0
  49. package/dist/features/config/defaults.d.ts +98 -0
  50. package/dist/features/config/defaults.js +95 -0
  51. package/dist/features/config/load.d.ts +11 -0
  52. package/dist/features/config/load.js +265 -0
  53. package/dist/features/config/public.d.ts +17 -0
  54. package/dist/features/config/public.js +75 -0
  55. package/dist/features/doctor/duplicate-ids.d.ts +18 -0
  56. package/dist/features/doctor/duplicate-ids.js +79 -0
  57. package/dist/features/doctor/field-consistency.d.ts +17 -0
  58. package/dist/features/doctor/field-consistency.js +48 -0
  59. package/dist/features/doctor/index.d.ts +39 -0
  60. package/dist/features/doctor/index.js +177 -0
  61. package/dist/features/doctor/relations.d.ts +22 -0
  62. package/dist/features/doctor/relations.js +90 -0
  63. package/dist/features/doctor/schema-guidance.d.ts +35 -0
  64. package/dist/features/doctor/schema-guidance.js +184 -0
  65. package/dist/features/generate/registry.d.ts +14 -0
  66. package/dist/features/generate/registry.js +37 -0
  67. package/dist/features/http/registry.d.ts +46 -0
  68. package/dist/features/http/registry.js +86 -0
  69. package/dist/features/operations/index.d.ts +49 -0
  70. package/dist/features/operations/index.js +199 -0
  71. package/dist/features/operations/maps.d.ts +1 -0
  72. package/dist/features/operations/maps.js +10 -0
  73. package/dist/features/operations/readiness.d.ts +30 -0
  74. package/dist/features/operations/readiness.js +228 -0
  75. package/dist/features/operations/runtime.d.ts +57 -0
  76. package/dist/features/operations/runtime.js +288 -0
  77. package/dist/features/runtime/collection.d.ts +51 -0
  78. package/dist/features/runtime/collection.js +198 -0
  79. package/dist/features/runtime/db.d.ts +152 -0
  80. package/dist/features/runtime/db.js +824 -0
  81. package/dist/features/runtime/document.d.ts +43 -0
  82. package/dist/features/runtime/document.js +111 -0
  83. package/dist/features/runtime/fanout.d.ts +24 -0
  84. package/dist/features/runtime/fanout.js +77 -0
  85. package/dist/features/runtime/json-pointer.d.ts +5 -0
  86. package/dist/features/runtime/json-pointer.js +49 -0
  87. package/dist/features/runtime/scope-state.d.ts +44 -0
  88. package/dist/features/runtime/scope-state.js +185 -0
  89. package/dist/features/runtime/state.d.ts +1 -0
  90. package/dist/features/runtime/state.js +1 -0
  91. package/dist/features/schema/api.d.ts +107 -0
  92. package/dist/features/schema/api.js +460 -0
  93. package/dist/features/schema/builders.d.ts +86 -0
  94. package/dist/features/schema/builders.js +110 -0
  95. package/dist/features/schema/fields.d.ts +38 -0
  96. package/dist/features/schema/fields.js +296 -0
  97. package/dist/features/schema/generated.d.ts +29 -0
  98. package/dist/features/schema/generated.js +32 -0
  99. package/dist/features/schema/locator.d.ts +16 -0
  100. package/dist/features/schema/locator.js +135 -0
  101. package/dist/features/schema/manifest.d.ts +91 -0
  102. package/dist/features/schema/manifest.js +384 -0
  103. package/dist/features/schema/metadata.d.ts +30 -0
  104. package/dist/features/schema/metadata.js +75 -0
  105. package/dist/features/schema/project.d.ts +46 -0
  106. package/dist/features/schema/project.js +442 -0
  107. package/dist/features/schema/relations.d.ts +38 -0
  108. package/dist/features/schema/relations.js +109 -0
  109. package/dist/features/schema/resolvers.d.ts +36 -0
  110. package/dist/features/schema/resolvers.js +111 -0
  111. package/dist/features/schema/resource.d.ts +75 -0
  112. package/dist/features/schema/resource.js +253 -0
  113. package/dist/features/schema/source-definitions.d.ts +21 -0
  114. package/dist/features/schema/source-definitions.js +29 -0
  115. package/dist/features/schema/sources.d.ts +83 -0
  116. package/dist/features/schema/sources.js +689 -0
  117. package/dist/features/schema/standard-schema.d.ts +57 -0
  118. package/dist/features/schema/standard-schema.js +232 -0
  119. package/dist/features/schema/validation.d.ts +69 -0
  120. package/dist/features/schema/validation.js +434 -0
  121. package/dist/features/storage/events.d.ts +12 -0
  122. package/dist/features/storage/events.js +30 -0
  123. package/dist/features/storage/json.d.ts +112 -0
  124. package/dist/features/storage/json.js +239 -0
  125. package/dist/features/storage/memory.d.ts +30 -0
  126. package/dist/features/storage/memory.js +44 -0
  127. package/dist/features/storage/resource-json.d.ts +31 -0
  128. package/dist/features/storage/resource-json.js +76 -0
  129. package/dist/features/storage/runtime.d.ts +37 -0
  130. package/dist/features/storage/runtime.js +184 -0
  131. package/dist/features/storage/source-metadata.d.ts +20 -0
  132. package/dist/features/storage/source-metadata.js +25 -0
  133. package/dist/features/storage/source.d.ts +37 -0
  134. package/dist/features/storage/source.js +60 -0
  135. package/dist/features/storage/static.d.ts +29 -0
  136. package/dist/features/storage/static.js +42 -0
  137. package/dist/features/sync/defaults.d.ts +21 -0
  138. package/dist/features/sync/defaults.js +21 -0
  139. package/dist/features/sync/index.d.ts +35 -0
  140. package/dist/features/sync/index.js +85 -0
  141. package/dist/features/sync/mirror-state.d.ts +14 -0
  142. package/dist/features/sync/mirror-state.js +4 -0
  143. package/dist/features/sync/runtime-dirs.d.ts +5 -0
  144. package/dist/features/sync/runtime-dirs.js +9 -0
  145. package/dist/features/sync/source-writes.d.ts +15 -0
  146. package/dist/features/sync/source-writes.js +27 -0
  147. package/dist/features/sync/synthetic-seed.d.ts +26 -0
  148. package/dist/features/sync/synthetic-seed.js +83 -0
  149. package/dist/features/viewer/manifest.d.ts +148 -0
  150. package/dist/features/viewer/manifest.js +165 -0
  151. package/dist/fs-utils.d.ts +1 -0
  152. package/dist/fs-utils.js +1 -0
  153. package/dist/generate/hono/app.d.ts +6 -0
  154. package/dist/generate/hono/app.js +51 -0
  155. package/dist/generate/hono/graphql.d.ts +7 -0
  156. package/dist/generate/hono/graphql.js +53 -0
  157. package/dist/generate/hono/index.d.ts +55 -0
  158. package/dist/generate/hono/index.js +140 -0
  159. package/dist/generate/hono/package.d.ts +6 -0
  160. package/dist/generate/hono/package.js +44 -0
  161. package/dist/generate/hono/readme.d.ts +13 -0
  162. package/dist/generate/hono/readme.js +28 -0
  163. package/dist/generate/hono/repository.d.ts +1 -0
  164. package/dist/generate/hono/repository.js +27 -0
  165. package/dist/generate/hono/rest.d.ts +1 -0
  166. package/dist/generate/hono/rest.js +38 -0
  167. package/dist/generate/hono/schema.d.ts +13 -0
  168. package/dist/generate/hono/schema.js +18 -0
  169. package/dist/generate/hono/sqlite.d.ts +20 -0
  170. package/dist/generate/hono/sqlite.js +266 -0
  171. package/dist/generate/hono/validators.d.ts +1 -0
  172. package/dist/generate/hono/validators.js +141 -0
  173. package/dist/generate/hono.d.ts +1 -0
  174. package/dist/generate/hono.js +1 -0
  175. package/dist/graphql/execute.d.ts +14 -0
  176. package/dist/graphql/execute.js +719 -0
  177. package/dist/graphql/http.d.ts +15 -0
  178. package/dist/graphql/http.js +29 -0
  179. package/dist/graphql/index.d.ts +3 -0
  180. package/dist/graphql/index.js +3 -0
  181. package/dist/graphql/parser.d.ts +54 -0
  182. package/dist/graphql/parser.js +433 -0
  183. package/dist/hono.d.ts +77 -0
  184. package/dist/hono.js +1 -0
  185. package/dist/index.d.ts +1065 -0
  186. package/dist/index.js +14 -0
  187. package/dist/integrations/hono.d.ts +136 -0
  188. package/dist/integrations/hono.js +508 -0
  189. package/dist/integrations/kv.d.ts +69 -0
  190. package/dist/integrations/kv.js +69 -0
  191. package/dist/integrations/postgres.d.ts +52 -0
  192. package/dist/integrations/postgres.js +113 -0
  193. package/dist/integrations/sqlite.d.ts +112 -0
  194. package/dist/integrations/sqlite.js +489 -0
  195. package/dist/integrations/vite.d.ts +45 -0
  196. package/dist/integrations/vite.js +111 -0
  197. package/dist/json.d.ts +48 -0
  198. package/dist/json.js +1 -0
  199. package/dist/jsonc.d.ts +1 -0
  200. package/dist/jsonc.js +1 -0
  201. package/dist/kv.d.ts +24 -0
  202. package/dist/kv.js +1 -0
  203. package/dist/mock.d.ts +1 -0
  204. package/dist/mock.js +1 -0
  205. package/dist/names.d.ts +1 -0
  206. package/dist/names.js +1 -0
  207. package/dist/operations.d.ts +3 -0
  208. package/dist/operations.js +3 -0
  209. package/dist/postgres.d.ts +24 -0
  210. package/dist/postgres.js +1 -0
  211. package/dist/redis.d.ts +14 -0
  212. package/dist/redis.js +1 -0
  213. package/dist/rest/formats.d.ts +80 -0
  214. package/dist/rest/formats.js +318 -0
  215. package/dist/rest/handler.d.ts +111 -0
  216. package/dist/rest/handler.js +833 -0
  217. package/dist/rest/shape.d.ts +33 -0
  218. package/dist/rest/shape.js +218 -0
  219. package/dist/schema-builders.d.ts +1 -0
  220. package/dist/schema-builders.js +1 -0
  221. package/dist/schema-manifest.d.ts +1 -0
  222. package/dist/schema-manifest.js +1 -0
  223. package/dist/schema.d.ts +193 -0
  224. package/dist/schema.js +6 -0
  225. package/dist/server.d.ts +116 -0
  226. package/dist/server.js +601 -0
  227. package/dist/shared/csv.d.ts +8 -0
  228. package/dist/shared/csv.js +149 -0
  229. package/dist/shared/errors.d.ts +40 -0
  230. package/dist/shared/errors.js +55 -0
  231. package/dist/shared/fs-utils.d.ts +4 -0
  232. package/dist/shared/fs-utils.js +30 -0
  233. package/dist/shared/jsonc.d.ts +2 -0
  234. package/dist/shared/jsonc.js +99 -0
  235. package/dist/shared/mock.d.ts +40 -0
  236. package/dist/shared/mock.js +83 -0
  237. package/dist/shared/names.d.ts +28 -0
  238. package/dist/shared/names.js +127 -0
  239. package/dist/shared/operations.d.ts +32 -0
  240. package/dist/shared/operations.js +302 -0
  241. package/dist/sqlite.d.ts +24 -0
  242. package/dist/sqlite.js +1 -0
  243. package/dist/state.d.ts +1 -0
  244. package/dist/state.js +1 -0
  245. package/dist/sync.d.ts +1 -0
  246. package/dist/sync.js +1 -0
  247. package/dist/tracing.d.ts +95 -0
  248. package/dist/tracing.js +260 -0
  249. package/dist/types.d.ts +51 -0
  250. package/dist/types.js +285 -0
  251. package/dist/viewer-manifest.d.ts +1 -0
  252. package/dist/viewer-manifest.js +1 -0
  253. package/dist/vite.d.ts +59 -0
  254. package/dist/vite.js +1 -0
  255. package/dist/web/json-viewer.d.ts +5 -0
  256. package/dist/web/json-viewer.js +176 -0
  257. package/dist/web/viewer.d.ts +12 -0
  258. package/dist/web/viewer.js +1015 -0
  259. package/docs/README.md +42 -0
  260. package/docs/architecture.md +112 -0
  261. package/docs/ci-and-release.md +177 -0
  262. package/docs/cms-storage-patterns.md +108 -0
  263. package/docs/concepts.md +141 -0
  264. package/docs/configuration.md +552 -0
  265. package/docs/fixtures-and-schemas.md +527 -0
  266. package/docs/fork-branch-workflows.md +108 -0
  267. package/docs/generated-files.md +174 -0
  268. package/docs/getting-started.md +165 -0
  269. package/docs/integrations.md +206 -0
  270. package/docs/json-production.md +120 -0
  271. package/docs/package-api.md +418 -0
  272. package/docs/prototype-to-production.md +378 -0
  273. package/docs/server-and-viewer.md +466 -0
  274. package/docs/store-graduation.md +120 -0
  275. package/docs/typescript-schema-sources.md +79 -0
  276. package/examples/advanced/README.md +55 -0
  277. package/examples/advanced/db/projects.schema.jsonc +44 -0
  278. package/examples/advanced/db/settings.jsonc +9 -0
  279. package/examples/advanced/db/users.json +23 -0
  280. package/examples/advanced/db/users.schema.mjs +31 -0
  281. package/examples/advanced/db.config.mjs +18 -0
  282. package/examples/advanced/example.json +5 -0
  283. package/examples/advanced/src/generated/db.types.d.ts +64 -0
  284. package/examples/basic/README.md +95 -0
  285. package/examples/basic/db/operations/get-user.jsonc +8 -0
  286. package/examples/basic/db/settings.json +7 -0
  287. package/examples/basic/db/users.schema.jsonc +36 -0
  288. package/examples/basic/db.config.mjs +68 -0
  289. package/examples/basic/example.json +5 -0
  290. package/examples/basic/src/generated/db.types.d.ts +39 -0
  291. package/examples/cms-json-publish/README.md +21 -0
  292. package/examples/cms-json-publish/db/navigation.json +7 -0
  293. package/examples/cms-json-publish/db/pages.json +18 -0
  294. package/examples/cms-json-publish/example.json +5 -0
  295. package/examples/cms-json-publish/src/cms.mjs +104 -0
  296. package/examples/computed-fields/README.md +93 -0
  297. package/examples/computed-fields/db/orders.schema.mjs +62 -0
  298. package/examples/computed-fields/db/posts.schema.mjs +59 -0
  299. package/examples/computed-fields/db/products.schema.mjs +39 -0
  300. package/examples/computed-fields/db/users.schema.mjs +43 -0
  301. package/examples/computed-fields/db.config.mjs +15 -0
  302. package/examples/computed-fields/example.json +5 -0
  303. package/examples/computed-fields/src/generated/db.types.d.ts +81 -0
  304. package/examples/content-collections/README.md +91 -0
  305. package/examples/content-collections/db/authors.json +12 -0
  306. package/examples/content-collections/db/authors.schema.mjs +20 -0
  307. package/examples/content-collections/db/blog/draft-roadmap.mdx +12 -0
  308. package/examples/content-collections/db/blog/index.schema.mjs +61 -0
  309. package/examples/content-collections/db/blog/launch-notes.mdx +15 -0
  310. package/examples/content-collections/db/docs/index.schema.mjs +32 -0
  311. package/examples/content-collections/db/docs/intro.mdx +11 -0
  312. package/examples/content-collections/db/docs/schema-workflow.mdx +10 -0
  313. package/examples/content-collections/db/site.schema.jsonc +21 -0
  314. package/examples/content-collections/db.config.mjs +26 -0
  315. package/examples/content-collections/example.json +5 -0
  316. package/examples/content-collections/src/content-preview.mjs +66 -0
  317. package/examples/content-collections/src/generated/db.types.d.ts +81 -0
  318. package/examples/csv/README.md +52 -0
  319. package/examples/csv/db/customers.csv +4 -0
  320. package/examples/csv/db.config.mjs +13 -0
  321. package/examples/csv/example.json +5 -0
  322. package/examples/data-first/README.md +54 -0
  323. package/examples/data-first/db/posts.json +16 -0
  324. package/examples/data-first/db/settings.json +8 -0
  325. package/examples/data-first/db/users.json +14 -0
  326. package/examples/data-first/db.config.mjs +13 -0
  327. package/examples/data-first/example.json +5 -0
  328. package/examples/diagnostics/README.md +55 -0
  329. package/examples/diagnostics/db/projects.schema.jsonc +27 -0
  330. package/examples/diagnostics/db/users.json +9 -0
  331. package/examples/diagnostics/db/users.schema.jsonc +23 -0
  332. package/examples/diagnostics/db.config.mjs +16 -0
  333. package/examples/diagnostics/example.json +5 -0
  334. package/examples/free-plan-upgrade/README.md +22 -0
  335. package/examples/free-plan-upgrade/db/appSettings.json +4 -0
  336. package/examples/free-plan-upgrade/db/projects.json +7 -0
  337. package/examples/free-plan-upgrade/example.json +5 -0
  338. package/examples/free-plan-upgrade/src/upgrade-tenant-to-paid.mjs +105 -0
  339. package/examples/hono-auth/README.md +74 -0
  340. package/examples/hono-auth/db/pages.schema.jsonc +44 -0
  341. package/examples/hono-auth/db/users.schema.jsonc +42 -0
  342. package/examples/hono-auth/db.config.mjs +17 -0
  343. package/examples/hono-auth/example.json +5 -0
  344. package/examples/hono-auth/package.json +14 -0
  345. package/examples/hono-auth/src/app.mjs +79 -0
  346. package/examples/hono-auth/src/server.mjs +13 -0
  347. package/examples/production-json/README.md +102 -0
  348. package/examples/production-json/db/appSettings.schema.jsonc +41 -0
  349. package/examples/production-json/db/featureFlags.schema.jsonc +84 -0
  350. package/examples/production-json/db/operations/get-control-plane.jsonc +6 -0
  351. package/examples/production-json/db/operations/get-feature-flag.jsonc +9 -0
  352. package/examples/production-json/db/operations/list-feature-flags.jsonc +8 -0
  353. package/examples/production-json/db/operations/read-public-settings.jsonc +8 -0
  354. package/examples/production-json/db.config.mjs +33 -0
  355. package/examples/production-json/example.json +5 -0
  356. package/examples/production-json/src/client-demo.mjs +28 -0
  357. package/examples/production-json/src/generated/db.types.d.ts +60 -0
  358. package/examples/relations/README.md +56 -0
  359. package/examples/relations/db/posts.schema.jsonc +46 -0
  360. package/examples/relations/db/users.schema.jsonc +34 -0
  361. package/examples/relations/db.config.mjs +13 -0
  362. package/examples/relations/example.json +5 -0
  363. package/examples/rest-client/README.md +54 -0
  364. package/examples/rest-client/db/settings.json +5 -0
  365. package/examples/rest-client/db/users.schema.jsonc +42 -0
  366. package/examples/rest-client/db.config.mjs +13 -0
  367. package/examples/rest-client/example.json +5 -0
  368. package/examples/rest-client/src/client-demo.mjs +24 -0
  369. package/examples/schema-first/README.md +55 -0
  370. package/examples/schema-first/db/auditEvents.schema.jsonc +24 -0
  371. package/examples/schema-first/db/settings.schema.jsonc +29 -0
  372. package/examples/schema-first/db/users.schema.jsonc +36 -0
  373. package/examples/schema-first/db.config.mjs +15 -0
  374. package/examples/schema-first/example.json +5 -0
  375. package/examples/schema-first/src/generated/db.types.d.ts +47 -0
  376. package/examples/schema-manifest/README.md +50 -0
  377. package/examples/schema-manifest/db/projects.schema.jsonc +48 -0
  378. package/examples/schema-manifest/db/users.schema.jsonc +35 -0
  379. package/examples/schema-manifest/db.config.mjs +41 -0
  380. package/examples/schema-manifest/example.json +5 -0
  381. package/examples/schema-manifest/src/generated/db.schema.json +130 -0
  382. package/examples/schema-manifest/src/generated/db.types.d.ts +50 -0
  383. package/examples/schema-ui/README.md +103 -0
  384. package/examples/schema-ui/db/pages.schema.jsonc +53 -0
  385. package/examples/schema-ui/db/users.schema.jsonc +30 -0
  386. package/examples/schema-ui/db.config.mjs +55 -0
  387. package/examples/schema-ui/example.json +5 -0
  388. package/examples/schema-ui/src/cms-ssr.mjs +276 -0
  389. package/examples/schema-ui/src/generated/db.schema.json +133 -0
  390. package/examples/schema-ui/src/generated/db.types.d.ts +46 -0
  391. package/examples/schema-ui/src/render-admin.mjs +175 -0
  392. package/examples/schema-ui/src/schema-ui-ssr-handler.mjs +149 -0
  393. package/examples/schema-ui/src/start-schema-ui-server.mjs +140 -0
  394. package/examples/standard-schema/README.md +55 -0
  395. package/examples/standard-schema/db/settings.schema.mjs +22 -0
  396. package/examples/standard-schema/db/users.schema.mjs +72 -0
  397. package/examples/standard-schema/example.json +5 -0
  398. package/package.json +108 -0
@@ -0,0 +1,527 @@
1
+ # Fixtures And Schemas
2
+
3
+ @async/db discovers fixture and schema sources recursively under the configured fixture folder, `./db` by default.
4
+
5
+ Supported built-in source formats:
6
+
7
+ ```txt
8
+ .json
9
+ .jsonc
10
+ .csv
11
+ .schema.json
12
+ .schema.jsonc
13
+ .schema.js
14
+ .schema.mjs
15
+ db.schema.js
16
+ db.schema.mjs
17
+ index.schema.js
18
+ index.schema.mjs
19
+ ```
20
+
21
+ `.schema.js` and `db.schema.js` use normal Node.js ESM rules. If the project root `package.json` is already `"type": "module"`, no extra marker is needed. If the root is not ESM, @async/db creates `db/package.json` with `"type": "module"` before loading `.schema.js` files inside the configured fixture folder. Set `schema.autoModulePackageJson: false` to manage that file yourself.
22
+
23
+ TypeScript schema files are intentionally not loaded directly in v1 because Node.js does not execute TypeScript without an explicit loader or build step. See [TypeScript Schema Sources](./typescript-schema-sources.md) for the supported compile-to-JavaScript workflow.
24
+
25
+ ## Data-First JSON Or JSONC
26
+
27
+ Use `db/users.json` or `db/users.jsonc` when you already have sample records and want @async/db to infer the collection schema.
28
+
29
+ ```json
30
+ [
31
+ {
32
+ "id": "u_1",
33
+ "name": "Ada Lovelace",
34
+ "active": true
35
+ }
36
+ ]
37
+ ```
38
+
39
+ Collections always get an id field. If a JSON, JSONC, or CSV collection fixture omits `id`, @async/db adds counter ids in the selected runtime store:
40
+
41
+ ```json
42
+ [
43
+ { "id": "1", "name": "Ada Lovelace" },
44
+ { "id": "2", "name": "Grace Hopper" }
45
+ ]
46
+ ```
47
+
48
+ By default, source files stay unchanged and generated ids are written to the selected runtime store.
49
+
50
+ ## CSV Fixtures
51
+
52
+ Use CSV when fixture data starts in a spreadsheet or export.
53
+
54
+ ```txt
55
+ db/users.csv
56
+ ```
57
+
58
+ ```csv
59
+ id,name,email,active
60
+ u_1,Ada Lovelace,ada@example.com,true
61
+ ```
62
+
63
+ `sync` parses the header row, infers a collection schema, and writes `.db/state/users.json` through the default JSON store. Source hashes are tracked so changed source fixtures refresh the selected runtime store, while unchanged source fixtures preserve runtime edits.
64
+
65
+ When a CSV is paired with a schema file, array fields stay arrays in runtime state. For example, a schema field like `"tags": { "type": "array", "items": { "type": "string" } }` accepts a CSV cell such as `renewal;priority` or a JSON array string such as `["renewal","priority"]`.
66
+
67
+ ## Schema Files
68
+
69
+ Use schema files when data inference is too loose or when you need future intent that is not represented in the seed records.
70
+
71
+ ```json
72
+ {
73
+ "kind": "collection",
74
+ "idField": "id",
75
+ "fields": {
76
+ "id": { "type": "string", "required": true },
77
+ "name": { "type": "string", "required": true },
78
+ "email": {
79
+ "type": "string",
80
+ "required": true,
81
+ "unique": true,
82
+ "pattern": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$",
83
+ "description": "Email address used for local sign-in."
84
+ },
85
+ "role": {
86
+ "type": "enum",
87
+ "values": ["admin", "user"],
88
+ "default": "user"
89
+ },
90
+ "tags": {
91
+ "type": "array",
92
+ "maxLength": 5,
93
+ "items": { "type": "string" }
94
+ },
95
+ "score": {
96
+ "type": "number",
97
+ "min": 0,
98
+ "max": 100
99
+ },
100
+ "schemaSnapshot": {
101
+ "type": "object",
102
+ "additionalProperties": true,
103
+ "fields": {
104
+ "version": { "type": "number" }
105
+ }
106
+ }
107
+ }
108
+ }
109
+ ```
110
+
111
+ Field constraints are checked during `sync`, schema validation, package API writes, REST writes, and GraphQL mutations. Use `nullable: true` when `null` is intentional. `datetime` fields validate as strings and generate TypeScript `string` types.
112
+
113
+ ## JavaScript Schema Sources
114
+
115
+ Executable schema files can use `@async/db/schema` helpers:
116
+
117
+ ```js
118
+ import { collection, field } from '@async/db/schema';
119
+
120
+ export default collection({
121
+ idField: 'id',
122
+ fields: {
123
+ id: field.string({ required: true }),
124
+ role: field.enum(['admin', 'user'], { default: 'user' }),
125
+ lastViewedAt: field.datetime(),
126
+ ownerPersonId: field.nullable(field.string()),
127
+ tags: field.array(field.string()),
128
+ schemaSnapshot: field.object({
129
+ version: field.number(),
130
+ }, { additionalProperties: true }),
131
+ },
132
+ seed: [],
133
+ });
134
+ ```
135
+
136
+ `.schema.js` files execute as trusted local project JavaScript. If the root package is not ESM, @async/db creates the fixture-folder package marker described at the top of this guide.
137
+
138
+ ## Standard Schema Validators
139
+
140
+ Executable schema modules can also use any object that implements the Standard Schema
141
+ v1 contract. @async/db imports your trusted schema module, recognizes
142
+ `value['~standard'].version === 1`, and calls
143
+ `value['~standard'].validate(...)` during schema helpers and runtime writes.
144
+ The core package does not bundle a validator-library dependency. That means
145
+ Zod, Valibot, ArkType, or a local validator can own parsing and validation
146
+ while Async DB still applies its own lightweight metadata checks for defaults,
147
+ read-only/computed fields, uniqueness, relations, generated metadata, REST, and
148
+ GraphQL.
149
+
150
+ ```js
151
+ import { collection, field } from '@async/db/schema';
152
+
153
+ const UserSchema = {
154
+ '~standard': {
155
+ version: 1,
156
+ vendor: 'my-validator',
157
+ async validate(value) {
158
+ if (!value || typeof value !== 'object' || typeof value.email !== 'string') {
159
+ return { issues: [{ message: 'Email is required', path: ['email'] }] };
160
+ }
161
+ return {
162
+ value: {
163
+ ...value,
164
+ email: value.email.trim().toLowerCase(),
165
+ },
166
+ };
167
+ },
168
+ jsonSchema: {
169
+ output() {
170
+ return {
171
+ type: 'object',
172
+ required: ['email'],
173
+ properties: {
174
+ id: { type: 'string' },
175
+ email: { type: 'string' },
176
+ },
177
+ };
178
+ },
179
+ },
180
+ },
181
+ };
182
+
183
+ export default collection({
184
+ idField: 'id',
185
+ validator: UserSchema,
186
+ fields: {
187
+ email: field.string({
188
+ required: true,
189
+ unique: true,
190
+ description: 'Normalized login email.',
191
+ }),
192
+ displayName: field.computed(field.string(), ({ record }) => record.email),
193
+ },
194
+ seed: [],
195
+ });
196
+ ```
197
+
198
+ That object-first form keeps Async DB's simplified schema as the main shape and
199
+ mixes Standard Schema in as the parser/validator through `validator`. The equivalent
200
+ validator-first shorthand is useful when the external validator owns the field
201
+ shape:
202
+
203
+ ```js
204
+ export default collection(UserSchema, {
205
+ fields: {
206
+ email: field.meta({ unique: true }),
207
+ displayName: field.computed(field.string(), ({ record }) => record.email),
208
+ },
209
+ });
210
+ ```
211
+
212
+ Set `schema.standardSchema: true` in `db.config.mjs` when generated
213
+ executable schema files should prefer that validator-first form for resources that
214
+ have a Standard Schema validator. Detection still works without the config flag;
215
+ the flag only changes generated authoring output.
216
+
217
+ If the validator exposes a Standard JSON Schema converter, @async/db uses that
218
+ for generated field metadata and TypeScript output. `field.meta(...)` overlays
219
+ Async DB metadata such as descriptions, defaults, uniqueness, relations,
220
+ constraints, and manifest hints. `field.computed(...)` remains the resolver
221
+ entrypoint.
222
+
223
+ Package API, REST, and GraphQL writes await async Standard Schema validators and
224
+ store the returned `value`. Synchronous helpers such as
225
+ `schema.validator('users').assert(...)` work for sync validators; if a validator
226
+ returns a Promise, use `validateAsync(...)` or `assertAsync(...)`. The sync path
227
+ throws `DB_SCHEMA_ASYNC_VALIDATOR_REQUIRED` with that hint.
228
+
229
+ Standard Schema issues become `STANDARD_SCHEMA_VALIDATION_FAILED` diagnostics
230
+ with a normalized field path, vendor, issue path, message, and recovery hint.
231
+ Validator and resolver functions stay in trusted local code and are never
232
+ serialized into generated schema, manifests, viewer metadata, or TypeScript
233
+ output.
234
+
235
+ When a Standard Schema validator has no JSON Schema converter and no
236
+ `field.meta(...)` overlays, generated TypeScript uses a conservative
237
+ `[key: string]: unknown` fallback and the project diagnostics include
238
+ `STANDARD_SCHEMA_FIELDS_UNKNOWN`. Add overlays or a converter when generated
239
+ metadata needs to be richer.
240
+
241
+ See [examples/standard-schema](../examples/standard-schema) for a runnable
242
+ dependency-free example.
243
+
244
+ ## Root Schema Registry
245
+
246
+ Use `db.schema.js` at the project root when you want one canonical schema registry:
247
+
248
+ ```js
249
+ import { collection, field } from '@async/db/schema';
250
+
251
+ export default {
252
+ users: collection({
253
+ idField: 'id',
254
+ fields: {
255
+ id: field.string({ required: true }),
256
+ firstName: field.string(),
257
+ lastName: field.string(),
258
+ fullName: field.computed(field.string(), function users_fullName_resolver({ record }) {
259
+ return `${record.firstName} ${record.lastName}`;
260
+ }),
261
+ },
262
+ }),
263
+ };
264
+ ```
265
+
266
+ When `db.schema.js` or `db.schema.mjs` exists it is authoritative for explicit schemas. `db.schema.js` follows the root package `type` setting, so use root `"type": "module"` for that file. If both root files exist, `db.schema.mjs` wins and @async/db emits a duplicate-root warning. Per-resource `db/**/*.schema.*` files are not auto-discovered as live schemas, though the root schema may import them like normal JavaScript.
267
+
268
+ ## Computed Fields
269
+
270
+ Use computed fields when an explicit schema needs trusted project code to derive
271
+ read-only values for REST and GraphQL projections:
272
+
273
+ ```js
274
+ import { collection, field } from '@async/db/schema';
275
+
276
+ export default collection({
277
+ idField: 'id',
278
+ fields: {
279
+ id: field.string({ required: true }),
280
+ firstName: field.string({ required: true }),
281
+ lastName: field.string({ required: true }),
282
+ fullName: field.computed(field.string(), function users_fullName_resolver({ record }) {
283
+ return `${record.firstName} ${record.lastName}`;
284
+ }),
285
+ displayName: field.computed(field.string(), {
286
+ resolve({ record }) {
287
+ return `${record.firstName} ${record.lastName}`;
288
+ },
289
+ resolveMany({ records }) {
290
+ return records.map((record) => `${record.firstName} ${record.lastName}`);
291
+ },
292
+ }),
293
+ },
294
+ });
295
+ ```
296
+
297
+ `field.computed(type, fn)` is shorthand for `{ resolve: fn }`. Normal function
298
+ resolvers receive a runtime `this` context with delegated lookup through
299
+ `this.get(name)` and `this.has(name)`. Internal values include `db`, `resource`,
300
+ `field`, `fieldName`, `config`, `services`, `cache`, `value`, `record`,
301
+ `records`, and `args`. App-provided context can override those names, and
302
+ `this._internal` exposes the original internal values when a resolver needs them.
303
+ Arrow functions preserve JavaScript arrow semantics and cannot use runtime
304
+ `this`.
305
+
306
+ Computed fields are read-only and are rejected on package API, REST, GraphQL,
307
+ and registered operation writes. Generated schema, viewer manifest, and
308
+ TypeScript output include serializable metadata such as `computed` and
309
+ `readOnly`, but resolver functions stay in memory only.
310
+
311
+ REST resolves computed fields only when selected:
312
+
313
+ ```txt
314
+ GET /db/users/u_1.json?select=id,fullName
315
+ ```
316
+
317
+ GraphQL selections use the same projection/fanout layer. Collection reads prefer
318
+ `resolveMany` so one resolver can handle the selected page of records; single
319
+ reads and fields without `resolveMany` fall back to `resolve`.
320
+
321
+ Server code can call the same field resolvers without opening writable stores:
322
+
323
+ ```ts
324
+ import { loadDbSchema } from '@async/db';
325
+
326
+ const schema = await loadDbSchema({ from: './db.schema.js' });
327
+ const userResolvers = schema.resolver('users', {
328
+ value: input,
329
+ context: {
330
+ locale: 'en-US',
331
+ nameFormatter,
332
+ },
333
+ });
334
+
335
+ const fullName = await userResolvers.fullName();
336
+ ```
337
+
338
+ Use `schema.resolver('users.fullName')` when one field resolver is enough. The
339
+ call argument is plain JavaScript; schema authors can type and interpret it for
340
+ their own use case.
341
+
342
+ ## Folder Content Collections
343
+
344
+ Use `index.schema.js` as an explicit folder-as-collection marker. The collection
345
+ name comes from the folder:
346
+
347
+ ```txt
348
+ db/docs/index.schema.js
349
+ db/docs/intro.mdx
350
+ db/docs/getting-started.mdx
351
+ ```
352
+
353
+ Folder collections require an explicit `source` glob:
354
+
355
+ ```js
356
+ import { collection, field, files } from '@async/db/schema';
357
+
358
+ export default collection({
359
+ source: files('./**/*.mdx', { read: 'frontmatter' }),
360
+ fields: {
361
+ id: field.string({ required: true }),
362
+ title: field.string({ required: true }),
363
+ body: field.string(),
364
+ },
365
+ });
366
+ ```
367
+
368
+ Runtime store behavior stays in `db.config.mjs`, not in the schema file:
369
+
370
+ ```js
371
+ export default {
372
+ resources: {
373
+ docs: {
374
+ store: 'static',
375
+ },
376
+ },
377
+ };
378
+ ```
379
+
380
+ Core parses frontmatter and raw `.md` / `.mdx` body text. MDX compilation stays
381
+ app-owned JavaScript and is not a core dependency.
382
+
383
+ The built-in frontmatter parser is deliberately small and dependency-free. It
384
+ supports scalar `key: value` pairs plus the raw body string; keep arrays, nested
385
+ frontmatter, rich validation, and MDX compilation in app code when you need
386
+ them. The [content collections example](../examples/content-collections) shows
387
+ that pattern with docs and blog folders, static stores, relations to normal
388
+ fixture records, computed fields, and an example-owned preview renderer.
389
+
390
+ ## Bundle And Unbundle
391
+
392
+ Single-resource bundle and unbundle commands keep their existing behavior:
393
+
394
+ ```bash
395
+ npm run db -- schema bundle users --out artifacts/users.bundle.schema.json
396
+ npm run db -- schema unbundle users
397
+ ```
398
+
399
+ These single-resource JSON artifacts serialize Async DB metadata and seed data;
400
+ they do not serialize executable validator or resolver functions.
401
+
402
+ If you omit the resource in an interactive terminal, the CLI prompts for either
403
+ `All schemas` or a specific resource. Use `--all` in scripts to skip the prompt.
404
+
405
+ Aggregate root schema output is schema-only and never embeds seed/data fixtures:
406
+
407
+ ```bash
408
+ npm run db -- schema bundle --all
409
+ npm run db -- schema unbundle --all --schema-dir db
410
+ ```
411
+
412
+ If aggregate bundling finds non-empty `seed` embedded in a schema source and no
413
+ separate data fixture is loaded, it first writes that seed to
414
+ `db/<resource>.json`, then writes the root schema without seed. The default root
415
+ schema output is `db.schema.js` when the project root package is ESM, and
416
+ `db.schema.mjs` otherwise.
417
+
418
+ Folder collection source globs are rebased for the generated root file. For
419
+ example, `source: files('./**/*.mdx', { read: 'frontmatter' })` inside
420
+ `db/blog/index.schema.js` becomes
421
+ `source: files('./db/blog/**/*.mdx', { read: 'frontmatter' })` in
422
+ the root schema, so the registry can load the same content files.
423
+
424
+ When aggregate bundling sees computed resolvers or Standard Schema validators
425
+ from existing executable schema modules, the generated root schema imports the
426
+ original module, references its validator, and emits inline named resolver
427
+ wrappers to preserve behavior. Aggregate unbundle writes `.schema.js` files for
428
+ resources with executable validators or resolvers when the output folder can be
429
+ ESM. For the default `db/` folder, @async/db creates `db/package.json` with
430
+ `"type": "module"` when the root package is not already ESM and
431
+ `schema.autoModulePackageJson` is enabled. If `.js` would not load safely,
432
+ unbundle falls back to `.schema.mjs`. Schema, manifest, type,
433
+ doctor, bundle, unbundle, and generated starter commands import trusted schema
434
+ modules for metadata but do not call computed resolvers.
435
+
436
+ ## Inference
437
+
438
+ Inspect inferred contracts:
439
+
440
+ ```bash
441
+ npm run db -- schema infer
442
+ npm run db -- schema infer users
443
+ npm run db -- schema infer users --out db/users.schema.jsonc
444
+ ```
445
+
446
+ Use inference to move from fuzzy seed data toward explicit schema. If an explicit schema already exists, inference can still show what the current data implies.
447
+
448
+ ## Source Readers
449
+
450
+ @async/db reads all source files through a reader pipeline. Built-in readers handle JSON, JSONC, CSV, and schema files. Add `sources.readers` when another file format should remain the source of truth.
451
+
452
+ ```js
453
+ // db.config.mjs
454
+ // @ts-check
455
+ import { defineConfig } from '@async/db/config';
456
+
457
+ export default defineConfig({
458
+ sources: {
459
+ readers: [
460
+ {
461
+ name: 'pipe-data',
462
+ match({ file }) {
463
+ return file.endsWith('.pipe');
464
+ },
465
+ async read({ readText }) {
466
+ const rows = (await readText()).trim().split('\n');
467
+ return {
468
+ kind: 'data',
469
+ resourceName: 'users',
470
+ format: 'pipe',
471
+ data: rows.map((row) => {
472
+ const [id, name] = row.split('|');
473
+ return { id, name };
474
+ }),
475
+ };
476
+ },
477
+ },
478
+ ],
479
+ },
480
+ });
481
+ ```
482
+
483
+ Custom readers run before built-in readers. The first reader that returns a result owns the file; returning `null` lets the next reader try. One file may return multiple sources, but every returned source must include `resourceName`.
484
+
485
+ ## Nested Fixture Folders
486
+
487
+ Fixtures can be grouped under `db/` without changing resource names when basenames are unique:
488
+
489
+ ```txt
490
+ db/
491
+ cms/
492
+ pages.schema.jsonc
493
+ pages.json
494
+ analytics/
495
+ charts.schema.jsonc
496
+ charts.json
497
+ ```
498
+
499
+ That layout creates `pages` and `charts` resources. If nested folders contain repeated basenames, configure naming:
500
+
501
+ ```js
502
+ import { defineConfig } from '@async/db/config';
503
+
504
+ export default defineConfig({
505
+ resources: {
506
+ naming: 'folder-prefixed',
507
+ },
508
+ });
509
+ ```
510
+
511
+ Naming options:
512
+
513
+ | Option | Example path | Resource name | Use when |
514
+ | --- | --- | --- | --- |
515
+ | `basename` | `db/cms/pages.json` | `pages` | Fixture basenames are unique. |
516
+ | `folder-prefixed` | `db/cms/pages.json` | `cmsPages` | Folders are domains and repeated filenames are common. |
517
+ | `path` | `db/cms/landing/pages.json` | `cmsLandingPages` | Deep folder structure should become part of the API name. |
518
+ | `customizeResource` | `db/marketing/pages.json` | `landingPages` | Public API names must be explicit and stable. |
519
+
520
+ Resource names affect state files, REST routes, GraphQL root fields, generated TypeScript, and relation targets.
521
+
522
+ ## Related Examples
523
+
524
+ - [Data-first](../examples/data-first/README.md)
525
+ - [CSV](../examples/csv/README.md)
526
+ - [Schema-first](../examples/schema-first/README.md)
527
+ - [Advanced](../examples/advanced/README.md)
@@ -0,0 +1,108 @@
1
+ # Fork And Branch Workflows
2
+
3
+ `@async/db` treats forks and branches as generic database lifecycle primitives. App code gives those primitives business meaning.
4
+
5
+ ## Concepts
6
+
7
+ - `fork`: isolated logical database instance, useful for tenants, sandboxes, templates, demos, and prod-debug copies.
8
+ - `branch`: named data line inside one fork, useful for `main`, `draft`, `published`, previews, migrations, and debug work.
9
+ - `snapshot`: immutable captured state of a fork branch.
10
+ - `migration`: app-controlled move of one resource from one store to another.
11
+
12
+ Fork and branch records keep app labels in `metadata`. Core does not have a top-level `kind` field for lifecycle records because concepts like `tenant`, `debug`, `draft`, or `preview` are app-owned.
13
+
14
+ The default root database already points at `main`, so simple apps can call resources and operations directly:
15
+
16
+ ```js
17
+ await db.query('projects.list');
18
+ await db.collection('projects').all();
19
+ ```
20
+
21
+ When an app has tenants or sandboxes, open or ensure the fork. A fork handle starts on its default `main` branch, so most app code does not need to mention `branch('main')`:
22
+
23
+ ```js
24
+ const tenant = await db.forks.ensure('tenant_acme', {
25
+ from: 'main',
26
+ metadata: { purpose: 'tenant' },
27
+ });
28
+
29
+ await tenant.query('projects.list');
30
+ ```
31
+
32
+ Open named branches only when you intentionally leave `main`:
33
+
34
+ ```js
35
+ const draft = await tenant.branches.open('draft');
36
+
37
+ await draft.query('projects.list');
38
+ ```
39
+
40
+ ## Free Plan To Paid Store
41
+
42
+ The app decides what "paid" means. `@async/db` only moves resources and switches routing:
43
+
44
+ ```js
45
+ export async function upgradeTenantToPaid({ tenantId }) {
46
+ const tenant = await db.forks.open(tenantId);
47
+
48
+ const backup = await tenant.snapshots.create({
49
+ label: 'before-paid-upgrade',
50
+ resources: ['projects'],
51
+ });
52
+
53
+ await tenant.migrations.start('projects-to-postgres', {
54
+ resources: ['projects'],
55
+ mode: 'read-only',
56
+ });
57
+
58
+ await tenant.resources.migrate('projects', {
59
+ from: 'json',
60
+ to: 'postgres',
61
+ });
62
+
63
+ await tenant.migrations.verify('projects-to-postgres', {
64
+ resources: ['projects'],
65
+ checks: ['count', 'checksum'],
66
+ });
67
+
68
+ await tenant.routing.set({
69
+ projects: 'postgres',
70
+ });
71
+
72
+ await tenant.migrations.finish('projects-to-postgres');
73
+ return backup;
74
+ }
75
+ ```
76
+
77
+ Before cutover, JSON is the live store. After cutover, Postgres is live and the JSON snapshot is backup/export data.
78
+
79
+ ## Prod Debug Snapshot
80
+
81
+ Debugging a production issue should not mutate production data:
82
+
83
+ ```js
84
+ export async function createDebugForkFromSnapshot({ snapshotId, ticketId }) {
85
+ return db.forks.create(`debug_${ticketId}`, {
86
+ from: { snapshot: snapshotId },
87
+ metadata: {
88
+ purpose: 'debug',
89
+ ticketId,
90
+ ttl: '24h',
91
+ },
92
+ });
93
+ }
94
+ ```
95
+
96
+ The debug fork can run destructive reproductions, then be deleted.
97
+
98
+ ## Other App-Owned Patterns
99
+
100
+ - Feature flag preview: branch flag resources, test rollout behavior, then promote.
101
+ - Settings rollback: snapshot settings before admin edits and restore if needed.
102
+ - Pricing plan staging: edit plan resources on a branch before publishing.
103
+ - Policy rule sandbox: branch permission rules and run access-decision tests.
104
+ - Prompt template experiment: compare generated outputs across prompt branches.
105
+ - Seed/demo template: create tenant forks from a template fork with `from: { fork: 'demo_template', branch: 'main' }`.
106
+ - Forked test environment: create a temporary fork, run destructive tests, delete it.
107
+
108
+ These helpers should live in app code. The package only owns the generic database lifecycle APIs.