@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/dist/server.js ADDED
@@ -0,0 +1,601 @@
1
+ import http from 'node:http';
2
+ import { watch } from 'node:fs';
3
+ import { mkdir } from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import { openDb } from './db.js';
6
+ import { serializeError } from './errors.js';
7
+ import { defaultHttpFeatureRegistry } from './features/http/registry.js';
8
+ import { assertOperationStrictModeReady } from './features/operations/readiness.js';
9
+ import { runMockBehavior } from './mock.js';
10
+ import { createDbOperationHandler } from './operations.js';
11
+ import { handleRestRequest, readJsonBody, sendJson, sendText } from './rest/handler.js';
12
+ import { syncDb } from './sync.js';
13
+ import { createRequestTrace, tracePhase, tracePhaseSync } from './tracing.js';
14
+ export async function startDbServer(options = {}) {
15
+ const db = await openDb({
16
+ ...options,
17
+ allowSourceErrors: true,
18
+ });
19
+ try {
20
+ await assertOperationStrictModeReady(db.config);
21
+ }
22
+ catch (error) {
23
+ await db.close?.();
24
+ throw error;
25
+ }
26
+ const host = options.host ?? db.config.server?.host ?? '127.0.0.1';
27
+ const port = Number(options.port ?? db.config.server?.port ?? 7331);
28
+ const events = createViewerEventHub();
29
+ const requestHandler = createDbRequestHandler(db, {
30
+ events,
31
+ rootRoutes: true,
32
+ });
33
+ let watcher;
34
+ const server = http.createServer((request, response) => {
35
+ requestHandler(request, response).catch((error) => {
36
+ sendJson(response, error.status ?? 500, serializeError(error, 'SERVER_ERROR'));
37
+ });
38
+ });
39
+ server.once('close', () => {
40
+ watcher?.close();
41
+ events.close();
42
+ void db.close?.();
43
+ });
44
+ try {
45
+ await new Promise((resolve, reject) => {
46
+ server.once('error', reject);
47
+ server.listen(port, host, () => resolve());
48
+ });
49
+ watcher = await watchSourceDir(db, events);
50
+ }
51
+ catch (error) {
52
+ events.close();
53
+ try {
54
+ server.close();
55
+ }
56
+ catch {
57
+ // The server may not have reached the listening state.
58
+ }
59
+ throw error;
60
+ }
61
+ const address = server.address();
62
+ const boundPort = address && typeof address === 'object' ? address.port : port;
63
+ return {
64
+ server,
65
+ db,
66
+ url: `http://${host}:${boundPort}`,
67
+ };
68
+ }
69
+ export function createDbRequestHandler(db, options = {}) {
70
+ const events = options.events ?? createViewerEventHub();
71
+ const routes = resolveRequestRoutes(db.config, options);
72
+ return async function dbRequestHandler(request, response, next) {
73
+ const trace = createRequestTrace(db, request, { trace: options.trace });
74
+ let handled = false;
75
+ try {
76
+ handled = await handleRequest(db, request, response, events, routes, trace);
77
+ if (!handled && typeof next === 'function') {
78
+ next();
79
+ }
80
+ return handled;
81
+ }
82
+ catch (error) {
83
+ trace?.setError(error);
84
+ throw error;
85
+ }
86
+ finally {
87
+ trace?.finish(db, response);
88
+ }
89
+ };
90
+ }
91
+ async function handleRequest(db, request, response, events, routes, trace = null) {
92
+ const url = new URL(request.url ?? '/', 'http://db.local');
93
+ if (request.method === 'GET' && url.pathname === routes.eventsPath) {
94
+ trace?.markHandled(response);
95
+ trace?.setRoute({ route: 'events', operation: 'subscribe' });
96
+ events.subscribe(request, response, db);
97
+ return true;
98
+ }
99
+ const operationRef = tracePhaseSync(trace, 'route-match', () => operationRefForRequest(url, routes), {
100
+ family: 'operation',
101
+ });
102
+ if (operationRef) {
103
+ trace?.markHandled(response);
104
+ trace?.setRoute({ route: 'operation', operation: 'execute', id: operationRef });
105
+ await handleRegisteredOperationRequest(db, request, response, operationRef, routes, trace);
106
+ return true;
107
+ }
108
+ const exposureViolation = tracePhaseSync(trace, 'route-exposure', () => routeExposureViolation(db.config, url, routes));
109
+ if (exposureViolation) {
110
+ trace?.markHandled(response);
111
+ trace?.setRoute({ route: exposureViolation.kind, operation: 'exposure-check' });
112
+ sendRouteExposureViolation(response, exposureViolation, routes);
113
+ return true;
114
+ }
115
+ const httpFeatures = defaultHttpFeatureRegistry();
116
+ const featureContext = { db, request, response, url, routes };
117
+ if (httpFeatures.matches(featureContext, { phase: 'preMock' })) {
118
+ trace?.markHandled(response);
119
+ trace?.setRoute(featureTraceRoute(url, routes));
120
+ await tracePhase(trace, 'registered-http-feature', () => httpFeatures.handle(featureContext, { phase: 'preMock' }), {
121
+ phase: 'preMock',
122
+ });
123
+ return true;
124
+ }
125
+ const restUrl = tracePhaseSync(trace, 'route-match', () => restUrlForRequest(url, routes), {
126
+ family: 'rest',
127
+ });
128
+ const handlesRegisteredFeature = httpFeatures.matches(featureContext, { phase: 'postMock' });
129
+ if (!restUrl && !handlesRegisteredFeature) {
130
+ return false;
131
+ }
132
+ if (restUrl && !handlesRegisteredFeature && db.config.rest?.enabled === false) {
133
+ trace?.markHandled(response);
134
+ await handleRestRequest(db, request, response, restUrl, { ...routes, trace });
135
+ return true;
136
+ }
137
+ const mockResult = await tracePhase(trace, 'mock', () => runMockBehavior(db.config, url));
138
+ if (mockResult) {
139
+ trace?.markHandled(response);
140
+ trace?.setRoute({ route: restUrl ? 'rest' : 'mock', operation: 'mock', shortCircuit: true });
141
+ sendJson(response, mockResult.status, mockResult.body);
142
+ return true;
143
+ }
144
+ if (handlesRegisteredFeature) {
145
+ trace?.markHandled(response);
146
+ trace?.setRoute(featureTraceRoute(url, routes));
147
+ await tracePhase(trace, 'registered-http-feature', () => httpFeatures.handle(featureContext, { phase: 'postMock' }), {
148
+ phase: 'postMock',
149
+ });
150
+ return true;
151
+ }
152
+ trace?.markHandled(response);
153
+ await tracePhase(trace, 'rest-handler', () => handleRestRequest(db, request, response, restUrl, { ...routes, trace }));
154
+ return true;
155
+ }
156
+ async function handleRegisteredOperationRequest(db, request, response, ref, routes, trace = null) {
157
+ const operationHandler = createDbOperationHandler(db);
158
+ if (!operationHandler.enabled) {
159
+ sendJson(response, 404, {
160
+ error: {
161
+ code: 'OPERATIONS_DISABLED',
162
+ message: 'Registered operations are not enabled.',
163
+ hint: 'Set operations.enabled to true and provide operations.registry or outputs.operationRegistry.',
164
+ },
165
+ });
166
+ return;
167
+ }
168
+ if (request.method !== 'POST') {
169
+ sendJson(response, 405, {
170
+ error: {
171
+ code: 'OPERATION_METHOD_NOT_ALLOWED',
172
+ message: 'Registered operations must be executed with POST.',
173
+ hint: `Use POST ${joinPaths(routes.apiBase || '', `/operations/${encodeURIComponent(ref)}`)} with a JSON variables body.`,
174
+ details: {
175
+ method: request.method,
176
+ ref,
177
+ },
178
+ },
179
+ });
180
+ return;
181
+ }
182
+ const body = await tracePhase(trace, 'registered-operation-body', () => readJsonBody(request, {
183
+ maxBytes: Number(db.config.server?.maxBodyBytes ?? 1048576),
184
+ }));
185
+ const result = await tracePhase(trace, 'registered-operation-execution', () => operationHandler.executeRequest(ref, body, {
186
+ routes,
187
+ trace,
188
+ }), {
189
+ ref,
190
+ });
191
+ sendOperationResult(response, result);
192
+ }
193
+ function sendOperationResult(response, result) {
194
+ const contentType = String(result.headers?.['content-type'] ?? '');
195
+ if (contentType.includes('application/json')) {
196
+ sendJson(response, result.status, result.body);
197
+ return;
198
+ }
199
+ sendText(response, result.status, result.rawBody ?? String(result.body ?? ''), contentType || 'text/plain; charset=utf-8');
200
+ }
201
+ export async function reloadDb(db) {
202
+ const project = await syncDb(db.config, { allowErrors: true });
203
+ db.resources = new Map(project.resources.map((resource) => [resource.name, resource]));
204
+ db.diagnostics = project.diagnostics;
205
+ db.schemaVersion = Date.now();
206
+ return project;
207
+ }
208
+ export async function watchSourceDir(db, events, options = {}) {
209
+ await mkdir(db.config.sourceDir, { recursive: true });
210
+ let timer;
211
+ let enabled = true;
212
+ const watchImpl = options.watch ?? watch;
213
+ const warn = options.warn ?? ((message) => console.warn(message));
214
+ let watcher;
215
+ try {
216
+ watcher = watchImpl(db.config.sourceDir, { recursive: true }, (_event, filename) => {
217
+ if (!enabled || shouldIgnoreSourceEvent(db, filename)) {
218
+ return;
219
+ }
220
+ clearTimeout(timer);
221
+ timer = setTimeout(async () => {
222
+ try {
223
+ const project = await reloadDb(db);
224
+ events.publish({
225
+ type: project.diagnostics.some((diagnostic) => diagnostic.severity === 'error') ? 'synced-with-errors' : 'synced',
226
+ version: db.schemaVersion,
227
+ diagnostics: project.diagnostics,
228
+ });
229
+ }
230
+ catch (error) {
231
+ const diagnostic = {
232
+ code: 'SERVER_SOURCE_RELOAD_FAILED',
233
+ severity: 'error',
234
+ message: error.message,
235
+ hint: 'Fix the source file and db will try to reload it on the next change.',
236
+ };
237
+ db.diagnostics = [diagnostic];
238
+ db.schemaVersion = Date.now();
239
+ events.publish({
240
+ type: 'sync-error',
241
+ version: db.schemaVersion,
242
+ diagnostics: db.diagnostics,
243
+ });
244
+ }
245
+ }, 75);
246
+ });
247
+ }
248
+ catch (error) {
249
+ enabled = false;
250
+ reportWatchUnavailable(db, events, error, warn);
251
+ return {
252
+ enabled,
253
+ close() {
254
+ clearTimeout(timer);
255
+ },
256
+ };
257
+ }
258
+ watcher.on?.('error', (error) => {
259
+ if (!enabled) {
260
+ return;
261
+ }
262
+ enabled = false;
263
+ clearTimeout(timer);
264
+ try {
265
+ watcher.close();
266
+ }
267
+ catch {
268
+ // The watcher may already be closed by the runtime.
269
+ }
270
+ reportWatchUnavailable(db, events, error, warn);
271
+ });
272
+ return {
273
+ get enabled() {
274
+ return enabled;
275
+ },
276
+ close() {
277
+ enabled = false;
278
+ clearTimeout(timer);
279
+ try {
280
+ watcher.close();
281
+ }
282
+ catch {
283
+ // The watcher may already be closed after an error event.
284
+ }
285
+ },
286
+ };
287
+ }
288
+ function reportWatchUnavailable(db, events, error, warn) {
289
+ const diagnostic = {
290
+ code: 'SERVER_WATCH_UNAVAILABLE',
291
+ severity: 'warn',
292
+ message: `File watching is disabled: ${error.message}`,
293
+ hint: 'async-db serve is still running, but fixture changes will require restarting the server.',
294
+ details: {
295
+ code: error.code,
296
+ },
297
+ };
298
+ db.diagnostics = [...(db.diagnostics ?? []), diagnostic];
299
+ db.schemaVersion = Date.now();
300
+ events.publish({
301
+ type: 'watch-disabled',
302
+ version: db.schemaVersion,
303
+ diagnostics: db.diagnostics,
304
+ });
305
+ warn(`async-db serve: file watching disabled (${error.message}). Restart the server to pick up fixture changes.`);
306
+ }
307
+ function shouldIgnoreSourceEvent(db, filename) {
308
+ if (!filename) {
309
+ return false;
310
+ }
311
+ const relativePath = path.normalize(String(filename));
312
+ if (relativePath.split(path.sep).some((part) => part.startsWith('.'))) {
313
+ return true;
314
+ }
315
+ const absolutePath = path.join(db.config.sourceDir, relativePath);
316
+ if (db.config.operations?.sourceDir && isInsideOrEqualPath(db.config.operations.sourceDir, absolutePath)) {
317
+ return true;
318
+ }
319
+ const relativeStatePath = path.relative(db.config.stateDir, absolutePath);
320
+ return relativeStatePath === '' || (!relativeStatePath.startsWith('..') && !path.isAbsolute(relativeStatePath));
321
+ }
322
+ function isInsideOrEqualPath(parent, child) {
323
+ const relative = path.relative(path.resolve(parent), path.resolve(child));
324
+ return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
325
+ }
326
+ export function createViewerEventHub() {
327
+ const clients = new Set();
328
+ return {
329
+ subscribe(request, response, db) {
330
+ response.writeHead(200, {
331
+ 'content-type': 'text/event-stream; charset=utf-8',
332
+ 'cache-control': 'no-cache, no-transform',
333
+ connection: 'keep-alive',
334
+ });
335
+ response.write(': connected\n\n');
336
+ writeViewerEvent(response, {
337
+ type: 'connected',
338
+ version: db.schemaVersion,
339
+ diagnostics: db.diagnostics ?? [],
340
+ });
341
+ clients.add(response);
342
+ request.on('close', () => {
343
+ clients.delete(response);
344
+ });
345
+ },
346
+ publish(payload) {
347
+ for (const response of clients) {
348
+ writeViewerEvent(response, payload);
349
+ }
350
+ },
351
+ close() {
352
+ for (const response of clients) {
353
+ response.end();
354
+ }
355
+ clients.clear();
356
+ },
357
+ };
358
+ }
359
+ function writeViewerEvent(response, payload) {
360
+ response.write(`event: db\ndata: ${JSON.stringify(payload)}\n\n`);
361
+ }
362
+ function resolveRequestRoutes(config, options = {}) {
363
+ const apiBase = normalizeBasePath(options.apiBase ?? config.server?.apiBase ?? '/__db');
364
+ const restBasePath = options.restBasePath === undefined
365
+ ? `${apiBase}/rest`
366
+ : normalizeOptionalBasePath(options.restBasePath);
367
+ const dataPath = options.dataPath === undefined
368
+ ? normalizeOptionalBasePath(config.server?.dataPath ?? '/db')
369
+ : normalizeOptionalBasePath(options.dataPath);
370
+ const graphqlPath = normalizeBasePath(options.graphqlPath ?? config.graphql?.path ?? '/graphql');
371
+ return {
372
+ apiBase,
373
+ rootRoutes: options.rootRoutes !== false,
374
+ restBasePath,
375
+ dataPath,
376
+ graphqlPath,
377
+ viewerPath: apiBase,
378
+ manifestPath: `${apiBase}/manifest`,
379
+ manifestJsonPath: `${apiBase}/manifest.json`,
380
+ manifestHtmlPath: `${apiBase}/manifest.html`,
381
+ manifestMarkdownPath: `${apiBase}/manifest.md`,
382
+ schemaPath: `${apiBase}/schema`,
383
+ batchPath: `${apiBase}/batch`,
384
+ importPath: `${apiBase}/import`,
385
+ eventsPath: `${apiBase}/events`,
386
+ logPath: `${apiBase}/log`,
387
+ };
388
+ }
389
+ function operationRefForRequest(url, routes) {
390
+ const prefix = `${joinPaths(routes.apiBase || '', '/operations')}/`;
391
+ if (!url.pathname.startsWith(prefix)) {
392
+ return null;
393
+ }
394
+ const [ref] = url.pathname.slice(prefix.length).split('/');
395
+ return ref ? decodeURIComponent(ref) : null;
396
+ }
397
+ function routeExposureViolation(config, url, routes) {
398
+ const kind = routeExposureKind(url, routes);
399
+ if (!kind) {
400
+ return null;
401
+ }
402
+ const exposure = config.server?.expose?.[kind] ?? 'open';
403
+ if (routeExposureAllows(exposure)) {
404
+ return null;
405
+ }
406
+ return {
407
+ kind,
408
+ exposure,
409
+ path: url.pathname,
410
+ };
411
+ }
412
+ function routeExposureKind(url, routes) {
413
+ if (url.pathname === routes.graphqlPath) {
414
+ return 'graphql';
415
+ }
416
+ if (url.pathname === routes.schemaPath) {
417
+ return 'schema';
418
+ }
419
+ if (isManifestRoutePath(url.pathname, routes)) {
420
+ return 'manifest';
421
+ }
422
+ if ([routes.viewerPath, routes.eventsPath, routes.logPath, routes.importPath].includes(url.pathname)) {
423
+ return 'viewer';
424
+ }
425
+ if (isRestExposurePath(url, routes)) {
426
+ return 'rest';
427
+ }
428
+ return null;
429
+ }
430
+ function isRestExposurePath(url, routes) {
431
+ if (url.pathname === routes.batchPath) {
432
+ return true;
433
+ }
434
+ if (routes.restBasePath && pathStartsWith(url.pathname, routes.restBasePath)) {
435
+ return true;
436
+ }
437
+ if (routes.dataPath && pathStartsWith(url.pathname, routes.dataPath)) {
438
+ return true;
439
+ }
440
+ return routes.rootRoutes === true;
441
+ }
442
+ function routeExposureAllows(exposure) {
443
+ if (exposure === undefined || exposure === null || exposure === 'open') {
444
+ return true;
445
+ }
446
+ if (exposure === 'dev') {
447
+ return process.env.NODE_ENV !== 'production';
448
+ }
449
+ return false;
450
+ }
451
+ function sendRouteExposureViolation(response, violation, routes) {
452
+ const label = routeExposureLabel(violation.kind);
453
+ if (violation.kind === 'rest' && violation.exposure === 'registered-only') {
454
+ sendJson(response, 403, {
455
+ error: {
456
+ code: 'REST_REGISTERED_ONLY',
457
+ message: 'Raw REST routes are configured for registered operations only.',
458
+ hint: `Use POST ${joinPaths(routes.apiBase || '', '/operations/{ref}')} with a registered operation ref.`,
459
+ details: {
460
+ path: violation.path,
461
+ exposure: violation.exposure,
462
+ route: violation.kind,
463
+ },
464
+ },
465
+ });
466
+ return;
467
+ }
468
+ if (violation.exposure === 'registered-only') {
469
+ sendJson(response, 403, {
470
+ error: {
471
+ code: `${label.code}_REGISTERED_ONLY`,
472
+ message: `${label.display} routes are configured for registered operations only.`,
473
+ hint: `Set server.expose.${violation.kind} to "open" or "dev" when this route should be reachable.`,
474
+ details: {
475
+ path: violation.path,
476
+ exposure: violation.exposure,
477
+ route: violation.kind,
478
+ },
479
+ },
480
+ });
481
+ return;
482
+ }
483
+ if (violation.exposure === 'dev') {
484
+ sendJson(response, 404, {
485
+ error: {
486
+ code: `${label.code}_DEV_ONLY`,
487
+ message: `${label.display} routes are only exposed outside NODE_ENV=production.`,
488
+ hint: `Set server.expose.${violation.kind} to "open" when this route should be reachable in production.`,
489
+ details: {
490
+ path: violation.path,
491
+ exposure: violation.exposure,
492
+ route: violation.kind,
493
+ },
494
+ },
495
+ });
496
+ return;
497
+ }
498
+ sendJson(response, 404, {
499
+ error: {
500
+ code: `${label.code}_DISABLED`,
501
+ message: `${label.display} routes are disabled by server exposure policy.`,
502
+ hint: `Set server.expose.${violation.kind} to "open" or "dev" when this route should be reachable.`,
503
+ details: {
504
+ path: violation.path,
505
+ exposure: violation.exposure,
506
+ route: violation.kind,
507
+ },
508
+ },
509
+ });
510
+ }
511
+ function routeExposureLabel(kind) {
512
+ const labels = {
513
+ graphql: {
514
+ code: 'GRAPHQL',
515
+ display: 'GraphQL',
516
+ },
517
+ manifest: {
518
+ code: 'MANIFEST',
519
+ display: 'Manifest',
520
+ },
521
+ rest: {
522
+ code: 'REST',
523
+ display: 'REST',
524
+ },
525
+ schema: {
526
+ code: 'SCHEMA',
527
+ display: 'Schema',
528
+ },
529
+ viewer: {
530
+ code: 'VIEWER',
531
+ display: 'Viewer',
532
+ },
533
+ };
534
+ return labels[kind] ?? {
535
+ code: 'ROUTE',
536
+ display: 'Route',
537
+ };
538
+ }
539
+ function featureTraceRoute(url, routes) {
540
+ if (url.pathname === routes.logPath) {
541
+ return { route: 'runtime-log', operation: 'subscribe' };
542
+ }
543
+ if (url.pathname === routes.graphqlPath) {
544
+ return { route: 'graphql', operation: 'execute' };
545
+ }
546
+ return { route: 'http-feature' };
547
+ }
548
+ function restUrlForRequest(url, routes) {
549
+ if (routes.restBasePath && pathStartsWith(url.pathname, routes.restBasePath)) {
550
+ return stripPathBase(url, routes.restBasePath);
551
+ }
552
+ if ([routes.viewerPath, routes.schemaPath, routes.batchPath, routes.importPath].includes(url.pathname) || isManifestRoutePath(url.pathname, routes)) {
553
+ return url;
554
+ }
555
+ if (routes.dataPath && pathStartsWith(url.pathname, routes.dataPath)) {
556
+ return stripPathBase(url, routes.dataPath);
557
+ }
558
+ if (routes.rootRoutes) {
559
+ return url;
560
+ }
561
+ return null;
562
+ }
563
+ function isManifestRoutePath(pathname, routes) {
564
+ if ([routes.manifestPath, routes.manifestJsonPath, routes.manifestHtmlPath, routes.manifestMarkdownPath].includes(pathname)) {
565
+ return true;
566
+ }
567
+ if (!pathname.startsWith(`${routes.manifestPath}.`)) {
568
+ return false;
569
+ }
570
+ const extension = pathname.slice(routes.manifestPath.length + 1);
571
+ return /^[A-Za-z][A-Za-z0-9_-]*$/.test(extension);
572
+ }
573
+ function joinPaths(basePath, routePath) {
574
+ const base = `/${String(basePath ?? '').replace(/^\/+/, '').replace(/\/+$/, '')}`;
575
+ const route = `/${String(routePath ?? '').replace(/^\/+/, '')}`;
576
+ if (base === '/') {
577
+ return route;
578
+ }
579
+ return `${base}${route === '/' ? '' : route}`;
580
+ }
581
+ function stripPathBase(url, basePath) {
582
+ const next = new URL(url.href);
583
+ const stripped = next.pathname.slice(basePath.length);
584
+ next.pathname = stripped.startsWith('/') ? stripped : `/${stripped}`;
585
+ if (next.pathname === '/') {
586
+ return next;
587
+ }
588
+ return next;
589
+ }
590
+ function normalizeOptionalBasePath(value) {
591
+ return value === false || value === null
592
+ ? null
593
+ : normalizeBasePath(value);
594
+ }
595
+ function pathStartsWith(pathname, basePath) {
596
+ return pathname === basePath || pathname.startsWith(`${basePath}/`);
597
+ }
598
+ function normalizeBasePath(value) {
599
+ const pathValue = `/${String(value ?? '').replace(/^\/+/, '').replace(/\/+$/, '')}`;
600
+ return pathValue === '/' ? '' : pathValue;
601
+ }
@@ -0,0 +1,8 @@
1
+ type CsvCell = {
2
+ value: string;
3
+ quoted: boolean;
4
+ };
5
+ type CsvRecordValue = string | number | boolean;
6
+ export declare function parseCsvRecords(text: string, filePath?: string): Array<Record<string, CsvRecordValue>>;
7
+ export declare function parseCsvRows(text: string): CsvCell[][];
8
+ export {};