@bajustone/fortress 1.0.0-rc.1
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/CHANGELOG.md +1011 -0
- package/LICENSE +21 -0
- package/README.md +760 -0
- package/SECURITY.md +197 -0
- package/bin/fortress.ts +667 -0
- package/dist/auth-service-BcyQ2PuZ.d.cts +307 -0
- package/dist/auth-service-BkzZkWfH.d.ts +307 -0
- package/dist/config-B2366s_G.d.ts +665 -0
- package/dist/config-GtlVt6ZD.d.cts +665 -0
- package/dist/convert-routes-BLCQT57f.d.ts +72 -0
- package/dist/convert-routes-BdMvP2_X.d.cts +72 -0
- package/dist/crypto.cjs +61 -0
- package/dist/crypto.d.cts +36 -0
- package/dist/crypto.d.ts +36 -0
- package/dist/crypto.js +35 -0
- package/dist/drift-BT1wtMDJ.d.cts +22 -0
- package/dist/drift-k9LiYpRP.d.ts +22 -0
- package/dist/drizzle/pg.cjs +481 -0
- package/dist/drizzle/pg.d.cts +15 -0
- package/dist/drizzle/pg.d.ts +15 -0
- package/dist/drizzle/pg.js +454 -0
- package/dist/drizzle.cjs +1442 -0
- package/dist/drizzle.d.cts +85 -0
- package/dist/drizzle.d.ts +85 -0
- package/dist/drizzle.js +1411 -0
- package/dist/express.cjs +1357 -0
- package/dist/express.d.cts +333 -0
- package/dist/express.d.ts +333 -0
- package/dist/express.js +1314 -0
- package/dist/fetcher.cjs +77 -0
- package/dist/fetcher.d.cts +7 -0
- package/dist/fetcher.d.ts +7 -0
- package/dist/fetcher.js +41 -0
- package/dist/fortress-BmSvFfqK.d.cts +1089 -0
- package/dist/fortress-uA-_cheR.d.ts +1089 -0
- package/dist/hono.cjs +1530 -0
- package/dist/hono.d.cts +514 -0
- package/dist/hono.d.ts +514 -0
- package/dist/hono.js +1483 -0
- package/dist/index-CxEkfCA0.d.cts +77 -0
- package/dist/index-CxEkfCA0.d.ts +77 -0
- package/dist/index-D054pcb-.d.cts +146 -0
- package/dist/index-jAMbbUAY.d.ts +146 -0
- package/dist/index.cjs +9046 -0
- package/dist/index.d.cts +717 -0
- package/dist/index.d.ts +717 -0
- package/dist/index.js +8935 -0
- package/dist/jwt.cjs +255 -0
- package/dist/jwt.d.cts +90 -0
- package/dist/jwt.d.ts +90 -0
- package/dist/jwt.js +227 -0
- package/dist/otel.cjs +108 -0
- package/dist/otel.d.cts +62 -0
- package/dist/otel.d.ts +62 -0
- package/dist/otel.js +73 -0
- package/dist/plugins/account-lockout.cjs +322 -0
- package/dist/plugins/account-lockout.d.cts +42 -0
- package/dist/plugins/account-lockout.d.ts +42 -0
- package/dist/plugins/account-lockout.js +295 -0
- package/dist/plugins/admin.cjs +2378 -0
- package/dist/plugins/admin.d.cts +72 -0
- package/dist/plugins/admin.d.ts +72 -0
- package/dist/plugins/admin.js +2351 -0
- package/dist/plugins/api-key.cjs +792 -0
- package/dist/plugins/api-key.d.cts +121 -0
- package/dist/plugins/api-key.d.ts +121 -0
- package/dist/plugins/api-key.js +765 -0
- package/dist/plugins/audit-log.cjs +493 -0
- package/dist/plugins/audit-log.d.cts +86 -0
- package/dist/plugins/audit-log.d.ts +86 -0
- package/dist/plugins/audit-log.js +468 -0
- package/dist/plugins/data-isolation.cjs +211 -0
- package/dist/plugins/data-isolation.d.cts +49 -0
- package/dist/plugins/data-isolation.d.ts +49 -0
- package/dist/plugins/data-isolation.js +186 -0
- package/dist/plugins/email-verification.cjs +273 -0
- package/dist/plugins/email-verification.d.cts +44 -0
- package/dist/plugins/email-verification.d.ts +44 -0
- package/dist/plugins/email-verification.js +246 -0
- package/dist/plugins/magic-link.cjs +231 -0
- package/dist/plugins/magic-link.d.cts +39 -0
- package/dist/plugins/magic-link.d.ts +39 -0
- package/dist/plugins/magic-link.js +204 -0
- package/dist/plugins/oauth.cjs +1696 -0
- package/dist/plugins/oauth.d.cts +406 -0
- package/dist/plugins/oauth.d.ts +406 -0
- package/dist/plugins/oauth.js +1669 -0
- package/dist/plugins/openapi.cjs +1185 -0
- package/dist/plugins/openapi.d.cts +6 -0
- package/dist/plugins/openapi.d.ts +6 -0
- package/dist/plugins/openapi.js +1158 -0
- package/dist/plugins/rate-limit/express.cjs +55 -0
- package/dist/plugins/rate-limit/express.d.cts +64 -0
- package/dist/plugins/rate-limit/express.d.ts +64 -0
- package/dist/plugins/rate-limit/express.js +30 -0
- package/dist/plugins/rate-limit/hono.cjs +51 -0
- package/dist/plugins/rate-limit/hono.d.cts +59 -0
- package/dist/plugins/rate-limit/hono.d.ts +59 -0
- package/dist/plugins/rate-limit/hono.js +26 -0
- package/dist/plugins/rate-limit/sveltekit.cjs +49 -0
- package/dist/plugins/rate-limit/sveltekit.d.cts +59 -0
- package/dist/plugins/rate-limit/sveltekit.d.ts +59 -0
- package/dist/plugins/rate-limit/sveltekit.js +24 -0
- package/dist/plugins/rate-limit.cjs +354 -0
- package/dist/plugins/rate-limit.d.cts +140 -0
- package/dist/plugins/rate-limit.d.ts +140 -0
- package/dist/plugins/rate-limit.js +325 -0
- package/dist/plugins/social-login.cjs +905 -0
- package/dist/plugins/social-login.d.cts +114 -0
- package/dist/plugins/social-login.d.ts +114 -0
- package/dist/plugins/social-login.js +880 -0
- package/dist/plugins/tenancy.cjs +780 -0
- package/dist/plugins/tenancy.d.cts +108 -0
- package/dist/plugins/tenancy.d.ts +108 -0
- package/dist/plugins/tenancy.js +753 -0
- package/dist/plugins/two-factor.cjs +555 -0
- package/dist/plugins/two-factor.d.cts +67 -0
- package/dist/plugins/two-factor.d.ts +67 -0
- package/dist/plugins/two-factor.js +526 -0
- package/dist/plugins/webauthn.cjs +786 -0
- package/dist/plugins/webauthn.d.cts +72 -0
- package/dist/plugins/webauthn.d.ts +72 -0
- package/dist/plugins/webauthn.js +766 -0
- package/dist/plugins/webhook.cjs +819 -0
- package/dist/plugins/webhook.d.cts +254 -0
- package/dist/plugins/webhook.d.ts +254 -0
- package/dist/plugins/webhook.js +789 -0
- package/dist/protect-D1v_0upK.d.cts +123 -0
- package/dist/protect-DsxV_-dJ.d.ts +123 -0
- package/dist/sveltekit.cjs +1258 -0
- package/dist/sveltekit.d.cts +420 -0
- package/dist/sveltekit.d.ts +420 -0
- package/dist/sveltekit.js +1207 -0
- package/dist/testing.cjs +4294 -0
- package/dist/testing.d.cts +197 -0
- package/dist/testing.d.ts +197 -0
- package/dist/testing.js +4264 -0
- package/dist/types-et0R8tsC.d.cts +217 -0
- package/dist/types-et0R8tsC.d.ts +217 -0
- package/docs/adapter-typed-helpers.md +192 -0
- package/docs/admin-recipes.md +227 -0
- package/docs/architecture.md +434 -0
- package/docs/ci/github-actions.yml +59 -0
- package/docs/ci.md +109 -0
- package/docs/compatibility.md +115 -0
- package/docs/deployment.md +305 -0
- package/docs/hardening.md +118 -0
- package/docs/host-owned-routes.md +154 -0
- package/docs/migrations/0001-schema-version.md +54 -0
- package/docs/migrations/0002-initial-schema.md +84 -0
- package/docs/migrations/upgrade-guide.md +160 -0
- package/docs/observability.md +394 -0
- package/docs/plugins/account-lockout.md +131 -0
- package/docs/plugins/admin.md +402 -0
- package/docs/plugins/api-key.md +327 -0
- package/docs/plugins/audit-log.md +261 -0
- package/docs/plugins/data-isolation.md +186 -0
- package/docs/plugins/email-verification.md +121 -0
- package/docs/plugins/magic-link.md +123 -0
- package/docs/plugins/oauth.md +544 -0
- package/docs/plugins/openapi.md +250 -0
- package/docs/plugins/rate-limit.md +223 -0
- package/docs/plugins/social-login.md +337 -0
- package/docs/plugins/tenancy.md +122 -0
- package/docs/plugins/two-factor.md +191 -0
- package/docs/plugins/webauthn.md +188 -0
- package/docs/plugins/webhook.md +242 -0
- package/docs/policy-as-code.md +156 -0
- package/docs/route-manifest.md +46 -0
- package/docs/security.md +261 -0
- package/docs/threat-model.md +161 -0
- package/examples/README.md +119 -0
- package/examples/express-app/index.ts +747 -0
- package/examples/hono-app/docker-compose.yml +14 -0
- package/examples/hono-app/index.ts +1009 -0
- package/examples/policy/fortress.policy.json +47 -0
- package/examples/sveltekit-app/README.md +125 -0
- package/examples/sveltekit-app/src/app.d.ts +16 -0
- package/examples/sveltekit-app/src/hooks.server.ts +23 -0
- package/examples/sveltekit-app/src/lib/server/fortress.ts +81 -0
- package/examples/sveltekit-app/src/routes/api/echo/[id]/+server.ts +32 -0
- package/examples/sveltekit-app/src/routes/api/fortress/[...path]/+server.ts +15 -0
- package/examples/sveltekit-app/src/routes/dashboard/+page.server.ts +20 -0
- package/examples/sveltekit-app/src/routes/login/+page.server.ts +48 -0
- package/examples/sveltekit-app/src/routes/oauth/consent/+page.server.ts +69 -0
- package/examples/sveltekit-app/src/routes/oauth/consent/+page.svelte +83 -0
- package/migrations/pg/0001_schema_version.down.sql +2 -0
- package/migrations/pg/0001_schema_version.sql +8 -0
- package/migrations/pg/0002_initial_schema.down.sql +36 -0
- package/migrations/pg/0002_initial_schema.sql +376 -0
- package/migrations/pg/0003_auth_continuation.down.sql +6 -0
- package/migrations/pg/0003_auth_continuation.sql +30 -0
- package/migrations/pg/0004_tenant_default_unique.down.sql +1 -0
- package/migrations/pg/0004_tenant_default_unique.sql +14 -0
- package/migrations/pg/0005_hot_indexes_timestamptz.down.sql +71 -0
- package/migrations/pg/0005_hot_indexes_timestamptz.sql +71 -0
- package/migrations/pg/0006_canonical_email.down.sql +3 -0
- package/migrations/pg/0006_canonical_email.sql +8 -0
- package/migrations/pg/0007_audit_chain_anchor.down.sql +1 -0
- package/migrations/pg/0007_audit_chain_anchor.sql +8 -0
- package/migrations/pg/0008_two_factor_hardening.down.sql +8 -0
- package/migrations/pg/0008_two_factor_hardening.sql +8 -0
- package/migrations/pg/0009_encrypt_totp_secrets.down.sql +2 -0
- package/migrations/pg/0009_encrypt_totp_secrets.sql +5 -0
- package/migrations/pg/0010_bigint_append_only_ids.down.sql +2 -0
- package/migrations/pg/0010_bigint_append_only_ids.sql +23 -0
- package/migrations/sqlite/0001_schema_version.down.sql +2 -0
- package/migrations/sqlite/0001_schema_version.sql +8 -0
- package/migrations/sqlite/0002_initial_schema.down.sql +36 -0
- package/migrations/sqlite/0002_initial_schema.sql +376 -0
- package/migrations/sqlite/0003_auth_continuation.down.sql +27 -0
- package/migrations/sqlite/0003_auth_continuation.sql +45 -0
- package/migrations/sqlite/0004_tenant_default_unique.down.sql +1 -0
- package/migrations/sqlite/0004_tenant_default_unique.sql +13 -0
- package/migrations/sqlite/0005_hot_indexes_timestamptz.down.sql +10 -0
- package/migrations/sqlite/0005_hot_indexes_timestamptz.sql +10 -0
- package/migrations/sqlite/0006_canonical_email.down.sql +3 -0
- package/migrations/sqlite/0006_canonical_email.sql +8 -0
- package/migrations/sqlite/0007_audit_chain_anchor.down.sql +1 -0
- package/migrations/sqlite/0007_audit_chain_anchor.sql +8 -0
- package/migrations/sqlite/0008_two_factor_hardening.down.sql +14 -0
- package/migrations/sqlite/0008_two_factor_hardening.sql +7 -0
- package/migrations/sqlite/0009_encrypt_totp_secrets.down.sql +2 -0
- package/migrations/sqlite/0009_encrypt_totp_secrets.sql +5 -0
- package/migrations/sqlite/0010_bigint_append_only_ids.down.sql +1 -0
- package/migrations/sqlite/0010_bigint_append_only_ids.sql +2 -0
- package/package.json +398 -0
- package/src/adapters/database/index.ts +63 -0
- package/src/adapters/database/types.ts +22 -0
- package/src/changelog-script.test.ts +21 -0
- package/src/cli.test.ts +79 -0
- package/src/core/auth/auth-admin.test.ts +247 -0
- package/src/core/auth/auth-endpoints.ts +558 -0
- package/src/core/auth/auth-observer.test.ts +191 -0
- package/src/core/auth/auth-service.ts +1464 -0
- package/src/core/auth/email.test.ts +17 -0
- package/src/core/auth/email.ts +11 -0
- package/src/core/auth/hooks.test.ts +251 -0
- package/src/core/auth/impersonation.test.ts +179 -0
- package/src/core/auth/jwt.test.ts +184 -0
- package/src/core/auth/jwt.ts +239 -0
- package/src/core/auth/login-timing.test.ts +77 -0
- package/src/core/auth/password-policy.test.ts +253 -0
- package/src/core/auth/password-policy.ts +220 -0
- package/src/core/auth/password.test.ts +42 -0
- package/src/core/auth/password.ts +62 -0
- package/src/core/auth/post-auth-gate.test.ts +258 -0
- package/src/core/auth/post-auth-gate.ts +228 -0
- package/src/core/auth/refresh-token.test.ts +86 -0
- package/src/core/auth/refresh-token.ts +105 -0
- package/src/core/auth/timing-safe.ts +23 -0
- package/src/core/config.ts +212 -0
- package/src/core/endpoint.ts +149 -0
- package/src/core/errors.ts +199 -0
- package/src/core/fetcher-interop.test.ts +106 -0
- package/src/core/fortress.test.ts +354 -0
- package/src/core/fortress.ts +550 -0
- package/src/core/http/call.test.ts +148 -0
- package/src/core/http/call.ts +149 -0
- package/src/core/http/cookie-serialize.test.ts +198 -0
- package/src/core/http/cookie-serialize.ts +107 -0
- package/src/core/http/csrf.test.ts +140 -0
- package/src/core/http/csrf.ts +173 -0
- package/src/core/http/dispatch.ts +652 -0
- package/src/core/http/error-response.test.ts +69 -0
- package/src/core/http/error-response.ts +93 -0
- package/src/core/http/fortress-rbac.test.ts +43 -0
- package/src/core/http/fortress-rbac.ts +127 -0
- package/src/core/http/handle-request.test.ts +441 -0
- package/src/core/http/handle-request.ts +354 -0
- package/src/core/http/match.test.ts +122 -0
- package/src/core/http/match.ts +126 -0
- package/src/core/http/outbound.test.ts +34 -0
- package/src/core/http/outbound.ts +105 -0
- package/src/core/http/plugin-middleware.ts +67 -0
- package/src/core/http/principal.test.ts +345 -0
- package/src/core/http/principal.ts +86 -0
- package/src/core/http/protect.test.ts +220 -0
- package/src/core/http/protect.ts +394 -0
- package/src/core/http/token-extraction.test.ts +59 -0
- package/src/core/http/token-extraction.ts +53 -0
- package/src/core/iam/explain.test.ts +177 -0
- package/src/core/iam/explain.ts +302 -0
- package/src/core/iam/iam-endpoints.ts +779 -0
- package/src/core/iam/iam-service.test.ts +829 -0
- package/src/core/iam/iam-service.ts +1137 -0
- package/src/core/iam/permission-cache.test.ts +115 -0
- package/src/core/iam/permission-cache.ts +71 -0
- package/src/core/iam/permission-check-observer.test.ts +90 -0
- package/src/core/iam/permission-evaluator.test.ts +291 -0
- package/src/core/iam/permission-evaluator.ts +198 -0
- package/src/core/iam/permission-sync.test.ts +166 -0
- package/src/core/iam/permission-sync.ts +192 -0
- package/src/core/iam/resource-sync.test.ts +70 -0
- package/src/core/iam/resource-sync.ts +182 -0
- package/src/core/iam/service-account-http.test.ts +529 -0
- package/src/core/iam/service-account.test.ts +282 -0
- package/src/core/internal-adapter.test.ts +389 -0
- package/src/core/internal-adapter.ts +435 -0
- package/src/core/json-schema.ts +50 -0
- package/src/core/manifest/__snapshots__/route-manifest.test.ts.snap +192 -0
- package/src/core/manifest/drift.ts +103 -0
- package/src/core/manifest/route-manifest.test.ts +142 -0
- package/src/core/manifest/route-manifest.ts +154 -0
- package/src/core/migrate.test.ts +72 -0
- package/src/core/migrations/engine.test.ts +308 -0
- package/src/core/migrations/engine.ts +548 -0
- package/src/core/migrations/migrations.ts +1771 -0
- package/src/core/migrations/pg-migration.integration-test.ts +353 -0
- package/src/core/migrations/upgrade-fixture.test.ts +378 -0
- package/src/core/observability/db-instrumentation.ts +205 -0
- package/src/core/observability/listener-list.test.ts +124 -0
- package/src/core/observability/listener-list.ts +73 -0
- package/src/core/observability/logger.test.ts +43 -0
- package/src/core/observability/logger.ts +49 -0
- package/src/core/observability/spans.test.ts +193 -0
- package/src/core/observability/types.ts +80 -0
- package/src/core/openapi-drift.test.ts +41 -0
- package/src/core/openapi.ts +47 -0
- package/src/core/plugin-methods-map.ts +75 -0
- package/src/core/plugin-runner.test.ts +233 -0
- package/src/core/plugin-runner.ts +276 -0
- package/src/core/plugin.ts +210 -0
- package/src/core/policy/apply.ts +272 -0
- package/src/core/policy/diff.ts +288 -0
- package/src/core/policy/loader.test.ts +63 -0
- package/src/core/policy/loader.ts +214 -0
- package/src/core/policy/policy.test.ts +232 -0
- package/src/core/policy/types.ts +105 -0
- package/src/core/schema-builder.test.ts +660 -0
- package/src/core/schema-builder.ts +881 -0
- package/src/core/standard-schema.ts +56 -0
- package/src/core/types.ts +258 -0
- package/src/core/validation.test.ts +195 -0
- package/src/core/validation.ts +192 -0
- package/src/drizzle/adapter.test.ts +425 -0
- package/src/drizzle/adapter.ts +474 -0
- package/src/drizzle/index.ts +31 -0
- package/src/drizzle/pg/adapter.integration-test.ts +456 -0
- package/src/drizzle/pg/index.ts +13 -0
- package/src/drizzle/pg/pg.integration-test.ts +1539 -0
- package/src/drizzle/pg/schema.ts +539 -0
- package/src/drizzle/pg-error-map.test.ts +218 -0
- package/src/drizzle/pg-error-map.ts +151 -0
- package/src/drizzle/schema.ts +544 -0
- package/src/drizzle/sqlite-serialization.test.ts +106 -0
- package/src/express/express-api-key-user-routes.test.ts +241 -0
- package/src/express/express.test.ts +442 -0
- package/src/express/handle.ts +191 -0
- package/src/express/index.ts +62 -0
- package/src/express/middleware.ts +551 -0
- package/src/express/protect.ts +154 -0
- package/src/express/validated.test.ts +86 -0
- package/src/express/validated.ts +99 -0
- package/src/fetcher/index.ts +81 -0
- package/src/hono/convert-routes.test.ts +220 -0
- package/src/hono/convert-routes.ts +196 -0
- package/src/hono/converters.test.ts +50 -0
- package/src/hono/converters.ts +43 -0
- package/src/hono/handle.ts +79 -0
- package/src/hono/helpers.ts +2 -0
- package/src/hono/hono-api-key-user-routes.test.ts +225 -0
- package/src/hono/hono-handlers.test.ts +861 -0
- package/src/hono/hono.test.ts +454 -0
- package/src/hono/index.ts +101 -0
- package/src/hono/middleware/auth.ts +236 -0
- package/src/hono/middleware/auth.types.test.ts +94 -0
- package/src/hono/middleware/csrf.test.ts +127 -0
- package/src/hono/middleware/csrf.ts +68 -0
- package/src/hono/middleware/error-handler.ts +12 -0
- package/src/hono/middleware/plugin-middleware.ts +35 -0
- package/src/hono/middleware/rbac.ts +131 -0
- package/src/hono/middleware/security-headers.test.ts +88 -0
- package/src/hono/middleware/security-headers.ts +68 -0
- package/src/hono/openapi.ts +237 -0
- package/src/hono/protect.ts +87 -0
- package/src/hono/validated.test.ts +123 -0
- package/src/hono/validated.ts +62 -0
- package/src/index.ts +309 -0
- package/src/integration.test.ts +718 -0
- package/src/internal/changelog.ts +56 -0
- package/src/otel/index.ts +158 -0
- package/src/otel/otel-types.test.ts +35 -0
- package/src/otel/otel.test.ts +235 -0
- package/src/plugins/account-lockout/account-lockout.test.ts +367 -0
- package/src/plugins/account-lockout/index.ts +267 -0
- package/src/plugins/admin/admin-api-key.test.ts +438 -0
- package/src/plugins/admin/index.ts +1612 -0
- package/src/plugins/api-key/api-key.test.ts +684 -0
- package/src/plugins/api-key/core.ts +336 -0
- package/src/plugins/api-key/index.ts +315 -0
- package/src/plugins/audit-log/audit-log.test.ts +749 -0
- package/src/plugins/audit-log/index.ts +680 -0
- package/src/plugins/data-isolation/data-isolation.test.ts +197 -0
- package/src/plugins/data-isolation/index.ts +157 -0
- package/src/plugins/email-verification/email-verification.test.ts +199 -0
- package/src/plugins/email-verification/index.ts +190 -0
- package/src/plugins/magic-link/index.ts +129 -0
- package/src/plugins/magic-link/magic-link.test.ts +156 -0
- package/src/plugins/oauth/id-token.ts +86 -0
- package/src/plugins/oauth/index.ts +2145 -0
- package/src/plugins/oauth/jwks.test.ts +32 -0
- package/src/plugins/oauth/jwks.ts +182 -0
- package/src/plugins/oauth/oauth.test.ts +2220 -0
- package/src/plugins/oauth/pkce.ts +58 -0
- package/src/plugins/openapi/index.ts +298 -0
- package/src/plugins/openapi/openapi.test.ts +425 -0
- package/src/plugins/openapi/spec-builder.ts +287 -0
- package/src/plugins/rate-limit/express.test.ts +89 -0
- package/src/plugins/rate-limit/express.ts +86 -0
- package/src/plugins/rate-limit/hono.test.ts +60 -0
- package/src/plugins/rate-limit/hono.ts +74 -0
- package/src/plugins/rate-limit/index.ts +383 -0
- package/src/plugins/rate-limit/memory-store.ts +61 -0
- package/src/plugins/rate-limit/rate-limit.test.ts +756 -0
- package/src/plugins/rate-limit/sveltekit.test.ts +58 -0
- package/src/plugins/rate-limit/sveltekit.ts +66 -0
- package/src/plugins/social-login/index.ts +715 -0
- package/src/plugins/social-login/providers/apple.ts +34 -0
- package/src/plugins/social-login/providers/discord.ts +26 -0
- package/src/plugins/social-login/providers/github.ts +54 -0
- package/src/plugins/social-login/providers/google.ts +23 -0
- package/src/plugins/social-login/providers/index.ts +22 -0
- package/src/plugins/social-login/providers/microsoft.ts +35 -0
- package/src/plugins/social-login/providers/oidc.ts +37 -0
- package/src/plugins/social-login/providers/providers.test.ts +211 -0
- package/src/plugins/social-login/social-login.test.ts +675 -0
- package/src/plugins/social-login/types.ts +80 -0
- package/src/plugins/tenancy/index.ts +544 -0
- package/src/plugins/tenancy/tenancy.test.ts +323 -0
- package/src/plugins/two-factor/index.ts +569 -0
- package/src/plugins/two-factor/two-factor.test.ts +235 -0
- package/src/plugins/webauthn/index.ts +554 -0
- package/src/plugins/webauthn/webauthn.test.ts +472 -0
- package/src/plugins/webhook/builtin-events.ts +29 -0
- package/src/plugins/webhook/delivery.test.ts +51 -0
- package/src/plugins/webhook/delivery.ts +154 -0
- package/src/plugins/webhook/hooks.ts +89 -0
- package/src/plugins/webhook/index.ts +445 -0
- package/src/plugins/webhook/queue/database.ts +67 -0
- package/src/plugins/webhook/queue/in-memory.test.ts +48 -0
- package/src/plugins/webhook/queue/in-memory.ts +71 -0
- package/src/plugins/webhook/queue/types.ts +51 -0
- package/src/plugins/webhook/registry.test.ts +48 -0
- package/src/plugins/webhook/registry.ts +64 -0
- package/src/plugins/webhook/signing.ts +45 -0
- package/src/plugins/webhook/ssrf.ts +198 -0
- package/src/plugins/webhook/types.ts +138 -0
- package/src/plugins/webhook/webhook.test.ts +324 -0
- package/src/sveltekit/actions.ts +233 -0
- package/src/sveltekit/catch-all.ts +43 -0
- package/src/sveltekit/cookies.ts +136 -0
- package/src/sveltekit/handle.ts +356 -0
- package/src/sveltekit/helpers.ts +69 -0
- package/src/sveltekit/index.ts +74 -0
- package/src/sveltekit/protect.ts +80 -0
- package/src/sveltekit/sveltekit-types.test.ts +31 -0
- package/src/sveltekit/sveltekit.test.ts +799 -0
- package/src/sveltekit/types.ts +134 -0
- package/src/sveltekit/validated.test.ts +117 -0
- package/src/sveltekit/validated.ts +78 -0
- package/src/testing/adapter-conformance.test.ts +408 -0
- package/src/testing/checks.test.ts +124 -0
- package/src/testing/checks.ts +304 -0
- package/src/testing/index.ts +107 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI-ready check utilities for Fortress projects (P1-10).
|
|
3
|
+
*
|
|
4
|
+
* Wraps the drift detectors and manifests Fortress already builds for
|
|
5
|
+
* itself so consumer apps can wire them into their own CI pipeline. The
|
|
6
|
+
* functions are framework-agnostic — they consume a `Fortress` instance
|
|
7
|
+
* and / or a `DatabaseAdapter` and return structured results suitable
|
|
8
|
+
* for `expect(...)`-style assertions or printing alongside a non-zero
|
|
9
|
+
* exit code.
|
|
10
|
+
*
|
|
11
|
+
* Re-exported from `@bajustone/fortress/testing` alongside the existing
|
|
12
|
+
* `createTestAdapter` helper.
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { DatabaseAdapter } from '../adapters/database';
|
|
18
|
+
import type { Fortress } from '../core/fortress';
|
|
19
|
+
import type { RouteManifestDrift } from '../core/manifest/drift';
|
|
20
|
+
import type { RouteClassification, RouteManifestEntry } from '../core/manifest/route-manifest';
|
|
21
|
+
import type { MigrationDrift } from '../core/migrations/engine';
|
|
22
|
+
import type { MigrationDialect } from '../core/migrations/migrations';
|
|
23
|
+
import { detectRouteManifestDrift, hasRouteManifestDrift } from '../core/manifest/drift';
|
|
24
|
+
import { buildRouteManifest } from '../core/manifest/route-manifest';
|
|
25
|
+
import { detectMigrationDrift, hasMigrationDrift } from '../core/migrations/engine';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Combined result returned by every `check*` helper.
|
|
29
|
+
*
|
|
30
|
+
* `ok: true` ⇒ the check passed cleanly; `messages` is informational
|
|
31
|
+
* only. `ok: false` ⇒ at least one finding requires attention; the
|
|
32
|
+
* messages describe what.
|
|
33
|
+
*/
|
|
34
|
+
export interface CheckResult {
|
|
35
|
+
ok: boolean;
|
|
36
|
+
messages: string[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ── Route manifest drift ────────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Run the route-manifest drift detector and return a {@link CheckResult}.
|
|
43
|
+
* Suitable for CI: a non-empty {@link RouteManifestDrift} flips
|
|
44
|
+
* `ok: false` and emits one message per drift category.
|
|
45
|
+
*/
|
|
46
|
+
export function checkRouteManifestDrift(
|
|
47
|
+
fortress: Fortress,
|
|
48
|
+
): CheckResult & { drift: RouteManifestDrift } {
|
|
49
|
+
const drift = detectRouteManifestDrift(fortress);
|
|
50
|
+
const messages: string[] = [];
|
|
51
|
+
if (drift.mountedMissingFromManifest.length > 0)
|
|
52
|
+
messages.push(`Mounted routes missing from manifest: ${drift.mountedMissingFromManifest.join(', ')}`);
|
|
53
|
+
if (drift.manifestMissingFromMounted.length > 0)
|
|
54
|
+
messages.push(`Manifest routes missing from mounted set: ${drift.manifestMissingFromMounted.join(', ')}`);
|
|
55
|
+
if (drift.rbacPermissionMismatches.length > 0) {
|
|
56
|
+
messages.push(
|
|
57
|
+
`RBAC permission mismatches:\n ${drift.rbacPermissionMismatches
|
|
58
|
+
.map(m => `${m.route}: expected=${m.expected ?? '(none)'} actual=${m.actual ?? '(none)'}`)
|
|
59
|
+
.join('\n ')}`,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
if (drift.openapiMissingFromManifest.length > 0)
|
|
63
|
+
messages.push(`Routes in OpenAPI but missing from manifest: ${drift.openapiMissingFromManifest.join(', ')}`);
|
|
64
|
+
if (drift.manifestMissingFromOpenapi.length > 0)
|
|
65
|
+
messages.push(`Routes in manifest but missing from OpenAPI: ${drift.manifestMissingFromOpenapi.join(', ')}`);
|
|
66
|
+
return { ok: !hasRouteManifestDrift(drift), drift, messages };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ── Public-route allow-list ─────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Options for {@link checkPublicRoutes}.
|
|
73
|
+
*/
|
|
74
|
+
export interface PublicRouteCheckOptions {
|
|
75
|
+
/**
|
|
76
|
+
* Explicit allow-list of `'<METHOD> <path>'` entries that are *allowed*
|
|
77
|
+
* to be `public` or `oauth-protocol`. Anything classified as `public`
|
|
78
|
+
* but **not** on this list is reported as a finding so a stray
|
|
79
|
+
* `.security('none')` on a sensitive route fails the build.
|
|
80
|
+
*
|
|
81
|
+
* Defaults to Fortress's own intentional public surface (auth open
|
|
82
|
+
* routes + OAuth protocol endpoints) so consumers only need to add
|
|
83
|
+
* their own public routes.
|
|
84
|
+
*/
|
|
85
|
+
allow?: string[];
|
|
86
|
+
/**
|
|
87
|
+
* Additional classifications considered "public-equivalent" for the
|
|
88
|
+
* purposes of the allow-list. Defaults to `['public', 'oauth-protocol']`
|
|
89
|
+
* because OAuth protocol endpoints are reachable without a Fortress
|
|
90
|
+
* session by design (the handler self-authenticates).
|
|
91
|
+
*/
|
|
92
|
+
classifications?: RouteClassification[];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const DEFAULT_PUBLIC_ALLOW: readonly string[] = [
|
|
96
|
+
'POST /auth/login',
|
|
97
|
+
'POST /auth/register',
|
|
98
|
+
'POST /auth/refresh',
|
|
99
|
+
'POST /auth/logout',
|
|
100
|
+
'POST /auth/2fa/verify',
|
|
101
|
+
'POST /auth/magic-link/verify',
|
|
102
|
+
'GET /oauth/authorize',
|
|
103
|
+
'POST /oauth/token',
|
|
104
|
+
'POST /oauth/introspect',
|
|
105
|
+
'POST /oauth/revoke',
|
|
106
|
+
'GET /oauth/userinfo',
|
|
107
|
+
'GET /oauth/.well-known/openid-configuration',
|
|
108
|
+
'GET /oauth/.well-known/jwks.json',
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Assert that no Fortress-managed route is unintentionally public.
|
|
113
|
+
*
|
|
114
|
+
* Walks the route manifest, collects every entry classified as `public`
|
|
115
|
+
* or `oauth-protocol` (configurable), and reports any entry not present
|
|
116
|
+
* in the allow-list. Defaults cover Fortress's own intentional public
|
|
117
|
+
* surface so consumers only need to add their own.
|
|
118
|
+
*/
|
|
119
|
+
export function checkPublicRoutes(
|
|
120
|
+
fortress: Fortress,
|
|
121
|
+
options: PublicRouteCheckOptions = {},
|
|
122
|
+
): CheckResult & { unexpected: RouteManifestEntry[] } {
|
|
123
|
+
const allow = new Set([...(options.allow ?? []), ...DEFAULT_PUBLIC_ALLOW]);
|
|
124
|
+
const classifications = new Set<RouteClassification>(
|
|
125
|
+
options.classifications ?? ['public', 'oauth-protocol'],
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
const manifest = buildRouteManifest(fortress);
|
|
129
|
+
const unexpected = manifest.filter(
|
|
130
|
+
entry => classifications.has(entry.classification)
|
|
131
|
+
&& !allow.has(`${entry.method} ${entry.path}`),
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const messages = unexpected.map(
|
|
135
|
+
entry => `Unexpected ${entry.classification} route: ${entry.method} ${entry.path} (plugin=${entry.plugin ?? 'core'})`,
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
return { ok: unexpected.length === 0, unexpected, messages };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ── Migration drift ─────────────────────────────────────────────────
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Run the migration drift detector and return a {@link CheckResult}.
|
|
145
|
+
* Reports missing version table, pending migrations, an unknown
|
|
146
|
+
* future version (DB ahead of the bundled catalog), missing
|
|
147
|
+
* Fortress-owned tables, and present-but-stale tables missing columns.
|
|
148
|
+
*/
|
|
149
|
+
export async function checkMigrationDrift(
|
|
150
|
+
db: DatabaseAdapter,
|
|
151
|
+
dialect?: MigrationDialect,
|
|
152
|
+
): Promise<CheckResult & { drift: MigrationDrift }> {
|
|
153
|
+
const drift = await detectMigrationDrift(db, dialect);
|
|
154
|
+
const messages: string[] = [];
|
|
155
|
+
if (drift.missingVersionTable)
|
|
156
|
+
messages.push('Schema version table is missing — run fortress.migrate()');
|
|
157
|
+
if (drift.pendingVersions.length > 0)
|
|
158
|
+
messages.push(`Pending migrations: ${drift.pendingVersions.join(', ')}`);
|
|
159
|
+
if (drift.unknownFutureVersion)
|
|
160
|
+
messages.push(`Database is at version ${drift.currentVersion} but bundled catalog stops at ${drift.latestVersion}`);
|
|
161
|
+
if (drift.missingTables.length > 0)
|
|
162
|
+
messages.push(`Missing Fortress tables: ${drift.missingTables.join(', ')}`);
|
|
163
|
+
if (drift.missingColumns.length > 0) {
|
|
164
|
+
messages.push(
|
|
165
|
+
`Stale Fortress tables missing columns: ${drift.missingColumns
|
|
166
|
+
.map(entry => `${entry.table} (${entry.columns.join(', ')})`)
|
|
167
|
+
.join('; ')}`,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
return { ok: !hasMigrationDrift(drift), drift, messages };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ── Auth smoke-test helper ──────────────────────────────────────────
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Inputs to {@link smokeTestAuth}.
|
|
177
|
+
*/
|
|
178
|
+
export interface AuthSmokeTestOptions {
|
|
179
|
+
/** Email used for the register/login round-trip. */
|
|
180
|
+
email?: string;
|
|
181
|
+
/** Password to register the smoke-test user with. */
|
|
182
|
+
password?: string;
|
|
183
|
+
/** Optional display name. */
|
|
184
|
+
name?: string;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* End-to-end auth smoke test: create user → login → verify access token → refresh → logout.
|
|
189
|
+
* Designed to run against a fortress instance backed by
|
|
190
|
+
* {@link createTestAdapter} or any disposable DB. Returns a
|
|
191
|
+
* {@link CheckResult} so it composes with the other checks for a single
|
|
192
|
+
* CI gate.
|
|
193
|
+
*
|
|
194
|
+
* Calls the direct `fortress.auth.*` service methods so auth persistence,
|
|
195
|
+
* hashing, token verification, refresh rotation, and revocation run without
|
|
196
|
+
* a network roundtrip.
|
|
197
|
+
*/
|
|
198
|
+
export async function smokeTestAuth(
|
|
199
|
+
fortress: Fortress,
|
|
200
|
+
options: AuthSmokeTestOptions = {},
|
|
201
|
+
): Promise<CheckResult> {
|
|
202
|
+
const email = options.email ?? `smoke+${Date.now()}@fortress.test`;
|
|
203
|
+
const password = options.password ?? 'smoke-test-password-1234564!';
|
|
204
|
+
const name = options.name ?? 'Smoke Test';
|
|
205
|
+
const messages: string[] = [];
|
|
206
|
+
try {
|
|
207
|
+
await fortress.auth.createUser({ email, password, name });
|
|
208
|
+
const login = await fortress.auth.login(email, password);
|
|
209
|
+
if (login.status !== 'success' || !login.accessToken)
|
|
210
|
+
throw new Error(`login returned status=${login.status}`);
|
|
211
|
+
const claims = await fortress.auth.verifyToken(login.accessToken);
|
|
212
|
+
if (claims.sub !== login.user.id)
|
|
213
|
+
throw new Error(`token sub mismatch: ${claims.sub} vs user.id ${login.user.id}`);
|
|
214
|
+
if (!login.refreshToken)
|
|
215
|
+
throw new Error('login did not return a refresh token');
|
|
216
|
+
const refreshed = await fortress.auth.refresh(login.refreshToken);
|
|
217
|
+
if (!refreshed.accessToken)
|
|
218
|
+
throw new Error('refresh did not return a new access token');
|
|
219
|
+
await fortress.auth.logout(refreshed.refreshToken);
|
|
220
|
+
return { ok: true, messages };
|
|
221
|
+
}
|
|
222
|
+
catch (err) {
|
|
223
|
+
messages.push(`Auth smoke test failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
224
|
+
return { ok: false, messages };
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// ── Convenience aggregator ──────────────────────────────────────────
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Inputs to {@link runFortressChecks}.
|
|
232
|
+
*/
|
|
233
|
+
export interface RunFortressChecksOptions {
|
|
234
|
+
fortress: Fortress;
|
|
235
|
+
/** Optional DB adapter for the migration check. Defaults to `fortress.config.database`. */
|
|
236
|
+
db?: DatabaseAdapter;
|
|
237
|
+
/** Skip the migration drift check (e.g. if you bring your own migration tool). */
|
|
238
|
+
skipMigrations?: boolean;
|
|
239
|
+
/** Skip the auth smoke test (e.g. in environments without a writable DB). */
|
|
240
|
+
skipAuthSmokeTest?: boolean;
|
|
241
|
+
/** Public-route allow-list overrides. */
|
|
242
|
+
publicRoutes?: PublicRouteCheckOptions;
|
|
243
|
+
/** Smoke-test overrides. */
|
|
244
|
+
smokeTest?: AuthSmokeTestOptions;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Aggregate result returned by {@link runFortressChecks}.
|
|
249
|
+
*/
|
|
250
|
+
export interface FortressChecksResult {
|
|
251
|
+
ok: boolean;
|
|
252
|
+
manifest: ReturnType<typeof checkRouteManifestDrift>;
|
|
253
|
+
publicRoutes: ReturnType<typeof checkPublicRoutes>;
|
|
254
|
+
migrations?: Awaited<ReturnType<typeof checkMigrationDrift>>;
|
|
255
|
+
authSmokeTest?: Awaited<ReturnType<typeof smokeTestAuth>>;
|
|
256
|
+
/** All non-ok messages flattened in run order. */
|
|
257
|
+
messages: string[];
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Run every CI check Fortress ships in one shot. Suitable for a single
|
|
262
|
+
* `fortress check` step in CI:
|
|
263
|
+
*
|
|
264
|
+
* ```ts
|
|
265
|
+
* import { runFortressChecks } from '@bajustone/fortress/testing';
|
|
266
|
+
*
|
|
267
|
+
* const result = await runFortressChecks({ fortress });
|
|
268
|
+
* if (!result.ok) {
|
|
269
|
+
* console.error(result.messages.join('\\n'));
|
|
270
|
+
* process.exit(1);
|
|
271
|
+
* }
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
274
|
+
export async function runFortressChecks(
|
|
275
|
+
options: RunFortressChecksOptions,
|
|
276
|
+
): Promise<FortressChecksResult> {
|
|
277
|
+
const manifest = checkRouteManifestDrift(options.fortress);
|
|
278
|
+
const publicRoutes = checkPublicRoutes(options.fortress, options.publicRoutes);
|
|
279
|
+
const migrations = options.skipMigrations
|
|
280
|
+
? undefined
|
|
281
|
+
: await checkMigrationDrift(options.db ?? options.fortress.config.database);
|
|
282
|
+
const authSmokeTest = options.skipAuthSmokeTest
|
|
283
|
+
? undefined
|
|
284
|
+
: await smokeTestAuth(options.fortress, options.smokeTest);
|
|
285
|
+
|
|
286
|
+
const messages: string[] = [];
|
|
287
|
+
if (!manifest.ok)
|
|
288
|
+
messages.push(...manifest.messages.map(m => `[manifest] ${m}`));
|
|
289
|
+
if (!publicRoutes.ok)
|
|
290
|
+
messages.push(...publicRoutes.messages.map(m => `[public-routes] ${m}`));
|
|
291
|
+
if (migrations && !migrations.ok)
|
|
292
|
+
messages.push(...migrations.messages.map(m => `[migrations] ${m}`));
|
|
293
|
+
if (authSmokeTest && !authSmokeTest.ok)
|
|
294
|
+
messages.push(...authSmokeTest.messages.map(m => `[auth-smoke] ${m}`));
|
|
295
|
+
|
|
296
|
+
return {
|
|
297
|
+
ok: manifest.ok && publicRoutes.ok && (migrations?.ok ?? true) && (authSmokeTest?.ok ?? true),
|
|
298
|
+
manifest,
|
|
299
|
+
publicRoutes,
|
|
300
|
+
...(migrations ? { migrations } : {}),
|
|
301
|
+
...(authSmokeTest ? { authSmokeTest } : {}),
|
|
302
|
+
messages,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory SQLite test adapter for fortress.
|
|
3
|
+
*
|
|
4
|
+
* Spins up a fresh `bun:sqlite` database with the full fortress schema and
|
|
5
|
+
* returns a {@link DatabaseAdapter} ready to pass into `createFortress` from
|
|
6
|
+
* unit tests. Designed for fast, isolated test runs — every call creates a
|
|
7
|
+
* new database, so tests cannot leak state into each other.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { createTestAdapter } from '@bajustone/fortress/testing';
|
|
12
|
+
*
|
|
13
|
+
* const db = createTestAdapter();
|
|
14
|
+
* const fortress = createFortress({ database: db, jwt: { key: 'test' } });
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import type { DatabaseAdapter } from '../adapters/database';
|
|
21
|
+
import { createRequire } from 'node:module';
|
|
22
|
+
import { resolve } from 'node:path';
|
|
23
|
+
import { getLatestMigrationVersion, getMigrationUpSql } from '../core/migrations/migrations';
|
|
24
|
+
import { createDrizzleAdapter } from '../drizzle/adapter';
|
|
25
|
+
|
|
26
|
+
export {
|
|
27
|
+
checkMigrationDrift,
|
|
28
|
+
checkPublicRoutes,
|
|
29
|
+
checkRouteManifestDrift,
|
|
30
|
+
runFortressChecks,
|
|
31
|
+
smokeTestAuth,
|
|
32
|
+
} from './checks';
|
|
33
|
+
export type {
|
|
34
|
+
AuthSmokeTestOptions,
|
|
35
|
+
CheckResult,
|
|
36
|
+
FortressChecksResult,
|
|
37
|
+
PublicRouteCheckOptions,
|
|
38
|
+
RunFortressChecksOptions,
|
|
39
|
+
} from './checks';
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Full SQLite schema for the test adapter, derived from the bundled
|
|
43
|
+
* migrations so the in-memory test database and a production `migrateUp`
|
|
44
|
+
* can never diverge. The migrations are the SQL-first source of truth (see
|
|
45
|
+
* `src/core/migrations/migrations.ts`); this just concatenates their
|
|
46
|
+
* forward SQL.
|
|
47
|
+
*/
|
|
48
|
+
const CREATE_TABLES_SQL = getMigrationUpSql('sqlite');
|
|
49
|
+
const STAMP_SCHEMA_VERSION_SQL = `
|
|
50
|
+
INSERT INTO fortress_schema_version (id, version, applied_at)
|
|
51
|
+
VALUES (1, ${getLatestMigrationVersion('sqlite')}, unixepoch())
|
|
52
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
53
|
+
version = excluded.version,
|
|
54
|
+
applied_at = excluded.applied_at;
|
|
55
|
+
`;
|
|
56
|
+
|
|
57
|
+
const isBun = typeof (globalThis as Record<string, unknown>).Bun !== 'undefined';
|
|
58
|
+
const runtimeRequire = (globalThis as typeof globalThis & { require?: NodeRequire }).require
|
|
59
|
+
?? createRequire(resolve(process.argv[1] ?? '__fortress_testing__.mjs'));
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Create a test DatabaseAdapter using in-memory SQLite.
|
|
63
|
+
* Automatically detects the runtime:
|
|
64
|
+
* - Bun: uses bun:sqlite
|
|
65
|
+
* - Node/Vitest: uses better-sqlite3
|
|
66
|
+
*
|
|
67
|
+
* Usage:
|
|
68
|
+
* import { createTestAdapter } from '@bajustone/fortress/testing';
|
|
69
|
+
* const fortress = createFortress({ database: createTestAdapter(), jwt: { key: 'test' } });
|
|
70
|
+
*/
|
|
71
|
+
export function createTestAdapter(): DatabaseAdapter {
|
|
72
|
+
if (isBun) {
|
|
73
|
+
return createBunAdapter();
|
|
74
|
+
}
|
|
75
|
+
return createNodeAdapter();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function createBunAdapter(): DatabaseAdapter {
|
|
79
|
+
// Dynamic import to avoid loading bun:sqlite in Node
|
|
80
|
+
const { Database } = runtimeRequire('bun:sqlite');
|
|
81
|
+
const { drizzle } = runtimeRequire('drizzle-orm/bun-sqlite');
|
|
82
|
+
|
|
83
|
+
const sqlite = new Database(':memory:');
|
|
84
|
+
sqlite.exec('PRAGMA journal_mode = WAL;');
|
|
85
|
+
sqlite.exec('PRAGMA foreign_keys = ON;');
|
|
86
|
+
sqlite.exec(CREATE_TABLES_SQL);
|
|
87
|
+
sqlite.exec(STAMP_SCHEMA_VERSION_SQL);
|
|
88
|
+
|
|
89
|
+
const db = drizzle(sqlite);
|
|
90
|
+
return createDrizzleAdapter(db);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function createNodeAdapter(): DatabaseAdapter {
|
|
94
|
+
// runtimeRequire works in both package formats while keeping the native
|
|
95
|
+
// dependency lazy and out of Bun's runtime branch.
|
|
96
|
+
const BetterSqlite3 = runtimeRequire('better-sqlite3');
|
|
97
|
+
const { drizzle } = runtimeRequire('drizzle-orm/better-sqlite3');
|
|
98
|
+
|
|
99
|
+
const sqlite = new BetterSqlite3(':memory:');
|
|
100
|
+
sqlite.pragma('journal_mode = WAL');
|
|
101
|
+
sqlite.pragma('foreign_keys = ON');
|
|
102
|
+
sqlite.exec(CREATE_TABLES_SQL);
|
|
103
|
+
sqlite.exec(STAMP_SCHEMA_VERSION_SQL);
|
|
104
|
+
|
|
105
|
+
const db = drizzle(sqlite);
|
|
106
|
+
return createDrizzleAdapter(db);
|
|
107
|
+
}
|