@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
@@ -21,41 +21,18 @@
21
21
  *
22
22
  * @module
23
23
  */
24
- import { z } from 'zod';
25
24
  import { clear_session_cookie, create_session_and_set_cookie } from './session_middleware.js';
26
- import { ActorSummaryJson, RoleGrantSummaryJson, SessionAccountJson, to_session_account, } from './account_schema.js';
27
- import { UsernameProvided } from '../primitive_schemas.js';
25
+ import { RoleGrantSummaryJson, to_session_account } from './account_schema.js';
26
+ import { account_status_route_shape, create_account_route_shapes, DEFAULT_MAX_SESSIONS, } from './account_route_schema.js';
28
27
  import { hash_session_token, query_session_revoke_all_for_account, query_session_revoke_by_hash_unscoped, } from './session_queries.js';
29
28
  import { query_account_by_username_or_email, query_update_account_password, } from './account_queries.js';
30
29
  import { query_revoke_all_api_tokens_for_account } from './api_token_queries.js';
31
30
  import { build_account_context, build_request_context, get_request_context, require_request_context, resolve_acting_actor, } from './request_context.js';
32
31
  import { ACCOUNT_ID_KEY, CREDENTIAL_TYPE_KEY } from '../hono_context.js';
33
32
  import { get_route_input } from '../http/route_spec.js';
34
- import { get_client_ip } from '../http/proxy.js';
33
+ import { get_client_ip } from '../http/client_ip.js';
35
34
  import { rate_limit_exceeded_response } from '../rate_limiter.js';
36
- import { Password, PasswordProvided } from './password.js';
37
- import { ERROR_AUTHENTICATION_REQUIRED, ERROR_INVALID_CREDENTIALS, ERROR_INVALID_JSON_BODY, ERROR_INVALID_REQUEST_BODY, } from '../http/error_schemas.js';
38
- /** Input for `GET /api/account/status`. No parameters — caller is the subject. */
39
- export const AccountStatusInput = z.null();
40
- /**
41
- * Output for `GET /api/account/status` on the authenticated path.
42
- *
43
- * `account` is always populated for authenticated callers. `actor` and
44
- * `role_grants` are populated when the caller's account has a unique actor or
45
- * the request supplies `?acting=<actor_id>`; on multi-actor accounts
46
- * without an `acting` query, `actor` is `null` and `role_grants` is empty so
47
- * the frontend can show a persona picker without a separate roundtrip.
48
- */
49
- export const AccountStatusOutput = z.strictObject({
50
- account: SessionAccountJson,
51
- actor: ActorSummaryJson.nullable(),
52
- role_grants: z.array(RoleGrantSummaryJson),
53
- });
54
- /** Error body for `GET /api/account/status` on the unauthenticated path. */
55
- export const AccountStatusUnauthenticatedError = z.looseObject({
56
- error: z.literal(ERROR_AUTHENTICATION_REQUIRED),
57
- bootstrap_available: z.boolean().optional(),
58
- });
35
+ import { ERROR_AUTHENTICATION_REQUIRED, ERROR_INVALID_CREDENTIALS } from '../http/error_schemas.js';
59
36
  /**
60
37
  * Create the account status route spec.
61
38
  *
@@ -70,15 +47,8 @@ export const AccountStatusUnauthenticatedError = z.looseObject({
70
47
  * @returns a single account status route spec
71
48
  */
72
49
  export const create_account_status_route_spec = (options) => ({
73
- method: 'GET',
74
- path: options?.path ?? '/api/account/status',
75
- auth: { account: 'none', actor: 'none' },
76
- description: 'Current account info (unauthenticated: 401 with bootstrap status)',
77
- input: AccountStatusInput,
78
- output: AccountStatusOutput,
79
- errors: {
80
- 401: AccountStatusUnauthenticatedError,
81
- },
50
+ ...account_status_route_shape,
51
+ path: options?.path ?? account_status_route_shape.path,
82
52
  handler: async (c, route) => {
83
53
  const account_id = c.get(ACCOUNT_ID_KEY) ?? null;
84
54
  if (!account_id) {
@@ -147,10 +117,6 @@ export const create_account_status_route_spec = (options) => ({
147
117
  });
148
118
  },
149
119
  });
150
- /** Default maximum sessions per account. */
151
- export const DEFAULT_MAX_SESSIONS = 5;
152
- /** Default maximum API tokens per account. */
153
- export const DEFAULT_MAX_TOKENS = 10;
154
120
  /**
155
121
  * Default minimum wall-clock time (ms) for a login failure (401) response.
156
122
  *
@@ -176,75 +142,43 @@ const login_fail_delay = (floor_ms, jitter_ms) => {
176
142
  const jitter = jitter_ms > 0 ? Math.floor(Math.random() * (jitter_ms * 2 + 1)) - jitter_ms : 0;
177
143
  return new Promise((resolve) => setTimeout(resolve, floor_ms + jitter));
178
144
  };
179
- // -- Input/output schemas ---------------------------------------------------
180
- /** Input for `POST /login`. Accepts a username or email in the `username` field. */
181
- export const LoginInput = z.strictObject({
182
- username: UsernameProvided,
183
- password: PasswordProvided,
184
- });
185
- /** Output for `POST /login`. The signed session cookie is the operative side effect. */
186
- export const LoginOutput = z.strictObject({
187
- ok: z.literal(true),
188
- });
189
- /** Input for `POST /logout`. Session identity flows through the cookie. */
190
- export const LogoutInput = z.null();
191
- /** Output for `POST /logout`. Includes the revoked account's username for UI redraw. */
192
- export const LogoutOutput = z.strictObject({
193
- ok: z.literal(true),
194
- username: z.string(),
195
- });
196
- /** Input for `POST /password`. `current_password` is minimally validated; `new_password` enforces the full policy. */
197
- export const PasswordChangeInput = z.strictObject({
198
- current_password: PasswordProvided,
199
- new_password: Password,
200
- });
201
- /** Output for `POST /password`. Counts are returned so the UI can summarize the revoke-all cascade. */
202
- export const PasswordChangeOutput = z.strictObject({
203
- ok: z.literal(true),
204
- sessions_revoked: z.number(),
205
- tokens_revoked: z.number(),
206
- });
145
+ // `create_account_route_specs` spreads each shape and attaches the live
146
+ // handler below.
207
147
  /**
208
148
  * Create account route specs for session-based auth.
209
149
  *
210
- * The returned specs cover the three flows that stay REST after the RPC
211
- * migration (login, logout, password change). Self-service session/token
212
- * management and verify are on `auth/account_actions.ts`.
150
+ * The returned specs cover the REST flows that stay after the RPC migration:
151
+ * `/status` (account info + bootstrap availability), `/verify` (nginx
152
+ * `auth_request` shim), `/login`, `/logout`, `/password`. `/status` is bundled
153
+ * here (relative path, prefixed to `/api/account/status` by the caller) so
154
+ * every account surface serves it, matching the Rust `account_router`.
155
+ * Self-service session/token management is on `auth/account_actions.ts`.
213
156
  *
214
157
  * @param deps - stateless capabilities (keyring, password, log)
215
- * @param options - per-factory configuration (session_options, ip_rate_limiter, login_account_rate_limiter)
158
+ * @param options - per-factory configuration (session_options, ip_rate_limiter, login_account_rate_limiter, bootstrap_status)
216
159
  * @returns route specs (not yet applied to Hono)
217
160
  */
218
161
  export const create_account_route_specs = (deps, options) => {
219
162
  const { keyring, password } = deps;
220
- const { session_options, ip_rate_limiter, login_account_rate_limiter, max_sessions = DEFAULT_MAX_SESSIONS, login_fail_floor_ms = DEFAULT_LOGIN_FAIL_FLOOR_MS, login_fail_jitter_ms = DEFAULT_LOGIN_FAIL_JITTER_MS, connection_closer = null, } = options;
163
+ const { session_options, ip_rate_limiter, login_account_rate_limiter, max_sessions = DEFAULT_MAX_SESSIONS, login_fail_floor_ms = DEFAULT_LOGIN_FAIL_FLOOR_MS, login_fail_jitter_ms = DEFAULT_LOGIN_FAIL_JITTER_MS, connection_closer = null, bootstrap_status, } = options;
164
+ const [verify_shape, login_shape, logout_shape, password_shape] = create_account_route_shapes({
165
+ login_account_rate_limited: login_account_rate_limiter !== null,
166
+ });
221
167
  return [
168
+ // `/status` is bundled into the account family (relative path, prefixed
169
+ // to `/api/account/status` by the caller) so every account surface serves
170
+ // it — matching the Rust `account_router`. The standalone
171
+ // `create_account_status_route_spec` stays the building block.
172
+ create_account_status_route_spec({ bootstrap_status }),
222
173
  {
223
- method: 'GET',
224
- path: '/verify',
225
- auth: { account: 'required', actor: 'none' },
226
- description: 'Session-validity probe for nginx auth_request (empty body, 200 or 401)',
227
- input: z.null(),
228
- output: z.null(),
174
+ ...verify_shape,
229
175
  handler: (c) => {
230
176
  require_request_context(c);
231
177
  return c.body(null, 200);
232
178
  },
233
179
  },
234
180
  {
235
- method: 'POST',
236
- path: '/login',
237
- auth: { account: 'none', actor: 'none' },
238
- description: 'Exchange credentials for session',
239
- input: LoginInput,
240
- output: LoginOutput,
241
- rate_limit: 'both',
242
- errors: {
243
- 400: z.looseObject({
244
- error: z.enum([ERROR_INVALID_JSON_BODY, ERROR_INVALID_REQUEST_BODY]),
245
- }),
246
- 401: z.looseObject({ error: z.literal(ERROR_INVALID_CREDENTIALS) }),
247
- },
181
+ ...login_shape,
248
182
  handler: async (c, route) => {
249
183
  // Per-IP rate limit check (before any DB/password work)
250
184
  const ip = ip_rate_limiter ? get_client_ip(c) : null;
@@ -331,16 +265,7 @@ export const create_account_route_specs = (deps, options) => {
331
265
  },
332
266
  },
333
267
  {
334
- method: 'POST',
335
- path: '/logout',
336
- // `credential_types: ['session']` — see `docs/security.md` §Credential-channel gating.
337
- // Logout is a session-bound operation; a bearer / daemon token holds no session
338
- // to end, so the dispatcher rejects it (403 `credential_type_required`) rather than
339
- // returning a misleading 200 + a phantom `logout` audit row for a no-op.
340
- auth: { account: 'required', actor: 'none', credential_types: ['session'] },
341
- description: 'Revoke current session and clear cookie',
342
- input: LogoutInput,
343
- output: LogoutOutput,
268
+ ...logout_shape,
344
269
  handler: async (c, route) => {
345
270
  const ctx = require_request_context(c);
346
271
  const session_token = c.get(session_options.context_key) ?? null;
@@ -377,19 +302,7 @@ export const create_account_route_specs = (deps, options) => {
377
302
  },
378
303
  },
379
304
  {
380
- method: 'POST',
381
- path: '/password',
382
- // `credential_types: ['session']` — see `docs/security.md` §Credential-channel gating.
383
- auth: { account: 'required', actor: 'none', credential_types: ['session'] },
384
- description: 'Change password (revokes all sessions and API tokens)',
385
- input: PasswordChangeInput,
386
- output: PasswordChangeOutput,
387
- rate_limit: login_account_rate_limiter ? 'both' : 'ip',
388
- errors: {
389
- 400: z.looseObject({
390
- error: z.enum([ERROR_INVALID_JSON_BODY, ERROR_INVALID_REQUEST_BODY]),
391
- }),
392
- },
305
+ ...password_shape,
393
306
  handler: async (c, route) => {
394
307
  // per-IP rate limit check (before argon2 work)
395
308
  const ip = ip_rate_limiter ? get_client_ip(c) : null;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Hono-free wire schemas + route shape for the audit-log SSE stream.
3
+ *
4
+ * Split from `audit_log_routes.ts` (whose handler pulls `hono/streaming` via
5
+ * `realtime/sse`) so cross-process test suites can build the audit-stream
6
+ * route shape without dragging the in-process SSE handler, and its optional
7
+ * `hono` peer, onto a backend-spawning consumer. `audit_log_routes.ts` imports
8
+ * these back and attaches the live SSE handler; single source of truth for the
9
+ * wire shape.
10
+ *
11
+ * @module
12
+ */
13
+ import { z } from 'zod';
14
+ import type { RouteSpec } from '../http/route_spec.js';
15
+ /** Query schema for the audit-log SSE route — multi-actor admins pass `?acting=<uuid>`. */
16
+ export declare const AuditStreamQuery: z.ZodObject<{
17
+ acting: z.ZodOptional<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
18
+ }, z.core.$strict>;
19
+ export type AuditStreamQuery = z.infer<typeof AuditStreamQuery>;
20
+ /** Default role required to access the audit-log SSE route. */
21
+ export declare const DEFAULT_AUDIT_STREAM_ROLE = "admin";
22
+ /**
23
+ * The `GET /audit/stream` SSE route shape minus its handler — pure hono-free
24
+ * data. `create_audit_log_route_specs` spreads this and attaches the live SSE
25
+ * handler; cross-process surface builders spread it with a stub handler. The
26
+ * output is `z.null()` because SSE streams have no JSON response body.
27
+ *
28
+ * @param required_role - role gating the stream (default `DEFAULT_AUDIT_STREAM_ROLE`)
29
+ * @returns the SSE route shape minus its handler
30
+ */
31
+ export declare const create_audit_log_route_shape: (required_role?: string) => Omit<RouteSpec, "handler">;
32
+ //# sourceMappingURL=audit_log_route_schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit_log_route_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/audit_log_route_schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAGrD,2FAA2F;AAC3F,eAAO,MAAM,gBAAgB;;kBAAwC,CAAC;AACtE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE,+DAA+D;AAC/D,eAAO,MAAM,yBAAyB,UAAU,CAAC;AAEjD;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,GACxC,gBAAe,MAAkC,KAC/C,IAAI,CAAC,SAAS,EAAE,SAAS,CAQ1B,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Hono-free wire schemas + route shape for the audit-log SSE stream.
3
+ *
4
+ * Split from `audit_log_routes.ts` (whose handler pulls `hono/streaming` via
5
+ * `realtime/sse`) so cross-process test suites can build the audit-stream
6
+ * route shape without dragging the in-process SSE handler, and its optional
7
+ * `hono` peer, onto a backend-spawning consumer. `audit_log_routes.ts` imports
8
+ * these back and attaches the live SSE handler; single source of truth for the
9
+ * wire shape.
10
+ *
11
+ * @module
12
+ */
13
+ import { z } from 'zod';
14
+ import { ActingActor } from '../http/auth_shape.js';
15
+ /** Query schema for the audit-log SSE route — multi-actor admins pass `?acting=<uuid>`. */
16
+ export const AuditStreamQuery = z.strictObject({ acting: ActingActor });
17
+ /** Default role required to access the audit-log SSE route. */
18
+ export const DEFAULT_AUDIT_STREAM_ROLE = 'admin';
19
+ /**
20
+ * The `GET /audit/stream` SSE route shape minus its handler — pure hono-free
21
+ * data. `create_audit_log_route_specs` spreads this and attaches the live SSE
22
+ * handler; cross-process surface builders spread it with a stub handler. The
23
+ * output is `z.null()` because SSE streams have no JSON response body.
24
+ *
25
+ * @param required_role - role gating the stream (default `DEFAULT_AUDIT_STREAM_ROLE`)
26
+ * @returns the SSE route shape minus its handler
27
+ */
28
+ export const create_audit_log_route_shape = (required_role = DEFAULT_AUDIT_STREAM_ROLE) => ({
29
+ method: 'GET',
30
+ path: '/audit/stream',
31
+ auth: { account: 'required', actor: 'required', roles: [required_role] },
32
+ description: 'Subscribe to realtime audit log events',
33
+ query: AuditStreamQuery,
34
+ input: z.null(),
35
+ output: z.null(), // SSE — no JSON response
36
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"audit_log_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/audit_log_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAsB,KAAK,SAAS,EAAE,KAAK,eAAe,EAAC,MAAM,oBAAoB,CAAC;AAC7F,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,oCAAoC,CAAC;AAQzE,yCAAyC;AACzC,MAAM,WAAW,oBAAoB;IACpC,+DAA+D;IAC/D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,MAAM,CAAC,EAAE;QACR,SAAS,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,KAAK,MAAM,IAAI,CAAC;QAC1F,GAAG,EAAE,MAAM,CAAC;KACZ,CAAC;CACF;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,GAAI,UAAU,oBAAoB,KAAG,KAAK,CAAC,SAAS,CAiC5F,CAAC"}
1
+ {"version":3,"file":"audit_log_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/audit_log_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAErD,OAAO,EAAsB,KAAK,SAAS,EAAE,KAAK,eAAe,EAAC,MAAM,oBAAoB,CAAC;AAC7F,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,oCAAoC,CAAC;AAIzE,yCAAyC;AACzC,MAAM,WAAW,oBAAoB;IACpC,+DAA+D;IAC/D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,MAAM,CAAC,EAAE;QACR,SAAS,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,KAAK,MAAM,IAAI,CAAC;QAC1F,GAAG,EAAE,MAAM,CAAC;KACZ,CAAC;CACF;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,GAAI,UAAU,oBAAoB,KAAG,KAAK,CAAC,SAAS,CAyB5F,CAAC"}
@@ -11,13 +11,10 @@
11
11
  *
12
12
  * @module
13
13
  */
14
- import { z } from 'zod';
14
+ import { create_audit_log_route_shape } from './audit_log_route_schema.js';
15
15
  import { create_sse_response } from '../realtime/sse.js';
16
16
  import { AUTH_SESSION_TOKEN_HASH_KEY, require_request_context } from './request_context.js';
17
17
  import { AUDIT_LOG_CHANNEL } from '../realtime/sse_auth_guard.js';
18
- import { ActingActor } from '../http/auth_shape.js';
19
- /** Query schema for the audit-log SSE route — multi-actor admins pass `?acting=<uuid>`. */
20
- const AuditStreamQuery = z.strictObject({ acting: ActingActor });
21
18
  /**
22
19
  * Create the optional audit-log SSE route spec.
23
20
  *
@@ -28,19 +25,12 @@ const AuditStreamQuery = z.strictObject({ acting: ActingActor });
28
25
  * @returns the SSE route spec (when `options.stream` is provided) or an empty array
29
26
  */
30
27
  export const create_audit_log_route_specs = (options) => {
31
- const role = options?.required_role ?? 'admin';
32
28
  if (!options?.stream)
33
29
  return [];
34
30
  const { subscribe, log } = options.stream;
35
31
  return [
36
32
  {
37
- method: 'GET',
38
- path: '/audit/stream',
39
- auth: { account: 'required', actor: 'required', roles: [role] },
40
- description: 'Subscribe to realtime audit log events',
41
- query: AuditStreamQuery,
42
- input: z.null(),
43
- output: z.null(), // SSE — no JSON response
33
+ ...create_audit_log_route_shape(options.required_role),
44
34
  handler: (c) => {
45
35
  const ctx = require_request_context(c);
46
36
  // scope = session hash (capped → tabs-per-session limit and
@@ -17,7 +17,7 @@
17
17
  import { DEV } from 'esm-env';
18
18
  import { AUTH_API_TOKEN_ID_KEY, ACCOUNT_ID_KEY, CREDENTIAL_TYPE_KEY } from '../hono_context.js';
19
19
  import { query_validate_api_token } from './api_token_queries.js';
20
- import { get_client_ip } from '../http/proxy.js';
20
+ import { get_client_ip } from '../http/client_ip.js';
21
21
  import { rate_limit_exceeded_response } from '../rate_limiter.js';
22
22
  /**
23
23
  * Create middleware that authenticates via bearer token.
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Hono-free wire shape + schemas for `POST /bootstrap`.
3
+ *
4
+ * Split from `bootstrap_routes.ts` so the route's declared shape (method,
5
+ * path, auth, input/output/error schemas) is importable **without** the
6
+ * hono-coupled handler (which sets a session cookie and reads the client
7
+ * IP off the Hono context). `create_bootstrap_route_specs` spreads
8
+ * `bootstrap_route_shape` and attaches the live handler; the test surface
9
+ * builder (`create_test_app_surface_spec`) spreads it with a stub handler so
10
+ * attack-surface generation reads the real shape without pulling the
11
+ * in-process Hono app onto cross-process consumers. Single source of truth —
12
+ * the shape can't drift between the live route and the surface.
13
+ *
14
+ * @module
15
+ */
16
+ import { z } from 'zod';
17
+ /** Input for `POST /bootstrap`. `token` is the one-shot token file contents. */
18
+ export declare const BootstrapInput: z.ZodObject<{
19
+ token: z.ZodString;
20
+ username: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
21
+ password: z.ZodString;
22
+ }, z.core.$strict>;
23
+ export type BootstrapInput = z.infer<typeof BootstrapInput>;
24
+ /** Output for `POST /bootstrap`. Session cookie is the operative side effect. */
25
+ export declare const BootstrapOutput: z.ZodObject<{
26
+ ok: z.ZodLiteral<true>;
27
+ account: z.ZodObject<{
28
+ id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
29
+ username: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
30
+ }, z.core.$strict>;
31
+ actor: z.ZodObject<{
32
+ id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
33
+ }, z.core.$strict>;
34
+ }, z.core.$strict>;
35
+ export type BootstrapOutput = z.infer<typeof BootstrapOutput>;
36
+ /**
37
+ * The `POST /bootstrap` route shape minus its handler — pure hono-free data.
38
+ * `create_bootstrap_route_specs` spreads this and attaches the live handler;
39
+ * surface generation spreads it with a stub handler (handlers are never run
40
+ * during surface assembly, only the shape is read).
41
+ */
42
+ export declare const bootstrap_route_shape: {
43
+ method: "POST";
44
+ path: string;
45
+ auth: {
46
+ account: "none";
47
+ actor: "none";
48
+ };
49
+ description: string;
50
+ transaction: false;
51
+ input: z.ZodObject<{
52
+ token: z.ZodString;
53
+ username: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
54
+ password: z.ZodString;
55
+ }, z.core.$strict>;
56
+ output: z.ZodObject<{
57
+ ok: z.ZodLiteral<true>;
58
+ account: z.ZodObject<{
59
+ id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
60
+ username: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
61
+ }, z.core.$strict>;
62
+ actor: z.ZodObject<{
63
+ id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
64
+ }, z.core.$strict>;
65
+ }, z.core.$strict>;
66
+ rate_limit: "ip";
67
+ errors: {
68
+ 400: z.ZodObject<{
69
+ error: z.ZodEnum<{
70
+ invalid_request_body: "invalid_request_body";
71
+ invalid_json_body: "invalid_json_body";
72
+ }>;
73
+ }, z.core.$loose>;
74
+ 401: z.ZodObject<{
75
+ error: z.ZodLiteral<"invalid_token">;
76
+ }, z.core.$loose>;
77
+ 403: z.ZodObject<{
78
+ error: z.ZodLiteral<"already_bootstrapped">;
79
+ }, z.core.$loose>;
80
+ 404: z.ZodObject<{
81
+ error: z.ZodLiteral<"token_file_missing">;
82
+ }, z.core.$loose>;
83
+ };
84
+ };
85
+ //# sourceMappingURL=bootstrap_route_schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap_route_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/bootstrap_route_schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AActB,gFAAgF;AAChF,eAAO,MAAM,cAAc;;;;kBAIzB,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE5D,iFAAiF;AACjF,eAAO,MAAM,eAAe;;;;;;;;;kBAI1B,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiBI,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Hono-free wire shape + schemas for `POST /bootstrap`.
3
+ *
4
+ * Split from `bootstrap_routes.ts` so the route's declared shape (method,
5
+ * path, auth, input/output/error schemas) is importable **without** the
6
+ * hono-coupled handler (which sets a session cookie and reads the client
7
+ * IP off the Hono context). `create_bootstrap_route_specs` spreads
8
+ * `bootstrap_route_shape` and attaches the live handler; the test surface
9
+ * builder (`create_test_app_surface_spec`) spreads it with a stub handler so
10
+ * attack-surface generation reads the real shape without pulling the
11
+ * in-process Hono app onto cross-process consumers. Single source of truth —
12
+ * the shape can't drift between the live route and the surface.
13
+ *
14
+ * @module
15
+ */
16
+ import { z } from 'zod';
17
+ import { Uuid } from '@fuzdev/fuz_util/id.js';
18
+ import { Username } from '../primitive_schemas.js';
19
+ import { Password } from './password.js';
20
+ import { ERROR_INVALID_TOKEN, ERROR_ALREADY_BOOTSTRAPPED, ERROR_TOKEN_FILE_MISSING, ERROR_INVALID_JSON_BODY, ERROR_INVALID_REQUEST_BODY, } from '../http/error_schemas.js';
21
+ /** Input for `POST /bootstrap`. `token` is the one-shot token file contents. */
22
+ export const BootstrapInput = z.strictObject({
23
+ token: z.string().min(1).meta({ sensitivity: 'secret' }),
24
+ username: Username,
25
+ password: Password,
26
+ });
27
+ /** Output for `POST /bootstrap`. Session cookie is the operative side effect. */
28
+ export const BootstrapOutput = z.strictObject({
29
+ ok: z.literal(true),
30
+ account: z.strictObject({ id: Uuid, username: Username }),
31
+ actor: z.strictObject({ id: Uuid }),
32
+ });
33
+ /**
34
+ * The `POST /bootstrap` route shape minus its handler — pure hono-free data.
35
+ * `create_bootstrap_route_specs` spreads this and attaches the live handler;
36
+ * surface generation spreads it with a stub handler (handlers are never run
37
+ * during surface assembly, only the shape is read).
38
+ */
39
+ export const bootstrap_route_shape = {
40
+ method: 'POST',
41
+ path: '/bootstrap',
42
+ auth: { account: 'none', actor: 'none' },
43
+ description: 'Create initial keeper account (one-shot)',
44
+ transaction: false, // bootstrap_account manages its own transaction
45
+ input: BootstrapInput,
46
+ output: BootstrapOutput,
47
+ rate_limit: 'ip',
48
+ errors: {
49
+ 400: z.looseObject({
50
+ error: z.enum([ERROR_INVALID_JSON_BODY, ERROR_INVALID_REQUEST_BODY]),
51
+ }),
52
+ 401: z.looseObject({ error: z.literal(ERROR_INVALID_TOKEN) }),
53
+ 403: z.looseObject({ error: z.literal(ERROR_ALREADY_BOOTSTRAPPED) }),
54
+ 404: z.looseObject({ error: z.literal(ERROR_TOKEN_FILE_MISSING) }),
55
+ },
56
+ };
@@ -6,7 +6,6 @@
6
6
  *
7
7
  * @module
8
8
  */
9
- import { z } from 'zod';
10
9
  import type { Context } from 'hono';
11
10
  import type { Logger } from '@fuzdev/fuz_util/log.js';
12
11
  import type { SessionOptions } from './session_cookie.js';
@@ -16,25 +15,6 @@ import { type RouteSpec } from '../http/route_spec.js';
16
15
  import { type RateLimiter } from '../rate_limiter.js';
17
16
  import type { RouteFactoryDeps } from './deps.js';
18
17
  import type { StatResult } from '../runtime/deps.js';
19
- /** Input for `POST /bootstrap`. `token` is the one-shot token file contents. */
20
- export declare const BootstrapInput: z.ZodObject<{
21
- token: z.ZodString;
22
- username: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
23
- password: z.ZodString;
24
- }, z.core.$strict>;
25
- export type BootstrapInput = z.infer<typeof BootstrapInput>;
26
- /** Output for `POST /bootstrap`. Session cookie is the operative side effect. */
27
- export declare const BootstrapOutput: z.ZodObject<{
28
- ok: z.ZodLiteral<true>;
29
- account: z.ZodObject<{
30
- id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
31
- username: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
32
- }, z.core.$strict>;
33
- actor: z.ZodObject<{
34
- id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
35
- }, z.core.$strict>;
36
- }, z.core.$strict>;
37
- export type BootstrapOutput = z.infer<typeof BootstrapOutput>;
38
18
  /**
39
19
  * Bootstrap status — runtime state computed once at startup.
40
20
  */
@@ -1 +1 @@
1
- {"version":3,"file":"bootstrap_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/bootstrap_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AACtB,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,MAAM,CAAC;AAClC,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAGpD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAoB,KAAK,uBAAuB,EAAC,MAAM,wBAAwB,CAAC;AAGvF,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AACpC,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,UAAU,EAAC,MAAM,oBAAoB,CAAC;AAWnD,gFAAgF;AAChF,eAAO,MAAM,cAAc;;;;kBAIzB,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE5D,iFAAiF;AACjF,eAAO,MAAM,eAAe;;;;;;;;;kBAI1B,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACrC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,8EAA8E;IAC9E,gBAAgB,EAAE,eAAe,CAAC;IAClC;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,uBAAuB,EAAE,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E,4EAA4E;IAC5E,eAAe,EAAE,WAAW,GAAG,IAAI,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACxC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACnD,EAAE,EAAE,EAAE,CAAC;IACP,GAAG,EAAE,MAAM,CAAC;CACZ;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,wBAAwB,EAC9B,SAAS;IAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAC,KAClC,OAAO,CAAC,eAAe,CAwBzB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,GACxC,MAAM,gBAAgB,EACtB,SAAS,qBAAqB,KAC5B,KAAK,CAAC,SAAS,CAkHjB,CAAC"}
1
+ {"version":3,"file":"bootstrap_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/bootstrap_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,MAAM,CAAC;AAClC,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAoB,KAAK,uBAAuB,EAAC,MAAM,wBAAwB,CAAC;AAEvF,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AACpC,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,UAAU,EAAC,MAAM,oBAAoB,CAAC;AAGnD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACrC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,8EAA8E;IAC9E,gBAAgB,EAAE,eAAe,CAAC;IAClC;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,uBAAuB,EAAE,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E,4EAA4E;IAC5E,eAAe,EAAE,WAAW,GAAG,IAAI,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACxC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACnD,EAAE,EAAE,EAAE,CAAC;IACP,GAAG,EAAE,MAAM,CAAC;CACZ;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,wBAAwB,EAC9B,SAAS;IAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAC,KAClC,OAAO,CAAC,eAAe,CAwBzB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,GACxC,MAAM,gBAAgB,EACtB,SAAS,qBAAqB,KAC5B,KAAK,CAAC,SAAS,CAmGjB,CAAC"}
@@ -6,29 +6,13 @@
6
6
  *
7
7
  * @module
8
8
  */
9
- import { z } from 'zod';
10
- import { Uuid } from '@fuzdev/fuz_util/id.js';
11
9
  import { create_session_and_set_cookie } from './session_middleware.js';
12
10
  import { bootstrap_account } from './bootstrap_account.js';
13
- import { Username } from '../primitive_schemas.js';
14
- import { Password } from './password.js';
11
+ import { bootstrap_route_shape } from './bootstrap_route_schema.js';
15
12
  import { get_route_input } from '../http/route_spec.js';
16
- import { get_client_ip } from '../http/proxy.js';
13
+ import { get_client_ip } from '../http/client_ip.js';
17
14
  import { rate_limit_exceeded_response } from '../rate_limiter.js';
18
- import { ERROR_INVALID_TOKEN, ERROR_ALREADY_BOOTSTRAPPED, ERROR_TOKEN_FILE_MISSING, ERROR_INVALID_JSON_BODY, ERROR_INVALID_REQUEST_BODY, } from '../http/error_schemas.js';
19
- // -- Input/output schemas ---------------------------------------------------
20
- /** Input for `POST /bootstrap`. `token` is the one-shot token file contents. */
21
- export const BootstrapInput = z.strictObject({
22
- token: z.string().min(1).meta({ sensitivity: 'secret' }),
23
- username: Username,
24
- password: Password,
25
- });
26
- /** Output for `POST /bootstrap`. Session cookie is the operative side effect. */
27
- export const BootstrapOutput = z.strictObject({
28
- ok: z.literal(true),
29
- account: z.strictObject({ id: Uuid, username: Username }),
30
- actor: z.strictObject({ id: Uuid }),
31
- });
15
+ import { ERROR_ALREADY_BOOTSTRAPPED } from '../http/error_schemas.js';
32
16
  /**
33
17
  * Check bootstrap availability at startup.
34
18
  *
@@ -73,22 +57,7 @@ export const create_bootstrap_route_specs = (deps, options) => {
73
57
  const { token_path } = bootstrap_status;
74
58
  return [
75
59
  {
76
- method: 'POST',
77
- path: '/bootstrap',
78
- auth: { account: 'none', actor: 'none' },
79
- description: 'Create initial keeper account (one-shot)',
80
- transaction: false, // bootstrap_account manages its own transaction
81
- input: BootstrapInput,
82
- output: BootstrapOutput,
83
- rate_limit: 'ip',
84
- errors: {
85
- 400: z.looseObject({
86
- error: z.enum([ERROR_INVALID_JSON_BODY, ERROR_INVALID_REQUEST_BODY]),
87
- }),
88
- 401: z.looseObject({ error: z.literal(ERROR_INVALID_TOKEN) }),
89
- 403: z.looseObject({ error: z.literal(ERROR_ALREADY_BOOTSTRAPPED) }),
90
- 404: z.looseObject({ error: z.literal(ERROR_TOKEN_FILE_MISSING) }),
91
- },
60
+ ...bootstrap_route_shape,
92
61
  handler: async (c, route) => {
93
62
  // Short-circuit if bootstrap already completed or surface-only mounted.
94
63
  // In 'surface_only' mode `bootstrap_status.token_path === null` and
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Hono-free wire schemas + route shape for `POST /signup`.
3
+ *
4
+ * Split from `signup_routes.ts` (whose handler pulls `hono/cookie` via
5
+ * `session_middleware` to set the new session cookie) so cross-process test
6
+ * suites can build the signup route shape — and assert on the response shape
7
+ * — without dragging the in-process Hono session handler, and its optional
8
+ * `hono` peer, onto a backend-spawning consumer. `signup_routes.ts` imports
9
+ * these back and attaches the live handler; single source of truth for the
10
+ * wire shape.
11
+ *
12
+ * @module
13
+ */
14
+ import { z } from 'zod';
15
+ import type { RouteSpec } from '../http/route_spec.js';
16
+ /** Input for `POST /signup`. `email` is optional and must match any referenced invite. */
17
+ export declare const SignupInput: z.ZodObject<{
18
+ username: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
19
+ password: z.ZodString;
20
+ email: z.ZodOptional<z.ZodEmail>;
21
+ }, z.core.$strict>;
22
+ export type SignupInput = z.infer<typeof SignupInput>;
23
+ /**
24
+ * Output for `POST /signup`.
25
+ *
26
+ * Session cookie is the operative side effect. The returned `account` and
27
+ * `actor` mirror `BootstrapOutput` so cross-process per-test setup can read
28
+ * the per-test identity straight off the signup response.
29
+ */
30
+ export declare const SignupOutput: z.ZodObject<{
31
+ ok: z.ZodLiteral<true>;
32
+ account: z.ZodObject<{
33
+ id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
34
+ username: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
35
+ }, z.core.$strict>;
36
+ actor: z.ZodObject<{
37
+ id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
38
+ }, z.core.$strict>;
39
+ }, z.core.$strict>;
40
+ export type SignupOutput = z.infer<typeof SignupOutput>;
41
+ /** Option inputs that shape the signup route metadata (not its handler). */
42
+ export interface SignupRouteShapeOptions {
43
+ /** Whether a per-account signup rate limiter is wired — toggles `rate_limit`. */
44
+ signup_account_rate_limited: boolean;
45
+ }
46
+ /**
47
+ * The `POST /signup` route shape minus its handler — pure hono-free data.
48
+ * `create_signup_route_specs` spreads this and attaches the live handler;
49
+ * cross-process surface builders spread it with a stub handler. Single source
50
+ * of truth — the shape can't drift between the live route and the surface.
51
+ */
52
+ export declare const create_signup_route_shape: (options: SignupRouteShapeOptions) => Omit<RouteSpec, "handler">;
53
+ //# sourceMappingURL=signup_route_schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signup_route_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/signup_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,0FAA0F;AAC1F,eAAO,MAAM,WAAW;;;;kBAItB,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD;;;;;;GAMG;AACH,eAAO,MAAM,YAAY;;;;;;;;;kBAIvB,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAExD,4EAA4E;AAC5E,MAAM,WAAW,uBAAuB;IACvC,iFAAiF;IACjF,2BAA2B,EAAE,OAAO,CAAC;CACrC;AAED;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,GACrC,SAAS,uBAAuB,KAC9B,IAAI,CAAC,SAAS,EAAE,SAAS,CAgB1B,CAAC"}