@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
@@ -139,8 +139,27 @@ function isAssumedRegistrar(receiver: Node, assumeRegistrarNames: ReadonlySet<st
139
139
  );
140
140
  }
141
141
 
142
+ function isFeatureNameLiteral(arg: Node): boolean {
143
+ return (
144
+ Node.isStringLiteral(arg) ||
145
+ Node.isNoSubstitutionTemplateLiteral(arg) ||
146
+ Node.isTemplateExpression(arg) ||
147
+ Node.isSpreadElement(arg)
148
+ );
149
+ }
150
+
151
+ // An identifier or call argument could be a features object (already the object form) or a name,
152
+ // so it is neither rewritten nor skipped silently.
153
+ function isAmbiguousRequires(method: RegistrarMethod, args: readonly Node[]): boolean {
154
+ return (
155
+ method === "requires" &&
156
+ !args.some(isFeatureNameLiteral) &&
157
+ args.some((arg) => !Node.isObjectLiteralExpression(arg))
158
+ );
159
+ }
160
+
142
161
  export function isPositionalShape(method: RegistrarMethod, args: readonly Node[]): boolean {
143
- if (method === "requires") return args.some((arg) => !Node.isObjectLiteralExpression(arg));
162
+ if (method === "requires") return args.some(isFeatureNameLiteral);
144
163
  // The object form takes exactly one argument, so a spread can only be a positional call.
145
164
  if (args.some((arg) => Node.isSpreadElement(arg))) return true;
146
165
  return args.length >= (method === "hook" ? 3 : 2);
@@ -477,13 +496,31 @@ function visitRegistrarCall(
477
496
  if (
478
497
  !Node.isPropertyAccessExpression(callee) ||
479
498
  method === undefined ||
480
- !isRegistrarMethod(method) ||
481
- !isPositionalShape(method, call.getArguments())
499
+ !isRegistrarMethod(method)
482
500
  ) {
483
- // skip: not a positional registrar call, nothing to rewrite
501
+ // skip: not a registrar method call, nothing to rewrite
484
502
  return;
485
503
  }
504
+ const args = call.getArguments();
486
505
  const receiver = callee.getExpression();
506
+ if (isAmbiguousRequires(method, args)) {
507
+ if (
508
+ isFeatureRegistrarReceiver(receiver) ||
509
+ isAssumedRegistrar(receiver, assumeRegistrarNames)
510
+ ) {
511
+ rewriter.recordManual(
512
+ call,
513
+ method,
514
+ "argument is not a string literal, so it may already be the object form — check by hand",
515
+ );
516
+ }
517
+ // skip: an ambiguous argument is never rewritten; registrar receivers were reported above
518
+ return;
519
+ }
520
+ if (!isPositionalShape(method, args)) {
521
+ // skip: not a positional registrar call, nothing to rewrite
522
+ return;
523
+ }
487
524
  if (isFeatureRegistrarReceiver(receiver) || isAssumedRegistrar(receiver, assumeRegistrarNames)) {
488
525
  rewriter.recordPlan(call, method, planRegistrarCall(rewriter, call, method));
489
526
  } else if (isUnresolvedRegistrarLookalike(receiver)) {
@@ -162,6 +162,10 @@ function mergeDependencyBlock(
162
162
  moved: ReadonlyMap<string, string>,
163
163
  ownName: unknown,
164
164
  describe: (description: string) => void,
165
+ hooks: {
166
+ readonly isDuplicatedElsewhere: (target: string) => boolean;
167
+ readonly onAdded: (target: string) => void;
168
+ },
165
169
  ): Record<string, string> | undefined {
166
170
  const next: Record<string, string> = {};
167
171
  let changed = false;
@@ -173,7 +177,10 @@ function mergeDependencyBlock(
173
177
  }
174
178
  changed = true;
175
179
  describe(`${name} -> ${target}`);
176
- if (target !== ownName && !(target in block)) next[target] = range;
180
+ if (target !== ownName && !(target in block) && !hooks.isDuplicatedElsewhere(target)) {
181
+ next[target] = range;
182
+ hooks.onAdded(target);
183
+ }
177
184
  }
178
185
  if (!changed) return undefined;
179
186
  return isSortedKeys(block)
@@ -181,6 +188,31 @@ function mergeDependencyBlock(
181
188
  : next;
182
189
  }
183
190
 
191
+ function dropAddedFromDevDependencies(
192
+ record: Record<string, unknown>,
193
+ addedToDependencies: ReadonlySet<string>,
194
+ filePath: string,
195
+ result: SiteLists,
196
+ ): boolean {
197
+ const devBlock = record["devDependencies"];
198
+ if (addedToDependencies.size === 0 || !isStringRecord(devBlock)) return false;
199
+ const kept = Object.fromEntries(
200
+ Object.entries(devBlock).filter(([name]) => !addedToDependencies.has(name)),
201
+ );
202
+ if (Object.keys(kept).length === Object.keys(devBlock).length) return false;
203
+ record["devDependencies"] = kept;
204
+ for (const target of addedToDependencies) {
205
+ if (target in devBlock) {
206
+ result.rewrites.push({
207
+ file: filePath,
208
+ line: 1,
209
+ description: `devDependencies: ${target} dropped, dependencies already lists it`,
210
+ });
211
+ }
212
+ }
213
+ return true;
214
+ }
215
+
184
216
  function transformPackageJson(
185
217
  source: string,
186
218
  filePath: string,
@@ -198,20 +230,38 @@ function transformPackageJson(
198
230
  const moved = movedPackages(mappings);
199
231
  const record: Record<string, unknown> = { ...manifest };
200
232
  let changed = false;
233
+ const addedToDependencies = new Set<string>();
201
234
  for (const blockName of DEPENDENCY_BLOCKS) {
202
235
  const block = record[blockName];
203
236
  if (!isStringRecord(block)) continue;
204
- const merged = mergeDependencyBlock(block, moved, record["name"], (description) => {
205
- result.rewrites.push({
206
- file: filePath,
207
- line: 1,
208
- description: `${blockName}: ${description}`,
209
- });
210
- });
237
+ const merged = mergeDependencyBlock(
238
+ block,
239
+ moved,
240
+ record["name"],
241
+ (description) => {
242
+ result.rewrites.push({
243
+ file: filePath,
244
+ line: 1,
245
+ description: `${blockName}: ${description}`,
246
+ });
247
+ },
248
+ {
249
+ // npm/bun resolve a package listed in both dependencies and devDependencies by
250
+ // block order, so the runtime entry wins and the dev copy is dropped below.
251
+ isDuplicatedElsewhere: (target) =>
252
+ blockName === "devDependencies" &&
253
+ isStringRecord(record["dependencies"]) &&
254
+ target in record["dependencies"],
255
+ onAdded: (target) => {
256
+ if (blockName === "dependencies") addedToDependencies.add(target);
257
+ },
258
+ },
259
+ );
211
260
  if (merged === undefined) continue;
212
261
  record[blockName] = merged;
213
262
  changed = true;
214
263
  }
264
+ if (dropAddedFromDevDependencies(record, addedToDependencies, filePath, result)) changed = true;
215
265
  if (!changed) return source;
216
266
  const trailingNewline = source.endsWith("\n") ? "\n" : "";
217
267
  return `${JSON.stringify(record, null, detectIndent(source))}${trailingNewline}`;
@@ -177,50 +177,40 @@ function reportRawSubjectLiterals(
177
177
  }
178
178
  }
179
179
 
180
- // kumiko-lint-ignore complexity-budget migration codemod, one-time script — splitting would add risk without benefit
181
- function processObjectLiteral(obj: ObjectLiteralExpression, factoryName: string): void {
182
- // skip: field already carries `personal` — already migrated, idempotent no-op
183
- if (obj.getProperty("personal")) return;
184
-
185
- const piiEncryptedProp = obj.getProperty("piiEncrypted");
186
- if (piiEncryptedProp) {
187
- report(
188
- piiEncryptedProp,
189
- "piiEncrypted: true on an entity field (removed from the type system) — needs a human decision, not a mechanical mapping",
190
- );
191
- // skip: reported above — piiEncrypted needs a human decision, not a mechanical mapping
192
- return;
193
- }
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
+ };
194
188
 
189
+ function singleSubjectFlag(obj: ObjectLiteralExpression): SubjectProp | undefined {
195
190
  const subjectProps = SUBJECT_FLAG_NAMES.map((n) => ({ name: n, prop: findProp(obj, n) })).filter(
196
191
  (x): x is { name: SubjectFlagName; prop: PropertyAssignment } => !!x.prop,
197
192
  );
198
- const lookupableProp = findProp(obj, "lookupable");
199
- const searchableProp = findProp(obj, "searchable");
200
- const sensitiveProp = findProp(obj, "sensitive");
201
- const anonymizeProp = obj.getProperty("anonymize");
202
-
203
193
  if (subjectProps.length === 0) {
204
194
  // `searchable`/`sensitive` alone remain valid, unrelated FieldDef
205
195
  // properties — left untouched. `lookupable` alone can never compile
206
196
  // (always an excess property without a `personal` to attach `find`
207
197
  // to). `anonymize` alone has no PersonalAnnotations arm to live on.
198
+ const lookupableProp = findProp(obj, "lookupable");
208
199
  if (lookupableProp) {
209
200
  report(
210
201
  lookupableProp,
211
202
  "lookupable without any subject annotation — no `personal` to attach `find` to",
212
203
  );
213
204
  }
205
+ const anonymizeProp = obj.getProperty("anonymize");
214
206
  if (anonymizeProp) {
215
207
  report(
216
208
  anonymizeProp,
217
209
  "anonymize without any subject annotation — no PersonalAnnotations arm accepts anonymize alone",
218
210
  );
219
211
  }
220
- // skip: no subject flag on this field — stray lookupable/anonymize already reported above if present
221
- return;
212
+ return undefined;
222
213
  }
223
-
224
214
  if (subjectProps.length > 1) {
225
215
  const first = subjectProps[0];
226
216
  if (first) {
@@ -229,152 +219,154 @@ function processObjectLiteral(obj: ObjectLiteralExpression, factoryName: string)
229
219
  `multiple subject annotations on one field (${subjectProps.map((s) => s.name).join(", ")}) — needs a human decision on which subject is correct`,
230
220
  );
231
221
  }
232
- // skip: reported above — multiple subject annotations need a human call on which one wins
233
- return;
234
- }
235
-
236
- const subject = subjectProps[0];
237
- if (!subject) {
238
- // skip: length===0 and length>1 already returned — empty [0] is unreachable
239
- return;
222
+ return undefined;
240
223
  }
224
+ return subjectProps[0];
225
+ }
241
226
 
227
+ function findSupportOf(factoryName: string, subject: SubjectProp): FindSupport | undefined {
242
228
  if (NO_PERSONAL_FACTORIES.has(factoryName)) {
243
229
  report(
244
230
  subject.prop,
245
231
  `${factoryName} has no personal-annotation support — subject flag "${subject.name}" cannot be expressed here`,
246
232
  );
247
- // skip: reported above — this factory has no personal-annotation support to migrate into
248
- return;
249
- }
250
- const isTextFind = TEXT_FIND_FACTORIES.has(factoryName);
251
- const isLongTextFind = LONGTEXT_FIND_FACTORIES.has(factoryName);
252
- const isNoFind = NO_FIND_FACTORIES.has(factoryName);
253
- if (!isTextFind && !isLongTextFind && !isNoFind) {
254
- report(
255
- subject.prop,
256
- `unrecognized field factory "${factoryName}" — cannot verify its PersonalAnnotations shape`,
257
- );
258
- // skip: reported above — unknown factory shape, cannot verify safely
259
- 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;
260
260
  }
261
+ const reason = Node.isStringLiteral(init)
262
+ ? `"${normalizeReasonSlug(init.getLiteralText())}"`
263
+ : init.getText();
264
+ return { personal: "false", reason };
265
+ }
261
266
 
262
- let personalInit: string;
263
- let reasonInit: string | undefined;
264
- if (subject.name === "pii") {
265
- if (!isTrueLiteral(subject.prop.getInitializer())) {
266
- report(subject.prop, "pii is set to a non-`true` value — cannot infer intent");
267
- // skip: reported above — non-`true` pii value, can't infer intent
268
- return;
269
- }
270
- personalInit = `"self"`;
271
- } else if (subject.name === "tenantOwned") {
272
- if (!isTrueLiteral(subject.prop.getInitializer())) {
273
- report(subject.prop, "tenantOwned is set to a non-`true` value — cannot infer intent");
274
- // skip: reported above — non-`true` tenantOwned value, can't infer intent
275
- return;
276
- }
277
- personalInit = `"tenant"`;
278
- } else if (subject.name === "subjectRef") {
279
- if (!isTrueLiteral(subject.prop.getInitializer())) {
280
- report(subject.prop, "subjectRef is set to a non-`true` value — cannot infer intent");
281
- // skip: reported above — non-`true` subjectRef value, can't infer intent
282
- return;
283
- }
284
- personalInit = `"ref"`;
285
- } else if (subject.name === "allowPlaintext") {
286
- const init = subject.prop.getInitializer();
287
- if (!init) {
288
- report(subject.prop, "allowPlaintext has no value");
289
- // skip: reported above — allowPlaintext has no value to migrate
290
- return;
291
- }
292
- personalInit = "false";
293
- reasonInit = Node.isStringLiteral(init)
294
- ? `"${normalizeReasonSlug(init.getLiteralText())}"`
295
- : init.getText();
296
- } else {
297
- const init = subject.prop.getInitializer();
298
- if (!init || !Node.isObjectLiteralExpression(init)) {
299
- report(subject.prop, "userOwned value is not an object literal — cannot extract ownerField");
300
- // skip: reported above — userOwned value isn't an object literal, can't extract ownerField
301
- return;
302
- }
303
- const ownerFieldProp = findProp(init, "ownerField");
304
- const ownerFieldInit = ownerFieldProp?.getInitializer();
305
- if (!ownerFieldInit) {
306
- report(subject.prop, 'userOwned is missing an "ownerField" property');
307
- // skip: reported above — userOwned is missing its ownerField property
308
- return;
309
- }
310
- 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;
311
277
  }
278
+ return { personal: `{ of: ${ownerFieldInit.getText()} }`, reason: undefined };
279
+ }
312
280
 
313
- const subjectIsRefOrPlaintext =
314
- 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
+ };
315
293
 
316
- if (subjectIsRefOrPlaintext) {
317
- // These PersonalAnnotations arms carry no `find` field at all.
318
- if (lookupableProp) {
319
- report(
320
- lookupableProp,
321
- `lookupable combined with personal:${subject.name === "subjectRef" ? '"ref"' : "false"} — findability doesn't apply to this subject, table has no mapping`,
322
- );
323
- // skip: reported above — lookupable doesn't apply to this subject, no mapping to migrate
324
- return;
325
- }
326
- applyTransform(obj, [subject.prop], {
327
- personal: personalInit,
328
- reason: reasonInit,
329
- find: undefined,
330
- });
331
- counts[subject.name === "subjectRef" ? "ref" : "personal-false"]++;
332
- // 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
333
308
  return;
334
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
+ }
335
317
 
336
- if (isNoFind) {
337
- const stray = lookupableProp ?? searchableProp ?? sensitiveProp;
338
- if (stray) {
339
- report(
340
- stray,
341
- `${factoryName} has no \`find\` in its PersonalAnnotations — findability flag present on a non-text field`,
342
- );
343
- // skip: reported above — findability flag on a non-text field, no valid find to attach
344
- return;
345
- }
346
- applyTransform(obj, [subject.prop], {
347
- personal: personalInit,
348
- reason: undefined,
349
- find: undefined,
350
- });
351
- counts["no-find"]++;
352
- // 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
353
332
  return;
354
333
  }
334
+ applyTransform(obj, [subject.prop], {
335
+ personal: personal.personal,
336
+ reason: undefined,
337
+ find: undefined,
338
+ });
339
+ counts["no-find"]++;
340
+ }
355
341
 
356
- const hasLookupable = !!lookupableProp;
357
- const hasSearchable = !!searchableProp;
358
- 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
+ }
359
349
 
360
- 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)) {
361
359
  report(
362
- sensitiveProp,
360
+ sensitive,
363
361
  'sensitive combined with lookupable/searchable — two find values ("secret" vs "exact"/"fuzzy"), needs a human decision',
364
362
  );
365
363
  // skip: reported above — sensitive plus lookupable/searchable is an ambiguous find value
366
364
  return;
367
365
  }
368
366
 
369
- let find: "exact" | "fuzzy" | "none" | "secret";
370
- if (hasSensitive) find = "secret";
371
- else if (hasLookupable && hasSearchable) find = "fuzzy";
372
- else if (hasLookupable) find = "exact";
373
- else if (hasSearchable) find = "fuzzy";
374
- else find = "none";
375
-
376
- if (isLongTextFind && (find === "exact" || find === "fuzzy")) {
377
- const findSite = lookupableProp ?? searchableProp;
367
+ const find = findValueOf(findability);
368
+ if (support === "longText" && (find === "exact" || find === "fuzzy")) {
369
+ const findSite = lookupable ?? searchable;
378
370
  if (findSite) {
379
371
  report(
380
372
  findSite,
@@ -388,7 +380,7 @@ function processObjectLiteral(obj: ObjectLiteralExpression, factoryName: string)
388
380
  // searchable-alone -> "fuzzy" makes expandPersonalAnnotations add
389
381
  // `lookupable: true`, which the field didn't carry before — that's a
390
382
  // new `_bidx` column (DDL migration), tracked separately for the report.
391
- if (hasSearchable && !hasLookupable) {
383
+ if (searchable && !lookupable) {
392
384
  newLookupableSites.push({
393
385
  file: obj.getSourceFile().getFilePath(),
394
386
  line: subject.prop.getStartLineNumber(),
@@ -397,13 +389,51 @@ function processObjectLiteral(obj: ObjectLiteralExpression, factoryName: string)
397
389
  });
398
390
  }
399
391
 
400
- const removedProps = [subject.prop, lookupableProp, searchableProp, sensitiveProp].filter(
392
+ const removedProps = [subject.prop, lookupable, searchable, sensitive].filter(
401
393
  (p): p is PropertyAssignment => !!p,
402
394
  );
403
- applyTransform(obj, removedProps, { personal: personalInit, reason: undefined, find });
395
+ applyTransform(obj, removedProps, { personal: personal.personal, reason: undefined, find });
404
396
  counts[find]++;
405
397
  }
406
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
+
407
437
  function applyTransform(
408
438
  obj: ObjectLiteralExpression,
409
439
  removedProps: readonly PropertyAssignment[],