@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,9 +1,9 @@
1
1
  #!/usr/bin/env bun
2
2
  // Migrates create*Field(...) calls from the old flag-based PII API (pii,
3
3
  // userOwned, tenantOwned, subjectRef, allowPlaintext, lookupable,
4
- // searchable, sensitive) to the author-facing personal/find API
5
- // (kumiko-framework#2250). Idempotent — re-running is a no-op on already
6
- // migrated fields (skipped once a `personal` property is present).
4
+ // searchable, sensitive) to the author-facing personal/find API.
5
+ // Idempotent — re-running is a no-op on already migrated fields (skipped once
6
+ // a `personal` property is present).
7
7
  //
8
8
  // Mapping table (exact):
9
9
  // pii: true -> personal: "self"
@@ -87,7 +87,16 @@ const OVERRIDES_ARG_INDEX_1 = new Set(["createEmbeddedField", "createEmbeddedLis
87
87
  const FIELD_FACTORY_RE = /^create\w*Field$/;
88
88
 
89
89
  type ReportEntry = { readonly file: string; readonly line: number; readonly note: string };
90
- type FindBucket = "exact" | "fuzzy" | "none" | "secret" | "ref" | "personal-false" | "no-find";
90
+ const FIND_BUCKETS = [
91
+ "exact",
92
+ "fuzzy",
93
+ "none",
94
+ "secret",
95
+ "ref",
96
+ "personal-false",
97
+ "no-find",
98
+ ] as const;
99
+ type FindBucket = (typeof FIND_BUCKETS)[number];
91
100
 
92
101
  const reports: ReportEntry[] = [];
93
102
  const newLookupableSites: ReportEntry[] = [];
@@ -168,50 +177,40 @@ function reportRawSubjectLiterals(
168
177
  }
169
178
  }
170
179
 
171
- // kumiko-lint-ignore complexity-budget migration codemod, one-time script — splitting would add risk without benefit
172
- function processObjectLiteral(obj: ObjectLiteralExpression, factoryName: string): void {
173
- // skip: field already carries `personal` — already migrated, idempotent no-op
174
- if (obj.getProperty("personal")) return;
175
-
176
- const piiEncryptedProp = obj.getProperty("piiEncrypted");
177
- if (piiEncryptedProp) {
178
- report(
179
- piiEncryptedProp,
180
- "piiEncrypted: true on an entity field (removed from the type system) — needs a human decision, not a mechanical mapping",
181
- );
182
- // skip: reported above — piiEncrypted needs a human decision, not a mechanical mapping
183
- return;
184
- }
180
+ type SubjectProp = { readonly name: SubjectFlagName; readonly prop: PropertyAssignment };
181
+ type PersonalInit = { readonly personal: string; readonly reason: string | undefined };
182
+ type FindSupport = "text" | "longText" | "none";
183
+ type FindabilityProps = {
184
+ readonly lookupable: PropertyAssignment | undefined;
185
+ readonly searchable: PropertyAssignment | undefined;
186
+ readonly sensitive: PropertyAssignment | undefined;
187
+ };
185
188
 
189
+ function singleSubjectFlag(obj: ObjectLiteralExpression): SubjectProp | undefined {
186
190
  const subjectProps = SUBJECT_FLAG_NAMES.map((n) => ({ name: n, prop: findProp(obj, n) })).filter(
187
191
  (x): x is { name: SubjectFlagName; prop: PropertyAssignment } => !!x.prop,
188
192
  );
189
- const lookupableProp = findProp(obj, "lookupable");
190
- const searchableProp = findProp(obj, "searchable");
191
- const sensitiveProp = findProp(obj, "sensitive");
192
- const anonymizeProp = obj.getProperty("anonymize");
193
-
194
193
  if (subjectProps.length === 0) {
195
194
  // `searchable`/`sensitive` alone remain valid, unrelated FieldDef
196
195
  // properties — left untouched. `lookupable` alone can never compile
197
196
  // (always an excess property without a `personal` to attach `find`
198
197
  // to). `anonymize` alone has no PersonalAnnotations arm to live on.
198
+ const lookupableProp = findProp(obj, "lookupable");
199
199
  if (lookupableProp) {
200
200
  report(
201
201
  lookupableProp,
202
202
  "lookupable without any subject annotation — no `personal` to attach `find` to",
203
203
  );
204
204
  }
205
+ const anonymizeProp = obj.getProperty("anonymize");
205
206
  if (anonymizeProp) {
206
207
  report(
207
208
  anonymizeProp,
208
209
  "anonymize without any subject annotation — no PersonalAnnotations arm accepts anonymize alone",
209
210
  );
210
211
  }
211
- // skip: no subject flag on this field — stray lookupable/anonymize already reported above if present
212
- return;
212
+ return undefined;
213
213
  }
214
-
215
214
  if (subjectProps.length > 1) {
216
215
  const first = subjectProps[0];
217
216
  if (first) {
@@ -220,152 +219,154 @@ function processObjectLiteral(obj: ObjectLiteralExpression, factoryName: string)
220
219
  `multiple subject annotations on one field (${subjectProps.map((s) => s.name).join(", ")}) — needs a human decision on which subject is correct`,
221
220
  );
222
221
  }
223
- // skip: reported above — multiple subject annotations need a human call on which one wins
224
- return;
225
- }
226
-
227
- const subject = subjectProps[0];
228
- if (!subject) {
229
- // skip: length===0 and length>1 already returned — empty [0] is unreachable
230
- return;
222
+ return undefined;
231
223
  }
224
+ return subjectProps[0];
225
+ }
232
226
 
227
+ function findSupportOf(factoryName: string, subject: SubjectProp): FindSupport | undefined {
233
228
  if (NO_PERSONAL_FACTORIES.has(factoryName)) {
234
229
  report(
235
230
  subject.prop,
236
231
  `${factoryName} has no personal-annotation support — subject flag "${subject.name}" cannot be expressed here`,
237
232
  );
238
- // skip: reported above — this factory has no personal-annotation support to migrate into
239
- return;
240
- }
241
- const isTextFind = TEXT_FIND_FACTORIES.has(factoryName);
242
- const isLongTextFind = LONGTEXT_FIND_FACTORIES.has(factoryName);
243
- const isNoFind = NO_FIND_FACTORIES.has(factoryName);
244
- if (!isTextFind && !isLongTextFind && !isNoFind) {
245
- report(
246
- subject.prop,
247
- `unrecognized field factory "${factoryName}" — cannot verify its PersonalAnnotations shape`,
248
- );
249
- // skip: reported above — unknown factory shape, cannot verify safely
250
- return;
233
+ return undefined;
234
+ }
235
+ if (TEXT_FIND_FACTORIES.has(factoryName)) return "text";
236
+ if (LONGTEXT_FIND_FACTORIES.has(factoryName)) return "longText";
237
+ if (NO_FIND_FACTORIES.has(factoryName)) return "none";
238
+ report(
239
+ subject.prop,
240
+ `unrecognized field factory "${factoryName}" — cannot verify its PersonalAnnotations shape`,
241
+ );
242
+ return undefined;
243
+ }
244
+
245
+ function personalFromTrueFlag(
246
+ personal: string,
247
+ ): (subject: SubjectProp) => PersonalInit | undefined {
248
+ return (subject) => {
249
+ if (isTrueLiteral(subject.prop.getInitializer())) return { personal, reason: undefined };
250
+ report(subject.prop, `${subject.name} is set to a non-\`true\` value — cannot infer intent`);
251
+ return undefined;
252
+ };
253
+ }
254
+
255
+ function personalFromAllowPlaintext(subject: SubjectProp): PersonalInit | undefined {
256
+ const init = subject.prop.getInitializer();
257
+ if (!init) {
258
+ report(subject.prop, "allowPlaintext has no value");
259
+ return undefined;
251
260
  }
261
+ const reason = Node.isStringLiteral(init)
262
+ ? `"${normalizeReasonSlug(init.getLiteralText())}"`
263
+ : init.getText();
264
+ return { personal: "false", reason };
265
+ }
252
266
 
253
- let personalInit: string;
254
- let reasonInit: string | undefined;
255
- if (subject.name === "pii") {
256
- if (!isTrueLiteral(subject.prop.getInitializer())) {
257
- report(subject.prop, "pii is set to a non-`true` value — cannot infer intent");
258
- // skip: reported above — non-`true` pii value, can't infer intent
259
- return;
260
- }
261
- personalInit = `"self"`;
262
- } else if (subject.name === "tenantOwned") {
263
- if (!isTrueLiteral(subject.prop.getInitializer())) {
264
- report(subject.prop, "tenantOwned is set to a non-`true` value — cannot infer intent");
265
- // skip: reported above — non-`true` tenantOwned value, can't infer intent
266
- return;
267
- }
268
- personalInit = `"tenant"`;
269
- } else if (subject.name === "subjectRef") {
270
- if (!isTrueLiteral(subject.prop.getInitializer())) {
271
- report(subject.prop, "subjectRef is set to a non-`true` value — cannot infer intent");
272
- // skip: reported above — non-`true` subjectRef value, can't infer intent
273
- return;
274
- }
275
- personalInit = `"ref"`;
276
- } else if (subject.name === "allowPlaintext") {
277
- const init = subject.prop.getInitializer();
278
- if (!init) {
279
- report(subject.prop, "allowPlaintext has no value");
280
- // skip: reported above — allowPlaintext has no value to migrate
281
- return;
282
- }
283
- personalInit = "false";
284
- reasonInit = Node.isStringLiteral(init)
285
- ? `"${normalizeReasonSlug(init.getLiteralText())}"`
286
- : init.getText();
287
- } else {
288
- const init = subject.prop.getInitializer();
289
- if (!init || !Node.isObjectLiteralExpression(init)) {
290
- report(subject.prop, "userOwned value is not an object literal — cannot extract ownerField");
291
- // skip: reported above — userOwned value isn't an object literal, can't extract ownerField
292
- return;
293
- }
294
- const ownerFieldProp = findProp(init, "ownerField");
295
- const ownerFieldInit = ownerFieldProp?.getInitializer();
296
- if (!ownerFieldInit) {
297
- report(subject.prop, 'userOwned is missing an "ownerField" property');
298
- // skip: reported above — userOwned is missing its ownerField property
299
- return;
300
- }
301
- personalInit = `{ of: ${ownerFieldInit.getText()} }`;
267
+ function personalFromUserOwned(subject: SubjectProp): PersonalInit | undefined {
268
+ const init = subject.prop.getInitializer();
269
+ if (!init || !Node.isObjectLiteralExpression(init)) {
270
+ report(subject.prop, "userOwned value is not an object literal — cannot extract ownerField");
271
+ return undefined;
272
+ }
273
+ const ownerFieldInit = findProp(init, "ownerField")?.getInitializer();
274
+ if (!ownerFieldInit) {
275
+ report(subject.prop, 'userOwned is missing an "ownerField" property');
276
+ return undefined;
302
277
  }
278
+ return { personal: `{ of: ${ownerFieldInit.getText()} }`, reason: undefined };
279
+ }
303
280
 
304
- const subjectIsRefOrPlaintext =
305
- subject.name === "subjectRef" || subject.name === "allowPlaintext";
281
+ // One row per subject flag of the mapping table in the file header; a row
282
+ // reports and returns undefined when the flag value cannot be mapped.
283
+ const PERSONAL_INIT_BY_SUBJECT: Record<
284
+ SubjectFlagName,
285
+ (subject: SubjectProp) => PersonalInit | undefined
286
+ > = {
287
+ pii: personalFromTrueFlag(`"self"`),
288
+ userOwned: personalFromUserOwned,
289
+ tenantOwned: personalFromTrueFlag(`"tenant"`),
290
+ subjectRef: personalFromTrueFlag(`"ref"`),
291
+ allowPlaintext: personalFromAllowPlaintext,
292
+ };
306
293
 
307
- if (subjectIsRefOrPlaintext) {
308
- // These PersonalAnnotations arms carry no `find` field at all.
309
- if (lookupableProp) {
310
- report(
311
- lookupableProp,
312
- `lookupable combined with personal:${subject.name === "subjectRef" ? '"ref"' : "false"} — findability doesn't apply to this subject, table has no mapping`,
313
- );
314
- // skip: reported above — lookupable doesn't apply to this subject, no mapping to migrate
315
- return;
316
- }
317
- applyTransform(obj, [subject.prop], {
318
- personal: personalInit,
319
- reason: reasonInit,
320
- find: undefined,
321
- });
322
- counts[subject.name === "subjectRef" ? "ref" : "personal-false"]++;
323
- // skip: already transformed above — nothing left to check for this subject
294
+ // These PersonalAnnotations arms carry no `find` field at all.
295
+ function migrateFindlessSubject(
296
+ obj: ObjectLiteralExpression,
297
+ subject: SubjectProp,
298
+ personal: PersonalInit,
299
+ lookupableProp: PropertyAssignment | undefined,
300
+ ): void {
301
+ const isRef = subject.name === "subjectRef";
302
+ if (lookupableProp) {
303
+ report(
304
+ lookupableProp,
305
+ `lookupable combined with personal:${isRef ? '"ref"' : "false"} — findability doesn't apply to this subject, table has no mapping`,
306
+ );
307
+ // skip: reported above — lookupable doesn't apply to this subject, no mapping to migrate
324
308
  return;
325
309
  }
310
+ applyTransform(obj, [subject.prop], {
311
+ personal: personal.personal,
312
+ reason: personal.reason,
313
+ find: undefined,
314
+ });
315
+ counts[isRef ? "ref" : "personal-false"]++;
316
+ }
326
317
 
327
- if (isNoFind) {
328
- const stray = lookupableProp ?? searchableProp ?? sensitiveProp;
329
- if (stray) {
330
- report(
331
- stray,
332
- `${factoryName} has no \`find\` in its PersonalAnnotations — findability flag present on a non-text field`,
333
- );
334
- // skip: reported above — findability flag on a non-text field, no valid find to attach
335
- return;
336
- }
337
- applyTransform(obj, [subject.prop], {
338
- personal: personalInit,
339
- reason: undefined,
340
- find: undefined,
341
- });
342
- counts["no-find"]++;
343
- // skip: already transformed above — no-find factory has nothing further to check
318
+ function migrateNoFindField(
319
+ obj: ObjectLiteralExpression,
320
+ factoryName: string,
321
+ subject: SubjectProp,
322
+ personal: PersonalInit,
323
+ findability: FindabilityProps,
324
+ ): void {
325
+ const stray = findability.lookupable ?? findability.searchable ?? findability.sensitive;
326
+ if (stray) {
327
+ report(
328
+ stray,
329
+ `${factoryName} has no \`find\` in its PersonalAnnotations — findability flag present on a non-text field`,
330
+ );
331
+ // skip: reported above — findability flag on a non-text field, no valid find to attach
344
332
  return;
345
333
  }
334
+ applyTransform(obj, [subject.prop], {
335
+ personal: personal.personal,
336
+ reason: undefined,
337
+ find: undefined,
338
+ });
339
+ counts["no-find"]++;
340
+ }
346
341
 
347
- const hasLookupable = !!lookupableProp;
348
- const hasSearchable = !!searchableProp;
349
- const hasSensitive = !!sensitiveProp;
342
+ // lookupable + searchable and searchable alone both map to "fuzzy".
343
+ function findValueOf(findability: FindabilityProps): "exact" | "fuzzy" | "none" | "secret" {
344
+ if (findability.sensitive) return "secret";
345
+ if (findability.searchable) return "fuzzy";
346
+ if (findability.lookupable) return "exact";
347
+ return "none";
348
+ }
350
349
 
351
- if (sensitiveProp && (hasLookupable || hasSearchable)) {
350
+ function migrateTextFindField(
351
+ obj: ObjectLiteralExpression,
352
+ subject: SubjectProp,
353
+ personal: PersonalInit,
354
+ findability: FindabilityProps,
355
+ support: Exclude<FindSupport, "none">,
356
+ ): void {
357
+ const { lookupable, searchable, sensitive } = findability;
358
+ if (sensitive && (lookupable || searchable)) {
352
359
  report(
353
- sensitiveProp,
360
+ sensitive,
354
361
  'sensitive combined with lookupable/searchable — two find values ("secret" vs "exact"/"fuzzy"), needs a human decision',
355
362
  );
356
363
  // skip: reported above — sensitive plus lookupable/searchable is an ambiguous find value
357
364
  return;
358
365
  }
359
366
 
360
- let find: "exact" | "fuzzy" | "none" | "secret";
361
- if (hasSensitive) find = "secret";
362
- else if (hasLookupable && hasSearchable) find = "fuzzy";
363
- else if (hasLookupable) find = "exact";
364
- else if (hasSearchable) find = "fuzzy";
365
- else find = "none";
366
-
367
- if (isLongTextFind && (find === "exact" || find === "fuzzy")) {
368
- const findSite = lookupableProp ?? searchableProp;
367
+ const find = findValueOf(findability);
368
+ if (support === "longText" && (find === "exact" || find === "fuzzy")) {
369
+ const findSite = lookupable ?? searchable;
369
370
  if (findSite) {
370
371
  report(
371
372
  findSite,
@@ -379,7 +380,7 @@ function processObjectLiteral(obj: ObjectLiteralExpression, factoryName: string)
379
380
  // searchable-alone -> "fuzzy" makes expandPersonalAnnotations add
380
381
  // `lookupable: true`, which the field didn't carry before — that's a
381
382
  // new `_bidx` column (DDL migration), tracked separately for the report.
382
- if (hasSearchable && !hasLookupable) {
383
+ if (searchable && !lookupable) {
383
384
  newLookupableSites.push({
384
385
  file: obj.getSourceFile().getFilePath(),
385
386
  line: subject.prop.getStartLineNumber(),
@@ -388,13 +389,51 @@ function processObjectLiteral(obj: ObjectLiteralExpression, factoryName: string)
388
389
  });
389
390
  }
390
391
 
391
- const removedProps = [subject.prop, lookupableProp, searchableProp, sensitiveProp].filter(
392
+ const removedProps = [subject.prop, lookupable, searchable, sensitive].filter(
392
393
  (p): p is PropertyAssignment => !!p,
393
394
  );
394
- applyTransform(obj, removedProps, { personal: personalInit, reason: undefined, find });
395
+ applyTransform(obj, removedProps, { personal: personal.personal, reason: undefined, find });
395
396
  counts[find]++;
396
397
  }
397
398
 
399
+ function processObjectLiteral(obj: ObjectLiteralExpression, factoryName: string): void {
400
+ // skip: field already carries `personal` — already migrated, idempotent no-op
401
+ if (obj.getProperty("personal")) return;
402
+
403
+ const piiEncryptedProp = obj.getProperty("piiEncrypted");
404
+ if (piiEncryptedProp) {
405
+ report(
406
+ piiEncryptedProp,
407
+ "piiEncrypted: true on an entity field (removed from the type system) — needs a human decision, not a mechanical mapping",
408
+ );
409
+ // skip: reported above — piiEncrypted needs a human decision, not a mechanical mapping
410
+ return;
411
+ }
412
+
413
+ const subject = singleSubjectFlag(obj);
414
+ // skip: no subject flag, or several, reported by singleSubjectFlag where needed
415
+ if (!subject) return;
416
+ const support = findSupportOf(factoryName, subject);
417
+ // skip: factory without a verifiable PersonalAnnotations shape, reported by findSupportOf
418
+ if (!support) return;
419
+ const personal = PERSONAL_INIT_BY_SUBJECT[subject.name](subject);
420
+ // skip: flag value cannot be mapped, reported by its PERSONAL_INIT_BY_SUBJECT row
421
+ if (!personal) return;
422
+
423
+ const findability: FindabilityProps = {
424
+ lookupable: findProp(obj, "lookupable"),
425
+ searchable: findProp(obj, "searchable"),
426
+ sensitive: findProp(obj, "sensitive"),
427
+ };
428
+ if (subject.name === "subjectRef" || subject.name === "allowPlaintext") {
429
+ migrateFindlessSubject(obj, subject, personal, findability.lookupable);
430
+ } else if (support === "none") {
431
+ migrateNoFindField(obj, factoryName, subject, personal, findability);
432
+ } else {
433
+ migrateTextFindField(obj, subject, personal, findability, support);
434
+ }
435
+ }
436
+
398
437
  function applyTransform(
399
438
  obj: ObjectLiteralExpression,
400
439
  removedProps: readonly PropertyAssignment[],
@@ -411,7 +450,9 @@ function applyTransform(
411
450
  const removedSet = new Set<PropertyAssignment>(removedProps);
412
451
  let insertIndex = 0;
413
452
  for (let i = 0; i < anchorIndex; i++) {
414
- if (!removedSet.has(properties[i] as PropertyAssignment)) insertIndex++;
453
+ const property = properties[i];
454
+ if (!(property && Node.isPropertyAssignment(property) && removedSet.has(property)))
455
+ insertIndex++;
415
456
  }
416
457
 
417
458
  for (const p of removedProps) p.remove();
@@ -746,9 +787,7 @@ async function main(): Promise<void> {
746
787
 
747
788
  const countsBefore = { ...counts };
748
789
  for (const { name, objArg } of calls) processObjectLiteral(objArg, name);
749
- const changed = (Object.keys(counts) as FindBucket[]).some(
750
- (k) => counts[k] !== countsBefore[k],
751
- );
790
+ const changed = FIND_BUCKETS.some((k) => counts[k] !== countsBefore[k]);
752
791
 
753
792
  if (changed) {
754
793
  touchedFiles++;
@@ -12,7 +12,14 @@
12
12
  // Without a mode flag it writes (kumiko-upgrade --apply calls it that way). --check exits 1
13
13
  // while any rewritable or manual site is left; --dry-run only reports.
14
14
 
15
- import { type ImportDeclaration, Project, SyntaxKind } from "ts-morph";
15
+ import {
16
+ type ExportDeclaration,
17
+ type ExportSpecifier,
18
+ type ImportDeclaration,
19
+ type ImportSpecifier,
20
+ Project,
21
+ SyntaxKind,
22
+ } from "ts-morph";
16
23
  import {
17
24
  type CodemodResult,
18
25
  ensureNamedImport,
@@ -64,6 +71,115 @@ function reportNamespaceUses(
64
71
  }
65
72
  }
66
73
 
74
+ function reportStarReexport(
75
+ declaration: ExportDeclaration,
76
+ filePath: string,
77
+ result: SiteLists,
78
+ ): void {
79
+ result.manual.push({
80
+ file: filePath,
81
+ line: declaration.getStartLineNumber(),
82
+ description: `export * from ${declaration.getModuleSpecifierValue()}: re-export the moved names from ${TARGET_MODULE} by hand`,
83
+ });
84
+ }
85
+
86
+ function moveNamedReexports(
87
+ declaration: ExportDeclaration,
88
+ movedExports: readonly ExportSpecifier[],
89
+ filePath: string,
90
+ result: SiteLists,
91
+ ): void {
92
+ const line = declaration.getStartLineNumber();
93
+ const snapshots = movedExports.map((named) => ({
94
+ name: named.getName(),
95
+ alias: named.getAliasNode()?.getText(),
96
+ isTypeOnly: named.isTypeOnly(),
97
+ }));
98
+ for (const named of movedExports) named.remove();
99
+ const typeOnlyDeclaration = declaration.isTypeOnly();
100
+ if (declaration.getNamedExports().length === 0) declaration.remove();
101
+ declaration.getSourceFile().addExportDeclaration({
102
+ moduleSpecifier: TARGET_MODULE,
103
+ namedExports: snapshots,
104
+ isTypeOnly: typeOnlyDeclaration,
105
+ });
106
+ result.rewrites.push({
107
+ file: filePath,
108
+ line,
109
+ description: `export { ${snapshots.map((snapshot) => snapshot.name).join(", ")} } -> ${TARGET_MODULE}`,
110
+ });
111
+ }
112
+
113
+ function rewriteReexports(
114
+ declaration: ExportDeclaration,
115
+ moved: ReadonlySet<string>,
116
+ filePath: string,
117
+ result: SiteLists,
118
+ ): void {
119
+ const namedExports = declaration.getNamedExports();
120
+ const movedExports = namedExports.filter((named) => moved.has(named.getName()));
121
+ if (namedExports.length === 0) reportStarReexport(declaration, filePath, result);
122
+ else if (movedExports.length > 0) moveNamedReexports(declaration, movedExports, filePath, result);
123
+ }
124
+
125
+ function moveNamedImports(
126
+ declaration: ImportDeclaration,
127
+ movedImports: readonly ImportSpecifier[],
128
+ filePath: string,
129
+ result: SiteLists,
130
+ ): void {
131
+ const line = declaration.getStartLineNumber();
132
+ const typeOnlyDeclaration = declaration.isTypeOnly();
133
+ const sourceFile = declaration.getSourceFile();
134
+ const names = movedImports.map((named) => named.getName());
135
+ // Collected first: removing a specifier invalidates the nodes of its siblings.
136
+ const snapshots = movedImports.map((named) => ({
137
+ name: named.getName(),
138
+ alias: named.getAliasNode()?.getText(),
139
+ typeOnly: named.isTypeOnly(),
140
+ }));
141
+ for (const named of movedImports) named.remove();
142
+ if (
143
+ declaration.getNamedImports().length === 0 &&
144
+ declaration.getNamespaceImport() === undefined &&
145
+ declaration.getDefaultImport() === undefined
146
+ ) {
147
+ declaration.remove();
148
+ }
149
+ for (const snapshot of snapshots) {
150
+ const typeOnly = typeOnlyDeclaration || snapshot.typeOnly;
151
+ if (snapshot.alias === undefined) {
152
+ ensureNamedImport(sourceFile, TARGET_MODULE, snapshot.name, typeOnly);
153
+ } else {
154
+ sourceFile.addImportDeclaration({
155
+ moduleSpecifier: TARGET_MODULE,
156
+ namedImports: [{ name: snapshot.name, alias: snapshot.alias }],
157
+ isTypeOnly: typeOnly,
158
+ });
159
+ }
160
+ }
161
+ result.rewrites.push({
162
+ file: filePath,
163
+ line,
164
+ description: `${names.join(", ")} -> ${TARGET_MODULE}`,
165
+ });
166
+ }
167
+
168
+ function rewriteImportDeclaration(
169
+ declaration: ImportDeclaration,
170
+ filePath: string,
171
+ result: SiteLists,
172
+ ): void {
173
+ const moved = movedNamesOf(declaration);
174
+ if (moved !== undefined) {
175
+ reportNamespaceUses(declaration, moved, filePath, result);
176
+ const movedImports = declaration
177
+ .getNamedImports()
178
+ .filter((named) => moved.has(named.getName()));
179
+ if (movedImports.length > 0) moveNamedImports(declaration, movedImports, filePath, result);
180
+ }
181
+ }
182
+
67
183
  export function transformTestingInjectionSource(
68
184
  source: string,
69
185
  filePath = "source.ts",
@@ -77,47 +193,11 @@ export function transformTestingInjectionSource(
77
193
  });
78
194
  const sourceFile = project.createSourceFile(filePath, source);
79
195
  for (const declaration of sourceFile.getImportDeclarations()) {
80
- const moved = movedNamesOf(declaration);
81
- if (moved === undefined) continue;
82
- reportNamespaceUses(declaration, moved, filePath, result);
83
- const movedImports = declaration
84
- .getNamedImports()
85
- .filter((named) => moved.has(named.getName()));
86
- if (movedImports.length === 0) continue;
87
- const line = declaration.getStartLineNumber();
88
- const typeOnlyDeclaration = declaration.isTypeOnly();
89
- const names = movedImports.map((named) => named.getName());
90
- // Collected first: removing a specifier invalidates the nodes of its siblings.
91
- const snapshots = movedImports.map((named) => ({
92
- name: named.getName(),
93
- alias: named.getAliasNode()?.getText(),
94
- typeOnly: named.isTypeOnly(),
95
- }));
96
- for (const named of movedImports) named.remove();
97
- if (
98
- declaration.getNamedImports().length === 0 &&
99
- declaration.getNamespaceImport() === undefined &&
100
- declaration.getDefaultImport() === undefined
101
- ) {
102
- declaration.remove();
103
- }
104
- for (const snapshot of snapshots) {
105
- const typeOnly = typeOnlyDeclaration || snapshot.typeOnly;
106
- if (snapshot.alias === undefined) {
107
- ensureNamedImport(sourceFile, TARGET_MODULE, snapshot.name, typeOnly);
108
- } else {
109
- sourceFile.addImportDeclaration({
110
- moduleSpecifier: TARGET_MODULE,
111
- namedImports: [{ name: snapshot.name, alias: snapshot.alias }],
112
- isTypeOnly: typeOnly,
113
- });
114
- }
115
- }
116
- result.rewrites.push({
117
- file: filePath,
118
- line,
119
- description: `${names.join(", ")} -> ${TARGET_MODULE}`,
120
- });
196
+ rewriteImportDeclaration(declaration, filePath, result);
197
+ }
198
+ for (const declaration of sourceFile.getExportDeclarations()) {
199
+ const moved = MOVED_NAMES_BY_MODULE.get(declaration.getModuleSpecifierValue() ?? "");
200
+ if (moved !== undefined) rewriteReexports(declaration, moved, filePath, result);
121
201
  }
122
202
  const output = result.rewrites.length > 0 ? sourceFile.getFullText() : source;
123
203
  return { output, ...result };