@cosmicdrift/kumiko-framework 0.388.0 → 0.390.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 +12 -4
- package/README.md +1 -1
- package/agents/examples/apex-surface-auth.md +2 -3
- package/agents/examples/api-worker-split.md +9 -2
- package/agents/examples/auth-claims.md +1 -1
- package/agents/examples/cross-feature-events.md +12 -12
- package/agents/examples/crypto-shredding-hr.md +2 -2
- package/agents/examples/encrypted-tenant-config.md +1 -1
- package/agents/examples/error-contract.md +12 -7
- package/agents/examples/event-sourcing.md +29 -29
- package/agents/examples/files-post-processing.md +10 -8
- package/agents/examples/golden-path.md +13 -1
- package/agents/examples/inbound-mail-triage.md +1 -0
- package/agents/examples/lane-routing.md +2 -1
- package/agents/examples/legal-pages.md +2 -2
- package/agents/examples/lifecycle-hooks.md +55 -2
- package/agents/examples/list-table-options.md +466 -0
- package/agents/examples/managed-config.md +4 -4
- package/agents/examples/managed-pages.md +1 -1
- package/agents/examples/pdf-audit-export.md +4 -2
- package/agents/examples/relations.md +19 -4
- package/agents/examples/search.md +4 -4
- package/agents/examples/secrets-demo.md +9 -6
- package/agents/examples/state-machine.md +5 -4
- package/agents/examples/tenant-isolation.md +73 -1
- package/agents/examples/tenant-settings.md +1 -1
- package/agents/examples/user-data-rights.md +3 -6
- package/agents/examples/workflow-engine.md +53 -47
- package/agents/examples.md +5 -4
- package/agents/features.md +1 -1
- package/agents/widgets.md +1 -0
- package/dist/api/api-constants.d.ts +1 -0
- package/dist/api/api-constants.js +2 -0
- package/dist/api/auth-middleware.js +1 -1
- package/dist/api/auth-routes-invite.js +2 -1
- 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 +2 -3
- package/dist/api/entity-read-access.d.ts +4 -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 +31 -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 +75 -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 +13 -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 +7 -1
- package/dist/api/index.d.ts +23 -27
- package/dist/api/index.js +13 -15
- package/dist/api/jwt.js +1 -1
- package/dist/api/observability-middleware.d.ts +1 -1
- package/dist/api/observability-middleware.js +2 -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-internals.d.ts +31 -0
- package/dist/api/request-context-internals.js +11 -0
- package/dist/api/request-context-scopes.d.ts +14 -0
- package/dist/api/request-context-scopes.js +69 -0
- package/dist/api/request-context.d.ts +1 -40
- package/dist/api/request-context.js +22 -51
- package/dist/api/request-id-middleware.d.ts +1 -1
- package/dist/api/route-registrars.d.ts +2 -1
- package/dist/api/route-registrars.js +1 -1
- package/dist/api/routes.d.ts +16 -0
- package/dist/api/routes.js +9 -5
- package/dist/api/server-boot-checks.d.ts +1 -1
- package/dist/api/server-boot-checks.js +2 -2
- package/dist/api/server-event-dispatcher.d.ts +6 -1
- package/dist/api/server-event-dispatcher.js +3 -4
- package/dist/api/server-extra-routes.d.ts +1 -1
- package/dist/api/server-extra-routes.js +5 -1
- package/dist/api/server-middleware.js +3 -1
- package/dist/api/server-routes.d.ts +3 -2
- package/dist/api/server-routes.js +8 -2
- package/dist/api/server-wiring.js +8 -4
- package/dist/api/server.d.ts +2 -1
- package/dist/api/sse-route.d.ts +3 -1
- package/dist/api/sse-route.js +11 -1
- package/dist/api/tenant-scoped-secrets.d.ts +2 -0
- package/dist/api/tenant-scoped-secrets.js +13 -0
- package/dist/api/websocket-route.d.ts +1 -1
- package/dist/auth/index.d.ts +0 -1
- package/dist/auth/roles.d.ts +0 -5
- package/dist/compliance/duration-spec.d.ts +0 -1
- package/dist/compliance/duration-spec.js +0 -5
- package/dist/compliance/index.d.ts +4 -4
- package/dist/compliance/index.js +2 -2
- package/dist/compliance/profiles.d.ts +0 -15
- package/dist/compliance/profiles.js +0 -23
- package/dist/config/index.d.ts +11 -9
- package/dist/config/index.js +13 -1
- package/dist/config/internals.d.ts +9 -0
- package/dist/config/internals.js +1 -0
- package/dist/consumer-cli-internals.d.ts +4 -0
- package/dist/consumer-cli-internals.js +1 -0
- package/dist/consumer-cli.d.ts +1 -4
- package/dist/consumer-cli.js +5 -1
- package/dist/crypto/event-pii.js +1 -1
- package/dist/crypto/index.d.ts +8 -9
- package/dist/crypto/index.js +7 -8
- package/dist/crypto/pg-kms-adapter.d.ts +1 -0
- package/dist/crypto/pg-kms-adapter.js +17 -18
- package/dist/db/aggregate-sql.d.ts +2 -0
- package/dist/db/aggregate-sql.js +10 -0
- package/dist/db/apply-entity-event.d.ts +1 -1
- package/dist/db/apply-entity-event.js +15 -47
- package/dist/db/blind-index-cleanup.js +91 -77
- package/dist/db/closed-connection.js +12 -19
- package/dist/db/collect-table-metas.js +1 -1
- package/dist/db/compound-types.d.ts +1 -1
- package/dist/db/compound-types.js +0 -7
- package/dist/db/config-seed.d.ts +2 -1
- package/dist/db/config-seed.js +6 -1
- package/dist/db/connection.d.ts +7 -6
- package/dist/db/connection.js +43 -24
- package/dist/db/cross-tenant-reads.d.ts +1 -1
- package/dist/db/cross-tenant-reads.js +1 -1
- package/dist/db/dialect.d.ts +0 -5
- package/dist/db/dialect.js +0 -3
- package/dist/db/driver-row-coercion.js +4 -4
- package/dist/db/driver-value-preparation.js +6 -4
- package/dist/db/eagerload.d.ts +2 -1
- package/dist/db/entity-field-encryption.d.ts +1 -1
- package/dist/db/entity-table-meta.d.ts +1 -1
- package/dist/db/event-store-executor-context.d.ts +3 -1
- package/dist/db/event-store-executor-context.js +4 -3
- 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.js +36 -5
- package/dist/db/event-store-executor-read-filter.d.ts +1 -1
- package/dist/db/event-store-executor-read-filter.js +6 -3
- package/dist/db/event-store-executor-read-rows.d.ts +2 -1
- package/dist/db/event-store-executor-read-search.d.ts +2 -1
- package/dist/db/event-store-executor-read-sort.d.ts +2 -1
- package/dist/db/event-store-executor-read.js +2 -2
- package/dist/db/event-store-executor-write-atomic.d.ts +4 -0
- package/dist/db/event-store-executor-write-atomic.js +42 -0
- package/dist/db/event-store-executor-write-steps.d.ts +3 -5
- package/dist/db/event-store-executor-write-steps.js +0 -5
- package/dist/db/event-store-executor-write.js +22 -19
- package/dist/db/event-store-executor.d.ts +1 -1
- package/dist/db/event-store-executor.js +2 -1
- package/dist/db/executor-managed-tables.d.ts +7 -1
- package/dist/db/executor-managed-tables.js +12 -3
- package/dist/db/index.d.ts +29 -28
- package/dist/db/index.js +22 -20
- package/dist/db/known-table-names.d.ts +6 -0
- package/dist/db/known-table-names.js +21 -0
- package/dist/db/list-aggregate-labels.d.ts +1 -1
- package/dist/db/located-timestamp.d.ts +1 -1
- package/dist/db/located-timestamp.js +0 -5
- package/dist/db/migrate-generator.d.ts +17 -1
- package/dist/db/migrate-generator.js +50 -20
- package/dist/db/migrate-primary-key.d.ts +12 -0
- package/dist/db/migrate-primary-key.js +45 -0
- package/dist/db/migrate-rls-diff.d.ts +20 -0
- package/dist/db/migrate-rls-diff.js +61 -0
- package/dist/db/migrate-runner.js +7 -3
- package/dist/db/money.d.ts +1 -1
- package/dist/db/money.js +1 -1
- package/dist/db/parent-ref-clause.d.ts +2 -1
- package/dist/db/pg-error.d.ts +2 -0
- package/dist/db/pg-error.js +37 -21
- package/dist/db/queries/backfill-pii.js +3 -6
- package/dist/db/queries/database-now.js +2 -1
- package/dist/db/queries/ddl.d.ts +3 -0
- package/dist/db/queries/ddl.js +34 -1
- package/dist/db/queries/event-store.js +4 -2
- package/dist/db/queries/event-tenants.d.ts +2 -0
- package/dist/db/queries/event-tenants.js +17 -0
- package/dist/db/queries/rebuild-rls-guard.d.ts +2 -0
- package/dist/db/queries/rebuild-rls-guard.js +29 -0
- package/dist/db/queries/rls-boot-catalog.d.ts +16 -0
- package/dist/db/queries/rls-boot-catalog.js +39 -0
- package/dist/db/queries/rls-catalog.d.ts +6 -0
- package/dist/db/queries/rls-catalog.js +82 -0
- package/dist/db/queries/rls-scope.d.ts +3 -0
- package/dist/db/queries/rls-scope.js +17 -0
- package/dist/db/queries/rls-shape.d.ts +10 -0
- package/dist/db/queries/rls-shape.js +42 -0
- package/dist/db/queries/session-advisory-lock.d.ts +2 -0
- package/dist/db/queries/session-advisory-lock.js +23 -0
- package/dist/db/queries/shadow-swap.d.ts +1 -2
- package/dist/db/queries/shadow-swap.js +11 -28
- package/dist/db/queries/test-stack.d.ts +4 -1
- package/dist/db/queries/test-stack.js +83 -2
- package/dist/db/query.d.ts +14 -0
- package/dist/db/query.js +77 -48
- package/dist/db/reference-data.d.ts +1 -1
- package/dist/db/reference-data.js +20 -3
- package/dist/db/render-ddl.d.ts +6 -2
- package/dist/db/render-ddl.js +7 -7
- package/dist/db/render-rls-ddl.d.ts +35 -0
- package/dist/db/render-rls-ddl.js +127 -0
- package/dist/db/replay-migration-sql.d.ts +3 -1
- package/dist/db/replay-migration-sql.js +22 -5
- package/dist/db/replay-rls.d.ts +25 -0
- package/dist/db/replay-rls.js +156 -0
- package/dist/db/row-level-security-boot-check.d.ts +5 -0
- package/dist/db/row-level-security-boot-check.js +85 -0
- package/dist/db/row-level-security-connection.d.ts +3 -0
- package/dist/db/row-level-security-connection.js +213 -0
- package/dist/db/row-level-security-scope.d.ts +33 -0
- package/dist/db/row-level-security-scope.js +127 -0
- package/dist/db/scope-tenant-db-to-tenant.d.ts +3 -0
- package/dist/db/scope-tenant-db-to-tenant.js +14 -0
- package/dist/db/sql-identifier.d.ts +1 -0
- package/dist/db/sql-identifier.js +3 -0
- package/dist/db/statement-timeout.js +2 -1
- package/dist/db/table-builder.d.ts +2 -1
- package/dist/db/table-builder.js +1 -1
- package/dist/db/table-info.js +1 -1
- package/dist/db/table-push.d.ts +11 -2
- package/dist/db/table-push.js +15 -2
- package/dist/db/tenant-db-runner.d.ts +3 -1
- package/dist/db/tenant-db-runner.js +4 -3
- package/dist/db/tenant-db-scoped-rebind.d.ts +10 -0
- package/dist/db/tenant-db-scoped-rebind.js +33 -0
- package/dist/db/tenant-db-statement-runner.d.ts +13 -0
- package/dist/db/tenant-db-statement-runner.js +48 -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.d.ts +17 -12
- package/dist/db/tenant-db.js +60 -121
- package/dist/db/tenant-id-filter.d.ts +3 -0
- package/dist/db/tenant-id-filter.js +30 -0
- package/dist/db/transaction-serialization.d.ts +6 -0
- package/dist/db/transaction-serialization.js +60 -0
- package/dist/derivatives/delete-stored-file.js +1 -1
- package/dist/derivatives/index.d.ts +2 -5
- package/dist/derivatives/index.js +2 -3
- package/dist/engine/access.d.ts +2 -1
- package/dist/engine/access.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/entity-handler.d.ts +2 -1
- package/dist/engine/boot-validator/entity-handler.js +1 -1
- 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/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 +11 -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/nav.d.ts +3 -1
- package/dist/engine/boot-validator/nav.js +1 -1
- package/dist/engine/boot-validator/ownership.d.ts +2 -1
- package/dist/engine/boot-validator/parent-ref.d.ts +2 -1
- package/dist/engine/boot-validator/projection-list-screens.d.ts +2 -1
- package/dist/engine/boot-validator/projection-list-screens.js +3 -13
- package/dist/engine/boot-validator/query-refs.d.ts +2 -1
- 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-forms.d.ts +2 -1
- package/dist/engine/boot-validator/screens-kind-entity-list.js +7 -0
- package/dist/engine/boot-validator/screens-kind-projection-list.js +4 -0
- package/dist/engine/boot-validator/screens-kind-shared.d.ts +3 -1
- package/dist/engine/boot-validator/screens-lists.js +12 -2
- package/dist/engine/boot-validator/screens.d.ts +2 -1
- package/dist/engine/boot-validator/workspaces.d.ts +3 -1
- package/dist/engine/bridge-stub.d.ts +5 -2
- package/dist/engine/bridge-stub.js +39 -24
- package/dist/engine/build-app-schema.d.ts +2 -1
- package/dist/engine/build-app-schema.js +9 -1
- package/dist/engine/build-config-feature-schema.js +6 -1
- package/dist/engine/config-helpers.d.ts +2 -2
- package/dist/engine/config-helpers.js +0 -9
- package/dist/engine/constants.d.ts +0 -10
- package/dist/engine/constants.js +0 -8
- package/dist/engine/create-app.js +1 -1
- package/dist/engine/define-feature.d.ts +2 -1
- package/dist/engine/define-feature.js +5 -2
- package/dist/engine/define-handler.d.ts +1 -1
- package/dist/engine/define-step.d.ts +0 -1
- package/dist/engine/define-step.js +0 -3
- package/dist/engine/embedded-derived.d.ts +1 -1
- package/dist/engine/entity-handlers.d.ts +2 -1
- package/dist/engine/entity-handlers.js +7 -7
- package/dist/engine/entity-list-runtime-options.js +3 -1
- package/dist/engine/entity-list-searchability.d.ts +2 -1
- package/dist/engine/event-def.d.ts +2 -1
- package/dist/engine/event-helpers.d.ts +3 -1
- package/dist/engine/event-helpers.js +3 -3
- package/dist/engine/extension-names.d.ts +2 -8
- package/dist/engine/extension-names.js +2 -2
- package/dist/engine/extension-selector-plugins.d.ts +1 -1
- package/dist/engine/extensions/storage-provider.d.ts +1 -2
- package/dist/engine/extensions/user-data.d.ts +0 -8
- package/dist/engine/factories.d.ts +2 -1
- package/dist/engine/feature-builder-state.d.ts +9 -1
- package/dist/engine/feature-builder-state.js +1 -0
- package/dist/engine/feature-changelog.d.ts +1 -1
- package/dist/engine/feature-config-events-jobs.d.ts +4 -1
- package/dist/engine/feature-entity-handlers.d.ts +5 -1
- package/dist/engine/feature-manifest.d.ts +1 -0
- package/dist/engine/feature-manifest.js +2 -1
- package/dist/engine/feature-ui-extensions.d.ts +7 -1
- package/dist/engine/feature-ui-extensions.js +6 -4
- package/dist/engine/field-access.d.ts +3 -1
- package/dist/engine/field-access.js +12 -0
- package/dist/engine/handler-field-references.d.ts +0 -4
- package/dist/engine/index.d.ts +58 -55
- package/dist/engine/index.js +23 -28
- package/dist/engine/instruction-fields.d.ts +1 -1
- package/dist/engine/personal-data-fields.d.ts +1 -1
- package/dist/engine/qualified-name.d.ts +0 -1
- package/dist/engine/read-claim.d.ts +2 -1
- package/dist/engine/registry-facade.js +0 -3
- package/dist/engine/registry-ingest.d.ts +2 -1
- package/dist/engine/registry-ingest.js +1 -2
- package/dist/engine/registry-state.d.ts +12 -2
- package/dist/engine/registry-state.js +2 -3
- package/dist/engine/registry-validate.d.ts +3 -1
- package/dist/engine/registry-validate.js +7 -7
- package/dist/engine/run-in.d.ts +1 -1
- package/dist/engine/run-in.js +0 -1
- package/dist/engine/schema-builder.d.ts +1 -1
- package/dist/engine/schema-builder.js +1 -1
- package/dist/engine/screen-filter-ops.d.ts +2 -1
- package/dist/engine/screen-filter-ops.js +2 -5
- package/dist/engine/soft-delete-cleanup.d.ts +1 -1
- package/dist/engine/soft-delete-cleanup.js +4 -4
- package/dist/engine/state-machine.js +2 -1
- package/dist/engine/steps/_read-source.js +1 -1
- package/dist/engine/steps/aggregate-append-event.js +2 -2
- package/dist/engine/steps/call-feature.js +2 -2
- package/dist/engine/steps/mail-send.js +1 -1
- package/dist/engine/steps/retry.js +1 -1
- package/dist/engine/steps/wait-for-event.js +1 -1
- package/dist/engine/steps/wait.js +1 -1
- package/dist/engine/steps/webhook-send.js +1 -1
- package/dist/engine/strip-disabled-features-from-app-schema.js +7 -3
- package/dist/engine/template-variables.d.ts +2 -1
- package/dist/engine/transfer-adjacency.d.ts +1 -1
- package/dist/engine/types/feature.d.ts +1 -0
- package/dist/engine/types/index.d.ts +8 -30
- package/dist/engine/types/index.js +2 -10
- package/dist/engine/types/screen.d.ts +1 -1
- package/dist/engine/types/screen.js +1 -1
- package/dist/engine/validation.d.ts +2 -1
- package/dist/entrypoint/index.d.ts +9 -35
- package/dist/entrypoint/index.js +10 -1
- package/dist/entrypoint/internals.d.ts +40 -0
- package/dist/entrypoint/internals.js +1 -0
- package/dist/entrypoint/row-level-security-mode-check.d.ts +3 -0
- package/dist/entrypoint/row-level-security-mode-check.js +19 -0
- package/dist/env/dry-run-internals.d.ts +10 -0
- package/dist/env/dry-run-internals.js +1 -0
- package/dist/env/dry-run.d.ts +1 -10
- package/dist/env/dry-run.js +2 -1
- package/dist/env/index.d.ts +1 -85
- package/dist/env/index.js +6 -50
- package/dist/env/internals.d.ts +87 -0
- package/dist/env/internals.js +53 -0
- package/dist/errors/classes.js +2 -2
- package/dist/errors/index.d.ts +5 -14
- package/dist/errors/index.js +4 -9
- package/dist/errors/member-resolution.js +1 -1
- package/dist/errors/reasons.d.ts +0 -1
- package/dist/errors/reasons.js +7 -7
- package/dist/errors/serialize.d.ts +0 -18
- package/dist/errors/serialize.js +0 -27
- package/dist/es-ops/index.d.ts +4 -4
- package/dist/es-ops/index.js +2 -2
- package/dist/es-ops/operations-schema.d.ts +1 -1
- package/dist/es-ops/types.d.ts +4 -5
- package/dist/event-store/admin-api-internals.d.ts +14 -0
- package/dist/event-store/admin-api-internals.js +1 -0
- package/dist/event-store/admin-api.d.ts +1 -14
- package/dist/event-store/admin-api.js +11 -6
- package/dist/event-store/archive.js +4 -2
- package/dist/event-store/errors.js +2 -2
- package/dist/event-store/event-store.d.ts +1 -0
- package/dist/event-store/event-store.js +42 -32
- package/dist/event-store/events-schema.js +4 -1
- package/dist/event-store/index.d.ts +11 -13
- package/dist/event-store/index.js +7 -9
- package/dist/event-store/provenance-append.js +1 -1
- package/dist/event-store/rebuild-dead-letter.js +4 -2
- package/dist/event-store/snapshot.js +4 -3
- package/dist/event-store/stream-scope.d.ts +2 -0
- package/dist/event-store/stream-scope.js +6 -0
- package/dist/event-store/upcaster-dead-letter.js +4 -2
- package/dist/event-store/upcaster.d.ts +2 -1
- package/dist/files/action-form-upload.d.ts +2 -1
- package/dist/files/action-form-upload.js +1 -1
- package/dist/files/assert-file-refs-belong-to-tenant.d.ts +2 -1
- package/dist/files/assert-file-refs-belong-to-tenant.js +1 -1
- package/dist/files/bind-pending-file-refs.d.ts +2 -1
- package/dist/files/bind-pending-file-refs.js +2 -1
- package/dist/files/bind-referenced-files.write.js +1 -1
- package/dist/files/create-file-ref-from-bytes.d.ts +6 -3
- package/dist/files/create-file-ref-from-bytes.js +6 -3
- package/dist/files/create-file-ref.d.ts +6 -2
- package/dist/files/create-file-ref.js +13 -4
- package/dist/files/dispose-removed-file-refs.d.ts +2 -1
- package/dist/files/dispose-removed-file-refs.js +1 -1
- package/dist/files/feature.js +2 -2
- package/dist/files/file-handle.js +1 -1
- package/dist/files/file-routes.d.ts +3 -1
- package/dist/files/file-routes.js +41 -4
- package/dist/files/index.d.ts +11 -13
- package/dist/files/index.js +5 -7
- package/dist/files/load-save-file-refs.d.ts +2 -1
- package/dist/files/load-save-file-refs.js +1 -1
- package/dist/files/provider-resolver.d.ts +6 -4
- package/dist/files/provider-resolver.js +5 -5
- package/dist/files/record-write-check.d.ts +6 -0
- package/dist/files/record-write-check.js +13 -0
- package/dist/files/storage-tracking.js +4 -2
- package/dist/files/sweep-orphaned-uploads.d.ts +1 -1
- package/dist/files/sweep-orphaned-uploads.js +1 -1
- package/dist/files/upload-ledger.d.ts +2 -2
- package/dist/files/upload-ledger.js +4 -4
- package/dist/i18n/index.d.ts +2 -8
- package/dist/i18n/index.js +1 -1
- package/dist/i18n/internals.d.ts +7 -0
- package/dist/i18n/internals.js +1 -0
- package/dist/i18n/required-surface-keys.d.ts +4 -1
- package/dist/i18n/required-surface-keys.js +7 -2
- package/dist/internal/row-level-security.d.ts +3 -0
- package/dist/internal/row-level-security.js +3 -0
- package/dist/internal/stack-support.d.ts +1 -1
- package/dist/internal/stack-support.js +1 -1
- package/dist/internal/testing.d.ts +1 -0
- package/dist/internal/testing.js +10 -6
- package/dist/jobs/index.d.ts +1 -1
- package/dist/jobs/job-append-event.d.ts +12 -0
- package/dist/jobs/job-append-event.js +30 -0
- package/dist/jobs/job-runner-context.d.ts +2 -2
- package/dist/jobs/job-runner-context.js +46 -16
- package/dist/jobs/job-runner-execution.d.ts +1 -1
- package/dist/jobs/job-runner-execution.js +6 -4
- package/dist/jobs/job-runner-gates.d.ts +1 -1
- package/dist/jobs/job-runner-gates.js +0 -1
- package/dist/jobs/job-runner-helpers.d.ts +11 -4
- package/dist/jobs/job-runner-helpers.js +10 -1
- package/dist/jobs/job-runner-scope.d.ts +5 -2
- package/dist/jobs/job-runner-scope.js +6 -6
- package/dist/jobs/job-runner-types.d.ts +2 -1
- package/dist/jobs/job-runner.js +22 -6
- package/dist/jobs/max-per-tenant.d.ts +1 -1
- package/dist/logging/index.d.ts +1 -2
- package/dist/logging/index.js +1 -1
- package/dist/logging/pino-logger.js +1 -1
- package/dist/logging/utils.d.ts +1 -0
- package/dist/logging/utils.js +6 -0
- package/dist/migrations/index.d.ts +3 -3
- package/dist/migrations/index.js +3 -3
- package/dist/migrations/pending-rebuilds.d.ts +1 -1
- package/dist/migrations/pending-rebuilds.js +1 -1
- package/dist/migrations/pii-event-backfill.js +4 -1
- package/dist/observability/escape-hatch-report.d.ts +2 -1
- package/dist/observability/escape-hatch-report.js +9 -2
- package/dist/observability/index.d.ts +7 -15
- package/dist/observability/index.js +4 -12
- package/dist/observability/metric-validator.js +1 -1
- package/dist/observability/metrics-handle.d.ts +0 -1
- package/dist/observability/metrics-handle.js +2 -15
- package/dist/observability/recording-tracer.d.ts +1 -2
- package/dist/observability/recording-tracer.js +0 -6
- package/dist/observability/sensitive-filter.d.ts +0 -2
- package/dist/observability/sensitive-filter.js +0 -29
- package/dist/pipeline/active-membership.d.ts +2 -1
- package/dist/pipeline/active-membership.js +6 -2
- package/dist/pipeline/after-rollback-sink.d.ts +6 -0
- package/dist/pipeline/after-rollback-sink.js +32 -0
- package/dist/pipeline/append-event-core.d.ts +2 -1
- package/dist/pipeline/append-event-core.js +2 -2
- package/dist/pipeline/attach-outside-transaction.d.ts +3 -0
- package/dist/pipeline/attach-outside-transaction.js +13 -0
- package/dist/pipeline/auth-claims-resolver.d.ts +2 -1
- package/dist/pipeline/cascade-handler.d.ts +2 -1
- package/dist/pipeline/cascade-handler.js +3 -2
- package/dist/pipeline/config-accessor-factory.d.ts +1 -1
- package/dist/pipeline/config-accessor-factory.js +1 -1
- package/dist/pipeline/context-services.d.ts +56 -0
- package/dist/pipeline/context-services.js +73 -0
- package/dist/pipeline/dispatch-batch.d.ts +2 -1
- package/dist/pipeline/dispatch-batch.js +39 -19
- package/dist/pipeline/dispatch-query.js +5 -4
- package/dist/pipeline/dispatch-scope.d.ts +6 -0
- package/dist/pipeline/dispatch-scope.js +27 -0
- package/dist/pipeline/dispatch-shared.d.ts +13 -9
- package/dist/pipeline/dispatch-shared.js +75 -194
- package/dist/pipeline/dispatch-stream.js +8 -4
- package/dist/pipeline/dispatch-write.d.ts +2 -1
- package/dist/pipeline/dispatch-write.js +10 -8
- package/dist/pipeline/dispatcher-utils.d.ts +3 -1
- package/dist/pipeline/dispatcher-utils.js +8 -2
- package/dist/pipeline/dispatcher.d.ts +3 -2
- package/dist/pipeline/dispatcher.js +4 -3
- package/dist/pipeline/event-consumer-state.d.ts +0 -1
- package/dist/pipeline/event-dispatcher-delivery.d.ts +1 -1
- package/dist/pipeline/event-dispatcher-delivery.js +7 -3
- package/dist/pipeline/event-dispatcher.d.ts +3 -2
- package/dist/pipeline/event-dispatcher.js +13 -12
- package/dist/pipeline/event-retention.js +59 -56
- package/dist/pipeline/handler-access-error.js +2 -1
- package/dist/pipeline/handler-context-auth.d.ts +5 -6
- package/dist/pipeline/handler-context-auth.js +7 -4
- package/dist/pipeline/handler-context-config.d.ts +2 -5
- package/dist/pipeline/handler-context-config.js +2 -2
- package/dist/pipeline/handler-context-db.d.ts +32 -7
- package/dist/pipeline/handler-context-db.js +26 -17
- package/dist/pipeline/handler-context-dispatch.d.ts +14 -6
- package/dist/pipeline/handler-context-dispatch.js +18 -5
- package/dist/pipeline/handler-context-events.d.ts +22 -38
- package/dist/pipeline/handler-context-events.js +42 -35
- package/dist/pipeline/handler-context-member-resolution.d.ts +2 -0
- package/dist/pipeline/handler-context-member-resolution.js +110 -0
- package/dist/pipeline/handler-context-notify.d.ts +3 -2
- package/dist/pipeline/handler-context-notify.js +1 -1
- package/dist/pipeline/handler-context-observability.d.ts +2 -4
- package/dist/pipeline/handler-context-observability.js +7 -5
- package/dist/pipeline/handler-context-phase-denials.d.ts +11 -0
- package/dist/pipeline/handler-context-phase-denials.js +98 -0
- package/dist/pipeline/handler-context-scope.d.ts +7 -10
- package/dist/pipeline/handler-context-scope.js +18 -20
- package/dist/pipeline/index.d.ts +9 -21
- package/dist/pipeline/index.js +5 -7
- package/dist/pipeline/irreversible-operation-gate.d.ts +1 -1
- package/dist/pipeline/irreversible-operation-gate.js +2 -2
- package/dist/pipeline/lifecycle-pipeline.d.ts +8 -7
- package/dist/pipeline/lifecycle-pipeline.js +9 -15
- package/dist/pipeline/member-read-only-transaction.js +11 -3
- package/dist/pipeline/member-reader.d.ts +1 -1
- package/dist/pipeline/member-reader.js +2 -2
- package/dist/pipeline/msp-rebuild.js +5 -3
- package/dist/pipeline/multi-stream-apply-context.js +3 -2
- package/dist/pipeline/notify-scope.d.ts +2 -1
- package/dist/pipeline/pre-handler-gates.d.ts +2 -1
- package/dist/pipeline/pre-transaction-reservations.d.ts +3 -3
- package/dist/pipeline/pre-transaction-reservations.js +9 -7
- package/dist/pipeline/projection-rebuild.js +12 -5
- package/dist/pipeline/projection-state.d.ts +0 -6
- package/dist/pipeline/projection-state.js +0 -5
- package/dist/pipeline/projections-runner.d.ts +2 -1
- package/dist/pipeline/projections-runner.js +10 -6
- package/dist/pipeline/system-hooks.d.ts +6 -2
- package/dist/pipeline/system-hooks.js +61 -10
- package/dist/pipeline/system-identity-switch.d.ts +4 -2
- package/dist/pipeline/system-identity-switch.js +51 -36
- package/dist/pipeline/typed-append-event.d.ts +2 -0
- package/dist/pipeline/typed-append-event.js +4 -0
- package/dist/pipeline/write-origin.js +2 -2
- package/dist/pipeline/write-result-masking.d.ts +1 -1
- package/dist/pipeline/write-result-masking.js +13 -10
- package/dist/random/index.d.ts +1 -2
- package/dist/random/index.js +1 -2
- package/dist/rate-limit/bucket.d.ts +2 -1
- package/dist/rate-limit/middleware.js +2 -1
- package/dist/redis/index.d.ts +3 -4
- package/dist/redis/index.js +2 -2
- package/dist/schema-cli/apply-command.js +5 -2
- package/dist/schema-cli/baseline-command.js +7 -2
- package/dist/schema-cli/generate-command.js +7 -9
- package/dist/schema-cli/shared.d.ts +4 -0
- package/dist/schema-cli/shared.js +21 -2
- package/dist/schema-cli/status-command.js +8 -2
- package/dist/schema-cli/validate-command.js +52 -4
- package/dist/schema-cli.d.ts +0 -1
- package/dist/search/configure-tenant-index.d.ts +1 -3
- package/dist/search/configure-tenant-index.js +0 -4
- package/dist/search/index.d.ts +1 -4
- package/dist/search/index.js +0 -2
- package/dist/search/meilisearch-adapter-internals.d.ts +8 -0
- package/dist/search/meilisearch-adapter-internals.js +9 -0
- package/dist/search/meilisearch-adapter.d.ts +1 -8
- package/dist/search/meilisearch-adapter.js +6 -7
- package/dist/search/meilisearch-peer.d.ts +2 -0
- package/dist/search/meilisearch-peer.js +5 -0
- package/dist/search/reindex-entity.js +107 -91
- package/dist/secrets/index.d.ts +3 -4
- package/dist/secrets/index.js +2 -3
- package/dist/seeding/index.d.ts +2 -2
- package/dist/seeding/index.js +1 -2
- package/dist/time/index.d.ts +2 -2
- package/dist/time/index.js +1 -1
- package/dist/time/tz-context.d.ts +0 -5
- package/dist/time/tz-context.js +0 -9
- package/dist/ui-types/index.d.ts +8 -11
- package/dist/ui-types/index.js +3 -3
- package/dist/upgrade-changelog.d.ts +3 -0
- package/dist/upgrade-changelog.js +18 -0
- package/dist/upgrade-cli-internals.d.ts +8 -0
- package/dist/upgrade-cli-internals.js +63 -0
- package/dist/upgrade-cli.d.ts +1 -18
- package/dist/upgrade-cli.js +29 -137
- package/dist/upgrade-marker-parse.d.ts +32 -0
- package/dist/upgrade-marker-parse.js +73 -0
- package/dist/utils/import-optional-peer.d.ts +6 -0
- package/dist/utils/import-optional-peer.js +19 -0
- package/dist/utils/index.d.ts +1 -4
- package/dist/utils/index.js +1 -4
- package/package.json +27 -20
- package/src/changes.json +451 -0
- package/dist/crypto/request-kms-cache.d.ts +0 -8
- package/dist/crypto/request-kms-cache.js +0 -21
- package/dist/engine/codemod/index.d.ts +0 -2
- package/dist/engine/codemod/index.js +0 -1
- package/dist/engine/codemod/pipeline-codemod.d.ts +0 -50
- package/dist/engine/codemod/pipeline-codemod.js +0 -536
- package/dist/engine/feature-ast/entity-field-types.d.ts +0 -13
- package/dist/engine/feature-ast/entity-field-types.js +0 -35
- package/dist/engine/feature-ast/extractors/ai-steps.d.ts +0 -6
- package/dist/engine/feature-ast/extractors/ai-steps.js +0 -254
- package/dist/engine/feature-ast/extractors/events.d.ts +0 -5
- package/dist/engine/feature-ast/extractors/events.js +0 -275
- package/dist/engine/feature-ast/extractors/handlers.d.ts +0 -24
- package/dist/engine/feature-ast/extractors/handlers.js +0 -358
- package/dist/engine/feature-ast/extractors/hooks.d.ts +0 -24
- package/dist/engine/feature-ast/extractors/hooks.js +0 -248
- package/dist/engine/feature-ast/extractors/index.d.ts +0 -14
- package/dist/engine/feature-ast/extractors/index.js +0 -13
- package/dist/engine/feature-ast/extractors/jobs-routes.d.ts +0 -7
- package/dist/engine/feature-ast/extractors/jobs-routes.js +0 -145
- package/dist/engine/feature-ast/extractors/projections-screens.d.ts +0 -10
- package/dist/engine/feature-ast/extractors/projections-screens.js +0 -195
- package/dist/engine/feature-ast/extractors/resolve-import.d.ts +0 -21
- package/dist/engine/feature-ast/extractors/resolve-import.js +0 -180
- package/dist/engine/feature-ast/extractors/round1.d.ts +0 -10
- package/dist/engine/feature-ast/extractors/round1.js +0 -81
- package/dist/engine/feature-ast/extractors/round2.d.ts +0 -7
- package/dist/engine/feature-ast/extractors/round2.js +0 -191
- package/dist/engine/feature-ast/extractors/round3.d.ts +0 -23
- package/dist/engine/feature-ast/extractors/round3.js +0 -292
- package/dist/engine/feature-ast/extractors/round5.d.ts +0 -9
- package/dist/engine/feature-ast/extractors/round5.js +0 -68
- package/dist/engine/feature-ast/extractors/round6.d.ts +0 -4
- package/dist/engine/feature-ast/extractors/round6.js +0 -17
- package/dist/engine/feature-ast/extractors/shared.d.ts +0 -94
- package/dist/engine/feature-ast/extractors/shared.js +0 -343
- package/dist/engine/feature-ast/index.d.ts +0 -16
- package/dist/engine/feature-ast/index.js +0 -10
- package/dist/engine/feature-ast/parse.d.ts +0 -31
- package/dist/engine/feature-ast/parse.js +0 -483
- package/dist/engine/feature-ast/patch.d.ts +0 -210
- package/dist/engine/feature-ast/patch.js +0 -610
- package/dist/engine/feature-ast/patcher.d.ts +0 -217
- package/dist/engine/feature-ast/patcher.js +0 -281
- package/dist/engine/feature-ast/pattern-change-schema.d.ts +0 -1476
- package/dist/engine/feature-ast/pattern-change-schema.js +0 -1226
- package/dist/engine/feature-ast/pattern-id-matchers.d.ts +0 -7
- package/dist/engine/feature-ast/pattern-id-matchers.js +0 -210
- package/dist/engine/feature-ast/patterns.d.ts +0 -318
- package/dist/engine/feature-ast/patterns.js +0 -106
- package/dist/engine/feature-ast/render.d.ts +0 -47
- package/dist/engine/feature-ast/render.js +0 -662
- package/dist/engine/feature-ast/source-location.d.ts +0 -17
- package/dist/engine/feature-ast/source-location.js +0 -27
- package/dist/engine/pattern-library/index.d.ts +0 -2
- package/dist/engine/pattern-library/index.js +0 -3
- package/dist/engine/pattern-library/library.d.ts +0 -14
- package/dist/engine/pattern-library/library.js +0 -93
- package/dist/engine/pattern-library/mixed-schemas.d.ts +0 -16
- package/dist/engine/pattern-library/mixed-schemas.js +0 -559
- package/dist/engine/pattern-library/opaque-schemas.d.ts +0 -7
- package/dist/engine/pattern-library/opaque-schemas.js +0 -115
- package/dist/engine/pattern-library/shared-fields.d.ts +0 -122
- package/dist/engine/pattern-library/shared-fields.js +0 -88
- package/dist/engine/pattern-library/static-schemas.d.ts +0 -20
- package/dist/engine/pattern-library/static-schemas.js +0 -478
- package/dist/engine/pattern-library/types.d.ts +0 -158
- package/dist/engine/pattern-library/types.js +0 -36
- package/dist/engine/provider-plugin-context.d.ts +0 -3
- package/dist/engine/provider-plugin-context.js +0 -28
- package/dist/engine/resolve-config-or-param.d.ts +0 -18
- package/dist/engine/resolve-config-or-param.js +0 -76
- package/src/scripts/codemod/README.md +0 -187
- package/src/scripts/codemod/codemod-support.ts +0 -334
- package/src/scripts/codemod/collect-source-files.ts +0 -48
- package/src/scripts/codemod/crypto-shredding-testing-move.ts +0 -119
- package/src/scripts/codemod/db-factory.ts +0 -810
- package/src/scripts/codemod/feature-factory.ts +0 -429
- package/src/scripts/codemod/feature-layout.ts +0 -424
- package/src/scripts/codemod/import-extensions.ts +0 -118
- package/src/scripts/codemod/migrate-cross-tenant.ts +0 -328
- package/src/scripts/codemod/migrate-db-raw.ts +0 -401
- package/src/scripts/codemod/migrate-open-to-all.ts +0 -235
- package/src/scripts/codemod/object-form.ts +0 -746
- package/src/scripts/codemod/package-merge.ts +0 -299
- package/src/scripts/codemod/pii-personal-migration.ts +0 -816
- package/src/scripts/codemod/remove-engine-payload-casts.ts +0 -318
- package/src/scripts/codemod/s3-imports.ts +0 -236
- package/src/scripts/codemod/tenant-db-options.ts +0 -454
- package/src/scripts/codemod/testing-injection.ts +0 -215
- package/src/scripts/codemod/testing-move-targets.ts +0 -161
- package/src/scripts/codemod/testing-move.ts +0 -542
- package/src/scripts/codemod/unprocessable-error-details-reason.ts +0 -411
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
2
|
+
import { referenceCell } from "./export-cells-reference.js";
|
|
3
|
+
const DATE_FORMAT = "yyyy-mm-dd";
|
|
4
|
+
const TIMESTAMP_FORMAT = "yyyy-mm-dd hh:mm";
|
|
5
|
+
const PLAIN_MONEY_FORMAT = "#,##0.00";
|
|
6
|
+
const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
7
|
+
const CURRENCY_CODE = /^[A-Z]{3}$/;
|
|
8
|
+
function isNullish(value) {
|
|
9
|
+
return value === null || value === undefined;
|
|
10
|
+
}
|
|
11
|
+
function textCell(value) {
|
|
12
|
+
return { value, type: String };
|
|
13
|
+
}
|
|
14
|
+
function numberCell(value, format) {
|
|
15
|
+
return { value, type: Number, ...(format !== undefined && { format }) };
|
|
16
|
+
}
|
|
17
|
+
function toFiniteNumber(value) {
|
|
18
|
+
const n = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
|
|
19
|
+
return value !== "" && Number.isFinite(n) ? n : undefined;
|
|
20
|
+
}
|
|
21
|
+
function stringify(value) {
|
|
22
|
+
if (typeof value === "string")
|
|
23
|
+
return value;
|
|
24
|
+
if (typeof value === "object" && value !== null) {
|
|
25
|
+
// Temporal values stringify to ISO via toString; plain data goes through JSON.
|
|
26
|
+
const text = String(value);
|
|
27
|
+
return text === "[object Object]" ? JSON.stringify(value) : text;
|
|
28
|
+
}
|
|
29
|
+
return String(value);
|
|
30
|
+
}
|
|
31
|
+
function fromEpochMillis(value) {
|
|
32
|
+
if (value instanceof Date)
|
|
33
|
+
return Number.isNaN(value.getTime()) ? undefined : value;
|
|
34
|
+
if (typeof value === "object" && value !== null && "epochMilliseconds" in value) {
|
|
35
|
+
return fromEpochMillis(new Date(Number(value.epochMilliseconds)));
|
|
36
|
+
}
|
|
37
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
38
|
+
return fromEpochMillis(new Date(value));
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
// Date-only values are anchored at UTC midnight, never parsed through the local zone.
|
|
43
|
+
function toUtcDate(value) {
|
|
44
|
+
const text = typeof value === "string" ? value : isNullish(value) ? "" : stringify(value);
|
|
45
|
+
if (ISO_DATE.test(text))
|
|
46
|
+
return fromEpochMillis(new Date(`${text}T00:00:00Z`));
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
function dateCell(value) {
|
|
50
|
+
const date = toUtcDate(value) ?? fromEpochMillis(value);
|
|
51
|
+
return date === undefined
|
|
52
|
+
? textCell(stringify(value))
|
|
53
|
+
: { value: date, type: Date, format: DATE_FORMAT };
|
|
54
|
+
}
|
|
55
|
+
// The sheet has no time zone, so the instant is shifted to the wall clock of `timeZone` and
|
|
56
|
+
// stored as if that wall clock were UTC.
|
|
57
|
+
function timestampCell(value, timeZone) {
|
|
58
|
+
const date = fromEpochMillis(value);
|
|
59
|
+
if (date === undefined)
|
|
60
|
+
return textCell(stringify(value));
|
|
61
|
+
const wall = Temporal.Instant.fromEpochMilliseconds(date.getTime()).toZonedDateTimeISO(timeZone);
|
|
62
|
+
const shifted = new Date(Date.UTC(wall.year, wall.month - 1, wall.day, wall.hour, wall.minute, wall.second));
|
|
63
|
+
return { value: shifted, type: Date, format: TIMESTAMP_FORMAT };
|
|
64
|
+
}
|
|
65
|
+
// `at` is the wall clock in the record's own zone without offset; reading it as UTC keeps the
|
|
66
|
+
// digits the user saw in the list.
|
|
67
|
+
function locatedTimestampCell(value) {
|
|
68
|
+
const at = typeof value === "object" && value !== null && "at" in value ? String(value.at) : undefined;
|
|
69
|
+
const date = at === undefined ? undefined : fromEpochMillis(new Date(`${at}Z`));
|
|
70
|
+
return date === undefined
|
|
71
|
+
? textCell(stringify(value))
|
|
72
|
+
: { value: date, type: Date, format: TIMESTAMP_FORMAT };
|
|
73
|
+
}
|
|
74
|
+
function moneyCell(value) {
|
|
75
|
+
if (typeof value !== "object" || value === null || !("amount" in value)) {
|
|
76
|
+
const n = toFiniteNumber(value);
|
|
77
|
+
return n === undefined ? textCell(stringify(value)) : numberCell(n, PLAIN_MONEY_FORMAT);
|
|
78
|
+
}
|
|
79
|
+
const amount = toFiniteNumber(value.amount);
|
|
80
|
+
if (amount === undefined)
|
|
81
|
+
return textCell(stringify(value.amount));
|
|
82
|
+
const currency = "currency" in value ? String(value.currency) : "";
|
|
83
|
+
const format = CURRENCY_CODE.test(currency)
|
|
84
|
+
? `${PLAIN_MONEY_FORMAT} "${currency}"`
|
|
85
|
+
: PLAIN_MONEY_FORMAT;
|
|
86
|
+
return numberCell(amount, format);
|
|
87
|
+
}
|
|
88
|
+
function integerCell(value, kind) {
|
|
89
|
+
const n = toFiniteNumber(value);
|
|
90
|
+
if (n === undefined)
|
|
91
|
+
return textCell(stringify(value));
|
|
92
|
+
// A bigInt beyond 2^53 would lose digits as a spreadsheet number.
|
|
93
|
+
if (kind === "bigInt" && !Number.isSafeInteger(n))
|
|
94
|
+
return textCell(stringify(value));
|
|
95
|
+
return numberCell(n);
|
|
96
|
+
}
|
|
97
|
+
function optionLabel(prefix, raw, translate) {
|
|
98
|
+
const text = String(raw);
|
|
99
|
+
const label = translate(`${prefix}${text}`);
|
|
100
|
+
return label === `${prefix}${text}` ? text : label;
|
|
101
|
+
}
|
|
102
|
+
function autoCell(value) {
|
|
103
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
104
|
+
return numberCell(value);
|
|
105
|
+
if (typeof value === "boolean")
|
|
106
|
+
return { value, type: Boolean };
|
|
107
|
+
return textCell(stringify(value));
|
|
108
|
+
}
|
|
109
|
+
function optionCell(cell, value, translate) {
|
|
110
|
+
const values = cell.kind === "multiSelect" && Array.isArray(value) ? value : [value];
|
|
111
|
+
return textCell(values.map((v) => optionLabel(cell.optionLabelKeyPrefix, v, translate)).join(", "));
|
|
112
|
+
}
|
|
113
|
+
const SIMPLE_CELL_BUILDERS = {
|
|
114
|
+
number: (value) => integerCell(value, "number"),
|
|
115
|
+
decimal: (value) => integerCell(value, "decimal"),
|
|
116
|
+
bigInt: (value) => integerCell(value, "bigInt"),
|
|
117
|
+
money: (value) => moneyCell(value),
|
|
118
|
+
date: (value) => dateCell(value),
|
|
119
|
+
timestamp: timestampCell,
|
|
120
|
+
locatedTimestamp: (value) => locatedTimestampCell(value),
|
|
121
|
+
boolean: (value) => typeof value === "boolean" ? { value, type: Boolean } : textCell(stringify(value)),
|
|
122
|
+
auto: (value) => autoCell(value),
|
|
123
|
+
text: (value) => textCell(stringify(value)),
|
|
124
|
+
};
|
|
125
|
+
/** Never emits a formula cell and never reinterprets strings: text always stays text. */
|
|
126
|
+
export function toExportCell(column, context) {
|
|
127
|
+
const { cell, field } = column;
|
|
128
|
+
const value = context.row[field];
|
|
129
|
+
if (isNullish(value))
|
|
130
|
+
return null;
|
|
131
|
+
if (cell.kind === "reference")
|
|
132
|
+
return referenceCell(cell, field, context.row);
|
|
133
|
+
if ("optionLabelKeyPrefix" in cell)
|
|
134
|
+
return optionCell(cell, value, context.translate);
|
|
135
|
+
return SIMPLE_CELL_BUILDERS[cell.kind](value, context.timeZone);
|
|
136
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { InternalError } from "../../errors/index.js";
|
|
2
|
+
import { createFallbackLogger } from "../../logging/utils.js";
|
|
3
|
+
import { CIPHERTEXT_RE, CIPHERTEXT_REDACT_RE, CIPHERTEXT_REDACTION } from "../pii-leak-guard.js";
|
|
4
|
+
const log = createFallbackLogger("kumiko:export-xlsx");
|
|
5
|
+
// The response guard only scans JSON, so a workbook needs its own check: a ciphertext in a cell
|
|
6
|
+
// means a raw DB read reached the export. Same policy as the guard: loud outside production,
|
|
7
|
+
// redacted and logged in production.
|
|
8
|
+
function guardText(text, isProduction) {
|
|
9
|
+
if (!CIPHERTEXT_RE.test(text))
|
|
10
|
+
return text;
|
|
11
|
+
const detail = "XLSX export contains a PII ciphertext — a raw DB read leaked into the sheet. " +
|
|
12
|
+
"Decrypt before returning (decryptStoredPii / executor read path).";
|
|
13
|
+
if (!isProduction)
|
|
14
|
+
throw new InternalError({ message: detail });
|
|
15
|
+
log.error(detail);
|
|
16
|
+
return text.replace(CIPHERTEXT_REDACT_RE, CIPHERTEXT_REDACTION);
|
|
17
|
+
}
|
|
18
|
+
function guardCell(cell, isProduction) {
|
|
19
|
+
if (typeof cell !== "object" || cell === null || !("value" in cell))
|
|
20
|
+
return cell;
|
|
21
|
+
if (typeof cell.value !== "string")
|
|
22
|
+
return cell;
|
|
23
|
+
return { ...cell, value: guardText(cell.value, isProduction) };
|
|
24
|
+
}
|
|
25
|
+
export function guardSheetAgainstCiphertext(sheet, isProduction = process.env["NODE_ENV"] === "production") {
|
|
26
|
+
return sheet.map((row) => row.map((cell) => guardCell(cell, isProduction)));
|
|
27
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { EntityDefinition } from "@cosmicdrift/kumiko-types/fields";
|
|
2
|
+
import type { ListColumnSpec } from "@cosmicdrift/kumiko-types/screen";
|
|
3
|
+
import type { SessionUser } from "../../engine/types/index.ts";
|
|
4
|
+
import type { ReferenceCellKind } from "./export-cells-reference.ts";
|
|
5
|
+
export type SimpleCellKind = "text" | "number" | "decimal" | "bigInt" | "money" | "date" | "timestamp" | "locatedTimestamp" | "boolean" | "auto";
|
|
6
|
+
export type ExportCellKind = {
|
|
7
|
+
readonly kind: SimpleCellKind;
|
|
8
|
+
} | ReferenceCellKind | {
|
|
9
|
+
readonly kind: "select" | "multiSelect";
|
|
10
|
+
/** `<feature>:entity:<entity>:field:<field>:option:` — the option value is appended. */
|
|
11
|
+
readonly optionLabelKeyPrefix: string;
|
|
12
|
+
};
|
|
13
|
+
export type ExportColumn = {
|
|
14
|
+
readonly field: string;
|
|
15
|
+
readonly headerKey: string;
|
|
16
|
+
readonly headerFallback: string;
|
|
17
|
+
readonly cell: ExportCellKind;
|
|
18
|
+
};
|
|
19
|
+
export type ExportColumnSource = {
|
|
20
|
+
readonly featureName: string;
|
|
21
|
+
readonly columns: readonly ListColumnSpec[];
|
|
22
|
+
readonly entity: EntityDefinition | undefined;
|
|
23
|
+
readonly entityName: string | undefined;
|
|
24
|
+
readonly user: Pick<SessionUser, "roles">;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Resolves the requested column ids against the screen's declared columns, in client order.
|
|
28
|
+
* Virtual columns and, on an entityList, encrypted/sensitive fields are dropped without error:
|
|
29
|
+
* the zip response bypasses the PII-leak guard, so this is the only place that keeps them out.
|
|
30
|
+
*/
|
|
31
|
+
export declare function resolveExportColumns(source: ExportColumnSource, requested: readonly string[]): ExportColumn[];
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { holdsCiphertextOrSecret } from "../../db/entity-field-encryption.js";
|
|
2
|
+
import { userMayReadFieldInBulk } from "../../engine/field-access.js";
|
|
3
|
+
import { normalizeListColumn } from "../../engine/screen-helpers.js";
|
|
4
|
+
import { exportRejection } from "./export-errors.js";
|
|
5
|
+
const PROJECTION_PSEUDO_ENTITY = "__projection__";
|
|
6
|
+
const FIELD_KIND_BY_TYPE = {
|
|
7
|
+
number: "number",
|
|
8
|
+
decimal: "decimal",
|
|
9
|
+
bigInt: "bigInt",
|
|
10
|
+
money: "money",
|
|
11
|
+
date: "date",
|
|
12
|
+
timestamp: "timestamp",
|
|
13
|
+
locatedTimestamp: "locatedTimestamp",
|
|
14
|
+
boolean: "boolean",
|
|
15
|
+
};
|
|
16
|
+
function fieldLabelKey(featureName, entityName, field) {
|
|
17
|
+
return `${featureName}:entity:${entityName}:field:${field}`;
|
|
18
|
+
}
|
|
19
|
+
function entityCellKind(source, entity, field) {
|
|
20
|
+
const def = entity.fields[field];
|
|
21
|
+
if (def?.type === "reference") {
|
|
22
|
+
return {
|
|
23
|
+
kind: "reference",
|
|
24
|
+
labelField: def.labelField ?? "id",
|
|
25
|
+
multiple: def.multiple === true,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const type = def?.type ?? "text";
|
|
29
|
+
if (type === "select" || type === "multiSelect") {
|
|
30
|
+
return {
|
|
31
|
+
kind: type,
|
|
32
|
+
optionLabelKeyPrefix: `${fieldLabelKey(source.featureName, source.entityName ?? "", field)}:option:`,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return { kind: FIELD_KIND_BY_TYPE[type] ?? "text" };
|
|
36
|
+
}
|
|
37
|
+
function projectionCellKind(valueType) {
|
|
38
|
+
if (valueType === "number" || valueType === "decimal" || valueType === "bigInt") {
|
|
39
|
+
return { kind: valueType };
|
|
40
|
+
}
|
|
41
|
+
return { kind: valueType === "money" ? "money" : "auto" };
|
|
42
|
+
}
|
|
43
|
+
function isExportableOnEntity(source, entity, field, virtual) {
|
|
44
|
+
if (virtual || !userMayReadFieldInBulk(entity, field, source.user))
|
|
45
|
+
return false;
|
|
46
|
+
return !holdsCiphertextOrSecret(entity, field);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Resolves the requested column ids against the screen's declared columns, in client order.
|
|
50
|
+
* Virtual columns and, on an entityList, encrypted/sensitive fields are dropped without error:
|
|
51
|
+
* the zip response bypasses the PII-leak guard, so this is the only place that keeps them out.
|
|
52
|
+
*/
|
|
53
|
+
export function resolveExportColumns(source, requested) {
|
|
54
|
+
const declared = new Map(source.columns.map((spec) => normalizeListColumn(spec)).map((c) => [c.field, c]));
|
|
55
|
+
const seen = new Set();
|
|
56
|
+
const resolved = [];
|
|
57
|
+
for (const field of requested) {
|
|
58
|
+
const column = declared.get(field);
|
|
59
|
+
if (column === undefined) {
|
|
60
|
+
throw exportRejection("export_unknown_column", "columns", { column: field });
|
|
61
|
+
}
|
|
62
|
+
if (seen.has(field))
|
|
63
|
+
continue;
|
|
64
|
+
seen.add(field);
|
|
65
|
+
const virtual = column.virtual === true;
|
|
66
|
+
const { entity } = source;
|
|
67
|
+
if (entity !== undefined && !isExportableOnEntity(source, entity, field, virtual))
|
|
68
|
+
continue;
|
|
69
|
+
if (entity === undefined && virtual)
|
|
70
|
+
continue;
|
|
71
|
+
const cell = entity !== undefined
|
|
72
|
+
? entityCellKind(source, entity, field)
|
|
73
|
+
: projectionCellKind(column.valueType);
|
|
74
|
+
const headerEntity = source.entityName ?? PROJECTION_PSEUDO_ENTITY;
|
|
75
|
+
resolved.push({
|
|
76
|
+
field,
|
|
77
|
+
headerKey: column.label ?? fieldLabelKey(source.featureName, headerEntity, field),
|
|
78
|
+
headerFallback: column.label ?? field,
|
|
79
|
+
cell,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
if (resolved.length === 0)
|
|
83
|
+
throw exportRejection("export_no_columns", "columns");
|
|
84
|
+
return resolved;
|
|
85
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ValidationError } from "../../errors/index.ts";
|
|
2
|
+
export declare const EXPORT_TOO_MANY_I18N_KEY = "kumiko.list.export-too-many";
|
|
3
|
+
export type ExportRejectReason = "export_invalid_request" | "export_unknown_screen" | "export_not_enabled" | "export_unknown_column" | "export_no_columns";
|
|
4
|
+
export declare function exportRejection(reason: ExportRejectReason, path: string, params?: Readonly<Record<string, unknown>>): ValidationError;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ValidationError } from "../../errors/index.js";
|
|
2
|
+
export const EXPORT_TOO_MANY_I18N_KEY = "kumiko.list.export-too-many";
|
|
3
|
+
export function exportRejection(reason, path, params) {
|
|
4
|
+
return new ValidationError({
|
|
5
|
+
fields: [
|
|
6
|
+
{
|
|
7
|
+
path,
|
|
8
|
+
code: reason,
|
|
9
|
+
i18nKey: `kumiko.list.${reason.replace(/_/g, "-")}`,
|
|
10
|
+
...(params !== undefined && { params }),
|
|
11
|
+
},
|
|
12
|
+
],
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import type { Registry } from "../../engine/types/index.ts";
|
|
3
|
+
import type { Dispatcher } from "../../pipeline/dispatcher.ts";
|
|
4
|
+
export declare function createExportRoutes(dispatcher: Dispatcher, registry: Registry): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import * as z from "zod";
|
|
3
|
+
import { toKumikoError } from "../../errors/to-kumiko-error.js";
|
|
4
|
+
import { createI18n } from "../../i18n/index.js";
|
|
5
|
+
import { DEFAULT_LOCALE } from "../../i18n/request-locale.js";
|
|
6
|
+
import { isValidIanaTimeZone } from "../../time/index.js";
|
|
7
|
+
import { Routes } from "../api-constants.js";
|
|
8
|
+
import { getUser } from "../auth-middleware.js";
|
|
9
|
+
import { requestContext } from "../request-context.js";
|
|
10
|
+
import { assertPayloadDepthAllowed, queryErrorResponse } from "../routes.js";
|
|
11
|
+
import { EXPORT_TOO_MANY_I18N_KEY, exportRejection } from "./export-errors.js";
|
|
12
|
+
import { readExportRows } from "./export-rows.js";
|
|
13
|
+
import { resolveExportPlan } from "./export-screen.js";
|
|
14
|
+
import { writeExportWorkbook } from "./export-xlsx-writer.js";
|
|
15
|
+
const XLSX_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
|
16
|
+
const MAX_ID_LENGTH = 200;
|
|
17
|
+
const exportBodySchema = z.object({
|
|
18
|
+
screen: z.string().max(MAX_ID_LENGTH),
|
|
19
|
+
payload: z.record(z.string(), z.unknown()).default({}),
|
|
20
|
+
columns: z.array(z.string().max(MAX_ID_LENGTH)).min(1).max(MAX_ID_LENGTH),
|
|
21
|
+
timeZone: z.string().max(64).optional(),
|
|
22
|
+
});
|
|
23
|
+
// Browser zone first (what the user saw in the list), then the profile zone, else UTC.
|
|
24
|
+
function exportTimeZone(requested, profile) {
|
|
25
|
+
for (const candidate of [requested, profile]) {
|
|
26
|
+
if (candidate !== undefined && isValidIanaTimeZone(candidate))
|
|
27
|
+
return candidate;
|
|
28
|
+
}
|
|
29
|
+
return "UTC";
|
|
30
|
+
}
|
|
31
|
+
function downloadFileName(screenShortId, now) {
|
|
32
|
+
const safeId = screenShortId.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
33
|
+
return `${safeId}-${now.toISOString().slice(0, 10)}.xlsx`;
|
|
34
|
+
}
|
|
35
|
+
export function createExportRoutes(dispatcher, registry) {
|
|
36
|
+
const api = new Hono();
|
|
37
|
+
const i18n = createI18n(registry, { defaultLocale: DEFAULT_LOCALE });
|
|
38
|
+
api.post(Routes.exportXlsx, async (c) => {
|
|
39
|
+
const user = getUser(c);
|
|
40
|
+
try {
|
|
41
|
+
const parsed = exportBodySchema.safeParse(await c.req.json().catch(() => undefined));
|
|
42
|
+
if (!parsed.success)
|
|
43
|
+
throw exportRejection("export_invalid_request", "body");
|
|
44
|
+
const body = parsed.data;
|
|
45
|
+
assertPayloadDepthAllowed(body.payload);
|
|
46
|
+
const plan = resolveExportPlan(registry, body.screen, body.columns, user);
|
|
47
|
+
const result = await readExportRows(dispatcher, plan, body.payload, user);
|
|
48
|
+
if (result.tooMany) {
|
|
49
|
+
return c.json({
|
|
50
|
+
error: {
|
|
51
|
+
code: "export_too_many_rows",
|
|
52
|
+
httpStatus: 413,
|
|
53
|
+
message: `export exceeds the limit of ${plan.maxRows} rows; narrow the filter`,
|
|
54
|
+
i18nKey: EXPORT_TOO_MANY_I18N_KEY,
|
|
55
|
+
details: { maxRows: plan.maxRows },
|
|
56
|
+
},
|
|
57
|
+
}, 413);
|
|
58
|
+
}
|
|
59
|
+
const locale = requestContext.get()?.locale ?? DEFAULT_LOCALE;
|
|
60
|
+
const bytes = await writeExportWorkbook(plan.columns, result.rows, (key) => i18n.t(key, locale), exportTimeZone(body.timeZone, user.timezone));
|
|
61
|
+
return new Response(bytes, {
|
|
62
|
+
status: 200,
|
|
63
|
+
headers: {
|
|
64
|
+
"Content-Type": XLSX_CONTENT_TYPE,
|
|
65
|
+
"Cache-Control": "no-store",
|
|
66
|
+
"Content-Disposition": `attachment; filename="${downloadFileName(plan.screenShortId, new Date())}"`,
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
catch (e) {
|
|
71
|
+
return queryErrorResponse(c, toKumikoError(e), Routes.exportXlsx);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
return api;
|
|
75
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { SessionUser } from "../../engine/types/index.ts";
|
|
2
|
+
import type { Dispatcher } from "../../pipeline/dispatcher.ts";
|
|
3
|
+
import type { ExportPlan } from "./export-screen.ts";
|
|
4
|
+
export type ExportRow = Readonly<Record<string, unknown>>;
|
|
5
|
+
export type ExportRowsResult = {
|
|
6
|
+
readonly tooMany: false;
|
|
7
|
+
readonly rows: readonly ExportRow[];
|
|
8
|
+
} | {
|
|
9
|
+
readonly tooMany: true;
|
|
10
|
+
};
|
|
11
|
+
export declare function sanitizeExportPayload(clientPayload: Readonly<Record<string, unknown>>, plan: Pick<ExportPlan, "screenFilter">): Record<string, unknown>;
|
|
12
|
+
/** Reads the whole filtered list page by page; gives up as soon as more than `maxRows` exist. */
|
|
13
|
+
export declare function readExportRows(dispatcher: Pick<Dispatcher, "query">, plan: ExportPlan, clientPayload: Readonly<Record<string, unknown>>, user: SessionUser): Promise<ExportRowsResult>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { MAX_LIST_LIMIT } from "../../engine/entity-handlers.js";
|
|
2
|
+
import { InternalError } from "../../errors/index.js";
|
|
3
|
+
import { isPlainObject } from "../../utils/is-plain-object.js";
|
|
4
|
+
// Paging, sizing and aggregation belong to the server here; `filter` is replaced from the screen
|
|
5
|
+
// below, so a client can neither widen the fixed screen filter nor smuggle another one in.
|
|
6
|
+
const CLIENT_CONTROLLED_PAGING_KEYS = [
|
|
7
|
+
"limit",
|
|
8
|
+
"offset",
|
|
9
|
+
"cursor",
|
|
10
|
+
"totalCount",
|
|
11
|
+
"aggregate",
|
|
12
|
+
"includeDeleted",
|
|
13
|
+
"filter",
|
|
14
|
+
];
|
|
15
|
+
export function sanitizeExportPayload(clientPayload, plan) {
|
|
16
|
+
const payload = { ...clientPayload };
|
|
17
|
+
for (const key of CLIENT_CONTROLLED_PAGING_KEYS)
|
|
18
|
+
delete payload[key];
|
|
19
|
+
if (plan.screenFilter !== undefined)
|
|
20
|
+
payload["filter"] = plan.screenFilter;
|
|
21
|
+
return payload;
|
|
22
|
+
}
|
|
23
|
+
function parsePage(result, queryType) {
|
|
24
|
+
if (!isPlainObject(result) || !Array.isArray(result["rows"])) {
|
|
25
|
+
throw new InternalError({
|
|
26
|
+
message: `export: query ${queryType} did not return a { rows } envelope`,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
const nextCursor = result["nextCursor"];
|
|
30
|
+
return {
|
|
31
|
+
rows: result["rows"].filter(isPlainObject),
|
|
32
|
+
nextCursor: typeof nextCursor === "string" && nextCursor !== "" ? nextCursor : undefined,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** Reads the whole filtered list page by page; gives up as soon as more than `maxRows` exist. */
|
|
36
|
+
export async function readExportRows(dispatcher, plan, clientPayload, user) {
|
|
37
|
+
const base = sanitizeExportPayload(clientPayload, plan);
|
|
38
|
+
const rows = [];
|
|
39
|
+
const seenCursors = new Set();
|
|
40
|
+
let cursor;
|
|
41
|
+
for (;;) {
|
|
42
|
+
const limit = Math.min(MAX_LIST_LIMIT, plan.maxRows + 1 - rows.length);
|
|
43
|
+
const pagingPayload = cursor !== undefined
|
|
44
|
+
? { ...base, limit, cursor }
|
|
45
|
+
: rows.length > 0
|
|
46
|
+
? { ...base, limit, offset: rows.length }
|
|
47
|
+
: { ...base, limit };
|
|
48
|
+
const page = parsePage(await dispatcher.query(plan.queryType, pagingPayload, user), plan.queryType);
|
|
49
|
+
rows.push(...page.rows);
|
|
50
|
+
if (rows.length > plan.maxRows)
|
|
51
|
+
return { tooMany: true };
|
|
52
|
+
// A short or empty page is the end of the list; a repeated cursor would loop forever.
|
|
53
|
+
if (page.rows.length < limit || page.rows.length === 0)
|
|
54
|
+
return { tooMany: false, rows };
|
|
55
|
+
if (page.nextCursor !== undefined) {
|
|
56
|
+
if (seenCursors.has(page.nextCursor))
|
|
57
|
+
return { tooMany: false, rows };
|
|
58
|
+
seenCursors.add(page.nextCursor);
|
|
59
|
+
}
|
|
60
|
+
cursor = page.nextCursor;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ScreenFilter } from "@cosmicdrift/kumiko-types/screen";
|
|
2
|
+
import type { Registry, SessionUser } from "../../engine/types/index.ts";
|
|
3
|
+
import { type ExportColumn } from "./export-columns.ts";
|
|
4
|
+
export type ExportPlan = {
|
|
5
|
+
readonly screenShortId: string;
|
|
6
|
+
readonly queryType: string;
|
|
7
|
+
readonly screenFilter: ScreenFilter | undefined;
|
|
8
|
+
readonly columns: readonly ExportColumn[];
|
|
9
|
+
readonly maxRows: number;
|
|
10
|
+
};
|
|
11
|
+
type ExportScreenRegistry = Pick<Registry, "getScreen" | "getScreenFeature" | "getEntity">;
|
|
12
|
+
export declare function resolveExportPlan(registry: ExportScreenRegistry, screenQn: string, requestedColumns: readonly string[], user: Pick<SessionUser, "roles">): ExportPlan;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { hasAccess } from "../../engine/access.js";
|
|
2
|
+
import { toKebab } from "../../engine/qualified-name.js";
|
|
3
|
+
import { DEFAULT_EXPORT_MAX_ROWS } from "../../engine/types/screen.js";
|
|
4
|
+
import { AccessDeniedError } from "../../errors/index.js";
|
|
5
|
+
import { resolveExportColumns } from "./export-columns.js";
|
|
6
|
+
import { exportRejection } from "./export-errors.js";
|
|
7
|
+
const SCREEN_QN = /^[A-Za-z0-9_-]+:screen:[A-Za-z0-9_-]+$/;
|
|
8
|
+
function findExportScreen(registry, screenQn) {
|
|
9
|
+
// The client sends the feature name as authored (camelCase allowed); the registry keys are kebab.
|
|
10
|
+
const registryQn = toKebab(screenQn);
|
|
11
|
+
const screen = SCREEN_QN.test(screenQn) ? registry.getScreen(registryQn) : undefined;
|
|
12
|
+
if (screen === undefined || (screen.type !== "entityList" && screen.type !== "projectionList")) {
|
|
13
|
+
throw exportRejection("export_unknown_screen", "screen");
|
|
14
|
+
}
|
|
15
|
+
return { registryQn, screen };
|
|
16
|
+
}
|
|
17
|
+
export function resolveExportPlan(registry, screenQn, requestedColumns, user) {
|
|
18
|
+
const { registryQn, screen } = findExportScreen(registry, screenQn);
|
|
19
|
+
if (screen.table?.export?.xlsx !== true)
|
|
20
|
+
throw exportRejection("export_not_enabled", "screen");
|
|
21
|
+
if (screen.access !== undefined && !hasAccess(user, screen.access)) {
|
|
22
|
+
throw new AccessDeniedError({ details: { screen: screenQn } });
|
|
23
|
+
}
|
|
24
|
+
// Label keys use the feature name as authored, query types the kebab form.
|
|
25
|
+
const featureName = registry.getScreenFeature(registryQn) ?? "";
|
|
26
|
+
const screenShortId = registryQn.split(":")[2] ?? "";
|
|
27
|
+
const maxRows = screen.table.export.maxRows ?? DEFAULT_EXPORT_MAX_ROWS;
|
|
28
|
+
if (screen.type === "projectionList") {
|
|
29
|
+
return {
|
|
30
|
+
screenShortId,
|
|
31
|
+
queryType: screen.query,
|
|
32
|
+
screenFilter: screen.filter,
|
|
33
|
+
columns: resolveExportColumns({ featureName, columns: screen.columns, entity: undefined, entityName: undefined, user }, requestedColumns),
|
|
34
|
+
maxRows,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const entity = registry.getEntity(screen.entity);
|
|
38
|
+
if (entity === undefined)
|
|
39
|
+
throw exportRejection("export_unknown_screen", "screen");
|
|
40
|
+
return {
|
|
41
|
+
screenShortId,
|
|
42
|
+
queryType: `${toKebab(featureName)}:query:${toKebab(screen.entity)}:list`,
|
|
43
|
+
screenFilter: screen.filter,
|
|
44
|
+
columns: resolveExportColumns({ featureName, columns: screen.columns, entity, entityName: screen.entity, user }, requestedColumns),
|
|
45
|
+
maxRows,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type Translate } from "./export-cells.ts";
|
|
2
|
+
import type { ExportColumn } from "./export-columns.ts";
|
|
3
|
+
import type { ExportRow } from "./export-rows.ts";
|
|
4
|
+
export declare function writeExportWorkbook(columns: readonly ExportColumn[], rows: readonly ExportRow[], translate: Translate, timeZone: string): Promise<Uint8Array<ArrayBuffer>>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import writeXlsxFile, {} from "write-excel-file/node";
|
|
2
|
+
import { toExportCell } from "./export-cells.js";
|
|
3
|
+
import { guardSheetAgainstCiphertext } from "./export-ciphertext-guard.js";
|
|
4
|
+
function headerLabel(column, translate) {
|
|
5
|
+
const translated = translate(column.headerKey);
|
|
6
|
+
return translated === column.headerKey ? column.headerFallback : translated;
|
|
7
|
+
}
|
|
8
|
+
export async function writeExportWorkbook(columns, rows, translate, timeZone) {
|
|
9
|
+
const header = columns.map((column) => ({
|
|
10
|
+
value: headerLabel(column, translate),
|
|
11
|
+
type: String,
|
|
12
|
+
fontWeight: "bold",
|
|
13
|
+
}));
|
|
14
|
+
const body = rows.map((row) => columns.map((column) => toExportCell(column, { row, translate, timeZone })));
|
|
15
|
+
const sheetData = guardSheetAgainstCiphertext([header, ...body]);
|
|
16
|
+
const buffer = await writeXlsxFile(sheetData, {}).toBuffer();
|
|
17
|
+
// Buffer is typed over ArrayBufferLike; a plain copy gives Response a BodyInit-compatible view.
|
|
18
|
+
const bytes = new Uint8Array(buffer.byteLength);
|
|
19
|
+
bytes.set(buffer);
|
|
20
|
+
return bytes;
|
|
21
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import type { WriteResult } from "@cosmicdrift/kumiko-types/handlers";
|
|
2
|
+
import type { HttpRouteMethod } from "@cosmicdrift/kumiko-types/http-route";
|
|
1
3
|
import type { Context, Hono } from "hono";
|
|
2
|
-
import type {
|
|
4
|
+
import type { Registry, SessionUser, TenantId } from "../engine/types/index.ts";
|
|
3
5
|
import type { SecretsContext } from "../secrets/index.ts";
|
|
4
6
|
export declare const ExtraRouteEntries: {
|
|
5
7
|
readonly anonymous: "anonymous";
|
|
@@ -70,6 +72,10 @@ export type SignatureExtraRouteDeps = {
|
|
|
70
72
|
* caller's authenticity (signature, HMAC state, etc.). */
|
|
71
73
|
readonly dispatchSystemWrite: (args: SystemDispatchArgs) => Promise<WriteResult>;
|
|
72
74
|
readonly dispatchSystemQuery: (args: SystemDispatchArgs) => Promise<unknown>;
|
|
75
|
+
/** Same trust as dispatchSystemWrite, but runs as the tenant's system user
|
|
76
|
+
* with the "system" role plus exactly `roles` (no SystemAdmin), so the write
|
|
77
|
+
* passes app-role access checks. */
|
|
78
|
+
readonly dispatchWriteAs: (args: RoleDispatchArgs) => Promise<WriteResult>;
|
|
73
79
|
};
|
|
74
80
|
export type SignatureExtraRoute<TVerified> = {
|
|
75
81
|
readonly method: HttpRouteMethod;
|
package/dist/api/index.d.ts
CHANGED
|
@@ -1,40 +1,36 @@
|
|
|
1
1
|
export { DEFAULT_GLOBAL_RATE_LIMIT } from "../rate-limit/middleware.ts";
|
|
2
|
-
export
|
|
3
|
-
export { deleteTenantCookie, setTenantCookie } from "./anonymous-cookie.ts";
|
|
2
|
+
export { setTenantCookie } from "./anonymous-cookie.ts";
|
|
4
3
|
export { collectAnonymousLiveEntities } from "./anonymous-live-entities.ts";
|
|
5
4
|
export { LOCALE_HEADER_NAME, NO_ROUTE_MATCH_HEADER_NAME } from "./api-constants.ts";
|
|
6
|
-
export type { AnonymousAccessConfig, AnonymousAccessResolved,
|
|
7
|
-
export {
|
|
5
|
+
export type { AnonymousAccessConfig, AnonymousAccessResolved, AuthSessionCheckResult, TokenVerifier, } from "./auth-middleware.ts";
|
|
6
|
+
export { CSRF_HEADER_NAME, PAT_TOKEN_PREFIX, } from "./auth-middleware.ts";
|
|
8
7
|
export type { AuthRoutesConfig, LoginRateLimiter, SessionChecker, SessionCreator, SessionMassRevoker, SessionMetadata, SessionRevoker, } from "./auth-routes.ts";
|
|
9
|
-
export {
|
|
10
|
-
export type {
|
|
11
|
-
export {
|
|
12
|
-
export type { AnonymousExtraRoute, AnonymousExtraRouteDeps, ExtraRouteDefinition,
|
|
13
|
-
export {
|
|
14
|
-
export type {
|
|
15
|
-
export { cacheControlHeader, cachedResponse, computeRevisionEtag, computeStrongEtag, computeWeakEtag, etagMatches,
|
|
16
|
-
export type { JwtHelper
|
|
17
|
-
export {
|
|
8
|
+
export { createInMemoryLoginRateLimiter, createRedisLoginRateLimiter, } from "./auth-routes.ts";
|
|
9
|
+
export type { ClientIpResolver } from "./client-ip.ts";
|
|
10
|
+
export { createClientIpResolver, parseTrustedProxyHopsEnv, TRUSTED_PROXY_HOPS_ENV, UNKNOWN_CLIENT_IP, } from "./client-ip.ts";
|
|
11
|
+
export type { AnonymousExtraRoute, AnonymousExtraRouteDeps, ExtraRouteDefinition, RoleDispatchArgs, SignatureExtraRouteDeps, SignatureExtraRouteVerifyRequest, SystemDispatchArgs, UserExtraRouteDeps, } from "./extra-route.ts";
|
|
12
|
+
export { ExtraRouteRejection, signatureRoute } from "./extra-route.ts";
|
|
13
|
+
export type { CachePolicy } from "./http-cache.ts";
|
|
14
|
+
export { cacheControlHeader, cachedResponse, computeRevisionEtag, computeStrongEtag, computeWeakEtag, etagMatches, } from "./http-cache.ts";
|
|
15
|
+
export type { JwtHelper } from "./jwt.ts";
|
|
16
|
+
export { loadJwtSecretOrKeyring } from "./jwt.ts";
|
|
18
17
|
export { publicOriginOf } from "./origin-middleware.ts";
|
|
19
|
-
export {
|
|
20
|
-
export type {
|
|
21
|
-
export { isSafeLandingPath } from "./post-auth-landing.ts";
|
|
22
|
-
export type { RedisSseBroker, RedisSseBrokerOptions } from "./redis-sse-broker.ts";
|
|
18
|
+
export type { PostAuthLandingArgs, PostAuthLandingResolver, } from "./post-auth-landing.ts";
|
|
19
|
+
export type { RedisSseBroker } from "./redis-sse-broker.ts";
|
|
23
20
|
export { createDefaultSseBroker, createRedisSseBroker, isRedisSseBroker, } from "./redis-sse-broker.ts";
|
|
24
|
-
export {
|
|
25
|
-
export { buildRequestContextDataFromRequest
|
|
21
|
+
export { readonlyRequestContext as requestContext } from "./request-context-scopes.ts";
|
|
22
|
+
export { buildRequestContextDataFromRequest } from "./request-id-middleware.ts";
|
|
26
23
|
export type { MetricsRouteOptions } from "./route-registrars.ts";
|
|
27
24
|
export { createMetricsApp, DEFAULT_MAX_REQUEST_BYTES, isPrometheusMeter, } from "./route-registrars.ts";
|
|
28
|
-
export {
|
|
29
|
-
export type { KumikoServer, ServerOptions } from "./server.ts";
|
|
25
|
+
export type { ServerOptions } from "./server.ts";
|
|
30
26
|
export { buildServer } from "./server.ts";
|
|
27
|
+
export { resolveBaseDbAndSearchAdapter } from "./server-event-dispatcher.ts";
|
|
31
28
|
export { makeDispatchSystemQuery, makeDispatchSystemWrite, makeDispatchWriteAs, } from "./server-extra-routes.ts";
|
|
32
29
|
export { withFileProviderResolver } from "./server-wiring.ts";
|
|
33
|
-
export
|
|
34
|
-
export {
|
|
35
|
-
export type { SseBroker, SseClient, SseEvent } from "./sse-broker.ts";
|
|
30
|
+
export { AUTH_COOKIE_BASE, CSRF_COOKIE_BASE, getSessionCookies, sessionCookieName, } from "./session-cookies.ts";
|
|
31
|
+
export type { SseBroker, SseEvent } from "./sse-broker.ts";
|
|
36
32
|
export { createSseBroker } from "./sse-broker.ts";
|
|
37
|
-
export {
|
|
33
|
+
export { SSE_HEARTBEAT_INTERVAL_MS } from "./sse-route.ts";
|
|
38
34
|
export { generateToken } from "./tokens.ts";
|
|
39
|
-
export type { KumikoServeEnv,
|
|
40
|
-
export { createKumikoWebSocketHandler, isWebSocketUpgradeRequest,
|
|
35
|
+
export type { KumikoServeEnv, KumikoWebSocketData, } from "./websocket-route.ts";
|
|
36
|
+
export { createKumikoWebSocketHandler, isWebSocketUpgradeRequest, WEBSOCKET_BACKPRESSURE_LIMIT_BYTES, WEBSOCKET_HEARTBEAT_INTERVAL_MS, WEBSOCKET_MAX_PAYLOAD_BYTES, WEBSOCKET_PENDING_BUFFER_LIMIT_BYTES, WEBSOCKET_ROUTE_PATH_PREFIX, } from "./websocket-route.ts";
|