@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,93 @@
1
+ # Computed Fields Example
2
+
3
+ ## What This Teaches
4
+
5
+ Use this when you want to see several ways to model computed fields with `.schema.mjs`. Computed fields are read-only projections: stored fixture records stay small, and REST or GraphQL resolves the computed value only when a client selects it.
6
+
7
+ ## Files To Inspect
8
+
9
+ - [db/users.schema.mjs](./db/users.schema.mjs): `field.computed(type, function)` shorthand using `this.value`.
10
+ - [db/posts.schema.mjs](./db/posts.schema.mjs): object form with `resolveMany` for reading time.
11
+ - [db/products.schema.mjs](./db/products.schema.mjs): arrow resolver for simple price formatting.
12
+ - [db/orders.schema.mjs](./db/orders.schema.mjs): normal function resolver that uses `this.get('db')` to read product prices.
13
+ - [src/generated/db.types.d.ts](./src/generated/db.types.d.ts): committed generated types where computed fields are optional read-only projections.
14
+
15
+ ## Run It
16
+
17
+ From the repository root:
18
+
19
+ ```bash
20
+ npm run db -- sync --cwd ./examples/computed-fields
21
+ npm run db -- serve --cwd ./examples/computed-fields
22
+ ```
23
+
24
+ Open the viewer:
25
+
26
+ ```txt
27
+ http://127.0.0.1:7331/__db
28
+ ```
29
+
30
+ ## REST Requests To Try
31
+
32
+ Default reads return stored fields only:
33
+
34
+ ```bash
35
+ curl http://127.0.0.1:7331/db/orders.json
36
+ ```
37
+
38
+ Select computed fields when you need them:
39
+
40
+ ```bash
41
+ curl 'http://127.0.0.1:7331/db/users.json?select=id,fullName'
42
+ curl 'http://127.0.0.1:7331/db/posts.json?select=id,title,readingTimeMinutes'
43
+ curl 'http://127.0.0.1:7331/db/products.json?select=id,name,priceLabel'
44
+ curl 'http://127.0.0.1:7331/db/orders.json?select=id,itemCount,totalCents,receiptLine'
45
+ ```
46
+
47
+ ## GraphQL Query To Try
48
+
49
+ ```graphql
50
+ {
51
+ users {
52
+ id
53
+ fullName
54
+ }
55
+ products {
56
+ id
57
+ priceLabel
58
+ }
59
+ orders {
60
+ id
61
+ itemCount
62
+ totalCents
63
+ receiptLine
64
+ }
65
+ }
66
+ ```
67
+
68
+ ## Why This Shape?
69
+
70
+ Each model shows a different computed-field use case:
71
+
72
+ - `users.fullName` combines stored fields from `this.value` for display.
73
+ - `posts.readingTimeMinutes` uses `resolveMany` so a collection page can be computed in one batch.
74
+ - `products.priceLabel` formats raw `priceCents` without changing the stored number.
75
+ - `orders.totalCents` and `orders.receiptLine` use normal resolver functions so the delegated resolver context can read related product prices with `this.get('db')`.
76
+
77
+ Normal function resolvers can read internal runtime values such as `db`, `value`,
78
+ `record`, `fieldName`, and `services` with `this.get(name)`. App code can pass a
79
+ context object to `schema.resolver(...)` to override or add values; `this._internal`
80
+ keeps the original internal view available when a resolver needs it.
81
+
82
+ Computed fields are useful for values that are cheap to derive, read-only, and easier to keep out of source fixtures. They should not replace stored data that users need to edit directly.
83
+
84
+ ## Features To Notice
85
+
86
+ - [Computed fields](../../docs/fixtures-and-schemas.md#computed-fields)
87
+ - [JavaScript schema sources](../../docs/fixtures-and-schemas.md#javascript-schema-sources)
88
+ - [Fixture-like `.json` REST routes](../../docs/server-and-viewer.md#fixture-like-json-routes)
89
+ - [Generated types](../../docs/generated-files.md#generated-types)
90
+
91
+ ## Cleanup
92
+
93
+ Generated `.db/` output is ignored by git and can be removed whenever you want fresh runtime state.
@@ -0,0 +1,62 @@
1
+ import { collection, field } from '@async/db/schema';
2
+
3
+ export default collection({
4
+ description: 'Orders that derive totals from nested items and related products.',
5
+ idField: 'id',
6
+ fields: {
7
+ id: field.string({
8
+ required: true,
9
+ description: 'Stable order id.',
10
+ }),
11
+ customerName: field.string({
12
+ required: true,
13
+ description: 'Stored customer display name.',
14
+ }),
15
+ items: field.array(field.object({
16
+ productId: field.string({ required: true }),
17
+ quantity: field.number({ required: true }),
18
+ }), {
19
+ description: 'Stored line items.',
20
+ }),
21
+ itemCount: field.computed(field.number({
22
+ description: 'Total quantity across line items.',
23
+ }), ({ record }) => itemCount(record)),
24
+ totalCents: field.computed(field.number({
25
+ description: 'Order total calculated from current product prices.',
26
+ }), async function orders_totalCents_resolver({ record }) {
27
+ return orderTotalCents(this.get('db'), record);
28
+ }),
29
+ receiptLine: field.computed(field.string({
30
+ description: 'Human-readable receipt summary.',
31
+ }), async function orders_receiptLine_resolver({ record }) {
32
+ const total = await orderTotalCents(this.get('db'), record);
33
+ return `${record.customerName} - ${itemCount(record)} items - ${formatMoney(total)}`;
34
+ }),
35
+ },
36
+ seed: [
37
+ {
38
+ id: 'ord_1',
39
+ customerName: 'Ada Lovelace',
40
+ items: [
41
+ { productId: 'prod_sticker', quantity: 3 },
42
+ { productId: 'prod_mug', quantity: 2 },
43
+ ],
44
+ },
45
+ ],
46
+ });
47
+
48
+ function itemCount(order) {
49
+ return (order.items ?? []).reduce((total, item) => total + Number(item.quantity ?? 0), 0);
50
+ }
51
+
52
+ async function orderTotalCents(db, order) {
53
+ const products = await db.collection('products').all();
54
+ const prices = new Map(products.map((product) => [product.id, product.priceCents]));
55
+ return (order.items ?? []).reduce((total, item) => (
56
+ total + Number(prices.get(item.productId) ?? 0) * Number(item.quantity ?? 0)
57
+ ), 0);
58
+ }
59
+
60
+ function formatMoney(cents) {
61
+ return `$${(Number(cents ?? 0) / 100).toFixed(2)}`;
62
+ }
@@ -0,0 +1,59 @@
1
+ import { collection, field } from '@async/db/schema';
2
+
3
+ export default collection({
4
+ description: 'Posts with derived reading metadata.',
5
+ idField: 'id',
6
+ fields: {
7
+ id: field.string({
8
+ required: true,
9
+ description: 'Stable post id.',
10
+ }),
11
+ title: field.string({
12
+ required: true,
13
+ description: 'Post title.',
14
+ }),
15
+ authorId: field.string({
16
+ required: true,
17
+ description: 'Author user id.',
18
+ relation: {
19
+ name: 'author',
20
+ to: 'users',
21
+ toField: 'id',
22
+ cardinality: 'one',
23
+ },
24
+ }),
25
+ body: field.string({
26
+ required: true,
27
+ description: 'Stored markdown body.',
28
+ }),
29
+ readingTimeMinutes: field.computed(field.number({
30
+ description: 'One-minute minimum reading estimate derived from the body.',
31
+ }), {
32
+ resolveMany({ records }) {
33
+ return new Map(records.map((record) => [
34
+ record.id,
35
+ readingTimeMinutes(record.body),
36
+ ]));
37
+ },
38
+ }),
39
+ },
40
+ seed: [
41
+ {
42
+ id: 'post_intro',
43
+ title: 'Computed fields are projections',
44
+ authorId: 'u_1',
45
+ body: 'Computed fields keep stored fixture data small while adding useful values when a client selects them.',
46
+ },
47
+ {
48
+ id: 'post_release',
49
+ title: 'Batch resolver example',
50
+ authorId: 'u_2',
51
+ body: 'resolveMany can derive values for an entire selected page at once.',
52
+ },
53
+ ],
54
+ });
55
+
56
+ function readingTimeMinutes(body) {
57
+ const wordCount = String(body ?? '').trim().split(/\s+/u).filter(Boolean).length;
58
+ return Math.max(1, Math.ceil(wordCount / 200));
59
+ }
@@ -0,0 +1,39 @@
1
+ import { collection, field } from '@async/db/schema';
2
+
3
+ export default collection({
4
+ description: 'Products that store cents and expose formatted display values.',
5
+ idField: 'id',
6
+ fields: {
7
+ id: field.string({
8
+ required: true,
9
+ description: 'Stable product id.',
10
+ }),
11
+ name: field.string({
12
+ required: true,
13
+ description: 'Product name.',
14
+ }),
15
+ priceCents: field.number({
16
+ required: true,
17
+ description: 'Stored integer price in cents.',
18
+ }),
19
+ priceLabel: field.computed(field.string({
20
+ description: 'Formatted price for UI display.',
21
+ }), ({ record }) => formatMoney(record.priceCents)),
22
+ },
23
+ seed: [
24
+ {
25
+ id: 'prod_sticker',
26
+ name: 'Async DB Sticker',
27
+ priceCents: 500,
28
+ },
29
+ {
30
+ id: 'prod_mug',
31
+ name: 'Local Data Mug',
32
+ priceCents: 2000,
33
+ },
34
+ ],
35
+ });
36
+
37
+ function formatMoney(cents) {
38
+ return `$${(Number(cents ?? 0) / 100).toFixed(2)}`;
39
+ }
@@ -0,0 +1,43 @@
1
+ import { collection, field } from '@async/db/schema';
2
+
3
+ export default collection({
4
+ description: 'People used by posts and orders.',
5
+ idField: 'id',
6
+ fields: {
7
+ id: field.string({
8
+ required: true,
9
+ description: 'Stable user id.',
10
+ }),
11
+ firstName: field.string({
12
+ required: true,
13
+ description: 'Given name stored in the fixture.',
14
+ }),
15
+ lastName: field.string({
16
+ required: true,
17
+ description: 'Family name stored in the fixture.',
18
+ }),
19
+ role: field.enum(['admin', 'editor', 'customer'], {
20
+ default: 'customer',
21
+ description: 'Local role for examples.',
22
+ }),
23
+ fullName: field.computed(field.string({
24
+ description: 'Display name assembled from first and last name.',
25
+ }), function users_fullName_resolver() {
26
+ return `${this.value.firstName} ${this.value.lastName}`;
27
+ }),
28
+ },
29
+ seed: [
30
+ {
31
+ id: 'u_1',
32
+ firstName: 'Ada',
33
+ lastName: 'Lovelace',
34
+ role: 'admin',
35
+ },
36
+ {
37
+ id: 'u_2',
38
+ firstName: 'Grace',
39
+ lastName: 'Hopper',
40
+ role: 'editor',
41
+ },
42
+ ],
43
+ });
@@ -0,0 +1,15 @@
1
+ // @ts-check
2
+ import { defineConfig } from '@async/db/config';
3
+
4
+ export default defineConfig({
5
+ dbDir: './db',
6
+ outputs: {
7
+ stateDir: './.db',
8
+ types: './.db/types/index.d.ts',
9
+ committedTypes: './src/generated/db.types.d.ts',
10
+ },
11
+ types: {
12
+ enabled: true,
13
+ emitComments: true,
14
+ },
15
+ });
@@ -0,0 +1,5 @@
1
+ {
2
+ "title": "Computed Fields",
3
+ "description": "Several schema-backed models showing computed fields for display names, formatted prices, reading time, and cross-resource order totals.",
4
+ "tags": ["computed", "schema-mjs", "graphql"]
5
+ }
@@ -0,0 +1,81 @@
1
+ /* eslint-disable */
2
+ // This file is generated by db. Do not edit by hand.
3
+
4
+ export type UserRole = "admin" | "editor" | "customer";
5
+
6
+ /** Orders that derive totals from nested items and related products. */
7
+ export type Order = {
8
+ /** Stable order id. */
9
+ id: string;
10
+ /** Stored customer display name. */
11
+ customerName: string;
12
+ /** Stored line items. */
13
+ items?: Array<{
14
+ productId: string;
15
+ quantity: number;
16
+ }>;
17
+ /** Total quantity across line items. */
18
+ itemCount?: number;
19
+ /** Order total calculated from current product prices. */
20
+ totalCents?: number;
21
+ /** Human-readable receipt summary. */
22
+ receiptLine?: string;
23
+ };
24
+
25
+ /** Posts with derived reading metadata. */
26
+ export type Post = {
27
+ /** Stable post id. */
28
+ id: string;
29
+ /** Post title. */
30
+ title: string;
31
+ /** Author user id. */
32
+ authorId: string;
33
+ /** Stored markdown body. */
34
+ body: string;
35
+ /** One-minute minimum reading estimate derived from the body. */
36
+ readingTimeMinutes?: number;
37
+ };
38
+
39
+ /** Products that store cents and expose formatted display values. */
40
+ export type Product = {
41
+ /** Stable product id. */
42
+ id: string;
43
+ /** Product name. */
44
+ name: string;
45
+ /** Stored integer price in cents. */
46
+ priceCents: number;
47
+ /** Formatted price for UI display. */
48
+ priceLabel?: string;
49
+ };
50
+
51
+ /** People used by posts and orders. */
52
+ export type User = {
53
+ /** Stable user id. */
54
+ id: string;
55
+ /** Given name stored in the fixture. */
56
+ firstName: string;
57
+ /** Family name stored in the fixture. */
58
+ lastName: string;
59
+ /** Local role for examples. */
60
+ role?: UserRole;
61
+ /** Display name assembled from first and last name. */
62
+ fullName?: string;
63
+ };
64
+
65
+ export type DbCollections = {
66
+ orders: Order;
67
+ posts: Post;
68
+ products: Product;
69
+ users: User;
70
+ };
71
+
72
+ export type DbDocuments = {
73
+ };
74
+
75
+ export type DbTypes = {
76
+ collections: DbCollections;
77
+ documents: DbDocuments;
78
+ };
79
+
80
+ export type DbCollectionName = keyof DbCollections;
81
+ export type DbDocumentName = keyof DbDocuments;
@@ -0,0 +1,91 @@
1
+ # Content Collections Example
2
+
3
+ ## What This Teaches
4
+
5
+ Use this when you want a dependency-free version of the content collection pattern: docs and blog posts live as MDX files, `index.schema.mjs` files describe the record shape, and async-db exposes the content through generated types, REST, GraphQL, and the local viewer.
6
+
7
+ This example does not install `content-collections`, a frontmatter package, or an MDX compiler. Core reads simple scalar frontmatter plus the raw MDX body. The app-owned preview script shows where rendering or richer parsing belongs.
8
+
9
+ ## Files To Inspect
10
+
11
+ - [db/docs/index.schema.mjs](./db/docs/index.schema.mjs): docs collection marker using `source: files(..., { read })`.
12
+ - [db/blog/index.schema.mjs](./db/blog/index.schema.mjs): blog collection with file sources, relations, and computed fields.
13
+ - [db.config.mjs](./db.config.mjs): config-owned `static` store selection for docs and blog.
14
+ - [db/blog/launch-notes.mdx](./db/blog/launch-notes.mdx): frontmatter plus raw MDX body.
15
+ - [db/authors.json](./db/authors.json): normal writable fixture records used by blog relations.
16
+ - [db/site.schema.jsonc](./db/site.schema.jsonc): embedded document seed for aggregate bundle seed splitting.
17
+ - [src/content-preview.mjs](./src/content-preview.mjs): dependency-free app-owned preview renderer.
18
+
19
+ ## Run It
20
+
21
+ From the repository root, use the repo-internal CLI path:
22
+
23
+ ```bash
24
+ npm run db -- sync --cwd ./examples/content-collections
25
+ npm run db -- serve --cwd ./examples/content-collections
26
+ ```
27
+
28
+ Open the viewer:
29
+
30
+ ```txt
31
+ http://127.0.0.1:7331/__db
32
+ ```
33
+
34
+ Render the local preview:
35
+
36
+ ```bash
37
+ node ./examples/content-collections/src/content-preview.mjs
38
+ ```
39
+
40
+ ## Expected Result
41
+
42
+ `sync` loads `authors`, `blog`, `docs`, and `site`. The docs and blog collections are static because `db.config.mjs` assigns those resources to the static store. The authors fixture stays writable in the runtime store.
43
+
44
+ ## REST And GraphQL Requests To Try
45
+
46
+ Leave `serve` running and run these from another terminal:
47
+
48
+ ```bash
49
+ curl http://127.0.0.1:7331/db/docs.json
50
+ curl 'http://127.0.0.1:7331/db/blog.json?select=id,title,permalink,readingTimeMinutes'
51
+ ```
52
+
53
+ GraphQL selections use the same computed fields:
54
+
55
+ ```graphql
56
+ {
57
+ blog {
58
+ id
59
+ title
60
+ permalink
61
+ readingTimeMinutes
62
+ }
63
+ }
64
+ ```
65
+
66
+ ## Bundle The Schemas
67
+
68
+ Aggregate bundle writes a root schema registry and keeps seed data out of that root file:
69
+
70
+ ```bash
71
+ npm run db -- schema bundle --all --cwd ./examples/content-collections --out db.schema.mjs
72
+ ```
73
+
74
+ Because `db/site.schema.jsonc` has embedded seed and no `db/site.json`, the command first writes `db/site.json`, then writes `db.schema.mjs`. Folder collection source globs are rebased so the root file points back to `files('./db/docs/**/*.mdx', { read: 'frontmatter' })` and `files('./db/blog/**/*.mdx', { read: 'frontmatter' })`.
75
+
76
+ ## Why This Shape?
77
+
78
+ Docs and blog posts are static content, so their source of truth is the MDX file. Authors are normal fixture data because an app may create or edit them during local development. The relation from `blog.authorId` to `authors.id` keeps the content file small while still letting REST and GraphQL expand author data.
79
+
80
+ `tags` is a comma-separated scalar string on purpose. The built-in frontmatter parser is lightweight and dependency-free; if your app needs arrays, nested frontmatter, or full MDX compilation, keep that parser or compiler in app code like [src/content-preview.mjs](./src/content-preview.mjs).
81
+
82
+ ## Features To Notice
83
+
84
+ - [Folder content collections](../../docs/fixtures-and-schemas.md#folder-content-collections)
85
+ - [Computed fields](../../docs/fixtures-and-schemas.md#computed-fields)
86
+ - [Bundle and unbundle](../../docs/fixtures-and-schemas.md#bundle-and-unbundle)
87
+ - [Fixture-like `.json` REST routes](../../docs/server-and-viewer.md#fixture-like-json-routes)
88
+
89
+ ## Cleanup
90
+
91
+ Generated `.db/` output is ignored by git and can be removed whenever you want fresh runtime state. If you run the aggregate bundle command in this example folder, it intentionally creates `db.schema.mjs` and `db/site.json`; remove those files when you want to return to the per-resource authoring shape.
@@ -0,0 +1,12 @@
1
+ [
2
+ {
3
+ "id": "author_ada",
4
+ "name": "Ada Lovelace",
5
+ "role": "Editor"
6
+ },
7
+ {
8
+ "id": "author_grace",
9
+ "name": "Grace Hopper",
10
+ "role": "Author"
11
+ }
12
+ ]
@@ -0,0 +1,20 @@
1
+ import { collection, field } from '@async/db/schema';
2
+
3
+ export default collection({
4
+ description: 'People who write or edit content records.',
5
+ idField: 'id',
6
+ fields: {
7
+ id: field.string({
8
+ required: true,
9
+ description: 'Stable author id used by content frontmatter.',
10
+ }),
11
+ name: field.string({
12
+ required: true,
13
+ description: 'Display name shown on bylines.',
14
+ }),
15
+ role: field.enum(['Editor', 'Author'], {
16
+ default: 'Author',
17
+ description: 'Editorial role in the local content workflow.',
18
+ }),
19
+ },
20
+ });
@@ -0,0 +1,12 @@
1
+ ---
2
+ title: Draft Roadmap
3
+ status: draft
4
+ publishedAt: 2026-05-22T12:00:00.000Z
5
+ authorId: author_grace
6
+ tags: roadmap,draft
7
+ summary: A draft post that still validates against the same schema.
8
+ ---
9
+ # Draft Roadmap
10
+
11
+ Draft content can live beside published content. The `status` field lets the app
12
+ decide what to show publicly.
@@ -0,0 +1,61 @@
1
+ import { collection, field, files } from '@async/db/schema';
2
+
3
+ export default collection({
4
+ description: 'Blog posts loaded from frontmatter and raw MDX body files.',
5
+ source: files('./**/*.mdx', { read: 'frontmatter' }),
6
+ idField: 'id',
7
+ fields: {
8
+ id: field.string({
9
+ required: true,
10
+ description: 'Stable post id. Defaults to the filename when frontmatter omits id.',
11
+ }),
12
+ title: field.string({
13
+ required: true,
14
+ description: 'Post title from frontmatter.',
15
+ }),
16
+ status: field.enum(['draft', 'published'], {
17
+ default: 'draft',
18
+ description: 'Publication state.',
19
+ }),
20
+ publishedAt: field.datetime({
21
+ description: 'Publication timestamp.',
22
+ }),
23
+ authorId: field.string({
24
+ required: true,
25
+ description: 'Author id from the authors fixture.',
26
+ relation: {
27
+ name: 'author',
28
+ to: 'authors',
29
+ toField: 'id',
30
+ cardinality: 'one',
31
+ },
32
+ }),
33
+ tags: field.string({
34
+ description: 'Comma-separated tags kept scalar for the dependency-free frontmatter parser.',
35
+ }),
36
+ summary: field.string({
37
+ description: 'Short post summary.',
38
+ }),
39
+ body: field.string({
40
+ required: true,
41
+ description: 'Raw MDX body. Rendering and compilation are app-owned.',
42
+ }),
43
+ permalink: field.computed(field.string({
44
+ description: 'Read-only URL path derived from the post id.',
45
+ }), function blog_permalink_resolver({ record }) {
46
+ return `/blog/${record.id}`;
47
+ }),
48
+ readingTimeMinutes: field.computed(field.number({
49
+ description: 'Read-only one-minute minimum estimate derived from the raw body.',
50
+ }), {
51
+ resolveMany({ records }) {
52
+ return records.map((record) => readingTimeMinutes(record.body));
53
+ },
54
+ }),
55
+ },
56
+ });
57
+
58
+ function readingTimeMinutes(body) {
59
+ const wordCount = String(body ?? '').trim().split(/\s+/u).filter(Boolean).length;
60
+ return Math.max(1, Math.ceil(wordCount / 200));
61
+ }
@@ -0,0 +1,15 @@
1
+ ---
2
+ title: Launch Notes
3
+ status: published
4
+ publishedAt: 2026-05-21T09:00:00.000Z
5
+ authorId: author_ada
6
+ tags: local,content,mdx
7
+ summary: Why folder content works well for local apps.
8
+ ---
9
+ # Launch Notes
10
+
11
+ ## Why local content
12
+
13
+ Docs and posts often start as files, but apps still need typed records, route
14
+ metadata, and API access. This example keeps the files readable while making the
15
+ records available through async-db.
@@ -0,0 +1,32 @@
1
+ import { collection, field, files } from '@async/db/schema';
2
+
3
+ export default collection({
4
+ description: 'Documentation pages loaded from frontmatter and raw MDX body files.',
5
+ source: files('./**/*.mdx', { read: 'frontmatter' }),
6
+ idField: 'id',
7
+ fields: {
8
+ id: field.string({
9
+ required: true,
10
+ description: 'Stable document id. Defaults to the filename when frontmatter omits id.',
11
+ }),
12
+ title: field.string({
13
+ required: true,
14
+ description: 'Page title from frontmatter.',
15
+ }),
16
+ section: field.string({
17
+ required: true,
18
+ description: 'Navigation group for this page.',
19
+ }),
20
+ order: field.number({
21
+ default: 100,
22
+ description: 'Sort order inside the section.',
23
+ }),
24
+ summary: field.string({
25
+ description: 'Short page summary.',
26
+ }),
27
+ body: field.string({
28
+ required: true,
29
+ description: 'Raw MDX body. Rendering and compilation are app-owned.',
30
+ }),
31
+ },
32
+ });
@@ -0,0 +1,11 @@
1
+ ---
2
+ title: Intro To Local Content
3
+ section: Guide
4
+ order: 1
5
+ summary: A first document loaded from a folder collection.
6
+ ---
7
+ # Intro To Local Content
8
+
9
+ This page is stored as a normal MDX file under `db/docs`.
10
+
11
+ <Callout tone="info">Apps can compile or render this body with their own MDX pipeline.</Callout>
@@ -0,0 +1,10 @@
1
+ ---
2
+ title: Schema Workflow
3
+ section: Guide
4
+ order: 2
5
+ summary: How folder content maps into typed local records.
6
+ ---
7
+ # Schema Workflow
8
+
9
+ The `index.schema.mjs` marker describes the content contract. The matching MDX
10
+ files provide frontmatter fields plus the raw body string.