@fuzdev/fuz_app 0.1.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/LICENSE +21 -0
- package/README.md +49 -0
- package/dist/actions/action_bridge.d.ts +65 -0
- package/dist/actions/action_bridge.d.ts.map +1 -0
- package/dist/actions/action_bridge.js +76 -0
- package/dist/actions/action_codegen.d.ts +97 -0
- package/dist/actions/action_codegen.d.ts.map +1 -0
- package/dist/actions/action_codegen.js +280 -0
- package/dist/actions/action_registry.d.ts +35 -0
- package/dist/actions/action_registry.d.ts.map +1 -0
- package/dist/actions/action_registry.js +83 -0
- package/dist/actions/action_spec.d.ts +169 -0
- package/dist/actions/action_spec.d.ts.map +1 -0
- package/dist/actions/action_spec.js +76 -0
- package/dist/auth/account_queries.d.ts +96 -0
- package/dist/auth/account_queries.d.ts.map +1 -0
- package/dist/auth/account_queries.js +172 -0
- package/dist/auth/account_routes.d.ts +86 -0
- package/dist/auth/account_routes.d.ts.map +1 -0
- package/dist/auth/account_routes.js +406 -0
- package/dist/auth/account_schema.d.ts +192 -0
- package/dist/auth/account_schema.d.ts.map +1 -0
- package/dist/auth/account_schema.js +105 -0
- package/dist/auth/admin_routes.d.ts +29 -0
- package/dist/auth/admin_routes.d.ts.map +1 -0
- package/dist/auth/admin_routes.js +193 -0
- package/dist/auth/api_token.d.ts +33 -0
- package/dist/auth/api_token.d.ts.map +1 -0
- package/dist/auth/api_token.js +36 -0
- package/dist/auth/api_token_queries.d.ts +80 -0
- package/dist/auth/api_token_queries.d.ts.map +1 -0
- package/dist/auth/api_token_queries.js +116 -0
- package/dist/auth/app_settings_queries.d.ts +33 -0
- package/dist/auth/app_settings_queries.d.ts.map +1 -0
- package/dist/auth/app_settings_queries.js +51 -0
- package/dist/auth/app_settings_routes.d.ts +27 -0
- package/dist/auth/app_settings_routes.d.ts.map +1 -0
- package/dist/auth/app_settings_routes.js +66 -0
- package/dist/auth/app_settings_schema.d.ts +35 -0
- package/dist/auth/app_settings_schema.d.ts.map +1 -0
- package/dist/auth/app_settings_schema.js +22 -0
- package/dist/auth/audit_log_queries.d.ts +90 -0
- package/dist/auth/audit_log_queries.d.ts.map +1 -0
- package/dist/auth/audit_log_queries.js +205 -0
- package/dist/auth/audit_log_routes.d.ts +33 -0
- package/dist/auth/audit_log_routes.d.ts.map +1 -0
- package/dist/auth/audit_log_routes.js +106 -0
- package/dist/auth/audit_log_schema.d.ts +259 -0
- package/dist/auth/audit_log_schema.d.ts.map +1 -0
- package/dist/auth/audit_log_schema.js +123 -0
- package/dist/auth/bearer_auth.d.ts +32 -0
- package/dist/auth/bearer_auth.d.ts.map +1 -0
- package/dist/auth/bearer_auth.js +90 -0
- package/dist/auth/bootstrap_account.d.ts +82 -0
- package/dist/auth/bootstrap_account.d.ts.map +1 -0
- package/dist/auth/bootstrap_account.js +97 -0
- package/dist/auth/bootstrap_routes.d.ts +74 -0
- package/dist/auth/bootstrap_routes.d.ts.map +1 -0
- package/dist/auth/bootstrap_routes.js +154 -0
- package/dist/auth/daemon_token.d.ts +49 -0
- package/dist/auth/daemon_token.d.ts.map +1 -0
- package/dist/auth/daemon_token.js +49 -0
- package/dist/auth/daemon_token_middleware.d.ts +93 -0
- package/dist/auth/daemon_token_middleware.d.ts.map +1 -0
- package/dist/auth/daemon_token_middleware.js +167 -0
- package/dist/auth/ddl.d.ts +27 -0
- package/dist/auth/ddl.d.ts.map +1 -0
- package/dist/auth/ddl.js +111 -0
- package/dist/auth/deps.d.ts +52 -0
- package/dist/auth/deps.d.ts.map +1 -0
- package/dist/auth/deps.js +10 -0
- package/dist/auth/invite_queries.d.ts +68 -0
- package/dist/auth/invite_queries.d.ts.map +1 -0
- package/dist/auth/invite_queries.js +105 -0
- package/dist/auth/invite_routes.d.ts +18 -0
- package/dist/auth/invite_routes.d.ts.map +1 -0
- package/dist/auth/invite_routes.js +129 -0
- package/dist/auth/invite_schema.d.ts +51 -0
- package/dist/auth/invite_schema.d.ts.map +1 -0
- package/dist/auth/invite_schema.js +25 -0
- package/dist/auth/keyring.d.ts +87 -0
- package/dist/auth/keyring.d.ts.map +1 -0
- package/dist/auth/keyring.js +142 -0
- package/dist/auth/middleware.d.ts +40 -0
- package/dist/auth/middleware.d.ts.map +1 -0
- package/dist/auth/middleware.js +64 -0
- package/dist/auth/migrations.d.ts +42 -0
- package/dist/auth/migrations.d.ts.map +1 -0
- package/dist/auth/migrations.js +79 -0
- package/dist/auth/password.d.ts +39 -0
- package/dist/auth/password.d.ts.map +1 -0
- package/dist/auth/password.js +25 -0
- package/dist/auth/password_argon2.d.ts +43 -0
- package/dist/auth/password_argon2.d.ts.map +1 -0
- package/dist/auth/password_argon2.js +76 -0
- package/dist/auth/permit_queries.d.ts +72 -0
- package/dist/auth/permit_queries.d.ts.map +1 -0
- package/dist/auth/permit_queries.js +116 -0
- package/dist/auth/request_context.d.ts +114 -0
- package/dist/auth/request_context.d.ts.map +1 -0
- package/dist/auth/request_context.js +176 -0
- package/dist/auth/require_keeper.d.ts +20 -0
- package/dist/auth/require_keeper.d.ts.map +1 -0
- package/dist/auth/require_keeper.js +35 -0
- package/dist/auth/role_schema.d.ts +69 -0
- package/dist/auth/role_schema.d.ts.map +1 -0
- package/dist/auth/role_schema.js +70 -0
- package/dist/auth/route_guards.d.ts +21 -0
- package/dist/auth/route_guards.d.ts.map +1 -0
- package/dist/auth/route_guards.js +32 -0
- package/dist/auth/session_cookie.d.ts +158 -0
- package/dist/auth/session_cookie.d.ts.map +1 -0
- package/dist/auth/session_cookie.js +135 -0
- package/dist/auth/session_lifecycle.d.ts +35 -0
- package/dist/auth/session_lifecycle.d.ts.map +1 -0
- package/dist/auth/session_lifecycle.js +27 -0
- package/dist/auth/session_middleware.d.ts +33 -0
- package/dist/auth/session_middleware.d.ts.map +1 -0
- package/dist/auth/session_middleware.js +62 -0
- package/dist/auth/session_queries.d.ts +135 -0
- package/dist/auth/session_queries.d.ts.map +1 -0
- package/dist/auth/session_queries.js +186 -0
- package/dist/auth/signup_routes.d.ts +32 -0
- package/dist/auth/signup_routes.d.ts.map +1 -0
- package/dist/auth/signup_routes.js +150 -0
- package/dist/cli/args.d.ts +48 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +76 -0
- package/dist/cli/config.d.ts +48 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +77 -0
- package/dist/cli/daemon.d.ts +82 -0
- package/dist/cli/daemon.d.ts.map +1 -0
- package/dist/cli/daemon.js +149 -0
- package/dist/cli/help.d.ts +85 -0
- package/dist/cli/help.d.ts.map +1 -0
- package/dist/cli/help.js +138 -0
- package/dist/cli/logger.d.ts +46 -0
- package/dist/cli/logger.d.ts.map +1 -0
- package/dist/cli/logger.js +48 -0
- package/dist/cli/util.d.ts +36 -0
- package/dist/cli/util.d.ts.map +1 -0
- package/dist/cli/util.js +50 -0
- package/dist/crypto.d.ts +13 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +19 -0
- package/dist/db/assert_row.d.ts +18 -0
- package/dist/db/assert_row.d.ts.map +1 -0
- package/dist/db/assert_row.js +24 -0
- package/dist/db/create_db.d.ts +38 -0
- package/dist/db/create_db.d.ts.map +1 -0
- package/dist/db/create_db.js +57 -0
- package/dist/db/db.d.ts +97 -0
- package/dist/db/db.d.ts.map +1 -0
- package/dist/db/db.js +76 -0
- package/dist/db/db_pg.d.ts +21 -0
- package/dist/db/db_pg.d.ts.map +1 -0
- package/dist/db/db_pg.js +45 -0
- package/dist/db/db_pglite.d.ts +21 -0
- package/dist/db/db_pglite.d.ts.map +1 -0
- package/dist/db/db_pglite.js +28 -0
- package/dist/db/migrate.d.ts +67 -0
- package/dist/db/migrate.d.ts.map +1 -0
- package/dist/db/migrate.js +118 -0
- package/dist/db/pg_error.d.ts +16 -0
- package/dist/db/pg_error.d.ts.map +1 -0
- package/dist/db/pg_error.js +15 -0
- package/dist/db/query_deps.d.ts +14 -0
- package/dist/db/query_deps.d.ts.map +1 -0
- package/dist/db/query_deps.js +9 -0
- package/dist/db/sql_identifier.d.ts +27 -0
- package/dist/db/sql_identifier.d.ts.map +1 -0
- package/dist/db/sql_identifier.js +31 -0
- package/dist/db/status.d.ts +62 -0
- package/dist/db/status.d.ts.map +1 -0
- package/dist/db/status.js +116 -0
- package/dist/dev/setup.d.ts +159 -0
- package/dist/dev/setup.d.ts.map +1 -0
- package/dist/dev/setup.js +265 -0
- package/dist/env/dotenv.d.ts +25 -0
- package/dist/env/dotenv.d.ts.map +1 -0
- package/dist/env/dotenv.js +52 -0
- package/dist/env/load.d.ts +52 -0
- package/dist/env/load.d.ts.map +1 -0
- package/dist/env/load.js +79 -0
- package/dist/env/mask.d.ts +19 -0
- package/dist/env/mask.d.ts.map +1 -0
- package/dist/env/mask.js +26 -0
- package/dist/env/resolve.d.ts +126 -0
- package/dist/env/resolve.d.ts.map +1 -0
- package/dist/env/resolve.js +200 -0
- package/dist/hono_context.d.ts +48 -0
- package/dist/hono_context.d.ts.map +1 -0
- package/dist/hono_context.js +22 -0
- package/dist/http/common_routes.d.ts +52 -0
- package/dist/http/common_routes.d.ts.map +1 -0
- package/dist/http/common_routes.js +65 -0
- package/dist/http/db_routes.d.ts +57 -0
- package/dist/http/db_routes.d.ts.map +1 -0
- package/dist/http/db_routes.js +176 -0
- package/dist/http/error_schemas.d.ts +169 -0
- package/dist/http/error_schemas.d.ts.map +1 -0
- package/dist/http/error_schemas.js +178 -0
- package/dist/http/middleware_spec.d.ts +19 -0
- package/dist/http/middleware_spec.d.ts.map +1 -0
- package/dist/http/middleware_spec.js +9 -0
- package/dist/http/origin.d.ts +57 -0
- package/dist/http/origin.d.ts.map +1 -0
- package/dist/http/origin.js +207 -0
- package/dist/http/proxy.d.ts +112 -0
- package/dist/http/proxy.d.ts.map +1 -0
- package/dist/http/proxy.js +240 -0
- package/dist/http/route_spec.d.ts +197 -0
- package/dist/http/route_spec.d.ts.map +1 -0
- package/dist/http/route_spec.js +243 -0
- package/dist/http/schema_helpers.d.ts +64 -0
- package/dist/http/schema_helpers.d.ts.map +1 -0
- package/dist/http/schema_helpers.js +90 -0
- package/dist/http/surface.d.ts +132 -0
- package/dist/http/surface.d.ts.map +1 -0
- package/dist/http/surface.js +156 -0
- package/dist/http/surface_query.d.ts +77 -0
- package/dist/http/surface_query.d.ts.map +1 -0
- package/dist/http/surface_query.js +86 -0
- package/dist/rate_limiter.d.ts +94 -0
- package/dist/rate_limiter.d.ts.map +1 -0
- package/dist/rate_limiter.js +156 -0
- package/dist/realtime/sse.d.ts +80 -0
- package/dist/realtime/sse.d.ts.map +1 -0
- package/dist/realtime/sse.js +109 -0
- package/dist/realtime/sse_auth_guard.d.ts +93 -0
- package/dist/realtime/sse_auth_guard.d.ts.map +1 -0
- package/dist/realtime/sse_auth_guard.js +111 -0
- package/dist/realtime/subscriber_registry.d.ts +85 -0
- package/dist/realtime/subscriber_registry.d.ts.map +1 -0
- package/dist/realtime/subscriber_registry.js +108 -0
- package/dist/runtime/deno.d.ts +21 -0
- package/dist/runtime/deno.d.ts.map +1 -0
- package/dist/runtime/deno.js +83 -0
- package/dist/runtime/deps.d.ts +113 -0
- package/dist/runtime/deps.d.ts.map +1 -0
- package/dist/runtime/deps.js +10 -0
- package/dist/runtime/fs.d.ts +15 -0
- package/dist/runtime/fs.d.ts.map +1 -0
- package/dist/runtime/fs.js +17 -0
- package/dist/runtime/mock.d.ts +81 -0
- package/dist/runtime/mock.d.ts.map +1 -0
- package/dist/runtime/mock.js +195 -0
- package/dist/runtime/node.d.ts +17 -0
- package/dist/runtime/node.d.ts.map +1 -0
- package/dist/runtime/node.js +117 -0
- package/dist/schema_meta.d.ts +16 -0
- package/dist/schema_meta.d.ts.map +1 -0
- package/dist/schema_meta.js +9 -0
- package/dist/sensitivity.d.ts +15 -0
- package/dist/sensitivity.d.ts.map +1 -0
- package/dist/sensitivity.js +9 -0
- package/dist/server/app_backend.d.ts +74 -0
- package/dist/server/app_backend.d.ts.map +1 -0
- package/dist/server/app_backend.js +39 -0
- package/dist/server/app_server.d.ts +201 -0
- package/dist/server/app_server.d.ts.map +1 -0
- package/dist/server/app_server.js +266 -0
- package/dist/server/env.d.ts +68 -0
- package/dist/server/env.d.ts.map +1 -0
- package/dist/server/env.js +95 -0
- package/dist/server/startup.d.ts +22 -0
- package/dist/server/startup.d.ts.map +1 -0
- package/dist/server/startup.js +48 -0
- package/dist/server/static.d.ts +39 -0
- package/dist/server/static.d.ts.map +1 -0
- package/dist/server/static.js +38 -0
- package/dist/server/validate_nginx.d.ts +34 -0
- package/dist/server/validate_nginx.d.ts.map +1 -0
- package/dist/server/validate_nginx.js +118 -0
- package/dist/testing/CLAUDE.md +3 -0
- package/dist/testing/admin_integration.d.ts +45 -0
- package/dist/testing/admin_integration.d.ts.map +1 -0
- package/dist/testing/admin_integration.js +840 -0
- package/dist/testing/adversarial_404.d.ts +15 -0
- package/dist/testing/adversarial_404.d.ts.map +1 -0
- package/dist/testing/adversarial_404.js +118 -0
- package/dist/testing/adversarial_headers.d.ts +36 -0
- package/dist/testing/adversarial_headers.d.ts.map +1 -0
- package/dist/testing/adversarial_headers.js +128 -0
- package/dist/testing/adversarial_input.d.ts +56 -0
- package/dist/testing/adversarial_input.d.ts.map +1 -0
- package/dist/testing/adversarial_input.js +494 -0
- package/dist/testing/app_server.d.ts +169 -0
- package/dist/testing/app_server.d.ts.map +1 -0
- package/dist/testing/app_server.js +240 -0
- package/dist/testing/assert_dev_env.d.ts +10 -0
- package/dist/testing/assert_dev_env.d.ts.map +1 -0
- package/dist/testing/assert_dev_env.js +13 -0
- package/dist/testing/assertions.d.ts +61 -0
- package/dist/testing/assertions.d.ts.map +1 -0
- package/dist/testing/assertions.js +96 -0
- package/dist/testing/attack_surface.d.ts +63 -0
- package/dist/testing/attack_surface.d.ts.map +1 -0
- package/dist/testing/attack_surface.js +224 -0
- package/dist/testing/audit_completeness.d.ts +29 -0
- package/dist/testing/audit_completeness.d.ts.map +1 -0
- package/dist/testing/audit_completeness.js +410 -0
- package/dist/testing/auth_apps.d.ts +55 -0
- package/dist/testing/auth_apps.d.ts.map +1 -0
- package/dist/testing/auth_apps.js +122 -0
- package/dist/testing/data_exposure.d.ts +62 -0
- package/dist/testing/data_exposure.d.ts.map +1 -0
- package/dist/testing/data_exposure.js +297 -0
- package/dist/testing/db.d.ts +111 -0
- package/dist/testing/db.d.ts.map +1 -0
- package/dist/testing/db.js +258 -0
- package/dist/testing/entities.d.ts +21 -0
- package/dist/testing/entities.d.ts.map +1 -0
- package/dist/testing/entities.js +42 -0
- package/dist/testing/error_coverage.d.ts +78 -0
- package/dist/testing/error_coverage.d.ts.map +1 -0
- package/dist/testing/error_coverage.js +135 -0
- package/dist/testing/integration.d.ts +37 -0
- package/dist/testing/integration.d.ts.map +1 -0
- package/dist/testing/integration.js +1139 -0
- package/dist/testing/integration_helpers.d.ts +107 -0
- package/dist/testing/integration_helpers.d.ts.map +1 -0
- package/dist/testing/integration_helpers.js +246 -0
- package/dist/testing/middleware.d.ts +125 -0
- package/dist/testing/middleware.d.ts.map +1 -0
- package/dist/testing/middleware.js +210 -0
- package/dist/testing/rate_limiting.d.ts +43 -0
- package/dist/testing/rate_limiting.d.ts.map +1 -0
- package/dist/testing/rate_limiting.js +216 -0
- package/dist/testing/round_trip.d.ts +37 -0
- package/dist/testing/round_trip.d.ts.map +1 -0
- package/dist/testing/round_trip.js +128 -0
- package/dist/testing/schema_generators.d.ts +33 -0
- package/dist/testing/schema_generators.d.ts.map +1 -0
- package/dist/testing/schema_generators.js +137 -0
- package/dist/testing/standard.d.ts +49 -0
- package/dist/testing/standard.d.ts.map +1 -0
- package/dist/testing/standard.js +16 -0
- package/dist/testing/stubs.d.ts +96 -0
- package/dist/testing/stubs.d.ts.map +1 -0
- package/dist/testing/stubs.js +192 -0
- package/dist/testing/surface_invariants.d.ts +189 -0
- package/dist/testing/surface_invariants.d.ts.map +1 -0
- package/dist/testing/surface_invariants.js +450 -0
- package/dist/ui/AccountSessions.svelte +75 -0
- package/dist/ui/AccountSessions.svelte.d.ts +19 -0
- package/dist/ui/AccountSessions.svelte.d.ts.map +1 -0
- package/dist/ui/AdminAccounts.svelte +107 -0
- package/dist/ui/AdminAccounts.svelte.d.ts +19 -0
- package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -0
- package/dist/ui/AdminAuditLog.svelte +144 -0
- package/dist/ui/AdminAuditLog.svelte.d.ts +4 -0
- package/dist/ui/AdminAuditLog.svelte.d.ts.map +1 -0
- package/dist/ui/AdminInvites.svelte +142 -0
- package/dist/ui/AdminInvites.svelte.d.ts +4 -0
- package/dist/ui/AdminInvites.svelte.d.ts.map +1 -0
- package/dist/ui/AdminOverview.svelte +337 -0
- package/dist/ui/AdminOverview.svelte.d.ts +4 -0
- package/dist/ui/AdminOverview.svelte.d.ts.map +1 -0
- package/dist/ui/AdminPermitHistory.svelte +61 -0
- package/dist/ui/AdminPermitHistory.svelte.d.ts +19 -0
- package/dist/ui/AdminPermitHistory.svelte.d.ts.map +1 -0
- package/dist/ui/AdminSessions.svelte +85 -0
- package/dist/ui/AdminSessions.svelte.d.ts +19 -0
- package/dist/ui/AdminSessions.svelte.d.ts.map +1 -0
- package/dist/ui/AdminSettings.svelte +32 -0
- package/dist/ui/AdminSettings.svelte.d.ts +19 -0
- package/dist/ui/AdminSettings.svelte.d.ts.map +1 -0
- package/dist/ui/AdminSurface.svelte +42 -0
- package/dist/ui/AdminSurface.svelte.d.ts +4 -0
- package/dist/ui/AdminSurface.svelte.d.ts.map +1 -0
- package/dist/ui/AppShell.svelte +93 -0
- package/dist/ui/AppShell.svelte.d.ts +20 -0
- package/dist/ui/AppShell.svelte.d.ts.map +1 -0
- package/dist/ui/BootstrapForm.svelte +105 -0
- package/dist/ui/BootstrapForm.svelte.d.ts +4 -0
- package/dist/ui/BootstrapForm.svelte.d.ts.map +1 -0
- package/dist/ui/ColumnLayout.svelte +46 -0
- package/dist/ui/ColumnLayout.svelte.d.ts +11 -0
- package/dist/ui/ColumnLayout.svelte.d.ts.map +1 -0
- package/dist/ui/ConfirmButton.svelte +125 -0
- package/dist/ui/ConfirmButton.svelte.d.ts +54 -0
- package/dist/ui/ConfirmButton.svelte.d.ts.map +1 -0
- package/dist/ui/Datatable.svelte +185 -0
- package/dist/ui/Datatable.svelte.d.ts +35 -0
- package/dist/ui/Datatable.svelte.d.ts.map +1 -0
- package/dist/ui/LoginForm.svelte +82 -0
- package/dist/ui/LoginForm.svelte.d.ts +8 -0
- package/dist/ui/LoginForm.svelte.d.ts.map +1 -0
- package/dist/ui/LogoutButton.svelte +36 -0
- package/dist/ui/LogoutButton.svelte.d.ts +10 -0
- package/dist/ui/LogoutButton.svelte.d.ts.map +1 -0
- package/dist/ui/MenuLink.svelte +35 -0
- package/dist/ui/MenuLink.svelte.d.ts +12 -0
- package/dist/ui/MenuLink.svelte.d.ts.map +1 -0
- package/dist/ui/OpenSignupToggle.svelte +36 -0
- package/dist/ui/OpenSignupToggle.svelte.d.ts +19 -0
- package/dist/ui/OpenSignupToggle.svelte.d.ts.map +1 -0
- package/dist/ui/PopoverButton.svelte +136 -0
- package/dist/ui/PopoverButton.svelte.d.ts +63 -0
- package/dist/ui/PopoverButton.svelte.d.ts.map +1 -0
- package/dist/ui/SignupForm.svelte +117 -0
- package/dist/ui/SignupForm.svelte.d.ts +7 -0
- package/dist/ui/SignupForm.svelte.d.ts.map +1 -0
- package/dist/ui/SurfaceExplorer.svelte +287 -0
- package/dist/ui/SurfaceExplorer.svelte.d.ts +8 -0
- package/dist/ui/SurfaceExplorer.svelte.d.ts.map +1 -0
- package/dist/ui/account_sessions_state.svelte.d.ts +15 -0
- package/dist/ui/account_sessions_state.svelte.d.ts.map +1 -0
- package/dist/ui/account_sessions_state.svelte.js +45 -0
- package/dist/ui/admin_accounts_state.svelte.d.ts +19 -0
- package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -0
- package/dist/ui/admin_accounts_state.svelte.js +65 -0
- package/dist/ui/admin_invites_state.svelte.d.ts +19 -0
- package/dist/ui/admin_invites_state.svelte.d.ts.map +1 -0
- package/dist/ui/admin_invites_state.svelte.js +71 -0
- package/dist/ui/admin_sessions_state.svelte.d.ts +18 -0
- package/dist/ui/admin_sessions_state.svelte.d.ts.map +1 -0
- package/dist/ui/admin_sessions_state.svelte.js +62 -0
- package/dist/ui/app_settings_state.svelte.d.ts +14 -0
- package/dist/ui/app_settings_state.svelte.d.ts.map +1 -0
- package/dist/ui/app_settings_state.svelte.js +44 -0
- package/dist/ui/audit_log_state.svelte.d.ts +40 -0
- package/dist/ui/audit_log_state.svelte.d.ts.map +1 -0
- package/dist/ui/audit_log_state.svelte.js +153 -0
- package/dist/ui/auth_state.svelte.d.ts +85 -0
- package/dist/ui/auth_state.svelte.d.ts.map +1 -0
- package/dist/ui/auth_state.svelte.js +238 -0
- package/dist/ui/datatable.d.ts +25 -0
- package/dist/ui/datatable.d.ts.map +1 -0
- package/dist/ui/datatable.js +9 -0
- package/dist/ui/enter_advance.d.ts +13 -0
- package/dist/ui/enter_advance.d.ts.map +1 -0
- package/dist/ui/enter_advance.js +30 -0
- package/dist/ui/loadable.svelte.d.ts +55 -0
- package/dist/ui/loadable.svelte.d.ts.map +1 -0
- package/dist/ui/loadable.svelte.js +75 -0
- package/dist/ui/popover.svelte.d.ts +137 -0
- package/dist/ui/popover.svelte.d.ts.map +1 -0
- package/dist/ui/popover.svelte.js +288 -0
- package/dist/ui/position_helpers.d.ts +27 -0
- package/dist/ui/position_helpers.d.ts.map +1 -0
- package/dist/ui/position_helpers.js +81 -0
- package/dist/ui/sidebar_state.svelte.d.ts +30 -0
- package/dist/ui/sidebar_state.svelte.d.ts.map +1 -0
- package/dist/ui/sidebar_state.svelte.js +39 -0
- package/dist/ui/table_state.svelte.d.ts +63 -0
- package/dist/ui/table_state.svelte.d.ts.map +1 -0
- package/dist/ui/table_state.svelte.js +117 -0
- package/dist/ui/ui_fetch.d.ts +29 -0
- package/dist/ui/ui_fetch.d.ts.map +1 -0
- package/dist/ui/ui_fetch.js +37 -0
- package/dist/ui/ui_format.d.ts +63 -0
- package/dist/ui/ui_format.d.ts.map +1 -0
- package/dist/ui/ui_format.js +196 -0
- package/package.json +121 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import './assert_dev_env.js';
|
|
2
|
+
import type { AppSurface } from '../http/surface.js';
|
|
3
|
+
/**
|
|
4
|
+
* Every protected route has 401 in `error_schemas`.
|
|
5
|
+
*/
|
|
6
|
+
export declare const assert_protected_routes_declare_401: (surface: AppSurface) => void;
|
|
7
|
+
/**
|
|
8
|
+
* Every role/keeper route has 403 in `error_schemas`.
|
|
9
|
+
*/
|
|
10
|
+
export declare const assert_role_routes_declare_403: (surface: AppSurface) => void;
|
|
11
|
+
/**
|
|
12
|
+
* Every route with non-null `input_schema` has 400 in `error_schemas`.
|
|
13
|
+
*/
|
|
14
|
+
export declare const assert_input_routes_declare_400: (surface: AppSurface) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Every route with non-null `params_schema` has 400 in `error_schemas`.
|
|
17
|
+
*/
|
|
18
|
+
export declare const assert_params_routes_declare_400: (surface: AppSurface) => void;
|
|
19
|
+
/**
|
|
20
|
+
* Every route with non-null `query_schema` has 400 in `error_schemas`.
|
|
21
|
+
*/
|
|
22
|
+
export declare const assert_query_routes_declare_400: (surface: AppSurface) => void;
|
|
23
|
+
/**
|
|
24
|
+
* Every route has a non-empty description.
|
|
25
|
+
*/
|
|
26
|
+
export declare const assert_descriptions_present: (surface: AppSurface) => void;
|
|
27
|
+
/**
|
|
28
|
+
* No duplicate method+path pairs.
|
|
29
|
+
*/
|
|
30
|
+
export declare const assert_no_duplicate_routes: (surface: AppSurface) => void;
|
|
31
|
+
/**
|
|
32
|
+
* Every applicable middleware that declares errors must have those status codes
|
|
33
|
+
* present in the route's `error_schemas`.
|
|
34
|
+
*/
|
|
35
|
+
export declare const assert_middleware_errors_propagated: (surface: AppSurface) => void;
|
|
36
|
+
/**
|
|
37
|
+
* Every route's declared error schemas must have an `error` field at the top level
|
|
38
|
+
* (conforming to the `ApiError` base shape `{error: string}`).
|
|
39
|
+
*
|
|
40
|
+
* Catches typos in error schema definitions and ensures consumers can always
|
|
41
|
+
* read `.error` from error responses.
|
|
42
|
+
*/
|
|
43
|
+
export declare const assert_error_schemas_structurally_valid: (surface: AppSurface) => void;
|
|
44
|
+
/**
|
|
45
|
+
* The same `z.literal()` error code should not appear at different HTTP status codes
|
|
46
|
+
* across routes.
|
|
47
|
+
*
|
|
48
|
+
* Extracts `const` values from error schema `error` properties (which correspond to
|
|
49
|
+
* `z.literal()` in the Zod source). Flags when the same literal appears at different
|
|
50
|
+
* status codes — e.g., `ERROR_INVALID_CREDENTIALS` at both 401 and 403 would be a bug.
|
|
51
|
+
*
|
|
52
|
+
* Only checks schemas with `const` values (literal schemas). Generic `z.string()`
|
|
53
|
+
* schemas (which produce `{type: 'string'}` in JSON Schema) are ignored.
|
|
54
|
+
*/
|
|
55
|
+
export declare const assert_error_code_status_consistency: (surface: AppSurface) => void;
|
|
56
|
+
/**
|
|
57
|
+
* Routes declaring 404 error schemas should use specific `z.literal()` or `z.enum()`
|
|
58
|
+
* error codes, not generic `z.string()`.
|
|
59
|
+
*
|
|
60
|
+
* A generic 404 schema (`ApiError` with `z.string()`) means the error code is
|
|
61
|
+
* unconstrained — the handler could return any string, making client error handling
|
|
62
|
+
* fragile. Routes with params (`:id`) are the primary 404 producers; their error
|
|
63
|
+
* schemas should use specific constants like `ERROR_ACCOUNT_NOT_FOUND`.
|
|
64
|
+
*
|
|
65
|
+
* Only flags routes that have `params_schema` (param-driven resource lookup) — routes
|
|
66
|
+
* declaring 404 for other reasons (e.g., bootstrap not configured) may legitimately
|
|
67
|
+
* use generic schemas.
|
|
68
|
+
*/
|
|
69
|
+
export declare const assert_404_schemas_use_specific_errors: (surface: AppSurface) => void;
|
|
70
|
+
/** Specificity level of an error schema's `error` field. */
|
|
71
|
+
export type ErrorSchemaSpecificity = 'literal' | 'enum' | 'generic';
|
|
72
|
+
/** A single entry in the error schema tightness audit report. */
|
|
73
|
+
export interface ErrorSchemaAuditEntry {
|
|
74
|
+
method: string;
|
|
75
|
+
route_path: string;
|
|
76
|
+
status: string;
|
|
77
|
+
specificity: ErrorSchemaSpecificity;
|
|
78
|
+
/** The literal value or enum values, if specific. */
|
|
79
|
+
error_codes: Array<string> | null;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Audit error schema tightness across all routes in a surface.
|
|
83
|
+
*
|
|
84
|
+
* Reports which route x status code combinations use generic `ApiError`
|
|
85
|
+
* (`z.string()`) vs specific `z.literal()` or `z.enum()` error codes.
|
|
86
|
+
* Use the output to prioritize progressive tightening of error schemas.
|
|
87
|
+
*
|
|
88
|
+
* @param surface - the app surface to audit
|
|
89
|
+
* @returns audit entries for every route x status combination
|
|
90
|
+
*/
|
|
91
|
+
export declare const audit_error_schema_tightness: (surface: AppSurface) => Array<ErrorSchemaAuditEntry>;
|
|
92
|
+
/**
|
|
93
|
+
* Configuration for security policy invariants.
|
|
94
|
+
*
|
|
95
|
+
* All fields have sensible defaults. Pass overrides for project-specific needs.
|
|
96
|
+
*/
|
|
97
|
+
export interface SurfaceSecurityPolicyOptions {
|
|
98
|
+
/**
|
|
99
|
+
* Path patterns for routes that should be rate-limited.
|
|
100
|
+
* Default: common sensitive patterns (login, password, bootstrap, tokens/create).
|
|
101
|
+
*/
|
|
102
|
+
sensitive_route_patterns?: Array<string | RegExp>;
|
|
103
|
+
/**
|
|
104
|
+
* Routes explicitly allowed to be public mutations (e.g., webhooks, bootstrap).
|
|
105
|
+
* Format: `'METHOD /path'` (e.g., `'POST /api/account/login'`).
|
|
106
|
+
*/
|
|
107
|
+
public_mutation_allowlist?: Array<string>;
|
|
108
|
+
/**
|
|
109
|
+
* Allowed path prefixes for keeper-protected routes.
|
|
110
|
+
* Default: `['/api/']`. Catches keeper routes outside expected namespaces.
|
|
111
|
+
*/
|
|
112
|
+
keeper_route_prefixes?: Array<string>;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Sensitive routes must declare rate limiting (`rate_limit_key` is non-null)
|
|
116
|
+
* or have 429 in their error schemas.
|
|
117
|
+
*
|
|
118
|
+
* Matches routes against sensitive patterns and flags any that lack rate limit
|
|
119
|
+
* declarations. Catches forgotten rate limiting on credential-handling routes.
|
|
120
|
+
*/
|
|
121
|
+
export declare const assert_sensitive_routes_rate_limited: (surface: AppSurface, sensitive_patterns?: Array<string | RegExp>) => void;
|
|
122
|
+
/**
|
|
123
|
+
* Public mutation routes (auth: none + is_mutation) must be in the allowlist.
|
|
124
|
+
*
|
|
125
|
+
* Catches accidentally unprotected POST/PUT/DELETE routes. Routes like login
|
|
126
|
+
* and bootstrap are public mutations by design — they go in the allowlist.
|
|
127
|
+
*/
|
|
128
|
+
export declare const assert_no_unexpected_public_mutations: (surface: AppSurface, allowlist?: Array<string>) => void;
|
|
129
|
+
/**
|
|
130
|
+
* Routes with non-null input schemas should use POST (or other mutation methods),
|
|
131
|
+
* not GET.
|
|
132
|
+
*
|
|
133
|
+
* GET routes with request bodies are technically allowed by HTTP but semantically
|
|
134
|
+
* suspicious — they bypass browser security assumptions about GET being idempotent.
|
|
135
|
+
* Query-string-driven filtering (audit log, list endpoints) should use params schemas
|
|
136
|
+
* or query string parsing, not input schemas.
|
|
137
|
+
*/
|
|
138
|
+
export declare const assert_mutation_routes_use_post: (surface: AppSurface) => void;
|
|
139
|
+
/**
|
|
140
|
+
* Keeper-protected routes must be under expected path prefixes.
|
|
141
|
+
*
|
|
142
|
+
* Catches keeper routes accidentally placed outside the API namespace
|
|
143
|
+
* (e.g., a keeper route at `/health` or `/admin/` instead of `/api/...`).
|
|
144
|
+
*/
|
|
145
|
+
export declare const assert_keeper_routes_under_prefix: (surface: AppSurface, prefixes?: Array<string>) => void;
|
|
146
|
+
/** Options for `assert_error_schema_tightness`. */
|
|
147
|
+
export interface ErrorSchemaTightnessOptions {
|
|
148
|
+
/** Minimum specificity level. Error schemas below this threshold fail. Default: `'enum'`. */
|
|
149
|
+
min_specificity?: ErrorSchemaSpecificity;
|
|
150
|
+
/** HTTP status codes to skip (e.g., middleware-injected codes). */
|
|
151
|
+
ignore_statuses?: Array<number>;
|
|
152
|
+
/** Routes to skip, in `'METHOD /path'` format. */
|
|
153
|
+
allowlist?: Array<string>;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Recommended baseline error schema tightness for consumer projects.
|
|
157
|
+
*
|
|
158
|
+
* Uses `min_specificity: 'enum'` (the assertion default) with `ignore_statuses`
|
|
159
|
+
* for middleware-derived status codes that are commonly generic (auth middleware
|
|
160
|
+
* produces multiple error codes at 401/403, and 429 comes from rate limiters).
|
|
161
|
+
* Consumers can extend with project-specific `allowlist` entries.
|
|
162
|
+
*/
|
|
163
|
+
export declare const DEFAULT_ERROR_SCHEMA_TIGHTNESS: ErrorSchemaTightnessOptions;
|
|
164
|
+
/**
|
|
165
|
+
* Assert that all error schemas meet a minimum specificity threshold.
|
|
166
|
+
*
|
|
167
|
+
* Calls `audit_error_schema_tightness` and fails on any entry below
|
|
168
|
+
* the configured threshold. Use `allowlist` and `ignore_statuses` to exclude
|
|
169
|
+
* known exceptions during progressive tightening.
|
|
170
|
+
*
|
|
171
|
+
* @param surface - the app surface to check
|
|
172
|
+
* @param options - threshold and exclusion configuration
|
|
173
|
+
*/
|
|
174
|
+
export declare const assert_error_schema_tightness: (surface: AppSurface, options?: ErrorSchemaTightnessOptions) => void;
|
|
175
|
+
/**
|
|
176
|
+
* Run all structural invariants. Options-free — applies universally.
|
|
177
|
+
*/
|
|
178
|
+
export declare const assert_surface_invariants: (surface: AppSurface) => void;
|
|
179
|
+
/**
|
|
180
|
+
* Run security policy invariants. Configurable with sensible defaults.
|
|
181
|
+
*
|
|
182
|
+
* Checks:
|
|
183
|
+
* - Sensitive routes are rate-limited
|
|
184
|
+
* - No unexpected public mutation routes
|
|
185
|
+
* - Input schemas use mutation methods (not GET)
|
|
186
|
+
* - Keeper routes under expected prefixes
|
|
187
|
+
*/
|
|
188
|
+
export declare const assert_surface_security_policy: (surface: AppSurface, options?: SurfaceSecurityPolicyOptions) => void;
|
|
189
|
+
//# sourceMappingURL=surface_invariants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"surface_invariants.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/surface_invariants.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
import './assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Surface invariant assertions for `AppSurface` data.
|
|
4
|
+
*
|
|
5
|
+
* Two categories:
|
|
6
|
+
* - **Structural** — validate internal consistency (error schema presence,
|
|
7
|
+
* descriptions, duplicates, middleware propagation, error schema validity,
|
|
8
|
+
* error code consistency). No options needed.
|
|
9
|
+
* - **Policy** — enforce security policy (sensitive routes rate-limited,
|
|
10
|
+
* no public mutations, mutation method conventions). Configurable with
|
|
11
|
+
* sensible defaults.
|
|
12
|
+
*
|
|
13
|
+
* Structural invariants catch schema/surface generation bugs. Policy invariants
|
|
14
|
+
* catch security misconfigurations. Both propagate automatically to consumers
|
|
15
|
+
* via `assert_surface_invariants`.
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
import { assert } from 'vitest';
|
|
20
|
+
import { middleware_applies } from '../http/schema_helpers.js';
|
|
21
|
+
import { filter_protected_routes, filter_role_routes, filter_keeper_routes, filter_routes_with_input, filter_routes_with_params, filter_routes_with_query, filter_public_routes, format_route_key, } from '../http/surface_query.js';
|
|
22
|
+
// --- Structural invariants ---
|
|
23
|
+
/**
|
|
24
|
+
* Every protected route has 401 in `error_schemas`.
|
|
25
|
+
*/
|
|
26
|
+
export const assert_protected_routes_declare_401 = (surface) => {
|
|
27
|
+
const routes = filter_protected_routes(surface);
|
|
28
|
+
for (const route of routes) {
|
|
29
|
+
assert.ok(route.error_schemas && '401' in route.error_schemas, `${format_route_key(route)} is protected but missing 401 error schema`);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Every role/keeper route has 403 in `error_schemas`.
|
|
34
|
+
*/
|
|
35
|
+
export const assert_role_routes_declare_403 = (surface) => {
|
|
36
|
+
const role_routes = filter_role_routes(surface);
|
|
37
|
+
const keeper_routes = filter_keeper_routes(surface);
|
|
38
|
+
for (const route of [...role_routes, ...keeper_routes]) {
|
|
39
|
+
assert.ok(route.error_schemas && '403' in route.error_schemas, `${format_route_key(route)} requires role/keeper but missing 403 error schema`);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Every route with non-null `input_schema` has 400 in `error_schemas`.
|
|
44
|
+
*/
|
|
45
|
+
export const assert_input_routes_declare_400 = (surface) => {
|
|
46
|
+
const routes = filter_routes_with_input(surface);
|
|
47
|
+
for (const route of routes) {
|
|
48
|
+
assert.ok(route.error_schemas && '400' in route.error_schemas, `${format_route_key(route)} has input but missing 400 error schema`);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Every route with non-null `params_schema` has 400 in `error_schemas`.
|
|
53
|
+
*/
|
|
54
|
+
export const assert_params_routes_declare_400 = (surface) => {
|
|
55
|
+
const routes = filter_routes_with_params(surface);
|
|
56
|
+
for (const route of routes) {
|
|
57
|
+
assert.ok(route.error_schemas && '400' in route.error_schemas, `${format_route_key(route)} has params but missing 400 error schema`);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Every route with non-null `query_schema` has 400 in `error_schemas`.
|
|
62
|
+
*/
|
|
63
|
+
export const assert_query_routes_declare_400 = (surface) => {
|
|
64
|
+
const routes = filter_routes_with_query(surface);
|
|
65
|
+
for (const route of routes) {
|
|
66
|
+
assert.ok(route.error_schemas && '400' in route.error_schemas, `${format_route_key(route)} has query schema but missing 400 error schema`);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Every route has a non-empty description.
|
|
71
|
+
*/
|
|
72
|
+
export const assert_descriptions_present = (surface) => {
|
|
73
|
+
for (const route of surface.routes) {
|
|
74
|
+
assert.ok(route.description.length > 0, `${format_route_key(route)} has empty description`);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* No duplicate method+path pairs.
|
|
79
|
+
*/
|
|
80
|
+
export const assert_no_duplicate_routes = (surface) => {
|
|
81
|
+
const seen = new Set();
|
|
82
|
+
for (const route of surface.routes) {
|
|
83
|
+
const key = format_route_key(route);
|
|
84
|
+
assert.ok(!seen.has(key), `Duplicate route: ${key}`);
|
|
85
|
+
seen.add(key);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Every applicable middleware that declares errors must have those status codes
|
|
90
|
+
* present in the route's `error_schemas`.
|
|
91
|
+
*/
|
|
92
|
+
export const assert_middleware_errors_propagated = (surface) => {
|
|
93
|
+
const middleware_with_errors = surface.middleware.filter((m) => m.error_schemas !== null);
|
|
94
|
+
for (const route of surface.routes) {
|
|
95
|
+
for (const mw of middleware_with_errors) {
|
|
96
|
+
if (!middleware_applies(mw.path, route.path))
|
|
97
|
+
continue;
|
|
98
|
+
for (const status of Object.keys(mw.error_schemas)) {
|
|
99
|
+
assert.ok(route.error_schemas && status in route.error_schemas, `${format_route_key(route)} missing status ${status} from middleware '${mw.name}'`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Every route's declared error schemas must have an `error` field at the top level
|
|
106
|
+
* (conforming to the `ApiError` base shape `{error: string}`).
|
|
107
|
+
*
|
|
108
|
+
* Catches typos in error schema definitions and ensures consumers can always
|
|
109
|
+
* read `.error` from error responses.
|
|
110
|
+
*/
|
|
111
|
+
export const assert_error_schemas_structurally_valid = (surface) => {
|
|
112
|
+
for (const route of surface.routes) {
|
|
113
|
+
if (!route.error_schemas)
|
|
114
|
+
continue;
|
|
115
|
+
for (const [status, schema] of Object.entries(route.error_schemas)) {
|
|
116
|
+
if (typeof schema !== 'object' || schema === null)
|
|
117
|
+
continue;
|
|
118
|
+
const s = schema;
|
|
119
|
+
// JSON Schema must have properties.error or be an object type
|
|
120
|
+
if (s.type === 'object' && s.properties && typeof s.properties === 'object') {
|
|
121
|
+
const props = s.properties;
|
|
122
|
+
assert.ok('error' in props, `${format_route_key(route)} error schema for status ${status} missing 'error' property`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* The same `z.literal()` error code should not appear at different HTTP status codes
|
|
129
|
+
* across routes.
|
|
130
|
+
*
|
|
131
|
+
* Extracts `const` values from error schema `error` properties (which correspond to
|
|
132
|
+
* `z.literal()` in the Zod source). Flags when the same literal appears at different
|
|
133
|
+
* status codes — e.g., `ERROR_INVALID_CREDENTIALS` at both 401 and 403 would be a bug.
|
|
134
|
+
*
|
|
135
|
+
* Only checks schemas with `const` values (literal schemas). Generic `z.string()`
|
|
136
|
+
* schemas (which produce `{type: 'string'}` in JSON Schema) are ignored.
|
|
137
|
+
*/
|
|
138
|
+
export const assert_error_code_status_consistency = (surface) => {
|
|
139
|
+
// Map from error code literal → Set of status codes where it appears
|
|
140
|
+
const code_to_statuses = new Map();
|
|
141
|
+
for (const route of surface.routes) {
|
|
142
|
+
if (!route.error_schemas)
|
|
143
|
+
continue;
|
|
144
|
+
for (const [status, schema] of Object.entries(route.error_schemas)) {
|
|
145
|
+
const error_const = extract_error_const(schema);
|
|
146
|
+
if (error_const === null)
|
|
147
|
+
continue;
|
|
148
|
+
let statuses = code_to_statuses.get(error_const);
|
|
149
|
+
if (!statuses) {
|
|
150
|
+
statuses = new Set();
|
|
151
|
+
code_to_statuses.set(error_const, statuses);
|
|
152
|
+
}
|
|
153
|
+
statuses.add(status);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// Also check middleware error schemas
|
|
157
|
+
for (const mw of surface.middleware) {
|
|
158
|
+
if (!mw.error_schemas)
|
|
159
|
+
continue;
|
|
160
|
+
for (const [status, schema] of Object.entries(mw.error_schemas)) {
|
|
161
|
+
const error_const = extract_error_const(schema);
|
|
162
|
+
if (error_const === null)
|
|
163
|
+
continue;
|
|
164
|
+
let statuses = code_to_statuses.get(error_const);
|
|
165
|
+
if (!statuses) {
|
|
166
|
+
statuses = new Set();
|
|
167
|
+
code_to_statuses.set(error_const, statuses);
|
|
168
|
+
}
|
|
169
|
+
statuses.add(status);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
for (const [code, statuses] of code_to_statuses) {
|
|
173
|
+
assert.ok(statuses.size === 1, `Error code '${code}' appears at multiple status codes: ${[...statuses].sort().join(', ')}`);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
/**
|
|
177
|
+
* Extract the `error` property's JSON Schema from a route error schema.
|
|
178
|
+
*
|
|
179
|
+
* Navigates `schema.properties.error` — the common structure for all
|
|
180
|
+
* `ApiError`-shaped schemas. Returns `null` if the schema doesn't conform.
|
|
181
|
+
*/
|
|
182
|
+
const get_error_property = (schema) => {
|
|
183
|
+
if (typeof schema !== 'object' || schema === null)
|
|
184
|
+
return null;
|
|
185
|
+
const s = schema;
|
|
186
|
+
if (s.type !== 'object' || !s.properties || typeof s.properties !== 'object')
|
|
187
|
+
return null;
|
|
188
|
+
const props = s.properties;
|
|
189
|
+
if (!props.error || typeof props.error !== 'object')
|
|
190
|
+
return null;
|
|
191
|
+
return props.error;
|
|
192
|
+
};
|
|
193
|
+
/**
|
|
194
|
+
* Extract the `const` value from a JSON Schema error property, if present.
|
|
195
|
+
*
|
|
196
|
+
* Looks for `schema.properties.error.const` — the JSON Schema representation
|
|
197
|
+
* of `z.literal('some_error_code')`.
|
|
198
|
+
*/
|
|
199
|
+
const extract_error_const = (schema) => {
|
|
200
|
+
const error_prop = get_error_property(schema);
|
|
201
|
+
if (!error_prop)
|
|
202
|
+
return null;
|
|
203
|
+
if (typeof error_prop.const === 'string')
|
|
204
|
+
return error_prop.const;
|
|
205
|
+
return null;
|
|
206
|
+
};
|
|
207
|
+
/**
|
|
208
|
+
* Check if a JSON Schema error property uses specific error codes (`const` or `enum`),
|
|
209
|
+
* not just generic `z.string()` (`{type: 'string'}`).
|
|
210
|
+
*
|
|
211
|
+
* Returns `true` for `z.literal()` (`{const: '...'}`) and `z.enum()` (`{enum: [...]}`).
|
|
212
|
+
*/
|
|
213
|
+
const has_specific_error_schema = (schema) => {
|
|
214
|
+
const error_prop = get_error_property(schema);
|
|
215
|
+
if (!error_prop)
|
|
216
|
+
return false;
|
|
217
|
+
return typeof error_prop.const === 'string' || Array.isArray(error_prop.enum);
|
|
218
|
+
};
|
|
219
|
+
/**
|
|
220
|
+
* Routes declaring 404 error schemas should use specific `z.literal()` or `z.enum()`
|
|
221
|
+
* error codes, not generic `z.string()`.
|
|
222
|
+
*
|
|
223
|
+
* A generic 404 schema (`ApiError` with `z.string()`) means the error code is
|
|
224
|
+
* unconstrained — the handler could return any string, making client error handling
|
|
225
|
+
* fragile. Routes with params (`:id`) are the primary 404 producers; their error
|
|
226
|
+
* schemas should use specific constants like `ERROR_ACCOUNT_NOT_FOUND`.
|
|
227
|
+
*
|
|
228
|
+
* Only flags routes that have `params_schema` (param-driven resource lookup) — routes
|
|
229
|
+
* declaring 404 for other reasons (e.g., bootstrap not configured) may legitimately
|
|
230
|
+
* use generic schemas.
|
|
231
|
+
*/
|
|
232
|
+
export const assert_404_schemas_use_specific_errors = (surface) => {
|
|
233
|
+
for (const route of surface.routes) {
|
|
234
|
+
if (!route.error_schemas || !('404' in route.error_schemas))
|
|
235
|
+
continue;
|
|
236
|
+
if (route.params_schema === null)
|
|
237
|
+
continue;
|
|
238
|
+
assert.ok(has_specific_error_schema(route.error_schemas['404']), `${format_route_key(route)} declares 404 with params but uses generic error schema — ` +
|
|
239
|
+
`use a specific z.literal() or z.enum() error code`);
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
/**
|
|
243
|
+
* Classify the specificity of a JSON Schema error property.
|
|
244
|
+
*
|
|
245
|
+
* - `'literal'` — `z.literal()` (`{const: '...'}`)
|
|
246
|
+
* - `'enum'` — `z.enum()` (`{enum: [...]}`)
|
|
247
|
+
* - `'generic'` — `z.string()` or unrecognized
|
|
248
|
+
*/
|
|
249
|
+
const classify_error_specificity = (schema) => {
|
|
250
|
+
const error_prop = get_error_property(schema);
|
|
251
|
+
if (!error_prop)
|
|
252
|
+
return 'generic';
|
|
253
|
+
if (typeof error_prop.const === 'string')
|
|
254
|
+
return 'literal';
|
|
255
|
+
if (Array.isArray(error_prop.enum))
|
|
256
|
+
return 'enum';
|
|
257
|
+
return 'generic';
|
|
258
|
+
};
|
|
259
|
+
/**
|
|
260
|
+
* Extract error code values from a JSON Schema error property.
|
|
261
|
+
*
|
|
262
|
+
* Returns the literal value or enum array, or `null` for generic schemas.
|
|
263
|
+
*/
|
|
264
|
+
const extract_error_codes = (schema) => {
|
|
265
|
+
const error_prop = get_error_property(schema);
|
|
266
|
+
if (!error_prop)
|
|
267
|
+
return null;
|
|
268
|
+
if (typeof error_prop.const === 'string')
|
|
269
|
+
return [error_prop.const];
|
|
270
|
+
if (Array.isArray(error_prop.enum))
|
|
271
|
+
return error_prop.enum.filter((v) => typeof v === 'string');
|
|
272
|
+
return null;
|
|
273
|
+
};
|
|
274
|
+
/**
|
|
275
|
+
* Audit error schema tightness across all routes in a surface.
|
|
276
|
+
*
|
|
277
|
+
* Reports which route x status code combinations use generic `ApiError`
|
|
278
|
+
* (`z.string()`) vs specific `z.literal()` or `z.enum()` error codes.
|
|
279
|
+
* Use the output to prioritize progressive tightening of error schemas.
|
|
280
|
+
*
|
|
281
|
+
* @param surface - the app surface to audit
|
|
282
|
+
* @returns audit entries for every route x status combination
|
|
283
|
+
*/
|
|
284
|
+
export const audit_error_schema_tightness = (surface) => {
|
|
285
|
+
const entries = [];
|
|
286
|
+
for (const route of surface.routes) {
|
|
287
|
+
if (!route.error_schemas)
|
|
288
|
+
continue;
|
|
289
|
+
for (const [status, schema] of Object.entries(route.error_schemas)) {
|
|
290
|
+
const specificity = classify_error_specificity(schema);
|
|
291
|
+
entries.push({
|
|
292
|
+
method: route.method,
|
|
293
|
+
route_path: route.path,
|
|
294
|
+
status,
|
|
295
|
+
specificity,
|
|
296
|
+
error_codes: extract_error_codes(schema),
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return entries;
|
|
301
|
+
};
|
|
302
|
+
/** Default patterns for sensitive routes that should be rate-limited. */
|
|
303
|
+
const DEFAULT_SENSITIVE_PATTERNS = [
|
|
304
|
+
/\/login$/,
|
|
305
|
+
/\/password$/,
|
|
306
|
+
/\/bootstrap$/,
|
|
307
|
+
/\/tokens\/create$/,
|
|
308
|
+
];
|
|
309
|
+
/**
|
|
310
|
+
* Sensitive routes must declare rate limiting (`rate_limit_key` is non-null)
|
|
311
|
+
* or have 429 in their error schemas.
|
|
312
|
+
*
|
|
313
|
+
* Matches routes against sensitive patterns and flags any that lack rate limit
|
|
314
|
+
* declarations. Catches forgotten rate limiting on credential-handling routes.
|
|
315
|
+
*/
|
|
316
|
+
export const assert_sensitive_routes_rate_limited = (surface, sensitive_patterns = DEFAULT_SENSITIVE_PATTERNS) => {
|
|
317
|
+
for (const route of surface.routes) {
|
|
318
|
+
const matches = sensitive_patterns.some((pattern) => typeof pattern === 'string' ? route.path.includes(pattern) : pattern.test(route.path));
|
|
319
|
+
if (!matches)
|
|
320
|
+
continue;
|
|
321
|
+
const has_rate_limit = route.rate_limit_key !== null ||
|
|
322
|
+
(route.error_schemas !== null && '429' in route.error_schemas);
|
|
323
|
+
assert.ok(has_rate_limit, `${format_route_key(route)} matches a sensitive pattern but has no rate limiting declared`);
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
/**
|
|
327
|
+
* Public mutation routes (auth: none + is_mutation) must be in the allowlist.
|
|
328
|
+
*
|
|
329
|
+
* Catches accidentally unprotected POST/PUT/DELETE routes. Routes like login
|
|
330
|
+
* and bootstrap are public mutations by design — they go in the allowlist.
|
|
331
|
+
*/
|
|
332
|
+
export const assert_no_unexpected_public_mutations = (surface, allowlist = []) => {
|
|
333
|
+
const public_routes = filter_public_routes(surface);
|
|
334
|
+
const mutations = public_routes.filter((r) => r.is_mutation);
|
|
335
|
+
const allowset = new Set(allowlist);
|
|
336
|
+
for (const route of mutations) {
|
|
337
|
+
const key = format_route_key(route);
|
|
338
|
+
assert.ok(allowset.has(key), `${key} is a public mutation route not in the allowlist. ` +
|
|
339
|
+
`Add it to public_mutation_allowlist if intentional.`);
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
/**
|
|
343
|
+
* Routes with non-null input schemas should use POST (or other mutation methods),
|
|
344
|
+
* not GET.
|
|
345
|
+
*
|
|
346
|
+
* GET routes with request bodies are technically allowed by HTTP but semantically
|
|
347
|
+
* suspicious — they bypass browser security assumptions about GET being idempotent.
|
|
348
|
+
* Query-string-driven filtering (audit log, list endpoints) should use params schemas
|
|
349
|
+
* or query string parsing, not input schemas.
|
|
350
|
+
*/
|
|
351
|
+
export const assert_mutation_routes_use_post = (surface) => {
|
|
352
|
+
const input_routes = filter_routes_with_input(surface);
|
|
353
|
+
for (const route of input_routes) {
|
|
354
|
+
assert.ok(route.method !== 'GET', `${format_route_key(route)} has input schema on GET route — use POST or move to params/query`);
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
/** Default allowed prefixes for keeper routes. */
|
|
358
|
+
const DEFAULT_KEEPER_ROUTE_PREFIXES = ['/api/'];
|
|
359
|
+
/**
|
|
360
|
+
* Keeper-protected routes must be under expected path prefixes.
|
|
361
|
+
*
|
|
362
|
+
* Catches keeper routes accidentally placed outside the API namespace
|
|
363
|
+
* (e.g., a keeper route at `/health` or `/admin/` instead of `/api/...`).
|
|
364
|
+
*/
|
|
365
|
+
export const assert_keeper_routes_under_prefix = (surface, prefixes = DEFAULT_KEEPER_ROUTE_PREFIXES) => {
|
|
366
|
+
const keeper_routes = filter_keeper_routes(surface);
|
|
367
|
+
for (const route of keeper_routes) {
|
|
368
|
+
const under_prefix = prefixes.some((prefix) => route.path.startsWith(prefix));
|
|
369
|
+
assert.ok(under_prefix, `${format_route_key(route)} is keeper-protected but not under any expected prefix: ${prefixes.join(', ')}`);
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
// --- Error schema tightness assertion ---
|
|
373
|
+
/** Numeric specificity ranking for threshold comparisons. */
|
|
374
|
+
const SPECIFICITY_ORDER = {
|
|
375
|
+
literal: 2,
|
|
376
|
+
enum: 1,
|
|
377
|
+
generic: 0,
|
|
378
|
+
};
|
|
379
|
+
/**
|
|
380
|
+
* Recommended baseline error schema tightness for consumer projects.
|
|
381
|
+
*
|
|
382
|
+
* Uses `min_specificity: 'enum'` (the assertion default) with `ignore_statuses`
|
|
383
|
+
* for middleware-derived status codes that are commonly generic (auth middleware
|
|
384
|
+
* produces multiple error codes at 401/403, and 429 comes from rate limiters).
|
|
385
|
+
* Consumers can extend with project-specific `allowlist` entries.
|
|
386
|
+
*/
|
|
387
|
+
export const DEFAULT_ERROR_SCHEMA_TIGHTNESS = {
|
|
388
|
+
ignore_statuses: [401, 403, 429],
|
|
389
|
+
};
|
|
390
|
+
/**
|
|
391
|
+
* Assert that all error schemas meet a minimum specificity threshold.
|
|
392
|
+
*
|
|
393
|
+
* Calls `audit_error_schema_tightness` and fails on any entry below
|
|
394
|
+
* the configured threshold. Use `allowlist` and `ignore_statuses` to exclude
|
|
395
|
+
* known exceptions during progressive tightening.
|
|
396
|
+
*
|
|
397
|
+
* @param surface - the app surface to check
|
|
398
|
+
* @param options - threshold and exclusion configuration
|
|
399
|
+
*/
|
|
400
|
+
export const assert_error_schema_tightness = (surface, options) => {
|
|
401
|
+
const min_specificity = options?.min_specificity ?? 'enum';
|
|
402
|
+
const ignore_statuses = new Set(options?.ignore_statuses?.map(String));
|
|
403
|
+
const allowlist = new Set(options?.allowlist);
|
|
404
|
+
const threshold = SPECIFICITY_ORDER[min_specificity];
|
|
405
|
+
const entries = audit_error_schema_tightness(surface);
|
|
406
|
+
const failures = [];
|
|
407
|
+
for (const entry of entries) {
|
|
408
|
+
if (ignore_statuses.has(entry.status))
|
|
409
|
+
continue;
|
|
410
|
+
const key = `${entry.method} ${entry.route_path}`;
|
|
411
|
+
if (allowlist.has(key))
|
|
412
|
+
continue;
|
|
413
|
+
if (SPECIFICITY_ORDER[entry.specificity] < threshold) {
|
|
414
|
+
failures.push(`${key} → ${entry.status} (${entry.specificity})`);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
assert.ok(failures.length === 0, `Error schemas below '${min_specificity}' threshold:\n ${failures.join('\n ')}`);
|
|
418
|
+
};
|
|
419
|
+
// --- Aggregate runners ---
|
|
420
|
+
/**
|
|
421
|
+
* Run all structural invariants. Options-free — applies universally.
|
|
422
|
+
*/
|
|
423
|
+
export const assert_surface_invariants = (surface) => {
|
|
424
|
+
assert_protected_routes_declare_401(surface);
|
|
425
|
+
assert_role_routes_declare_403(surface);
|
|
426
|
+
assert_input_routes_declare_400(surface);
|
|
427
|
+
assert_params_routes_declare_400(surface);
|
|
428
|
+
assert_query_routes_declare_400(surface);
|
|
429
|
+
assert_descriptions_present(surface);
|
|
430
|
+
assert_no_duplicate_routes(surface);
|
|
431
|
+
assert_middleware_errors_propagated(surface);
|
|
432
|
+
assert_error_schemas_structurally_valid(surface);
|
|
433
|
+
assert_error_code_status_consistency(surface);
|
|
434
|
+
assert_404_schemas_use_specific_errors(surface);
|
|
435
|
+
};
|
|
436
|
+
/**
|
|
437
|
+
* Run security policy invariants. Configurable with sensible defaults.
|
|
438
|
+
*
|
|
439
|
+
* Checks:
|
|
440
|
+
* - Sensitive routes are rate-limited
|
|
441
|
+
* - No unexpected public mutation routes
|
|
442
|
+
* - Input schemas use mutation methods (not GET)
|
|
443
|
+
* - Keeper routes under expected prefixes
|
|
444
|
+
*/
|
|
445
|
+
export const assert_surface_security_policy = (surface, options = {}) => {
|
|
446
|
+
assert_sensitive_routes_rate_limited(surface, options.sensitive_route_patterns);
|
|
447
|
+
assert_no_unexpected_public_mutations(surface, options.public_mutation_allowlist);
|
|
448
|
+
assert_mutation_routes_use_post(surface);
|
|
449
|
+
assert_keeper_routes_under_prefix(surface, options.keeper_route_prefixes);
|
|
450
|
+
};
|