@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
|
@@ -9,16 +9,17 @@ import { createProjectingTenantDb } from "../db/tenant-db.js";
|
|
|
9
9
|
import { createDerivativesContext, resolveFieldVariant, resolveRenderer, } from "../derivatives/index.js";
|
|
10
10
|
import { isAnonymousSessionUser, SYSTEM_USER_ID } from "../engine/system-user.js";
|
|
11
11
|
import { isFileField, isUuid, } from "../engine/types/index.js";
|
|
12
|
+
import { createFailOpenReporter, respondRateLimited } from "../rate-limit/middleware.js";
|
|
12
13
|
import { listActionFormFileFields, mayUseActionForm } from "./action-form-upload.js";
|
|
13
14
|
import { buildContentDispositionHeader } from "./content-disposition.js";
|
|
14
15
|
import { DEFAULT_MAX_UPLOAD_SIZE, getFileRefExecutor, storeFileAsFileRef, } from "./create-file-ref.js";
|
|
15
|
-
import { createFileContext } from "./file-handle.js";
|
|
16
|
+
import { createFileContext, createTenantFileContext } from "./file-handle.js";
|
|
16
17
|
import { fileRefEntity } from "./file-ref-entity.js";
|
|
17
18
|
import { fileRefsTable } from "./file-ref-table.js";
|
|
18
|
-
import { RecordWriteDecisions } from "./record-write-check.js";
|
|
19
|
+
import { isStoredRecordLocked, RecordWriteDecisions, } from "./record-write-check.js";
|
|
19
20
|
import { neutralFileNameForMimeType } from "./resolve-content-type.js";
|
|
20
21
|
import { parseMaxSize, resolveServedContentType } from "./types.js";
|
|
21
|
-
import {
|
|
22
|
+
import { withUploadLedger } from "./upload-ledger.js";
|
|
22
23
|
// Exactly the httpStatus values declared by KumikoError subclasses across packages/*.
|
|
23
24
|
const ERROR_STATUSES = [400, 401, 403, 404, 409, 422, 429, 500, 503, 504];
|
|
24
25
|
export function deleteFailureResponseFor(error) {
|
|
@@ -31,6 +32,8 @@ const DEFAULT_PRIVILEGED_ROLES = ["Admin", "SystemAdmin"];
|
|
|
31
32
|
// credential (see "Signed-URL default expiry" in core-files.md).
|
|
32
33
|
const SIGNED_URL_DEFAULT_EXPIRY_SECONDS = 15 * 60;
|
|
33
34
|
const MAX_BATCH_META_IDS = 100;
|
|
35
|
+
const META_BATCH_IDS_PER_WINDOW = 1_000;
|
|
36
|
+
const META_BATCH_WINDOW_SECONDS = 60;
|
|
34
37
|
// Each detail() read decrypts through the KMS pool; a bounded chunk keeps one batch from hogging it.
|
|
35
38
|
const BATCH_META_CONCURRENCY = 10;
|
|
36
39
|
// Default guard: on attached files, allow the uploader or a privileged role,
|
|
@@ -101,7 +104,12 @@ export function readFilesRouteOptions(registry) {
|
|
|
101
104
|
return {};
|
|
102
105
|
}
|
|
103
106
|
export function createFileRoutes(options) {
|
|
104
|
-
const { db } = options;
|
|
107
|
+
const { db, metaBatchRateLimitResolver } = options;
|
|
108
|
+
const reportMetaBatchLimiterFailOpen = createFailOpenReporter("files-meta-batch");
|
|
109
|
+
// Plain resolvers (not built via withFileProviderResolver) get the ledger on the route's own root connection.
|
|
110
|
+
const resolveProvider = options.resolveProvider.uploadLedgerFor
|
|
111
|
+
? options.resolveProvider
|
|
112
|
+
: withUploadLedger(options.resolveProvider, db);
|
|
105
113
|
const privilegedRoles = options.privilegedRoles ?? DEFAULT_PRIVILEGED_ROLES;
|
|
106
114
|
const guard = options.accessGuard ??
|
|
107
115
|
createDefaultGuard(privilegedRoles, options.canReadRecord, options.unclaimedUploadReaders ?? []);
|
|
@@ -200,19 +208,23 @@ export function createFileRoutes(options) {
|
|
|
200
208
|
}
|
|
201
209
|
}
|
|
202
210
|
const data = new Uint8Array(await file.arrayBuffer());
|
|
203
|
-
const
|
|
204
|
-
const
|
|
211
|
+
const files = createTenantFileContext(resolveProvider, user.tenantId);
|
|
212
|
+
const { registry } = options;
|
|
213
|
+
const isAttachTargetLocked = registry !== undefined && entityType !== undefined && entityId !== undefined
|
|
214
|
+
? (rowDb) => isStoredRecordLocked({ registry, db: rowDb, entityType, entityId })
|
|
215
|
+
: undefined;
|
|
205
216
|
const outcome = await storeFileAsFileRef({
|
|
206
217
|
user,
|
|
207
218
|
tenantDb: tenantDbFor(user.tenantId),
|
|
208
219
|
rowInOwnTransaction: true,
|
|
209
220
|
registry: options.registry,
|
|
210
221
|
defaultMaxUploadSize: options.maxUploadSize ?? DEFAULT_MAX_UPLOAD_SIZE,
|
|
211
|
-
writeBytes:
|
|
212
|
-
|
|
213
|
-
await
|
|
222
|
+
writeBytes: (key, bytes, mimeType) => files.writeUploadBytes(key, bytes, mimeType),
|
|
223
|
+
deleteBytes: async (key) => {
|
|
224
|
+
await files.ref(key).delete();
|
|
225
|
+
await files.forgetUploadBytes(key);
|
|
214
226
|
},
|
|
215
|
-
|
|
227
|
+
...(isAttachTargetLocked ? { isAttachTargetLocked } : {}),
|
|
216
228
|
}, {
|
|
217
229
|
fileName: file.name,
|
|
218
230
|
declaredMimeType: file.type,
|
|
@@ -224,6 +236,9 @@ export function createFileRoutes(options) {
|
|
|
224
236
|
});
|
|
225
237
|
if (outcome.kind === "rejected")
|
|
226
238
|
return c.json({ error: outcome.error }, 400);
|
|
239
|
+
if (outcome.kind === "target-locked") {
|
|
240
|
+
return c.json({ error: "record_locked: the record is locked" }, 409);
|
|
241
|
+
}
|
|
227
242
|
if (outcome.kind === "failed")
|
|
228
243
|
return c.json({ error: "upload_failed" }, 500);
|
|
229
244
|
return c.json(outcome.fileRef, 201);
|
|
@@ -237,6 +252,21 @@ export function createFileRoutes(options) {
|
|
|
237
252
|
const ids = [...new Set((c.req.query("ids") ?? "").split(",").filter((id) => isUuid(id)))];
|
|
238
253
|
if (ids.length > MAX_BATCH_META_IDS)
|
|
239
254
|
return c.json({ error: "too_many_ids" }, 400);
|
|
255
|
+
if (metaBatchRateLimitResolver && ids.length > 0) {
|
|
256
|
+
const bucket = `files-meta:${user.tenantId}:${user.id}`;
|
|
257
|
+
try {
|
|
258
|
+
const decision = await metaBatchRateLimitResolver.check(bucket, {
|
|
259
|
+
limit: META_BATCH_IDS_PER_WINDOW,
|
|
260
|
+
windowSeconds: META_BATCH_WINDOW_SECONDS,
|
|
261
|
+
cost: ids.length,
|
|
262
|
+
});
|
|
263
|
+
if (!decision.allowed)
|
|
264
|
+
return respondRateLimited(c, decision, bucket);
|
|
265
|
+
}
|
|
266
|
+
catch (e) {
|
|
267
|
+
reportMetaBatchLimiterFailOpen(e);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
240
270
|
const files = [];
|
|
241
271
|
for (let start = 0; start < ids.length; start += BATCH_META_CONCURRENCY) {
|
|
242
272
|
const chunk = ids.slice(start, start + BATCH_META_CONCURRENCY);
|
|
@@ -56,5 +56,6 @@ export type FileProviderResolverDeps = {
|
|
|
56
56
|
readonly secrets?: SecretsContext;
|
|
57
57
|
readonly db?: DbConnection | TenantDb;
|
|
58
58
|
readonly cacheSync?: CacheSyncBus;
|
|
59
|
+
readonly now?: () => Temporal.Instant;
|
|
59
60
|
};
|
|
60
61
|
export declare function makeFileProviderResolver(deps: FileProviderResolverDeps): FileProviderResolver;
|
|
@@ -119,5 +119,5 @@ export function makeFileProviderResolver(deps) {
|
|
|
119
119
|
cache.set(tenantId, built);
|
|
120
120
|
return built;
|
|
121
121
|
};
|
|
122
|
-
return deps.db ? withUploadLedger(resolveProvider, deps.db) : resolveProvider;
|
|
122
|
+
return deps.db ? withUploadLedger(resolveProvider, deps.db, deps.now) : resolveProvider;
|
|
123
123
|
}
|
|
@@ -20,3 +20,9 @@ export declare function createRecordWriteCheck(args: {
|
|
|
20
20
|
readonly canReadRecord: RecordReadCheck;
|
|
21
21
|
readonly tenantDbFor: (tenantId: TenantId) => TenantDb;
|
|
22
22
|
}): RecordWriteCheck;
|
|
23
|
+
export declare function isStoredRecordLocked(args: {
|
|
24
|
+
readonly registry: Registry;
|
|
25
|
+
readonly db: TenantDb;
|
|
26
|
+
readonly entityType: string;
|
|
27
|
+
readonly entityId: string;
|
|
28
|
+
}): Promise<boolean>;
|
|
@@ -62,3 +62,16 @@ export function createRecordWriteCheck(args) {
|
|
|
62
62
|
return RecordWriteDecisions.writable;
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
|
+
// The stored row, read through the given db so a transaction sees the lock state it commits against.
|
|
66
|
+
export async function isStoredRecordLocked(args) {
|
|
67
|
+
const { registry, db, entityType, entityId } = args;
|
|
68
|
+
const entity = registry.getEntity(entityType);
|
|
69
|
+
const table = findImplicitEntityTable(registry, entityType);
|
|
70
|
+
if (!entity || !table || entity.lockedWhen === undefined)
|
|
71
|
+
return false;
|
|
72
|
+
const row = await db.fetchOne(table, {
|
|
73
|
+
id: entityId,
|
|
74
|
+
...(entity.softDelete === true && { isDeleted: false }),
|
|
75
|
+
});
|
|
76
|
+
return row !== undefined && isRecordLocked(entity.lockedWhen, row);
|
|
77
|
+
}
|
|
@@ -46,6 +46,49 @@ const NON_XML_ROOT_ELEMENTS = new Set([
|
|
|
46
46
|
"article",
|
|
47
47
|
"font",
|
|
48
48
|
"center",
|
|
49
|
+
"form",
|
|
50
|
+
"iframe",
|
|
51
|
+
"input",
|
|
52
|
+
"button",
|
|
53
|
+
"nav",
|
|
54
|
+
"header",
|
|
55
|
+
"footer",
|
|
56
|
+
"main",
|
|
57
|
+
"pre",
|
|
58
|
+
"code",
|
|
59
|
+
"b",
|
|
60
|
+
"i",
|
|
61
|
+
"strong",
|
|
62
|
+
"em",
|
|
63
|
+
"label",
|
|
64
|
+
"textarea",
|
|
65
|
+
"select",
|
|
66
|
+
"fieldset",
|
|
67
|
+
"legend",
|
|
68
|
+
"tr",
|
|
69
|
+
"td",
|
|
70
|
+
"th",
|
|
71
|
+
"thead",
|
|
72
|
+
"tbody",
|
|
73
|
+
"tfoot",
|
|
74
|
+
"object",
|
|
75
|
+
"embed",
|
|
76
|
+
"video",
|
|
77
|
+
"audio",
|
|
78
|
+
"canvas",
|
|
79
|
+
"math",
|
|
80
|
+
"picture",
|
|
81
|
+
"figure",
|
|
82
|
+
"figcaption",
|
|
83
|
+
"blockquote",
|
|
84
|
+
"dl",
|
|
85
|
+
"dt",
|
|
86
|
+
"dd",
|
|
87
|
+
"aside",
|
|
88
|
+
"details",
|
|
89
|
+
"summary",
|
|
90
|
+
"dialog",
|
|
91
|
+
"noscript",
|
|
49
92
|
]);
|
|
50
93
|
const ROOT_ELEMENT_PATTERN = /^<(?:[A-Za-z_][\w.-]*:)?([A-Za-z_][\w.-]*)/;
|
|
51
94
|
const EXTENSION_FALLBACK = {
|
|
@@ -5,6 +5,6 @@ import type { TenantId } from "@cosmicdrift/kumiko-types/identifiers";
|
|
|
5
5
|
import type { DbConnection } from "../db/connection.ts";
|
|
6
6
|
import { type TenantDb } from "../db/tenant-db.ts";
|
|
7
7
|
export declare const fileUploadLedgerTable: WritableTable;
|
|
8
|
-
export declare function createFileUploadLedger(db: DbConnection | TenantDb, tenantId: TenantId): FileUploadLedger;
|
|
9
|
-
export declare function withUploadLedger(resolver: (tenantId: TenantId) => Promise<FileStorageProvider>, db: DbConnection | TenantDb): FileProviderResolver;
|
|
8
|
+
export declare function createFileUploadLedger(db: DbConnection | TenantDb, tenantId: TenantId, now?: () => Temporal.Instant): FileUploadLedger;
|
|
9
|
+
export declare function withUploadLedger(resolver: (tenantId: TenantId) => Promise<FileStorageProvider>, db: DbConnection | TenantDb, now?: () => Temporal.Instant): FileProviderResolver;
|
|
10
10
|
export declare function takeUploadLedgerEntry(txDb: TenantDb, storageKey: string): Promise<boolean>;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { defineUnmanagedTable } from "../db/entity-table-meta.js";
|
|
2
|
+
import { runnerCanBeginTransaction } from "../db/query.js";
|
|
2
3
|
import { createTenantDb } from "../db/tenant-db.js";
|
|
3
4
|
import { tenantDbRunner } from "../db/tenant-db-runner.js";
|
|
5
|
+
import { InternalError } from "../errors/index.js";
|
|
4
6
|
const CREATED_AT_INDEX = "store_file_upload_ledger_created_at_idx";
|
|
5
7
|
// One row per upload whose bytes are in storage but whose fileRef row is not committed yet. The
|
|
6
8
|
// commit path deletes it inside the transaction that creates the fileRef; the sweep finds the
|
|
@@ -19,15 +21,20 @@ function isTenantDb(db) {
|
|
|
19
21
|
}
|
|
20
22
|
// The ledger writes on a connection of its own, never on a handler transaction: a rollback of the
|
|
21
23
|
// handler must not take the row with it, or the bytes it uploaded would be unfindable.
|
|
22
|
-
export function createFileUploadLedger(db, tenantId) {
|
|
24
|
+
export function createFileUploadLedger(db, tenantId, now = () => Temporal.Now.instant()) {
|
|
23
25
|
const runner = isTenantDb(db) ? tenantDbRunner(db) : db;
|
|
26
|
+
if (!runnerCanBeginTransaction(runner)) {
|
|
27
|
+
throw new InternalError({
|
|
28
|
+
message: "createFileUploadLedger: the connection is already inside a transaction; the ledger row would roll back with it and the uploaded bytes become unfindable",
|
|
29
|
+
});
|
|
30
|
+
}
|
|
24
31
|
const ledgerDb = createTenantDb({ db: runner, tenantId });
|
|
25
32
|
return {
|
|
26
33
|
record: async (storageKey) => {
|
|
27
34
|
await ledgerDb.insertOne(fileUploadLedgerTable, {
|
|
28
35
|
storageKey,
|
|
29
36
|
tenantId,
|
|
30
|
-
createdAt:
|
|
37
|
+
createdAt: now(),
|
|
31
38
|
});
|
|
32
39
|
},
|
|
33
40
|
forget: async (storageKey) => {
|
|
@@ -35,9 +42,9 @@ export function createFileUploadLedger(db, tenantId) {
|
|
|
35
42
|
},
|
|
36
43
|
};
|
|
37
44
|
}
|
|
38
|
-
export function withUploadLedger(resolver, db) {
|
|
45
|
+
export function withUploadLedger(resolver, db, now) {
|
|
39
46
|
return Object.assign(resolver, {
|
|
40
|
-
uploadLedgerFor: (tenantId) => createFileUploadLedger(db, tenantId),
|
|
47
|
+
uploadLedgerFor: (tenantId) => createFileUploadLedger(db, tenantId, now),
|
|
41
48
|
});
|
|
42
49
|
}
|
|
43
50
|
// Runs inside the transaction that creates the fileRef row. False means no ledger row was left to
|