@cosmicdrift/kumiko-framework 0.386.0 → 0.388.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 (468) hide show
  1. package/AGENTS.md +6 -1
  2. package/LICENSE +21 -57
  3. package/README.md +2 -2
  4. package/agents/examples/anonymous-access.md +1 -1
  5. package/agents/examples/apex-landing.md +204 -0
  6. package/agents/examples/api-worker-split.md +64 -0
  7. package/agents/examples/app-config.md +14 -0
  8. package/agents/examples/auth-mfa.md +115 -0
  9. package/agents/examples/env-composition.md +28 -1
  10. package/agents/examples/legal-pages.md +1 -1
  11. package/agents/examples/prod-boot-options.md +16 -0
  12. package/agents/examples/rate-limiting.md +48 -0
  13. package/agents/examples/record-detail-layout.md +692 -0
  14. package/agents/examples/seed-migration.md +212 -0
  15. package/agents/examples/wizard-form.md +941 -0
  16. package/agents/examples/writeform-section.md +548 -0
  17. package/agents/features.md +19 -2
  18. package/agents/widgets.md +40 -39
  19. package/dist/api/api-constants.js +2 -2
  20. package/dist/api/auth-middleware.js +7 -7
  21. package/dist/api/auth-rate-limiters.js +7 -3
  22. package/dist/api/auth-routes-mfa.js +1 -1
  23. package/dist/api/auth-routes-session.js +1 -1
  24. package/dist/api/auth-routes-shared.js +7 -3
  25. package/dist/api/auth-routes-signup.js +1 -1
  26. package/dist/api/client-error-route.js +8 -2
  27. package/dist/api/client-ip.js +3 -1
  28. package/dist/api/extra-route.js +1 -1
  29. package/dist/api/index.d.ts +3 -1
  30. package/dist/api/index.js +3 -1
  31. package/dist/api/pii-leak-guard.js +4 -2
  32. package/dist/api/readiness.js +5 -5
  33. package/dist/api/redis-sse-broker.js +8 -6
  34. package/dist/api/request-context.d.ts +1 -1
  35. package/dist/api/request-context.js +1 -1
  36. package/dist/api/routes.js +2 -2
  37. package/dist/api/server-boot-checks.d.ts +10 -0
  38. package/dist/api/server-boot-checks.js +157 -0
  39. package/dist/api/server-event-dispatcher.d.ts +26 -0
  40. package/dist/api/server-event-dispatcher.js +208 -0
  41. package/dist/api/server-extra-routes.d.ts +11 -0
  42. package/dist/api/server-extra-routes.js +203 -0
  43. package/dist/api/server-middleware.d.ts +20 -0
  44. package/dist/api/server-middleware.js +211 -0
  45. package/dist/api/server-routes.d.ts +19 -0
  46. package/dist/api/server-routes.js +293 -0
  47. package/dist/api/server-websocket-routes.d.ts +4 -0
  48. package/dist/api/server-websocket-routes.js +108 -0
  49. package/dist/api/server-wiring.d.ts +32 -0
  50. package/dist/api/server-wiring.js +236 -0
  51. package/dist/api/server.d.ts +10 -15
  52. package/dist/api/server.js +48 -1250
  53. package/dist/api/session-cookies.js +4 -4
  54. package/dist/api/sse-broker.js +1 -1
  55. package/dist/api/sse-route.d.ts +13 -13
  56. package/dist/api/sse-route.js +31 -27
  57. package/dist/auth/index.js +4 -4
  58. package/dist/auth/roles.d.ts +4 -4
  59. package/dist/auth/roles.js +24 -24
  60. package/dist/compliance/duration-spec.js +18 -20
  61. package/dist/compliance/index.js +2 -3
  62. package/dist/compliance/override-schema.d.ts +4 -4
  63. package/dist/compliance/override-schema.js +19 -22
  64. package/dist/compliance/profiles.d.ts +26 -29
  65. package/dist/compliance/profiles.js +62 -69
  66. package/dist/compliance/sub-processors.d.ts +28 -30
  67. package/dist/compliance/sub-processors.js +17 -21
  68. package/dist/consumer-cli.js +4 -4
  69. package/dist/crypto/blind-index.js +18 -17
  70. package/dist/crypto/ciphertext-pattern.js +1 -1
  71. package/dist/crypto/event-pii.js +3 -3
  72. package/dist/crypto/kek-source.d.ts +1 -1
  73. package/dist/crypto/kek-source.js +3 -2
  74. package/dist/crypto/kms-adapter.d.ts +1 -1
  75. package/dist/crypto/kms-adapter.js +4 -6
  76. package/dist/crypto/kms-wiring.d.ts +1 -2
  77. package/dist/crypto/kms-wiring.js +2 -2
  78. package/dist/crypto/owner-transition-rekey.d.ts +5 -1
  79. package/dist/crypto/owner-transition-rekey.js +1 -1
  80. package/dist/crypto/pg-kms-adapter.js +2 -6
  81. package/dist/crypto/pii-field-encryption.d.ts +5 -1
  82. package/dist/crypto/pii-field-encryption.js +7 -5
  83. package/dist/crypto/subject-resolver.d.ts +1 -1
  84. package/dist/crypto/subject-resolver.js +2 -2
  85. package/dist/db/aggregate-sql.d.ts +21 -0
  86. package/dist/db/aggregate-sql.js +155 -0
  87. package/dist/db/apply-entity-event.d.ts +6 -6
  88. package/dist/db/apply-entity-event.js +55 -55
  89. package/dist/db/blind-index-cleanup.js +7 -7
  90. package/dist/db/closed-connection.d.ts +9 -0
  91. package/dist/db/closed-connection.js +95 -0
  92. package/dist/db/collect-table-metas.js +10 -11
  93. package/dist/db/compound-types.d.ts +1 -1
  94. package/dist/db/compound-types.js +12 -12
  95. package/dist/db/cursor.js +3 -4
  96. package/dist/db/dialect.d.ts +2 -2
  97. package/dist/db/dialect.js +3 -3
  98. package/dist/db/distinct-on.d.ts +1 -1
  99. package/dist/db/driver-row-coercion.d.ts +2 -0
  100. package/dist/db/driver-row-coercion.js +186 -0
  101. package/dist/db/driver-value-preparation.d.ts +16 -0
  102. package/dist/db/driver-value-preparation.js +99 -0
  103. package/dist/db/eagerload.d.ts +3 -3
  104. package/dist/db/eagerload.js +21 -12
  105. package/dist/db/entity-field-encryption.d.ts +1 -0
  106. package/dist/db/entity-field-encryption.js +1 -1
  107. package/dist/db/entity-table-meta.d.ts +2 -2
  108. package/dist/db/entity-table-meta.js +105 -112
  109. package/dist/db/event-store-executor-context.d.ts +3 -2
  110. package/dist/db/event-store-executor-context.js +13 -13
  111. package/dist/db/event-store-executor-read-aggregate.d.ts +8 -0
  112. package/dist/db/event-store-executor-read-aggregate.js +112 -0
  113. package/dist/db/event-store-executor-read-filter.d.ts +31 -0
  114. package/dist/db/event-store-executor-read-filter.js +203 -0
  115. package/dist/db/event-store-executor-read-paging.d.ts +14 -0
  116. package/dist/db/event-store-executor-read-paging.js +99 -0
  117. package/dist/db/event-store-executor-read-rows.d.ts +6 -0
  118. package/dist/db/event-store-executor-read-rows.js +32 -0
  119. package/dist/db/event-store-executor-read-search.d.ts +28 -0
  120. package/dist/db/event-store-executor-read-search.js +221 -0
  121. package/dist/db/event-store-executor-read-sort.d.ts +41 -0
  122. package/dist/db/event-store-executor-read-sort.js +130 -0
  123. package/dist/db/event-store-executor-read-where.d.ts +24 -0
  124. package/dist/db/event-store-executor-read-where.js +81 -0
  125. package/dist/db/event-store-executor-read.d.ts +2 -9
  126. package/dist/db/event-store-executor-read.js +50 -704
  127. package/dist/db/event-store-executor-write-steps.d.ts +20 -0
  128. package/dist/db/event-store-executor-write-steps.js +100 -0
  129. package/dist/db/event-store-executor-write.js +401 -510
  130. package/dist/db/index.d.ts +6 -2
  131. package/dist/db/index.js +5 -1
  132. package/dist/db/list-aggregate-labels.d.ts +14 -0
  133. package/dist/db/list-aggregate-labels.js +26 -0
  134. package/dist/db/located-timestamp.d.ts +11 -11
  135. package/dist/db/located-timestamp.js +29 -30
  136. package/dist/db/migrate-generator.d.ts +1 -1
  137. package/dist/db/migrate-generator.js +97 -105
  138. package/dist/db/migrate-runner.js +29 -26
  139. package/dist/db/money.d.ts +6 -6
  140. package/dist/db/money.js +29 -27
  141. package/dist/db/queries/backfill-pii.js +5 -5
  142. package/dist/db/queries/ddl.js +1 -1
  143. package/dist/db/queries/es-ops.d.ts +1 -1
  144. package/dist/db/queries/es-ops.js +1 -1
  145. package/dist/db/queries/event-consumer.js +3 -3
  146. package/dist/db/queries/event-store.js +4 -2
  147. package/dist/db/queries/projection-rebuild.js +3 -3
  148. package/dist/db/queries/raw-sql.d.ts +2 -2
  149. package/dist/db/queries/raw-sql.js +2 -2
  150. package/dist/db/queries/shadow-swap.js +19 -19
  151. package/dist/db/query.d.ts +3 -16
  152. package/dist/db/query.js +8 -761
  153. package/dist/db/rebuild-marker.js +20 -21
  154. package/dist/db/reference-data.js +6 -1
  155. package/dist/db/replay-migration-sql.js +2 -2
  156. package/dist/db/schema-inspection.js +4 -5
  157. package/dist/db/table-builder.d.ts +1 -2
  158. package/dist/db/table-builder.js +137 -190
  159. package/dist/db/table-info.d.ts +10 -0
  160. package/dist/db/table-info.js +45 -0
  161. package/dist/db/temporal-value-guards.d.ts +6 -0
  162. package/dist/db/temporal-value-guards.js +15 -0
  163. package/dist/db/tenant-db-runner.d.ts +1 -0
  164. package/dist/db/tenant-db-runner.js +3 -0
  165. package/dist/db/tenant-db.d.ts +2 -2
  166. package/dist/db/tenant-db.js +48 -11
  167. package/dist/db/where-clause.d.ts +6 -0
  168. package/dist/db/where-clause.js +236 -0
  169. package/dist/derivatives/delete-stored-file.js +6 -2
  170. package/dist/derivatives/derivatives-context.js +1 -1
  171. package/dist/engine/boot-validator/access-roles.js +4 -3
  172. package/dist/engine/boot-validator/api-ext.js +14 -14
  173. package/dist/engine/boot-validator/boot-check.js +1 -1
  174. package/dist/engine/boot-validator/config-deps.js +7 -6
  175. package/dist/engine/boot-validator/detail-screens.js +4 -4
  176. package/dist/engine/boot-validator/entity-handler.d.ts +1 -0
  177. package/dist/engine/boot-validator/entity-handler.js +77 -61
  178. package/dist/engine/boot-validator/form-derivations.js +35 -1
  179. package/dist/engine/boot-validator/gdpr-storage.js +4 -4
  180. package/dist/engine/boot-validator/i18n-keys.js +6 -4
  181. package/dist/engine/boot-validator/index.d.ts +2 -2
  182. package/dist/engine/boot-validator/index.js +22 -20
  183. package/dist/engine/boot-validator/nav.js +5 -5
  184. package/dist/engine/boot-validator/ownership.js +12 -9
  185. package/dist/engine/boot-validator/parent-ref.js +1 -1
  186. package/dist/engine/boot-validator/pii-retention.js +258 -187
  187. package/dist/engine/boot-validator/projection-list-screens.js +7 -7
  188. package/dist/engine/boot-validator/query-refs.js +1 -1
  189. package/dist/engine/boot-validator/screens-actions.d.ts +1 -0
  190. package/dist/engine/boot-validator/screens-actions.js +23 -17
  191. package/dist/engine/boot-validator/screens-dashboard.js +2 -5
  192. package/dist/engine/boot-validator/screens-forms.js +12 -17
  193. package/dist/engine/boot-validator/screens-kind-config-edit.d.ts +3 -0
  194. package/dist/engine/boot-validator/screens-kind-config-edit.js +88 -0
  195. package/dist/engine/boot-validator/screens-kind-entity-edit.d.ts +3 -0
  196. package/dist/engine/boot-validator/screens-kind-entity-edit.js +55 -0
  197. package/dist/engine/boot-validator/screens-kind-entity-list.d.ts +3 -0
  198. package/dist/engine/boot-validator/screens-kind-entity-list.js +228 -0
  199. package/dist/engine/boot-validator/screens-kind-projection-detail.d.ts +3 -0
  200. package/dist/engine/boot-validator/screens-kind-projection-detail.js +235 -0
  201. package/dist/engine/boot-validator/screens-kind-projection-list.d.ts +3 -0
  202. package/dist/engine/boot-validator/screens-kind-projection-list.js +52 -0
  203. package/dist/engine/boot-validator/screens-kind-secret-mint.d.ts +3 -0
  204. package/dist/engine/boot-validator/screens-kind-secret-mint.js +28 -0
  205. package/dist/engine/boot-validator/screens-kind-shared.d.ts +37 -0
  206. package/dist/engine/boot-validator/screens-kind-shared.js +142 -0
  207. package/dist/engine/boot-validator/screens-lists.d.ts +2 -1
  208. package/dist/engine/boot-validator/screens-lists.js +26 -24
  209. package/dist/engine/boot-validator/screens.d.ts +0 -2
  210. package/dist/engine/boot-validator/screens.js +102 -869
  211. package/dist/engine/boot-validator/security-baseline.js +3 -2
  212. package/dist/engine/boot-validator/transfer-graph.js +2 -2
  213. package/dist/engine/boot-validator/workspaces.js +3 -3
  214. package/dist/engine/bridge-stub.js +3 -4
  215. package/dist/engine/build-app-schema.js +130 -170
  216. package/dist/engine/build-config-feature-schema.js +50 -50
  217. package/dist/engine/build-target.js +20 -23
  218. package/dist/engine/codemod/pipeline-codemod.js +20 -13
  219. package/dist/engine/config-helpers.js +4 -4
  220. package/dist/engine/constants.js +2 -2
  221. package/dist/engine/create-app.js +2 -2
  222. package/dist/engine/define-feature.js +21 -13
  223. package/dist/engine/define-handler.js +13 -11
  224. package/dist/engine/define-step.js +1 -1
  225. package/dist/engine/define-workflow.d.ts +5 -5
  226. package/dist/engine/define-workflow.js +6 -6
  227. package/dist/engine/entity-handlers.d.ts +13 -0
  228. package/dist/engine/entity-handlers.js +22 -34
  229. package/dist/engine/entity-list-runtime-options.d.ts +8 -0
  230. package/dist/engine/entity-list-runtime-options.js +18 -0
  231. package/dist/engine/event-def.js +2 -2
  232. package/dist/engine/extension-names.d.ts +16 -17
  233. package/dist/engine/extension-names.js +37 -38
  234. package/dist/engine/extensions/escape-hatch-usage.js +1 -1
  235. package/dist/engine/extensions/tenant-data.js +2 -2
  236. package/dist/engine/extensions/user-data.d.ts +23 -24
  237. package/dist/engine/factories.d.ts +14 -13
  238. package/dist/engine/factories.js +38 -37
  239. package/dist/engine/feature-ast/entity-field-types.js +1 -1
  240. package/dist/engine/feature-ast/extractors/handlers.js +2 -1
  241. package/dist/engine/feature-ast/extractors/hooks.js +1 -1
  242. package/dist/engine/feature-ast/extractors/round2.js +25 -6
  243. package/dist/engine/feature-ast/extractors/shared.d.ts +2 -3
  244. package/dist/engine/feature-ast/extractors/shared.js +2 -3
  245. package/dist/engine/feature-ast/parse.js +13 -13
  246. package/dist/engine/feature-ast/patch.js +4 -182
  247. package/dist/engine/feature-ast/pattern-change-schema.d.ts +3 -0
  248. package/dist/engine/feature-ast/pattern-change-schema.js +2 -1
  249. package/dist/engine/feature-ast/pattern-id-matchers.d.ts +7 -0
  250. package/dist/engine/feature-ast/pattern-id-matchers.js +210 -0
  251. package/dist/engine/feature-ast/patterns.d.ts +11 -2
  252. package/dist/engine/feature-ast/patterns.js +43 -51
  253. package/dist/engine/feature-ast/render.js +50 -89
  254. package/dist/engine/feature-config-events-jobs.js +15 -12
  255. package/dist/engine/feature-entity-handlers.js +12 -5
  256. package/dist/engine/feature-manifest.d.ts +9 -9
  257. package/dist/engine/feature-manifest.js +6 -7
  258. package/dist/engine/feature-ui-extensions.d.ts +3 -3
  259. package/dist/engine/feature-ui-extensions.js +30 -22
  260. package/dist/engine/field-access.js +5 -6
  261. package/dist/engine/form-derivations.js +18 -7
  262. package/dist/engine/handler-field-references.js +1 -1
  263. package/dist/engine/index.d.ts +1 -0
  264. package/dist/engine/index.js +4 -4
  265. package/dist/engine/object-form.d.ts +1 -0
  266. package/dist/engine/object-form.js +14 -4
  267. package/dist/engine/ownership.js +3 -3
  268. package/dist/engine/parse-ref-target.js +5 -7
  269. package/dist/engine/pattern-library/index.js +2 -2
  270. package/dist/engine/pattern-library/library.js +2 -2
  271. package/dist/engine/pattern-library/types.js +3 -3
  272. package/dist/engine/pipeline.js +2 -2
  273. package/dist/engine/project-app-schema-for-roles.d.ts +2 -0
  274. package/dist/engine/project-app-schema-for-roles.js +7 -5
  275. package/dist/engine/projection-helpers.js +9 -9
  276. package/dist/engine/qualified-name.js +2 -2
  277. package/dist/engine/registry-facade.js +1 -1
  278. package/dist/engine/registry-ingest.js +13 -13
  279. package/dist/engine/registry-state.js +17 -19
  280. package/dist/engine/registry-validate.js +12 -12
  281. package/dist/engine/run-in.js +2 -2
  282. package/dist/engine/run-pipeline.js +7 -0
  283. package/dist/engine/schema-builder.js +251 -233
  284. package/dist/engine/screen-filter-ops.js +17 -20
  285. package/dist/engine/screen-helpers.js +4 -3
  286. package/dist/engine/screen-kind.d.ts +7 -0
  287. package/dist/engine/screen-kind.js +1 -0
  288. package/dist/engine/state-machine.d.ts +12 -12
  289. package/dist/engine/state-machine.js +7 -7
  290. package/dist/engine/steps/_resolver-utils.js +4 -3
  291. package/dist/engine/steps/for-each.js +4 -4
  292. package/dist/engine/steps/unsafe-projection-delete.js +1 -1
  293. package/dist/engine/steps/unsafe-projection-upsert.js +4 -4
  294. package/dist/engine/steps/wait-for-event.js +4 -4
  295. package/dist/engine/steps/workflow-system-events.js +3 -3
  296. package/dist/engine/strip-disabled-features-from-app-schema.js +51 -14
  297. package/dist/engine/tier-resolver-extension.d.ts +29 -27
  298. package/dist/engine/tier-resolver-extension.js +26 -26
  299. package/dist/engine/transfer-adjacency.js +2 -2
  300. package/dist/engine/validate-projection-allowlist.js +9 -28
  301. package/dist/engine/walk-step-instances.d.ts +2 -0
  302. package/dist/engine/walk-step-instances.js +38 -0
  303. package/dist/engine/where-rule-lint.js +1 -1
  304. package/dist/engine/write-handler-qn-extract.js +1 -1
  305. package/dist/entrypoint/index.d.ts +4 -1
  306. package/dist/entrypoint/index.js +10 -4
  307. package/dist/env/index.d.ts +7 -6
  308. package/dist/env/index.js +10 -9
  309. package/dist/errors/docs-url.d.ts +1 -1
  310. package/dist/errors/docs-url.js +22 -2
  311. package/dist/errors/transition-details.js +7 -7
  312. package/dist/errors/write-error-info.d.ts +6 -6
  313. package/dist/errors/write-error-info.js +6 -6
  314. package/dist/es-ops/context.d.ts +9 -9
  315. package/dist/es-ops/context.js +29 -29
  316. package/dist/es-ops/index.js +7 -9
  317. package/dist/es-ops/operations-schema.js +4 -10
  318. package/dist/es-ops/runner.d.ts +11 -11
  319. package/dist/es-ops/runner.js +49 -53
  320. package/dist/es-ops/types.d.ts +55 -55
  321. package/dist/es-ops/types.js +11 -11
  322. package/dist/event-store/admin-api.js +7 -8
  323. package/dist/event-store/errors.js +1 -1
  324. package/dist/event-store/event-store.js +12 -6
  325. package/dist/event-store/rebuild-dead-letter.js +1 -1
  326. package/dist/files/bind-pending-file-refs.js +3 -3
  327. package/dist/files/create-file-ref-from-bytes.js +4 -1
  328. package/dist/files/feature.js +9 -10
  329. package/dist/files/file-handle.js +7 -9
  330. package/dist/files/file-ref-entity.js +15 -15
  331. package/dist/files/file-ref-table.js +7 -8
  332. package/dist/files/file-routes.d.ts +7 -0
  333. package/dist/files/file-routes.js +22 -13
  334. package/dist/files/in-memory-provider.js +8 -9
  335. package/dist/files/local-provider.js +18 -25
  336. package/dist/files/provider-resolver.d.ts +7 -7
  337. package/dist/files/resolve-content-type.js +43 -0
  338. package/dist/files/storage-tracking.js +2 -2
  339. package/dist/files/upload-ledger.js +7 -0
  340. package/dist/files/zip-stream.d.ts +12 -19
  341. package/dist/files/zip-stream.js +44 -63
  342. package/dist/i18n/required-surface-keys.d.ts +1 -1
  343. package/dist/i18n/required-surface-keys.js +188 -212
  344. package/dist/internal/stack-support.d.ts +2 -1
  345. package/dist/internal/stack-support.js +1 -1
  346. package/dist/jobs/job-runner-context.d.ts +32 -0
  347. package/dist/jobs/job-runner-context.js +305 -0
  348. package/dist/jobs/job-runner-execution.d.ts +7 -0
  349. package/dist/jobs/job-runner-execution.js +160 -0
  350. package/dist/jobs/job-runner-gates.d.ts +19 -0
  351. package/dist/jobs/job-runner-gates.js +198 -0
  352. package/dist/jobs/job-runner-helpers.d.ts +17 -3
  353. package/dist/jobs/job-runner-helpers.js +69 -4
  354. package/dist/jobs/job-runner-schedulers.d.ts +1 -1
  355. package/dist/jobs/job-runner-schedulers.js +3 -4
  356. package/dist/jobs/job-runner-scope.d.ts +44 -0
  357. package/dist/jobs/job-runner-scope.js +102 -0
  358. package/dist/jobs/job-runner-types.d.ts +12 -0
  359. package/dist/jobs/job-runner.d.ts +0 -1
  360. package/dist/jobs/job-runner.js +57 -669
  361. package/dist/lifecycle/lifecycle.js +3 -5
  362. package/dist/lifecycle/signal-handlers.js +5 -4
  363. package/dist/logging/utils.d.ts +2 -1
  364. package/dist/logging/utils.js +38 -10
  365. package/dist/migrations/index.js +1 -1
  366. package/dist/migrations/kumiko-drift.js +14 -13
  367. package/dist/migrations/pending-rebuilds.d.ts +21 -20
  368. package/dist/migrations/pending-rebuilds.js +27 -27
  369. package/dist/migrations/projection-table-index.d.ts +3 -3
  370. package/dist/migrations/projection-table-index.js +8 -8
  371. package/dist/observability/escape-hatch-report.d.ts +3 -0
  372. package/dist/observability/escape-hatch-report.js +12 -1
  373. package/dist/observability/metric-validator.js +1 -1
  374. package/dist/observability/metrics-handle.js +3 -3
  375. package/dist/pipeline/append-event-core.js +11 -2
  376. package/dist/pipeline/cascade-handler.d.ts +1 -1
  377. package/dist/pipeline/cascade-handler.js +165 -138
  378. package/dist/pipeline/dispatch-batch.js +2 -2
  379. package/dist/pipeline/dispatch-query.js +20 -13
  380. package/dist/pipeline/dispatch-shared.d.ts +14 -3
  381. package/dist/pipeline/dispatch-shared.js +67 -721
  382. package/dist/pipeline/dispatch-stream.js +1 -1
  383. package/dist/pipeline/dispatch-write.js +297 -190
  384. package/dist/pipeline/dispatcher-utils.d.ts +1 -1
  385. package/dist/pipeline/dispatcher-utils.js +1 -1
  386. package/dist/pipeline/event-consumer-state.js +2 -2
  387. package/dist/pipeline/event-dispatcher-admin.js +2 -1
  388. package/dist/pipeline/event-dispatcher-delivery.js +4 -6
  389. package/dist/pipeline/event-dispatcher.js +17 -28
  390. package/dist/pipeline/handler-context-auth.d.ts +9 -0
  391. package/dist/pipeline/handler-context-auth.js +54 -0
  392. package/dist/pipeline/handler-context-config.d.ts +14 -0
  393. package/dist/pipeline/handler-context-config.js +113 -0
  394. package/dist/pipeline/handler-context-db.d.ts +24 -0
  395. package/dist/pipeline/handler-context-db.js +149 -0
  396. package/dist/pipeline/handler-context-dispatch.d.ts +21 -0
  397. package/dist/pipeline/handler-context-dispatch.js +89 -0
  398. package/dist/pipeline/handler-context-events.d.ts +43 -0
  399. package/dist/pipeline/handler-context-events.js +237 -0
  400. package/dist/pipeline/handler-context-notify.d.ts +12 -0
  401. package/dist/pipeline/handler-context-notify.js +55 -0
  402. package/dist/pipeline/handler-context-observability.d.ts +7 -0
  403. package/dist/pipeline/handler-context-observability.js +21 -0
  404. package/dist/pipeline/handler-context-scope.d.ts +48 -0
  405. package/dist/pipeline/handler-context-scope.js +88 -0
  406. package/dist/pipeline/irreversible-operation-gate.js +1 -1
  407. package/dist/pipeline/lifecycle-pipeline.js +9 -9
  408. package/dist/pipeline/member-read-only-transaction.js +1 -3
  409. package/dist/pipeline/msp-rebuild.js +1 -1
  410. package/dist/pipeline/notify-scope.js +12 -1
  411. package/dist/pipeline/pre-handler-gates.js +20 -4
  412. package/dist/pipeline/pre-transaction-reservations.d.ts +1 -0
  413. package/dist/pipeline/pre-transaction-reservations.js +9 -3
  414. package/dist/pipeline/projection-rebuild.js +15 -13
  415. package/dist/pipeline/system-hooks.js +39 -41
  416. package/dist/pipeline/system-identity-switch.js +1 -3
  417. package/dist/pipeline/tenant-timezone-cache.js +1 -1
  418. package/dist/random/generate.d.ts +29 -29
  419. package/dist/random/generate.js +30 -30
  420. package/dist/random/words.js +20 -20
  421. package/dist/rate-limit/middleware.js +9 -5
  422. package/dist/redis/client.js +2 -2
  423. package/dist/redis/pubsub-signal.js +14 -17
  424. package/dist/schema-cli/apply-command.d.ts +2 -0
  425. package/dist/schema-cli/apply-command.js +97 -0
  426. package/dist/schema-cli/baseline-command.d.ts +2 -0
  427. package/dist/schema-cli/baseline-command.js +33 -0
  428. package/dist/schema-cli/generate-command.d.ts +2 -0
  429. package/dist/schema-cli/generate-command.js +90 -0
  430. package/dist/schema-cli/shared.d.ts +39 -0
  431. package/dist/schema-cli/shared.js +95 -0
  432. package/dist/schema-cli/status-command.d.ts +2 -0
  433. package/dist/schema-cli/status-command.js +73 -0
  434. package/dist/schema-cli/validate-boot-input.d.ts +5 -0
  435. package/dist/schema-cli/validate-boot-input.js +130 -0
  436. package/dist/schema-cli/validate-command.d.ts +2 -0
  437. package/dist/schema-cli/validate-command.js +130 -0
  438. package/dist/schema-cli.d.ts +2 -18
  439. package/dist/schema-cli.js +47 -510
  440. package/dist/search/index.js +4 -4
  441. package/dist/search/meilisearch-adapter.js +2 -1
  442. package/dist/search/purge-subject.js +4 -5
  443. package/dist/search/reindex-entity.js +6 -9
  444. package/dist/time/boot-tz-warning.d.ts +2 -2
  445. package/dist/time/boot-tz-warning.js +8 -8
  446. package/dist/time/iana.js +10 -10
  447. package/dist/time/index.js +9 -9
  448. package/dist/time/legacy-date.d.ts +2 -2
  449. package/dist/time/legacy-date.js +6 -8
  450. package/dist/time/polyfill.js +1 -1
  451. package/dist/time/tz-context.d.ts +1 -1
  452. package/dist/time/tz-context.js +1 -1
  453. package/dist/ui-types/app-schema.js +7 -7
  454. package/dist/ui-types/list-row-meta.js +4 -4
  455. package/dist/upgrade-cli.js +159 -130
  456. package/dist/utils/compare.js +1 -1
  457. package/package.json +5 -5
  458. package/src/changes.json +358 -0
  459. package/src/scripts/codemod/crypto-shredding-testing-move.ts +2 -2
  460. package/src/scripts/codemod/feature-layout.ts +10 -2
  461. package/src/scripts/codemod/migrate-cross-tenant.ts +1 -1
  462. package/src/scripts/codemod/migrate-open-to-all.ts +1 -1
  463. package/src/scripts/codemod/object-form.ts +41 -4
  464. package/src/scripts/codemod/package-merge.ts +58 -8
  465. package/src/scripts/codemod/pii-personal-migration.ts +192 -153
  466. package/src/scripts/codemod/testing-injection.ts +122 -42
  467. package/src/scripts/codemod/testing-move.ts +29 -7
  468. package/src/scripts/codemod/unprocessable-error-details-reason.ts +2 -2
@@ -1,23 +1,23 @@
1
- // Random-Generator für human-readable Resource-Identifier:
2
- // - Tenant-Keys ("happy-cloud", "swift-river-k8x")
3
- // - Webhook-Subscriber-Slugs ("bold-falcon-receiver" mit custom nouns)
4
- // - API-Key-Display-Names (3 Vorschläge anbieten beim Create)
5
- // - Tenant-Subdomain-Vorschläge im Subdomain-Setup-Screen
6
- // - Test-Fixtures mit lesbaren Identifiern
1
+ // Random generator for human-readable resource identifiers:
2
+ // - Tenant keys ("happy-cloud", "swift-river-k8x")
3
+ // - Webhook subscriber slugs ("bold-falcon-receiver" with custom nouns)
4
+ // - API key display names (offer 3 suggestions on creation)
5
+ // - Tenant subdomain suggestions in the subdomain setup screen
6
+ // - Test fixtures with readable identifiers
7
7
  //
8
- // NICHT für Security-Token (CSRF, Session, API-Keys, OAuth-State,
9
- // Reset-Token). Math.random() ist nicht kryptografisch unvorhersagbar.
10
- // Authority-binding für Tenant-Keys läuft über JWT + DB-Lookup, nicht
11
- // über Slug-Geheimhaltung — der Slug darf erratbar sein.
8
+ // NOT for security tokens (CSRF, session, API keys, OAuth state,
9
+ // reset tokens). Math.random() is not cryptographically unpredictable.
10
+ // Authority binding for tenant keys uses JWT + DB lookup, not slug secrecy;
11
+ // the slug may be guessable.
12
12
  //
13
- // Universal-safe: Math.random() läuft in Bun, Node, Metro/RN, Expo-Web.
14
- // Keine node:crypto-Imports.
13
+ // Safe across environments: Math.random() runs in Bun, Node, Metro/RN, and
14
+ // Expo Web. No node:crypto imports.
15
15
  import { ADJECTIVES, NOUNS } from "./words.js";
16
- // Alphabet ohne handgetippt verwechselbare Zeichen:
17
- // - keine 0/O (Null vs Großbuchstabe O)
18
- // - keine 1/l/I (Eins vs Kleinbuchstabe L vs Großbuchstabe I)
19
- // Resultat: 32 Zeichen, sicher beim Telefon-Buchstabieren UND beim
20
- // Mailtext-Copy-Paste in fremde Schriftarten.
16
+ // Alphabet without characters that are easily confused when entered manually:
17
+ // - no 0/O (zero versus uppercase O)
18
+ // - no 1/l/I (one versus lowercase L versus uppercase I)
19
+ // Result: 32 characters, safe for spelling over the phone AND for copying
20
+ // from email text in unfamiliar fonts.
21
21
  const NO_CONFUSABLE_ALPHABET = "23456789abcdefghjkmnpqrstuvwxyz";
22
22
  const NO_CONFUSABLE_CHARS = Object.freeze(NO_CONFUSABLE_ALPHABET.split(""));
23
23
  function pickRandom(arr) {
@@ -25,15 +25,15 @@ function pickRandom(arr) {
25
25
  throw new Error("pickRandom: cannot pick from empty array");
26
26
  }
27
27
  const value = arr[Math.floor(Math.random() * arr.length)];
28
- // Length-Check oben garantiert dass index in-range ist.
28
+ // The length check above guarantees that the index is in range.
29
29
  if (value === undefined) {
30
30
  throw new Error("pickRandom: indexed value undefined (sparse array?)");
31
31
  }
32
32
  return value;
33
33
  }
34
- /** Sync Generator: produziert "happy-cloud" oder "happy-cloud-k8x"
35
- * (mit suffix). Kein Conflict-Check — Caller verantwortlich für
36
- * Uniqueness, oder generateUniqueName() für die async Variante. */
34
+ /** Sync generator: produces "happy-cloud" or "happy-cloud-k8x"
35
+ * (with suffix). No conflict check — the caller is responsible for
36
+ * uniqueness, or can use generateUniqueName() for the async variant. */
37
37
  export function generateAdjNounName(options = {}) {
38
38
  const sep = options.separator ?? "-";
39
39
  const adjs = options.adjectives ?? ADJECTIVES;
@@ -44,9 +44,9 @@ export function generateAdjNounName(options = {}) {
44
44
  }
45
45
  return name;
46
46
  }
47
- /** Random-String aus dem no-confusable-Alphabet. Für Suffix-bei-Kollision
48
- * oder als standalone short-IDs (z.B. Webhook-Verifizierungs-Codes
49
- * zum Vorlesen am Telefon). length ≥ 1. */
47
+ /** Random string from the no-confusable alphabet. For suffixes after
48
+ * collisions or as standalone short IDs (e.g. webhook verification codes
49
+ * to be read aloud over the phone). length ≥ 1. */
50
50
  export function generateNoConfusableId(length) {
51
51
  if (length < 1) {
52
52
  throw new Error(`generateNoConfusableId: length must be ≥ 1 (got ${length})`);
@@ -57,17 +57,17 @@ export function generateNoConfusableId(length) {
57
57
  }
58
58
  return id;
59
59
  }
60
- /** Generiert einen unique Adj-Noun-Slug indem es bis zu maxCleanAttempts
61
- * saubere Wortpaare versucht, danach mit random Suffix bis maxTotal-
62
- * Attempts. Wirft wenn auch das nicht hilft (= caller hat ein Bug
63
- * mit isAvailable, oder die Wortliste ist defekt).
60
+ /** Generates a unique Adj-Noun slug by trying up to maxCleanAttempts
61
+ * clean word pairs, then using a random suffix up to maxTotal-
62
+ * Attempts. Throws if that also fails (= the caller has a bug
63
+ * in isAvailable, or the word list is defective).
64
64
  *
65
- * Beispiel:
65
+ * Example:
66
66
  * const slug = await generateUniqueName({
67
67
  * isAvailable: async (s) =>
68
68
  * !(await db.select().from(tenants).where(eq(tenants.tenantKey, s)).then(r => r.length > 0)),
69
69
  * });
70
- * // → "happy-cloud" oder "happy-cloud-k8x" bei Kollision
70
+ * // → "happy-cloud" or "happy-cloud-k8x" on collision
71
71
  */
72
72
  export async function generateUniqueName(options) {
73
73
  const maxClean = options.maxCleanAttempts ?? 3;
@@ -1,26 +1,26 @@
1
- // Wortlisten für human-readable Resource-Slugs (Tenant-Keys, Webhook-
2
- // Subscribers, API-Key-Display-Names, Test-Fixtures). Heroku/Docker-
3
- // Style — adjective + noun produzieren aussprechbare, merkbare,
4
- // PII-freie Identifier.
1
+ // Word lists for human-readable resource slugs (tenant keys, webhook-
2
+ // subscribers, API-key display names, test fixtures). Heroku/Docker-
3
+ // style — adjective + noun produce pronounceable, memorable,
4
+ // PII-free identifiers.
5
5
  //
6
- // Auswahl-Kriterien:
7
- // - Emotional-neutral und professionell (kein "fluffy", "sneaky")
8
- // - Keine Tiernamen (Heroku-Cliché)
9
- // - Keine Personennamen (cultural appropriation, prominenten-collision)
10
- // - Keine Themen-Cluster (kein Wetter-only, kein Geographie-only)
11
- // - Lowercase, ASCII-only, keine Bindestriche im Wort selbst
12
- // - 3-10 Buchstaben pro Wort (kompakter Slug)
13
- // - Aussprechbar in Deutsch UND Englisch (User-Telefon-Support)
14
- // - Keine Wörter mit ambiguer Bedeutung in Englisch+Deutsch
6
+ // Selection criteria:
7
+ // - Emotionally neutral and professional (no "fluffy", "sneaky")
8
+ // - No animal names (Heroku cliché)
9
+ // - No personal names (cultural appropriation, celebrity collisions)
10
+ // - No thematic clusters (neither weather-only nor geography-only)
11
+ // - Lowercase, ASCII-only, with no hyphens within a word
12
+ // - 3–10 letters per word (compact slug)
13
+ // - Pronounceable in German AND English (user phone support)
14
+ // - No words with ambiguous meanings in English and German
15
15
  //
16
- // 191 × 173 = 33.043 saubere Kombinationen. Birthday-Bound: ab ~180
17
- // Tenants wird kumulativ die erste Kollision wahrscheinlich (~50%);
18
- // pro Einzel-Draw bleibt p(Kollision) < 1% (vgl. generate.ts). Bei
19
- // Kollision greift der Suffix-Pfad in generateUniqueName.
16
+ // 191 × 173 = 33,043 clean combinations. Birthday bound: from ~180
17
+ // tenants onward, the first collision becomes likely cumulatively (~50%);
18
+ // for an individual draw, p(collision) remains < 1% (see generate.ts). On
19
+ // collision, the suffix path in generateUniqueName is used.
20
20
  //
21
- // Erweiterung: weitere Wörter unten anhängen reicht (sortiert ist
22
- // hilfreich für Reviews aber nicht erforderlich). Caller können auch
23
- // eigene Listen via generateUniqueName({ adjectives, nouns }) reichen.
21
+ // Extension: appending more words below is sufficient (sorting helps with
22
+ // reviews but is not required). Callers can also provide custom lists via
23
+ // generateUniqueName({ adjectives, nouns }).
24
24
  export const ADJECTIVES = Object.freeze([
25
25
  "agile",
26
26
  "amber",
@@ -108,21 +108,25 @@ export function createFailOpenReporter(label, meter, now = Date.now) {
108
108
  lastWarnAt = nowMs;
109
109
  logger.warn("rate limiter unavailable, failing open", {
110
110
  label,
111
- // ioredis errors can carry the command args, i.e. the bucket key with IP/target.
111
+ // ioredis errors can carry the command args, i.e. the bucket key. scrubLogText masks an e-mail
112
+ // address in it; the client IP stays in the line as an operational datum.
112
113
  error: scrubLogText(err instanceof Error ? err.message : String(err)),
113
114
  });
114
115
  };
115
116
  }
117
+ const failClosedLogger = createFallbackLogger("rate-limit");
116
118
  function defaultOnFailClosed(label) {
117
119
  return (err) => {
118
120
  // Loud by default — fail-closed for an unknown reason is an ops
119
121
  // signal, not something to swallow silently. Production deploys
120
122
  // override this with a structured logger; the default keeps the
121
123
  // noise visible if no logger is wired.
122
- // biome-ignore lint/suspicious/noConsole: ops-visible fallback when no logger is wired
123
- console.error(`[rate-limit ${label}] fail-closed (refusing request):`,
124
- // ioredis errors can carry the command args, i.e. the bucket key with IP/target.
125
- err instanceof Error ? err.message : String(err));
124
+ failClosedLogger.error("fail-closed (refusing request)", {
125
+ label,
126
+ // ioredis errors can carry the command args, i.e. the bucket key. scrubLogText masks an e-mail
127
+ // address in it; the client IP stays in the line as an operational datum.
128
+ error: scrubLogText(err instanceof Error ? err.message : String(err)),
129
+ });
126
130
  };
127
131
  }
128
132
  function setRateLimitHeaders(c, decision) {
@@ -8,8 +8,8 @@
8
8
  // production defaults stay consistent and env-var wiring lives in one
9
9
  // place. Tests / one-off scripts can still `new Redis()` directly.
10
10
  //
11
- // Split out of index.ts (fw#2625) so pubsub-signal.ts can import this
12
- // without a same-directory cycle back through the barrel.
11
+ // Split out of index.ts so pubsub-signal.ts can import this without a
12
+ // same-directory cycle back through the barrel.
13
13
  import { Redis } from "ioredis";
14
14
  import { scrubLogText } from "../logging/scrub.js";
15
15
  import { readPositiveIntEnv } from "../utils/env-parse.js";
@@ -1,18 +1,19 @@
1
+ import { createFallbackLogger } from "../logging/utils.js";
1
2
  import { createRedisClient } from "./client.js";
2
3
  // Redis emits 'error' on connection blips (reconnects transparently); an
3
- // unhandled listener crashes the process (fw#1805 — job-runner hit the same
4
- // class with BullMQ's internal client).
5
- function logConnectionError(label, role) {
4
+ // unhandled listener crashes the process — job-runner hit the same
5
+ // class with BullMQ's internal client.
6
+ function logConnectionError(log, role) {
6
7
  return (err) => {
7
- // biome-ignore lint/suspicious/noConsole: ops-visible fallback — no logger is threaded through PubSubSignalOptions.
8
- console.error(`[kumiko:${label}] ${role} connection error:`, err.message);
8
+ log.error(`${role} connection error`, { error: err.message });
9
9
  };
10
10
  }
11
11
  export function createRedisPubSubSignal(opts) {
12
+ const log = createFallbackLogger(`kumiko:${opts.label}`);
12
13
  const publisher = createRedisClient(opts.redisUrl, opts.clientOptions);
13
14
  const subscriber = createRedisClient(opts.redisUrl, opts.clientOptions);
14
- publisher.on("error", logConnectionError(opts.label, "publisher"));
15
- subscriber.on("error", logConnectionError(opts.label, "subscriber"));
15
+ publisher.on("error", logConnectionError(log, "publisher"));
16
+ subscriber.on("error", logConnectionError(log, "subscriber"));
16
17
  const listeners = new Set();
17
18
  const reconnectListeners = new Set();
18
19
  // Sole purpose: stop close() from racing the initial psubscribe (see
@@ -26,8 +27,7 @@ export function createRedisPubSubSignal(opts) {
26
27
  payload = JSON.parse(message);
27
28
  }
28
29
  catch (err) {
29
- // biome-ignore lint/suspicious/noConsole: ops-visible fallback, see logConnectionError above.
30
- console.error(`[kumiko:${opts.label}] dropping unparseable message on "${channel}":`, err);
30
+ log.error(`dropping unparseable message on "${channel}"`, { err });
31
31
  // skip: unparseable payload already logged above, nothing to deliver
32
32
  return;
33
33
  }
@@ -37,14 +37,14 @@ export function createRedisPubSubSignal(opts) {
37
37
  // ponytail: psubscribe on one pattern rather than per-channel
38
38
  // subscribe/unsubscribe — every instance receives every message that
39
39
  // matches, filtered by the caller. That's strictly no worse than the
40
- // pre-fw#2625 per-instance-cursor model (which already read every event
40
+ // per-instance-cursor model (which already read every event
41
41
  // from Postgres in every process) and sidesteps the subscribe-ack/
42
42
  // first-publish race a per-channel scheme would have. Revisit with
43
43
  // per-channel subscribe only if fanout volume becomes the bottleneck.
44
44
  //
45
45
  // Awaited by close() below — quitting while this is still in flight (a
46
46
  // shutdown landing immediately after construction, e.g. a test closing
47
- // right after building the signal) makes ioredis rejects the queued
47
+ // right after building the signal) makes ioredis reject the queued
48
48
  // psubscribe with "Connection is closed" instead of it completing
49
49
  // cleanly first. Not caught here or logged when a close is already
50
50
  // underway (`closed`), so an intentional shutdown never surfaces this
@@ -54,8 +54,7 @@ export function createRedisPubSubSignal(opts) {
54
54
  // this rejection is the expected side effect, not an ops-visible error.
55
55
  if (closed)
56
56
  return;
57
- // biome-ignore lint/suspicious/noConsole: ops-visible fallback, see logConnectionError above.
58
- console.error(`[kumiko:${opts.label}] psubscribe failed:`, err);
57
+ log.error("psubscribe failed", { err });
59
58
  });
60
59
  // ioredis re-sends the psubscribe itself (autoResubscribe) but emits no ack
61
60
  // event; repeating it here queues behind that one on the same connection,
@@ -77,8 +76,7 @@ export function createRedisPubSubSignal(opts) {
77
76
  // skip: a close() already underway intentionally quit the connection
78
77
  if (closed)
79
78
  return;
80
- // biome-ignore lint/suspicious/noConsole: ops-visible fallback, see logConnectionError above.
81
- console.error(`[kumiko:${opts.label}] resubscribe failed:`, err);
79
+ log.error("resubscribe failed", { err });
82
80
  });
83
81
  });
84
82
  return {
@@ -88,8 +86,7 @@ export function createRedisPubSubSignal(opts) {
88
86
  // connections — this rejection is the expected side effect.
89
87
  if (closed)
90
88
  return;
91
- // biome-ignore lint/suspicious/noConsole: ops-visible fallback, see logConnectionError above.
92
- console.error(`[kumiko:${opts.label}] publish failed:`, err);
89
+ log.error("publish failed", { err });
93
90
  });
94
91
  },
95
92
  async publishConfirmed(channel, payload) {
@@ -0,0 +1,2 @@
1
+ import { type SchemaCliContext } from "./shared.ts";
2
+ export declare function runApplyCommand(ctx: SchemaCliContext): Promise<number>;
@@ -0,0 +1,97 @@
1
+ import { configureBlindIndexKey, configurePiiSubjectKms, resolveKmsWiringAsync, } from "../crypto/index.js";
2
+ import { createDbConnection, runMigrationsFromDir } from "../db/index.js";
3
+ import { createRegistry } from "../engine/registry.js";
4
+ import { createEventsTable } from "../event-store/index.js";
5
+ import { queueRebuildsFromMarkers, reportPendingRebuildRun, runPendingRebuilds, } from "../migrations/index.js";
6
+ import { createEventConsumerStateTable, createProjectionStateTable } from "../pipeline/index.js";
7
+ import { migrationsDirExistsOrReport, readDatabaseUrlOrReport, } from "./shared.js";
8
+ async function ensureFrameworkInfrastructureTables(db) {
9
+ // Framework infrastructure tables (event store + pipeline state) are not captured by
10
+ // `generate`, which handles only entity read tables. Existing databases have them
11
+ // from the legacy-drizzle foundation; without them, a greenfield database (the
12
+ // first app without a cutover) would lack kumiko_events and runProdApp would fail
13
+ // during boot. All three are idempotent (tableExists-gated), so they are no-ops for
14
+ // existing databases.
15
+ await createEventsTable(db);
16
+ await createEventConsumerStateTable(db);
17
+ await createProjectionStateTable(db);
18
+ }
19
+ function reportAppliedMigrations(out, result) {
20
+ out.log("");
21
+ if (result.applied.length === 0) {
22
+ out.log(` ✓ All ${result.skipped.length} migrations already applied.`);
23
+ }
24
+ else {
25
+ out.log(` ✓ Applied ${result.applied.length}:`);
26
+ for (const id of result.applied)
27
+ out.log(` + ${id}`);
28
+ if (result.skipped.length > 0)
29
+ out.log(` (${result.skipped.length} already applied)`);
30
+ }
31
+ out.log("");
32
+ }
33
+ function configureRebuildKms(wiring) {
34
+ if ("kms" in wiring) {
35
+ configurePiiSubjectKms(wiring.kms);
36
+ configureBlindIndexKey(wiring.blindIndexKey);
37
+ }
38
+ }
39
+ async function rebuildQueuedProjections(db, ctx, features, appliedIds) {
40
+ const thisRunTables = await queueRebuildsFromMarkers(db, {
41
+ migrationsDir: ctx.migrationsDir,
42
+ appliedIds,
43
+ });
44
+ const registry = createRegistry(features);
45
+ const rebuildRun = await runPendingRebuilds(db, registry, { thisRunTables });
46
+ reportPendingRebuildRun(rebuildRun, ctx.out.log);
47
+ }
48
+ export async function runApplyCommand(ctx) {
49
+ const { out, options, migrationsDir } = ctx;
50
+ const dbUrl = readDatabaseUrlOrReport(out);
51
+ if (dbUrl === undefined)
52
+ return 1;
53
+ if (!migrationsDirExistsOrReport(ctx))
54
+ return 1;
55
+ const { db, close } = createDbConnection({ url: dbUrl });
56
+ let wiring;
57
+ try {
58
+ const result = await runMigrationsFromDir(db, migrationsDir);
59
+ await ensureFrameworkInfrastructureTables(db);
60
+ reportAppliedMigrations(out, result);
61
+ // Projection-rebuild for tables a freshly applied migration changed
62
+ // (marker NNNN_<name>.rebuild.json from `generate`). Needs the composed
63
+ // feature set → only when the caller passed `features` (the app bin);
64
+ // the dev CLI omits it and applies migrations only. Runs unconditionally
65
+ // (not gated on result.applied.length > 0): a persistent queue
66
+ // (kumiko_pending_rebuilds) survives a failed/crashed rebuild from an
67
+ // earlier apply, so a later apply with zero new migrations still must
68
+ // retry it — otherwise a failed rebuild is silently never retried,
69
+ // since the migration itself is already tracked applied.
70
+ if (options.features) {
71
+ // A rebuild replays applyEntityEvent, which computes bidx columns from
72
+ // configureBlindIndexKey/configurePiiSubjectKms — both boot-injected by
73
+ // run{Prod,Dev}App but never by this standalone CLI (the migrate-db
74
+ // initContainer). Without wiring them here, a rebuild would silently
75
+ // null out bidx columns and, for ciphertext, throw only once it hits
76
+ // the row.
77
+ wiring = await resolveKmsWiringAsync(process.env, {
78
+ logPrefix: "[kumiko schema apply]",
79
+ log: out.log,
80
+ slots: options.kmsSlots,
81
+ });
82
+ configureRebuildKms(wiring);
83
+ await rebuildQueuedProjections(db, ctx, options.features, result.applied);
84
+ }
85
+ return 0;
86
+ }
87
+ catch (e) {
88
+ out.err("");
89
+ out.err(` ✗ ${e instanceof Error ? (e.stack ?? e.message) : String(e)}`);
90
+ out.err("");
91
+ return 1;
92
+ }
93
+ finally {
94
+ await wiring?.close();
95
+ await close();
96
+ }
97
+ }
@@ -0,0 +1,2 @@
1
+ import { type SchemaCliContext } from "./shared.ts";
2
+ export declare function runBaselineCommand(ctx: SchemaCliContext): Promise<number>;
@@ -0,0 +1,33 @@
1
+ import { baselineMigrations, createDbConnection, loadMigrationsFromDir } from "../db/index.js";
2
+ import { describeThrown, migrationsDirExistsOrReport, readDatabaseUrlOrReport, } from "./shared.js";
3
+ export async function runBaselineCommand(ctx) {
4
+ const { out, migrationsDir } = ctx;
5
+ // Adopt an existing DB: mark all checked-in migrations as applied WITHOUT
6
+ // running their SQL (prod tables already exist — cutover from the legacy
7
+ // drizzle system). Afterwards the boot-gate is drift-free.
8
+ const dbUrl = readDatabaseUrlOrReport(out);
9
+ if (dbUrl === undefined)
10
+ return 1;
11
+ if (!migrationsDirExistsOrReport(ctx))
12
+ return 1;
13
+ const { db, close } = createDbConnection({ url: dbUrl });
14
+ try {
15
+ const result = await baselineMigrations(db, loadMigrationsFromDir(migrationsDir));
16
+ out.log("");
17
+ out.log(` ✓ Marked ${result.marked.length} migration(s) as applied (no SQL run):`);
18
+ for (const id of result.marked)
19
+ out.log(` + ${id}`);
20
+ if (result.alreadyTracked.length > 0) {
21
+ out.log(` (${result.alreadyTracked.length} already tracked)`);
22
+ }
23
+ out.log("");
24
+ return 0;
25
+ }
26
+ catch (e) {
27
+ out.err(` ✗ ${describeThrown(e)}`);
28
+ return 1;
29
+ }
30
+ finally {
31
+ await close();
32
+ }
33
+ }
@@ -0,0 +1,2 @@
1
+ import { type SchemaCliContext } from "./shared.ts";
2
+ export declare function runGenerateCommand(ctx: SchemaCliContext): Promise<number>;
@@ -0,0 +1,90 @@
1
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { assertValidMigrationName, generateMigration, readMigrationSqlTexts, rebuildTablesFromDiff, writeRebuildMarker, writeSnapshotJson, } from "../db/index.js";
4
+ import { describeThrown, loadPreviousSnapshot, loadSchemaModOrReport, nextSequenceNumber, SNAPSHOT_FILENAME, } from "./shared.js";
5
+ const GENERATE_USAGE = " Usage: schema generate <name>";
6
+ function parseGenerateName(ctx) {
7
+ const { argv, out } = ctx;
8
+ const name = argv[1];
9
+ if (name === "--help" || name === "-h") {
10
+ out.log(GENERATE_USAGE);
11
+ return { kind: "exit", code: 0 };
12
+ }
13
+ if (!name) {
14
+ out.err(GENERATE_USAGE);
15
+ return { kind: "exit", code: 1 };
16
+ }
17
+ // name lands unescaped in `${seq}_${name}.sql` (generateMigration) —
18
+ // shared allowlist blocks flag-like names, path traversal, and
19
+ // ENAMETOOLONG-length names.
20
+ try {
21
+ assertValidMigrationName(name);
22
+ }
23
+ catch (e) {
24
+ out.err(` ${describeThrown(e)}`);
25
+ out.err(GENERATE_USAGE);
26
+ return { kind: "exit", code: 1 };
27
+ }
28
+ // Trailing args (e.g. a typo'd `--dry-run` — there is no such flag) were
29
+ // silently ignored and the migration got written anyway. Reject them
30
+ // before any IO instead of pretending they did something.
31
+ if (argv.length > 2) {
32
+ out.err(` Unrecognized argument(s): ${argv.slice(2).join(" ")}`);
33
+ out.err(GENERATE_USAGE);
34
+ return { kind: "exit", code: 1 };
35
+ }
36
+ return { kind: "name", name };
37
+ }
38
+ function isEmptyMigration(result) {
39
+ return (result.diff.newTables.length === 0 &&
40
+ result.diff.changedTables.length === 0 &&
41
+ result.diff.droppedTables.length === 0 &&
42
+ result.retiredDrops.length === 0);
43
+ }
44
+ function writeMigrationFiles(migrationsDir, snapshotPath, result) {
45
+ if (!existsSync(migrationsDir))
46
+ mkdirSync(migrationsDir, { recursive: true });
47
+ writeFileSync(join(migrationsDir, result.filename), result.sqlContent);
48
+ writeSnapshotJson(snapshotPath, result.snapshot);
49
+ }
50
+ function reportGeneratedMigration(out, result, rebuildTables) {
51
+ out.log("");
52
+ out.log(` ✓ ${result.filename}`);
53
+ out.log(` new tables: ${result.diff.newTables.length}, changed: ${result.diff.changedTables.length}, dropped: ${result.diff.droppedTables.length}, retired framework tables dropped: ${result.retiredDrops.length}`);
54
+ if (rebuildTables.length > 0) {
55
+ out.log(` rebuild-marker: ${result.filename.replace(/\.sql$/, ".rebuild.json")} (${rebuildTables.length} table(s))`);
56
+ }
57
+ out.log("");
58
+ out.log(" Review, hand-edit if needed, then git add + commit. Apply via: schema apply");
59
+ out.log("");
60
+ }
61
+ export async function runGenerateCommand(ctx) {
62
+ const { out, migrationsDir } = ctx;
63
+ const parsed = parseGenerateName(ctx);
64
+ if (parsed.kind === "exit")
65
+ return parsed.code;
66
+ const schemaMod = await loadSchemaModOrReport(ctx);
67
+ if (schemaMod === undefined)
68
+ return 1;
69
+ const snapshotPath = join(migrationsDir, SNAPSHOT_FILENAME);
70
+ const prevSnapshot = loadPreviousSnapshot(snapshotPath);
71
+ const result = generateMigration({
72
+ metas: schemaMod.entityMetas,
73
+ prevSnapshot,
74
+ name: parsed.name,
75
+ sequenceNumber: nextSequenceNumber(migrationsDir),
76
+ priorMigrationsSql: readMigrationSqlTexts(migrationsDir),
77
+ });
78
+ if (isEmptyMigration(result)) {
79
+ out.log(" No schema changes detected — no new migration file written.");
80
+ return 0;
81
+ }
82
+ writeMigrationFiles(migrationsDir, snapshotPath, result);
83
+ // Rebuild markers are written only for incremental migrations — the initial
84
+ // migration (prevSnapshot===null) only creates tables, so there are no historical
85
+ // events to replay.
86
+ const rebuildTables = prevSnapshot === null ? [] : rebuildTablesFromDiff(result.diff);
87
+ writeRebuildMarker(migrationsDir, result.filename, rebuildTables);
88
+ reportGeneratedMigration(out, result, rebuildTables);
89
+ return 0;
90
+ }
@@ -0,0 +1,39 @@
1
+ import { type renderTablesDdl } from "../db/index.ts";
2
+ import type { FeatureDefinition } from "../engine/types/feature.ts";
3
+ export type SchemaCliOut = {
4
+ readonly log: (line: string) => void;
5
+ readonly err: (line: string) => void;
6
+ };
7
+ export type RunSchemaCliOptions = {
8
+ readonly features?: undefined;
9
+ readonly kmsSlots?: undefined;
10
+ } | {
11
+ /** Composed app features. When given, `apply` rebuilds the projections whose
12
+ * tables a freshly applied migration changed (via its `.rebuild.json`
13
+ * marker). Omitted (dev `kumiko schema`) → no rebuild, migrations only. */
14
+ readonly features: readonly FeatureDefinition[];
15
+ /** Schema-declared Key Manager slots, `kmsSlotsOf(composedEnv.schema)`, or
16
+ * `[]` when the app wires none. This CLI parses no env schema, so the
17
+ * rebuild's KMS wiring cannot derive them itself. */
18
+ readonly kmsSlots: readonly string[];
19
+ };
20
+ export type SchemaCliContext = {
21
+ readonly argv: readonly string[];
22
+ readonly out: SchemaCliOut;
23
+ readonly options: RunSchemaCliOptions;
24
+ readonly schemaFile: string;
25
+ readonly migrationsDir: string;
26
+ };
27
+ export type SchemaSubcommandHandler = (ctx: SchemaCliContext) => Promise<number>;
28
+ export declare const SNAPSHOT_FILENAME = ".snapshot.json";
29
+ export type SchemaMod = {
30
+ readonly entityMetas: Parameters<typeof renderTablesDdl>[0];
31
+ readonly features?: unknown;
32
+ readonly validateBootOptions?: unknown;
33
+ };
34
+ export declare function describeThrown(e: unknown): string;
35
+ export declare function nextSequenceNumber(migrationsDir: string): number;
36
+ export declare function loadPreviousSnapshot(snapshotPath: string): import("../db/migrate-generator.ts").Snapshot | null;
37
+ export declare function loadSchemaModOrReport(ctx: SchemaCliContext): Promise<SchemaMod | undefined>;
38
+ export declare function readDatabaseUrlOrReport(out: SchemaCliOut): string | undefined;
39
+ export declare function migrationsDirExistsOrReport(ctx: SchemaCliContext): boolean;