@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.
- package/AGENTS.md +9 -1
- package/agents/examples/anonymous-access.md +1 -1
- package/agents/examples/apex-landing.md +204 -0
- package/agents/examples/api-worker-split.md +64 -0
- package/agents/examples/app-config.md +14 -0
- package/agents/examples/auth-mfa.md +115 -0
- package/agents/examples/env-composition.md +28 -1
- package/agents/examples/golden-path.md +13 -1
- package/agents/examples/legal-pages.md +1 -1
- package/agents/examples/lifecycle-hooks.md +55 -2
- package/agents/examples/list-table-options.md +466 -0
- package/agents/examples/pdf-audit-export.md +4 -2
- package/agents/examples/prod-boot-options.md +16 -0
- package/agents/examples/rate-limiting.md +48 -0
- package/agents/examples/record-detail-layout.md +692 -0
- package/agents/examples/seed-migration.md +212 -0
- package/agents/examples/state-machine.md +3 -2
- package/agents/examples/tenant-isolation.md +73 -1
- package/agents/examples/wizard-form.md +941 -0
- package/agents/examples/writeform-section.md +548 -0
- package/agents/examples.md +1 -0
- package/agents/features.md +19 -2
- package/agents/widgets.md +2 -0
- package/dist/api/api-constants.d.ts +1 -0
- package/dist/api/api-constants.js +2 -0
- package/dist/api/auth-routes-shared.d.ts +2 -0
- package/dist/api/auth-routes-shared.js +5 -0
- package/dist/api/client-error-route.js +10 -5
- package/dist/api/entity-read-access.d.ts +2 -1
- package/dist/api/entity-read-access.js +14 -0
- package/dist/api/export/export-cells-reference.d.ts +9 -0
- package/dist/api/export/export-cells-reference.js +21 -0
- package/dist/api/export/export-cells.d.ts +11 -0
- package/dist/api/export/export-cells.js +136 -0
- package/dist/api/export/export-ciphertext-guard.d.ts +2 -0
- package/dist/api/export/export-ciphertext-guard.js +27 -0
- package/dist/api/export/export-columns.d.ts +29 -0
- package/dist/api/export/export-columns.js +85 -0
- package/dist/api/export/export-errors.d.ts +4 -0
- package/dist/api/export/export-errors.js +14 -0
- package/dist/api/export/export-route.d.ts +4 -0
- package/dist/api/export/export-route.js +74 -0
- package/dist/api/export/export-rows.d.ts +13 -0
- package/dist/api/export/export-rows.js +62 -0
- package/dist/api/export/export-screen.d.ts +12 -0
- package/dist/api/export/export-screen.js +47 -0
- package/dist/api/export/export-xlsx-writer.d.ts +4 -0
- package/dist/api/export/export-xlsx-writer.js +21 -0
- package/dist/api/extra-route.d.ts +4 -0
- package/dist/api/index.d.ts +3 -1
- package/dist/api/index.js +3 -1
- package/dist/api/pii-leak-guard.d.ts +3 -0
- package/dist/api/pii-leak-guard.js +3 -2
- package/dist/api/request-context.d.ts +1 -1
- package/dist/api/request-context.js +1 -1
- package/dist/api/routes.d.ts +16 -0
- package/dist/api/routes.js +2 -2
- package/dist/api/server-boot-checks.d.ts +10 -0
- package/dist/api/server-boot-checks.js +157 -0
- package/dist/api/server-event-dispatcher.d.ts +26 -0
- package/dist/api/server-event-dispatcher.js +208 -0
- package/dist/api/server-extra-routes.d.ts +11 -0
- package/dist/api/server-extra-routes.js +206 -0
- package/dist/api/server-middleware.d.ts +20 -0
- package/dist/api/server-middleware.js +211 -0
- package/dist/api/server-routes.d.ts +19 -0
- package/dist/api/server-routes.js +299 -0
- package/dist/api/server-websocket-routes.d.ts +4 -0
- package/dist/api/server-websocket-routes.js +108 -0
- package/dist/api/server-wiring.d.ts +32 -0
- package/dist/api/server-wiring.js +238 -0
- package/dist/api/server.d.ts +10 -15
- package/dist/api/server.js +48 -1251
- package/dist/api/session-cookies.js +4 -4
- package/dist/api/sse-route.d.ts +2 -1
- package/dist/api/sse-route.js +27 -13
- package/dist/config/index.d.ts +3 -1
- package/dist/crypto/owner-transition-rekey.d.ts +5 -1
- package/dist/crypto/owner-transition-rekey.js +1 -1
- package/dist/crypto/pg-kms-adapter.d.ts +1 -0
- package/dist/crypto/pg-kms-adapter.js +12 -18
- package/dist/crypto/pii-field-encryption.d.ts +5 -1
- package/dist/crypto/pii-field-encryption.js +3 -1
- package/dist/db/aggregate-sql.d.ts +23 -0
- package/dist/db/aggregate-sql.js +165 -0
- package/dist/db/closed-connection.d.ts +9 -0
- package/dist/db/closed-connection.js +88 -0
- package/dist/db/connection.d.ts +4 -5
- package/dist/db/connection.js +29 -20
- package/dist/db/dialect.js +1 -1
- package/dist/db/distinct-on.d.ts +1 -1
- package/dist/db/driver-row-coercion.d.ts +2 -0
- package/dist/db/driver-row-coercion.js +186 -0
- package/dist/db/driver-value-preparation.d.ts +16 -0
- package/dist/db/driver-value-preparation.js +101 -0
- package/dist/db/entity-field-encryption.d.ts +1 -0
- package/dist/db/entity-field-encryption.js +1 -1
- package/dist/db/entity-table-meta.js +103 -110
- package/dist/db/event-store-executor-context.d.ts +2 -1
- package/dist/db/event-store-executor-read-aggregate-currencies.d.ts +17 -0
- package/dist/db/event-store-executor-read-aggregate-currencies.js +40 -0
- package/dist/db/event-store-executor-read-aggregate.d.ts +8 -0
- package/dist/db/event-store-executor-read-aggregate.js +143 -0
- package/dist/db/event-store-executor-read-filter.d.ts +31 -0
- package/dist/db/event-store-executor-read-filter.js +206 -0
- package/dist/db/event-store-executor-read-paging.d.ts +14 -0
- package/dist/db/event-store-executor-read-paging.js +99 -0
- package/dist/db/event-store-executor-read-rows.d.ts +6 -0
- package/dist/db/event-store-executor-read-rows.js +32 -0
- package/dist/db/event-store-executor-read-search.d.ts +28 -0
- package/dist/db/event-store-executor-read-search.js +221 -0
- package/dist/db/event-store-executor-read-sort.d.ts +41 -0
- package/dist/db/event-store-executor-read-sort.js +130 -0
- package/dist/db/event-store-executor-read-where.d.ts +24 -0
- package/dist/db/event-store-executor-read-where.js +81 -0
- package/dist/db/event-store-executor-read.d.ts +2 -9
- package/dist/db/event-store-executor-read.js +48 -701
- package/dist/db/event-store-executor-write-atomic.d.ts +4 -0
- package/dist/db/event-store-executor-write-atomic.js +41 -0
- package/dist/db/event-store-executor-write-steps.d.ts +17 -0
- package/dist/db/event-store-executor-write-steps.js +95 -0
- package/dist/db/event-store-executor-write.js +405 -509
- package/dist/db/event-store-executor.js +2 -1
- package/dist/db/executor-managed-tables.js +1 -1
- package/dist/db/index.d.ts +7 -3
- package/dist/db/index.js +6 -2
- package/dist/db/list-aggregate-labels.d.ts +14 -0
- package/dist/db/list-aggregate-labels.js +26 -0
- package/dist/db/migrate-generator.js +71 -86
- package/dist/db/migrate-runner.js +7 -4
- package/dist/db/pg-error.js +26 -21
- package/dist/db/queries/backfill-pii.js +3 -6
- package/dist/db/queries/database-now.js +2 -1
- package/dist/db/queries/shadow-swap.js +2 -3
- package/dist/db/query.d.ts +16 -15
- package/dist/db/query.js +76 -804
- package/dist/db/reference-data.js +19 -2
- package/dist/db/statement-timeout.js +2 -1
- package/dist/db/table-builder.js +113 -166
- package/dist/db/table-info.d.ts +10 -0
- package/dist/db/table-info.js +45 -0
- package/dist/db/temporal-value-guards.d.ts +6 -0
- package/dist/db/temporal-value-guards.js +15 -0
- package/dist/db/tenant-db-runner.d.ts +1 -0
- package/dist/db/tenant-db-runner.js +3 -0
- package/dist/db/tenant-db-upsert.d.ts +5 -0
- package/dist/db/tenant-db-upsert.js +91 -0
- package/dist/db/tenant-db.js +50 -10
- package/dist/db/where-clause.d.ts +6 -0
- package/dist/db/where-clause.js +236 -0
- package/dist/derivatives/delete-stored-file.js +2 -1
- package/dist/engine/boot-validator/boot-check.js +1 -1
- package/dist/engine/boot-validator/bundled-lists-table.d.ts +2 -0
- package/dist/engine/boot-validator/bundled-lists-table.js +36 -0
- package/dist/engine/boot-validator/detail-screens.js +2 -2
- package/dist/engine/boot-validator/entity-handler.d.ts +1 -0
- package/dist/engine/boot-validator/entity-handler.js +17 -0
- package/dist/engine/boot-validator/entity-list-aggregate-options.d.ts +3 -0
- package/dist/engine/boot-validator/entity-list-aggregate-options.js +23 -0
- package/dist/engine/boot-validator/form-derivations.js +35 -1
- package/dist/engine/boot-validator/i18n-keys.js +5 -3
- package/dist/engine/boot-validator/index.js +2 -0
- package/dist/engine/boot-validator/list-bulk-actions.d.ts +3 -0
- package/dist/engine/boot-validator/list-bulk-actions.js +49 -0
- package/dist/engine/boot-validator/list-column-layout.d.ts +8 -0
- package/dist/engine/boot-validator/list-column-layout.js +41 -0
- package/dist/engine/boot-validator/list-table-aggregate-options.d.ts +24 -0
- package/dist/engine/boot-validator/list-table-aggregate-options.js +145 -0
- package/dist/engine/boot-validator/list-table-aggregate-query.d.ts +10 -0
- package/dist/engine/boot-validator/list-table-aggregate-query.js +31 -0
- package/dist/engine/boot-validator/list-table-export-options.d.ts +1 -0
- package/dist/engine/boot-validator/list-table-export-options.js +27 -0
- package/dist/engine/boot-validator/list-table-options.d.ts +3 -0
- package/dist/engine/boot-validator/list-table-options.js +77 -0
- package/dist/engine/boot-validator/list-table-reject-unknown-keys.d.ts +1 -0
- package/dist/engine/boot-validator/list-table-reject-unknown-keys.js +7 -0
- package/dist/engine/boot-validator/pii-retention.js +233 -155
- package/dist/engine/boot-validator/projection-list-screens.d.ts +1 -1
- package/dist/engine/boot-validator/projection-list-screens.js +3 -13
- package/dist/engine/boot-validator/related-list-table-rules.d.ts +4 -0
- package/dist/engine/boot-validator/related-list-table-rules.js +42 -0
- package/dist/engine/boot-validator/screens-actions.d.ts +1 -0
- package/dist/engine/boot-validator/screens-actions.js +8 -1
- package/dist/engine/boot-validator/screens-dashboard.js +2 -5
- package/dist/engine/boot-validator/screens-forms.js +2 -6
- package/dist/engine/boot-validator/screens-kind-config-edit.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-config-edit.js +88 -0
- package/dist/engine/boot-validator/screens-kind-entity-edit.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-entity-edit.js +55 -0
- package/dist/engine/boot-validator/screens-kind-entity-list.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-entity-list.js +235 -0
- package/dist/engine/boot-validator/screens-kind-projection-detail.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-projection-detail.js +235 -0
- package/dist/engine/boot-validator/screens-kind-projection-list.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-projection-list.js +56 -0
- package/dist/engine/boot-validator/screens-kind-secret-mint.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-secret-mint.js +28 -0
- package/dist/engine/boot-validator/screens-kind-shared.d.ts +37 -0
- package/dist/engine/boot-validator/screens-kind-shared.js +142 -0
- package/dist/engine/boot-validator/screens-lists.d.ts +2 -1
- package/dist/engine/boot-validator/screens-lists.js +24 -11
- package/dist/engine/boot-validator/screens.d.ts +0 -2
- package/dist/engine/boot-validator/screens.js +99 -860
- package/dist/engine/build-app-schema.d.ts +2 -1
- package/dist/engine/build-app-schema.js +65 -97
- package/dist/engine/build-config-feature-schema.js +6 -1
- package/dist/engine/define-feature.js +13 -2
- package/dist/engine/define-handler.js +1 -0
- package/dist/engine/entity-handlers.d.ts +13 -0
- package/dist/engine/entity-handlers.js +10 -21
- package/dist/engine/entity-list-runtime-options.d.ts +8 -0
- package/dist/engine/entity-list-runtime-options.js +18 -0
- package/dist/engine/feature-ast/extractors/handlers.js +1 -0
- package/dist/engine/feature-ast/extractors/index.d.ts +1 -1
- package/dist/engine/feature-ast/extractors/index.js +1 -1
- package/dist/engine/feature-ast/extractors/round1.d.ts +2 -1
- package/dist/engine/feature-ast/extractors/round1.js +6 -0
- package/dist/engine/feature-ast/extractors/round2.js +25 -6
- package/dist/engine/feature-ast/index.d.ts +1 -1
- package/dist/engine/feature-ast/parse.js +3 -1
- package/dist/engine/feature-ast/patch.d.ts +2 -0
- package/dist/engine/feature-ast/patch.js +5 -182
- package/dist/engine/feature-ast/pattern-change-schema.d.ts +10 -245
- package/dist/engine/feature-ast/pattern-change-schema.js +10 -163
- package/dist/engine/feature-ast/pattern-id-matchers.d.ts +7 -0
- package/dist/engine/feature-ast/pattern-id-matchers.js +211 -0
- package/dist/engine/feature-ast/pattern-id-schema.d.ts +251 -0
- package/dist/engine/feature-ast/pattern-id-schema.js +166 -0
- package/dist/engine/feature-ast/patterns.d.ts +14 -1
- package/dist/engine/feature-ast/patterns.js +44 -51
- package/dist/engine/feature-ast/render.js +52 -87
- package/dist/engine/feature-builder-state.d.ts +1 -0
- package/dist/engine/feature-builder-state.js +1 -0
- package/dist/engine/feature-entity-handlers.js +3 -0
- package/dist/engine/feature-manifest.d.ts +1 -0
- package/dist/engine/feature-manifest.js +1 -0
- package/dist/engine/field-access.d.ts +1 -0
- package/dist/engine/field-access.js +12 -0
- package/dist/engine/form-derivations.js +18 -7
- package/dist/engine/handler-field-references.js +1 -1
- package/dist/engine/index.d.ts +2 -2
- package/dist/engine/index.js +1 -1
- package/dist/engine/pattern-library/library.js +2 -1
- package/dist/engine/pattern-library/static-schemas.d.ts +1 -0
- package/dist/engine/pattern-library/static-schemas.js +9 -0
- package/dist/engine/project-app-schema-for-roles.d.ts +2 -0
- package/dist/engine/project-app-schema-for-roles.js +6 -4
- package/dist/engine/registry-ingest.js +1 -1
- package/dist/engine/schema-builder.js +228 -212
- package/dist/engine/screen-filter-ops.js +2 -0
- package/dist/engine/screen-helpers.js +1 -1
- package/dist/engine/screen-kind.d.ts +7 -0
- package/dist/engine/screen-kind.js +1 -0
- package/dist/engine/strip-disabled-features-from-app-schema.js +58 -17
- package/dist/engine/types/feature.d.ts +1 -0
- package/dist/engine/types/index.d.ts +4 -3
- package/dist/engine/types/screen.d.ts +1 -1
- package/dist/engine/types/screen.js +1 -1
- package/dist/engine/validate-projection-allowlist.js +2 -1
- package/dist/entrypoint/index.d.ts +6 -1
- package/dist/entrypoint/index.js +10 -1
- package/dist/errors/docs-url.d.ts +1 -1
- package/dist/errors/docs-url.js +22 -2
- package/dist/es-ops/context.js +1 -1
- package/dist/event-store/admin-api.js +1 -1
- package/dist/event-store/event-store.d.ts +1 -0
- package/dist/event-store/event-store.js +17 -8
- package/dist/event-store/provenance-append.js +1 -1
- package/dist/files/bind-pending-file-refs.js +3 -3
- package/dist/files/create-file-ref-from-bytes.js +7 -1
- package/dist/files/create-file-ref.d.ts +3 -0
- package/dist/files/create-file-ref.js +11 -2
- package/dist/files/file-handle.js +2 -3
- package/dist/files/file-routes.d.ts +2 -0
- package/dist/files/file-routes.js +40 -10
- package/dist/files/provider-resolver.d.ts +1 -0
- package/dist/files/provider-resolver.js +1 -1
- package/dist/files/record-write-check.d.ts +6 -0
- package/dist/files/record-write-check.js +13 -0
- package/dist/files/resolve-content-type.js +43 -0
- package/dist/files/upload-ledger.d.ts +2 -2
- package/dist/files/upload-ledger.js +11 -4
- package/dist/i18n/required-surface-keys.js +189 -208
- package/dist/internal/stack-support.d.ts +2 -1
- package/dist/internal/stack-support.js +1 -1
- package/dist/internal/testing.js +1 -1
- package/dist/jobs/job-runner-context.d.ts +32 -0
- package/dist/jobs/job-runner-context.js +305 -0
- package/dist/jobs/job-runner-execution.d.ts +7 -0
- package/dist/jobs/job-runner-execution.js +160 -0
- package/dist/jobs/job-runner-gates.d.ts +19 -0
- package/dist/jobs/job-runner-gates.js +198 -0
- package/dist/jobs/job-runner-helpers.d.ts +17 -3
- package/dist/jobs/job-runner-helpers.js +61 -2
- package/dist/jobs/job-runner-scope.d.ts +44 -0
- package/dist/jobs/job-runner-scope.js +102 -0
- package/dist/jobs/job-runner-types.d.ts +12 -0
- package/dist/jobs/job-runner.d.ts +0 -1
- package/dist/jobs/job-runner.js +50 -657
- package/dist/observability/escape-hatch-report.d.ts +3 -0
- package/dist/observability/escape-hatch-report.js +21 -3
- package/dist/pipeline/after-rollback-sink.d.ts +6 -0
- package/dist/pipeline/after-rollback-sink.js +32 -0
- package/dist/pipeline/cascade-handler.js +165 -138
- package/dist/pipeline/dispatch-batch.js +8 -1
- package/dist/pipeline/dispatch-query.js +10 -3
- package/dist/pipeline/dispatch-shared.d.ts +13 -3
- package/dist/pipeline/dispatch-shared.js +62 -710
- package/dist/pipeline/dispatch-write.js +296 -189
- package/dist/pipeline/event-dispatcher-admin.js +2 -1
- package/dist/pipeline/event-dispatcher-delivery.js +6 -6
- package/dist/pipeline/event-dispatcher.d.ts +1 -0
- package/dist/pipeline/event-dispatcher.js +6 -9
- package/dist/pipeline/handler-context-auth.d.ts +9 -0
- package/dist/pipeline/handler-context-auth.js +54 -0
- package/dist/pipeline/handler-context-config.d.ts +14 -0
- package/dist/pipeline/handler-context-config.js +113 -0
- package/dist/pipeline/handler-context-db.d.ts +24 -0
- package/dist/pipeline/handler-context-db.js +149 -0
- package/dist/pipeline/handler-context-dispatch.d.ts +23 -0
- package/dist/pipeline/handler-context-dispatch.js +101 -0
- package/dist/pipeline/handler-context-events.d.ts +41 -0
- package/dist/pipeline/handler-context-events.js +239 -0
- package/dist/pipeline/handler-context-notify.d.ts +12 -0
- package/dist/pipeline/handler-context-notify.js +55 -0
- package/dist/pipeline/handler-context-observability.d.ts +7 -0
- package/dist/pipeline/handler-context-observability.js +21 -0
- package/dist/pipeline/handler-context-scope.d.ts +48 -0
- package/dist/pipeline/handler-context-scope.js +88 -0
- package/dist/pipeline/notify-scope.js +12 -1
- package/dist/pipeline/pre-handler-gates.js +20 -4
- package/dist/pipeline/pre-transaction-reservations.d.ts +1 -0
- package/dist/pipeline/pre-transaction-reservations.js +9 -3
- package/dist/pipeline/projection-rebuild.js +2 -1
- package/dist/pipeline/system-hooks.d.ts +3 -1
- package/dist/pipeline/system-hooks.js +55 -6
- package/dist/pipeline/write-result-masking.js +13 -10
- package/dist/schema-cli/apply-command.d.ts +2 -0
- package/dist/schema-cli/apply-command.js +97 -0
- package/dist/schema-cli/baseline-command.d.ts +2 -0
- package/dist/schema-cli/baseline-command.js +33 -0
- package/dist/schema-cli/generate-command.d.ts +2 -0
- package/dist/schema-cli/generate-command.js +90 -0
- package/dist/schema-cli/shared.d.ts +39 -0
- package/dist/schema-cli/shared.js +95 -0
- package/dist/schema-cli/status-command.d.ts +2 -0
- package/dist/schema-cli/status-command.js +73 -0
- package/dist/schema-cli/validate-boot-input.d.ts +5 -0
- package/dist/schema-cli/validate-boot-input.js +130 -0
- package/dist/schema-cli/validate-command.d.ts +2 -0
- package/dist/schema-cli/validate-command.js +130 -0
- package/dist/schema-cli.d.ts +2 -18
- package/dist/schema-cli.js +47 -604
- package/dist/search/meilisearch-adapter.js +2 -1
- package/dist/search/reindex-entity.js +15 -5
- package/dist/ui-types/index.d.ts +2 -2
- package/dist/ui-types/index.js +1 -1
- package/dist/upgrade-changelog.d.ts +2 -0
- package/dist/upgrade-changelog.js +14 -0
- package/dist/upgrade-cli.d.ts +3 -12
- package/dist/upgrade-cli.js +171 -197
- package/dist/upgrade-marker-parse.d.ts +32 -0
- package/dist/upgrade-marker-parse.js +73 -0
- package/package.json +9 -6
- package/src/changes.json +555 -0
- package/src/scripts/codemod/codemod-support.ts +1 -1
- package/src/scripts/codemod/db-factory.ts +1 -1
- package/src/scripts/codemod/feature-layout.ts +10 -2
- package/src/scripts/codemod/object-form.ts +41 -4
- package/src/scripts/codemod/package-merge.ts +58 -8
- package/src/scripts/codemod/pii-personal-migration.ts +175 -145
- package/src/scripts/codemod/testing-injection.ts +122 -42
- package/src/scripts/codemod/testing-move.ts +29 -7
- package/dist/engine/provider-plugin-context.d.ts +0 -3
- 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(
|
|
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
|
|
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)
|
|
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(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
-
|
|
263
|
-
|
|
264
|
-
if (
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
-
|
|
314
|
-
|
|
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
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
personal:
|
|
328
|
-
|
|
329
|
-
|
|
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
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
370
|
-
if (
|
|
371
|
-
|
|
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 (
|
|
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,
|
|
392
|
+
const removedProps = [subject.prop, lookupable, searchable, sensitive].filter(
|
|
401
393
|
(p): p is PropertyAssignment => !!p,
|
|
402
394
|
);
|
|
403
|
-
applyTransform(obj, removedProps, { personal:
|
|
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[],
|