@fuzdev/fuz_app 0.87.0 → 0.89.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.
Files changed (85) hide show
  1. package/dist/actions/action_rpc.js +1 -1
  2. package/dist/actions/register_action_ws.js +1 -1
  3. package/dist/auth/CLAUDE.md +15 -0
  4. package/dist/auth/account_actions.js +1 -1
  5. package/dist/auth/account_route_schema.d.ts +152 -0
  6. package/dist/auth/account_route_schema.d.ts.map +1 -0
  7. package/dist/auth/account_route_schema.js +147 -0
  8. package/dist/auth/account_routes.d.ts +18 -83
  9. package/dist/auth/account_routes.d.ts.map +1 -1
  10. package/dist/auth/account_routes.js +28 -115
  11. package/dist/auth/audit_log_route_schema.d.ts +32 -0
  12. package/dist/auth/audit_log_route_schema.d.ts.map +1 -0
  13. package/dist/auth/audit_log_route_schema.js +36 -0
  14. package/dist/auth/audit_log_routes.d.ts.map +1 -1
  15. package/dist/auth/audit_log_routes.js +2 -12
  16. package/dist/auth/bearer_auth.js +1 -1
  17. package/dist/auth/bootstrap_route_schema.d.ts +85 -0
  18. package/dist/auth/bootstrap_route_schema.d.ts.map +1 -0
  19. package/dist/auth/bootstrap_route_schema.js +56 -0
  20. package/dist/auth/bootstrap_routes.d.ts +0 -20
  21. package/dist/auth/bootstrap_routes.d.ts.map +1 -1
  22. package/dist/auth/bootstrap_routes.js +4 -35
  23. package/dist/auth/signup_route_schema.d.ts +53 -0
  24. package/dist/auth/signup_route_schema.d.ts.map +1 -0
  25. package/dist/auth/signup_route_schema.js +59 -0
  26. package/dist/auth/signup_routes.d.ts +0 -26
  27. package/dist/auth/signup_routes.d.ts.map +1 -1
  28. package/dist/auth/signup_routes.js +8 -40
  29. package/dist/http/CLAUDE.md +2 -1
  30. package/dist/http/client_ip.d.ts +15 -0
  31. package/dist/http/client_ip.d.ts.map +1 -0
  32. package/dist/http/client_ip.js +13 -0
  33. package/dist/http/proxy.d.ts +0 -7
  34. package/dist/http/proxy.d.ts.map +1 -1
  35. package/dist/http/proxy.js +0 -7
  36. package/dist/realtime/sse.d.ts +0 -2
  37. package/dist/realtime/sse.d.ts.map +1 -1
  38. package/dist/realtime/sse.js +1 -2
  39. package/dist/realtime/sse_constants.d.ts +17 -0
  40. package/dist/realtime/sse_constants.d.ts.map +1 -0
  41. package/dist/realtime/sse_constants.js +16 -0
  42. package/dist/testing/CLAUDE.md +9 -1
  43. package/dist/testing/admin_integration.d.ts.map +1 -1
  44. package/dist/testing/admin_integration.js +1 -1
  45. package/dist/testing/app_server.d.ts +0 -15
  46. package/dist/testing/app_server.d.ts.map +1 -1
  47. package/dist/testing/app_server.js +1 -15
  48. package/dist/testing/audit_completeness.d.ts.map +1 -1
  49. package/dist/testing/audit_completeness.js +1 -1
  50. package/dist/testing/cross_backend/body_size_smuggling.d.ts +12 -0
  51. package/dist/testing/cross_backend/body_size_smuggling.d.ts.map +1 -1
  52. package/dist/testing/cross_backend/body_size_smuggling.js +68 -41
  53. package/dist/testing/cross_backend/capabilities.d.ts +29 -0
  54. package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
  55. package/dist/testing/cross_backend/capabilities.js +15 -0
  56. package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
  57. package/dist/testing/cross_backend/default_backend_configs.js +13 -0
  58. package/dist/testing/cross_backend/default_spine_surface.d.ts.map +1 -1
  59. package/dist/testing/cross_backend/default_spine_surface.js +1 -0
  60. package/dist/testing/cross_backend/in_process_setup.d.ts +143 -0
  61. package/dist/testing/cross_backend/in_process_setup.d.ts.map +1 -0
  62. package/dist/testing/cross_backend/in_process_setup.js +166 -0
  63. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
  64. package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +13 -6
  65. package/dist/testing/cross_backend/setup.d.ts +31 -140
  66. package/dist/testing/cross_backend/setup.d.ts.map +1 -1
  67. package/dist/testing/cross_backend/setup.js +11 -171
  68. package/dist/testing/cross_backend/sse_round_trip.js +1 -1
  69. package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
  70. package/dist/testing/cross_backend/testing_reset_actions.js +2 -1
  71. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
  72. package/dist/testing/cross_backend/ts_spine_backend_config.js +16 -1
  73. package/dist/testing/integration.d.ts.map +1 -1
  74. package/dist/testing/integration.js +15 -18
  75. package/dist/testing/middleware.d.ts.map +1 -1
  76. package/dist/testing/middleware.js +2 -1
  77. package/dist/testing/sse_round_trip.d.ts +1 -1
  78. package/dist/testing/sse_round_trip.d.ts.map +1 -1
  79. package/dist/testing/sse_round_trip.js +1 -1
  80. package/dist/testing/stubs.d.ts.map +1 -1
  81. package/dist/testing/stubs.js +7 -9
  82. package/dist/testing/test_credentials.d.ts +23 -0
  83. package/dist/testing/test_credentials.d.ts.map +1 -0
  84. package/dist/testing/test_credentials.js +22 -0
  85. package/package.json +4 -4
@@ -14,7 +14,7 @@
14
14
  import { z } from 'zod';
15
15
  import { DEV } from 'esm-env';
16
16
  import {} from '../http/route_spec.js';
17
- import { get_client_ip } from '../http/proxy.js';
17
+ import { get_client_ip } from '../http/client_ip.js';
18
18
  import { get_request_context, } from '../auth/request_context.js';
19
19
  import { ACCOUNT_ID_KEY, CREDENTIAL_TYPE_KEY, TEST_CONTEXT_PRESET_KEY, } from '../hono_context.js';
20
20
  import { compile_action_registry } from './compile_action_registry.js';
@@ -30,7 +30,7 @@ import { wait } from '@fuzdev/fuz_util/async.js';
30
30
  import { Logger } from '@fuzdev/fuz_util/log.js';
31
31
  import { get_request_context, require_request_context, } from '../auth/request_context.js';
32
32
  import { hash_session_token } from '../auth/session_queries.js';
33
- import { get_client_ip } from '../http/proxy.js';
33
+ import { get_client_ip } from '../http/client_ip.js';
34
34
  import { flush_pending_effects, flush_post_commit_effects } from '../http/pending_effects.js';
35
35
  import { jsonrpc_error_messages } from '../http/jsonrpc_errors.js';
36
36
  import { create_jsonrpc_error_response, create_jsonrpc_notification, to_jsonrpc_message_id, to_jsonrpc_params, is_jsonrpc_request, } from '../http/jsonrpc_helpers.js';
@@ -121,6 +121,21 @@ they track the same config. Sample via `get_*`; `reset_*` are test-only.
121
121
  - `auth/audit_log_routes.ts` — optional `GET /audit/stream` (SSE); list/history are on the RPC surface.
122
122
  - `auth/auth_guard_resolver.ts` — `fuz_auth_guard_resolver` injected into `apply_route_specs` so the framework stays auth-agnostic.
123
123
 
124
+ **Hono-free route shapes.** Each cookie/SSE-coupled route module has a sibling
125
+ `*_route_schema.ts` (`account_route_schema.ts`, `signup_route_schema.ts`,
126
+ `audit_log_route_schema.ts`, `bootstrap_route_schema.ts`) holding the I/O
127
+ schemas **and** the route _shapes_ (`Omit<RouteSpec, 'handler'>` —
128
+ method/path/auth/io/errors, via `create_*_route_shapes(options)` or a static
129
+ `*_route_shape` const). The `create_*_route_specs` factories spread a shape and
130
+ attach the live (hono-coupled) handler; a cross-process surface builder spreads
131
+ the same shape with a stub handler (surface generation never runs handlers).
132
+ Single source of truth — the shape can't drift between the live route and the
133
+ attack surface — and a backend-spawning consumer assembling its surface imports
134
+ the shapes without dragging `hono/cookie` / `hono/streaming` (and the optional
135
+ `hono` peer) onto a Rust-only cross-process suite. Shared route-limit constants
136
+ (`DEFAULT_MAX_SESSIONS` / `_TOKENS`) live in `account_route_schema.ts` for the
137
+ same reason (the RPC `account_actions` reads `DEFAULT_MAX_TOKENS`).
138
+
124
139
  **`POST /login` timing floor.** Login 401s are floored to
125
140
  `DEFAULT_LOGIN_FAIL_FLOOR_MS` (250ms) + uniform jitter (±25ms) via
126
141
  `Promise.all(work, setTimeout)` so observed time is `max(work, delay)` and
@@ -27,7 +27,7 @@ import { to_session_account } from './account_schema.js';
27
27
  import { query_session_list_for_account, query_session_revoke_for_account, query_session_revoke_all_for_account, } from './session_queries.js';
28
28
  import { query_api_token_enforce_limit, query_api_token_list_for_account, query_create_api_token, query_revoke_api_token_for_account, } from './api_token_queries.js';
29
29
  import { generate_api_token } from './api_token.js';
30
- import { DEFAULT_MAX_TOKENS } from './account_routes.js';
30
+ import { DEFAULT_MAX_TOKENS } from './account_route_schema.js';
31
31
  import { account_verify_action_spec, account_session_list_action_spec, account_session_revoke_action_spec, account_session_revoke_all_action_spec, account_token_create_action_spec, account_token_list_action_spec, account_token_revoke_action_spec, } from './account_action_specs.js';
32
32
  /**
33
33
  * Create the self-service account RPC actions.
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Hono-free wire schemas + route shapes for the account REST routes.
3
+ *
4
+ * Split from `account_routes.ts` (whose handlers pull `hono/cookie` via
5
+ * `session_middleware`) so cross-process test suites can build the account
6
+ * route shapes — and assert on the `POST /login` / `GET /api/account/status`
7
+ * response shapes — without dragging the in-process Hono session handler, and
8
+ * its optional `hono` peer, onto a backend-spawning consumer. `account_routes.ts`
9
+ * imports these back and attaches the live handlers; single source of truth
10
+ * for the wire shape.
11
+ *
12
+ * @module
13
+ */
14
+ import { z } from 'zod';
15
+ import type { RouteSpec } from '../http/route_spec.js';
16
+ /** Input for `GET /api/account/status`. No parameters — caller is the subject. */
17
+ export declare const AccountStatusInput: z.ZodNull;
18
+ export type AccountStatusInput = z.infer<typeof AccountStatusInput>;
19
+ /** Output for `GET /api/account/status`. */
20
+ export declare const AccountStatusOutput: z.ZodObject<{
21
+ account: z.ZodObject<{
22
+ id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
23
+ username: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
24
+ email: z.ZodNullable<z.ZodEmail>;
25
+ email_verified: z.ZodBoolean;
26
+ created_at: z.ZodString;
27
+ }, z.core.$strict>;
28
+ actor: z.ZodNullable<z.ZodObject<{
29
+ id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
30
+ name: z.ZodString;
31
+ }, z.core.$strict>>;
32
+ role_grants: z.ZodArray<z.ZodObject<{
33
+ id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
34
+ role: z.ZodString;
35
+ scope_kind: z.ZodNullable<z.ZodString>;
36
+ scope_id: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
37
+ created_at: z.ZodString;
38
+ expires_at: z.ZodNullable<z.ZodString>;
39
+ granted_by: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
40
+ }, z.core.$strict>>;
41
+ }, z.core.$strict>;
42
+ export type AccountStatusOutput = z.infer<typeof AccountStatusOutput>;
43
+ /** Error body for `GET /api/account/status` on the unauthenticated path. */
44
+ export declare const AccountStatusUnauthenticatedError: z.ZodObject<{
45
+ error: z.ZodLiteral<"authentication_required">;
46
+ bootstrap_available: z.ZodOptional<z.ZodBoolean>;
47
+ }, z.core.$loose>;
48
+ export type AccountStatusUnauthenticatedError = z.infer<typeof AccountStatusUnauthenticatedError>;
49
+ /** Input for `POST /login`. Accepts a username or email in the `username` field. */
50
+ export declare const LoginInput: z.ZodObject<{
51
+ username: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
52
+ password: z.ZodString;
53
+ }, z.core.$strict>;
54
+ export type LoginInput = z.infer<typeof LoginInput>;
55
+ /** Output for `POST /login`. Session cookie is the operative side effect. */
56
+ export declare const LoginOutput: z.ZodObject<{
57
+ ok: z.ZodLiteral<true>;
58
+ }, z.core.$strict>;
59
+ export type LoginOutput = z.infer<typeof LoginOutput>;
60
+ /** Input for `POST /logout`. Session identity flows through the cookie. */
61
+ export declare const LogoutInput: z.ZodNull;
62
+ export type LogoutInput = z.infer<typeof LogoutInput>;
63
+ /** Output for `POST /logout`. Includes the revoked account's username for UI redraw. */
64
+ export declare const LogoutOutput: z.ZodObject<{
65
+ ok: z.ZodLiteral<true>;
66
+ username: z.ZodString;
67
+ }, z.core.$strict>;
68
+ export type LogoutOutput = z.infer<typeof LogoutOutput>;
69
+ /** Input for `POST /password`. `current_password` is minimally validated; `new_password` enforces the full policy. */
70
+ export declare const PasswordChangeInput: z.ZodObject<{
71
+ current_password: z.ZodString;
72
+ new_password: z.ZodString;
73
+ }, z.core.$strict>;
74
+ export type PasswordChangeInput = z.infer<typeof PasswordChangeInput>;
75
+ /** Output for `POST /password`. Counts are returned so the UI can summarize the revoke-all cascade. */
76
+ export declare const PasswordChangeOutput: z.ZodObject<{
77
+ ok: z.ZodLiteral<true>;
78
+ sessions_revoked: z.ZodNumber;
79
+ tokens_revoked: z.ZodNumber;
80
+ }, z.core.$strict>;
81
+ export type PasswordChangeOutput = z.infer<typeof PasswordChangeOutput>;
82
+ /** Default maximum sessions per account. */
83
+ export declare const DEFAULT_MAX_SESSIONS = 5;
84
+ /** Default maximum API tokens per account. */
85
+ export declare const DEFAULT_MAX_TOKENS = 10;
86
+ /**
87
+ * The `GET /status` route shape minus its handler — pure hono-free data.
88
+ * `create_account_status_route_spec` spreads this and attaches the live handler
89
+ * (which reads the account id off the request context); surface generation
90
+ * spreads it with a stub handler.
91
+ *
92
+ * The path is **relative** like the sibling account shapes (`/login`,
93
+ * `/verify`), so it composes under `prefix_route_specs('/api/account', …)` into
94
+ * `/api/account/status`. `create_account_route_specs` bundles it (so every
95
+ * account surface serves `/status`, matching the Rust `account_router`);
96
+ * mirror Rust by mounting it as part of the account family, not separately.
97
+ */
98
+ export declare const account_status_route_shape: {
99
+ method: "GET";
100
+ path: string;
101
+ auth: {
102
+ account: "none";
103
+ actor: "none";
104
+ };
105
+ description: string;
106
+ input: z.ZodNull;
107
+ output: z.ZodObject<{
108
+ account: z.ZodObject<{
109
+ id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
110
+ username: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
111
+ email: z.ZodNullable<z.ZodEmail>;
112
+ email_verified: z.ZodBoolean;
113
+ created_at: z.ZodString;
114
+ }, z.core.$strict>;
115
+ actor: z.ZodNullable<z.ZodObject<{
116
+ id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
117
+ name: z.ZodString;
118
+ }, z.core.$strict>>;
119
+ role_grants: z.ZodArray<z.ZodObject<{
120
+ id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
121
+ role: z.ZodString;
122
+ scope_kind: z.ZodNullable<z.ZodString>;
123
+ scope_id: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
124
+ created_at: z.ZodString;
125
+ expires_at: z.ZodNullable<z.ZodString>;
126
+ granted_by: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
127
+ }, z.core.$strict>>;
128
+ }, z.core.$strict>;
129
+ errors: {
130
+ 401: z.ZodObject<{
131
+ error: z.ZodLiteral<"authentication_required">;
132
+ bootstrap_available: z.ZodOptional<z.ZodBoolean>;
133
+ }, z.core.$loose>;
134
+ };
135
+ };
136
+ /** Option inputs that shape the account route metadata (not its handlers). */
137
+ export interface AccountRouteShapeOptions {
138
+ /** Whether a per-account login rate limiter is wired — toggles `/password`'s `rate_limit`. */
139
+ login_account_rate_limited: boolean;
140
+ }
141
+ /**
142
+ * The four account route shapes (`/verify`, `/login`, `/logout`, `/password`)
143
+ * minus their handlers — pure hono-free data. `create_account_route_specs`
144
+ * spreads each and attaches the live handler; cross-process surface builders
145
+ * spread them with stub handlers. Single source of truth — the shapes can't
146
+ * drift between the live routes and the surface.
147
+ *
148
+ * Returns a fixed 4-tuple `[verify, login, logout, password]` so destructuring
149
+ * yields non-optional shapes under `noUncheckedIndexedAccess`.
150
+ */
151
+ export declare const create_account_route_shapes: (options: AccountRouteShapeOptions) => [Omit<RouteSpec, "handler">, Omit<RouteSpec, "handler">, Omit<RouteSpec, "handler">, Omit<RouteSpec, "handler">];
152
+ //# sourceMappingURL=account_route_schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account_route_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_route_schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAKtB,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAQrD,kFAAkF;AAClF,eAAO,MAAM,kBAAkB,WAAW,CAAC;AAC3C,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,4CAA4C;AAC5C,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;kBAI9B,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEtE,4EAA4E;AAC5E,eAAO,MAAM,iCAAiC;;;iBAG5C,CAAC;AACH,MAAM,MAAM,iCAAiC,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AAElG,oFAAoF;AACpF,eAAO,MAAM,UAAU;;;kBAGrB,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEpD,6EAA6E;AAC7E,eAAO,MAAM,WAAW;;kBAEtB,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD,2EAA2E;AAC3E,eAAO,MAAM,WAAW,WAAW,CAAC;AACpC,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD,wFAAwF;AACxF,eAAO,MAAM,YAAY;;;kBAGvB,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAExD,sHAAsH;AACtH,eAAO,MAAM,mBAAmB;;;kBAG9B,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEtE,uGAAuG;AACvG,eAAO,MAAM,oBAAoB;;;;kBAI/B,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE,4CAA4C;AAC5C,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,8CAA8C;AAC9C,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAUD,CAAC;AAEvC,8EAA8E;AAC9E,MAAM,WAAW,wBAAwB;IACxC,8FAA8F;IAC9F,0BAA0B,EAAE,OAAO,CAAC;CACpC;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,GACvC,SAAS,wBAAwB,KAC/B,CACF,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,EAC1B,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,EAC1B,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,EAC1B,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAoD1B,CAAC"}
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Hono-free wire schemas + route shapes for the account REST routes.
3
+ *
4
+ * Split from `account_routes.ts` (whose handlers pull `hono/cookie` via
5
+ * `session_middleware`) so cross-process test suites can build the account
6
+ * route shapes — and assert on the `POST /login` / `GET /api/account/status`
7
+ * response shapes — without dragging the in-process Hono session handler, and
8
+ * its optional `hono` peer, onto a backend-spawning consumer. `account_routes.ts`
9
+ * imports these back and attaches the live handlers; single source of truth
10
+ * for the wire shape.
11
+ *
12
+ * @module
13
+ */
14
+ import { z } from 'zod';
15
+ import { ActorSummaryJson, RoleGrantSummaryJson, SessionAccountJson } from './account_schema.js';
16
+ import { UsernameProvided } from '../primitive_schemas.js';
17
+ import { Password, PasswordProvided } from './password.js';
18
+ import { ERROR_AUTHENTICATION_REQUIRED, ERROR_INVALID_CREDENTIALS, ERROR_INVALID_JSON_BODY, ERROR_INVALID_REQUEST_BODY, } from '../http/error_schemas.js';
19
+ /** Input for `GET /api/account/status`. No parameters — caller is the subject. */
20
+ export const AccountStatusInput = z.null();
21
+ /** Output for `GET /api/account/status`. */
22
+ export const AccountStatusOutput = z.strictObject({
23
+ account: SessionAccountJson,
24
+ actor: ActorSummaryJson.nullable(),
25
+ role_grants: z.array(RoleGrantSummaryJson),
26
+ });
27
+ /** Error body for `GET /api/account/status` on the unauthenticated path. */
28
+ export const AccountStatusUnauthenticatedError = z.looseObject({
29
+ error: z.literal(ERROR_AUTHENTICATION_REQUIRED),
30
+ bootstrap_available: z.boolean().optional(),
31
+ });
32
+ /** Input for `POST /login`. Accepts a username or email in the `username` field. */
33
+ export const LoginInput = z.strictObject({
34
+ username: UsernameProvided,
35
+ password: PasswordProvided,
36
+ });
37
+ /** Output for `POST /login`. Session cookie is the operative side effect. */
38
+ export const LoginOutput = z.strictObject({
39
+ ok: z.literal(true),
40
+ });
41
+ /** Input for `POST /logout`. Session identity flows through the cookie. */
42
+ export const LogoutInput = z.null();
43
+ /** Output for `POST /logout`. Includes the revoked account's username for UI redraw. */
44
+ export const LogoutOutput = z.strictObject({
45
+ ok: z.literal(true),
46
+ username: z.string(),
47
+ });
48
+ /** Input for `POST /password`. `current_password` is minimally validated; `new_password` enforces the full policy. */
49
+ export const PasswordChangeInput = z.strictObject({
50
+ current_password: PasswordProvided,
51
+ new_password: Password,
52
+ });
53
+ /** Output for `POST /password`. Counts are returned so the UI can summarize the revoke-all cascade. */
54
+ export const PasswordChangeOutput = z.strictObject({
55
+ ok: z.literal(true),
56
+ sessions_revoked: z.number(),
57
+ tokens_revoked: z.number(),
58
+ });
59
+ /** Default maximum sessions per account. */
60
+ export const DEFAULT_MAX_SESSIONS = 5;
61
+ /** Default maximum API tokens per account. */
62
+ export const DEFAULT_MAX_TOKENS = 10;
63
+ /**
64
+ * The `GET /status` route shape minus its handler — pure hono-free data.
65
+ * `create_account_status_route_spec` spreads this and attaches the live handler
66
+ * (which reads the account id off the request context); surface generation
67
+ * spreads it with a stub handler.
68
+ *
69
+ * The path is **relative** like the sibling account shapes (`/login`,
70
+ * `/verify`), so it composes under `prefix_route_specs('/api/account', …)` into
71
+ * `/api/account/status`. `create_account_route_specs` bundles it (so every
72
+ * account surface serves `/status`, matching the Rust `account_router`);
73
+ * mirror Rust by mounting it as part of the account family, not separately.
74
+ */
75
+ export const account_status_route_shape = {
76
+ method: 'GET',
77
+ path: '/status',
78
+ auth: { account: 'none', actor: 'none' },
79
+ description: 'Current account info (unauthenticated: 401 with bootstrap status)',
80
+ input: AccountStatusInput,
81
+ output: AccountStatusOutput,
82
+ errors: {
83
+ 401: AccountStatusUnauthenticatedError,
84
+ },
85
+ };
86
+ /**
87
+ * The four account route shapes (`/verify`, `/login`, `/logout`, `/password`)
88
+ * minus their handlers — pure hono-free data. `create_account_route_specs`
89
+ * spreads each and attaches the live handler; cross-process surface builders
90
+ * spread them with stub handlers. Single source of truth — the shapes can't
91
+ * drift between the live routes and the surface.
92
+ *
93
+ * Returns a fixed 4-tuple `[verify, login, logout, password]` so destructuring
94
+ * yields non-optional shapes under `noUncheckedIndexedAccess`.
95
+ */
96
+ export const create_account_route_shapes = (options) => [
97
+ {
98
+ method: 'GET',
99
+ path: '/verify',
100
+ auth: { account: 'required', actor: 'none' },
101
+ description: 'Session-validity probe for nginx auth_request (empty body, 200 or 401)',
102
+ input: z.null(),
103
+ output: z.null(),
104
+ },
105
+ {
106
+ method: 'POST',
107
+ path: '/login',
108
+ auth: { account: 'none', actor: 'none' },
109
+ description: 'Exchange credentials for session',
110
+ input: LoginInput,
111
+ output: LoginOutput,
112
+ rate_limit: 'both',
113
+ errors: {
114
+ 400: z.looseObject({
115
+ error: z.enum([ERROR_INVALID_JSON_BODY, ERROR_INVALID_REQUEST_BODY]),
116
+ }),
117
+ 401: z.looseObject({ error: z.literal(ERROR_INVALID_CREDENTIALS) }),
118
+ },
119
+ },
120
+ {
121
+ method: 'POST',
122
+ path: '/logout',
123
+ // `credential_types: ['session']` — see `docs/security.md` §Credential-channel gating.
124
+ // Logout is a session-bound operation; a bearer / daemon token holds no session
125
+ // to end, so the dispatcher rejects it (403 `credential_type_required`) rather than
126
+ // returning a misleading 200 + a phantom `logout` audit row for a no-op.
127
+ auth: { account: 'required', actor: 'none', credential_types: ['session'] },
128
+ description: 'Revoke current session and clear cookie',
129
+ input: LogoutInput,
130
+ output: LogoutOutput,
131
+ },
132
+ {
133
+ method: 'POST',
134
+ path: '/password',
135
+ // `credential_types: ['session']` — see `docs/security.md` §Credential-channel gating.
136
+ auth: { account: 'required', actor: 'none', credential_types: ['session'] },
137
+ description: 'Change password (revokes all sessions and API tokens)',
138
+ input: PasswordChangeInput,
139
+ output: PasswordChangeOutput,
140
+ rate_limit: options.login_account_rate_limited ? 'both' : 'ip',
141
+ errors: {
142
+ 400: z.looseObject({
143
+ error: z.enum([ERROR_INVALID_JSON_BODY, ERROR_INVALID_REQUEST_BODY]),
144
+ }),
145
+ },
146
+ },
147
+ ];
@@ -21,53 +21,11 @@
21
21
  *
22
22
  * @module
23
23
  */
24
- import { z } from 'zod';
25
24
  import type { SessionOptions } from './session_cookie.js';
26
25
  import { type RouteSpec } from '../http/route_spec.js';
27
26
  import { type RateLimiter } from '../rate_limiter.js';
28
27
  import type { RouteFactoryDeps } from './deps.js';
29
28
  import type { ConnectionCloser } from '../actions/connection_closer.js';
30
- /** Input for `GET /api/account/status`. No parameters — caller is the subject. */
31
- export declare const AccountStatusInput: z.ZodNull;
32
- export type AccountStatusInput = z.infer<typeof AccountStatusInput>;
33
- /**
34
- * Output for `GET /api/account/status` on the authenticated path.
35
- *
36
- * `account` is always populated for authenticated callers. `actor` and
37
- * `role_grants` are populated when the caller's account has a unique actor or
38
- * the request supplies `?acting=<actor_id>`; on multi-actor accounts
39
- * without an `acting` query, `actor` is `null` and `role_grants` is empty so
40
- * the frontend can show a persona picker without a separate roundtrip.
41
- */
42
- export declare const AccountStatusOutput: z.ZodObject<{
43
- account: z.ZodObject<{
44
- id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
45
- username: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
46
- email: z.ZodNullable<z.ZodEmail>;
47
- email_verified: z.ZodBoolean;
48
- created_at: z.ZodString;
49
- }, z.core.$strict>;
50
- actor: z.ZodNullable<z.ZodObject<{
51
- id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
52
- name: z.ZodString;
53
- }, z.core.$strict>>;
54
- role_grants: z.ZodArray<z.ZodObject<{
55
- id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
56
- role: z.ZodString;
57
- scope_kind: z.ZodNullable<z.ZodString>;
58
- scope_id: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
59
- created_at: z.ZodString;
60
- expires_at: z.ZodNullable<z.ZodString>;
61
- granted_by: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
62
- }, z.core.$strict>>;
63
- }, z.core.$strict>;
64
- export type AccountStatusOutput = z.infer<typeof AccountStatusOutput>;
65
- /** Error body for `GET /api/account/status` on the unauthenticated path. */
66
- export declare const AccountStatusUnauthenticatedError: z.ZodObject<{
67
- error: z.ZodLiteral<"authentication_required">;
68
- bootstrap_available: z.ZodOptional<z.ZodBoolean>;
69
- }, z.core.$loose>;
70
- export type AccountStatusUnauthenticatedError = z.infer<typeof AccountStatusUnauthenticatedError>;
71
29
  /**
72
30
  * Create the account status route spec.
73
31
  *
@@ -91,10 +49,6 @@ export interface AccountStatusOptions {
91
49
  available: boolean;
92
50
  };
93
51
  }
94
- /** Default maximum sessions per account. */
95
- export declare const DEFAULT_MAX_SESSIONS = 5;
96
- /** Default maximum API tokens per account. */
97
- export declare const DEFAULT_MAX_TOKENS = 10;
98
52
  /**
99
53
  * Default minimum wall-clock time (ms) for a login failure (401) response.
100
54
  *
@@ -153,49 +107,30 @@ export interface AccountRouteOptions extends AuthSessionRouteOptions {
153
107
  * `audit.on_event_chain`) runs.
154
108
  */
155
109
  connection_closer?: ConnectionCloser | null;
110
+ /**
111
+ * Runtime bootstrap status for the bundled `GET /status` route — when
112
+ * `available`, its unauthenticated 401 carries `bootstrap_available: true`
113
+ * so a fresh frontend can route to the bootstrap flow. Pass
114
+ * `ctx.bootstrap_status` (the live `BootstrapStatus` ref) so the flag tracks
115
+ * the one-shot bootstrap completing. Omit when no bootstrap flow is wired —
116
+ * `/status` is still served, just without the flag.
117
+ */
118
+ bootstrap_status?: {
119
+ available: boolean;
120
+ };
156
121
  }
157
- /** Input for `POST /login`. Accepts a username or email in the `username` field. */
158
- export declare const LoginInput: z.ZodObject<{
159
- username: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
160
- password: z.ZodString;
161
- }, z.core.$strict>;
162
- export type LoginInput = z.infer<typeof LoginInput>;
163
- /** Output for `POST /login`. The signed session cookie is the operative side effect. */
164
- export declare const LoginOutput: z.ZodObject<{
165
- ok: z.ZodLiteral<true>;
166
- }, z.core.$strict>;
167
- export type LoginOutput = z.infer<typeof LoginOutput>;
168
- /** Input for `POST /logout`. Session identity flows through the cookie. */
169
- export declare const LogoutInput: z.ZodNull;
170
- export type LogoutInput = z.infer<typeof LogoutInput>;
171
- /** Output for `POST /logout`. Includes the revoked account's username for UI redraw. */
172
- export declare const LogoutOutput: z.ZodObject<{
173
- ok: z.ZodLiteral<true>;
174
- username: z.ZodString;
175
- }, z.core.$strict>;
176
- export type LogoutOutput = z.infer<typeof LogoutOutput>;
177
- /** Input for `POST /password`. `current_password` is minimally validated; `new_password` enforces the full policy. */
178
- export declare const PasswordChangeInput: z.ZodObject<{
179
- current_password: z.ZodString;
180
- new_password: z.ZodString;
181
- }, z.core.$strict>;
182
- export type PasswordChangeInput = z.infer<typeof PasswordChangeInput>;
183
- /** Output for `POST /password`. Counts are returned so the UI can summarize the revoke-all cascade. */
184
- export declare const PasswordChangeOutput: z.ZodObject<{
185
- ok: z.ZodLiteral<true>;
186
- sessions_revoked: z.ZodNumber;
187
- tokens_revoked: z.ZodNumber;
188
- }, z.core.$strict>;
189
- export type PasswordChangeOutput = z.infer<typeof PasswordChangeOutput>;
190
122
  /**
191
123
  * Create account route specs for session-based auth.
192
124
  *
193
- * The returned specs cover the three flows that stay REST after the RPC
194
- * migration (login, logout, password change). Self-service session/token
195
- * management and verify are on `auth/account_actions.ts`.
125
+ * The returned specs cover the REST flows that stay after the RPC migration:
126
+ * `/status` (account info + bootstrap availability), `/verify` (nginx
127
+ * `auth_request` shim), `/login`, `/logout`, `/password`. `/status` is bundled
128
+ * here (relative path, prefixed to `/api/account/status` by the caller) so
129
+ * every account surface serves it, matching the Rust `account_router`.
130
+ * Self-service session/token management is on `auth/account_actions.ts`.
196
131
  *
197
132
  * @param deps - stateless capabilities (keyring, password, log)
198
- * @param options - per-factory configuration (session_options, ip_rate_limiter, login_account_rate_limiter)
133
+ * @param options - per-factory configuration (session_options, ip_rate_limiter, login_account_rate_limiter, bootstrap_status)
199
134
  * @returns route specs (not yet applied to Hono)
200
135
  */
201
136
  export declare const create_account_route_specs: (deps: RouteFactoryDeps, options: AccountRouteOptions) => Array<RouteSpec>;
@@ -1 +1 @@
1
- {"version":3,"file":"account_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_routes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"account_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AA4BxD,OAAO,EAAkB,KAAK,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAEtE,OAAO,EAA+B,KAAK,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAClF,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,iCAAiC,CAAC;AAGtE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gCAAgC,GAAI,UAAU,oBAAoB,KAAG,SA4EhF,CAAC;AAEH,iDAAiD;AACjD,MAAM,WAAW,oBAAoB;IACpC,yDAAyD;IACzD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8FAA8F;IAC9F,gBAAgB,CAAC,EAAE;QAAC,SAAS,EAAE,OAAO,CAAA;KAAC,CAAC;CACxC;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAQ/C;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB;IACvC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,kFAAkF;IAClF,eAAe,EAAE,WAAW,GAAG,IAAI,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAoB,SAAQ,uBAAuB;IACnE,4FAA4F;IAC5F,0BAA0B,EAAE,WAAW,GAAG,IAAI,CAAC;IAC/C,2FAA2F;IAC3F,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC5C;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE;QAAC,SAAS,EAAE,OAAO,CAAA;KAAC,CAAC;CACxC;AAID;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,0BAA0B,GACtC,MAAM,gBAAgB,EACtB,SAAS,mBAAmB,KAC1B,KAAK,CAAC,SAAS,CAwRjB,CAAC"}