@cosmicdrift/kumiko-framework 0.387.0 → 0.389.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 (375) hide show
  1. package/AGENTS.md +9 -1
  2. package/agents/examples/anonymous-access.md +1 -1
  3. package/agents/examples/apex-landing.md +204 -0
  4. package/agents/examples/api-worker-split.md +64 -0
  5. package/agents/examples/app-config.md +14 -0
  6. package/agents/examples/auth-mfa.md +115 -0
  7. package/agents/examples/env-composition.md +28 -1
  8. package/agents/examples/golden-path.md +13 -1
  9. package/agents/examples/legal-pages.md +1 -1
  10. package/agents/examples/lifecycle-hooks.md +55 -2
  11. package/agents/examples/list-table-options.md +466 -0
  12. package/agents/examples/pdf-audit-export.md +4 -2
  13. package/agents/examples/prod-boot-options.md +16 -0
  14. package/agents/examples/rate-limiting.md +48 -0
  15. package/agents/examples/record-detail-layout.md +692 -0
  16. package/agents/examples/seed-migration.md +212 -0
  17. package/agents/examples/state-machine.md +3 -2
  18. package/agents/examples/tenant-isolation.md +73 -1
  19. package/agents/examples/wizard-form.md +941 -0
  20. package/agents/examples/writeform-section.md +548 -0
  21. package/agents/examples.md +1 -0
  22. package/agents/features.md +19 -2
  23. package/agents/widgets.md +2 -0
  24. package/dist/api/api-constants.d.ts +1 -0
  25. package/dist/api/api-constants.js +2 -0
  26. package/dist/api/auth-routes-shared.d.ts +2 -0
  27. package/dist/api/auth-routes-shared.js +5 -0
  28. package/dist/api/client-error-route.js +10 -5
  29. package/dist/api/entity-read-access.d.ts +2 -1
  30. package/dist/api/entity-read-access.js +14 -0
  31. package/dist/api/export/export-cells-reference.d.ts +9 -0
  32. package/dist/api/export/export-cells-reference.js +21 -0
  33. package/dist/api/export/export-cells.d.ts +11 -0
  34. package/dist/api/export/export-cells.js +136 -0
  35. package/dist/api/export/export-ciphertext-guard.d.ts +2 -0
  36. package/dist/api/export/export-ciphertext-guard.js +27 -0
  37. package/dist/api/export/export-columns.d.ts +29 -0
  38. package/dist/api/export/export-columns.js +85 -0
  39. package/dist/api/export/export-errors.d.ts +4 -0
  40. package/dist/api/export/export-errors.js +14 -0
  41. package/dist/api/export/export-route.d.ts +4 -0
  42. package/dist/api/export/export-route.js +74 -0
  43. package/dist/api/export/export-rows.d.ts +13 -0
  44. package/dist/api/export/export-rows.js +62 -0
  45. package/dist/api/export/export-screen.d.ts +12 -0
  46. package/dist/api/export/export-screen.js +47 -0
  47. package/dist/api/export/export-xlsx-writer.d.ts +4 -0
  48. package/dist/api/export/export-xlsx-writer.js +21 -0
  49. package/dist/api/extra-route.d.ts +4 -0
  50. package/dist/api/index.d.ts +3 -1
  51. package/dist/api/index.js +3 -1
  52. package/dist/api/pii-leak-guard.d.ts +3 -0
  53. package/dist/api/pii-leak-guard.js +3 -2
  54. package/dist/api/request-context.d.ts +1 -1
  55. package/dist/api/request-context.js +1 -1
  56. package/dist/api/routes.d.ts +16 -0
  57. package/dist/api/routes.js +2 -2
  58. package/dist/api/server-boot-checks.d.ts +10 -0
  59. package/dist/api/server-boot-checks.js +157 -0
  60. package/dist/api/server-event-dispatcher.d.ts +26 -0
  61. package/dist/api/server-event-dispatcher.js +208 -0
  62. package/dist/api/server-extra-routes.d.ts +11 -0
  63. package/dist/api/server-extra-routes.js +206 -0
  64. package/dist/api/server-middleware.d.ts +20 -0
  65. package/dist/api/server-middleware.js +211 -0
  66. package/dist/api/server-routes.d.ts +19 -0
  67. package/dist/api/server-routes.js +299 -0
  68. package/dist/api/server-websocket-routes.d.ts +4 -0
  69. package/dist/api/server-websocket-routes.js +108 -0
  70. package/dist/api/server-wiring.d.ts +32 -0
  71. package/dist/api/server-wiring.js +238 -0
  72. package/dist/api/server.d.ts +10 -15
  73. package/dist/api/server.js +48 -1251
  74. package/dist/api/session-cookies.js +4 -4
  75. package/dist/api/sse-route.d.ts +2 -1
  76. package/dist/api/sse-route.js +27 -13
  77. package/dist/config/index.d.ts +3 -1
  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.d.ts +1 -0
  81. package/dist/crypto/pg-kms-adapter.js +12 -18
  82. package/dist/crypto/pii-field-encryption.d.ts +5 -1
  83. package/dist/crypto/pii-field-encryption.js +3 -1
  84. package/dist/db/aggregate-sql.d.ts +23 -0
  85. package/dist/db/aggregate-sql.js +165 -0
  86. package/dist/db/closed-connection.d.ts +9 -0
  87. package/dist/db/closed-connection.js +88 -0
  88. package/dist/db/connection.d.ts +4 -5
  89. package/dist/db/connection.js +29 -20
  90. package/dist/db/dialect.js +1 -1
  91. package/dist/db/distinct-on.d.ts +1 -1
  92. package/dist/db/driver-row-coercion.d.ts +2 -0
  93. package/dist/db/driver-row-coercion.js +186 -0
  94. package/dist/db/driver-value-preparation.d.ts +16 -0
  95. package/dist/db/driver-value-preparation.js +101 -0
  96. package/dist/db/entity-field-encryption.d.ts +1 -0
  97. package/dist/db/entity-field-encryption.js +1 -1
  98. package/dist/db/entity-table-meta.js +103 -110
  99. package/dist/db/event-store-executor-context.d.ts +2 -1
  100. package/dist/db/event-store-executor-read-aggregate-currencies.d.ts +17 -0
  101. package/dist/db/event-store-executor-read-aggregate-currencies.js +40 -0
  102. package/dist/db/event-store-executor-read-aggregate.d.ts +8 -0
  103. package/dist/db/event-store-executor-read-aggregate.js +143 -0
  104. package/dist/db/event-store-executor-read-filter.d.ts +31 -0
  105. package/dist/db/event-store-executor-read-filter.js +206 -0
  106. package/dist/db/event-store-executor-read-paging.d.ts +14 -0
  107. package/dist/db/event-store-executor-read-paging.js +99 -0
  108. package/dist/db/event-store-executor-read-rows.d.ts +6 -0
  109. package/dist/db/event-store-executor-read-rows.js +32 -0
  110. package/dist/db/event-store-executor-read-search.d.ts +28 -0
  111. package/dist/db/event-store-executor-read-search.js +221 -0
  112. package/dist/db/event-store-executor-read-sort.d.ts +41 -0
  113. package/dist/db/event-store-executor-read-sort.js +130 -0
  114. package/dist/db/event-store-executor-read-where.d.ts +24 -0
  115. package/dist/db/event-store-executor-read-where.js +81 -0
  116. package/dist/db/event-store-executor-read.d.ts +2 -9
  117. package/dist/db/event-store-executor-read.js +48 -701
  118. package/dist/db/event-store-executor-write-atomic.d.ts +4 -0
  119. package/dist/db/event-store-executor-write-atomic.js +41 -0
  120. package/dist/db/event-store-executor-write-steps.d.ts +17 -0
  121. package/dist/db/event-store-executor-write-steps.js +95 -0
  122. package/dist/db/event-store-executor-write.js +405 -509
  123. package/dist/db/event-store-executor.js +2 -1
  124. package/dist/db/executor-managed-tables.js +1 -1
  125. package/dist/db/index.d.ts +7 -3
  126. package/dist/db/index.js +6 -2
  127. package/dist/db/list-aggregate-labels.d.ts +14 -0
  128. package/dist/db/list-aggregate-labels.js +26 -0
  129. package/dist/db/migrate-generator.js +71 -86
  130. package/dist/db/migrate-runner.js +7 -4
  131. package/dist/db/pg-error.js +26 -21
  132. package/dist/db/queries/backfill-pii.js +3 -6
  133. package/dist/db/queries/database-now.js +2 -1
  134. package/dist/db/queries/shadow-swap.js +2 -3
  135. package/dist/db/query.d.ts +16 -15
  136. package/dist/db/query.js +76 -804
  137. package/dist/db/reference-data.js +19 -2
  138. package/dist/db/statement-timeout.js +2 -1
  139. package/dist/db/table-builder.js +113 -166
  140. package/dist/db/table-info.d.ts +10 -0
  141. package/dist/db/table-info.js +45 -0
  142. package/dist/db/temporal-value-guards.d.ts +6 -0
  143. package/dist/db/temporal-value-guards.js +15 -0
  144. package/dist/db/tenant-db-runner.d.ts +1 -0
  145. package/dist/db/tenant-db-runner.js +3 -0
  146. package/dist/db/tenant-db-upsert.d.ts +5 -0
  147. package/dist/db/tenant-db-upsert.js +91 -0
  148. package/dist/db/tenant-db.js +50 -10
  149. package/dist/db/where-clause.d.ts +6 -0
  150. package/dist/db/where-clause.js +236 -0
  151. package/dist/derivatives/delete-stored-file.js +2 -1
  152. package/dist/engine/boot-validator/boot-check.js +1 -1
  153. package/dist/engine/boot-validator/bundled-lists-table.d.ts +2 -0
  154. package/dist/engine/boot-validator/bundled-lists-table.js +36 -0
  155. package/dist/engine/boot-validator/detail-screens.js +2 -2
  156. package/dist/engine/boot-validator/entity-handler.d.ts +1 -0
  157. package/dist/engine/boot-validator/entity-handler.js +17 -0
  158. package/dist/engine/boot-validator/entity-list-aggregate-options.d.ts +3 -0
  159. package/dist/engine/boot-validator/entity-list-aggregate-options.js +23 -0
  160. package/dist/engine/boot-validator/form-derivations.js +35 -1
  161. package/dist/engine/boot-validator/i18n-keys.js +5 -3
  162. package/dist/engine/boot-validator/index.js +2 -0
  163. package/dist/engine/boot-validator/list-bulk-actions.d.ts +3 -0
  164. package/dist/engine/boot-validator/list-bulk-actions.js +49 -0
  165. package/dist/engine/boot-validator/list-column-layout.d.ts +8 -0
  166. package/dist/engine/boot-validator/list-column-layout.js +41 -0
  167. package/dist/engine/boot-validator/list-table-aggregate-options.d.ts +24 -0
  168. package/dist/engine/boot-validator/list-table-aggregate-options.js +145 -0
  169. package/dist/engine/boot-validator/list-table-aggregate-query.d.ts +10 -0
  170. package/dist/engine/boot-validator/list-table-aggregate-query.js +31 -0
  171. package/dist/engine/boot-validator/list-table-export-options.d.ts +1 -0
  172. package/dist/engine/boot-validator/list-table-export-options.js +27 -0
  173. package/dist/engine/boot-validator/list-table-options.d.ts +3 -0
  174. package/dist/engine/boot-validator/list-table-options.js +77 -0
  175. package/dist/engine/boot-validator/list-table-reject-unknown-keys.d.ts +1 -0
  176. package/dist/engine/boot-validator/list-table-reject-unknown-keys.js +7 -0
  177. package/dist/engine/boot-validator/pii-retention.js +233 -155
  178. package/dist/engine/boot-validator/projection-list-screens.d.ts +1 -1
  179. package/dist/engine/boot-validator/projection-list-screens.js +3 -13
  180. package/dist/engine/boot-validator/related-list-table-rules.d.ts +4 -0
  181. package/dist/engine/boot-validator/related-list-table-rules.js +42 -0
  182. package/dist/engine/boot-validator/screens-actions.d.ts +1 -0
  183. package/dist/engine/boot-validator/screens-actions.js +8 -1
  184. package/dist/engine/boot-validator/screens-dashboard.js +2 -5
  185. package/dist/engine/boot-validator/screens-forms.js +2 -6
  186. package/dist/engine/boot-validator/screens-kind-config-edit.d.ts +3 -0
  187. package/dist/engine/boot-validator/screens-kind-config-edit.js +88 -0
  188. package/dist/engine/boot-validator/screens-kind-entity-edit.d.ts +3 -0
  189. package/dist/engine/boot-validator/screens-kind-entity-edit.js +55 -0
  190. package/dist/engine/boot-validator/screens-kind-entity-list.d.ts +3 -0
  191. package/dist/engine/boot-validator/screens-kind-entity-list.js +235 -0
  192. package/dist/engine/boot-validator/screens-kind-projection-detail.d.ts +3 -0
  193. package/dist/engine/boot-validator/screens-kind-projection-detail.js +235 -0
  194. package/dist/engine/boot-validator/screens-kind-projection-list.d.ts +3 -0
  195. package/dist/engine/boot-validator/screens-kind-projection-list.js +56 -0
  196. package/dist/engine/boot-validator/screens-kind-secret-mint.d.ts +3 -0
  197. package/dist/engine/boot-validator/screens-kind-secret-mint.js +28 -0
  198. package/dist/engine/boot-validator/screens-kind-shared.d.ts +37 -0
  199. package/dist/engine/boot-validator/screens-kind-shared.js +142 -0
  200. package/dist/engine/boot-validator/screens-lists.d.ts +2 -1
  201. package/dist/engine/boot-validator/screens-lists.js +24 -11
  202. package/dist/engine/boot-validator/screens.d.ts +0 -2
  203. package/dist/engine/boot-validator/screens.js +99 -860
  204. package/dist/engine/build-app-schema.d.ts +2 -1
  205. package/dist/engine/build-app-schema.js +65 -97
  206. package/dist/engine/build-config-feature-schema.js +6 -1
  207. package/dist/engine/define-feature.js +13 -2
  208. package/dist/engine/define-handler.js +1 -0
  209. package/dist/engine/entity-handlers.d.ts +13 -0
  210. package/dist/engine/entity-handlers.js +10 -21
  211. package/dist/engine/entity-list-runtime-options.d.ts +8 -0
  212. package/dist/engine/entity-list-runtime-options.js +18 -0
  213. package/dist/engine/feature-ast/extractors/handlers.js +1 -0
  214. package/dist/engine/feature-ast/extractors/index.d.ts +1 -1
  215. package/dist/engine/feature-ast/extractors/index.js +1 -1
  216. package/dist/engine/feature-ast/extractors/round1.d.ts +2 -1
  217. package/dist/engine/feature-ast/extractors/round1.js +6 -0
  218. package/dist/engine/feature-ast/extractors/round2.js +25 -6
  219. package/dist/engine/feature-ast/index.d.ts +1 -1
  220. package/dist/engine/feature-ast/parse.js +3 -1
  221. package/dist/engine/feature-ast/patch.d.ts +2 -0
  222. package/dist/engine/feature-ast/patch.js +5 -182
  223. package/dist/engine/feature-ast/pattern-change-schema.d.ts +10 -245
  224. package/dist/engine/feature-ast/pattern-change-schema.js +10 -163
  225. package/dist/engine/feature-ast/pattern-id-matchers.d.ts +7 -0
  226. package/dist/engine/feature-ast/pattern-id-matchers.js +211 -0
  227. package/dist/engine/feature-ast/pattern-id-schema.d.ts +251 -0
  228. package/dist/engine/feature-ast/pattern-id-schema.js +166 -0
  229. package/dist/engine/feature-ast/patterns.d.ts +14 -1
  230. package/dist/engine/feature-ast/patterns.js +44 -51
  231. package/dist/engine/feature-ast/render.js +52 -87
  232. package/dist/engine/feature-builder-state.d.ts +1 -0
  233. package/dist/engine/feature-builder-state.js +1 -0
  234. package/dist/engine/feature-entity-handlers.js +3 -0
  235. package/dist/engine/feature-manifest.d.ts +1 -0
  236. package/dist/engine/feature-manifest.js +1 -0
  237. package/dist/engine/field-access.d.ts +1 -0
  238. package/dist/engine/field-access.js +12 -0
  239. package/dist/engine/form-derivations.js +18 -7
  240. package/dist/engine/handler-field-references.js +1 -1
  241. package/dist/engine/index.d.ts +2 -2
  242. package/dist/engine/index.js +1 -1
  243. package/dist/engine/pattern-library/library.js +2 -1
  244. package/dist/engine/pattern-library/static-schemas.d.ts +1 -0
  245. package/dist/engine/pattern-library/static-schemas.js +9 -0
  246. package/dist/engine/project-app-schema-for-roles.d.ts +2 -0
  247. package/dist/engine/project-app-schema-for-roles.js +6 -4
  248. package/dist/engine/registry-ingest.js +1 -1
  249. package/dist/engine/schema-builder.js +228 -212
  250. package/dist/engine/screen-filter-ops.js +2 -0
  251. package/dist/engine/screen-helpers.js +1 -1
  252. package/dist/engine/screen-kind.d.ts +7 -0
  253. package/dist/engine/screen-kind.js +1 -0
  254. package/dist/engine/strip-disabled-features-from-app-schema.js +58 -17
  255. package/dist/engine/types/feature.d.ts +1 -0
  256. package/dist/engine/types/index.d.ts +4 -3
  257. package/dist/engine/types/screen.d.ts +1 -1
  258. package/dist/engine/types/screen.js +1 -1
  259. package/dist/engine/validate-projection-allowlist.js +2 -1
  260. package/dist/entrypoint/index.d.ts +6 -1
  261. package/dist/entrypoint/index.js +10 -1
  262. package/dist/errors/docs-url.d.ts +1 -1
  263. package/dist/errors/docs-url.js +22 -2
  264. package/dist/es-ops/context.js +1 -1
  265. package/dist/event-store/admin-api.js +1 -1
  266. package/dist/event-store/event-store.d.ts +1 -0
  267. package/dist/event-store/event-store.js +17 -8
  268. package/dist/event-store/provenance-append.js +1 -1
  269. package/dist/files/bind-pending-file-refs.js +3 -3
  270. package/dist/files/create-file-ref-from-bytes.js +7 -1
  271. package/dist/files/create-file-ref.d.ts +3 -0
  272. package/dist/files/create-file-ref.js +11 -2
  273. package/dist/files/file-handle.js +2 -3
  274. package/dist/files/file-routes.d.ts +2 -0
  275. package/dist/files/file-routes.js +40 -10
  276. package/dist/files/provider-resolver.d.ts +1 -0
  277. package/dist/files/provider-resolver.js +1 -1
  278. package/dist/files/record-write-check.d.ts +6 -0
  279. package/dist/files/record-write-check.js +13 -0
  280. package/dist/files/resolve-content-type.js +43 -0
  281. package/dist/files/upload-ledger.d.ts +2 -2
  282. package/dist/files/upload-ledger.js +11 -4
  283. package/dist/i18n/required-surface-keys.js +189 -208
  284. package/dist/internal/stack-support.d.ts +2 -1
  285. package/dist/internal/stack-support.js +1 -1
  286. package/dist/internal/testing.js +1 -1
  287. package/dist/jobs/job-runner-context.d.ts +32 -0
  288. package/dist/jobs/job-runner-context.js +305 -0
  289. package/dist/jobs/job-runner-execution.d.ts +7 -0
  290. package/dist/jobs/job-runner-execution.js +160 -0
  291. package/dist/jobs/job-runner-gates.d.ts +19 -0
  292. package/dist/jobs/job-runner-gates.js +198 -0
  293. package/dist/jobs/job-runner-helpers.d.ts +17 -3
  294. package/dist/jobs/job-runner-helpers.js +61 -2
  295. package/dist/jobs/job-runner-scope.d.ts +44 -0
  296. package/dist/jobs/job-runner-scope.js +102 -0
  297. package/dist/jobs/job-runner-types.d.ts +12 -0
  298. package/dist/jobs/job-runner.d.ts +0 -1
  299. package/dist/jobs/job-runner.js +50 -657
  300. package/dist/observability/escape-hatch-report.d.ts +3 -0
  301. package/dist/observability/escape-hatch-report.js +21 -3
  302. package/dist/pipeline/after-rollback-sink.d.ts +6 -0
  303. package/dist/pipeline/after-rollback-sink.js +32 -0
  304. package/dist/pipeline/cascade-handler.js +165 -138
  305. package/dist/pipeline/dispatch-batch.js +8 -1
  306. package/dist/pipeline/dispatch-query.js +10 -3
  307. package/dist/pipeline/dispatch-shared.d.ts +13 -3
  308. package/dist/pipeline/dispatch-shared.js +62 -710
  309. package/dist/pipeline/dispatch-write.js +296 -189
  310. package/dist/pipeline/event-dispatcher-admin.js +2 -1
  311. package/dist/pipeline/event-dispatcher-delivery.js +6 -6
  312. package/dist/pipeline/event-dispatcher.d.ts +1 -0
  313. package/dist/pipeline/event-dispatcher.js +6 -9
  314. package/dist/pipeline/handler-context-auth.d.ts +9 -0
  315. package/dist/pipeline/handler-context-auth.js +54 -0
  316. package/dist/pipeline/handler-context-config.d.ts +14 -0
  317. package/dist/pipeline/handler-context-config.js +113 -0
  318. package/dist/pipeline/handler-context-db.d.ts +24 -0
  319. package/dist/pipeline/handler-context-db.js +149 -0
  320. package/dist/pipeline/handler-context-dispatch.d.ts +23 -0
  321. package/dist/pipeline/handler-context-dispatch.js +101 -0
  322. package/dist/pipeline/handler-context-events.d.ts +41 -0
  323. package/dist/pipeline/handler-context-events.js +239 -0
  324. package/dist/pipeline/handler-context-notify.d.ts +12 -0
  325. package/dist/pipeline/handler-context-notify.js +55 -0
  326. package/dist/pipeline/handler-context-observability.d.ts +7 -0
  327. package/dist/pipeline/handler-context-observability.js +21 -0
  328. package/dist/pipeline/handler-context-scope.d.ts +48 -0
  329. package/dist/pipeline/handler-context-scope.js +88 -0
  330. package/dist/pipeline/notify-scope.js +12 -1
  331. package/dist/pipeline/pre-handler-gates.js +20 -4
  332. package/dist/pipeline/pre-transaction-reservations.d.ts +1 -0
  333. package/dist/pipeline/pre-transaction-reservations.js +9 -3
  334. package/dist/pipeline/projection-rebuild.js +2 -1
  335. package/dist/pipeline/system-hooks.d.ts +3 -1
  336. package/dist/pipeline/system-hooks.js +55 -6
  337. package/dist/pipeline/write-result-masking.js +13 -10
  338. package/dist/schema-cli/apply-command.d.ts +2 -0
  339. package/dist/schema-cli/apply-command.js +97 -0
  340. package/dist/schema-cli/baseline-command.d.ts +2 -0
  341. package/dist/schema-cli/baseline-command.js +33 -0
  342. package/dist/schema-cli/generate-command.d.ts +2 -0
  343. package/dist/schema-cli/generate-command.js +90 -0
  344. package/dist/schema-cli/shared.d.ts +39 -0
  345. package/dist/schema-cli/shared.js +95 -0
  346. package/dist/schema-cli/status-command.d.ts +2 -0
  347. package/dist/schema-cli/status-command.js +73 -0
  348. package/dist/schema-cli/validate-boot-input.d.ts +5 -0
  349. package/dist/schema-cli/validate-boot-input.js +130 -0
  350. package/dist/schema-cli/validate-command.d.ts +2 -0
  351. package/dist/schema-cli/validate-command.js +130 -0
  352. package/dist/schema-cli.d.ts +2 -18
  353. package/dist/schema-cli.js +47 -604
  354. package/dist/search/meilisearch-adapter.js +2 -1
  355. package/dist/search/reindex-entity.js +15 -5
  356. package/dist/ui-types/index.d.ts +2 -2
  357. package/dist/ui-types/index.js +1 -1
  358. package/dist/upgrade-changelog.d.ts +2 -0
  359. package/dist/upgrade-changelog.js +14 -0
  360. package/dist/upgrade-cli.d.ts +3 -12
  361. package/dist/upgrade-cli.js +171 -197
  362. package/dist/upgrade-marker-parse.d.ts +32 -0
  363. package/dist/upgrade-marker-parse.js +73 -0
  364. package/package.json +9 -6
  365. package/src/changes.json +555 -0
  366. package/src/scripts/codemod/codemod-support.ts +1 -1
  367. package/src/scripts/codemod/db-factory.ts +1 -1
  368. package/src/scripts/codemod/feature-layout.ts +10 -2
  369. package/src/scripts/codemod/object-form.ts +41 -4
  370. package/src/scripts/codemod/package-merge.ts +58 -8
  371. package/src/scripts/codemod/pii-personal-migration.ts +175 -145
  372. package/src/scripts/codemod/testing-injection.ts +122 -42
  373. package/src/scripts/codemod/testing-move.ts +29 -7
  374. package/dist/engine/provider-plugin-context.d.ts +0 -3
  375. package/dist/engine/provider-plugin-context.js +0 -28
@@ -34,7 +34,8 @@ function isSubsetOf(stored, wanted) {
34
34
  }
35
35
  // A stored list that lies inside the current default and misses attributes is an
36
36
  // older default that gained fields since; anything with a foreign attribute is
37
- // an explicit configure() and stays untouched.
37
+ // an explicit configure() and stays untouched. Known limit: an explicit configure() that
38
+ // narrowed to a strict subset of the default is indistinguishable and gets widened again.
38
39
  function isOutdatedDefault(stored, wanted) {
39
40
  const pairs = [
40
41
  [stored.searchable, wanted.searchable],
@@ -6,7 +6,7 @@
6
6
  // existing row and indexes it, same as a live write would.
7
7
  import { resolveTableName } from "../db/entity-table-meta.js";
8
8
  import { executeRawQueryRead } from "../db/queries/raw-sql.js";
9
- import { withoutStatementTimeout } from "../db/statement-timeout.js";
9
+ import { runInNewTransaction, runnerCanBeginTransaction } from "../db/query.js";
10
10
  import { buildSearchDocument, decryptSearchableSubjectFields, hasErasedSearchableSubjectField, } from "../pipeline/system-hooks.js";
11
11
  import { toSnakeCase } from "../utils/case.js";
12
12
  function quoteIdent(name) {
@@ -26,10 +26,20 @@ function rowToState(row, fieldNames) {
26
26
  }
27
27
  return state;
28
28
  }
29
- export function reindexEntity(db, registry, searchAdapter, entityName, tenantId, options = {}) {
30
- return withoutStatementTimeout(db, (unboundedDb) => scanAndIndexEntity(unboundedDb, registry, searchAdapter, entityName, tenantId, options));
29
+ // Server pools cap statement_timeout, and a batch scan over a large table can outlast it. The caller's
30
+ // runner (often a gated proxy around the pool) is used as is, so the cap is lifted per batch
31
+ // with a transaction-local set_config instead of moving the scan to an ungated sibling pool. A runner already inside a
32
+ // transaction keeps the caller's setting rather than leaking ours into its remaining statements.
33
+ async function readBatchWithoutStatementTimeout(db, sqlText, params) {
34
+ if (!runnerCanBeginTransaction(db)) {
35
+ return executeRawQueryRead(db, sqlText, params);
36
+ }
37
+ return runInNewTransaction(db, async (tx) => {
38
+ await executeRawQueryRead(tx, "SELECT set_config('statement_timeout', '0', true)", []);
39
+ return executeRawQueryRead(tx, sqlText, params);
40
+ });
31
41
  }
32
- async function scanAndIndexEntity(db, registry, searchAdapter, entityName, tenantId, options) {
42
+ export async function reindexEntity(db, registry, searchAdapter, entityName, tenantId, options = {}) {
33
43
  const entity = registry.getEntity(entityName);
34
44
  if (!entity) {
35
45
  throw new Error(`reindexEntity: unknown entity "${entityName}"`);
@@ -61,7 +71,7 @@ async function scanAndIndexEntity(db, registry, searchAdapter, entityName, tenan
61
71
  for (;;) {
62
72
  // Keyset, not OFFSET: rows deleted or changed while the loop runs must not shift the window.
63
73
  const afterLastId = lastId === undefined ? "" : `AND ${quoteIdent("id")} > $3`;
64
- const rows = await executeRawQueryRead(db, `SELECT * FROM ${quoteIdent(tableName)}
74
+ const rows = await readBatchWithoutStatementTimeout(db, `SELECT * FROM ${quoteIdent(tableName)}
65
75
  WHERE ${quoteIdent("tenant_id")} = $1 ${deletedFilter} ${afterLastId}
66
76
  ORDER BY ${quoteIdent("id")} ASC
67
77
  LIMIT $2`, lastId === undefined ? [tenantId, batchSize] : [tenantId, batchSize, lastId]);
@@ -11,8 +11,8 @@ export type { BooleanFieldDef, DateFieldDef, EmbeddedDerivedCellDef, EntityDefin
11
11
  export type { AccessRule, OpenToAllAccessRule, OpenToAllDeclaration, OpenToAllPersonalData, RoleAccessPersonalData, RoleAccessRule, } from "../engine/types/handlers.ts";
12
12
  export { isOpenToAllGranted, isUiAccessGranted } from "../engine/types/handlers.ts";
13
13
  export type { IconKey, NavDefinition, NavIconKey } from "../engine/types/nav.ts";
14
- export type { ActionFormFooterAction, ActionFormLoad, ActionFormRedirect, ActionFormScreenDefinition, ActionFormSuccessFact, ActionFormSuccessLink, ConfigEditScreenDefinition, CustomScreenDefinition, CustomScreenRoute, DashboardChartKind, DashboardChartMarkerKind, DashboardChartPanel, DashboardChartRangeOption, DashboardChartRanges, DashboardChartTone, DashboardCurrencyFormat, DashboardCustomPanel, DashboardDateParam, DashboardFeedPanel, DashboardFilterDefinition, DashboardI18nText, DashboardListPanel, DashboardMoneyParam, DashboardPanelDefinition, DashboardPanelEmptyState, DashboardPanelGate, DashboardPanelQueryOptions, DashboardPanelSpan, DashboardPanelVisibility, DashboardProgressListPanel, DashboardProgressTone, DashboardRecordBinding, DashboardRefresh, DashboardRelativeTimeFormat, DashboardRelativeTimeParam, DashboardScopeDefinition, DashboardScreenDefinition, DashboardScreenPanel, DashboardStatGroupPanel, DashboardStatPanel, DashboardText, DashboardTextBlock, DashboardTextPanel, DashboardTextParam, DashboardTimeRangeDefinition, DashboardValueFormat, EditExtensionSection, EditFieldPrefill, EditFieldSpec, EditFieldsSection, EditLayout, EditRelatedListSection, EditSectionSpec, EditWriteFormSection, EmbeddedListEditSpec, EntityEditAction, EntityEditActionPlacement, EntityEditScreenDefinition, EntityListExpandableRow, EntityListFacetConfig, EntityListFacetExtraOption, EntityListScreenDefinition, EntityWriteExcludedFields, FieldAffix, FieldCondition, FieldFillAction, FieldIconKey, FieldRenderer, FormWidth, ListBadgeTone, ListColumnDisplay, ListColumnSpec, ListEmptyStateSpec, ListFacetSpec, ListSortSpec, MetricNavigate, MetricSpec, PlatformComponent, ProjectionDetailScreenDefinition, ProjectionListScreenDefinition, RecordHeaderSubtitlePart, RelatedListGroupBy, RelatedListRowTone, RelatedListToolbarAction, RowAction, RowActionClientAction, RowActionDisplay, RowActionDrawer, RowActionMode, RowActionNavigate, RowActionWriteHandler, RowFieldExtractor, ScreenDefinition, ScreenFilter, ScreenFilterOp, ScreenSlots, SecretMintConfirmStep, SecretMintScreenDefinition, SecretReveal, SecretRevealField, SecretsEditScreenDefinition, SecretsEditSection, ToolbarAction, } from "../engine/types/screen.ts";
15
- export { relatedListGroupHeaderLabel, relatedListGroupKey, } from "../engine/types/screen.ts";
14
+ export type { ActionFormFooterAction, ActionFormLoad, ActionFormRedirect, ActionFormScreenDefinition, ActionFormSuccessFact, ActionFormSuccessLink, ConfigEditScreenDefinition, CustomScreenDefinition, CustomScreenRoute, DashboardChartKind, DashboardChartMarkerKind, DashboardChartPanel, DashboardChartRangeOption, DashboardChartRanges, DashboardChartTone, DashboardCurrencyFormat, DashboardCustomPanel, DashboardDateParam, DashboardFeedPanel, DashboardFilterDefinition, DashboardI18nText, DashboardListPanel, DashboardMoneyParam, DashboardPanelDefinition, DashboardPanelEmptyState, DashboardPanelGate, DashboardPanelQueryOptions, DashboardPanelSpan, DashboardPanelVisibility, DashboardProgressListPanel, DashboardProgressTone, DashboardRecordBinding, DashboardRefresh, DashboardRelativeTimeFormat, DashboardRelativeTimeParam, DashboardScopeDefinition, DashboardScreenDefinition, DashboardScreenPanel, DashboardStatGroupPanel, DashboardStatPanel, DashboardText, DashboardTextBlock, DashboardTextPanel, DashboardTextParam, DashboardTimeRangeDefinition, DashboardValueFormat, EditExtensionSection, EditFieldPrefill, EditFieldSpec, EditFieldsSection, EditLayout, EditRelatedListSection, EditSectionSpec, EditWriteFormSection, EmbeddedListEditSpec, EntityEditAction, EntityEditActionPlacement, EntityEditScreenDefinition, EntityListExpandableRow, EntityListFacetConfig, EntityListFacetExtraOption, EntityListScreenDefinition, EntityWriteExcludedFields, ExpandableRowGrouping, FieldAffix, FieldCondition, FieldFillAction, FieldIconKey, FieldRenderer, FormWidth, ListBadgeTone, ListBulkAction, ListColumnDisplay, ListColumnSpec, ListEmptyStateSpec, ListFacetSpec, ListSortSpec, ListTableDensity, ListTableOptions, MetricNavigate, MetricSpec, PlatformComponent, ProjectionDetailScreenDefinition, ProjectionListScreenDefinition, RecordHeaderSubtitlePart, RelatedListRowTone, RelatedListToolbarAction, RowAction, RowActionClientAction, RowActionDisplay, RowActionDrawer, RowActionMode, RowActionNavigate, RowActionWriteHandler, RowFieldExtractor, ScreenDefinition, ScreenFilter, ScreenFilterOp, ScreenSlots, SecretMintConfirmStep, SecretMintScreenDefinition, SecretReveal, SecretRevealField, SecretsEditScreenDefinition, SecretsEditSection, ToolbarAction, } from "../engine/types/screen.ts";
15
+ export { MAX_BULK_SELECTION, relatedListGroupHeaderLabel, relatedListGroupKey, } from "../engine/types/screen.ts";
16
16
  export type { TargetRef } from "../engine/types/target-ref.ts";
17
17
  export type { TreeAction, TreeNode, TreeNodeState } from "../engine/types/tree-node.ts";
18
18
  export type { WorkspaceDefinition } from "../engine/types/workspace.ts";
@@ -25,7 +25,7 @@ export { applyFormDerivations, isFormDerivationInputMode, isUnusedFallbackOperan
25
25
  export { parseRefTarget } from "../engine/parse-ref-target.js";
26
26
  export { ACTION_FORM_PSEUDO_ENTITY, availableSelectOptions, evalFieldCondition, explicitListScreenId, findUnavailableSelectOptions, isExtensionEditSection, isFieldsEditSection, isFormatSpec, isRecordLocked, isUnchangedValue, isWriteFormEditSection, normalizeEditField, normalizeListColumn, resolveNavParentScreen, sectionFieldSpecs, } from "../engine/screen-helpers.js";
27
27
  export { isOpenToAllGranted, isUiAccessGranted } from "../engine/types/handlers.js";
28
- export { relatedListGroupHeaderLabel, relatedListGroupKey, } from "../engine/types/screen.js";
28
+ export { MAX_BULK_SELECTION, relatedListGroupHeaderLabel, relatedListGroupKey, } from "../engine/types/screen.js";
29
29
  export { ACTION_FORM_ENTITY, handlerFieldLabelKey, handlerTitleKey, PROJECTION_DETAIL_ENTITY, WRITE_FORM_SECTION_ENTITY, } from "../i18n/required-surface-keys.js";
30
30
  export { CARD_META_MAX, cardColumnRoles, cardFileDisplayKind } from "./card-column-roles.js";
31
31
  export { formalLocaleTag } from "./formality.js";
@@ -23,3 +23,5 @@ export type UpgradeChangelogSection = {
23
23
  export declare function renderUpgradeSection(section: UpgradeChangelogSection): string;
24
24
  /** Puts the section on top; a section for the same target version is replaced, so a re-run stays idempotent. */
25
25
  export declare function mergeUpgradeSection(existing: string | undefined, section: UpgradeChangelogSection): string;
26
+ /** Pure string match on the rendered `- [open] <id> · ` prefix, in every section. */
27
+ export declare function setManualStatusInChangelog(content: string, ids: ReadonlySet<string>, resolution: "migrated" | "not-applicable"): string;
@@ -46,3 +46,17 @@ export function mergeUpgradeSection(existing, section) {
46
46
  const others = sections.filter((candidate) => !candidate.startsWith(prefix));
47
47
  return [header, renderUpgradeSection(section), ...others].join("\n");
48
48
  }
49
+ /** Pure string match on the rendered `- [open] <id> · ` prefix, in every section. */
50
+ export function setManualStatusInChangelog(content, ids, resolution) {
51
+ return content
52
+ .split("\n")
53
+ .map((line) => {
54
+ for (const id of ids) {
55
+ const open = `- [open] ${id} · `;
56
+ if (line.startsWith(open))
57
+ return `- [${resolution}] ${id} · ${line.slice(open.length)}`;
58
+ }
59
+ return line;
60
+ })
61
+ .join("\n");
62
+ }
@@ -1,3 +1,6 @@
1
+ import { type ManualResolution, manualEntryId, type UpgradeMarkerManual, type UpgradeMarkerResolvedManual } from "./upgrade-marker-parse.ts";
2
+ export type { ManualResolution, UpgradeMarkerManual, UpgradeMarkerResolvedManual };
3
+ export { manualEntryId };
1
4
  export type UpgradeCliOut = {
2
5
  readonly log: (line: string) => void;
3
6
  readonly err: (line: string) => void;
@@ -7,18 +10,6 @@ export declare function findPackageChangelogFiles(cwd: string): string[];
7
10
  export declare function findFeaturesDirs(cwd: string): string[];
8
11
  export declare function findCodemodScriptsRoot(repoRoot: string): string | null;
9
12
  export declare function resolveCodemodScript(repoRoot: string, codemodField: string | undefined): string | null;
10
- export type UpgradeMarkerManual = {
11
- readonly id: string;
12
- readonly version: string;
13
- readonly title: string;
14
- };
15
- export type ManualResolution = "migrated" | "not-applicable";
16
- export type UpgradeMarkerResolvedManual = UpgradeMarkerManual & {
17
- readonly resolution: ManualResolution;
18
- readonly reason: string;
19
- readonly resolvedAt: string;
20
- };
21
- export declare function manualEntryId(version: string, title: string): string;
22
13
  export declare function runUpgradeCli(argv: readonly string[], cwd: string, out: UpgradeCliOut, options?: {
23
14
  readonly repoRoot?: string;
24
15
  }): Promise<number>;
@@ -1,12 +1,12 @@
1
- import { createHash } from "node:crypto";
2
1
  import { existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, statSync, writeFileSync, } from "node:fs";
3
2
  import { dirname, isAbsolute, join, relative, resolve } from "node:path";
4
3
  import { propertyOf } from "@cosmicdrift/kumiko-types/type-guards";
5
4
  import { getFlag, getStringFlag, parseArgs } from "./arg-parser.js";
6
5
  import { compareVersions, filterEntriesAfter, parseFeatureChangelog, sortEntries, } from "./engine/index.js";
7
6
  import { ensureTemporalPolyfill } from "./time/index.js";
8
- import { mergeUpgradeSection } from "./upgrade-changelog.js";
9
- const SEMVER_RE = /^\d+\.\d+\.\d+$/;
7
+ import { mergeUpgradeSection, setManualStatusInChangelog, } from "./upgrade-changelog.js";
8
+ import { dedupeManualById, manualEntryId, parseStoredMarker, SEMVER_RE, } from "./upgrade-marker-parse.js";
9
+ export { manualEntryId };
10
10
  const SEMVER_CORE_RE = /^(\d+\.\d+\.\d+)(?:-[0-9A-Za-z.-]+)?$/;
11
11
  const CODEMOD_SUBDIR = "scripts/codemod";
12
12
  function readPackageJsonVersion(packageJsonPath) {
@@ -304,13 +304,7 @@ function hasCodemod(e) {
304
304
  function needsManualMigration(e) {
305
305
  return !hasCodemod(e) || e.manualAfterCodemod === true;
306
306
  }
307
- const MANUAL_ID_HASH_LENGTH = 8;
308
307
  const MAX_RESOLVE_REASON_LENGTH = 500;
309
- // The hash tells apart entries that share a version (studio has several at 0.341.0).
310
- export function manualEntryId(version, title) {
311
- const hash = createHash("sha256").update(title).digest("hex").slice(0, MANUAL_ID_HASH_LENGTH);
312
- return `${version}:${hash}`;
313
- }
314
308
  // Breaking entries that need hand work carry the id `kumiko-upgrade --resolve` takes.
315
309
  function manualIdOf(entry) {
316
310
  return entry.type === "breaking" && needsManualMigration(entry)
@@ -324,15 +318,6 @@ function toMarkerManual(entry) {
324
318
  title: entry.title,
325
319
  };
326
320
  }
327
- function dedupeManualById(entries) {
328
- const seen = new Set();
329
- return entries.filter((entry) => {
330
- if (seen.has(entry.id))
331
- return false;
332
- seen.add(entry.id);
333
- return true;
334
- });
335
- }
336
321
  // Open manual items survive every marker write until --resolve moves them.
337
322
  function carryOverManual(previous, newManual) {
338
323
  const resolvedIds = new Set(previous.resolvedManual.map((entry) => entry.id));
@@ -360,64 +345,6 @@ function writeUpgradeMarker(targetDir, marker) {
360
345
  writeFileSync(join(dir, "upgrade-state.json"), `${JSON.stringify(marker, null, 2)}\n`, "utf-8");
361
346
  }
362
347
  const NO_CARRY = { codemods: [], pendingManual: [], resolvedManual: [] };
363
- function isRecord(value) {
364
- return typeof value === "object" && value !== null && !Array.isArray(value);
365
- }
366
- function parseManualEntry(value) {
367
- if (!isRecord(value))
368
- return null;
369
- const { id, version, title } = value;
370
- if (typeof version !== "string" || typeof title !== "string")
371
- return null;
372
- return {
373
- id: typeof id === "string" && id.length > 0 ? id : manualEntryId(version, title),
374
- version,
375
- title,
376
- };
377
- }
378
- function parseResolvedEntry(value) {
379
- const base = parseManualEntry(value);
380
- if (base === null || !isRecord(value))
381
- return null;
382
- const { resolution, reason, resolvedAt } = value;
383
- if (resolution !== "migrated" && resolution !== "not-applicable")
384
- return null;
385
- if (typeof reason !== "string" || typeof resolvedAt !== "string")
386
- return null;
387
- return { ...base, resolution, reason, resolvedAt };
388
- }
389
- function parseList(value, parseEntry) {
390
- if (value === undefined)
391
- return [];
392
- if (!Array.isArray(value))
393
- return null;
394
- const parsed = value.map(parseEntry);
395
- return parsed.every((entry) => entry !== null) ? parsed : null;
396
- }
397
- function isMarkerCodemod(value) {
398
- return (isRecord(value) &&
399
- typeof value["version"] === "string" &&
400
- typeof value["codemod"] === "string" &&
401
- typeof value["title"] === "string");
402
- }
403
- function parseStoredMarker(raw) {
404
- if (!isRecord(raw))
405
- return null;
406
- const { version, appliedAt, codemods } = raw;
407
- if (typeof version !== "string" || !SEMVER_RE.test(version))
408
- return null;
409
- const pendingManual = parseList(raw["pendingManual"], parseManualEntry);
410
- const resolvedManual = parseList(raw["resolvedManual"], parseResolvedEntry);
411
- if (pendingManual === null || resolvedManual === null)
412
- return null;
413
- return {
414
- version,
415
- appliedAt: typeof appliedAt === "string" ? appliedAt : Temporal.Now.instant().toString(),
416
- codemods: Array.isArray(codemods) ? codemods.filter(isMarkerCodemod) : [],
417
- pendingManual: dedupeManualById(pendingManual),
418
- resolvedManual: dedupeManualById(resolvedManual),
419
- };
420
- }
421
348
  // A missing marker is a bootstrap app that never ran `--apply`; an
422
349
  // existing but unreadable one must not silently fall back to the installed
423
350
  // version, or a bare `--apply` would skip every codemod in between.
@@ -659,27 +586,27 @@ function matchManualRefs(open, refs) {
659
586
  }
660
587
  return { resolvedIds, errors };
661
588
  }
589
+ function failUpgrade(out, ...lines) {
590
+ out.err("");
591
+ for (const line of lines)
592
+ out.err(` ${line}`);
593
+ out.err("");
594
+ return 1;
595
+ }
662
596
  function resolveManualMigrations(out, args, targetDir) {
663
- const fail = (...lines) => {
664
- out.err("");
665
- for (const line of lines)
666
- out.err(` ${line}`);
667
- out.err("");
668
- return 1;
669
- };
670
597
  const input = parseResolveInput(args);
671
598
  if ("error" in input)
672
- return fail(input.error);
599
+ return failUpgrade(out, input.error);
673
600
  const read = readMarker(targetDir);
674
601
  if (read.kind === "missing") {
675
- return fail(`No .kumiko/upgrade-state.json under ${targetDir}, nothing to resolve.`);
602
+ return failUpgrade(out, `No .kumiko/upgrade-state.json under ${targetDir}, nothing to resolve.`);
676
603
  }
677
604
  if (read.kind === "invalid")
678
- return fail("invalid .kumiko/upgrade-state.json, fix it first.");
605
+ return failUpgrade(out, "invalid .kumiko/upgrade-state.json, fix it first.");
679
606
  const open = read.marker.pendingManual;
680
607
  const { resolvedIds, errors } = matchManualRefs(open, input.refs);
681
608
  if (errors.length > 0)
682
- return fail(...errors, "Nothing was written.");
609
+ return failUpgrade(out, ...errors, "Nothing was written.");
683
610
  const { reason } = input;
684
611
  const resolution = getFlag(args, "not-applicable")
685
612
  ? "not-applicable"
@@ -697,6 +624,11 @@ function resolveManualMigrations(out, args, targetDir) {
697
624
  ...(remaining.length > 0 && { pendingManual: remaining }),
698
625
  resolvedManual,
699
626
  });
627
+ const changelogPath = join(targetDir, "CHANGELOG.md");
628
+ if (existsSync(changelogPath)) {
629
+ const content = readFileSync(changelogPath, "utf-8");
630
+ writeFileSync(changelogPath, setManualStatusInChangelog(content, resolvedIds, resolution), "utf-8");
631
+ }
700
632
  out.log("");
701
633
  out.log(` ✓ Marked ${newlyResolved.length} manual migration(s) as ${resolution}:`);
702
634
  for (const entry of newlyResolved) {
@@ -721,32 +653,7 @@ kumiko-upgrade --resolve <id|version>[,<id|version>...] --reason "<text>" [--not
721
653
  --not-applicable with --resolve, record the entries as not applicable instead of migrated
722
654
  --dry-run with --apply, report what would run without writing anything
723
655
  --help, -h print this usage and exit`;
724
- // kumiko-lint-ignore complexity-budget CLI orchestration moved from bin/commands/upgrade.ts — same branching surface, shared by kumiko upgrade + published kumiko-upgrade bin
725
- export async function runUpgradeCli(argv, cwd, out, options) {
726
- if (argv.includes("--help") || argv.includes("-h")) {
727
- out.log(UPGRADE_USAGE);
728
- return 0;
729
- }
730
- // Standalone CLI entry, not booted via runProdApp/runDevApp — Temporal needs an explicit polyfill here.
731
- await ensureTemporalPolyfill();
732
- const repoRoot = options?.repoRoot ?? cwd;
733
- const args = parseArgs(argv);
734
- const jsonMode = getFlag(args, "json");
735
- const verbose = getFlag(args, "verbose");
736
- const fromFlag = getStringFlag(args, "from");
737
- // Resolved once here (not just inside --apply) so both the filter
738
- // baseline below and the --apply codemod run target the same directory.
739
- const dirFlag = getStringFlag(args, "dir");
740
- const targetDir = dirFlag ? resolve(dirFlag) : cwd;
741
- if (!existsSync(targetDir) || !statSync(targetDir).isDirectory()) {
742
- out.err("");
743
- out.err(` --dir path is not a directory: ${targetDir}`);
744
- out.err("");
745
- return 1;
746
- }
747
- if (args.flags.has("resolve")) {
748
- return resolveManualMigrations(out, args, targetDir);
749
- }
656
+ function resolveUpgradeBaseline(cwd, targetDir, fromFlag) {
750
657
  // Always resolve the actually installed version, even when --from is set,
751
658
  // so --json can report both the filter baseline and what's really there.
752
659
  const installedVersion = readCurrentVersion(cwd);
@@ -761,33 +668,40 @@ export async function runUpgradeCli(argv, cwd, out, options) {
761
668
  const markerRead = readMarker(targetDir);
762
669
  const marker = fromFlag === undefined ? markerRead : { kind: "missing" };
763
670
  if (marker.kind === "invalid") {
764
- out.err("");
765
- out.err(" invalid .kumiko/upgrade-state.json, fix it or pass --from <version>");
766
- out.err("");
767
- return 1;
671
+ return {
672
+ kind: "error",
673
+ lines: ["invalid .kumiko/upgrade-state.json, fix it or pass --from <version>"],
674
+ };
768
675
  }
769
676
  const currentVersion = fromFlag ?? (marker.kind === "ok" ? marker.version : undefined) ?? installedVersion;
770
677
  if (!currentVersion) {
771
- out.err("");
772
- out.err(" Could not detect Kumiko version.");
773
- out.err(" Run from an app directory with node_modules, or use --from <version>.");
774
- out.err("");
775
- return 1;
678
+ return {
679
+ kind: "error",
680
+ lines: [
681
+ "Could not detect Kumiko version.",
682
+ "Run from an app directory with node_modules, or use --from <version>.",
683
+ ],
684
+ };
776
685
  }
777
686
  if (!SEMVER_RE.test(currentVersion)) {
778
- out.err("");
779
- out.err(` Invalid version format: "${currentVersion}" — expected x.y.z`);
780
- out.err("");
781
- return 1;
687
+ return {
688
+ kind: "error",
689
+ lines: [`Invalid version format: "${currentVersion}" — expected x.y.z`],
690
+ };
782
691
  }
692
+ return { kind: "ok", value: { currentVersion, installedVersion, markerRead } };
693
+ }
694
+ function collectAllChangelogEntries(cwd) {
783
695
  const featuresDirs = findFeaturesDirs(cwd);
784
696
  const packageChangelogFiles = findPackageChangelogFiles(cwd);
785
697
  if (featuresDirs.length === 0 && packageChangelogFiles.length === 0) {
786
- out.err("");
787
- out.err(" Could not find bundled-features directory.");
788
- out.err(" Run from framework/enterprise repo or an app with node_modules.");
789
- out.err("");
790
- return 1;
698
+ return {
699
+ kind: "error",
700
+ lines: [
701
+ "Could not find bundled-features directory.",
702
+ "Run from framework/enterprise repo or an app with node_modules.",
703
+ ],
704
+ };
791
705
  }
792
706
  const allEntries = [];
793
707
  for (const dir of featuresDirs) {
@@ -796,6 +710,18 @@ export async function runUpgradeCli(argv, cwd, out, options) {
796
710
  for (const changelogFile of packageChangelogFiles) {
797
711
  allEntries.push(...readChangelogFile(changelogFile));
798
712
  }
713
+ return { kind: "ok", value: allEntries };
714
+ }
715
+ function planUpgrade(args, cwd, targetDir) {
716
+ const dirFlag = getStringFlag(args, "dir");
717
+ const baseline = resolveUpgradeBaseline(cwd, targetDir, getStringFlag(args, "from"));
718
+ if (baseline.kind === "error")
719
+ return baseline;
720
+ const entries = collectAllChangelogEntries(cwd);
721
+ if (entries.kind === "error")
722
+ return entries;
723
+ const { currentVersion, installedVersion } = baseline.value;
724
+ const allEntries = entries.value;
799
725
  // Installed version of the target the marker belongs to (cwd unless --dir).
800
726
  const targetInstalledVersion = dirFlag ? readCurrentVersion(targetDir) : installedVersion;
801
727
  // Changelogs can be found in a parent workspace's newer install. A change
@@ -804,87 +730,135 @@ export async function runUpgradeCli(argv, cwd, out, options) {
804
730
  ? allEntries
805
731
  : allEntries.filter((entry) => compareVersions(entry.version, targetInstalledVersion) <= 0);
806
732
  const pending = sortEntries(filterEntriesAfter(installedEntries, currentVersion));
807
- if (getFlag(args, "apply")) {
808
- // Marker must reflect what is actually installed under the target (or
809
- // cwd), not the filter baseline above — otherwise CI stays green forever.
810
- const markerVersion = targetInstalledVersion;
811
- const dryRun = getFlag(args, "dry-run");
812
- if (markerVersion === null && !dryRun) {
813
- out.err("");
814
- out.err(` Could not detect the installed Kumiko version under ${targetDir}.`);
815
- out.err(" Refusing to write an upgrade marker without it (--from is only a filter).");
816
- out.err("");
817
- return 1;
733
+ return {
734
+ kind: "ok",
735
+ value: { ...baseline.value, targetInstalledVersion, allEntries, pending },
736
+ };
737
+ }
738
+ async function applyPlannedUpgrade(out, args, plan, repoRoot, targetDir) {
739
+ // Marker must reflect what is actually installed under the target (or
740
+ // cwd), not the filter baseline above — otherwise CI stays green forever.
741
+ const markerVersion = plan.targetInstalledVersion;
742
+ const dryRun = getFlag(args, "dry-run");
743
+ if (markerVersion === null && !dryRun) {
744
+ return failUpgrade(out, `Could not detect the installed Kumiko version under ${targetDir}.`, "Refusing to write an upgrade marker without it (--from is only a filter).");
745
+ }
746
+ out.log("");
747
+ const code = await applyCodemods(out, plan.pending, repoRoot, targetDir, dryRun, markerVersion ?? plan.currentVersion, carryOf(plan.markerRead), plan.currentVersion, getFlag(args, "verbose") ? manualDetailsOf(plan.allEntries) : undefined);
748
+ out.log("");
749
+ return code;
750
+ }
751
+ function reportUpgradeJson(out, plan) {
752
+ const { currentVersion, installedVersion } = plan;
753
+ const { pendingManual } = carryOf(plan.markerRead);
754
+ const pendingWithManualId = plan.pending.map((entry) => {
755
+ const manualId = manualIdOf(entry);
756
+ return manualId === undefined ? entry : { ...entry, manualId };
757
+ });
758
+ out.log(JSON.stringify({ currentVersion, installedVersion, pending: pendingWithManualId, pendingManual }, null, 2));
759
+ return 0;
760
+ }
761
+ function logEntryDetail(out, entry, verbose) {
762
+ if (verbose && entry.detail) {
763
+ out.log(` ${entry.detail}`);
764
+ }
765
+ }
766
+ function logBreakingSection(out, breaking, verbose) {
767
+ // skip: no breaking entries, the section is omitted
768
+ if (breaking.length === 0)
769
+ return;
770
+ out.log(` ⚠ BREAKING (${breaking.length})`);
771
+ out.log("");
772
+ for (const e of breaking) {
773
+ const manualId = manualIdOf(e);
774
+ out.log(` ${e.version} · ${e.title}${manualId === undefined ? "" : ` [${manualId}]`}`);
775
+ logEntryDetail(out, e, verbose);
776
+ if (e.migration) {
777
+ out.log(` → Migration: ${e.migration}`);
818
778
  }
819
779
  out.log("");
820
- const code = await applyCodemods(out, pending, repoRoot, targetDir, dryRun, markerVersion ?? currentVersion, carryOf(markerRead), currentVersion, verbose ? manualDetailsOf(allEntries) : undefined);
821
- out.log("");
822
- return code;
823
- }
824
- if (jsonMode) {
825
- const { pendingManual } = carryOf(markerRead);
826
- const pendingWithManualId = pending.map((entry) => {
827
- const manualId = manualIdOf(entry);
828
- return manualId === undefined ? entry : { ...entry, manualId };
829
- });
830
- out.log(JSON.stringify({ currentVersion, installedVersion, pending: pendingWithManualId, pendingManual }, null, 2));
831
- return 0;
832
780
  }
781
+ }
782
+ function logNonBreakingSection(out, heading, entries, verbose, showCodemod) {
783
+ // skip: no entries of this type, the section is omitted
784
+ if (entries.length === 0)
785
+ return;
786
+ out.log(` ✓ ${heading} (${entries.length})`);
787
+ out.log("");
788
+ for (const e of entries) {
789
+ out.log(` ${e.version} · ${e.title}`);
790
+ logEntryDetail(out, e, verbose);
791
+ if (showCodemod && hasCodemod(e)) {
792
+ out.log(` → codemod: ${e.codemod} (runs on --apply)`);
793
+ }
794
+ }
795
+ out.log("");
796
+ }
797
+ function logApplyHint(out, plan) {
798
+ const { markerRead, targetInstalledVersion } = plan;
799
+ const markerBehindInstalled = markerRead.kind === "ok" &&
800
+ targetInstalledVersion !== null &&
801
+ compareVersions(markerRead.version, targetInstalledVersion) < 0;
802
+ // skip: nothing differs between marker and installed version, so there is nothing to record.
803
+ if (plan.pending.length === 0 && !markerBehindInstalled)
804
+ return;
805
+ out.log(" Nothing is recorded yet: the upgrade CHANGELOG and the marker are only");
806
+ out.log(" written by --apply (add --dry-run to preview without writing).");
807
+ out.log("");
808
+ }
809
+ function reportUpgradeText(out, plan, verbose) {
810
+ const { pending } = plan;
833
811
  const breaking = pending.filter((e) => e.type === "breaking");
834
- const improvements = pending.filter((e) => e.type === "improvement");
835
- const fixes = pending.filter((e) => e.type === "fix");
836
- const openManual = carryOf(markerRead).pendingManual;
812
+ const openManual = carryOf(plan.markerRead).pendingManual;
813
+ const manualDetails = verbose ? manualDetailsOf(plan.allEntries) : undefined;
837
814
  out.log("");
838
- out.log(` Upgrade: ${currentVersion} → latest`);
815
+ out.log(` Upgrade: ${plan.currentVersion} → latest`);
839
816
  out.log("");
840
817
  if (pending.length === 0) {
841
818
  out.log(" ✓ Nothing new since your version.");
842
819
  out.log("");
843
- logOpenManual(out, openManual, verbose ? manualDetailsOf(allEntries) : undefined);
820
+ logApplyHint(out, plan);
821
+ logOpenManual(out, openManual, manualDetails);
844
822
  return 0;
845
823
  }
846
- if (breaking.length > 0) {
847
- out.log(` ⚠ BREAKING (${breaking.length})`);
848
- out.log("");
849
- for (const e of breaking) {
850
- const manualId = manualIdOf(e);
851
- out.log(` ${e.version} · ${e.title}${manualId === undefined ? "" : ` [${manualId}]`}`);
852
- if (verbose && e.detail) {
853
- out.log(` ${e.detail}`);
854
- }
855
- if (e.migration) {
856
- out.log(` → Migration: ${e.migration}`);
857
- }
858
- out.log("");
859
- }
860
- }
861
- if (improvements.length > 0) {
862
- out.log(` ✓ IMPROVEMENTS (${improvements.length})`);
863
- out.log("");
864
- for (const e of improvements) {
865
- out.log(` ${e.version} · ${e.title}`);
866
- if (verbose && e.detail) {
867
- out.log(` ${e.detail}`);
868
- }
869
- }
870
- out.log("");
871
- }
872
- if (fixes.length > 0) {
873
- out.log(` ✓ FIXES (${fixes.length})`);
874
- out.log("");
875
- for (const e of fixes) {
876
- out.log(` ${e.version} · ${e.title}`);
877
- if (verbose && e.detail) {
878
- out.log(` ${e.detail}`);
879
- }
880
- }
881
- out.log("");
882
- }
824
+ logBreakingSection(out, breaking, verbose);
825
+ logNonBreakingSection(out, "IMPROVEMENTS", pending.filter((e) => e.type === "improvement"), verbose, true);
826
+ logNonBreakingSection(out, "FIXES", pending.filter((e) => e.type === "fix"), verbose, false);
883
827
  if (breaking.length > 0) {
884
828
  out.log(" ⚠ Review breaking changes above before upgrading.");
885
829
  out.log(" Run with --verbose for full migration details.");
886
830
  out.log("");
887
831
  }
888
- logOpenManual(out, openManual, verbose ? manualDetailsOf(allEntries) : undefined);
832
+ logApplyHint(out, plan);
833
+ logOpenManual(out, openManual, manualDetails);
889
834
  return 0;
890
835
  }
836
+ // Shared by `kumiko upgrade` and the published kumiko-upgrade bin.
837
+ export async function runUpgradeCli(argv, cwd, out, options) {
838
+ if (argv.includes("--help") || argv.includes("-h")) {
839
+ out.log(UPGRADE_USAGE);
840
+ return 0;
841
+ }
842
+ // Standalone CLI entry, not booted via runProdApp/runDevApp — Temporal needs an explicit polyfill here.
843
+ await ensureTemporalPolyfill();
844
+ const args = parseArgs(argv);
845
+ // Resolved once here (not just inside --apply) so both the filter
846
+ // baseline and the --apply codemod run target the same directory.
847
+ const dirFlag = getStringFlag(args, "dir");
848
+ const targetDir = dirFlag ? resolve(dirFlag) : cwd;
849
+ if (!existsSync(targetDir) || !statSync(targetDir).isDirectory()) {
850
+ return failUpgrade(out, `--dir path is not a directory: ${targetDir}`);
851
+ }
852
+ if (args.flags.has("resolve")) {
853
+ return resolveManualMigrations(out, args, targetDir);
854
+ }
855
+ const planned = planUpgrade(args, cwd, targetDir);
856
+ if (planned.kind === "error")
857
+ return failUpgrade(out, ...planned.lines);
858
+ if (getFlag(args, "apply")) {
859
+ return applyPlannedUpgrade(out, args, planned.value, options?.repoRoot ?? cwd, targetDir);
860
+ }
861
+ if (getFlag(args, "json"))
862
+ return reportUpgradeJson(out, planned.value);
863
+ return reportUpgradeText(out, planned.value, getFlag(args, "verbose"));
864
+ }
@@ -0,0 +1,32 @@
1
+ export declare const SEMVER_RE: RegExp;
2
+ export type UpgradeMarkerCodemod = {
3
+ readonly version: string;
4
+ readonly codemod: string;
5
+ readonly title: string;
6
+ };
7
+ export type UpgradeMarkerManual = {
8
+ readonly id: string;
9
+ readonly version: string;
10
+ readonly title: string;
11
+ };
12
+ export type ManualResolution = "migrated" | "not-applicable";
13
+ export type UpgradeMarkerResolvedManual = UpgradeMarkerManual & {
14
+ readonly resolution: ManualResolution;
15
+ readonly reason: string;
16
+ readonly resolvedAt: string;
17
+ };
18
+ export declare function manualEntryId(version: string, title: string): string;
19
+ export declare function dedupeManualById<T extends {
20
+ readonly id: string;
21
+ }>(entries: readonly T[]): readonly T[];
22
+ export type MarkerCarry = {
23
+ readonly codemods: readonly UpgradeMarkerCodemod[];
24
+ readonly pendingManual: readonly UpgradeMarkerManual[];
25
+ readonly resolvedManual: readonly UpgradeMarkerResolvedManual[];
26
+ };
27
+ export type StoredMarker = MarkerCarry & {
28
+ readonly version: string;
29
+ readonly appliedAt: string;
30
+ readonly codemods: readonly UpgradeMarkerCodemod[];
31
+ };
32
+ export declare function parseStoredMarker(raw: unknown): StoredMarker | null;