@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,86 @@
1
+ import { handleGraphqlRequest } from '../../graphql/http.js';
2
+ import { sendJson } from '../../rest/handler.js';
3
+ export function defaultHttpFeatureRegistry() {
4
+ return createHttpFeatureRegistry([
5
+ runtimeLogHttpFeature(),
6
+ graphqlDisabledHttpFeature(),
7
+ graphqlHttpFeature(),
8
+ ]);
9
+ }
10
+ export function createHttpFeatureRegistry(features) {
11
+ return {
12
+ matches(context, options = {}) {
13
+ return features.some((feature) => featureInPhase(feature, options.phase) && feature.match(context));
14
+ },
15
+ async handle(context, options = {}) {
16
+ for (const feature of features) {
17
+ if (featureInPhase(feature, options.phase) && feature.match(context)) {
18
+ await feature.handle(context);
19
+ return true;
20
+ }
21
+ }
22
+ return false;
23
+ },
24
+ };
25
+ }
26
+ function runtimeLogHttpFeature() {
27
+ return {
28
+ name: 'runtime-log',
29
+ phase: 'preMock',
30
+ match({ request, url, routes }) {
31
+ return request.method === 'GET' && url.pathname === routes.logPath;
32
+ },
33
+ async handle({ request, response, db }) {
34
+ subscribeRuntimeLog(request, response, db);
35
+ },
36
+ };
37
+ }
38
+ function graphqlHttpFeature() {
39
+ return {
40
+ name: 'graphql',
41
+ phase: 'postMock',
42
+ match({ db, url, routes }) {
43
+ return db.config.graphql?.enabled !== false && url.pathname === routes.graphqlPath;
44
+ },
45
+ async handle({ db, request, response }) {
46
+ await handleGraphqlRequest(db, request, response);
47
+ },
48
+ };
49
+ }
50
+ function graphqlDisabledHttpFeature() {
51
+ return {
52
+ name: 'graphql-disabled',
53
+ phase: 'preMock',
54
+ match({ db, url, routes }) {
55
+ return db.config.graphql?.enabled === false && url.pathname === routes.graphqlPath;
56
+ },
57
+ async handle({ response, routes }) {
58
+ sendJson(response, 404, {
59
+ error: {
60
+ code: 'GRAPHQL_DISABLED',
61
+ message: 'GraphQL endpoint is disabled.',
62
+ hint: 'Set graphql.enabled to true in db.config.mjs to enable the GraphQL endpoint.',
63
+ details: {
64
+ graphqlEnabled: false,
65
+ path: routes.graphqlPath,
66
+ },
67
+ },
68
+ });
69
+ },
70
+ };
71
+ }
72
+ function featureInPhase(feature, phase) {
73
+ return !phase || (feature.phase ?? 'postMock') === phase;
74
+ }
75
+ function subscribeRuntimeLog(request, response, db) {
76
+ response.writeHead(200, {
77
+ 'content-type': 'text/event-stream; charset=utf-8',
78
+ 'cache-control': 'no-cache, no-transform',
79
+ connection: 'keep-alive',
80
+ });
81
+ response.write(': connected\n\n');
82
+ const unsubscribe = db.events.subscribe((event) => {
83
+ response.write(`event: db-log\ndata: ${JSON.stringify(event)}\n\n`);
84
+ });
85
+ request.on('close', unsubscribe);
86
+ }
@@ -0,0 +1,49 @@
1
+ import { type OperationTemplate, type RegisteredOperation } from '../../shared/operations.js';
2
+ type OperationConfig = {
3
+ cwd?: string;
4
+ operations?: {
5
+ sourceDir?: string;
6
+ outFile?: string | null;
7
+ refsOutFile?: string | null;
8
+ };
9
+ };
10
+ type BuildOperationOptions = {
11
+ outFile?: string | null;
12
+ refsOutFile?: string | null;
13
+ generatedAt?: string;
14
+ operations?: OperationTemplate[];
15
+ write?: boolean;
16
+ createDirectory?: boolean;
17
+ };
18
+ type OperationRef = {
19
+ name: string;
20
+ ref: string;
21
+ };
22
+ type OperationManifest = {
23
+ version: 1;
24
+ kind: 'db.operations';
25
+ generatedAt: string;
26
+ operations: Record<string, RegisteredOperation>;
27
+ };
28
+ type OperationRefsManifest = {
29
+ version: 1;
30
+ kind: 'db.operationRefs';
31
+ generatedAt: string;
32
+ operations: Record<string, OperationRef>;
33
+ };
34
+ type OperationContract = {
35
+ version: 1;
36
+ kind: 'db.operationContract';
37
+ operations: Record<string, OperationRef>;
38
+ };
39
+ export declare function hashOperation(input: OperationTemplate): string;
40
+ export declare function buildOperationManifest(config: OperationConfig, options?: BuildOperationOptions): Promise<{
41
+ manifest: OperationManifest;
42
+ refs: OperationRefsManifest;
43
+ outFiles: string[];
44
+ refsOutFiles: string[];
45
+ }>;
46
+ export declare function buildOperationRegistry(config: OperationConfig, options?: BuildOperationOptions): Promise<Record<string, RegisteredOperation>>;
47
+ export declare function operationClientContract(refs: Partial<OperationRefsManifest> | null | undefined): OperationContract;
48
+ export declare function loadOperationSources(config: OperationConfig, options?: BuildOperationOptions): Promise<OperationTemplate[]>;
49
+ export {};
@@ -0,0 +1,199 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { mkdir, readdir, readFile } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { dbError } from '../../errors.js';
5
+ import { parseJsonc } from '../../jsonc.js';
6
+ import { resolveFrom, writeText } from '../../fs-utils.js';
7
+ import { canonicalOperation, normalizeOperationTemplate, stableStringify, } from '../../shared/operations.js';
8
+ import { operationMapFromEntries } from './maps.js';
9
+ export function hashOperation(input) {
10
+ return `sha256:${createHash('sha256').update(stableStringify(canonicalOperation(input))).digest('hex')}`;
11
+ }
12
+ export async function buildOperationManifest(config, options = {}) {
13
+ const operations = await loadOperationSources(config, {
14
+ ...options,
15
+ createDirectory: true,
16
+ });
17
+ const generatedAt = options.generatedAt ?? new Date().toISOString();
18
+ const registryEntries = buildRegistryEntries(operations);
19
+ const registry = operationMapFromEntries(registryEntries);
20
+ const refs = {
21
+ version: 1,
22
+ kind: 'db.operationRefs',
23
+ generatedAt,
24
+ operations: operationMapFromEntries(registryEntries.map(([ref, operation]) => [
25
+ operation.name ?? ref,
26
+ {
27
+ name: operation.name ?? ref,
28
+ ref: operation.ref,
29
+ },
30
+ ])),
31
+ };
32
+ const manifest = {
33
+ version: 1,
34
+ kind: 'db.operations',
35
+ generatedAt,
36
+ operations: registry,
37
+ };
38
+ const outFiles = [];
39
+ const refsOutFiles = [];
40
+ const shouldWrite = options.write !== false;
41
+ const outFile = outputPath(config, optionValue(options, 'outFile', config.operations?.outFile));
42
+ const refsOutFile = outputPath(config, optionValue(options, 'refsOutFile', config.operations?.refsOutFile));
43
+ if (shouldWrite && outFile) {
44
+ await writeText(outFile, `${JSON.stringify(manifest, null, 2)}\n`);
45
+ outFiles.push(outFile);
46
+ }
47
+ if (shouldWrite && refsOutFile) {
48
+ await writeText(refsOutFile, `${JSON.stringify(refs, null, 2)}\n`);
49
+ refsOutFiles.push(refsOutFile);
50
+ }
51
+ return {
52
+ manifest,
53
+ refs,
54
+ outFiles,
55
+ refsOutFiles,
56
+ };
57
+ }
58
+ export async function buildOperationRegistry(config, options = {}) {
59
+ const operations = await loadOperationSources(config, options);
60
+ return operationMapFromEntries(buildRegistryEntries(operations));
61
+ }
62
+ export function operationClientContract(refs) {
63
+ return {
64
+ version: 1,
65
+ kind: 'db.operationContract',
66
+ operations: operationMapFromEntries(Object.entries(refs?.operations ?? {})
67
+ .sort(([left], [right]) => left.localeCompare(right))
68
+ .map(([name, operationRef]) => [
69
+ name,
70
+ {
71
+ name: operationRef?.name ?? name,
72
+ ref: operationRef?.ref,
73
+ },
74
+ ])),
75
+ };
76
+ }
77
+ function buildRegistryEntries(operations) {
78
+ const seenNames = new Map();
79
+ const seenRefs = new Map();
80
+ return operations.map((operation, index) => {
81
+ const normalized = normalizeOperationTemplate(operation);
82
+ const ref = normalized.ref ?? hashOperation(normalized);
83
+ const name = normalized.name ?? ref;
84
+ const previousName = seenNames.get(name);
85
+ if (previousName) {
86
+ throw duplicateOperationName(name, previousName, { index, ref });
87
+ }
88
+ const previousRef = seenRefs.get(ref);
89
+ if (previousRef) {
90
+ throw duplicateOperationRef(ref, previousRef, { index, name });
91
+ }
92
+ seenNames.set(name, { index, ref });
93
+ seenRefs.set(ref, { index, name });
94
+ return [ref, {
95
+ ...normalized,
96
+ name,
97
+ ref,
98
+ }];
99
+ });
100
+ }
101
+ function duplicateOperationName(name, previous, current) {
102
+ return dbError('OPERATION_DUPLICATE_NAME', `Registered operation name "${name}" is used more than once.`, {
103
+ status: 400,
104
+ hint: 'Give each registered operation a unique name so generated client refs map to one callable operation.',
105
+ details: {
106
+ name,
107
+ firstIndex: previous.index,
108
+ duplicateIndex: current.index,
109
+ firstRef: previous.ref,
110
+ duplicateRef: current.ref,
111
+ },
112
+ });
113
+ }
114
+ function duplicateOperationRef(ref, previous, current) {
115
+ return dbError('OPERATION_DUPLICATE_REF', `Registered operation ref "${ref}" is used more than once.`, {
116
+ status: 400,
117
+ hint: 'Give each registered operation a unique ref so generated client refs map to one callable operation.',
118
+ details: {
119
+ ref,
120
+ firstIndex: previous.index,
121
+ duplicateIndex: current.index,
122
+ firstName: previous.name,
123
+ duplicateName: current.name,
124
+ },
125
+ });
126
+ }
127
+ export async function loadOperationSources(config, options = {}) {
128
+ if (Array.isArray(options.operations)) {
129
+ return options.operations;
130
+ }
131
+ const sourceDir = config.operations?.sourceDir;
132
+ if (!sourceDir) {
133
+ return [];
134
+ }
135
+ if (options.createDirectory === true) {
136
+ try {
137
+ await mkdir(sourceDir, { recursive: true });
138
+ }
139
+ catch {
140
+ return [];
141
+ }
142
+ }
143
+ let files = [];
144
+ try {
145
+ files = await listOperationFiles(sourceDir);
146
+ }
147
+ catch (error) {
148
+ if (error?.code === 'ENOENT' || error?.code === 'ENOTDIR') {
149
+ return [];
150
+ }
151
+ throw error;
152
+ }
153
+ const operations = [];
154
+ for (const filePath of files) {
155
+ const text = await readFile(filePath, 'utf8');
156
+ const extension = path.extname(filePath);
157
+ if (extension === '.json' || extension === '.jsonc') {
158
+ const parsed = parseJsonc(text, filePath);
159
+ operations.push(...(Array.isArray(parsed) ? parsed : [parsed]));
160
+ continue;
161
+ }
162
+ const lines = text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
163
+ const template = lines.find((line) => !line.startsWith('#'));
164
+ if (template) {
165
+ operations.push({
166
+ name: operationNameFromFile(filePath),
167
+ ...normalizeOperationTemplate(template),
168
+ });
169
+ }
170
+ }
171
+ return operations;
172
+ }
173
+ async function listOperationFiles(directory) {
174
+ const entries = await readdir(directory, { withFileTypes: true });
175
+ const files = [];
176
+ for (const entry of entries) {
177
+ const filePath = path.join(directory, entry.name);
178
+ if (entry.isDirectory()) {
179
+ files.push(...await listOperationFiles(filePath));
180
+ }
181
+ else if (/\.(jsonc?|rest|txt)$/i.test(entry.name)) {
182
+ files.push(filePath);
183
+ }
184
+ }
185
+ return files.sort();
186
+ }
187
+ function operationNameFromFile(filePath) {
188
+ const basename = path.basename(filePath).replace(/\.(jsonc?|rest|txt)$/i, '');
189
+ return basename.replace(/(^|[-_])([a-z0-9])/gi, (_match, _separator, char) => char.toUpperCase());
190
+ }
191
+ function optionValue(options, key, fallback) {
192
+ return Object.hasOwn(options, key) && options[key] !== undefined ? options[key] : fallback;
193
+ }
194
+ function outputPath(config, value) {
195
+ if (!value) {
196
+ return null;
197
+ }
198
+ return resolveFrom(config.cwd, String(value));
199
+ }
@@ -0,0 +1 @@
1
+ export declare function operationMapFromEntries<T = unknown>(entries?: Iterable<readonly [PropertyKey, T]>): Record<string, T>;
@@ -0,0 +1,10 @@
1
+ export function operationMapFromEntries(entries = []) {
2
+ // Operation names and refs are developer-controlled contract keys. Keep maps
3
+ // null-prototype so "__proto__" and "constructor" stay data keys instead of
4
+ // resolving inherited Object.prototype members during dynamic lookup.
5
+ const map = Object.create(null);
6
+ for (const [key, value] of entries) {
7
+ map[String(key)] = value;
8
+ }
9
+ return map;
10
+ }
@@ -0,0 +1,30 @@
1
+ export declare const OPERATIONS_STRICT_MODE_CODE = "OPERATIONS_STRICT_MODE_WITHOUT_OPERATIONS";
2
+ type OperationConfig = {
3
+ server?: {
4
+ expose?: {
5
+ rest?: string;
6
+ };
7
+ };
8
+ operations?: {
9
+ enabled?: boolean;
10
+ acceptRefs?: string;
11
+ registry?: Record<string, unknown>;
12
+ outFile?: string | null;
13
+ sourceDir?: string;
14
+ resolveRef?: unknown;
15
+ };
16
+ [key: string]: unknown;
17
+ };
18
+ type OperationDiagnostic = {
19
+ code: string;
20
+ severity: 'error' | 'info';
21
+ source: 'doctor';
22
+ message: string;
23
+ hint: string;
24
+ details: Record<string, unknown>;
25
+ };
26
+ export declare function operationStrictModeFindings(config: OperationConfig, options?: {
27
+ includeGuidance?: boolean;
28
+ }): Promise<OperationDiagnostic[]>;
29
+ export declare function assertOperationStrictModeReady(config: OperationConfig): Promise<void>;
30
+ export {};
@@ -0,0 +1,228 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { normalizeOperationTemplate } from '../../shared/operations.js';
3
+ import { buildOperationRegistry } from './index.js';
4
+ export const OPERATIONS_STRICT_MODE_CODE = 'OPERATIONS_STRICT_MODE_WITHOUT_OPERATIONS';
5
+ const ACCEPT_REFS_RECOMMENDATION_CODE = 'OPERATIONS_STRICT_MODE_ACCEPT_REFS_RECOMMENDED';
6
+ export async function operationStrictModeFindings(config, options = {}) {
7
+ if (config.server?.expose?.rest !== 'registered-only') {
8
+ return [];
9
+ }
10
+ const includeGuidance = options.includeGuidance !== false;
11
+ const readiness = await operationStrictModeReadiness(config);
12
+ if (!readiness.ready) {
13
+ return [operationStrictModeDiagnostic(readiness)];
14
+ }
15
+ if (includeGuidance && (config.operations?.acceptRefs ?? 'both') !== 'ref') {
16
+ return [operationAcceptRefsRecommendation(config)];
17
+ }
18
+ return [];
19
+ }
20
+ export async function assertOperationStrictModeReady(config) {
21
+ const findings = await operationStrictModeFindings(config, {
22
+ includeGuidance: false,
23
+ });
24
+ const errors = findings.filter((finding) => finding.severity === 'error');
25
+ if (errors.length === 0) {
26
+ return;
27
+ }
28
+ const error = new Error(errors.map((finding) => finding.message).join('\n'));
29
+ error.code = errors[0].code;
30
+ error.hint = errors[0].hint;
31
+ error.details = errors[0].details;
32
+ error.diagnostics = errors;
33
+ throw error;
34
+ }
35
+ async function operationStrictModeReadiness(config) {
36
+ const operations = config.operations ?? {};
37
+ const details = {
38
+ restExposure: 'registered-only',
39
+ operationsEnabled: operations.enabled === true,
40
+ registry: inlineRegistryDetails(operations),
41
+ outFile: operations.outFile ? { configured: true, path: String(operations.outFile) } : { configured: false },
42
+ resolveRef: { configured: typeof operations.resolveRef === 'function' },
43
+ sourceDir: operations.sourceDir ? { configured: true, path: String(operations.sourceDir) } : { configured: false },
44
+ };
45
+ if (operations.enabled !== true) {
46
+ return {
47
+ ready: false,
48
+ reason: 'disabled',
49
+ details,
50
+ };
51
+ }
52
+ if (details.registry.count > 0) {
53
+ return {
54
+ ready: true,
55
+ source: 'registry',
56
+ details,
57
+ };
58
+ }
59
+ if (operations.outFile) {
60
+ const registryFile = await inspectOperationRegistryFile(operations.outFile);
61
+ details.outFile = {
62
+ ...details.outFile,
63
+ ...registryFile.details,
64
+ };
65
+ return registryFile.ready
66
+ ? {
67
+ ready: true,
68
+ source: 'outFile',
69
+ details,
70
+ }
71
+ : {
72
+ ready: false,
73
+ reason: registryFile.reason,
74
+ details,
75
+ };
76
+ }
77
+ if (typeof operations.resolveRef === 'function') {
78
+ return {
79
+ ready: true,
80
+ source: 'resolveRef',
81
+ details,
82
+ };
83
+ }
84
+ if (operations.sourceDir) {
85
+ const sourceDir = await inspectOperationSourceDir(config);
86
+ details.sourceDir = {
87
+ ...details.sourceDir,
88
+ ...sourceDir.details,
89
+ };
90
+ if (sourceDir.ready) {
91
+ return {
92
+ ready: true,
93
+ source: 'sourceDir',
94
+ details,
95
+ };
96
+ }
97
+ if (sourceDir.reason !== 'source-empty') {
98
+ return {
99
+ ready: false,
100
+ reason: sourceDir.reason,
101
+ details,
102
+ };
103
+ }
104
+ }
105
+ return {
106
+ ready: false,
107
+ reason: 'no-source',
108
+ details,
109
+ };
110
+ }
111
+ function inlineRegistryDetails(operations = {}) {
112
+ const count = operations.registry && typeof operations.registry === 'object'
113
+ ? Object.keys(operations.registry).length
114
+ : 0;
115
+ return {
116
+ configured: count > 0,
117
+ count,
118
+ };
119
+ }
120
+ async function inspectOperationRegistryFile(outFile) {
121
+ try {
122
+ const manifest = JSON.parse(await readFile(outFile, 'utf8'));
123
+ const entries = Object.entries(manifest.operations ?? {});
124
+ for (const [, operation] of entries) {
125
+ normalizeOperationTemplate(operation);
126
+ }
127
+ if (entries.length === 0) {
128
+ return {
129
+ ready: false,
130
+ reason: 'registry-empty',
131
+ details: {
132
+ reason: 'empty',
133
+ count: 0,
134
+ },
135
+ };
136
+ }
137
+ return {
138
+ ready: true,
139
+ details: {
140
+ count: entries.length,
141
+ },
142
+ };
143
+ }
144
+ catch (error) {
145
+ return {
146
+ ready: false,
147
+ reason: 'registry-load-failed',
148
+ details: {
149
+ reason: operationRegistryLoadReason(error),
150
+ error: error.message,
151
+ },
152
+ };
153
+ }
154
+ }
155
+ async function inspectOperationSourceDir(config) {
156
+ try {
157
+ const registry = await buildOperationRegistry(config, {
158
+ createDirectory: false,
159
+ });
160
+ const count = Object.keys(registry).length;
161
+ return count > 0
162
+ ? {
163
+ ready: true,
164
+ details: {
165
+ count,
166
+ },
167
+ }
168
+ : {
169
+ ready: false,
170
+ reason: 'source-empty',
171
+ details: {
172
+ reason: 'empty',
173
+ count: 0,
174
+ },
175
+ };
176
+ }
177
+ catch (error) {
178
+ return {
179
+ ready: false,
180
+ reason: 'source-load-failed',
181
+ details: {
182
+ reason: operationRegistryLoadReason(error),
183
+ error: error.message,
184
+ },
185
+ };
186
+ }
187
+ }
188
+ function operationStrictModeDiagnostic(readiness) {
189
+ return {
190
+ code: OPERATIONS_STRICT_MODE_CODE,
191
+ severity: 'error',
192
+ source: 'doctor',
193
+ message: strictModeMessage(readiness.reason),
194
+ hint: 'Set operations.enabled: true and provide outputs.operationRegistry, operations.registry, operations.resolveRef, or operation source files; or use server.expose.rest: "open" for local REST routes.',
195
+ details: {
196
+ ...readiness.details,
197
+ reason: readiness.reason,
198
+ },
199
+ };
200
+ }
201
+ function strictModeMessage(reason) {
202
+ if (reason === 'disabled') {
203
+ return 'server.expose.rest: "registered-only" requires registered operations to be enabled.';
204
+ }
205
+ return 'server.expose.rest: "registered-only" requires registered operations to be enabled and resolvable.';
206
+ }
207
+ function operationAcceptRefsRecommendation(config) {
208
+ return {
209
+ code: ACCEPT_REFS_RECOMMENDATION_CODE,
210
+ severity: 'info',
211
+ source: 'doctor',
212
+ message: 'Operation-only REST exposure is easiest to review when public clients use operations.acceptRefs: "ref".',
213
+ hint: 'Set operations.acceptRefs: "ref" for public operation-only APIs, or keep the current value for readable local/internal refs.',
214
+ details: {
215
+ acceptRefs: config.operations?.acceptRefs ?? 'both',
216
+ restExposure: 'registered-only',
217
+ },
218
+ };
219
+ }
220
+ function operationRegistryLoadReason(error) {
221
+ if ('code' in error && error.code === 'ENOENT') {
222
+ return 'missing';
223
+ }
224
+ if (error instanceof SyntaxError) {
225
+ return 'invalid-json';
226
+ }
227
+ return 'read-failed';
228
+ }
@@ -0,0 +1,57 @@
1
+ import { type OperationTemplate, type OperationVariables, type RegisteredOperation } from '../../shared/operations.js';
2
+ type OperationAcceptRefs = 'ref' | 'name' | 'both';
3
+ type OperationRegistry = Record<string, OperationTemplate>;
4
+ type OperationRefContext = {
5
+ ref: string;
6
+ decodedRef: string;
7
+ acceptRefs: OperationAcceptRefs;
8
+ registry: Record<string, RegisteredOperation>;
9
+ operation: RegisteredOperation | null;
10
+ };
11
+ type OperationOptions = {
12
+ enabled?: boolean;
13
+ acceptRefs?: OperationAcceptRefs | 'hash';
14
+ registry?: OperationRegistry;
15
+ outFile?: string | null;
16
+ sourceDir?: string;
17
+ resolveRef?: (ref: string, context: Omit<OperationRefContext, 'operation'>) => OperationTemplate | null | undefined | Promise<OperationTemplate | null | undefined>;
18
+ validateRef?: (context: OperationRefContext) => boolean | null | undefined | OperationTemplate | Promise<boolean | null | undefined | OperationTemplate>;
19
+ };
20
+ type DbLike = {
21
+ config: {
22
+ cwd?: string;
23
+ graphql?: {
24
+ enabled?: boolean;
25
+ };
26
+ operations?: OperationOptions;
27
+ [key: string]: unknown;
28
+ };
29
+ [key: string]: unknown;
30
+ };
31
+ type CreateHandlerOptions = OperationOptions | {
32
+ operations?: boolean | 'auto' | OperationOptions;
33
+ };
34
+ type ExecutionOptions = {
35
+ routes?: Record<string, unknown>;
36
+ trace?: unknown;
37
+ };
38
+ type OperationExecutionResult = {
39
+ kind: 'rest' | 'graphql';
40
+ status: number;
41
+ headers: Record<string, unknown>;
42
+ body: unknown;
43
+ rawBody?: string;
44
+ };
45
+ export declare function createDbOperationHandler(db: DbLike, options?: CreateHandlerOptions): {
46
+ enabled: boolean;
47
+ options: OperationOptions & {
48
+ enabled: boolean;
49
+ acceptRefs: OperationAcceptRefs;
50
+ };
51
+ resolve(ref: string): Promise<RegisteredOperation | null>;
52
+ execute(ref: string, variables?: OperationVariables, executionOptions?: ExecutionOptions): Promise<OperationExecutionResult>;
53
+ executeRequest(ref: string, body?: {
54
+ variables?: OperationVariables;
55
+ } | null, executionOptions?: ExecutionOptions): Promise<any>;
56
+ };
57
+ export {};