@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,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reactive state for admin app settings management.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { Loadable } from './loadable.svelte.js';
|
|
7
|
+
import { parse_response_error, ui_fetch } from './ui_fetch.js';
|
|
8
|
+
export class AppSettingsState extends Loadable {
|
|
9
|
+
settings = $state(null);
|
|
10
|
+
updating = $state(false);
|
|
11
|
+
async fetch() {
|
|
12
|
+
await this.run(async () => {
|
|
13
|
+
const response = await ui_fetch('/api/admin/settings');
|
|
14
|
+
if (!response.ok) {
|
|
15
|
+
throw new Error(await parse_response_error(response, 'Failed to fetch settings'));
|
|
16
|
+
}
|
|
17
|
+
const data = await response.json();
|
|
18
|
+
this.settings = data.settings ?? null;
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
async update_open_signup(value) {
|
|
22
|
+
this.updating = true;
|
|
23
|
+
this.error = null;
|
|
24
|
+
try {
|
|
25
|
+
const response = await ui_fetch('/api/admin/settings', {
|
|
26
|
+
method: 'PATCH',
|
|
27
|
+
headers: { 'Content-Type': 'application/json' },
|
|
28
|
+
body: JSON.stringify({ open_signup: value }),
|
|
29
|
+
});
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
this.error = await parse_response_error(response);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const data = await response.json();
|
|
35
|
+
this.settings = data.settings ?? null;
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
this.error = e instanceof Error ? e.message : 'Failed to update settings';
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
this.updating = false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reactive state for the audit log viewer.
|
|
3
|
+
*
|
|
4
|
+
* Supports both fetch-based pagination and realtime SSE streaming.
|
|
5
|
+
* SSE events are prepended to the list; use `fetch()` for initial load and filters.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { Loadable } from './loadable.svelte.js';
|
|
10
|
+
import type { AuditLogEventWithUsernamesJson, PermitHistoryEventJson } from '../auth/audit_log_schema.js';
|
|
11
|
+
/** Options for fetching audit log events. */
|
|
12
|
+
export interface AuditLogFetchOptions {
|
|
13
|
+
event_type?: string;
|
|
14
|
+
account_id?: string;
|
|
15
|
+
limit?: number;
|
|
16
|
+
offset?: number;
|
|
17
|
+
}
|
|
18
|
+
export declare class AuditLogState extends Loadable {
|
|
19
|
+
#private;
|
|
20
|
+
events: Array<AuditLogEventWithUsernamesJson>;
|
|
21
|
+
permit_history_events: Array<PermitHistoryEventJson>;
|
|
22
|
+
readonly count: number;
|
|
23
|
+
/** Whether the SSE stream is currently connected. */
|
|
24
|
+
connected: boolean;
|
|
25
|
+
constructor(stream_url?: string);
|
|
26
|
+
fetch(options?: AuditLogFetchOptions): Promise<void>;
|
|
27
|
+
fetch_permit_history(limit?: number, offset?: number): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Connect to the SSE stream for realtime audit events.
|
|
30
|
+
*
|
|
31
|
+
* New events are prepended to `events`. EventSource auto-reconnects on
|
|
32
|
+
* transient errors; `since_seq` fills gaps on reconnection.
|
|
33
|
+
*
|
|
34
|
+
* @returns cleanup function that closes the connection
|
|
35
|
+
*/
|
|
36
|
+
subscribe(): () => void;
|
|
37
|
+
/** Close the SSE connection. */
|
|
38
|
+
disconnect(): void;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=audit_log_state.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit_log_state.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/audit_log_state.svelte.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAC,QAAQ,EAAC,MAAM,sBAAsB,CAAC;AAE9C,OAAO,KAAK,EAEX,8BAA8B,EAC9B,sBAAsB,EACtB,MAAM,6BAA6B,CAAC;AAGrC,6CAA6C;AAC7C,MAAM,WAAW,oBAAoB;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,aAAc,SAAQ,QAAQ;;IAC1C,MAAM,EAAE,KAAK,CAAC,8BAA8B,CAAC,CAAc;IAC3D,qBAAqB,EAAE,KAAK,CAAC,sBAAsB,CAAC,CAAc;IAElE,QAAQ,CAAC,KAAK,SAAgC;IAE9C,qDAAqD;IACrD,SAAS,UAAiB;gBAWd,UAAU,SAAgC;IAKhD,KAAK,CAAC,OAAO,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBpD,oBAAoB,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB1E;;;;;;;OAOG;IACH,SAAS,IAAI,MAAM,IAAI;IA0CvB,gCAAgC;IAChC,UAAU,IAAI,IAAI;CAmClB"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reactive state for the audit log viewer.
|
|
3
|
+
*
|
|
4
|
+
* Supports both fetch-based pagination and realtime SSE streaming.
|
|
5
|
+
* SSE events are prepended to the list; use `fetch()` for initial load and filters.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { DEV } from 'esm-env';
|
|
10
|
+
import { Loadable } from './loadable.svelte.js';
|
|
11
|
+
import { parse_response_error, ui_fetch } from './ui_fetch.js';
|
|
12
|
+
export class AuditLogState extends Loadable {
|
|
13
|
+
events = $state([]);
|
|
14
|
+
permit_history_events = $state([]);
|
|
15
|
+
count = $derived(this.events.length);
|
|
16
|
+
/** Whether the SSE stream is currently connected. */
|
|
17
|
+
connected = $state(false);
|
|
18
|
+
/** The highest `seq` seen — used for gap fill on reconnection. */
|
|
19
|
+
#last_seq = null;
|
|
20
|
+
/** Active EventSource instance. */
|
|
21
|
+
#event_source = null;
|
|
22
|
+
/** Path to the SSE stream endpoint. */
|
|
23
|
+
#stream_url;
|
|
24
|
+
constructor(stream_url = '/api/admin/audit-log/stream') {
|
|
25
|
+
super();
|
|
26
|
+
this.#stream_url = stream_url;
|
|
27
|
+
}
|
|
28
|
+
async fetch(options) {
|
|
29
|
+
await this.run(async () => {
|
|
30
|
+
const params = new URLSearchParams();
|
|
31
|
+
if (options?.event_type)
|
|
32
|
+
params.set('event_type', options.event_type);
|
|
33
|
+
if (options?.account_id)
|
|
34
|
+
params.set('account_id', options.account_id);
|
|
35
|
+
if (options?.limit != null)
|
|
36
|
+
params.set('limit', String(options.limit));
|
|
37
|
+
if (options?.offset != null)
|
|
38
|
+
params.set('offset', String(options.offset));
|
|
39
|
+
const qs = params.toString();
|
|
40
|
+
const url = `/api/admin/audit-log${qs ? `?${qs}` : ''}`;
|
|
41
|
+
const response = await ui_fetch(url);
|
|
42
|
+
if (!response.ok) {
|
|
43
|
+
throw new Error(await parse_response_error(response, 'Failed to fetch audit log'));
|
|
44
|
+
}
|
|
45
|
+
const data = await response.json();
|
|
46
|
+
this.events = data.events ?? [];
|
|
47
|
+
// track the highest seq for gap fill
|
|
48
|
+
this.#update_last_seq(this.events);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
async fetch_permit_history(limit, offset) {
|
|
52
|
+
await this.run(async () => {
|
|
53
|
+
const params = new URLSearchParams();
|
|
54
|
+
if (limit != null)
|
|
55
|
+
params.set('limit', String(limit));
|
|
56
|
+
if (offset != null)
|
|
57
|
+
params.set('offset', String(offset));
|
|
58
|
+
const qs = params.toString();
|
|
59
|
+
const url = `/api/admin/audit-log/permit-history${qs ? `?${qs}` : ''}`;
|
|
60
|
+
const response = await ui_fetch(url);
|
|
61
|
+
if (!response.ok) {
|
|
62
|
+
throw new Error(await parse_response_error(response, 'Failed to fetch permit history'));
|
|
63
|
+
}
|
|
64
|
+
const data = await response.json();
|
|
65
|
+
this.permit_history_events = data.events ?? [];
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Connect to the SSE stream for realtime audit events.
|
|
70
|
+
*
|
|
71
|
+
* New events are prepended to `events`. EventSource auto-reconnects on
|
|
72
|
+
* transient errors; `since_seq` fills gaps on reconnection.
|
|
73
|
+
*
|
|
74
|
+
* @returns cleanup function that closes the connection
|
|
75
|
+
*/
|
|
76
|
+
subscribe() {
|
|
77
|
+
this.disconnect();
|
|
78
|
+
const source = new EventSource(this.#stream_url);
|
|
79
|
+
this.#event_source = source;
|
|
80
|
+
source.onopen = () => {
|
|
81
|
+
if (DEV)
|
|
82
|
+
console.log('[audit_log_sse] connected');
|
|
83
|
+
this.connected = true;
|
|
84
|
+
// fill any gap from reconnection
|
|
85
|
+
if (this.#last_seq != null) {
|
|
86
|
+
void this.#fill_gap(this.#last_seq);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
source.onmessage = (e) => {
|
|
90
|
+
try {
|
|
91
|
+
const notification = JSON.parse(e.data);
|
|
92
|
+
const raw = notification.params;
|
|
93
|
+
if (DEV)
|
|
94
|
+
console.log('[audit_log_sse]', notification.method, raw);
|
|
95
|
+
// normalize SSE events to include username fields
|
|
96
|
+
const event = {
|
|
97
|
+
...raw,
|
|
98
|
+
username: null,
|
|
99
|
+
target_username: null,
|
|
100
|
+
};
|
|
101
|
+
// prepend — newest first, matching the fetch sort order
|
|
102
|
+
this.events = [event, ...this.events];
|
|
103
|
+
this.#last_seq = Math.max(this.#last_seq ?? 0, event.seq);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// ignore malformed messages
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
source.onerror = () => {
|
|
110
|
+
if (DEV)
|
|
111
|
+
console.log('[audit_log_sse] error, readyState:', source.readyState);
|
|
112
|
+
this.connected = source.readyState === EventSource.OPEN;
|
|
113
|
+
};
|
|
114
|
+
return () => this.disconnect();
|
|
115
|
+
}
|
|
116
|
+
/** Close the SSE connection. */
|
|
117
|
+
disconnect() {
|
|
118
|
+
if (this.#event_source) {
|
|
119
|
+
this.#event_source.close();
|
|
120
|
+
this.#event_source = null;
|
|
121
|
+
}
|
|
122
|
+
this.connected = false;
|
|
123
|
+
}
|
|
124
|
+
/** Fetch events missed during disconnection, keyed by `since_seq`. */
|
|
125
|
+
async #fill_gap(since_seq) {
|
|
126
|
+
try {
|
|
127
|
+
const url = `/api/admin/audit-log?since_seq=${since_seq}&limit=200`;
|
|
128
|
+
const response = await ui_fetch(url);
|
|
129
|
+
if (!response.ok)
|
|
130
|
+
return;
|
|
131
|
+
const data = await response.json();
|
|
132
|
+
const gap_events = data.events ?? [];
|
|
133
|
+
if (gap_events.length === 0)
|
|
134
|
+
return;
|
|
135
|
+
// merge — deduplicate by id, keep newest-first order
|
|
136
|
+
const existing_ids = new Set(this.events.map((e) => e.id));
|
|
137
|
+
const new_events = gap_events.filter((e) => !existing_ids.has(e.id));
|
|
138
|
+
if (new_events.length > 0) {
|
|
139
|
+
this.events = [...new_events, ...this.events];
|
|
140
|
+
this.#update_last_seq(new_events);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// gap fill is best-effort
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/** Update `#last_seq` from an array of events. */
|
|
148
|
+
#update_last_seq(events) {
|
|
149
|
+
for (const event of events) {
|
|
150
|
+
this.#last_seq = Math.max(this.#last_seq ?? 0, event.seq);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reactive state for cookie-based authentication.
|
|
3
|
+
*
|
|
4
|
+
* SPA auth pattern: prerendered static HTML served by Hono, no SvelteKit
|
|
5
|
+
* server for SSR sessions. On load, fetches `GET /api/account/status` which
|
|
6
|
+
* returns the current account (200) or 401 with optional `bootstrap_available`.
|
|
7
|
+
* Login sends username + password once, then a signed httpOnly cookie handles
|
|
8
|
+
* all subsequent requests.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```svelte
|
|
12
|
+
* <script lang="ts">
|
|
13
|
+
* import {AuthState, auth_state_context} from '@fuzdev/fuz_app/ui/auth_state.svelte.js';
|
|
14
|
+
*
|
|
15
|
+
* const auth = new AuthState();
|
|
16
|
+
* auth_state_context.set(auth);
|
|
17
|
+
* auth.check_session();
|
|
18
|
+
* </script>
|
|
19
|
+
*
|
|
20
|
+
* {#if auth.verifying}
|
|
21
|
+
* <p>checking session…</p>
|
|
22
|
+
* {:else if auth.needs_bootstrap}
|
|
23
|
+
* <BootstrapForm />
|
|
24
|
+
* {:else if !auth.verified}
|
|
25
|
+
* <LoginForm />
|
|
26
|
+
* {:else}
|
|
27
|
+
* <p>logged in as {auth.account?.username}</p>
|
|
28
|
+
* <button onclick={() => auth.logout()}>logout</button>
|
|
29
|
+
* {/if}
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @module
|
|
33
|
+
*/
|
|
34
|
+
import { type Permit, type SessionAccount } from '../auth/account_schema.js';
|
|
35
|
+
export declare class AuthState {
|
|
36
|
+
verifying: boolean;
|
|
37
|
+
verified: boolean;
|
|
38
|
+
verify_error: string | null;
|
|
39
|
+
account: SessionAccount | null;
|
|
40
|
+
permits: Array<Permit>;
|
|
41
|
+
readonly active_permits: Array<Permit>;
|
|
42
|
+
readonly roles: Array<string>;
|
|
43
|
+
/** True when bootstrap is available (no accounts exist yet). */
|
|
44
|
+
needs_bootstrap: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Check auth state and bootstrap availability.
|
|
47
|
+
*
|
|
48
|
+
* Fetches `GET /api/account/status` — returns account info (200) or
|
|
49
|
+
* 401 with optional `bootstrap_available` flag.
|
|
50
|
+
* Called on init, and after login/bootstrap to refresh state.
|
|
51
|
+
*/
|
|
52
|
+
check_session(): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Log in with username and password.
|
|
55
|
+
*
|
|
56
|
+
* @returns `true` if login succeeded, `false` otherwise
|
|
57
|
+
*/
|
|
58
|
+
login(username: string, password: string): Promise<boolean>;
|
|
59
|
+
/**
|
|
60
|
+
* Bootstrap the first keeper account.
|
|
61
|
+
*
|
|
62
|
+
* @returns `true` if bootstrap succeeded, `false` otherwise
|
|
63
|
+
*/
|
|
64
|
+
bootstrap(token: string, username: string, password: string): Promise<boolean>;
|
|
65
|
+
/**
|
|
66
|
+
* Sign up with an invite.
|
|
67
|
+
*
|
|
68
|
+
* @returns `true` if signup succeeded, `false` otherwise
|
|
69
|
+
*/
|
|
70
|
+
signup(username: string, password: string, email?: string): Promise<boolean>;
|
|
71
|
+
/**
|
|
72
|
+
* Log out by clearing the session cookie.
|
|
73
|
+
*/
|
|
74
|
+
logout(): Promise<void>;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Svelte context for `AuthState`.
|
|
78
|
+
* Use `auth_state_context.set(state)` in the provider and `auth_state_context.get()` to access.
|
|
79
|
+
*/
|
|
80
|
+
export declare const auth_state_context: {
|
|
81
|
+
get: (error_message?: string) => AuthState;
|
|
82
|
+
get_maybe: () => AuthState | undefined;
|
|
83
|
+
set: (value: AuthState) => AuthState;
|
|
84
|
+
};
|
|
85
|
+
//# sourceMappingURL=auth_state.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth_state.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/auth_state.svelte.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAMH,OAAO,EAAC,KAAK,MAAM,EAAoB,KAAK,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAE7F,qBAAa,SAAS;IACrB,SAAS,UAAiB;IAC1B,QAAQ,UAAiB;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAgB;IAC3C,OAAO,EAAE,cAAc,GAAG,IAAI,CAAgB;IAC9C,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAc;IACpC,QAAQ,CAAC,cAAc,EAAE,KAAK,CAAC,MAAM,CAAC,CAEpC;IACF,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAoD;IAEjF,gEAAgE;IAChE,eAAe,UAAiB;IAEhC;;;;;;OAMG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IA4BpC;;;;OAIG;IACG,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAwCjE;;;;OAIG;IACG,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiCpF;;;;OAIG;IACG,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA4ClF;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAU7B;AAED;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;;;CAA8B,CAAC"}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reactive state for cookie-based authentication.
|
|
3
|
+
*
|
|
4
|
+
* SPA auth pattern: prerendered static HTML served by Hono, no SvelteKit
|
|
5
|
+
* server for SSR sessions. On load, fetches `GET /api/account/status` which
|
|
6
|
+
* returns the current account (200) or 401 with optional `bootstrap_available`.
|
|
7
|
+
* Login sends username + password once, then a signed httpOnly cookie handles
|
|
8
|
+
* all subsequent requests.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```svelte
|
|
12
|
+
* <script lang="ts">
|
|
13
|
+
* import {AuthState, auth_state_context} from '@fuzdev/fuz_app/ui/auth_state.svelte.js';
|
|
14
|
+
*
|
|
15
|
+
* const auth = new AuthState();
|
|
16
|
+
* auth_state_context.set(auth);
|
|
17
|
+
* auth.check_session();
|
|
18
|
+
* </script>
|
|
19
|
+
*
|
|
20
|
+
* {#if auth.verifying}
|
|
21
|
+
* <p>checking session…</p>
|
|
22
|
+
* {:else if auth.needs_bootstrap}
|
|
23
|
+
* <BootstrapForm />
|
|
24
|
+
* {:else if !auth.verified}
|
|
25
|
+
* <LoginForm />
|
|
26
|
+
* {:else}
|
|
27
|
+
* <p>logged in as {auth.account?.username}</p>
|
|
28
|
+
* <button onclick={() => auth.logout()}>logout</button>
|
|
29
|
+
* {/if}
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @module
|
|
33
|
+
*/
|
|
34
|
+
import { create_context } from '@fuzdev/fuz_ui/context_helpers.js';
|
|
35
|
+
import { ui_fetch } from './ui_fetch.js';
|
|
36
|
+
import { is_permit_active } from '../auth/account_schema.js';
|
|
37
|
+
export class AuthState {
|
|
38
|
+
verifying = $state(false);
|
|
39
|
+
verified = $state(false);
|
|
40
|
+
verify_error = $state(null);
|
|
41
|
+
account = $state(null);
|
|
42
|
+
permits = $state([]);
|
|
43
|
+
active_permits = $derived(this.permits.filter((p) => is_permit_active(p)));
|
|
44
|
+
roles = $derived(this.active_permits.map((p) => p.role));
|
|
45
|
+
/** True when bootstrap is available (no accounts exist yet). */
|
|
46
|
+
needs_bootstrap = $state(false);
|
|
47
|
+
/**
|
|
48
|
+
* Check auth state and bootstrap availability.
|
|
49
|
+
*
|
|
50
|
+
* Fetches `GET /api/account/status` — returns account info (200) or
|
|
51
|
+
* 401 with optional `bootstrap_available` flag.
|
|
52
|
+
* Called on init, and after login/bootstrap to refresh state.
|
|
53
|
+
*/
|
|
54
|
+
async check_session() {
|
|
55
|
+
this.verifying = true;
|
|
56
|
+
try {
|
|
57
|
+
const response = await ui_fetch('/api/account/status');
|
|
58
|
+
if (response.ok) {
|
|
59
|
+
const data = await response.json();
|
|
60
|
+
this.verified = true;
|
|
61
|
+
this.account = data.account ?? null;
|
|
62
|
+
this.permits = data.permits ?? [];
|
|
63
|
+
this.needs_bootstrap = false;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
this.verified = false;
|
|
67
|
+
if (response.status === 401) {
|
|
68
|
+
try {
|
|
69
|
+
const data = await response.json();
|
|
70
|
+
this.needs_bootstrap = data.bootstrap_available ?? false;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// non-JSON error response
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
this.verified = false;
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
this.verifying = false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Log in with username and password.
|
|
87
|
+
*
|
|
88
|
+
* @returns `true` if login succeeded, `false` otherwise
|
|
89
|
+
*/
|
|
90
|
+
async login(username, password) {
|
|
91
|
+
this.verifying = true;
|
|
92
|
+
this.verify_error = null;
|
|
93
|
+
try {
|
|
94
|
+
const response = await ui_fetch('/api/account/login', {
|
|
95
|
+
method: 'POST',
|
|
96
|
+
headers: { 'Content-Type': 'application/json' },
|
|
97
|
+
body: JSON.stringify({ username, password }),
|
|
98
|
+
});
|
|
99
|
+
if (response.ok) {
|
|
100
|
+
this.verified = true;
|
|
101
|
+
// Fetch account info
|
|
102
|
+
await this.check_session();
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
if (response.status === 429) {
|
|
106
|
+
try {
|
|
107
|
+
const data = await response.json();
|
|
108
|
+
const minutes = Math.ceil((data.retry_after ?? 60) / 60);
|
|
109
|
+
this.verify_error = `Too many attempts. Try again in ${minutes} minute${minutes === 1 ? '' : 's'}.`;
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
this.verify_error = 'Too many attempts. Try again later.';
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
else if (response.status === 401) {
|
|
116
|
+
this.verify_error = 'Invalid credentials';
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
this.verify_error = `Error: ${response.status}`;
|
|
120
|
+
}
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
catch (e) {
|
|
124
|
+
this.verify_error = e instanceof Error ? e.message : 'Connection failed';
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
this.verifying = false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Bootstrap the first keeper account.
|
|
133
|
+
*
|
|
134
|
+
* @returns `true` if bootstrap succeeded, `false` otherwise
|
|
135
|
+
*/
|
|
136
|
+
async bootstrap(token, username, password) {
|
|
137
|
+
this.verifying = true;
|
|
138
|
+
this.verify_error = null;
|
|
139
|
+
try {
|
|
140
|
+
const response = await ui_fetch('/api/account/bootstrap', {
|
|
141
|
+
method: 'POST',
|
|
142
|
+
headers: { 'Content-Type': 'application/json' },
|
|
143
|
+
body: JSON.stringify({ token, username, password }),
|
|
144
|
+
});
|
|
145
|
+
if (response.ok) {
|
|
146
|
+
this.verified = true;
|
|
147
|
+
this.needs_bootstrap = false;
|
|
148
|
+
await this.check_session();
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
const data = await response.json();
|
|
153
|
+
this.verify_error = data.error ?? `Error: ${response.status}`;
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
this.verify_error = `Error: ${response.status}`;
|
|
157
|
+
}
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
catch (e) {
|
|
161
|
+
this.verify_error = e instanceof Error ? e.message : 'Connection failed';
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
finally {
|
|
165
|
+
this.verifying = false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Sign up with an invite.
|
|
170
|
+
*
|
|
171
|
+
* @returns `true` if signup succeeded, `false` otherwise
|
|
172
|
+
*/
|
|
173
|
+
async signup(username, password, email) {
|
|
174
|
+
this.verifying = true;
|
|
175
|
+
this.verify_error = null;
|
|
176
|
+
try {
|
|
177
|
+
const body = { username, password };
|
|
178
|
+
if (email)
|
|
179
|
+
body.email = email;
|
|
180
|
+
const response = await ui_fetch('/api/account/signup', {
|
|
181
|
+
method: 'POST',
|
|
182
|
+
headers: { 'Content-Type': 'application/json' },
|
|
183
|
+
body: JSON.stringify(body),
|
|
184
|
+
});
|
|
185
|
+
if (response.ok) {
|
|
186
|
+
this.verified = true;
|
|
187
|
+
await this.check_session();
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
if (response.status === 429) {
|
|
191
|
+
try {
|
|
192
|
+
const data = await response.json();
|
|
193
|
+
const minutes = Math.ceil((data.retry_after ?? 60) / 60);
|
|
194
|
+
this.verify_error = `Too many attempts. Try again in ${minutes} minute${minutes === 1 ? '' : 's'}.`;
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
this.verify_error = 'Too many attempts. Try again later.';
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
else if (response.status === 403) {
|
|
201
|
+
this.verify_error = 'No matching invite found for these credentials.';
|
|
202
|
+
}
|
|
203
|
+
else if (response.status === 409) {
|
|
204
|
+
this.verify_error = 'Username or email is already in use.';
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
this.verify_error = `Error: ${response.status}`;
|
|
208
|
+
}
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
catch (e) {
|
|
212
|
+
this.verify_error = e instanceof Error ? e.message : 'Connection failed';
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
finally {
|
|
216
|
+
this.verifying = false;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Log out by clearing the session cookie.
|
|
221
|
+
*/
|
|
222
|
+
async logout() {
|
|
223
|
+
try {
|
|
224
|
+
await ui_fetch('/api/account/logout', { method: 'POST' });
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
// Best-effort — clear local state regardless
|
|
228
|
+
}
|
|
229
|
+
this.verified = false;
|
|
230
|
+
this.account = null;
|
|
231
|
+
this.permits = [];
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Svelte context for `AuthState`.
|
|
236
|
+
* Use `auth_state_context.set(state)` in the provider and `auth_state_context.get()` to access.
|
|
237
|
+
*/
|
|
238
|
+
export const auth_state_context = create_context();
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types and constants for the `Datatable` component.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
/** Default minimum column width in pixels. */
|
|
7
|
+
export declare const DATATABLE_MIN_COLUMN_WIDTH = 50;
|
|
8
|
+
/** Default initial column width in pixels. */
|
|
9
|
+
export declare const DATATABLE_COLUMN_WIDTH_DEFAULT = 120;
|
|
10
|
+
/**
|
|
11
|
+
* Column definition for a `Datatable`.
|
|
12
|
+
*/
|
|
13
|
+
export interface DatatableColumn<T = unknown> {
|
|
14
|
+
/** Row data accessor key. */
|
|
15
|
+
key: string & keyof T;
|
|
16
|
+
/** Header label text. */
|
|
17
|
+
label: string;
|
|
18
|
+
/** Initial column width in pixels. */
|
|
19
|
+
width?: number;
|
|
20
|
+
/** Minimum column width in pixels. */
|
|
21
|
+
min_width?: number;
|
|
22
|
+
/** Format a cell value for display. Falls back to `format_value` when absent. */
|
|
23
|
+
format?: (value: T[keyof T], row: T) => string;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=datatable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"datatable.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/datatable.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,8CAA8C;AAC9C,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAE7C,8CAA8C;AAC9C,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,eAAe,CAAC,CAAC,GAAG,OAAO;IAC3C,6BAA6B;IAC7B,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;IACtB,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,KAAK,MAAM,CAAC;CAC/C"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types and constants for the `Datatable` component.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
/** Default minimum column width in pixels. */
|
|
7
|
+
export const DATATABLE_MIN_COLUMN_WIDTH = 50;
|
|
8
|
+
/** Default initial column width in pixels. */
|
|
9
|
+
export const DATATABLE_COLUMN_WIDTH_DEFAULT = 120;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelte action that makes Enter advance to the next focusable form element
|
|
3
|
+
* (inputs and buttons), or activate the element if it's already the last one.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```svelte
|
|
7
|
+
* <form {@attach enter_advance()}>
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
export declare const enter_advance: () => ((form: HTMLFormElement) => () => void);
|
|
13
|
+
//# sourceMappingURL=enter_advance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enter_advance.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/enter_advance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,eAAO,MAAM,aAAa,QAAO,CAAC,CAAC,IAAI,EAAE,eAAe,KAAK,MAAM,IAAI,CAiBtE,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelte action that makes Enter advance to the next focusable form element
|
|
3
|
+
* (inputs and buttons), or activate the element if it's already the last one.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```svelte
|
|
7
|
+
* <form {@attach enter_advance()}>
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
const FOCUSABLE_SELECTOR = 'input:not(:disabled), button:not(:disabled)';
|
|
13
|
+
export const enter_advance = () => {
|
|
14
|
+
return (form) => {
|
|
15
|
+
const handle_keydown = (e) => {
|
|
16
|
+
if (e.key !== 'Enter')
|
|
17
|
+
return;
|
|
18
|
+
if (!(e.target instanceof HTMLInputElement))
|
|
19
|
+
return;
|
|
20
|
+
const elements = Array.from(form.querySelectorAll(FOCUSABLE_SELECTOR));
|
|
21
|
+
const index = elements.indexOf(e.target);
|
|
22
|
+
if (index < 0)
|
|
23
|
+
return;
|
|
24
|
+
e.preventDefault();
|
|
25
|
+
elements[(index + 1) % elements.length].focus();
|
|
26
|
+
};
|
|
27
|
+
form.addEventListener('keydown', handle_keydown);
|
|
28
|
+
return () => form.removeEventListener('keydown', handle_keydown);
|
|
29
|
+
};
|
|
30
|
+
};
|