@cosmicdrift/kumiko-bundled-features 1.0.0 → 2.0.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/package.json +41 -7
- package/src/__tests__/access-invalidation-consumer.integration.test.ts +279 -0
- package/src/__tests__/access-invalidation-stream.integration.test.ts +217 -0
- package/src/admin-shell/__tests__/admin-shell-security.integration.test.ts +200 -0
- package/src/admin-shell/__tests__/admin-shell.boot.test.ts +147 -0
- package/src/admin-shell/__tests__/overview-allowlist.test.ts +43 -0
- package/src/admin-shell/__tests__/overview-screens.boot.test.ts +35 -0
- package/src/admin-shell/constants.ts +11 -0
- package/src/admin-shell/feature.ts +135 -0
- package/src/admin-shell/i18n.ts +36 -0
- package/src/admin-shell/index.ts +17 -0
- package/src/admin-shell/overview-allowlist.ts +37 -0
- package/src/admin-shell/web/client-plugin.tsx +26 -0
- package/src/admin-shell/web/i18n.ts +16 -0
- package/src/admin-shell/web/index.ts +2 -0
- package/src/admin-shell/web/overview-layout.tsx +69 -0
- package/src/admin-shell/web/overview-query.ts +15 -0
- package/src/admin-shell/web/platform-overview-screen.tsx +90 -0
- package/src/admin-shell/web/tenant-overview-screen.tsx +90 -0
- package/src/agent-tools/__tests__/tool-catalog.test.ts +179 -0
- package/src/agent-tools/__tests__/tool-dispatch.integration.test.ts +100 -0
- package/src/agent-tools/__tests__/tool-dispatch.test.ts +126 -0
- package/src/agent-tools/index.ts +9 -0
- package/src/agent-tools/tool-catalog.ts +130 -0
- package/src/agent-tools/tool-dispatch.ts +82 -0
- package/src/agent-tools/types.ts +38 -0
- package/src/audit/__tests__/audit-screens.boot.test.ts +51 -0
- package/src/audit/__tests__/audit-security.integration.test.ts +201 -0
- package/src/audit/__tests__/audit.integration.test.ts +50 -25
- package/src/audit/constants.ts +9 -2
- package/src/audit/feature.ts +33 -1
- package/src/audit/handlers/details.query.ts +47 -0
- package/src/audit/handlers/list.query.ts +3 -2
- package/src/audit/i18n.ts +31 -0
- package/src/audit/web/audit-log-detail-screen.tsx +131 -0
- package/src/audit/web/audit-log-screen.tsx +250 -0
- package/src/audit/web/client-plugin.tsx +22 -0
- package/src/audit/web/i18n.ts +12 -0
- package/src/audit/web/index.ts +5 -0
- package/src/auth-email-password/__tests__/account-lockout-no-redis.integration.test.ts +4 -4
- package/src/auth-email-password/__tests__/account-lockout.integration.test.ts +4 -4
- package/src/auth-email-password/__tests__/account-unlock.integration.test.ts +283 -0
- package/src/auth-email-password/__tests__/auth-claims.integration.test.ts +10 -11
- package/src/auth-email-password/__tests__/auth-user-row.test.ts +36 -0
- package/src/auth-email-password/__tests__/auth.integration.test.ts +33 -6
- package/src/auth-email-password/__tests__/email-verification.integration.test.ts +9 -6
- package/src/auth-email-password/__tests__/feature-options.test.ts +33 -0
- package/src/auth-email-password/__tests__/identity-v3-login.integration.test.ts +3 -3
- package/src/auth-email-password/__tests__/invite-flow-kms.integration.test.ts +283 -0
- package/src/auth-email-password/__tests__/invite-flow.integration.test.ts +35 -1
- package/src/auth-email-password/__tests__/login-mfa-setup-required.integration.test.ts +129 -0
- package/src/auth-email-password/__tests__/multi-roles.integration.test.ts +1 -1
- package/src/auth-email-password/__tests__/password-policy.test.ts +15 -0
- package/src/auth-email-password/__tests__/password-reset.integration.test.ts +19 -6
- package/src/auth-email-password/__tests__/public-routes-rate-limit.integration.test.ts +3 -3
- package/src/auth-email-password/__tests__/seed-admin.integration.test.ts +1 -1
- package/src/auth-email-password/__tests__/self-registration-toggle.integration.test.ts +264 -0
- package/src/auth-email-password/__tests__/session-callbacks.integration.test.ts +20 -4
- package/src/auth-email-password/__tests__/session-strict-mode.integration.test.ts +7 -14
- package/src/auth-email-password/__tests__/signup-flow.integration.test.ts +91 -0
- package/src/auth-email-password/constants.ts +25 -0
- package/src/auth-email-password/email-templates.ts +32 -0
- package/src/auth-email-password/errors.ts +9 -0
- package/src/auth-email-password/feature.ts +59 -11
- package/src/auth-email-password/handlers/change-password.write.ts +14 -13
- package/src/auth-email-password/handlers/confirm-account-unlock.write.ts +59 -0
- package/src/auth-email-password/handlers/confirm-token-flow.ts +34 -105
- package/src/auth-email-password/handlers/invite-accept-with-login.write.ts +12 -5
- package/src/auth-email-password/handlers/invite-accept.write.ts +9 -2
- package/src/auth-email-password/handlers/invite-create.write.ts +2 -2
- package/src/auth-email-password/handlers/invite-signup-complete.write.ts +12 -4
- package/src/auth-email-password/handlers/login.write.ts +86 -16
- package/src/auth-email-password/handlers/logout.write.ts +4 -2
- package/src/auth-email-password/handlers/request-account-unlock.write.ts +38 -0
- package/src/auth-email-password/handlers/reset-password.write.ts +3 -2
- package/src/auth-email-password/handlers/self-registration-status.query.ts +23 -0
- package/src/auth-email-password/handlers/signup-confirm.write.ts +19 -11
- package/src/auth-email-password/handlers/signup-request.write.ts +22 -0
- package/src/auth-email-password/handlers/token-request-handler.ts +4 -2
- package/src/auth-email-password/i18n.ts +52 -0
- package/src/auth-email-password/index.ts +13 -2
- package/src/auth-email-password/lockout-store.ts +10 -5
- package/src/auth-email-password/password-policy.ts +2108 -0
- package/src/auth-email-password/seeding.ts +55 -27
- package/src/auth-email-password/self-registration-toggle.ts +25 -0
- package/src/auth-email-password/signed-token.ts +1 -0
- package/src/auth-email-password/unlock-token.ts +28 -0
- package/src/auth-email-password/web/__tests__/auth-client.test.ts +542 -0
- package/src/auth-email-password/web/__tests__/auth-form-logic.test.ts +44 -0
- package/src/auth-email-password/web/__tests__/auth-gate.test.tsx +194 -6
- package/src/auth-email-password/web/__tests__/client-plugin.test.tsx +34 -0
- package/src/auth-email-password/web/__tests__/confirm-account-unlock-screen.test.tsx +64 -0
- package/src/auth-email-password/web/__tests__/forgot-password-screen.test.tsx +5 -1
- package/src/auth-email-password/web/__tests__/invite-accept-screen.test.tsx +129 -2
- package/src/auth-email-password/web/__tests__/login-screen.test.tsx +149 -5
- package/src/auth-email-password/web/__tests__/request-account-unlock-screen.test.tsx +137 -0
- package/src/auth-email-password/web/__tests__/reset-password-screen.test.tsx +5 -1
- package/src/auth-email-password/web/__tests__/signup-complete-screen.test.tsx +134 -0
- package/src/auth-email-password/web/__tests__/signup-screen.test.tsx +93 -0
- package/src/auth-email-password/web/__tests__/test-utils.tsx +12 -2
- package/src/auth-email-password/web/__tests__/use-url-token.test.tsx +29 -0
- package/src/auth-email-password/web/__tests__/verify-email-screen.test.tsx +5 -1
- package/src/auth-email-password/web/auth-client.ts +96 -18
- package/src/auth-email-password/web/auth-form-logic.ts +30 -0
- package/src/auth-email-password/web/auth-form-primitives.tsx +22 -20
- package/src/auth-email-password/web/auth-gate.tsx +140 -19
- package/src/auth-email-password/web/client-plugin.ts +24 -2
- package/src/auth-email-password/web/confirm-account-unlock-screen.tsx +95 -0
- package/src/auth-email-password/web/forgot-password-screen.tsx +6 -6
- package/src/auth-email-password/web/index.ts +16 -4
- package/src/auth-email-password/web/invite-accept-screen.tsx +5 -5
- package/src/auth-email-password/web/login-screen.tsx +78 -22
- package/src/auth-email-password/web/request-account-unlock-screen.tsx +112 -0
- package/src/auth-email-password/web/reset-password-screen.tsx +15 -18
- package/src/auth-email-password/web/session.tsx +11 -6
- package/src/auth-email-password/web/signup-complete-screen.tsx +13 -15
- package/src/auth-email-password/web/signup-screen.tsx +8 -10
- package/src/auth-email-password/web/use-shell-user.ts +5 -2
- package/src/auth-email-password/web/user-menu.tsx +3 -1
- package/src/auth-email-password/web/verify-email-screen.tsx +10 -14
- package/src/auth-foundation/__tests__/feature.test.ts +46 -0
- package/src/auth-foundation/__tests__/session-store.test.ts +77 -0
- package/src/auth-foundation/__tests__/tenant-providers.test.ts +160 -0
- package/src/auth-foundation/__tests__/token-verifier.test.ts +150 -0
- package/src/auth-foundation/boot-checks.ts +150 -0
- package/src/auth-foundation/feature.ts +157 -0
- package/src/auth-foundation/index.ts +37 -0
- package/src/auth-foundation/resolve-anonymous-access.ts +62 -0
- package/src/auth-foundation/types.ts +170 -0
- package/src/auth-mfa/__tests__/base32.test.ts +28 -0
- package/src/auth-mfa/__tests__/disable-and-regenerate.integration.test.ts +265 -0
- package/src/auth-mfa/__tests__/enable-confirm-preauth.integration.test.ts +321 -0
- package/src/auth-mfa/__tests__/enable-flow.integration.test.ts +322 -0
- package/src/auth-mfa/__tests__/enable-start-preauth.integration.test.ts +237 -0
- package/src/auth-mfa/__tests__/errors-i18n-parity.test.ts +39 -0
- package/src/auth-mfa/__tests__/mfa-preauth-setup-token.test.ts +113 -0
- package/src/auth-mfa/__tests__/no-redis.integration.test.ts +209 -0
- package/src/auth-mfa/__tests__/pii-subject-encryption.integration.test.ts +139 -0
- package/src/auth-mfa/__tests__/reencrypt-job.integration.test.ts +247 -0
- package/src/auth-mfa/__tests__/session-auto-revoke.integration.test.ts +169 -0
- package/src/auth-mfa/__tests__/totp.test.ts +131 -0
- package/src/auth-mfa/__tests__/verify.integration.test.ts +319 -0
- package/src/auth-mfa/base32.ts +51 -0
- package/src/auth-mfa/config.ts +26 -0
- package/src/auth-mfa/constants.ts +45 -0
- package/src/auth-mfa/db/queries.ts +45 -0
- package/src/auth-mfa/errors.ts +66 -0
- package/src/auth-mfa/feature.ts +170 -0
- package/src/auth-mfa/handlers/disable.write.ts +67 -0
- package/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +200 -0
- package/src/auth-mfa/handlers/enable-confirm.write.ts +106 -0
- package/src/auth-mfa/handlers/enable-start-preauth.write.ts +87 -0
- package/src/auth-mfa/handlers/enable-start.write.ts +66 -0
- package/src/auth-mfa/handlers/reencrypt.job.ts +234 -0
- package/src/auth-mfa/handlers/regenerate-recovery.write.ts +77 -0
- package/src/auth-mfa/handlers/status.query.ts +23 -0
- package/src/auth-mfa/handlers/verify.write.ts +179 -0
- package/src/auth-mfa/i18n.ts +12 -0
- package/src/auth-mfa/index.ts +19 -0
- package/src/auth-mfa/mfa-challenge-token.ts +90 -0
- package/src/auth-mfa/mfa-preauth-setup-token.ts +86 -0
- package/src/auth-mfa/mfa-setup-token.ts +96 -0
- package/src/auth-mfa/mfa-status-checker.ts +84 -0
- package/src/auth-mfa/mfa-verify-attempts.ts +90 -0
- package/src/auth-mfa/otpauth-uri.ts +21 -0
- package/src/auth-mfa/recovery-codes.ts +54 -0
- package/src/auth-mfa/schema/user-mfa.ts +60 -0
- package/src/auth-mfa/testing.ts +5 -0
- package/src/auth-mfa/totp.ts +67 -0
- package/src/auth-mfa/verify-factor.ts +58 -0
- package/src/auth-mfa/web/__tests__/mfa-client.test.ts +265 -0
- package/src/auth-mfa/web/__tests__/mfa-enable-screen.test.tsx +182 -0
- package/src/auth-mfa/web/__tests__/mfa-setup-preauth-screen.test.tsx +379 -0
- package/src/auth-mfa/web/__tests__/mfa-verify-screen.test.tsx +104 -0
- package/src/auth-mfa/web/client-plugin.ts +37 -0
- package/src/auth-mfa/web/i18n.ts +136 -0
- package/src/auth-mfa/web/index.ts +27 -0
- package/src/auth-mfa/web/mfa-client.ts +184 -0
- package/src/auth-mfa/web/mfa-disable-dialog.tsx +69 -0
- package/src/auth-mfa/web/mfa-enable-screen.tsx +228 -0
- package/src/auth-mfa/web/mfa-recovery-codes-reveal.tsx +53 -0
- package/src/auth-mfa/web/mfa-regenerate-recovery-dialog.tsx +68 -0
- package/src/auth-mfa/web/mfa-setup-preauth-screen.tsx +267 -0
- package/src/auth-mfa/web/mfa-verify-screen.tsx +117 -0
- package/src/auth-mfa/web/qrcode-browser.d.ts +12 -0
- package/src/auth-mfa-user-data/__tests__/hooks.integration.test.ts +134 -0
- package/src/auth-mfa-user-data/hooks.ts +53 -0
- package/src/auth-mfa-user-data/index.ts +21 -0
- package/src/billing-foundation/__tests__/billing-foundation.integration.test.ts +206 -2
- package/src/billing-foundation/__tests__/subscription-provider-contract.ts +93 -0
- package/src/billing-foundation/entities.ts +28 -4
- package/src/billing-foundation/events.ts +6 -2
- package/src/billing-foundation/feature.ts +9 -1
- package/src/billing-foundation/get-subscription-for-tenant.ts +16 -5
- package/src/billing-foundation/handlers/create-portal-session.write.ts +17 -1
- package/src/billing-foundation/handlers/list-subscriptions.query.ts +15 -1
- package/src/billing-foundation/handlers/process-event.write.ts +39 -3
- package/src/billing-foundation/tenant-destroy-hook.ts +46 -0
- package/src/cap-counter/__tests__/cap-list.boot.test.ts +27 -0
- package/src/cap-counter/__tests__/cap-list.security.integration.test.ts +53 -0
- package/src/cap-counter/__tests__/entity-ddl.test.ts +15 -0
- package/src/cap-counter/__tests__/with-cap-enforcement.integration.test.ts +8 -5
- package/src/cap-counter/constants.ts +2 -0
- package/src/cap-counter/enforce-cap.ts +15 -18
- package/src/cap-counter/entity.ts +3 -3
- package/src/cap-counter/feature.ts +28 -1
- package/src/cap-counter/i18n.ts +12 -0
- package/src/cap-counter/with-cap-enforcement.ts +2 -2
- package/src/channel-email/__tests__/pii-guard.test.ts +56 -0
- package/src/channel-email/email-channel.ts +2 -1
- package/src/channel-email/index.ts +1 -0
- package/src/channel-email/pii-guard.ts +39 -0
- package/src/compliance-profiles/__tests__/profile-picker.boot.test.ts +28 -0
- package/src/compliance-profiles/__tests__/profile-picker.security.integration.test.ts +91 -0
- package/src/compliance-profiles/constants.ts +15 -0
- package/src/compliance-profiles/feature.ts +22 -1
- package/src/compliance-profiles/handlers/set-profile.write.ts +2 -2
- package/src/compliance-profiles/i18n.ts +6 -0
- package/src/compliance-profiles/index.ts +6 -0
- package/src/compliance-profiles/web/client-plugin.tsx +22 -0
- package/src/compliance-profiles/web/compliance-profile-screen.tsx +152 -0
- package/src/compliance-profiles/web/i18n.ts +25 -0
- package/src/compliance-profiles/web/index.ts +6 -0
- package/src/config/__tests__/app-overrides.test.ts +11 -0
- package/src/config/__tests__/backing-secrets.integration.test.ts +22 -0
- package/src/config/__tests__/cascade.integration.test.ts +35 -0
- package/src/config/__tests__/config.integration.test.ts +71 -10
- package/src/config/__tests__/deserialize-value.test.ts +5 -8
- package/src/config/__tests__/inherited-redaction.integration.test.ts +4 -3
- package/src/config/__tests__/pii-encrypted.integration.test.ts +179 -0
- package/src/config/__tests__/reencrypt-job-concurrent-write-race.integration.test.ts +234 -0
- package/src/config/__tests__/reencrypt-job-kek-rotation.integration.test.ts +243 -0
- package/src/config/__tests__/write-helpers.test.ts +23 -0
- package/src/config/db/queries/resolver.ts +3 -3
- package/src/config/feature.ts +15 -8
- package/src/config/handlers/cascade.query.ts +2 -1
- package/src/config/handlers/reencrypt.job.ts +209 -0
- package/src/config/handlers/set.write.ts +55 -3
- package/src/config/handlers/values.query.ts +2 -1
- package/src/config/i18n.ts +9 -0
- package/src/config/index.ts +5 -4
- package/src/config/resolver.ts +75 -61
- package/src/config/table.ts +1 -1
- package/src/config/write-helpers.ts +22 -0
- package/src/crypto-shredding/__tests__/forget-subject.integration.test.ts +148 -0
- package/src/crypto-shredding/constants.ts +5 -0
- package/src/crypto-shredding/feature.ts +18 -0
- package/src/crypto-shredding/handlers/forget-subject.write.ts +91 -0
- package/src/crypto-shredding/index.ts +11 -0
- package/src/custom-fields/__tests__/audit-integration.integration.test.ts +1 -1
- package/src/custom-fields/__tests__/custom-fields.integration.test.ts +92 -53
- package/src/custom-fields/__tests__/field-access.integration.test.ts +25 -0
- package/src/custom-fields/__tests__/parse-serialized-field.test.ts +11 -3
- package/src/custom-fields/__tests__/retention.integration.test.ts +40 -0
- package/src/custom-fields/__tests__/user-data-rights.integration.test.ts +26 -79
- package/src/custom-fields/constants.ts +1 -0
- package/src/custom-fields/db/queries/user-data-rights.ts +0 -20
- package/src/custom-fields/entity.ts +1 -1
- package/src/custom-fields/events.ts +4 -3
- package/src/custom-fields/feature.ts +8 -0
- package/src/custom-fields/handlers/delete-system-field.write.ts +2 -1
- package/src/custom-fields/handlers/delete-tenant-field.write.ts +2 -1
- package/src/custom-fields/handlers/set-custom-field.write.ts +3 -44
- package/src/custom-fields/handlers/update-tenant-field.write.ts +0 -17
- package/src/custom-fields/lib/field-access.ts +8 -1
- package/src/custom-fields/lib/parse-serialized-field.ts +12 -4
- package/src/custom-fields/lib/value-schema.ts +1 -1
- package/src/custom-fields/run-retention.ts +9 -1
- package/src/custom-fields/schemas.ts +9 -9
- package/src/custom-fields/wire-for-entity.ts +110 -78
- package/src/custom-fields/wire-user-data-rights.ts +5 -54
- package/src/data-retention/__tests__/parse-override.test.ts +14 -13
- package/src/data-retention/__tests__/retention-cleanup.integration.test.ts +145 -8
- package/src/data-retention/feature.ts +3 -3
- package/src/data-retention/run-retention-cleanup.ts +102 -19
- package/src/delivery/__tests__/delivery-pii-kms.integration.test.ts +171 -0
- package/src/delivery/__tests__/delivery-screens.boot.test.ts +27 -0
- package/src/delivery/__tests__/delivery-security.integration.test.ts +106 -0
- package/src/delivery/constants.ts +13 -51
- package/src/delivery/feature.ts +29 -5
- package/src/delivery/handlers/log.query.ts +28 -7
- package/src/delivery/i18n.ts +6 -0
- package/src/delivery/index.ts +6 -1
- package/src/delivery/public-names.ts +36 -0
- package/src/delivery/tables.ts +3 -3
- package/src/delivery/web/client-plugin.tsx +20 -0
- package/src/delivery/web/delivery-log-screen.tsx +103 -0
- package/src/delivery/web/i18n.ts +25 -0
- package/src/delivery/web/index.ts +3 -0
- package/src/document-ingest-foundation/__tests__/feature.integration.test.ts +147 -0
- package/src/document-ingest-foundation/__tests__/feature.test.ts +105 -0
- package/src/document-ingest-foundation/__tests__/pages.test.ts +37 -0
- package/src/document-ingest-foundation/entity.ts +43 -0
- package/src/document-ingest-foundation/events.ts +25 -0
- package/src/document-ingest-foundation/feature.ts +126 -0
- package/src/document-ingest-foundation/index.ts +12 -0
- package/src/document-ingest-foundation/pages.ts +34 -0
- package/src/feature-toggles/__tests__/compose-tier-resolver.test.ts +304 -0
- package/src/feature-toggles/__tests__/feature-toggles.integration.test.ts +7 -6
- package/src/feature-toggles/__tests__/toggle-admin.boot.test.ts +31 -0
- package/src/feature-toggles/__tests__/toggle-admin.security.integration.test.ts +69 -0
- package/src/feature-toggles/compose-tier-resolver.ts +134 -0
- package/src/feature-toggles/constants.ts +14 -0
- package/src/feature-toggles/feature.ts +23 -3
- package/src/feature-toggles/global-feature-state-table.ts +20 -1
- package/src/feature-toggles/handlers/list.query.ts +1 -1
- package/src/feature-toggles/handlers/registered.query.ts +1 -1
- package/src/feature-toggles/i18n.ts +6 -0
- package/src/feature-toggles/index.ts +8 -0
- package/src/feature-toggles/toggle-runtime.ts +14 -0
- package/src/feature-toggles/web/client-plugin.tsx +22 -0
- package/src/feature-toggles/web/i18n.ts +29 -0
- package/src/feature-toggles/web/index.ts +6 -0
- package/src/feature-toggles/web/toggle-admin-screen.tsx +136 -0
- package/src/file-foundation/__tests__/feature.test.ts +5 -5
- package/src/file-foundation/__tests__/file-foundation.integration.test.ts +4 -3
- package/src/file-foundation/feature.ts +10 -11
- package/src/files-provider-s3/__tests__/s3-provider.integration.test.ts +27 -2
- package/src/files-provider-s3/s3-provider.ts +12 -1
- package/src/folders/__tests__/folders.integration.test.ts +3 -0
- package/src/folders/web/__tests__/folder-manager.test.tsx +43 -2
- package/src/folders/web/__tests__/tree.test.ts +8 -0
- package/src/folders/web/folder-manager.tsx +2 -3
- package/src/folders/web/i18n.ts +0 -2
- package/src/folders/web/tree.ts +13 -2
- package/src/folders-user-data/__tests__/hooks.integration.test.ts +1 -0
- package/src/folders-user-data/index.ts +4 -4
- package/src/inbound-mail-foundation/__tests__/account-state.test.ts +30 -0
- package/src/inbound-mail-foundation/__tests__/connect-routes.integration.test.ts +307 -0
- package/src/inbound-mail-foundation/__tests__/feature.test.ts +169 -0
- package/src/inbound-mail-foundation/__tests__/inbound-mail-foundation.integration.test.ts +644 -0
- package/src/inbound-mail-foundation/__tests__/inbound-mail-provider-contract.ts +111 -0
- package/src/inbound-mail-foundation/__tests__/retention.integration.test.ts +237 -0
- package/src/inbound-mail-foundation/__tests__/watch-supervisor.integration.test.ts +433 -0
- package/src/inbound-mail-foundation/aggregate-id.ts +40 -0
- package/src/inbound-mail-foundation/connect-routes.ts +230 -0
- package/src/inbound-mail-foundation/constants.ts +80 -0
- package/src/inbound-mail-foundation/db/queries/inbound-projections.ts +38 -0
- package/src/inbound-mail-foundation/entities.ts +172 -0
- package/src/inbound-mail-foundation/events.ts +147 -0
- package/src/inbound-mail-foundation/feature.ts +243 -0
- package/src/inbound-mail-foundation/handlers/account-state.ts +23 -0
- package/src/inbound-mail-foundation/handlers/connect-account.write.ts +112 -0
- package/src/inbound-mail-foundation/handlers/disconnect-account.write.ts +69 -0
- package/src/inbound-mail-foundation/handlers/ingest-message.write.ts +333 -0
- package/src/inbound-mail-foundation/handlers/list-accounts.query.ts +44 -0
- package/src/inbound-mail-foundation/handlers/list-messages.query.ts +58 -0
- package/src/inbound-mail-foundation/handlers/scope-visibility.ts +14 -0
- package/src/inbound-mail-foundation/handlers/update-account.write.ts +78 -0
- package/src/inbound-mail-foundation/index.ts +97 -0
- package/src/inbound-mail-foundation/oauth-state.ts +103 -0
- package/src/inbound-mail-foundation/projection.ts +152 -0
- package/src/inbound-mail-foundation/provider-factory.ts +53 -0
- package/src/inbound-mail-foundation/retention-sweep.ts +168 -0
- package/src/inbound-mail-foundation/tenant-destroy-hook.ts +93 -0
- package/src/inbound-mail-foundation/types.ts +250 -0
- package/src/inbound-mail-foundation/watch-supervisor.ts +482 -0
- package/src/inbound-provider-imap/__tests__/feature.test.ts +115 -0
- package/src/inbound-provider-imap/__tests__/imap-foundation.integration.test.ts +302 -0
- package/src/inbound-provider-imap/__tests__/imap-live.integration.test.ts +206 -0
- package/src/inbound-provider-imap/__tests__/plugin-mocked.test.ts +358 -0
- package/src/inbound-provider-imap/credential-document.ts +51 -0
- package/src/inbound-provider-imap/feature.ts +283 -0
- package/src/inbound-provider-imap/imap-client.ts +158 -0
- package/src/inbound-provider-imap/index.ts +17 -0
- package/src/inbound-provider-inmemory/__tests__/feature.test.ts +52 -0
- package/src/inbound-provider-inmemory/feature.ts +154 -0
- package/src/inbound-provider-inmemory/index.ts +12 -0
- package/src/jobs/__tests__/entity-ddl.test.ts +15 -0
- package/src/jobs/__tests__/job-instant.test.ts +15 -0
- package/src/jobs/__tests__/job-system-user.integration.test.ts +4 -8
- package/src/jobs/__tests__/jobs-events.integration.test.ts +37 -1
- package/src/jobs/__tests__/jobs-pii-kms.integration.test.ts +120 -0
- package/src/jobs/__tests__/jobs-screens.boot.test.ts +36 -0
- package/src/jobs/__tests__/jobs-security.integration.test.ts +108 -0
- package/src/jobs/__tests__/reindex-entity-job.integration.test.ts +169 -0
- package/src/jobs/constants.ts +8 -1
- package/src/jobs/feature.ts +52 -8
- package/src/jobs/handlers/detail.query.ts +6 -0
- package/src/jobs/handlers/list.query.ts +8 -1
- package/src/jobs/handlers/projection-rebuild.job.ts +14 -4
- package/src/jobs/handlers/reindex-entity.job.ts +49 -0
- package/src/jobs/i18n.ts +37 -0
- package/src/jobs/job-instant.ts +9 -0
- package/src/jobs/job-run-table.ts +4 -4
- package/src/jobs/web/client-plugin.tsx +22 -0
- package/src/jobs/web/i18n.ts +12 -0
- package/src/jobs/web/index.ts +4 -0
- package/src/jobs/web/job-run-detail-screen.tsx +180 -0
- package/src/jobs/web/job-runs-screen.tsx +146 -0
- package/src/ledger/__tests__/ledger.integration.test.ts +8 -3
- package/src/ledger/handlers/confirm-schedule-period.write.ts +17 -4
- package/src/ledger/recurring.ts +17 -8
- package/src/ledger/web/index.ts +3 -2
- package/src/legal-pages/__tests__/legal-pages.integration.test.ts +55 -0
- package/src/legal-pages/feature.ts +24 -31
- package/src/legal-pages/index.ts +1 -0
- package/src/mail-foundation/__tests__/feature.test.ts +5 -6
- package/src/mail-foundation/__tests__/mail-foundation.integration.test.ts +4 -3
- package/src/mail-foundation/__tests__/mail-transport-contract.ts +58 -0
- package/src/mail-foundation/feature.ts +26 -24
- package/src/mail-transport-inmemory/__tests__/feature.test.ts +23 -0
- package/src/mail-transport-smtp/__tests__/feature.test.ts +65 -0
- package/src/managed-pages/__tests__/branding-coerce.test.ts +2 -6
- package/src/managed-pages/__tests__/i18n-surface.boot.test.ts +30 -0
- package/src/managed-pages/__tests__/managed-pages.integration.test.ts +137 -0
- package/src/managed-pages/feature.ts +45 -47
- package/src/managed-pages/handlers/branding.query.ts +5 -1
- package/src/managed-pages/handlers/by-tenant-published.query.ts +52 -0
- package/src/managed-pages/i18n.ts +66 -0
- package/src/managed-pages/screens/page-screens.ts +5 -1
- package/src/managed-pages/web/client-plugin.tsx +22 -0
- package/src/managed-pages/web/i18n.ts +24 -0
- package/src/managed-pages/web/index.ts +3 -0
- package/src/page-render/__tests__/layout.test.ts +63 -0
- package/src/page-render/__tests__/markdown.test.ts +29 -0
- package/src/page-render/branding.ts +4 -3
- package/src/page-render/index.ts +1 -1
- package/src/page-render/layout.ts +18 -3
- package/src/page-render/markdown.ts +1 -9
- package/src/page-render/web.ts +13 -0
- package/src/personal-access-tokens/__tests__/feature.test.ts +42 -0
- package/src/personal-access-tokens/__tests__/pat.integration.test.ts +239 -0
- package/src/personal-access-tokens/__tests__/scopes.test.ts +42 -0
- package/src/personal-access-tokens/constants.ts +33 -0
- package/src/personal-access-tokens/feature.ts +105 -0
- package/src/personal-access-tokens/handlers/available-scopes.query.ts +21 -0
- package/src/personal-access-tokens/handlers/create.write.ts +47 -0
- package/src/personal-access-tokens/handlers/list.query.ts +50 -0
- package/src/personal-access-tokens/handlers/revoke.write.ts +37 -0
- package/src/personal-access-tokens/hash.ts +17 -0
- package/src/personal-access-tokens/i18n.ts +5 -0
- package/src/personal-access-tokens/index.ts +31 -0
- package/src/personal-access-tokens/resolver.ts +58 -0
- package/src/personal-access-tokens/roles.ts +29 -0
- package/src/personal-access-tokens/schema/api-token.ts +63 -0
- package/src/personal-access-tokens/scopes.ts +43 -0
- package/src/personal-access-tokens/web/client-plugin.tsx +29 -0
- package/src/personal-access-tokens/web/i18n.ts +69 -0
- package/src/personal-access-tokens/web/index.ts +7 -0
- package/src/personal-access-tokens/web/pat-tokens-screen.tsx +265 -0
- package/src/readiness/__tests__/readiness.integration.test.ts +4 -3
- package/src/renderer-simple/__tests__/resolve-variables.test.ts +37 -0
- package/src/renderer-simple/resolve-variables.ts +3 -1
- package/src/secrets/__tests__/rotate.integration.test.ts +115 -18
- package/src/secrets/handlers/rotate.job.ts +77 -98
- package/src/secrets/secrets-context.ts +7 -38
- package/src/secrets/table.ts +7 -8
- package/src/seo/__tests__/llms-txt.test.ts +38 -0
- package/src/seo/__tests__/robots-txt.test.ts +18 -0
- package/src/seo/__tests__/schema-builders.test.ts +57 -0
- package/src/seo/__tests__/seo.integration.test.ts +350 -0
- package/src/seo/__tests__/sitemap.test.ts +38 -0
- package/src/seo/constants.ts +60 -0
- package/src/seo/feature.ts +316 -0
- package/src/seo/handlers/seo-config.query.ts +35 -0
- package/src/seo/index.ts +19 -0
- package/src/seo/llms-txt.ts +33 -0
- package/src/seo/robots-txt.ts +13 -0
- package/src/seo/schema-builders.ts +57 -0
- package/src/seo/sitemap.ts +33 -0
- package/src/sessions/__tests__/auto-revoke-binding.test.ts +71 -0
- package/src/sessions/__tests__/cleanup.integration.test.ts +2 -1
- package/src/sessions/__tests__/password-auto-revoke.integration.test.ts +11 -7
- package/src/sessions/__tests__/rebuild-survival.integration.test.ts +18 -7
- package/src/sessions/__tests__/session-store-registration.test.ts +21 -0
- package/src/sessions/__tests__/sessions-screens.boot.test.ts +55 -0
- package/src/sessions/__tests__/sessions.integration.test.ts +272 -20
- package/src/sessions/__tests__/test-helpers.ts +16 -3
- package/src/sessions/constants.ts +5 -0
- package/src/sessions/db/queries/cleanup.ts +2 -2
- package/src/sessions/feature.ts +175 -26
- package/src/sessions/handlers/detail.query.ts +36 -0
- package/src/sessions/handlers/list.query.ts +14 -9
- package/src/sessions/handlers/mine.query.ts +13 -8
- package/src/sessions/handlers/revoke-all-for-user.write.ts +51 -12
- package/src/sessions/handlers/revoke-all-others.write.ts +19 -1
- package/src/sessions/handlers/revoke.write.ts +13 -0
- package/src/sessions/i18n.ts +23 -0
- package/src/sessions/index.ts +9 -2
- package/src/sessions/schema/user-session.ts +8 -2
- package/src/sessions/session-callbacks.ts +99 -14
- package/src/sessions/session-checker-fail-open.test.ts +77 -0
- package/src/sessions/session-revoked-event.ts +29 -0
- package/src/sessions/testing.ts +14 -1
- package/src/shared/__tests__/chunked-entity-migration.test.ts +145 -0
- package/src/shared/chunked-entity-migration.ts +89 -0
- package/src/shared/decrypt-stored-pii.ts +26 -0
- package/src/shared/encrypt-for-direct-write.ts +23 -0
- package/src/shared/entities-of.ts +17 -0
- package/src/shared/grace-period.test.ts +26 -0
- package/src/shared/grace-period.ts +20 -0
- package/src/{auth-email-password/__tests__ → shared}/identity-v3-hash.test.ts +2 -2
- package/src/shared/index.ts +16 -0
- package/src/shared/map-with-concurrency.test.ts +104 -0
- package/src/shared/map-with-concurrency.ts +38 -0
- package/src/shared/password-hashing.test.ts +35 -0
- package/src/{auth-email-password → shared}/password-hashing.ts +11 -0
- package/src/shared/system-query.ts +5 -0
- package/src/{auth-email-password → shared}/token-burn-store.ts +1 -1
- package/src/subscription-mollie/__tests__/feature.test.ts +97 -0
- package/src/subscription-mollie/__tests__/mollie-foundation.integration.test.ts +12 -1
- package/src/subscription-mollie/__tests__/verify-webhook.test.ts +21 -0
- package/src/subscription-mollie/verify-webhook.ts +1 -0
- package/src/subscription-stripe/__tests__/feature.test.ts +127 -1
- package/src/subscription-stripe/__tests__/runtime.test.ts +14 -24
- package/src/subscription-stripe/__tests__/stripe-foundation.integration.test.ts +15 -5
- package/src/subscription-stripe/__tests__/verify-webhook.test.ts +20 -0
- package/src/subscription-stripe/feature.ts +2 -0
- package/src/subscription-stripe/verify-webhook.ts +3 -2
- package/src/tags/__tests__/schemas.test.ts +34 -0
- package/src/tags/__tests__/tags.integration.test.ts +12 -1
- package/src/tags/entity.ts +6 -1
- package/src/tags/feature.ts +2 -0
- package/src/tags/i18n.ts +5 -0
- package/src/tags/schemas.ts +9 -2
- package/src/tags/web/__tests__/tag-manager.test.tsx +333 -0
- package/src/tags/web/__tests__/tag-section.test.tsx +47 -1
- package/src/tags/web/tag-manager.tsx +1 -1
- package/src/tags/web/tag-section.tsx +6 -3
- package/src/template-resolver/testing.ts +2 -1
- package/src/tenant/__tests__/members-query-kms.integration.test.ts +98 -0
- package/src/tenant/__tests__/members-screens.boot.test.ts +51 -0
- package/src/tenant/__tests__/multi-tenant.integration.test.ts +1 -1
- package/src/tenant/__tests__/tenant-security.integration.test.ts +310 -0
- package/src/tenant/__tests__/tenant.integration.test.ts +8 -4
- package/src/tenant/constants.ts +5 -0
- package/src/tenant/feature.ts +19 -0
- package/src/tenant/handlers/active-tenant-ids.query.ts +8 -2
- package/src/tenant/handlers/cancel-invitation.write.ts +2 -2
- package/src/tenant/handlers/invitations.query.ts +21 -4
- package/src/tenant/handlers/members.query.ts +41 -6
- package/src/tenant/i18n.ts +28 -0
- package/src/tenant/index.ts +7 -2
- package/src/tenant/invitation-table.ts +3 -2
- package/src/tenant/schema/tenant.ts +27 -2
- package/src/tenant/screens.ts +3 -2
- package/src/tenant/seeding.ts +136 -3
- package/src/tenant/web/client-plugin.tsx +20 -0
- package/src/tenant/web/i18n.ts +43 -0
- package/src/tenant/web/index.ts +4 -0
- package/src/tenant/web/members-screen.tsx +270 -0
- package/src/tenant-lifecycle/__tests__/stages.test.ts +22 -0
- package/src/tenant-lifecycle/__tests__/tenant-lifecycle.integration.test.ts +471 -0
- package/src/tenant-lifecycle/boot-checks.ts +45 -0
- package/src/tenant-lifecycle/constants.ts +56 -0
- package/src/tenant-lifecycle/events.ts +46 -0
- package/src/tenant-lifecycle/feature.ts +112 -0
- package/src/tenant-lifecycle/handlers/cancel-destruction.write.ts +79 -0
- package/src/tenant-lifecycle/handlers/request-destruction.write.ts +83 -0
- package/src/tenant-lifecycle/index.ts +7 -0
- package/src/tenant-lifecycle/lib/revoke-tenant-sessions.ts +16 -0
- package/src/tenant-lifecycle/lifecycle-gate.ts +65 -0
- package/src/tenant-lifecycle/run-tenant-destroy.ts +368 -0
- package/src/tenant-lifecycle/stages.ts +206 -0
- package/src/tenant-settings/__tests__/tenant-settings.integration.test.ts +115 -0
- package/src/tenant-settings/config.ts +44 -0
- package/src/tenant-settings/constants.ts +16 -0
- package/src/tenant-settings/feature.ts +49 -0
- package/src/tenant-settings/index.ts +8 -0
- package/src/tenant-settings/tenant-defaults.ts +67 -0
- package/src/text-content/web/__tests__/editor-read-only.test.tsx +33 -6
- package/src/text-content/web/__tests__/group-blocks.test.ts +28 -3
- package/src/text-content/web/client-plugin.tsx +126 -104
- package/src/tier-engine/__tests__/auto-default-tier.integration.test.ts +1 -1
- package/src/tier-engine/__tests__/resolver.integration.test.ts +1 -1
- package/src/tier-engine/__tests__/tier-engine.integration.test.ts +7 -4
- package/src/tier-engine/__tests__/trial-config-validation.test.ts +30 -0
- package/src/tier-engine/feature.ts +13 -6
- package/src/tier-engine/i18n.ts +8 -0
- package/src/tier-engine/web/tier-admin-screen.tsx +26 -19
- package/src/user/__tests__/stream-tenant-backfill.integration.test.ts +186 -0
- package/src/user/__tests__/user.integration.test.ts +26 -0
- package/src/user/constants.ts +1 -0
- package/src/user/db/queries/stream-tenant-backfill.ts +112 -0
- package/src/user/feature.ts +3 -0
- package/src/user/handlers/find-for-auth.query.ts +8 -6
- package/src/user/i18n.ts +20 -0
- package/src/user/index.ts +4 -0
- package/src/user/schema/user.ts +6 -2
- package/src/user/screens.ts +3 -15
- package/src/user/seeding.ts +8 -0
- package/src/user-data-rights/COMPLIANCE.md +21 -1
- package/src/user-data-rights/__tests__/anonymous-deletion-kms.integration.test.ts +145 -0
- package/src/user-data-rights/__tests__/anonymous-deletion.integration.test.ts +3 -0
- package/src/user-data-rights/__tests__/audit-log.integration.test.ts +67 -0
- package/src/user-data-rights/__tests__/boot-checks.test.ts +170 -0
- package/src/user-data-rights/__tests__/cross-data-matrix.integration.test.ts +4 -1
- package/src/user-data-rights/__tests__/download-by-token-resolver-trust.integration.test.ts +169 -0
- package/src/user-data-rights/__tests__/download.integration.test.ts +101 -33
- package/src/user-data-rights/__tests__/email-templates.test.ts +15 -0
- package/src/user-data-rights/__tests__/export-encrypted-fields.integration.test.ts +154 -0
- package/src/user-data-rights/__tests__/export-job-idempotency.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/export-job-list-cross-tenant.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/file-binary-forget-cleanup.integration.test.ts +4 -1
- package/src/user-data-rights/__tests__/file-binary-forget-failure.integration.test.ts +4 -1
- package/src/user-data-rights/__tests__/file-retention.integration.test.ts +4 -1
- package/src/user-data-rights/__tests__/file-storage-unification.integration.test.ts +4 -1
- package/src/user-data-rights/__tests__/forget-cleanup-hook-ordering.integration.test.ts +41 -62
- package/src/user-data-rights/__tests__/forget-hook-registry.integration.test.ts +110 -0
- package/src/user-data-rights/__tests__/inspector-screens.boot.test.ts +14 -0
- package/src/user-data-rights/__tests__/mail-default-bridge.integration.test.ts +19 -2
- package/src/user-data-rights/__tests__/read-users-rebuild-survives-lifecycle.integration.test.ts +25 -14
- package/src/user-data-rights/__tests__/request-cancel-deletion.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/request-deletion-callback.integration.test.ts +4 -1
- package/src/user-data-rights/__tests__/request-export.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/restriction-flow.integration.test.ts +172 -19
- package/src/user-data-rights/__tests__/run-export-jobs-cron-context.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/run-export-jobs.integration.test.ts +257 -26
- package/src/user-data-rights/__tests__/run-forget-cleanup.integration.test.ts +339 -5
- package/src/user-data-rights/__tests__/run-user-export.integration.test.ts +77 -1
- package/src/user-data-rights/__tests__/tenant-model-erasure.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/user-data-rights.integration.test.ts +9 -1
- package/src/user-data-rights/boot-checks.ts +70 -0
- package/src/user-data-rights/db/queries/__tests__/forget-cleanup.integration.test.ts +69 -0
- package/src/user-data-rights/db/queries/forget-cleanup.ts +9 -2
- package/src/user-data-rights/email-templates.ts +6 -6
- package/src/user-data-rights/feature.ts +162 -43
- package/src/user-data-rights/handlers/cancel-deletion.write.ts +8 -11
- package/src/user-data-rights/handlers/deletion-grace-period.ts +4 -1
- package/src/user-data-rights/handlers/download-by-job.query.ts +26 -18
- package/src/user-data-rights/handlers/download-by-token.query.ts +23 -15
- package/src/user-data-rights/handlers/lift-restriction.write.ts +44 -19
- package/src/user-data-rights/handlers/list-download-attempts.query.ts +1 -0
- package/src/user-data-rights/handlers/my-audit-log.query.ts +1 -0
- package/src/user-data-rights/handlers/request-deletion-by-email.write.ts +3 -1
- package/src/user-data-rights/handlers/restrict-account.write.ts +58 -7
- package/src/user-data-rights/handlers/run-forget-cleanup.write.ts +2 -0
- package/src/user-data-rights/i18n.ts +35 -32
- package/src/user-data-rights/lib/is-admin-actor.ts +19 -0
- package/src/user-data-rights/lib/storage-provider-resolver.ts +6 -5
- package/src/user-data-rights/lib/update-user-lifecycle.ts +8 -24
- package/src/user-data-rights/run-export-jobs.ts +55 -19
- package/src/user-data-rights/run-forget-cleanup.ts +112 -7
- package/src/user-data-rights/run-user-export.ts +70 -4
- package/src/user-data-rights/schema/download-token.ts +5 -3
- package/src/user-data-rights/schema/export-job.ts +2 -0
- package/src/user-data-rights/screens.ts +2 -1
- package/src/user-data-rights/web/__tests__/privacy-center-screen.test.tsx +1 -0
- package/src/user-data-rights/web/__tests__/public-deletion-gate.test.tsx +7 -1
- package/src/user-data-rights/web/client-plugin.tsx +6 -1
- package/src/user-data-rights/web/i18n.ts +30 -10
- package/src/user-data-rights/web/privacy-center-screen.tsx +5 -3
- package/src/user-data-rights-defaults/__tests__/bundled-entities.userdata-hooks.integration.test.ts +455 -0
- package/src/user-data-rights-defaults/__tests__/feature-mounted.test.ts +24 -0
- package/src/user-data-rights-defaults/__tests__/user-data-rights-defaults.integration.test.ts +33 -9
- package/src/user-data-rights-defaults/feature.ts +78 -1
- package/src/user-data-rights-defaults/hooks/api-token.userdata-hook.ts +37 -0
- package/src/user-data-rights-defaults/hooks/config-value.userdata-hook.ts +51 -0
- package/src/user-data-rights-defaults/hooks/delivery-attempt.userdata-hook.ts +37 -0
- package/src/user-data-rights-defaults/hooks/feature-mounted.ts +10 -0
- package/src/user-data-rights-defaults/hooks/in-app-message.userdata-hook.ts +38 -0
- package/src/user-data-rights-defaults/hooks/job-run.userdata-hook.ts +35 -0
- package/src/user-data-rights-defaults/hooks/notification-preference.userdata-hook.ts +54 -0
- package/src/user-data-rights-defaults/hooks/tenant-invitation.userdata-hook.ts +112 -0
- package/src/user-data-rights-defaults/hooks/user-session.userdata-hook.ts +42 -0
- package/src/user-data-rights-defaults/index.ts +26 -0
- package/src/user-profile/__tests__/change-email.integration.test.ts +7 -1
- package/src/user-profile/__tests__/profile-screen.test.tsx +5 -0
- package/src/user-profile/feature.ts +4 -1
- package/src/user-profile/handlers/change-email.write.ts +15 -10
- package/src/user-profile/web/profile-screen.tsx +15 -3
- package/src/auth-email-password/stream-tenant.ts +0 -31
- package/src/feature-toggles/db/queries/toggle-state.ts +0 -25
- package/src/legal-pages/security-headers.ts +0 -1
- package/src/sessions/schema/index.ts +0 -5
- package/src/tenant/schema/index.ts +0 -5
- package/src/user/schema/index.ts +0 -5
- /package/src/{auth-email-password → shared}/identity-v3-hash.ts +0 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// kumiko-feature-version: 1
|
|
2
|
+
//
|
|
3
|
+
// auth-foundation — declares auth-middleware extension points:
|
|
4
|
+
// - tokenVerifier (multi, shape-routed)
|
|
5
|
+
// - sessionStore (single, required when foundation mounts)
|
|
6
|
+
// - tenantResolver (single, optional)
|
|
7
|
+
// - tenantExistence (single, optional)
|
|
8
|
+
//
|
|
9
|
+
// Provider-features register via `r.useExtension(EXT_*, "<name>", plugin)`.
|
|
10
|
+
// Middleware / runProdApp resolve providers from the registry at boot
|
|
11
|
+
// (tokenVerifier/sessionStore) or request-time (tokenVerifier shape match).
|
|
12
|
+
|
|
13
|
+
import type { SessionUser } from "@cosmicdrift/kumiko-framework/engine";
|
|
14
|
+
import { defineFeature, type Registry } from "@cosmicdrift/kumiko-framework/engine";
|
|
15
|
+
import {
|
|
16
|
+
validateSessionStoreMultiplicity,
|
|
17
|
+
validateTenantExistenceMultiplicity,
|
|
18
|
+
validateTenantResolverMultiplicity,
|
|
19
|
+
validateTokenVerifierMultiplicity,
|
|
20
|
+
} from "./boot-checks";
|
|
21
|
+
import {
|
|
22
|
+
type AuthProviderBuildDeps,
|
|
23
|
+
EXT_SESSION_STORE,
|
|
24
|
+
EXT_TENANT_EXISTENCE,
|
|
25
|
+
EXT_TENANT_RESOLVER,
|
|
26
|
+
EXT_TOKEN_VERIFIER,
|
|
27
|
+
isAuthProviderPlugin,
|
|
28
|
+
isSessionStoreProvider,
|
|
29
|
+
isTenantExistenceProvider,
|
|
30
|
+
isTenantResolverProvider,
|
|
31
|
+
type SessionStore,
|
|
32
|
+
type TenantExistsFn,
|
|
33
|
+
type TenantResolverFn,
|
|
34
|
+
type TenantResolverTrust,
|
|
35
|
+
tokenShapeMatches,
|
|
36
|
+
} from "./types";
|
|
37
|
+
|
|
38
|
+
export {
|
|
39
|
+
EXT_SESSION_STORE,
|
|
40
|
+
EXT_TENANT_EXISTENCE,
|
|
41
|
+
EXT_TENANT_RESOLVER,
|
|
42
|
+
EXT_TOKEN_VERIFIER,
|
|
43
|
+
} from "./types";
|
|
44
|
+
|
|
45
|
+
const FEATURE_NAME = "auth-foundation";
|
|
46
|
+
|
|
47
|
+
export const authFoundationFeature = defineFeature(FEATURE_NAME, (r) => {
|
|
48
|
+
r.describe(
|
|
49
|
+
"Declares auth-middleware extension points: `tokenVerifier` (Bearer), `sessionStore` (revocable JWT sid), optional `tenantResolver` + `tenantExistence` for anonymous multi-tenant. Provider-features register via r.useExtension; mount auth-foundation with the matching provider features.",
|
|
50
|
+
);
|
|
51
|
+
r.uiHints({
|
|
52
|
+
displayLabel: "Auth Provider Foundation",
|
|
53
|
+
category: "identity",
|
|
54
|
+
recommended: false,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
r.extendsRegistrar(EXT_TOKEN_VERIFIER, {
|
|
58
|
+
onRegister: () => {
|
|
59
|
+
// No side-effects at register-time — registry stores the usage,
|
|
60
|
+
// resolveTokenVerifier looks it up at request-time.
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
r.bootCheck(({ features }) => validateTokenVerifierMultiplicity(features));
|
|
64
|
+
|
|
65
|
+
r.extendsRegistrar(EXT_SESSION_STORE, {
|
|
66
|
+
onRegister: () => {
|
|
67
|
+
// No side-effects at register-time — resolveSessionStore at boot/request.
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
r.bootCheck(({ features }) => validateSessionStoreMultiplicity(features));
|
|
71
|
+
|
|
72
|
+
r.extendsRegistrar(EXT_TENANT_RESOLVER, {
|
|
73
|
+
onRegister: () => {
|
|
74
|
+
// Optional — zero providers is fine (single-tenant / header-cookie).
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
r.bootCheck(({ features }) => validateTenantResolverMultiplicity(features));
|
|
78
|
+
|
|
79
|
+
r.extendsRegistrar(EXT_TENANT_EXISTENCE, {
|
|
80
|
+
onRegister: () => {
|
|
81
|
+
// Optional — zero providers skips the existence check.
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
r.bootCheck(({ features }) => validateTenantExistenceMultiplicity(features));
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Tries every registered tokenVerifier provider's shape against `rawToken`
|
|
89
|
+
* in registration order and calls the first match's verifier. Returns null
|
|
90
|
+
* when no provider's shape matches, or when the matching provider's
|
|
91
|
+
* verifier rejects the token.
|
|
92
|
+
*/
|
|
93
|
+
export async function resolveTokenVerifier(
|
|
94
|
+
deps: AuthProviderBuildDeps & { readonly registry: Registry },
|
|
95
|
+
rawToken: string,
|
|
96
|
+
): Promise<SessionUser | null> {
|
|
97
|
+
const usages = deps.registry.getExtensionUsages(EXT_TOKEN_VERIFIER);
|
|
98
|
+
for (const usage of usages) {
|
|
99
|
+
if (!isAuthProviderPlugin(usage.options)) continue;
|
|
100
|
+
if (!tokenShapeMatches(usage.options.shape, rawToken)) continue;
|
|
101
|
+
const verify = await usage.options.build(deps);
|
|
102
|
+
return verify(rawToken);
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Resolves the single registered sessionStore provider. Assumes
|
|
109
|
+
* validateSessionStoreMultiplicity already ran at boot — a missing provider
|
|
110
|
+
* here means the app booted without going through the framework's boot
|
|
111
|
+
* validator, not a runtime state resolveSessionStore should model.
|
|
112
|
+
*/
|
|
113
|
+
export async function resolveSessionStore(
|
|
114
|
+
deps: AuthProviderBuildDeps & { readonly registry: Registry },
|
|
115
|
+
): Promise<SessionStore> {
|
|
116
|
+
const usage = deps.registry.getExtensionUsages(EXT_SESSION_STORE)[0];
|
|
117
|
+
if (!usage || !isSessionStoreProvider(usage.options)) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
"[auth-foundation] no sessionStore provider registered — did the boot validator run?",
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
return usage.options.build(deps);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export type ResolvedTenantResolver = {
|
|
126
|
+
readonly resolve: TenantResolverFn;
|
|
127
|
+
readonly trust: TenantResolverTrust;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/** Optional tenantResolver — null when no provider registered. */
|
|
131
|
+
export async function resolveTenantResolver(
|
|
132
|
+
deps: AuthProviderBuildDeps & { readonly registry: Registry },
|
|
133
|
+
): Promise<ResolvedTenantResolver | null> {
|
|
134
|
+
const usage = deps.registry.getExtensionUsages(EXT_TENANT_RESOLVER)[0];
|
|
135
|
+
if (!usage) return null;
|
|
136
|
+
if (!isTenantResolverProvider(usage.options)) {
|
|
137
|
+
throw new Error(
|
|
138
|
+
"[auth-foundation] tenantResolver provider registered without a valid TenantResolverProvider",
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
const resolve = await usage.options.build(deps);
|
|
142
|
+
return { resolve, trust: usage.options.trust };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Optional tenantExistence — null when no provider registered. */
|
|
146
|
+
export async function resolveTenantExistence(
|
|
147
|
+
deps: AuthProviderBuildDeps & { readonly registry: Registry },
|
|
148
|
+
): Promise<TenantExistsFn | null> {
|
|
149
|
+
const usage = deps.registry.getExtensionUsages(EXT_TENANT_EXISTENCE)[0];
|
|
150
|
+
if (!usage) return null;
|
|
151
|
+
if (!isTenantExistenceProvider(usage.options)) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
"[auth-foundation] tenantExistence provider registered without a valid TenantExistenceProvider",
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
return usage.options.build(deps);
|
|
157
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Public API of the auth-foundation bundled-feature.
|
|
2
|
+
|
|
3
|
+
export {
|
|
4
|
+
authFoundationFeature,
|
|
5
|
+
type ResolvedTenantResolver,
|
|
6
|
+
resolveSessionStore,
|
|
7
|
+
resolveTenantExistence,
|
|
8
|
+
resolveTenantResolver,
|
|
9
|
+
resolveTokenVerifier,
|
|
10
|
+
} from "./feature";
|
|
11
|
+
export { resolveAnonymousAccessFromRegistry } from "./resolve-anonymous-access";
|
|
12
|
+
export {
|
|
13
|
+
type AuthProviderBuildDeps,
|
|
14
|
+
type AuthProviderPlugin,
|
|
15
|
+
type AuthVerifier,
|
|
16
|
+
EXT_SESSION_STORE,
|
|
17
|
+
EXT_TENANT_EXISTENCE,
|
|
18
|
+
EXT_TENANT_RESOLVER,
|
|
19
|
+
EXT_TOKEN_VERIFIER,
|
|
20
|
+
isAuthProviderPlugin,
|
|
21
|
+
isSessionStoreProvider,
|
|
22
|
+
isTenantExistenceProvider,
|
|
23
|
+
isTenantResolverProvider,
|
|
24
|
+
isTenantResolverTrust,
|
|
25
|
+
isValidTokenShape,
|
|
26
|
+
type SessionMassRevoker,
|
|
27
|
+
type SessionStore,
|
|
28
|
+
type SessionStoreProvider,
|
|
29
|
+
type TenantExistenceProvider,
|
|
30
|
+
type TenantExistsFn,
|
|
31
|
+
type TenantResolverFn,
|
|
32
|
+
type TenantResolverProvider,
|
|
33
|
+
type TenantResolverTrust,
|
|
34
|
+
type TokenShape,
|
|
35
|
+
tokenShapeKey,
|
|
36
|
+
tokenShapeMatches,
|
|
37
|
+
} from "./types";
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge app-facing AnonymousAccessConfig with auth-foundation tenant providers.
|
|
3
|
+
* Providers win over any leftover callback fields (hard cutover #1374).
|
|
4
|
+
* Test stacks may still pass Resolved callbacks when no provider is mounted.
|
|
5
|
+
*/
|
|
6
|
+
import type {
|
|
7
|
+
AnonymousAccessConfig,
|
|
8
|
+
AnonymousAccessResolved,
|
|
9
|
+
} from "@cosmicdrift/kumiko-framework/api";
|
|
10
|
+
import type { Registry } from "@cosmicdrift/kumiko-framework/engine";
|
|
11
|
+
import { resolveTenantExistence, resolveTenantResolver } from "./feature";
|
|
12
|
+
import type { AuthProviderBuildDeps } from "./types";
|
|
13
|
+
|
|
14
|
+
export async function resolveAnonymousAccessFromRegistry(
|
|
15
|
+
base: AnonymousAccessResolved | AnonymousAccessConfig | undefined,
|
|
16
|
+
deps: AuthProviderBuildDeps & { readonly registry: Registry },
|
|
17
|
+
): Promise<AnonymousAccessResolved | undefined> {
|
|
18
|
+
const [tenantResolver, tenantExists] = await Promise.all([
|
|
19
|
+
resolveTenantResolver(deps),
|
|
20
|
+
resolveTenantExistence(deps),
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
// Anonymous access is app opt-in only: a mounted routing provider
|
|
24
|
+
// (EXT_TENANT_RESOLVER/EXT_TENANT_EXISTENCE) alone must never make
|
|
25
|
+
// anonymousAccess become defined for an app that never set it — that
|
|
26
|
+
// would upgrade every roles:["anonymous"] handler to reachable without
|
|
27
|
+
// a JWT the moment such a routing feature is mounted (#1452).
|
|
28
|
+
if (!base) return undefined;
|
|
29
|
+
|
|
30
|
+
// Fail-closed (#1374 security): inline tenantResolver without trust is the
|
|
31
|
+
// old ambiguous config — providers always carry trust; test stacks must too.
|
|
32
|
+
if (
|
|
33
|
+
!tenantResolver &&
|
|
34
|
+
base &&
|
|
35
|
+
"tenantResolver" in base &&
|
|
36
|
+
base.tenantResolver !== undefined &&
|
|
37
|
+
base.resolverTrust === undefined
|
|
38
|
+
) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
"[auth-foundation] anonymousAccess.tenantResolver is set without resolverTrust — " +
|
|
41
|
+
'declare "authoritative" or "fallback-only" (or mount an EXT_TENANT_RESOLVER provider).',
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Providers win (#1374). When none are mounted, keep any test-injected
|
|
46
|
+
// Resolved callbacks so framework middleware suites stay self-contained.
|
|
47
|
+
return {
|
|
48
|
+
...(base ?? {}),
|
|
49
|
+
...(tenantResolver
|
|
50
|
+
? {
|
|
51
|
+
// No cast: TenantResolverFn's `(c: unknown) => string | null`
|
|
52
|
+
// and the framework's TenantResolver `(c: Context) => TenantId
|
|
53
|
+
// | null` are structurally identical (TenantId is a plain
|
|
54
|
+
// string alias, no brand) — `unknown` is a supertype of
|
|
55
|
+
// Context, so this is already sound by parameter contravariance.
|
|
56
|
+
tenantResolver: tenantResolver.resolve,
|
|
57
|
+
resolverTrust: tenantResolver.trust,
|
|
58
|
+
}
|
|
59
|
+
: {}),
|
|
60
|
+
...(tenantExists ? { tenantExists } : {}),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// kumiko-feature-version: 1
|
|
2
|
+
//
|
|
3
|
+
// Type contracts for auth-foundation's `tokenVerifier` extension point.
|
|
4
|
+
// Provider-features (future: auth-provider-jwt, auth-provider-pat) register
|
|
5
|
+
// an AuthProviderPlugin via `r.useExtension(EXT_TOKEN_VERIFIER, "<name>", plugin)`.
|
|
6
|
+
//
|
|
7
|
+
// **Shape-match routing:** unlike file-foundation/mail-foundation (one active
|
|
8
|
+
// provider per tenant, chosen via a config-key), token verification happens
|
|
9
|
+
// BEFORE the tenant is known — there's nothing to select on. Every registered
|
|
10
|
+
// provider declares a static `shape` (how to recognize its own tokens by
|
|
11
|
+
// looking at the raw bearer value); `resolveTokenVerifier` tries each
|
|
12
|
+
// provider's shape in turn and calls the first match's verifier.
|
|
13
|
+
|
|
14
|
+
import type {
|
|
15
|
+
SessionChecker,
|
|
16
|
+
SessionCreator,
|
|
17
|
+
SessionMassRevoker,
|
|
18
|
+
SessionRevoker,
|
|
19
|
+
} from "@cosmicdrift/kumiko-framework/api";
|
|
20
|
+
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
21
|
+
import type { SessionUser } from "@cosmicdrift/kumiko-framework/engine";
|
|
22
|
+
|
|
23
|
+
export type { SessionMassRevoker };
|
|
24
|
+
|
|
25
|
+
export const EXT_TOKEN_VERIFIER = "tokenVerifier";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Deps available to a provider's `build()`. DB access only — a hot-path
|
|
29
|
+
* resolver (see personal-access-tokens/resolver.ts) needs a raw-DB handle
|
|
30
|
+
* for a point-read + live-role-resolution, not a per-request ctx (there is
|
|
31
|
+
* no tenant/session yet at verify-time).
|
|
32
|
+
*/
|
|
33
|
+
export type AuthProviderBuildDeps = {
|
|
34
|
+
readonly db: DbConnection;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** How to recognize this provider's tokens by looking at the raw bearer value. */
|
|
38
|
+
export type TokenShape =
|
|
39
|
+
| { readonly kind: "jwt" }
|
|
40
|
+
| { readonly kind: "prefix"; readonly prefix: string };
|
|
41
|
+
|
|
42
|
+
export type AuthVerifier = (rawToken: string) => Promise<SessionUser | null>;
|
|
43
|
+
|
|
44
|
+
export type AuthProviderPlugin = {
|
|
45
|
+
readonly shape: TokenShape;
|
|
46
|
+
readonly build: (deps: AuthProviderBuildDeps) => Promise<AuthVerifier> | AuthVerifier;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export function isValidTokenShape(shape: unknown): shape is TokenShape {
|
|
50
|
+
if (typeof shape !== "object" || shape === null || !("kind" in shape)) return false;
|
|
51
|
+
const kind = (shape as { kind: unknown }).kind;
|
|
52
|
+
if (kind === "jwt") return true;
|
|
53
|
+
if (kind === "prefix") {
|
|
54
|
+
const prefix = "prefix" in shape ? (shape as { prefix: unknown }).prefix : undefined;
|
|
55
|
+
return typeof prefix === "string" && prefix.length > 0;
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// extension-usage `options` is engine-payload (unknown) — structurally validate
|
|
61
|
+
// instead of casting blind. Mirrors file-foundation's isFileProviderPlugin.
|
|
62
|
+
export function isAuthProviderPlugin(o: unknown): o is AuthProviderPlugin {
|
|
63
|
+
return (
|
|
64
|
+
typeof o === "object" &&
|
|
65
|
+
o !== null &&
|
|
66
|
+
"build" in o &&
|
|
67
|
+
typeof (o as { build: unknown }).build === "function" &&
|
|
68
|
+
"shape" in o &&
|
|
69
|
+
isValidTokenShape((o as { shape: unknown }).shape)
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function tokenShapeMatches(shape: TokenShape, rawToken: string): boolean {
|
|
74
|
+
if (shape.kind === "prefix") return rawToken.startsWith(shape.prefix);
|
|
75
|
+
return rawToken.split(".").length === 3;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Static discriminator used by the multiplicity boot-check to detect two providers claiming the same shape. */
|
|
79
|
+
export function tokenShapeKey(shape: TokenShape): string {
|
|
80
|
+
return shape.kind === "jwt" ? "jwt" : `prefix:${shape.prefix}`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Session-store extension point (#1370). Unlike EXT_TOKEN_VERIFIER, this is
|
|
84
|
+
// single-provider — sessions have no per-provider shape to route on, so
|
|
85
|
+
// exactly one implementation must be registered (boot-fails on 0 or ≥2).
|
|
86
|
+
|
|
87
|
+
export const EXT_SESSION_STORE = "sessionStore";
|
|
88
|
+
|
|
89
|
+
/** Keep current sid (if any); revoke every other live session. Used by auth-mfa. */
|
|
90
|
+
export type SessionRevokeAllOthers = (
|
|
91
|
+
userId: string,
|
|
92
|
+
currentSid: string | undefined,
|
|
93
|
+
) => Promise<number>;
|
|
94
|
+
|
|
95
|
+
export type SessionStore = {
|
|
96
|
+
readonly creator: SessionCreator;
|
|
97
|
+
readonly revoker: SessionRevoker;
|
|
98
|
+
readonly checker: SessionChecker;
|
|
99
|
+
readonly massRevoker: SessionMassRevoker;
|
|
100
|
+
readonly revokeAllOthers: SessionRevokeAllOthers;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export type SessionStoreProvider = {
|
|
104
|
+
readonly build: (deps: AuthProviderBuildDeps) => Promise<SessionStore> | SessionStore;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export function isSessionStoreProvider(o: unknown): o is SessionStoreProvider {
|
|
108
|
+
return (
|
|
109
|
+
typeof o === "object" &&
|
|
110
|
+
o !== null &&
|
|
111
|
+
"build" in o &&
|
|
112
|
+
typeof (o as { build: unknown }).build === "function"
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Tenant-resolution extension points (#1373). Both are single-provider and
|
|
117
|
+
// optional — zero registered means today's single-tenant / header-cookie
|
|
118
|
+
// path (no subdomain resolver, no existence check). ≥2 fails boot.
|
|
119
|
+
|
|
120
|
+
export const EXT_TENANT_RESOLVER = "tenantResolver";
|
|
121
|
+
export const EXT_TENANT_EXISTENCE = "tenantExistence";
|
|
122
|
+
|
|
123
|
+
/** Trust mode for a registered tenantResolver — mirrors AnonymousAccessConfig.resolverTrust. */
|
|
124
|
+
export type TenantResolverTrust = "authoritative" | "fallback-only";
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Resolves the tenant for an unauthenticated request. Return null when no
|
|
128
|
+
* tenant can be determined — middleware rejects with 400. Throw only on
|
|
129
|
+
* infrastructure failures.
|
|
130
|
+
*/
|
|
131
|
+
export type TenantResolverFn = (
|
|
132
|
+
// Hono Context — typed loosely so auth-foundation stays free of a hono dep.
|
|
133
|
+
// Callers cast at the boundary (same shape as framework TenantResolver).
|
|
134
|
+
c: unknown,
|
|
135
|
+
) => Promise<string | null> | string | null;
|
|
136
|
+
|
|
137
|
+
export type TenantExistsFn = (tenantId: string) => Promise<boolean> | boolean;
|
|
138
|
+
|
|
139
|
+
export type TenantResolverProvider = {
|
|
140
|
+
readonly trust: TenantResolverTrust;
|
|
141
|
+
readonly build: (deps: AuthProviderBuildDeps) => Promise<TenantResolverFn> | TenantResolverFn;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export type TenantExistenceProvider = {
|
|
145
|
+
readonly build: (deps: AuthProviderBuildDeps) => Promise<TenantExistsFn> | TenantExistsFn;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
export function isTenantResolverTrust(v: unknown): v is TenantResolverTrust {
|
|
149
|
+
return v === "authoritative" || v === "fallback-only";
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function isTenantResolverProvider(o: unknown): o is TenantResolverProvider {
|
|
153
|
+
return (
|
|
154
|
+
typeof o === "object" &&
|
|
155
|
+
o !== null &&
|
|
156
|
+
"build" in o &&
|
|
157
|
+
typeof (o as { build: unknown }).build === "function" &&
|
|
158
|
+
"trust" in o &&
|
|
159
|
+
isTenantResolverTrust((o as { trust: unknown }).trust)
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function isTenantExistenceProvider(o: unknown): o is TenantExistenceProvider {
|
|
164
|
+
return (
|
|
165
|
+
typeof o === "object" &&
|
|
166
|
+
o !== null &&
|
|
167
|
+
"build" in o &&
|
|
168
|
+
typeof (o as { build: unknown }).build === "function"
|
|
169
|
+
);
|
|
170
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { base32Decode, base32Encode } from "../base32";
|
|
3
|
+
|
|
4
|
+
describe("base32 encode/decode", () => {
|
|
5
|
+
test("round-trips arbitrary bytes", () => {
|
|
6
|
+
const bytes = Buffer.from("12345678901234567890", "ascii");
|
|
7
|
+
expect(base32Decode(base32Encode(bytes))).toEqual(bytes);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test("decodes lowercase and RFC 4648 '=' padding", () => {
|
|
11
|
+
const bytes = Buffer.from("12345678901234567890", "ascii");
|
|
12
|
+
const encoded = base32Encode(bytes);
|
|
13
|
+
const paddingLength = (8 - (encoded.length % 8)) % 8;
|
|
14
|
+
const withPadding = encoded + "=".repeat(paddingLength);
|
|
15
|
+
expect(base32Decode(withPadding.toLowerCase())).toEqual(bytes);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("rejects a non-canonical length with non-zero residual bits", () => {
|
|
19
|
+
// "AA" decodes to 10 residual bits of all-zero value — canonical (padding
|
|
20
|
+
// zeros). A single stray char (5 bits, below the byte boundary but not a
|
|
21
|
+
// clean multiple) with non-zero value must be rejected, not truncated.
|
|
22
|
+
expect(() => base32Decode("B")).toThrow(/invalid length/);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("throws on a genuinely invalid character", () => {
|
|
26
|
+
expect(() => base32Decode("01")).toThrow(/invalid character/);
|
|
27
|
+
});
|
|
28
|
+
});
|