@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,95 @@
1
+ export const DEFAULT_CONFIG = {
2
+ dbDir: './db',
3
+ sourceDir: './db',
4
+ stateDir: './.db',
5
+ outputs: {
6
+ stateDir: './.db',
7
+ types: './.db/types/index.d.ts',
8
+ committedTypes: null,
9
+ schemaManifest: null,
10
+ viewerManifest: null,
11
+ operationRegistry: null,
12
+ operationRefs: null,
13
+ honoStarterDir: './db-api',
14
+ },
15
+ schemaOutFile: null,
16
+ viewerManifestOutFile: null,
17
+ schemaManifest: {},
18
+ sources: {
19
+ readers: [],
20
+ writePolicy: 'preserve',
21
+ },
22
+ stores: {
23
+ default: 'json',
24
+ json: {
25
+ driver: 'json',
26
+ },
27
+ },
28
+ types: {
29
+ enabled: true,
30
+ outFile: './.db/types/index.d.ts',
31
+ commitOutFile: null,
32
+ useReadonly: false,
33
+ emitComments: true,
34
+ exportRuntimeHelpers: true,
35
+ },
36
+ schema: {
37
+ source: 'auto',
38
+ allowJsonc: true,
39
+ autoModulePackageJson: true,
40
+ standardSchema: false,
41
+ unknownFields: 'warn',
42
+ additiveChanges: 'auto',
43
+ destructiveChanges: 'manual',
44
+ typeChanges: 'manual',
45
+ },
46
+ defaults: {
47
+ applyOnCreate: true,
48
+ applyOnSafeMigration: true,
49
+ },
50
+ seed: {
51
+ generateFromSchema: false,
52
+ generatedCount: 5,
53
+ },
54
+ collections: {},
55
+ resources: {
56
+ naming: 'basename',
57
+ },
58
+ server: {
59
+ apiBase: '/__db',
60
+ dataPath: '/db',
61
+ host: '127.0.0.1',
62
+ port: 7331,
63
+ maxBodyBytes: 1048576,
64
+ viewerLinks: [],
65
+ },
66
+ rest: {
67
+ enabled: true,
68
+ },
69
+ graphql: {
70
+ enabled: true,
71
+ path: '/graphql',
72
+ },
73
+ operations: {
74
+ enabled: false,
75
+ sourceDir: './db/operations',
76
+ outFile: null,
77
+ refsOutFile: null,
78
+ acceptRefs: 'both',
79
+ registry: {},
80
+ },
81
+ mock: {
82
+ delay: [30, 100],
83
+ errors: null,
84
+ },
85
+ generate: {
86
+ hono: {
87
+ outDir: './db-api',
88
+ api: ['rest'],
89
+ db: 'sqlite',
90
+ app: 'standalone',
91
+ runtime: 'node-sqlite',
92
+ seed: false,
93
+ },
94
+ },
95
+ };
@@ -0,0 +1,11 @@
1
+ import { type SchemaLoadMode } from '../schema/locator.js';
2
+ type ConfigRecord = Record<string, any>;
3
+ type LoadConfigOptions = string | (ConfigRecord & {
4
+ configPath?: string;
5
+ cwd?: string;
6
+ from?: string;
7
+ load?: SchemaLoadMode;
8
+ });
9
+ export declare function loadConfig(options?: LoadConfigOptions): Promise<ConfigRecord>;
10
+ export declare function mergeDeep(base: unknown, override: unknown): unknown;
11
+ export {};
@@ -0,0 +1,265 @@
1
+ import { access } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
4
+ import { dbError } from '../../errors.js';
5
+ import { resolveFrom } from '../../fs-utils.js';
6
+ import { DEFAULT_CONFIG } from './defaults.js';
7
+ import { normalizeSchemaLoadMode, resolveSchemaLocator } from '../schema/locator.js';
8
+ export async function loadConfig(options = {}) {
9
+ const rawOptions = typeof options === 'string' ? { from: options } : options;
10
+ const locator = await resolveSchemaLocator(rawOptions);
11
+ const cwd = locator.cwd;
12
+ const configPath = rawOptions.configPath
13
+ ? resolveFrom(cwd, rawOptions.configPath)
14
+ : await findConfigPath(cwd);
15
+ let userConfig = {};
16
+ if (configPath) {
17
+ const url = pathToFileURL(configPath);
18
+ url.searchParams.set('dbConfigLoad', String(Date.now()));
19
+ const module = await import(url.href);
20
+ userConfig = module.default ?? module.config ?? {};
21
+ }
22
+ const inlineOptions = { ...rawOptions };
23
+ delete inlineOptions.cwd;
24
+ delete inlineOptions.configPath;
25
+ delete inlineOptions.from;
26
+ delete inlineOptions.load;
27
+ rejectUnsupportedRuntimeConfig(userConfig);
28
+ rejectUnsupportedRuntimeConfig(inlineOptions);
29
+ rejectRemovedFixtureForkConfig(userConfig);
30
+ rejectRemovedFixtureForkConfig(inlineOptions);
31
+ const merged = mergeDeep(mergeDeep(structuredClone(DEFAULT_CONFIG), userConfig), inlineOptions);
32
+ normalizeOutputAliases(merged, userConfig, inlineOptions);
33
+ merged.cwd = cwd;
34
+ merged.configPath = configPath;
35
+ const hasInlineSourceDir = hasOwnConfigValue(inlineOptions, 'sourceDir') || hasOwnConfigValue(inlineOptions, 'dbDir');
36
+ const hasUserSourceDir = hasOwnConfigValue(userConfig, 'sourceDir') || hasOwnConfigValue(userConfig, 'dbDir');
37
+ const sourceDir = hasInlineSourceDir
38
+ ? (hasOwnConfigValue(inlineOptions, 'sourceDir') ? merged.sourceDir : merged.dbDir)
39
+ : rawOptions.from
40
+ ? locator.sourceDir
41
+ : hasUserSourceDir
42
+ ? (hasOwnConfigValue(userConfig, 'sourceDir') ? merged.sourceDir : merged.dbDir)
43
+ : merged.dbDir;
44
+ merged.sourceDir = resolveFrom(cwd, sourceDir);
45
+ merged.dbDir = merged.sourceDir;
46
+ merged.schemaLocator = {
47
+ ...locator,
48
+ cwd,
49
+ sourceDir: merged.sourceDir,
50
+ };
51
+ merged.schemaLoadMode = normalizeSchemaLoadMode(rawOptions.load ?? 'data');
52
+ merged.stateDir = resolveFrom(cwd, merged.stateDir);
53
+ merged.outputs.stateDir = merged.stateDir;
54
+ if (merged.types?.outFile) {
55
+ merged.types.outFile = resolveFrom(cwd, merged.types.outFile);
56
+ }
57
+ merged.outputs.types = merged.types?.outFile ?? null;
58
+ if (merged.types?.commitOutFile) {
59
+ merged.types.commitOutFile = resolveFrom(cwd, merged.types.commitOutFile);
60
+ }
61
+ merged.outputs.committedTypes = merged.types?.commitOutFile ?? null;
62
+ if (merged.schemaOutFile) {
63
+ merged.schemaOutFile = resolveFrom(cwd, merged.schemaOutFile);
64
+ }
65
+ merged.outputs.schemaManifest = merged.schemaOutFile ?? null;
66
+ if (merged.viewerManifestOutFile) {
67
+ merged.viewerManifestOutFile = resolveFrom(cwd, merged.viewerManifestOutFile);
68
+ }
69
+ merged.outputs.viewerManifest = merged.viewerManifestOutFile ?? null;
70
+ if (merged.operations?.sourceDir) {
71
+ merged.operations.sourceDir = resolveFrom(cwd, merged.operations.sourceDir);
72
+ }
73
+ if (merged.operations?.outFile) {
74
+ merged.operations.outFile = resolveFrom(cwd, merged.operations.outFile);
75
+ }
76
+ merged.outputs.operationRegistry = merged.operations?.outFile ?? null;
77
+ if (merged.operations?.refsOutFile) {
78
+ merged.operations.refsOutFile = resolveFrom(cwd, merged.operations.refsOutFile);
79
+ }
80
+ merged.outputs.operationRefs = merged.operations?.refsOutFile ?? null;
81
+ if (merged.generate?.hono?.outDir) {
82
+ merged.generate.hono.outDir = resolveFrom(cwd, merged.generate.hono.outDir);
83
+ }
84
+ merged.outputs.honoStarterDir = merged.generate?.hono?.outDir ?? null;
85
+ return merged;
86
+ }
87
+ function normalizeOutputAliases(config, userConfig, inlineOptions) {
88
+ config.outputs = config.outputs ?? {};
89
+ config.types = config.types ?? {};
90
+ config.operations = config.operations ?? {};
91
+ config.generate = config.generate ?? {};
92
+ config.generate.hono = config.generate.hono ?? {};
93
+ mirrorOutput(config, userConfig, inlineOptions, 'stateDir', ['stateDir']);
94
+ mirrorOutput(config, userConfig, inlineOptions, 'types', ['types', 'outFile']);
95
+ mirrorOutput(config, userConfig, inlineOptions, 'committedTypes', ['types', 'commitOutFile']);
96
+ mirrorOutput(config, userConfig, inlineOptions, 'schemaManifest', ['schemaOutFile']);
97
+ mirrorOutput(config, userConfig, inlineOptions, 'viewerManifest', ['viewerManifestOutFile']);
98
+ mirrorOutput(config, userConfig, inlineOptions, 'operationRegistry', ['operations', 'outFile']);
99
+ mirrorOutput(config, userConfig, inlineOptions, 'operationRefs', ['operations', 'refsOutFile']);
100
+ mirrorOutput(config, userConfig, inlineOptions, 'honoStarterDir', ['generate', 'hono', 'outDir']);
101
+ }
102
+ function mirrorOutput(config, userConfig, inlineOptions, outputKey, legacyPath) {
103
+ const outputPath = ['outputs', outputKey];
104
+ const value = configuredOutputValue({
105
+ config,
106
+ userConfig,
107
+ inlineOptions,
108
+ outputPath,
109
+ legacyPath,
110
+ });
111
+ setPath(config, outputPath, value);
112
+ setPath(config, legacyPath, value);
113
+ }
114
+ function configuredOutputValue({ config, userConfig, inlineOptions, outputPath, legacyPath, }) {
115
+ if (hasOwnPath(inlineOptions, outputPath)) {
116
+ return getPath(inlineOptions, outputPath);
117
+ }
118
+ if (hasOwnPath(inlineOptions, legacyPath)) {
119
+ return getPath(inlineOptions, legacyPath);
120
+ }
121
+ if (hasOwnPath(userConfig, outputPath)) {
122
+ return getPath(userConfig, outputPath);
123
+ }
124
+ if (hasOwnPath(userConfig, legacyPath)) {
125
+ return getPath(userConfig, legacyPath);
126
+ }
127
+ return getPath(config, legacyPath);
128
+ }
129
+ async function findConfigPath(cwd) {
130
+ for (const filename of ['db.config.mjs', 'db.config.js']) {
131
+ const candidate = path.join(cwd, filename);
132
+ try {
133
+ await access(candidate);
134
+ return candidate;
135
+ }
136
+ catch {
137
+ // keep looking
138
+ }
139
+ }
140
+ return null;
141
+ }
142
+ export function mergeDeep(base, override) {
143
+ if (!isPlainObject(base) || !isPlainObject(override)) {
144
+ return override === undefined ? base : override;
145
+ }
146
+ const output = { ...base };
147
+ for (const [key, value] of Object.entries(override)) {
148
+ if (value === undefined) {
149
+ continue;
150
+ }
151
+ if (isPlainObject(value) && isPlainObject(output[key])) {
152
+ output[key] = mergeDeep(output[key], value);
153
+ }
154
+ else {
155
+ output[key] = value;
156
+ }
157
+ }
158
+ return output;
159
+ }
160
+ function isPlainObject(value) {
161
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
162
+ }
163
+ function hasOwnConfigValue(config, key) {
164
+ if (!isPlainObject(config)) {
165
+ return false;
166
+ }
167
+ return Object.prototype.hasOwnProperty.call(config, key) && config[key] !== undefined;
168
+ }
169
+ function hasOwnPath(config, pathParts) {
170
+ let current = config;
171
+ for (const pathPart of pathParts) {
172
+ if (!isPlainObject(current) || !Object.prototype.hasOwnProperty.call(current, pathPart)) {
173
+ return false;
174
+ }
175
+ current = current[pathPart];
176
+ }
177
+ return current !== undefined;
178
+ }
179
+ function getPath(config, pathParts) {
180
+ let current = config;
181
+ for (const pathPart of pathParts) {
182
+ if (!isPlainObject(current)) {
183
+ return undefined;
184
+ }
185
+ current = current[pathPart];
186
+ }
187
+ return current;
188
+ }
189
+ function setPath(config, pathParts, value) {
190
+ const last = pathParts.at(-1);
191
+ if (last === undefined) {
192
+ return;
193
+ }
194
+ let current = config;
195
+ for (const pathPart of pathParts.slice(0, -1)) {
196
+ if (!isPlainObject(current[pathPart])) {
197
+ current[pathPart] = {};
198
+ }
199
+ current = current[pathPart];
200
+ }
201
+ current[last] = value;
202
+ }
203
+ function rejectRemovedFixtureForkConfig(config) {
204
+ if (!isPlainObject(config)) {
205
+ return;
206
+ }
207
+ for (const key of ['forks', 'templates']) {
208
+ if (!hasOwnConfigValue(config, key)) {
209
+ continue;
210
+ }
211
+ throw dbError('CONFIG_LEGACY_FIXTURE_FORKS_REMOVED', `Unsupported config "${key}". Fixture-folder forks were removed so forks only mean runtime logical databases.`, {
212
+ hint: 'Use runtime forks with db.forks.create(), db.forks.open(), db.forks.ensure(), branches, and snapshots. Keep alternate seed data in normal fixture folders or app-owned import scripts.',
213
+ details: {
214
+ configKey: key,
215
+ },
216
+ });
217
+ }
218
+ }
219
+ function rejectUnsupportedRuntimeConfig(config) {
220
+ const diagnostics = unsupportedRuntimeConfigDiagnostics(config);
221
+ if (diagnostics.length === 0) {
222
+ return;
223
+ }
224
+ const error = new Error(diagnostics.map((diagnostic) => diagnostic.message).join('\n'));
225
+ error.code = 'CONFIG_UNSUPPORTED_RUNTIME_BOUNDARY';
226
+ error.diagnostics = diagnostics;
227
+ throw error;
228
+ }
229
+ function unsupportedRuntimeConfigDiagnostics(config) {
230
+ if (!isPlainObject(config)) {
231
+ return [];
232
+ }
233
+ const diagnostics = [];
234
+ if (hasOwnConfigValue(config, 'mode')) {
235
+ diagnostics.push(unsupportedRuntimeConfigDiagnostic('mode'));
236
+ }
237
+ if (isPlainObject(config.runtime)) {
238
+ if (hasOwnConfigValue(config.runtime, 'default')) {
239
+ diagnostics.push(unsupportedRuntimeConfigDiagnostic('runtime.default'));
240
+ }
241
+ if (hasOwnConfigValue(config.runtime, 'adapters')) {
242
+ diagnostics.push(unsupportedRuntimeConfigDiagnostic('runtime.adapters'));
243
+ }
244
+ }
245
+ else if (hasOwnConfigValue(config, 'runtime')) {
246
+ diagnostics.push(unsupportedRuntimeConfigDiagnostic('runtime'));
247
+ }
248
+ if (isPlainObject(config.resources)) {
249
+ for (const [resourceName, resourceConfig] of Object.entries(config.resources)) {
250
+ if (isPlainObject(resourceConfig) && hasOwnConfigValue(resourceConfig, 'runtime')) {
251
+ diagnostics.push(unsupportedRuntimeConfigDiagnostic(`resources.${resourceName}.runtime`));
252
+ }
253
+ }
254
+ }
255
+ return diagnostics;
256
+ }
257
+ function unsupportedRuntimeConfigDiagnostic(configPath) {
258
+ return {
259
+ code: 'CONFIG_UNSUPPORTED_RUNTIME_BOUNDARY',
260
+ severity: 'error',
261
+ path: configPath,
262
+ message: `Unsupported runtime config at "${configPath}". Runtime config is no longer a public configuration boundary.`,
263
+ hint: 'Use stores.default, named stores, and resources.<name>.store to configure the public storage boundary.',
264
+ };
265
+ }
@@ -0,0 +1,17 @@
1
+ type PlainObject = Record<string, unknown>;
2
+ type ResourceNamingStrategy = 'basename' | 'folder-prefixed' | 'path';
3
+ export type ParsedFixturePath = {
4
+ file: string;
5
+ folders: string[];
6
+ folder: string | null;
7
+ filename: string;
8
+ basename: string;
9
+ extension: string;
10
+ };
11
+ export declare function defineConfig<TConfig extends PlainObject>(config: TConfig): TConfig;
12
+ export declare function mergeManifest(base: unknown, patch: unknown): unknown;
13
+ export declare function resourceNameFromPath(file: string, options?: {
14
+ strategy?: ResourceNamingStrategy;
15
+ }): string;
16
+ export declare function parseFixturePath(file: string): ParsedFixturePath;
17
+ export {};
@@ -0,0 +1,75 @@
1
+ export function defineConfig(config) {
2
+ return config;
3
+ }
4
+ export function mergeManifest(base, patch) {
5
+ return mergePlainObjects(structuredClone(base), patch);
6
+ }
7
+ export function resourceNameFromPath(file, options = {}) {
8
+ const strategy = options.strategy ?? 'basename';
9
+ const parsed = parseFixturePath(file);
10
+ const parts = strategy === 'basename'
11
+ ? [parsed.basename]
12
+ : strategy === 'folder-prefixed'
13
+ ? [...parsed.folders.slice(-1), parsed.basename]
14
+ : [...parsed.folders, parsed.basename];
15
+ return camelCase(parts.filter(Boolean).join('-'));
16
+ }
17
+ export function parseFixturePath(file) {
18
+ const normalized = String(file).split('\\').join('/');
19
+ const parts = normalized.split('/').filter(Boolean);
20
+ const filename = parts.at(-1) ?? '';
21
+ const extension = fixtureExtension(filename);
22
+ const folders = parts.slice(1, -1);
23
+ const basename = extension ? filename.slice(0, -extension.length) : filename;
24
+ return {
25
+ file: normalized,
26
+ folders,
27
+ folder: folders.at(-1) ?? null,
28
+ filename,
29
+ basename,
30
+ extension,
31
+ };
32
+ }
33
+ function mergePlainObjects(base, patch) {
34
+ if (!isPlainObject(base) || !isPlainObject(patch)) {
35
+ return structuredClone(patch);
36
+ }
37
+ const output = { ...base };
38
+ for (const [key, value] of Object.entries(patch)) {
39
+ if (value === undefined) {
40
+ continue;
41
+ }
42
+ if (isPlainObject(value) && isPlainObject(output[key])) {
43
+ output[key] = mergePlainObjects(output[key], value);
44
+ continue;
45
+ }
46
+ output[key] = structuredClone(value);
47
+ }
48
+ return output;
49
+ }
50
+ function isPlainObject(value) {
51
+ return value !== null
52
+ && typeof value === 'object'
53
+ && !Array.isArray(value)
54
+ && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
55
+ }
56
+ function camelCase(value) {
57
+ const words = String(value)
58
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
59
+ .split(/[^A-Za-z0-9]+/)
60
+ .filter(Boolean)
61
+ .map((part) => part.toLowerCase());
62
+ return words.map((word, index) => (index === 0 ? word : `${word.charAt(0).toUpperCase()}${word.slice(1)}`)).join('');
63
+ }
64
+ function fixtureExtension(filename) {
65
+ const schemaMatch = filename.match(/\.schema\.(json|jsonc|mjs|js)$/i);
66
+ if (schemaMatch) {
67
+ return `.schema.${schemaMatch[1].toLowerCase()}`;
68
+ }
69
+ const dataMatch = filename.match(/\.(json|jsonc|csv)$/i);
70
+ if (dataMatch) {
71
+ return `.${dataMatch[1].toLowerCase()}`;
72
+ }
73
+ const genericMatch = filename.match(/(\.[^./\\]+)$/);
74
+ return genericMatch ? genericMatch[1].toLowerCase() : '';
75
+ }
@@ -0,0 +1,18 @@
1
+ type DoctorResource = {
2
+ name: string;
3
+ idField: string;
4
+ seed: Array<Record<string, unknown>>;
5
+ };
6
+ type DoctorFinding = {
7
+ code: string;
8
+ severity: 'warn';
9
+ source: 'doctor';
10
+ resource: string;
11
+ field: string;
12
+ message: string;
13
+ hint: string;
14
+ details: Record<string, unknown>;
15
+ };
16
+ export declare function duplicateIdFindings(resource: DoctorResource): DoctorFinding[];
17
+ export declare function mixedIdTypeFindings(resource: DoctorResource): DoctorFinding[];
18
+ export {};
@@ -0,0 +1,79 @@
1
+ export function duplicateIdFindings(resource) {
2
+ const seen = new Map();
3
+ const findings = [];
4
+ for (const [index, record] of resource.seed.entries()) {
5
+ const value = record?.[resource.idField];
6
+ if (isEmpty(value)) {
7
+ continue;
8
+ }
9
+ const key = String(value);
10
+ const firstIndex = seen.get(key);
11
+ if (firstIndex !== undefined) {
12
+ findings.push({
13
+ code: 'DOCTOR_DUPLICATE_ID',
14
+ severity: 'warn',
15
+ source: 'doctor',
16
+ resource: resource.name,
17
+ field: resource.idField,
18
+ message: `${resource.name} has duplicate ${resource.idField} "${value}" in records ${firstIndex} and ${index}.`,
19
+ hint: `Make each ${resource.name}.${resource.idField} value unique before relying on update, delete, or relation expansion behavior.`,
20
+ details: {
21
+ idField: resource.idField,
22
+ value,
23
+ firstRecordIndex: firstIndex,
24
+ recordIndex: index,
25
+ },
26
+ });
27
+ continue;
28
+ }
29
+ seen.set(key, index);
30
+ }
31
+ return findings;
32
+ }
33
+ export function mixedIdTypeFindings(resource) {
34
+ const counts = valueTypeCounts(resource.seed.map((record) => record?.[resource.idField]));
35
+ if (counts.size <= 1) {
36
+ return [];
37
+ }
38
+ return [
39
+ {
40
+ code: 'DOCTOR_MIXED_ID_TYPES',
41
+ severity: 'warn',
42
+ source: 'doctor',
43
+ resource: resource.name,
44
+ field: resource.idField,
45
+ message: `${resource.name}.${resource.idField} uses mixed value types: ${describeCounts(counts)}.`,
46
+ hint: 'Use one id type consistently. String ids are usually safest for JSON fixtures.',
47
+ details: {
48
+ idField: resource.idField,
49
+ types: Object.fromEntries(counts),
50
+ },
51
+ },
52
+ ];
53
+ }
54
+ function valueTypeCounts(values) {
55
+ const counts = new Map();
56
+ for (const value of values) {
57
+ if (isEmpty(value)) {
58
+ continue;
59
+ }
60
+ const kind = valueKind(value);
61
+ counts.set(kind, (counts.get(kind) ?? 0) + 1);
62
+ }
63
+ return counts;
64
+ }
65
+ function valueKind(value) {
66
+ if (Array.isArray(value)) {
67
+ return 'array';
68
+ }
69
+ if (value === null) {
70
+ return 'null';
71
+ }
72
+ return typeof value;
73
+ }
74
+ function describeCounts(counts) {
75
+ return [...counts.entries()].map(([kind, count]) => `${kind} (${count})`).join(', ');
76
+ }
77
+ function isEmpty(value) {
78
+ return value === undefined || value === null || value === '';
79
+ }
@@ -0,0 +1,17 @@
1
+ type DoctorResource = {
2
+ name: string;
3
+ idField: string;
4
+ seed: unknown[];
5
+ };
6
+ type DoctorFinding = {
7
+ code: string;
8
+ severity: 'warn';
9
+ source: 'doctor';
10
+ resource: string;
11
+ field: string;
12
+ message: string;
13
+ hint: string;
14
+ details: Record<string, unknown>;
15
+ };
16
+ export declare function inconsistentFieldTypeFindings(resource: DoctorResource): DoctorFinding[];
17
+ export {};
@@ -0,0 +1,48 @@
1
+ export function inconsistentFieldTypeFindings(resource) {
2
+ const fieldTypes = new Map();
3
+ for (const record of resource.seed) {
4
+ if (!isPlainRecord(record)) {
5
+ continue;
6
+ }
7
+ for (const [fieldName, value] of Object.entries(record)) {
8
+ if (fieldName === resource.idField || isEmpty(value)) {
9
+ continue;
10
+ }
11
+ const counts = fieldTypes.get(fieldName) ?? new Map();
12
+ counts.set(valueKind(value), (counts.get(valueKind(value)) ?? 0) + 1);
13
+ fieldTypes.set(fieldName, counts);
14
+ }
15
+ }
16
+ return [...fieldTypes.entries()]
17
+ .filter(([, counts]) => counts.size > 1)
18
+ .map(([fieldName, counts]) => ({
19
+ code: 'DOCTOR_INCONSISTENT_FIELD_TYPES',
20
+ severity: 'warn',
21
+ source: 'doctor',
22
+ resource: resource.name,
23
+ field: fieldName,
24
+ message: `${resource.name}.${fieldName} has inconsistent value types: ${describeCounts(counts)}.`,
25
+ hint: `Normalize ${resource.name}.${fieldName} values or add a schema if the mixed shape is intentional.`,
26
+ details: {
27
+ types: Object.fromEntries(counts),
28
+ },
29
+ }));
30
+ }
31
+ function valueKind(value) {
32
+ if (Array.isArray(value)) {
33
+ return 'array';
34
+ }
35
+ if (value === null) {
36
+ return 'null';
37
+ }
38
+ return typeof value;
39
+ }
40
+ function describeCounts(counts) {
41
+ return [...counts.entries()].map(([kind, count]) => `${kind} (${count})`).join(', ');
42
+ }
43
+ function isPlainRecord(value) {
44
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
45
+ }
46
+ function isEmpty(value) {
47
+ return value === undefined || value === null || value === '';
48
+ }
@@ -0,0 +1,39 @@
1
+ type DiagnosticSeverity = 'error' | 'warn' | 'info';
2
+ type DoctorFinding = {
3
+ code: string;
4
+ severity: DiagnosticSeverity;
5
+ source: string;
6
+ resource?: string;
7
+ field?: string;
8
+ message: string;
9
+ hint: string;
10
+ details: Record<string, unknown>;
11
+ [key: string]: unknown;
12
+ };
13
+ type DoctorSummary = Record<DiagnosticSeverity, number>;
14
+ type ResourceConfig = {
15
+ store?: string;
16
+ indexes?: unknown[];
17
+ [key: string]: unknown;
18
+ };
19
+ type StoreConfig = string | {
20
+ driver?: string;
21
+ [key: string]: unknown;
22
+ };
23
+ type DoctorConfig = {
24
+ doctor?: {
25
+ production?: boolean;
26
+ };
27
+ schema?: Record<string, unknown>;
28
+ resources?: Record<string, ResourceConfig>;
29
+ stores?: Record<string, StoreConfig> & {
30
+ default?: string;
31
+ };
32
+ [key: string]: unknown;
33
+ };
34
+ type DoctorResult = {
35
+ summary: DoctorSummary;
36
+ findings: DoctorFinding[];
37
+ };
38
+ export declare function runDbDoctor(config: DoctorConfig): Promise<DoctorResult>;
39
+ export {};