@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,5 +1,6 @@
1
1
  // Process lifecycle: 4-state machine + LIFO shutdown hooks.
2
2
  // Signal wiring lives in signal-handlers.ts; v1 scope in architecture/lifecycle.md.
3
+ import { createFallbackLogger } from "../logging/utils.js";
3
4
  const DEFAULT_DRAIN_TIMEOUT_MS = 40_000;
4
5
  export function createLifecycle(opts = {}) {
5
6
  const now = opts.now ?? (() => Date.now());
@@ -103,9 +104,6 @@ export function createLifecycle(opts = {}) {
103
104
  };
104
105
  }
105
106
  function makeErrorLogger(logger) {
106
- if (logger) {
107
- return (msg, err) => logger.error(`[lifecycle] ${msg}`, { err });
108
- }
109
- // biome-ignore lint/suspicious/noConsole: ops-visible fallback when no logger is wired
110
- return (msg, err) => console.error(`[lifecycle] ${msg}:`, err);
107
+ const log = createFallbackLogger("lifecycle", logger);
108
+ return (msg, err) => log.error(msg, { err });
111
109
  }
@@ -4,6 +4,9 @@
4
4
  export function attachSignalHandlers(lifecycle, opts = {}) {
5
5
  const signals = opts.signals ?? ["SIGTERM", "SIGINT"];
6
6
  const exitFn = opts.exit ?? ((code) => process.exit(code));
7
+ // bun-types vs @types/node: signal overload on process.on/off is not
8
+ // selectable via bind() under the merged types — EventEmitter seam is intentional.
9
+ const processEmitter = process;
7
10
  const listeners = new Map();
8
11
  // Guard so double-SIGTERM doesn't call exitFn twice. drain() is already
9
12
  // idempotent via its shared promise, but its .then/.catch chain would fire
@@ -24,15 +27,13 @@ export function attachSignalHandlers(lifecycle, opts = {}) {
24
27
  .then(() => exitFn(0))
25
28
  .catch(() => exitFn(1));
26
29
  };
27
- // bun-types vs @types/node: signal overload on process.on/off is not
28
- // selectable via bind() under the merged types — EventEmitter seam is intentional.
29
- process.on(sig, handler);
30
+ processEmitter.on(sig, handler);
30
31
  listeners.set(sig, handler);
31
32
  }
32
33
  return {
33
34
  detach: () => {
34
35
  for (const [sig, handler] of listeners) {
35
- process.off(sig, handler);
36
+ processEmitter.off(sig, handler);
36
37
  }
37
38
  listeners.clear();
38
39
  },
@@ -1,9 +1,10 @@
1
1
  import type { Logger } from "./types.ts";
2
2
  type FallbackLogger = {
3
+ info(msg: string, data?: Record<string, unknown>): void;
3
4
  error(msg: string, data?: Record<string, unknown>): void;
4
5
  warn(msg: string, data?: Record<string, unknown>): void;
5
6
  debug(msg: string, data?: Record<string, unknown>): void;
6
7
  };
7
8
  export declare function setProcessLogger(logger: Logger | null): void;
8
- export declare function createFallbackLogger(namespace: string, logger?: (Pick<Logger, "error"> & Partial<Pick<Logger, "warn" | "debug">>) | undefined): FallbackLogger;
9
+ export declare function createFallbackLogger(namespace: string, logger?: (Pick<Logger, "error"> & Partial<Pick<Logger, "info" | "warn" | "debug" | "child">>) | undefined): FallbackLogger;
9
10
  export {};
@@ -3,9 +3,33 @@ let processLogger = null;
3
3
  export function setProcessLogger(logger) {
4
4
  processLogger = logger;
5
5
  }
6
+ // Bun prints a trailing "undefined" when a second argument is passed explicitly as undefined.
7
+ function writeConsole(write, line, data) {
8
+ if (data === undefined)
9
+ write(line);
10
+ else
11
+ write(line, data);
12
+ }
6
13
  export function createFallbackLogger(namespace, logger) {
14
+ if (logger?.child) {
15
+ const child = logger.child({ component: namespace });
16
+ return {
17
+ info: (msg, data) => child.info(msg, data),
18
+ error: (msg, data) => child.error(msg, data),
19
+ warn: (msg, data) => child.warn(msg, data),
20
+ debug: (msg, data) => child.debug(msg, data),
21
+ };
22
+ }
7
23
  if (logger) {
8
24
  return {
25
+ info(msg, data) {
26
+ if (logger.info) {
27
+ logger.info(`[${namespace}] ${msg}`, data);
28
+ }
29
+ else {
30
+ writeConsole(console.info, `[${namespace}] ${msg}`, data);
31
+ }
32
+ },
9
33
  error(msg, data) {
10
34
  logger.error(`[${namespace}] ${msg}`, data);
11
35
  },
@@ -14,8 +38,7 @@ export function createFallbackLogger(namespace, logger) {
14
38
  logger.warn(`[${namespace}] ${msg}`, data);
15
39
  }
16
40
  else {
17
- // biome-ignore lint/suspicious/noConsole: ops-visible fallback when the wrapped logger has no warn method
18
- console.warn(`[${namespace}] ${msg}`, data);
41
+ writeConsole(console.warn, `[${namespace}] ${msg}`, data);
19
42
  }
20
43
  },
21
44
  debug(msg, data) {
@@ -23,8 +46,7 @@ export function createFallbackLogger(namespace, logger) {
23
46
  logger.debug(`[${namespace}] ${msg}`, data);
24
47
  }
25
48
  else {
26
- // biome-ignore lint/suspicious/noConsole: ops-visible fallback when the wrapped logger has no debug method
27
- console.debug(`[${namespace}] ${msg}`, data);
49
+ writeConsole(console.debug, `[${namespace}] ${msg}`, data);
28
50
  }
29
51
  },
30
52
  };
@@ -41,14 +63,22 @@ export function createFallbackLogger(namespace, logger) {
41
63
  return cachedChild;
42
64
  };
43
65
  return {
66
+ info(msg, data) {
67
+ const child = resolveProcessChild();
68
+ if (child) {
69
+ child.info(msg, data);
70
+ }
71
+ else {
72
+ writeConsole(console.info, `[${namespace}] ${msg}`, data);
73
+ }
74
+ },
44
75
  error(msg, data) {
45
76
  const child = resolveProcessChild();
46
77
  if (child) {
47
78
  child.error(msg, data);
48
79
  }
49
80
  else {
50
- // biome-ignore lint/suspicious/noConsole: ops-visible fallback when no logger is wired
51
- console.error(`[${namespace}] ${msg}`, data);
81
+ writeConsole(console.error, `[${namespace}] ${msg}`, data);
52
82
  }
53
83
  },
54
84
  warn(msg, data) {
@@ -57,8 +87,7 @@ export function createFallbackLogger(namespace, logger) {
57
87
  child.warn(msg, data);
58
88
  }
59
89
  else {
60
- // biome-ignore lint/suspicious/noConsole: ops-visible fallback when no logger is wired
61
- console.warn(`[${namespace}] ${msg}`, data);
90
+ writeConsole(console.warn, `[${namespace}] ${msg}`, data);
62
91
  }
63
92
  },
64
93
  debug(msg, data) {
@@ -67,8 +96,7 @@ export function createFallbackLogger(namespace, logger) {
67
96
  child.debug(msg, data);
68
97
  }
69
98
  else {
70
- // biome-ignore lint/suspicious/noConsole: ops-visible fallback when no logger is wired
71
- console.debug(`[${namespace}] ${msg}`, data);
99
+ writeConsole(console.debug, `[${namespace}] ${msg}`, data);
72
100
  }
73
101
  },
74
102
  };
@@ -4,5 +4,5 @@ export { assertKumikoSchemaCurrent, detectKumikoDrift, formatKumikoDriftReport,
4
4
  export { createPendingRebuildsTable, enqueueProjectionRebuild, listPendingRebuilds, PROJECTION_REBUILD_JOB, pendingRebuildsTable, queueRebuildsFromMarkers, rebuildProjectionOrMultiStream, reportPendingRebuildRun, runPendingRebuilds, } from "./pending-rebuilds.js";
5
5
  // Boot-time backfill: re-encrypts plaintext events after a PII annotation was added.
6
6
  export { createPiiBackfillStateTable, piiAnnotationFingerprint, piiBackfillStateTable, runPiiEventBackfill, } from "./pii-event-backfill.js";
7
- // tableName → projection-name, für den app-seitigen Projection-Rebuild.
7
+ // tableName → projection-name, for application-side projection rebuilds.
8
8
  export { buildProjectionTableIndex } from "./projection-table-index.js";
@@ -28,18 +28,19 @@ export class SchemaDriftError extends Error {
28
28
  }
29
29
  }
30
30
  export async function detectKumikoDrift(db, migrationsDir) {
31
- // App auf neuer Framework-Version, hat aber `./kumiko/migrations` noch nicht
32
- // generiert (z.B. erstes Upgrade, custom Schema-Setup ohne kumiko schema
33
- // generate) → keine local migrations → keine drift. Konsistent zum
34
- // status-Subcommand, der ebenfalls existsSync-guarded ist. Ohne diesen Guard
35
- // würde loadMigrationsFromDir → readdirSync synchron ENOENT werfen
36
- // (plain Error, kein SchemaDriftError) und der Boot crasht roh.
31
+ // A newer framework version may not have generated `./kumiko/migrations` yet
32
+ // (for example, a first upgrade or custom schema setup without `kumiko schema
33
+ // generate`). Treat this as no local migrations and therefore no drift, matching
34
+ // the status subcommand's `existsSync` guard. Without it, `loadMigrationsFromDir`
35
+ // calls `readdirSync`, which throws synchronous ENOENT as a plain Error instead
36
+ // of SchemaDriftError and causes an unhandled boot failure.
37
37
  if (!existsSync(migrationsDir)) {
38
38
  return { ok: true, pending: [], checksumMismatches: [], missingTables: [], missingColumns: [] };
39
39
  }
40
40
  const local = loadMigrationsFromDir(migrationsDir);
41
- // Frische DB ohne je gelaufenes `kumiko schema apply` → tracking-table fehlt.
42
- // Das ist kein Fehler, sondern "nichts applied" → alle local sind pending.
41
+ // A fresh database may not have the tracking table because `kumiko schema apply`
42
+ // has never run. Treat that as no applied migrations, so all local migrations are
43
+ // pending rather than reporting an error.
43
44
  const trackingExists = await tableExists(db, "_kumiko_migrations");
44
45
  const applied = trackingExists
45
46
  ? new Map((await fetchAppliedMigrations(db)).map((a) => [a.id, a.checksum]))
@@ -117,11 +118,11 @@ export function formatKumikoDriftReport(report) {
117
118
  lines.push(` - ${m.table}: ${m.columns.join(", ")}`);
118
119
  }
119
120
  }
120
- // Per-Cause Remediation — `kumiko schema apply` löst NUR pending. Checksum-
121
- // mismatch ist eine Sackgasse für apply (MigrationChecksumMismatchError) und
122
- // baseline (ON CONFLICT DO NOTHING → landet in alreadyTracked). Missing
123
- // tables ohne pending = manuell gelöschte Tabelle nach apply → ebenfalls
124
- // nicht durch apply heilbar.
121
+ // Remediation depends on the cause: `kumiko schema apply` handles only pending
122
+ // migrations. A checksum mismatch causes MigrationChecksumMismatchError during
123
+ // apply, and baseline's ON CONFLICT DO NOTHING leaves it in alreadyTracked.
124
+ // A missing table with no pending migration was deleted after apply and cannot be
125
+ // restored by apply.
125
126
  lines.push("");
126
127
  if (report.pending.length > 0) {
127
128
  lines.push("Run 'kumiko schema apply' to apply the pending migration(s).");
@@ -5,10 +5,10 @@ import type { Logger } from "../logging/types.ts";
5
5
  import { type RebuildResult } from "../pipeline/index.ts";
6
6
  export declare const pendingRebuildsTable: import("@cosmicdrift/kumiko-types/executor-brand").WritableSchemaTable;
7
7
  export declare function createPendingRebuildsTable(db: DbConnection): Promise<void>;
8
- /** Liest die rebuild-Marker der frisch applizierten Migrations und queued
9
- * die betroffenen Tabellen. Upsert: ein bereits pending-er Tisch behält
10
- * seinen Queue-Slot (queued_at bleibt, damit die Reihenfolge stabil ist) —
11
- * migration_id zeigt auf die zuletzt flaggende Migration (Debug-Bezug). */
8
+ /** Reads rebuild markers from freshly applied migrations and queues the
9
+ * affected tables. Upsert: an already-pending table keeps its queue slot
10
+ * (`queued_at` remains unchanged, preserving order), while `migration_id`
11
+ * points to the latest migration that flagged it for debugging. */
12
12
  export declare function queueRebuildsFromMarkers(db: DbConnection, options: {
13
13
  readonly migrationsDir: string;
14
14
  readonly appliedIds: readonly string[];
@@ -21,7 +21,7 @@ export declare function listPendingRebuildRows(db: DbConnection): Promise<readon
21
21
  export declare function listPendingRebuilds(db: DbConnection): Promise<readonly string[]>;
22
22
  export declare function clearPendingRebuilds(db: DbConnection, rows: readonly PendingRebuildRow[]): Promise<void>;
23
23
  export type PendingRebuildRun = {
24
- /** Erfolgreich rebuildte Projektionen (Queue-Einträge geräumt). */
24
+ /** Projections rebuilt successfully (queue entries removed). */
25
25
  readonly rebuilt: readonly {
26
26
  readonly projection: string;
27
27
  readonly eventsProcessed: number;
@@ -36,18 +36,19 @@ export type PendingRebuildRun = {
36
36
  * marker) OR were deliberately DROPped by a later migration in the same
37
37
  * run (no data loss, no projection left to resolve) — drained, silent. */
38
38
  readonly unmapped: readonly string[];
39
- /** In DIESEM Run via Marker geleerte managed-Tabellen ohne auflösbare
40
- * Projektion = das owning-Feature fehlt in der Komposition. Geräumt (kein
41
- * Stuck-Loop), aber LAUT geloggt — die Projektion ist jetzt leer. */
39
+ /** Managed tables emptied by a marker in THIS run without a resolvable
40
+ * projection, indicating that the owning feature is missing from the
41
+ * composition. They are drained to avoid a stuck loop, but logged loudly
42
+ * because the projection is now empty. */
42
43
  readonly unresolvedManaged: readonly string[];
43
44
  };
44
45
  export type RunPendingRebuildsOptions = {
45
- /** Tabellen, die in DIESEM apply-Run frisch via Marker gequeued wurden
46
- * (Rückgabe von `queueRebuildsFromMarkers`). Marker tragen nur managed
47
- * Tabellen — eine davon ohne auflösbare Projektion ist ein echter Defekt
48
- * (fehlendes Feature) und wird laut gemeldet statt still geleert. Fehlt die
49
- * Option, wird jede unmapped Tabelle als pre-existing/benign behandelt
50
- * (Verhalten vor #361). */
46
+ /** Tables freshly queued via markers in THIS apply run
47
+ * (returned by `queueRebuildsFromMarkers`). Markers contain only managed
48
+ * tables, so one without a resolvable projection is a real defect (a
49
+ * missing feature) and is reported loudly instead of being silently
50
+ * drained. Without this option, every unmapped table is treated as
51
+ * pre-existing and benign. */
51
52
  readonly thisRunTables?: readonly string[];
52
53
  };
53
54
  export declare function rebuildProjectionOrMultiStream(name: string, deps: {
@@ -63,12 +64,12 @@ export declare function rebuildProjectionOrMultiStream(name: string, deps: {
63
64
  * failed tables stay queued. Shared by every `schema apply` entry point so the
64
65
  * report and the error text cannot drift apart. */
65
66
  export declare function reportPendingRebuildRun(run: PendingRebuildRun, log: (line: string) => void): void;
66
- /** Arbeitet die persistierte Queue ab: mappt Tabellen auf Projektionen,
67
- * rebuildet jede betroffene Projektion und räumt ihre Tabellen erst nach
68
- * ERFOLG aus der Queue. Fehlgeschlagene bleiben pending — der nächste
69
- * apply (oder ein direkter Re-Call) holt sie nach. Unmapped-Tabellen werden
70
- * geräumt (kein Stuck-Loop); die in diesem Run frisch geleerten managed-
71
- * Tabellen ohne Projektion zusätzlich laut gemeldet (`unresolvedManaged`). */
67
+ /** Processes the persistent queue: maps tables to projections, rebuilds each
68
+ * affected projection, and removes its tables from the queue only after
69
+ * SUCCESS. Failed tables remain pending and are retried by the next apply
70
+ * (or a direct re-call). Unmapped tables are drained to avoid a stuck loop;
71
+ * managed tables freshly emptied in this run without a projection are also
72
+ * reported through `unresolvedManaged`. */
72
73
  export declare function runPendingRebuilds(db: DbConnection, registry: Registry, options?: RunPendingRebuildsOptions): Promise<PendingRebuildRun>;
73
74
  export declare const PROJECTION_REBUILD_JOB = "jobs:job:projection-rebuild";
74
75
  export type EnqueueProjectionRebuildResult = {
@@ -1,15 +1,15 @@
1
- // Persistente Pending-Rebuild-Queue für den `kumiko schema apply`-Pfad.
1
+ // Persistent pending-rebuild queue for the `kumiko schema apply` path.
2
2
  //
3
- // Problem (studio#36/#46): Apps lasen die rebuild-Marker nur für
4
- // `result.applied` der AKTUELLEN apply-Runde. Schlug der Projection-Rebuild
5
- // fehl (oder crashte der Prozess dazwischen), war der Marker-Bezug beim
6
- // nächsten apply weg — `applied` ist dann leer — und die Projektion blieb
7
- // stillschweigend unfertig, ohne Self-Service-Retry-Pfad.
3
+ // Problem: Apps read rebuild markers only from `result.applied` for the
4
+ // CURRENT apply round. If the projection rebuild failed (or the process
5
+ // crashed in between), the marker reference was gone on the next apply —
6
+ // `applied` was empty — leaving the projection silently incomplete without a
7
+ // self-service retry path.
8
8
  //
9
- // Lösung: die betroffenen Tabellen werden VOR dem Rebuild in
10
- // `kumiko_pending_rebuilds` persistiert und erst nach erfolgreichem Rebuild
11
- // der zugehörigen Projektion gelöscht. Ein erneuter apply (auch ohne neue
12
- // Migrations) holt offene Rebuilds über `runPendingRebuilds` nach.
9
+ // Solution: persist the affected tables in `kumiko_pending_rebuilds` BEFORE
10
+ // the rebuild and delete them only after the associated projection rebuild
11
+ // succeeds. A subsequent apply, even without new migrations, processes open
12
+ // rebuilds through `runPendingRebuilds`.
13
13
  import { instant, table as pgTable, sql, text } from "../db/dialect.js";
14
14
  import { deleteMany, selectMany, upsertOnConflict } from "../db/query.js";
15
15
  import { readRebuildMarker } from "../db/rebuild-marker.js";
@@ -24,15 +24,15 @@ export const pendingRebuildsTable = pgTable("kumiko_pending_rebuilds", {
24
24
  queuedAt: instant("queued_at", { precision: 3 }).notNull().default(sql `now()`),
25
25
  });
26
26
  export async function createPendingRebuildsTable(db) {
27
- // skip: table already exists — bootstrap läuft aus mehreren Pfaden
27
+ // skip: table already exists — bootstrap runs from multiple paths
28
28
  if (await tableExists(db, "public.kumiko_pending_rebuilds"))
29
29
  return;
30
30
  await unsafePushTables(db, { kumikoPendingRebuilds: pendingRebuildsTable });
31
31
  }
32
- /** Liest die rebuild-Marker der frisch applizierten Migrations und queued
33
- * die betroffenen Tabellen. Upsert: ein bereits pending-er Tisch behält
34
- * seinen Queue-Slot (queued_at bleibt, damit die Reihenfolge stabil ist) —
35
- * migration_id zeigt auf die zuletzt flaggende Migration (Debug-Bezug). */
32
+ /** Reads rebuild markers from freshly applied migrations and queues the
33
+ * affected tables. Upsert: an already-pending table keeps its queue slot
34
+ * (`queued_at` remains unchanged, preserving order), while `migration_id`
35
+ * points to the latest migration that flagged it for debugging. */
36
36
  export async function queueRebuildsFromMarkers(db, options) {
37
37
  await createPendingRebuildsTable(db);
38
38
  const queued = new Set();
@@ -58,7 +58,7 @@ export async function listPendingRebuilds(db) {
58
58
  // table_name alone: a concurrent queueRebuildsFromMarkers can re-queue the same
59
59
  // table for a NEWER migration (upsert bumps migration_id, keeps the slot)
60
60
  // between the read and the clear. Scoping the delete to the snapshot's
61
- // migration_id leaves that fresh re-queue intact instead of dropping it. (#328)
61
+ // migration_id leaves that fresh re-queue intact instead of dropping it.
62
62
  export async function clearPendingRebuilds(db, rows) {
63
63
  for (const { tableName, migrationId } of rows) {
64
64
  await deleteMany(db, pendingRebuildsTable, { tableName, migrationId });
@@ -106,12 +106,12 @@ export function reportPendingRebuildRun(run, log) {
106
106
  .join("; ")}. Table(s) stay queued in kumiko_pending_rebuilds — retried on the next apply.`);
107
107
  }
108
108
  }
109
- /** Arbeitet die persistierte Queue ab: mappt Tabellen auf Projektionen,
110
- * rebuildet jede betroffene Projektion und räumt ihre Tabellen erst nach
111
- * ERFOLG aus der Queue. Fehlgeschlagene bleiben pending — der nächste
112
- * apply (oder ein direkter Re-Call) holt sie nach. Unmapped-Tabellen werden
113
- * geräumt (kein Stuck-Loop); die in diesem Run frisch geleerten managed-
114
- * Tabellen ohne Projektion zusätzlich laut gemeldet (`unresolvedManaged`). */
109
+ /** Processes the persistent queue: maps tables to projections, rebuilds each
110
+ * affected projection, and removes its tables from the queue only after
111
+ * SUCCESS. Failed tables remain pending and are retried by the next apply
112
+ * (or a direct re-call). Unmapped tables are drained to avoid a stuck loop;
113
+ * managed tables freshly emptied in this run without a projection are also
114
+ * reported through `unresolvedManaged`. */
115
115
  export async function runPendingRebuilds(db, registry, options = {}) {
116
116
  const pendingRows = await listPendingRebuildRows(db);
117
117
  if (pendingRows.length === 0) {
@@ -119,7 +119,7 @@ export async function runPendingRebuilds(db, registry, options = {}) {
119
119
  }
120
120
  // (table → migration) snapshot read together with the table list; the clear
121
121
  // below scopes its DELETE to this migration_id so a concurrent re-queue for a
122
- // newer migration survives instead of being dropped (#328).
122
+ // newer migration survives instead of being dropped.
123
123
  const migrationByTable = new Map(pendingRows.map((r) => [r.tableName, r.migrationId]));
124
124
  const snapshotRows = (tables) => tables.flatMap((tableName) => {
125
125
  const migrationId = migrationByTable.get(tableName);
@@ -136,7 +136,7 @@ export async function runPendingRebuilds(db, registry, options = {}) {
136
136
  if (projection === undefined) {
137
137
  // Markers only carry managed tables (rebuild-marker.ts). A table emptied
138
138
  // in THIS run with no resolving projection is a real defect (owning
139
- // feature missing from the composition) → loud (#361) — unless the table
139
+ // feature missing from the composition) → loud — unless the table
140
140
  // no longer exists: then a later migration in the same run deliberately
141
141
  // DROPped it after migrating its data elsewhere, no data loss. Pre-
142
142
  // existing pending tables stay indistinguishable from old unmanaged
@@ -151,9 +151,9 @@ export async function runPendingRebuilds(db, registry, options = {}) {
151
151
  }
152
152
  byProjection.set(projection, [...(byProjection.get(projection) ?? []), tableName]);
153
153
  }
154
- // Beide Klassen räumen: die Queue darf nicht ewig wachsen, und ein Re-Apply
155
- // darf nicht sticky-stuck werfen. Die Lautstärke liegt im Log + Return-Feld,
156
- // nicht im Liegenlassen.
154
+ // Both classes are drained: the queue must not grow indefinitely, and a
155
+ // re-apply must not remain stuck. The distinction is conveyed through the
156
+ // log and return field rather than by leaving entries queued.
157
157
  const drained = [...unmapped, ...unresolvedManaged];
158
158
  if (drained.length > 0) {
159
159
  await clearPendingRebuilds(db, snapshotRows(drained));
@@ -1,5 +1,5 @@
1
1
  import type { Registry } from "../engine/types/feature.ts";
2
- /** Index `tableName → projection-name` aus der Registry. Nur Projections mit
3
- * table-Definition (single-stream + multi-stream-with-table) zählen.
4
- * Side-effect-only MSPs (table omitted) haben keinen Rebuild-Sinn. */
2
+ /** Index `tableName → projection-name` from the registry. Only projections with
3
+ * a table definition (single-stream + multi-stream-with-table) are included.
4
+ * Side-effect-only MSPs (table omitted) cannot be rebuilt. */
5
5
  export declare function buildProjectionTableIndex(registry: Registry): ReadonlyMap<string, string>;
@@ -1,13 +1,13 @@
1
- // Index `tableName → projection-name` aus der Registry. Genutzt vom
2
- // app-seitigen Projection-Rebuild (`kumiko schema apply` liest die
3
- // rebuild-Marker → mappt Tabellen auf Projektionen → rebuildProjection).
1
+ // Index `tableName → projection-name` from the registry, used by the
2
+ // app-side projection rebuild (`kumiko schema apply` reads rebuild markers,
3
+ // maps tables to projections, then calls rebuildProjection).
4
4
  //
5
- // Drizzle-frei: der Tabellen-Name kommt aus dem kumiko-Symbol das
6
- // buildEntityTable/deriveEntityTableMeta an die Table-Definition hängt.
5
+ // Drizzle-free: the table name comes from the kumiko symbol attached to the
6
+ // table definition by buildEntityTable/deriveEntityTableMeta.
7
7
  import { extractTableName } from "../db/index.js";
8
- /** Index `tableName → projection-name` aus der Registry. Nur Projections mit
9
- * table-Definition (single-stream + multi-stream-with-table) zählen.
10
- * Side-effect-only MSPs (table omitted) haben keinen Rebuild-Sinn. */
8
+ /** Index `tableName → projection-name` from the registry. Only projections with
9
+ * a table definition (single-stream + multi-stream-with-table) are included.
10
+ * Side-effect-only MSPs (table omitted) cannot be rebuilt. */
11
11
  export function buildProjectionTableIndex(registry) {
12
12
  const index = new Map();
13
13
  for (const [name, def] of registry.getAllProjections()) {
@@ -12,6 +12,9 @@ export declare function createEscapeHatchReportWindow(opts?: {
12
12
  export type EscapeHatchProcessDedup = {
13
13
  readonly kinds: ReadonlySet<EscapeHatchKind>;
14
14
  readonly lastAuditedAt: Map<string, number>;
15
+ readonly lastSweep: {
16
+ atMs: number;
17
+ };
15
18
  };
16
19
  export declare function createEscapeHatchProcessDedup(kinds: readonly EscapeHatchKind[]): EscapeHatchProcessDedup;
17
20
  export declare function withEscapeHatchAuditScope<T>(run: () => Promise<T>): Promise<T>;
@@ -40,8 +40,18 @@ const READ_WINDOW_KINDS = new Set([
40
40
  // A long-lived worker must leave an audit event inside every retention window
41
41
  // (90 days), or its crons vanish from the log once the first event is pruned.
42
42
  const PROCESS_REAUDIT_INTERVAL_MS = 24 * 60 * 60 * 1000;
43
+ // Row-level targets add a key per touched row; without a sweep a long-lived worker keeps them all.
44
+ function sweepExpiredProcessDedup(dedup, nowMs) {
45
+ if (nowMs - dedup.lastSweep.atMs >= PROCESS_REAUDIT_INTERVAL_MS) {
46
+ dedup.lastSweep.atMs = nowMs;
47
+ for (const [key, auditedAt] of dedup.lastAuditedAt) {
48
+ if (nowMs - auditedAt >= PROCESS_REAUDIT_INTERVAL_MS)
49
+ dedup.lastAuditedAt.delete(key);
50
+ }
51
+ }
52
+ }
43
53
  export function createEscapeHatchProcessDedup(kinds) {
44
- return { kinds: new Set(kinds), lastAuditedAt: new Map() };
54
+ return { kinds: new Set(kinds), lastAuditedAt: new Map(), lastSweep: { atMs: 0 } };
45
55
  }
46
56
  // Default window for createEscapeHatchReporter callers that don't pass their own.
47
57
  const fallbackReportWindow = createEscapeHatchReportWindow();
@@ -160,6 +170,7 @@ export function createEscapeHatchReporter(opts) {
160
170
  if (lastAuditedAt !== undefined && nowMs - lastAuditedAt < PROCESS_REAUDIT_INTERVAL_MS) {
161
171
  return;
162
172
  }
173
+ sweepExpiredProcessDedup(processDedup, nowMs);
163
174
  processDedup.lastAuditedAt.set(processKey, nowMs);
164
175
  reportEscapeHatchUse(eventFor(kind, reason, target), deps);
165
176
  // skip: audited above, the window dedup below only serves the other kinds
@@ -52,7 +52,7 @@ export function validateMetricName(name, type) {
52
52
  // time) — normalize "-" to "_" here so a feature like "ai-foundation"
53
53
  // resolves to the same "kumiko_ai_foundation_x" on both the registration
54
54
  // path (registry-ingest.ts) and the read path (ctx.metrics / ctx.metricsFor),
55
- // instead of the kebab form being rejected outright (framework#1844).
55
+ // instead of the kebab form being rejected outright.
56
56
  export function buildMetricName(featureName, shortName) {
57
57
  const normalizedFeatureName = toKebab(featureName).replace(/-/g, "_");
58
58
  if (!SNAKE_CASE.test(normalizedFeatureName)) {
@@ -25,12 +25,12 @@ export function createMetricsHandle(meter, featureName) {
25
25
  }
26
26
  // Same feature-bound resolution as createMetricsHandle, but for an
27
27
  // explicit `featureName` chosen by the caller rather than the dispatching
28
- // handler's own feature (framework#1844). Meant for shared/library code
28
+ // handler's own feature. Meant for shared/library code
29
29
  // invoked from many features' HandlerContext (ctx.metricsFor) — the
30
30
  // library owns one stable metric name instead of splintering into
31
31
  // kumiko_<caller>_x per consumer.
32
32
  //
33
- // Decision (framework#1844 DoD): unlike createMetricsHandle, an
33
+ // Decision: unlike createMetricsHandle, an
34
34
  // unregistered name here is a silent no-op, not a throw. This handle is
35
35
  // meant for error/catch-path counters in shared code — a missing
36
36
  // registration (consuming feature not mounted, metric not declared yet)
@@ -101,7 +101,7 @@ export function createNoopMetricsHandle() {
101
101
  };
102
102
  }
103
103
  // Shared factory behind ctx.metricsFor for handler and job contexts, so both
104
- // keep the same no-op-on-unregistered contract (framework#1844).
104
+ // keep the same no-op-on-unregistered contract.
105
105
  export function createMetricsForFactory(meter) {
106
106
  return meter
107
107
  ? (featureName) => createSafeMetricsHandle(meter, featureName)
@@ -1,3 +1,4 @@
1
+ import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
1
2
  import { requestContext } from "../api/request-context.js";
2
3
  import { SYSTEM_EVENT_PREFIX } from "../crypto/system-event-pii.js";
3
4
  import { qnScope, toKebab } from "../engine/qualified-name.js";
@@ -6,6 +7,14 @@ import { isStreamArchived } from "../event-store/archive.js";
6
7
  import { ArchivedStreamError } from "../event-store/errors.js";
7
8
  import { append, getStreamVersion } from "../event-store/event-store.js";
8
9
  import { runProjectionsForEvent } from "./projections-runner.js";
10
+ function requirePayloadObject(payload, eventType) {
11
+ if (!isPlainObject(payload)) {
12
+ throw new InternalError({
13
+ message: `appendEvent("${eventType}"): payload must be an object, got ${Array.isArray(payload) ? "array" : typeof payload}`,
14
+ });
15
+ }
16
+ return payload;
17
+ }
9
18
  export async function appendDomainEventCore(deps, args) {
10
19
  const isSystemEvent = args.type.startsWith(SYSTEM_EVENT_PREFIX);
11
20
  const eventDef = deps.registry.getEvent(args.type);
@@ -42,10 +51,10 @@ export async function appendDomainEventCore(deps, args) {
42
51
  const parsed = eventDef.schema.safeParse(args.payload ?? {});
43
52
  if (!parsed.success)
44
53
  throw validationErrorFromZod(parsed.error);
45
- validatedPayload = parsed.data;
54
+ validatedPayload = requirePayloadObject(parsed.data, args.type);
46
55
  }
47
56
  else {
48
- validatedPayload = (args.payload ?? {});
57
+ validatedPayload = requirePayloadObject(args.payload ?? {}, args.type);
49
58
  }
50
59
  // Archive guard: block writes on archived streams. Without this an append
51
60
  // would produce an "invisible" row that loadAggregate filters out by default
@@ -1,6 +1,6 @@
1
1
  import type { TableColumns } from "../db/dialect.ts";
2
2
  import type { PreDeleteHookFn, Registry } from "../engine/types/index.ts";
3
3
  import type { SystemHookDef } from "./lifecycle-pipeline.ts";
4
- type TableMap = ReadonlyMap<string, TableColumns<any>>;
4
+ type TableMap = ReadonlyMap<string, TableColumns>;
5
5
  export declare function createCascadeDeleteHook(registry: Registry, tables: TableMap): SystemHookDef<PreDeleteHookFn>;
6
6
  export {};