@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
package/SPEC.md ADDED
@@ -0,0 +1,1429 @@
1
+ # JSON Fixture DB Spec
2
+
3
+ ## Schema And Type Generation
4
+
5
+ The `db/` folder can contain fixture data, schema/type definitions, or both.
6
+
7
+ ```txt
8
+ db/
9
+ users.json optional seed data
10
+ posts.json optional seed data
11
+ settings.json optional singleton data
12
+
13
+ users.schema.json optional schema/type source (strict JSON)
14
+ users.schema.jsonc optional schema/type source (JSON with comments)
15
+ users.schema.mjs optional executable schema source
16
+ users.schema.js optional executable schema source when package type is module
17
+ posts.schema.jsonc
18
+ settings.schema.jsonc
19
+
20
+ .db/
21
+ state/
22
+ wal/
23
+ migrations/
24
+ schema.generated.json
25
+ types/
26
+ index.d.ts generated TypeScript declarations
27
+ ```
28
+
29
+ Projects can also opt into committed generated types:
30
+
31
+ ```txt
32
+ src/generated/
33
+ db.types.d.ts committed generated types
34
+ ```
35
+
36
+ ## Developer Workflows
37
+
38
+ Developers can choose among data-first fixtures, schema/type-first fixtures, or mixed mode.
39
+
40
+ ### Data-First Fixtures
41
+
42
+ The simplest path is a JSON fixture:
43
+
44
+ ```txt
45
+ db/users.json
46
+ ```
47
+
48
+ ```json
49
+ [
50
+ {
51
+ "id": "u_1",
52
+ "name": "Ada Lovelace",
53
+ "email": "ada@example.com",
54
+ "role": "admin"
55
+ }
56
+ ]
57
+ ```
58
+
59
+ The tool infers:
60
+
61
+ ```txt
62
+ users collection
63
+ id field: id
64
+ fields: id, name, email, role
65
+ TypeScript type: User
66
+ REST routes
67
+ GraphQL fields
68
+ ```
69
+
70
+ ### Schema/Type-First Fixtures
71
+
72
+ Developers can define types without real data:
73
+
74
+ ```txt
75
+ db/users.schema.jsonc
76
+ ```
77
+
78
+ ```jsonc
79
+ {
80
+ // Users who can sign into the local test app.
81
+ "kind": "collection",
82
+ "idField": "id",
83
+
84
+ "fields": {
85
+ "id": {
86
+ "type": "string",
87
+ "required": true,
88
+ "description": "Stable user id."
89
+ },
90
+
91
+ "name": {
92
+ "type": "string",
93
+ "required": true,
94
+ "description": "Display name shown in the UI."
95
+ },
96
+
97
+ "email": {
98
+ "type": "string",
99
+ "required": true,
100
+ "unique": true,
101
+ "pattern": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$",
102
+ "description": "Unique email address."
103
+ },
104
+
105
+ "role": {
106
+ "type": "enum",
107
+ "values": ["admin", "user"],
108
+ "required": false,
109
+ "default": "user",
110
+ "description": "Local authorization role."
111
+ }
112
+ },
113
+
114
+ "seed": [
115
+ {
116
+ "id": "u_1",
117
+ "name": "Ada Lovelace",
118
+ "email": "ada@example.com",
119
+ "role": "admin"
120
+ }
121
+ ]
122
+ }
123
+ ```
124
+
125
+ This file acts as:
126
+
127
+ ```txt
128
+ schema source
129
+ TypeScript source
130
+ REST/GraphQL source
131
+ optional default seed data
132
+ documentation source
133
+ ```
134
+
135
+ Field schemas may declare practical local constraints:
136
+
137
+ ```txt
138
+ unique: true collection values must not repeat
139
+ min/max numeric lower and upper bounds
140
+ minLength/maxLength string or array length bounds
141
+ pattern JavaScript RegExp source for string values
142
+ ```
143
+
144
+ ### Mixed Mode
145
+
146
+ Developers can provide both a data fixture and a schema fixture:
147
+
148
+ ```txt
149
+ db/users.json
150
+ db/users.schema.jsonc
151
+ ```
152
+
153
+ In mixed mode:
154
+
155
+ ```txt
156
+ users.schema.jsonc controls the type/schema
157
+ users.json controls the seed records
158
+ ```
159
+
160
+ If the schema file also contains `seed`, that embedded seed is ignored in favor of
161
+ the data fixture. The CLI should warn and suggest unbundling the seed from the
162
+ schema source so mixed mode keeps contracts and seed records in separate files.
163
+ `async-db schema unbundle users` removes embedded seed from the schema source and
164
+ writes non-empty seed data to `db/users.json`. Empty schema-only seed is removed
165
+ without creating an empty fixture unless `--empty-seed` is passed. In-place JSONC
166
+ rewrites may lose comments, so the CLI should warn when it rewrites `.schema.jsonc`
167
+ without `--schema-out`.
168
+
169
+ `async-db schema bundle users` creates a portable schema-plus-seed artifact. Bundled
170
+ outputs should live outside the active fixture directory by default, such as
171
+ `artifacts/users.bundle.schema.json`, so they are not rediscovered as live schema
172
+ sources. Writing a bundle inside `db/` requires `--force`. Overwriting an existing
173
+ different seed or bundle output also requires `--force`.
174
+
175
+ `db.schema.mjs` at the project root is the canonical aggregate schema registry.
176
+ When present, it is authoritative for explicit schema definitions and
177
+ `db/**/*.schema.*` files are not auto-discovered as live schemas unless imported
178
+ by the root module. `async-db schema bundle --all --out db.schema.mjs` creates a
179
+ schema-only root registry without embedding seed/data fixtures. If a schema source
180
+ has embedded seed and no separate data fixture is loaded, aggregate bundle first
181
+ writes that seed to `db/<resource>.json` and leaves the root schema seed-free.
182
+ `async-db schema unbundle --all --schema-dir db` spreads a root registry back to
183
+ per-resource schema files and leaves seed/data fixtures untouched.
184
+ When bundling folder collection markers into a root registry, source globs are
185
+ rebased from the marker folder to the project root; for example,
186
+ `db/blog/index.schema.mjs` with `source: files('./**/*.mdx', { read: 'frontmatter' })`
187
+ becomes `source: files('./db/blog/**/*.mdx', { read: 'frontmatter' })` in
188
+ `db.schema.mjs`.
189
+
190
+ If the two disagree, the CLI reports the mismatch:
191
+
192
+ ```txt
193
+ users.json has field "twitterHandle"
194
+ users.schema.jsonc does not define "twitterHandle"
195
+ ```
196
+
197
+ Default behavior should be permissive in local development:
198
+
199
+ ```txt
200
+ warn and allow
201
+ ```
202
+
203
+ Configuration can enable stricter behavior:
204
+
205
+ ```js
206
+ export default {
207
+ schema: {
208
+ unknownFields: 'warn', // "allow" | "warn" | "error"
209
+ },
210
+ };
211
+ ```
212
+
213
+ ## Type Generation
214
+
215
+ By default, generated TypeScript types are written to:
216
+
217
+ ```txt
218
+ .db/types/index.d.ts
219
+ ```
220
+
221
+ Projects can customize the output location:
222
+
223
+ ```js
224
+ export default {
225
+ dbDir: './db',
226
+ stateDir: './.db',
227
+ schemaOutFile: './src/generated/db.schema.json',
228
+
229
+ schemaManifest: {
230
+ customizeField({ fieldName, defaultManifest }) {
231
+ if (fieldName.endsWith('Markdown')) {
232
+ return {
233
+ ...defaultManifest,
234
+ ui: {
235
+ ...defaultManifest.ui,
236
+ component: 'markdown',
237
+ },
238
+ };
239
+ }
240
+
241
+ return defaultManifest;
242
+ },
243
+ },
244
+
245
+ types: {
246
+ enabled: true,
247
+
248
+ // Default gitignored output.
249
+ outFile: './.db/types/index.d.ts',
250
+
251
+ // Optional committed output.
252
+ // If set, generate the same types here too.
253
+ commitOutFile: './src/generated/db.types.d.ts',
254
+
255
+ // Optional.
256
+ useReadonly: false,
257
+ exportRuntimeHelpers: true,
258
+ },
259
+ };
260
+ ```
261
+
262
+ This supports two common workflows.
263
+
264
+ ### Gitignored Generated Types
265
+
266
+ Good for quick local development:
267
+
268
+ ```ts
269
+ import type { DbTypes } from '../.db/types/index';
270
+ ```
271
+
272
+ ### Committed Generated Types
273
+
274
+ Better for apps and CI:
275
+
276
+ ```ts
277
+ import type { DbTypes } from './generated/db.types';
278
+ ```
279
+
280
+ If the app relies on generated types, committing them is usually better because CI and other developers do not need to run `async-db sync` before TypeScript can resolve imports.
281
+
282
+ ## Example Generated TypeScript
283
+
284
+ From `users.schema.jsonc`, generate something like this:
285
+
286
+ ```ts
287
+ export type UserRole = 'admin' | 'user';
288
+
289
+ export type User = {
290
+ /** Stable user id. */
291
+ id: string;
292
+
293
+ /** Display name shown in the UI. */
294
+ name: string;
295
+
296
+ /** Unique email address. */
297
+ email: string;
298
+
299
+ /** Local authorization role. */
300
+ role?: UserRole;
301
+ };
302
+
303
+ export type Settings = {
304
+ theme?: string;
305
+ locale?: string;
306
+ features?: {
307
+ billing?: boolean;
308
+ };
309
+ };
310
+
311
+ export type DbCollections = {
312
+ users: User;
313
+ };
314
+
315
+ export type DbDocuments = {
316
+ settings: Settings;
317
+ };
318
+
319
+ export type DbTypes = {
320
+ collections: DbCollections;
321
+ documents: DbDocuments;
322
+ };
323
+ ```
324
+
325
+ Package usage:
326
+
327
+ ```ts
328
+ import { openDb } from '@async/db';
329
+ import type { DbTypes } from './generated/db.types';
330
+
331
+ const db = await openDb<DbTypes>({
332
+ dbDir: './db',
333
+ stateDir: './.db',
334
+ stores: {
335
+ default: 'json',
336
+ },
337
+ });
338
+
339
+ const users = db.collection('users');
340
+
341
+ await users.create({
342
+ id: 'u_2',
343
+ name: 'Grace Hopper',
344
+ email: 'grace@example.com',
345
+ role: 'user',
346
+ });
347
+
348
+ const user = users.get('u_2');
349
+
350
+ if (user) {
351
+ console.log(user.email);
352
+ }
353
+ ```
354
+
355
+ Singleton document usage:
356
+
357
+ ```ts
358
+ const settings = db.document('settings');
359
+
360
+ await settings.set('/theme', 'dark');
361
+
362
+ const value = settings.get('/theme');
363
+ ```
364
+
365
+ ## JavaScript Schema Sources
366
+
367
+ JSONC is useful, but a JavaScript schema file can be more expressive while staying simple.
368
+
369
+ ```txt
370
+ db/users.schema.mjs
371
+ db/users.schema.js
372
+ ```
373
+
374
+ ```js
375
+ import { collection, field } from '@async/db/schema';
376
+
377
+ export default collection({
378
+ description: 'Users who can sign into the local test app.',
379
+ idField: 'id',
380
+
381
+ fields: {
382
+ id: field.string({
383
+ required: true,
384
+ description: 'Stable user id.',
385
+ }),
386
+
387
+ name: field.string({
388
+ required: true,
389
+ description: 'Display name shown in the UI.',
390
+ }),
391
+
392
+ email: field.string({
393
+ required: true,
394
+ description: 'Unique email address.',
395
+ }),
396
+
397
+ role: field.enum(['admin', 'user'], {
398
+ default: 'user',
399
+ description: 'Local authorization role.',
400
+ }),
401
+ },
402
+
403
+ seed: [
404
+ {
405
+ id: 'u_1',
406
+ name: 'Ada Lovelace',
407
+ email: 'ada@example.com',
408
+ role: 'admin',
409
+ },
410
+ ],
411
+ });
412
+ ```
413
+
414
+ This provides normal comments and a clean authoring API without requiring Node.js to load TypeScript files directly.
415
+
416
+ For v1, support:
417
+
418
+ ```txt
419
+ .json
420
+ .jsonc
421
+ .csv
422
+ .schema.json
423
+ .schema.jsonc
424
+ .schema.mjs
425
+ .schema.js
426
+ ```
427
+
428
+ Use `.schema.js` only with normal Node ESM rules: the nearest `package.json` must declare `"type": "module"`. Avoid direct `.ts` schema sources in v1; projects that author schemas in TypeScript should compile to `.schema.js` or `.schema.mjs`.
429
+
430
+ ## Source Readers
431
+
432
+ All built-in source loading should use the same reader pipeline:
433
+
434
+ ```txt
435
+ .json data
436
+ .jsonc data
437
+ .csv data
438
+ .schema.json
439
+ .schema.jsonc
440
+ .schema.mjs
441
+ .schema.js
442
+ ```
443
+
444
+ Projects may add `sources.readers` in `db.config.mjs` to parse custom files into raw db inputs:
445
+
446
+ ```ts
447
+ type DbSourceReader = {
448
+ name: string;
449
+ match(context): boolean | Promise<boolean>;
450
+ read(context): DbSourceReaderResult | Promise<DbSourceReaderResult>;
451
+ };
452
+
453
+ type DbSourceReaderResult =
454
+ | { kind: 'data'; data: unknown; format?: string; resourceName?: string }
455
+ | { kind: 'schema'; schema: unknown; format?: string; resourceName?: string }
456
+ | Array<DbSourceReaderResult>
457
+ | null;
458
+ ```
459
+
460
+ Custom readers run before built-in readers. Returning `null` lets later readers try; the first non-null result owns the file. Reader context includes repo-relative file path, absolute source path, parsed fixture path metadata, config, source hash, `readText()`, and `readBuffer()`.
461
+
462
+ Readers must return raw data or raw schema only. Resource normalization, diagnostics, type generation, schema manifest output, REST/GraphQL metadata, generated ids, and runtime sync stay centralized in db. A reader may return multiple sources from one file, but each result must include `resourceName`; otherwise db reports a structured diagnostic.
463
+
464
+ ## Type-Only Fixtures
465
+
466
+ A schema file can define a resource without seed data.
467
+
468
+ ```jsonc
469
+ {
470
+ // Audit events generated during local development.
471
+ "kind": "collection",
472
+ "idField": "id",
473
+
474
+ "fields": {
475
+ "id": {
476
+ "type": "string",
477
+ "required": true
478
+ },
479
+ "type": {
480
+ "type": "string",
481
+ "required": true
482
+ },
483
+ "createdAt": {
484
+ "type": "string",
485
+ "required": true
486
+ },
487
+ "payload": {
488
+ "type": "object",
489
+ "required": false,
490
+ "default": {}
491
+ }
492
+ },
493
+
494
+ "seed": []
495
+ }
496
+ ```
497
+
498
+ Generated runtime state:
499
+
500
+ ```txt
501
+ .db/state/auditEvents.json
502
+ ```
503
+
504
+ ```json
505
+ []
506
+ ```
507
+
508
+ Generated TypeScript:
509
+
510
+ ```ts
511
+ export type AuditEvent = {
512
+ id: string;
513
+ type: string;
514
+ createdAt: string;
515
+ payload?: Record<string, unknown>;
516
+ };
517
+ ```
518
+
519
+ Generated REST:
520
+
521
+ ```txt
522
+ GET /audit-events
523
+ GET /audit-events/:id
524
+ POST /audit-events
525
+ PATCH /audit-events/:id
526
+ DELETE /audit-events/:id
527
+ ```
528
+
529
+ Generated GraphQL:
530
+
531
+ ```graphql
532
+ type AuditEvent {
533
+ id: ID!
534
+ type: String
535
+ createdAt: String
536
+ payload: JSON
537
+ }
538
+ ```
539
+
540
+ ## Defaults
541
+
542
+ Defaults should be used in three places:
543
+
544
+ ```txt
545
+ 1. When creating new records through REST/GraphQL/package API.
546
+ 2. When backfilling safe additive schema changes.
547
+ 3. When initializing an empty runtime store.
548
+ ```
549
+
550
+ Example schema:
551
+
552
+ ```jsonc
553
+ {
554
+ "kind": "collection",
555
+ "idField": "id",
556
+ "fields": {
557
+ "id": {
558
+ "type": "string",
559
+ "required": true
560
+ },
561
+ "name": {
562
+ "type": "string",
563
+ "required": true
564
+ },
565
+ "role": {
566
+ "type": "enum",
567
+ "values": ["admin", "user"],
568
+ "default": "user"
569
+ },
570
+ "active": {
571
+ "type": "boolean",
572
+ "default": true
573
+ }
574
+ }
575
+ }
576
+ ```
577
+
578
+ Creating a user:
579
+
580
+ ```bash
581
+ async-db create users '{"id":"u_3","name":"Linus"}'
582
+ ```
583
+
584
+ Stored result:
585
+
586
+ ```json
587
+ {
588
+ "id": "u_3",
589
+ "name": "Linus",
590
+ "role": "user",
591
+ "active": true
592
+ }
593
+ ```
594
+
595
+ ## Comments And Descriptions
596
+
597
+ JSON itself does not support comments, so support comments through one or both of these:
598
+
599
+ ```txt
600
+ .schema.jsonc
601
+ .schema.mjs
602
+ ```
603
+
604
+ Comments are primarily for humans. For generated TypeScript and GraphQL docs, use machine-readable descriptions:
605
+
606
+ ```jsonc
607
+ {
608
+ "email": {
609
+ "type": "string",
610
+ "description": "Unique email address used for login."
611
+ }
612
+ }
613
+ ```
614
+
615
+ Generate:
616
+
617
+ ```ts
618
+ export type User = {
619
+ /** Unique email address used for login. */
620
+ email: string;
621
+ };
622
+ ```
623
+
624
+ And GraphQL:
625
+
626
+ ```graphql
627
+ type User {
628
+ "Unique email address used for login."
629
+ email: String
630
+ }
631
+ ```
632
+
633
+ ## Config
634
+
635
+ Add this to `db.config.mjs`:
636
+
637
+ ```js
638
+ export default {
639
+ dbDir: './db',
640
+ stateDir: './.db',
641
+
642
+ sources: {
643
+ writePolicy: 'preserve',
644
+ },
645
+
646
+ stores: {
647
+ default: 'json',
648
+ },
649
+
650
+ types: {
651
+ enabled: true,
652
+ outFile: './.db/types/index.d.ts',
653
+ commitOutFile: './src/generated/db.types.d.ts',
654
+ useReadonly: false,
655
+ emitComments: true,
656
+ },
657
+
658
+ schema: {
659
+ source: 'auto', // "auto" | "data" | "schema"
660
+ allowJsonc: true,
661
+ unknownFields: 'warn', // "allow" | "warn" | "error"
662
+ additiveChanges: 'auto',
663
+ destructiveChanges: 'manual',
664
+ typeChanges: 'manual',
665
+ },
666
+
667
+ defaults: {
668
+ applyOnCreate: true,
669
+ applyOnSafeMigration: true,
670
+ },
671
+
672
+ collections: {
673
+ users: {
674
+ idField: 'id',
675
+ },
676
+ },
677
+
678
+ server: {
679
+ apiBase: '/__db',
680
+ dataPath: '/db',
681
+ host: '127.0.0.1',
682
+ port: 7331,
683
+ maxBodyBytes: 1048576,
684
+ },
685
+
686
+ rest: {
687
+ enabled: true,
688
+ },
689
+
690
+ graphql: {
691
+ enabled: true,
692
+ path: '/graphql',
693
+ },
694
+ };
695
+ ```
696
+
697
+ Set `dbDir: './db'` to use `db/` instead of the default `db/` fixture folder. Existing `sourceDir` configs remain supported; if both are provided, `sourceDir` wins for backwards compatibility.
698
+
699
+ ## CLI
700
+
701
+ Add type-specific commands:
702
+
703
+ ```bash
704
+ async-db types
705
+ async-db types --watch
706
+ async-db types --out ./src/generated/db.types.d.ts
707
+ ```
708
+
709
+ Add schema commands:
710
+
711
+ ```bash
712
+ async-db schema
713
+ async-db schema users
714
+ async-db schema manifest --out ./src/generated/db.schema.json
715
+ async-db schema validate
716
+ ```
717
+
718
+ `async-db sync` should also regenerate types and should write the committed schema manifest when `schemaOutFile` is configured.
719
+
720
+ Expected output:
721
+
722
+ ```txt
723
+ Loaded db/users.schema.jsonc
724
+ Loaded db/posts.json
725
+ Generated .db/schema.generated.json
726
+ Generated .db/types/index.d.ts
727
+ Generated src/generated/db.types.d.ts
728
+ Generated src/generated/db.schema.json
729
+ Synced runtime store
730
+ ```
731
+
732
+ ## REST And GraphQL Runtime
733
+
734
+ The package should keep protocol-specific implementation in dedicated modules:
735
+
736
+ ```txt
737
+ src/rest/
738
+ src/graphql/
739
+ src/web/
740
+ ```
741
+
742
+ REST should expose generated collection and singleton document routes.
743
+
744
+ Collection and single-record reads should support selective response shapes without changing the REST-first model:
745
+
746
+ ```txt
747
+ GET /posts?select=id,title
748
+ GET /posts?offset=0&limit=20
749
+ GET /posts/p1?select=id,title
750
+ ```
751
+
752
+ `offset` must be a non-negative integer, `limit` must be a positive integer, and collection responses should remain arrays. Pagination is applied before projection.
753
+
754
+ Schema-backed scalar fields can declare explicit to-one relation metadata:
755
+
756
+ ```jsonc
757
+ {
758
+ "authorId": {
759
+ "type": "string",
760
+ "required": true,
761
+ "relation": {
762
+ "name": "author",
763
+ "to": "authors",
764
+ "toField": "id",
765
+ "cardinality": "one"
766
+ }
767
+ }
768
+ }
769
+ ```
770
+
771
+ Generated schema metadata should include normalized `relations` both on the resource and at the top level. REST should support depth-1 explicit to-one expansion:
772
+
773
+ ```txt
774
+ GET /posts/p1?expand=author
775
+ GET /posts/p1?expand=author&select=id,title,author.name
776
+ ```
777
+
778
+ `select=author.name` without `expand=author` should fail with a structured hint. Missing required relation targets should produce schema diagnostics; optional missing targets should expand as `null`.
779
+
780
+ REST should support sequential batch requests:
781
+
782
+ ```txt
783
+ POST /__db/batch
784
+ ```
785
+
786
+ ```json
787
+ [
788
+ {
789
+ "method": "GET",
790
+ "path": "/users"
791
+ },
792
+ {
793
+ "method": "PATCH",
794
+ "path": "/settings",
795
+ "body": {
796
+ "theme": "dark"
797
+ }
798
+ }
799
+ ]
800
+ ```
801
+
802
+ REST batches are non-transactional by design. Items execute in order, and earlier successful writes remain committed if a later item fails.
803
+
804
+ Schema-backed writes should validate declared field types before mutating runtime state. Required fields, primitive types, enum values, arrays, nullable fields, datetime strings, flexible objects with intentional additional properties, nested objects, and field constraints (`unique`, `min`, `max`, `minLength`, `maxLength`, `pattern`) should be checked for package API writes, REST writes, GraphQL mutations, `async-db sync`, and `async-db schema validate`.
805
+
806
+ The root route should work as a discovery endpoint. API-style requests to `GET /` should return JSON with resource names plus links for the data viewer, schema endpoint, GraphQL endpoint, resource routes, and registered response formats. Browser-style requests that prefer `text/html` should return a small HTML index with those same links.
807
+
808
+ The local server should also expose a built-in dependency-free viewer:
809
+
810
+ ```txt
811
+ GET /__db
812
+ GET /__db/manifest
813
+ GET /__db/manifest.json
814
+ GET /__db/manifest.html
815
+ GET /__db/manifest.md
816
+ ```
817
+
818
+ `server.apiBase` should default to `/__db` and should configure the
819
+ standalone viewer, viewer manifest, schema, batch, import, events, log, and fork route base
820
+ without changing root REST resource routes or the standalone GraphQL path.
821
+
822
+ `server.dataPath` should default to `/db` and should mount an app-facing REST
823
+ resource alias. For a fixture at `db/users.json`, `GET /db/users.json` should
824
+ return the same synced runtime JSON resource as the REST resource route, not raw
825
+ static file contents. `GET /db/users.json?id=u_1` should return the same single
826
+ record shape as `GET /db/users/u_1.json`. Setting `server.dataPath: false`
827
+ should disable the alias while keeping scoped REST under `/__db/rest` and
828
+ standalone root REST routes.
829
+
830
+ The viewer manifest should be the shared JSON contract used by the built-in viewer and custom data viewers. `/manifest.json` should return JSON by default. `/manifest.html` should render a formatted JSON viewer with dark mode as the default, dark/light/system controls, copy, and pretty/raw formatting controls. `/manifest.md` should render Markdown with the manifest JSON in a fenced code block for AI clients. `/manifest` should choose among registered media types from `Accept`, and explicit `/manifest.<extension>` routes should use the matching registered response format. The manifest should include API links, capabilities, diagnostics, configured viewer links, response format metadata, collections, documents, field metadata, UI hints, and relation hints. It must not include seed records, source paths, source hashes, runtime state paths, or GraphQL SDL. Custom viewers should use the manifest for UI metadata and fetch actual records from REST or GraphQL.
831
+
832
+ The viewer should support:
833
+
834
+ ```txt
835
+ resource list
836
+ collection table viewer
837
+ singleton document JSON viewer
838
+ selected JSON copy
839
+ CSV drag-and-drop import into the configured fixture folder
840
+ REST route specs with copy/paste examples
841
+ REST request runner
842
+ GraphQL SDL viewer
843
+ GraphQL query and mutation examples
844
+ GraphQL runner with variables
845
+ schema and field inspection
846
+ diagnostics summary
847
+ ```
848
+
849
+ The CLI should include a fixture health check:
850
+
851
+ ```txt
852
+ async-db doctor
853
+ async-db doctor --json
854
+ async-db doctor --strict
855
+ async-db check --strict
856
+ ```
857
+
858
+ `doctor` should include existing source/schema diagnostics and advisory fixture findings. It should detect duplicate collection ids, mixed id value types, inconsistent field value types, likely relation fields such as `todos.userId -> users.id`, and likely relation values missing from a target collection. Relation inference must be suggestive only; it must not mutate fixtures, write schema files, or silently change REST/GraphQL shape behavior. `doctor` should exit nonzero for error diagnostics, while `--strict` should also exit nonzero for warnings. Informational relation suggestions should not fail strict mode.
859
+
860
+ CSV data-first fixtures should be treated as collections. The first row is the header row, headers become JSON field names, values are parsed into records, and the default JSON store is written as `.db/state/<resource>.json`. When a CSV data file is paired with a schema file, schema-declared array fields should coerce semicolon-delimited cells and JSON array string cells into arrays before validation and store hydration.
861
+
862
+ Collection fixtures should always have an id field. If a JSON/JSONC/CSV collection source omits `id`, generate counter ids in the selected runtime store, starting at `"1"` and avoiding existing ids. Source files stay unchanged by default. For resources bound to the `sourceFile` store, write generated ids back to plain `.json` fixtures.
863
+
864
+ Runtime stores should track source hashes for JSON, JSONC, and CSV files. If a source hash changes during sync, regenerate runtime state for that resource from the source fixture. If the hash is unchanged, preserve runtime edits.
865
+
866
+ The viewer should support uploading a CSV through:
867
+
868
+ ```txt
869
+ POST /__db/import
870
+ ```
871
+
872
+ The upload should copy the CSV into the configured fixture folder, run sync, reload the in-memory resources, update the URL query parameter to the imported resource, and reload the dashboard view.
873
+
874
+ While serving, db should watch the configured fixture folder for fixture and schema changes, ignoring `.db/`. On change, reload resources and notify the single-file viewer through the configured events route, defaulting to `/__db/events`, so the dashboard refreshes automatically. If one source file fails to parse or load, report a file-specific diagnostic in the viewer and keep the remaining valid resources available. If the runtime cannot create a file watcher because of environment resource limits such as `EMFILE` or `ENOSPC`, keep the HTTP server running, publish a warning diagnostic, and serve without live reload.
875
+
876
+ Vite development should be supported through a dependency-light plugin export:
877
+
878
+ ```js
879
+ import { dbPlugin } from '@async/db/vite';
880
+
881
+ export default {
882
+ plugins: [dbPlugin()],
883
+ };
884
+ ```
885
+
886
+ The plugin should return a plain Vite-compatible plugin object with `apply: 'serve'`, mount @async/db into the existing Vite dev middleware stack, and avoid bundling Node-only fixture runtime code into production builds. By default, Vite dev routes should be scoped under `/__db` and should not answer root app routes. A plugin-level `apiBase` should win over `server.apiBase`:
887
+
888
+ ```txt
889
+ GET /db/users.json
890
+ GET /__db
891
+ GET /__db/schema
892
+ POST /__db/batch
893
+ POST /__db/graphql
894
+ GET /__db/rest/users
895
+ ```
896
+
897
+ Standalone `async-db serve` should keep root REST routes such as `/users` and `/graphql`. The Vite plugin may opt into root REST routes with `rootRoutes: true`.
898
+
899
+ GraphQL should support a dependency-free subset suitable for local app development:
900
+
901
+ ```graphql
902
+ query GetUser($id: ID!) {
903
+ allUsers: users {
904
+ id
905
+ displayName: name
906
+ }
907
+ ada: user(id: $id) {
908
+ email
909
+ }
910
+ }
911
+ ```
912
+
913
+ Supported GraphQL behavior:
914
+
915
+ ```txt
916
+ queries
917
+ mutations
918
+ root and nested aliases
919
+ variables
920
+ operationName selection for multi-operation documents
921
+ __typename meta fields
922
+ named fragments and inline fragments
923
+ @include and @skip executable directives
924
+ object/list/scalar input values
925
+ collection list queries
926
+ collection single-record queries by id
927
+ collection create/update/delete mutations
928
+ singleton document queries
929
+ singleton document update/set mutations
930
+ selection-set projection
931
+ minimal __schema and __type introspection
932
+ HTTP batching by posting an array to /graphql
933
+ ```
934
+
935
+ Unsupported in the dependency-free v1 subset:
936
+
937
+ ```txt
938
+ subscriptions
939
+ full spec introspection coverage
940
+ general-purpose GraphQL validation
941
+ ```
942
+
943
+ ## Repo Example Launcher
944
+
945
+ The repo should include an npm task that starts every example database and serves an index page of viewer links:
946
+
947
+ ```bash
948
+ npm run examples
949
+ ```
950
+
951
+ The index page should list each example and link to:
952
+
953
+ ```txt
954
+ /__db
955
+ /__db/schema
956
+ /graphql
957
+ ```
958
+
959
+ Examples should range from basic to advanced:
960
+
961
+ ```txt
962
+ examples/basic
963
+ examples/data-first
964
+ examples/schema-first
965
+ examples/advanced
966
+ ```
967
+
968
+ ## Client API
969
+
970
+ Provide a small HTTP client for consuming db from apps and tests:
971
+
972
+ ```ts
973
+ import { createDbClient } from '@async/db/client';
974
+
975
+ const client = createDbClient({
976
+ baseUrl: 'http://127.0.0.1:7331',
977
+ restBasePath: '',
978
+ batching: {
979
+ enabled: true,
980
+ delayMs: 0,
981
+ },
982
+ });
983
+ ```
984
+
985
+ The client should support:
986
+
987
+ ```txt
988
+ client.graphql(query, variables)
989
+ client.graphql.batch(requests)
990
+ client.rest(method, path, body)
991
+ client.rest.batch(requests)
992
+ optional scoped REST base paths for embedded dev servers
993
+ optional automatic batching for individual GraphQL and REST calls
994
+ 10ms default automatic batching window
995
+ read-safe dedupe for identical REST GET and GraphQL query requests
996
+ explicit dedupe: 'all' opt-in for deduping writes and mutations
997
+ ```
998
+
999
+ Local mock behavior should support latency and chaos errors:
1000
+
1001
+ ```js
1002
+ export default {
1003
+ mock: {
1004
+ delay: [50, 300],
1005
+ errors: {
1006
+ rate: 0.05,
1007
+ status: 503,
1008
+ message: 'Random local mock failure',
1009
+ },
1010
+ },
1011
+ };
1012
+ ```
1013
+
1014
+ ## Error Messages
1015
+
1016
+ Errors should be readable by humans and useful to AI agents. They should explain:
1017
+
1018
+ ```txt
1019
+ what failed
1020
+ where it failed
1021
+ what value was received when useful
1022
+ what values or commands are valid
1023
+ what to try next
1024
+ ```
1025
+
1026
+ REST and server errors should use this shape:
1027
+
1028
+ ```json
1029
+ {
1030
+ "error": {
1031
+ "code": "REST_BATCH_INVALID_PATH",
1032
+ "message": "REST batch path must start with \"/\": users",
1033
+ "hint": "Use absolute local paths such as \"/users\", \"/settings\", or \"/__db/schema\".",
1034
+ "details": {
1035
+ "path": "users"
1036
+ }
1037
+ }
1038
+ }
1039
+ ```
1040
+
1041
+ GraphQL errors should use standard GraphQL `errors[]` entries with `extensions`:
1042
+
1043
+ ```json
1044
+ {
1045
+ "data": null,
1046
+ "errors": [
1047
+ {
1048
+ "message": "Unknown GraphQL query field \"nope\".",
1049
+ "extensions": {
1050
+ "code": "GRAPHQL_UNKNOWN_QUERY_FIELD",
1051
+ "hint": "Use one of: \"users\", \"user\".",
1052
+ "details": {
1053
+ "field": "nope",
1054
+ "availableFields": ["users", "user"]
1055
+ }
1056
+ }
1057
+ }
1058
+ ]
1059
+ }
1060
+ ```
1061
+
1062
+ ## Codex Prompt Add-On
1063
+
1064
+ Append this to the Codex prompt:
1065
+
1066
+ ````md
1067
+ ## Type generation and schema-only fixtures
1068
+
1069
+ Add automatic TypeScript type generation.
1070
+
1071
+ By default, generated types should be written to:
1072
+
1073
+ ```txt
1074
+ .db/types/index.d.ts
1075
+ ```
1076
+
1077
+ Also support a configurable committed output file:
1078
+
1079
+ ```js
1080
+ export default {
1081
+ types: {
1082
+ enabled: true,
1083
+ outFile: './.db/types/index.d.ts',
1084
+ commitOutFile: './src/generated/db.types.d.ts',
1085
+ emitComments: true,
1086
+ useReadonly: false
1087
+ }
1088
+ };
1089
+ ```
1090
+
1091
+ If `commitOutFile` is set, generate the same TypeScript types there so users can import and commit them.
1092
+
1093
+ The generated file should export:
1094
+
1095
+ ```ts
1096
+ export type DbCollections = {};
1097
+ export type DbDocuments = {};
1098
+ export type DbTypes = {
1099
+ collections: DbCollections;
1100
+ documents: DbDocuments;
1101
+ };
1102
+ ```
1103
+
1104
+ For each collection, generate a record type:
1105
+
1106
+ ```ts
1107
+ export type User = {
1108
+ id: string;
1109
+ name: string;
1110
+ email: string;
1111
+ role?: 'admin' | 'user';
1112
+ };
1113
+ ```
1114
+
1115
+ Use schema field descriptions to emit JSDoc comments.
1116
+
1117
+ ## Schema manifest output and model-driven admin UIs
1118
+
1119
+ Add optional JSON schema manifest generation for local-first admin/CMS UIs that render forms from db models instead of duplicating per-resource form configuration.
1120
+
1121
+ This is separate from `.db/schema.generated.json`. The existing generated schema file remains runtime/server metadata and may include diagnostics, source paths, seeds, REST route lists, and GraphQL SDL. The committed manifest is a small importable artifact for applications.
1122
+
1123
+ Configure it with:
1124
+
1125
+ ```js
1126
+ export default {
1127
+ schemaOutFile: './src/generated/db.schema.json',
1128
+ };
1129
+ ```
1130
+
1131
+ When `schemaOutFile` is set, `async-db sync` writes the manifest. The CLI can also write one directly:
1132
+
1133
+ ```bash
1134
+ async-db schema manifest --out ./src/generated/db.schema.json
1135
+ ```
1136
+
1137
+ Custom viewer UIs can use the live `GET /__db/manifest.json` route or a committed viewer manifest. Browser users can open `GET /__db/manifest.html`, AI clients can open `GET /__db/manifest.md`, and `GET /__db/manifest` negotiates from registered `Accept` media types:
1138
+
1139
+ ```js
1140
+ export default {
1141
+ viewerManifestOutFile: './src/generated/db.viewer.json',
1142
+ server: {
1143
+ viewerLinks: [
1144
+ { label: 'App Data Viewer', href: 'http://127.0.0.1:5173/db' },
1145
+ ],
1146
+ },
1147
+ };
1148
+ ```
1149
+
1150
+ ```bash
1151
+ async-db viewer manifest --out ./src/generated/db.viewer.json
1152
+ ```
1153
+
1154
+ The manifest should have this top-level shape:
1155
+
1156
+ ```json
1157
+ {
1158
+ "version": 1,
1159
+ "collections": {},
1160
+ "documents": {}
1161
+ }
1162
+ ```
1163
+
1164
+ Each resource entry should include `kind`, `name`, `idField` for collections, optional `description`, and `fields`. Each field should include normalized field metadata such as `type`, `required`, `nullable`, `default`, `values`, nested object `fields`, array `items`, `relation`, constraints, and inferred `ui` defaults.
1165
+
1166
+ The manifest must not include seed records, source hashes, source paths, runtime state, diagnostics, REST route lists, or GraphQL SDL.
1167
+
1168
+ Default UI inference should be deterministic and safe:
1169
+
1170
+ ```txt
1171
+ boolean -> toggle
1172
+ small enum -> radio
1173
+ larger enum -> select
1174
+ email-like field name -> email
1175
+ url-like field name -> url
1176
+ image/avatar/photo-like field name -> image
1177
+ description/body/content/notes/bio/markdown-like field name -> textarea
1178
+ array<string> -> tags
1179
+ array<enum> -> multiSelect
1180
+ object with declared fields -> fieldset
1181
+ open object or unknown field -> json
1182
+ relation field -> relationSelect with optionsFrom
1183
+ collection id field -> readonly
1184
+ ```
1185
+
1186
+ Manifest defaults are metadata only. They must not change fixtures, seed data, runtime state, validation, REST, or GraphQL behavior.
1187
+
1188
+ Apps can customize or omit field entries with a visitor hook:
1189
+
1190
+ ```js
1191
+ export default {
1192
+ schemaManifest: {
1193
+ customizeField({ field, fieldName, resource, resourceName, path, file, sourceFile, defaultManifest }) {
1194
+ if (resourceName === 'users' && fieldName === 'passwordHash') {
1195
+ return null;
1196
+ }
1197
+
1198
+ if (fieldName.endsWith('Markdown')) {
1199
+ return {
1200
+ ...defaultManifest,
1201
+ ui: {
1202
+ ...defaultManifest.ui,
1203
+ component: 'markdown',
1204
+ },
1205
+ };
1206
+ }
1207
+
1208
+ return defaultManifest;
1209
+ },
1210
+ },
1211
+ };
1212
+ ```
1213
+
1214
+ The visitor return value must be JSON-serializable. Functions, classes, symbols, bigint values, non-finite numbers, and non-plain objects should fail generation with a diagnostic that includes resource and field path. Returning `null` omits the field from the manifest.
1215
+
1216
+ The intended first use is permissioned admin CRUD for resources such as dashboards, users, and permission policies. Admin screens can map manifest field metadata to reusable create/edit/view components while policy checks decide whether fields are hidden, readonly, or editable for a given session.
1217
+
1218
+ Support schema-only fixtures.
1219
+
1220
+ The package should accept these source formats:
1221
+
1222
+ ```txt
1223
+ db/users.json data-first fixture
1224
+ db/users.jsonc data-first fixture with comments
1225
+ db/users.csv data-first collection fixture
1226
+ db/users.schema.jsonc schema/type-first fixture
1227
+ db/users.schema.mjs schema/type-first fixture using JS helpers
1228
+ db/users.schema.js schema/type-first fixture using JS helpers in type: module projects
1229
+ ```
1230
+
1231
+ The main source JSON/JSONC/CSV fixture can be used to infer schema and generate types.
1232
+
1233
+ A `.schema.jsonc` file can define a resource without seed data:
1234
+
1235
+ ```jsonc
1236
+ {
1237
+ // Users who can sign into the local test app.
1238
+ "kind": "collection",
1239
+ "idField": "id",
1240
+ "fields": {
1241
+ "id": {
1242
+ "type": "string",
1243
+ "required": true,
1244
+ "description": "Stable user id."
1245
+ },
1246
+ "role": {
1247
+ "type": "enum",
1248
+ "values": ["admin", "user"],
1249
+ "default": "user",
1250
+ "description": "Local authorization role."
1251
+ }
1252
+ },
1253
+ "seed": []
1254
+ }
1255
+ ```
1256
+
1257
+ Support `.schema.mjs` and `.schema.js` files for richer authoring:
1258
+
1259
+ ```js
1260
+ import { collection, field } from '@async/db/schema';
1261
+
1262
+ export default collection({
1263
+ description: 'Users who can sign into the local test app.',
1264
+ idField: 'id',
1265
+ fields: {
1266
+ id: field.string({
1267
+ required: true,
1268
+ description: 'Stable user id.'
1269
+ }),
1270
+ role: field.enum(['admin', 'user'], {
1271
+ default: 'user',
1272
+ description: 'Local authorization role.'
1273
+ })
1274
+ },
1275
+ seed: []
1276
+ });
1277
+ ```
1278
+
1279
+ Support a root `db.schema.mjs` or `db.schema.js` registry for one-file schema authoring:
1280
+
1281
+ ```js
1282
+ import { collection, field } from '@async/db/schema';
1283
+
1284
+ export default {
1285
+ users: collection({
1286
+ idField: 'id',
1287
+ fields: {
1288
+ id: field.string({ required: true }),
1289
+ firstName: field.string(),
1290
+ lastName: field.string(),
1291
+ fullName: field.computed(field.string(), function users_fullName_resolver({ record }) {
1292
+ return `${record.firstName} ${record.lastName}`;
1293
+ })
1294
+ }
1295
+ })
1296
+ };
1297
+ ```
1298
+
1299
+ `field.computed(type, fn)` is shorthand for `{ resolve: fn }`. Normal function
1300
+ resolvers are invoked with `this` bound to a delegated runtime resolver context.
1301
+ The context exposes `this.get(name)`, `this.has(name)`, direct property aliases,
1302
+ and `this._internal` for the unoverridden internal view. Internal values include
1303
+ `db`, `resource`, `field`, `fieldName`, `config`, `services`, `cache`, `value`,
1304
+ `record`, `records`, and `args`. App-provided context values win over internal
1305
+ values with the same key. Schema/type/manifest/doctor/bundle/unbundle/generate
1306
+ commands may import trusted schema modules for metadata, but must not call
1307
+ computed resolvers.
1308
+
1309
+ The package API should expose `loadDbSchema({ from })` for metadata-only schema
1310
+ loading from a project root, `db/` folder, `db.schema.mjs`, or individual schema
1311
+ file. `db.schema.js` follows the same locator rules when the project uses `"type": "module"`. Loaded schemas expose `schema.validator(resource, options)` for endpoint
1312
+ input validation and `schema.resolver(resourceOrField, options)` for direct
1313
+ computed field execution. Validators reject computed/read-only fields, default
1314
+ unknown fields to `error`, and support `strip`, `allow`, `warn`, and patch/replace
1315
+ validation modes. `openDb({ schema })` accepts a loaded schema object and opens
1316
+ runtime stores from the same locator.
1317
+
1318
+ Folder-backed content collections use `index.schema.mjs` or `index.schema.js` as an explicit marker:
1319
+
1320
+ ```txt
1321
+ db/docs/index.schema.mjs
1322
+ db/docs/index.schema.js
1323
+ db/docs/intro.mdx
1324
+ ```
1325
+
1326
+ The resource name comes from the containing folder. Folder collections require an
1327
+ explicit `source: files(pattern, { read })` declaration. Runtime store behavior
1328
+ belongs in `db.config.mjs` through `resources.<name>.store`; use `store: 'static'`
1329
+ there when file-backed content should be read-only. Core only parses frontmatter
1330
+ plus raw `.md` / `.mdx` body text. MDX compilation remains app-owned.
1331
+
1332
+ Do not require TypeScript execution for schema files in v1. Use `.mjs` for package-type-independent executable schema definitions, or compile TypeScript-authored schema files to `.schema.js` / `.schema.mjs`.
1333
+
1334
+ Rules:
1335
+
1336
+ 1. If only `users.json` exists, infer schema from data.
1337
+ 2. If only `users.schema.json`, `users.schema.jsonc`, `users.schema.mjs`, or `users.schema.js` exists, create the collection from schema and optional seed/default data.
1338
+ 3. If both `users.json` and `users.schema.*` exist, the schema file is authoritative for types and validation, while the JSON file provides seed data.
1339
+ 4. Additive fields are safe and automatic.
1340
+ 5. Removed fields and type changes require explicit approval.
1341
+ 6. Defaults should apply when creating records and when safely backfilling additive fields.
1342
+ 7. Generated TypeScript types should update during `async-db sync`, `async-db types`, and service startup when needed.
1343
+
1344
+ Add CLI commands:
1345
+
1346
+ ```bash
1347
+ async-db types
1348
+ async-db types --watch
1349
+ async-db types --out ./src/generated/db.types.d.ts
1350
+ async-db schema
1351
+ async-db schema validate
1352
+ async-db schema unbundle users
1353
+ async-db schema unbundle --all --schema-dir db
1354
+ async-db schema bundle users --out artifacts/users.bundle.schema.json
1355
+ async-db schema bundle --all --out db.schema.mjs
1356
+ async-db generate hono
1357
+ async-db generate hono --api rest,graphql --out ./server
1358
+ async-db generate hono --api none --app module
1359
+ ```
1360
+
1361
+ ## Hono And SQLite Starter Generation
1362
+
1363
+ Add `async-db generate hono` for graduating a fixture-backed app into a starter API backed by SQLite.
1364
+
1365
+ Default behavior:
1366
+
1367
+ ```txt
1368
+ outDir: ./db-api
1369
+ api: rest
1370
+ db: sqlite
1371
+ app: standalone
1372
+ runtime: node-sqlite
1373
+ seed: false
1374
+ ```
1375
+
1376
+ Generated output should be TypeScript-first and include a portable repository interface, SQLite adapter using `node:sqlite`, validators, initial SQL migration, and optional Hono REST/GraphQL route modules. Standalone output should include `package.json`, `tsconfig.json`, `src/app.ts`, and `src/server.ts`.
1377
+
1378
+ API selection:
1379
+
1380
+ ```bash
1381
+ async-db generate hono --api rest
1382
+ async-db generate hono --api graphql
1383
+ async-db generate hono --api rest,graphql
1384
+ async-db generate hono --api none
1385
+ ```
1386
+
1387
+ SQLite generation rules:
1388
+
1389
+ ```txt
1390
+ collections -> SQLite tables with id TEXT PRIMARY KEY
1391
+ documents -> _db_documents(name TEXT PRIMARY KEY, value TEXT)
1392
+ string/enum -> TEXT
1393
+ number -> REAL
1394
+ boolean -> INTEGER
1395
+ object/array/unknown -> JSON text in TEXT columns
1396
+ ```
1397
+
1398
+ Generation should fail on schema errors. For production SQLite output, warning diagnostics should also block generation unless `--allow-warnings` is provided. Seed insertion is disabled by default; `--seed fixtures` can emit fixture seed support for local SQLite mimicry.
1399
+
1400
+ Keep Hono and SQLite runtime support isolated under optional exports:
1401
+
1402
+ ```txt
1403
+ db/hono
1404
+ db/sqlite
1405
+ ```
1406
+
1407
+ The core package must not add mandatory Hono or SQLite npm dependencies.
1408
+
1409
+ Acceptance criteria:
1410
+
1411
+ * Data-first fixtures generate TypeScript types.
1412
+ * Schema-only fixtures generate TypeScript types.
1413
+ * JSONC schema comments are allowed.
1414
+ * Field descriptions become JSDoc in generated TypeScript.
1415
+ * `types.outFile` writes to `.db/types/index.d.ts` by default.
1416
+ * `types.commitOutFile` writes to a custom importable location.
1417
+ * Package API can be typed with the generated `DbTypes`.
1418
+ ````
1419
+
1420
+ The intended developer loop is:
1421
+
1422
+ ```txt
1423
+ create/edit JSON or schema fixtures
1424
+ run async-db sync
1425
+ types are generated
1426
+ REST and GraphQL are generated
1427
+ runtime store is updated
1428
+ source files stay clean unless writeback is requested
1429
+ ```