@fuzdev/fuz_app 0.35.0 → 0.37.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/dist/actions/CLAUDE.md +1 -1
- package/dist/actions/action_codegen.d.ts +10 -4
- package/dist/actions/action_codegen.d.ts.map +1 -1
- package/dist/actions/action_codegen.js +11 -5
- package/dist/actions/rpc_client.d.ts +10 -6
- package/dist/actions/rpc_client.d.ts.map +1 -1
- package/dist/actions/rpc_client.js +9 -5
- package/dist/auth/account_routes.d.ts.map +1 -1
- package/dist/auth/account_routes.js +12 -2
- package/dist/auth/bootstrap_routes.d.ts.map +1 -1
- package/dist/auth/bootstrap_routes.js +4 -1
- package/dist/auth/signup_routes.d.ts.map +1 -1
- package/dist/auth/signup_routes.js +4 -1
- package/dist/http/db_routes.d.ts.map +1 -1
- package/dist/http/db_routes.js +12 -4
- package/dist/http/error_schemas.d.ts +2 -0
- package/dist/http/error_schemas.d.ts.map +1 -1
- package/dist/http/error_schemas.js +2 -0
- package/dist/testing/CLAUDE.md +17 -4
- package/dist/testing/attack_surface.d.ts +24 -3
- package/dist/testing/attack_surface.d.ts.map +1 -1
- package/dist/testing/attack_surface.js +32 -2
- package/dist/testing/surface_invariants.d.ts +26 -3
- package/dist/testing/surface_invariants.d.ts.map +1 -1
- package/dist/testing/surface_invariants.js +27 -3
- package/package.json +1 -1
package/dist/actions/CLAUDE.md
CHANGED
|
@@ -81,7 +81,7 @@ not the runtime):
|
|
|
81
81
|
- `get_executor_phases(spec, executor)` — phases a given executor (`'frontend' | 'backend'`) participates in for the spec. Deduplicates via `Set` (handles `initiator: 'both'` overlap).
|
|
82
82
|
- `get_handler_return_type(spec, phase, imports, path_prefix)` — the TS type a phase handler must return; triggers the `ActionOutputs` import as a side effect.
|
|
83
83
|
- `generate_phase_handlers(spec, executor, imports, {action_event_type?})` — emits the typed handler-map fragment for one action; consumers compose these into `ActionHandlers` types.
|
|
84
|
-
- `generate_actions_api_method_signature(spec, {sync_returns_value?})` — single source of truth for the typed `ActionsApi` method shape. Threads `options?: RpcClientCallOptions` (`{signal?, transport_name?, queue?}`) onto every async method
|
|
84
|
+
- `generate_actions_api_method_signature(spec, {sync_returns_value?})` — single source of truth for the typed `ActionsApi` method shape. Threads `options?: RpcClientCallOptions` (`{signal?, transport_name?, queue?}`) onto every async method — `request_response`, `remote_notification`, and async `local_call` — and wraps the return in `Promise<Result<...>>`. Notifications were previously emitted as `=> void`, mismatching the runtime (`create_remote_notification_method` returns a Promise that resolves to `Result<{value: void}>`); regenerate consumer typed clients to pick up the corrected shape. Older inline templates using `get_innermost_type_name` directly drop the options arg — also regenerate those.
|
|
85
85
|
- `create_banner(origin_path)` — gen banner comment.
|
|
86
86
|
- `to_action_spec_identifier(method)` / `to_action_spec_input_identifier` / `to_action_spec_output_identifier` — naming convention helpers (emit `foo_action_spec` / `foo_action_spec.input` / `foo_action_spec.output`).
|
|
87
87
|
- `get_innermost_type(schema)` / `get_innermost_type_name(schema)` — unwrap Zod `ZodOptional` / `ZodNullable` / `ZodDefault` / transforms / pipes / prefaults to reach the base schema.
|
|
@@ -116,10 +116,16 @@ export declare const get_innermost_type_name: (schema: z.ZodType) => string;
|
|
|
116
116
|
* surface evolves in one place when fields like `signal` or `transport_name`
|
|
117
117
|
* are added to per-call options.
|
|
118
118
|
*
|
|
119
|
-
* Async methods (request_response
|
|
120
|
-
* `options?: RpcClientCallOptions` arg
|
|
121
|
-
*
|
|
122
|
-
*
|
|
119
|
+
* Async methods (`request_response`, `remote_notification`, async
|
|
120
|
+
* `local_call`) get an optional second `options?: RpcClientCallOptions` arg
|
|
121
|
+
* (`{signal?, transport_name?, queue?}`) and a `Promise<Result<...>>` return
|
|
122
|
+
* type. Sync `local_call` methods omit the options arg — `signal` can't
|
|
123
|
+
* cooperatively interrupt a synchronous handler and there's no transport to
|
|
124
|
+
* select. `remote_notification` is async because
|
|
125
|
+
* `create_remote_notification_method` returns a Promise that resolves to a
|
|
126
|
+
* `Result<{value: void}>` (success) or `Result<{error}>` (transport send
|
|
127
|
+
* failure). Earlier emit shapes declared notifications as `=> void` —
|
|
128
|
+
* regenerate consumer typed clients to pick up the corrected return.
|
|
123
129
|
*
|
|
124
130
|
* Consumers must import `ActionInputs`, `ActionOutputs`, `Result`,
|
|
125
131
|
* `JsonrpcErrorObject`, and (for async) `RpcClientCallOptions` into the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action_codegen.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_codegen.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAGtB,OAAO,KAAK,EAAC,eAAe,EAAE,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAOxE;;GAEG;AACH,UAAU,UAAU;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,WAAW,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,aAAa;;IACzB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAa;IAE1D;;;;OAIG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAQrC;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAI1C;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI;IAOrD;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI;IAgCtD;;;OAGG;IACH,KAAK,IAAI,MAAM;IAIf;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED;;;OAGG;IACH,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC;IAIxB;;OAEG;IACH,KAAK,IAAI,IAAI;CAqDb;AAED;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAC/B,MAAM,eAAe,EACrB,UAAU,UAAU,GAAG,SAAS,KAC9B,KAAK,CAAC,gBAAgB,CA4DxB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,GACnC,MAAM,eAAe,EACrB,OAAO,gBAAgB,EACvB,SAAS,aAAa,EACtB,aAAa,MAAM,KACjB,MAkBF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,GACnC,MAAM,eAAe,EACrB,UAAU,UAAU,GAAG,SAAS,EAChC,SAAS,aAAa,EACtB,UAAU;IAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAC,KACpC,MA4BF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,GAAI,aAAa,MAAM,KAAG,MACU,CAAC;AAG/D,eAAO,MAAM,yBAAyB,GAAI,QAAQ,MAAM,KAAG,MAAiC,CAAC;AAC7F,eAAO,MAAM,+BAA+B,GAAI,QAAQ,MAAM,KAAG,MACpB,CAAC;AAC9C,eAAO,MAAM,gCAAgC,GAAI,QAAQ,MAAM,KAAG,MACpB,CAAC;AAE/C;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,CAAC,CAAC,OAwBxD,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,MAI3D,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"action_codegen.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_codegen.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAGtB,OAAO,KAAK,EAAC,eAAe,EAAE,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAOxE;;GAEG;AACH,UAAU,UAAU;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,WAAW,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,aAAa;;IACzB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAa;IAE1D;;;;OAIG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAQrC;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAI1C;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI;IAOrD;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI;IAgCtD;;;OAGG;IACH,KAAK,IAAI,MAAM;IAIf;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED;;;OAGG;IACH,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC;IAIxB;;OAEG;IACH,KAAK,IAAI,IAAI;CAqDb;AAED;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAC/B,MAAM,eAAe,EACrB,UAAU,UAAU,GAAG,SAAS,KAC9B,KAAK,CAAC,gBAAgB,CA4DxB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,GACnC,MAAM,eAAe,EACrB,OAAO,gBAAgB,EACvB,SAAS,aAAa,EACtB,aAAa,MAAM,KACjB,MAkBF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,GACnC,MAAM,eAAe,EACrB,UAAU,UAAU,GAAG,SAAS,EAChC,SAAS,aAAa,EACtB,UAAU;IAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAC,KACpC,MA4BF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,GAAI,aAAa,MAAM,KAAG,MACU,CAAC;AAG/D,eAAO,MAAM,yBAAyB,GAAI,QAAQ,MAAM,KAAG,MAAiC,CAAC;AAC7F,eAAO,MAAM,+BAA+B,GAAI,QAAQ,MAAM,KAAG,MACpB,CAAC;AAC9C,eAAO,MAAM,gCAAgC,GAAI,QAAQ,MAAM,KAAG,MACpB,CAAC;AAE/C;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,CAAC,CAAC,OAwBxD,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,MAI3D,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,qCAAqC,GACjD,MAAM,eAAe,EACrB,UAAU;IAAC,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAAC,KACtC,MAoBF,CAAC"}
|
|
@@ -322,10 +322,16 @@ export const get_innermost_type_name = (schema) => {
|
|
|
322
322
|
* surface evolves in one place when fields like `signal` or `transport_name`
|
|
323
323
|
* are added to per-call options.
|
|
324
324
|
*
|
|
325
|
-
* Async methods (request_response
|
|
326
|
-
* `options?: RpcClientCallOptions` arg
|
|
327
|
-
*
|
|
328
|
-
*
|
|
325
|
+
* Async methods (`request_response`, `remote_notification`, async
|
|
326
|
+
* `local_call`) get an optional second `options?: RpcClientCallOptions` arg
|
|
327
|
+
* (`{signal?, transport_name?, queue?}`) and a `Promise<Result<...>>` return
|
|
328
|
+
* type. Sync `local_call` methods omit the options arg — `signal` can't
|
|
329
|
+
* cooperatively interrupt a synchronous handler and there's no transport to
|
|
330
|
+
* select. `remote_notification` is async because
|
|
331
|
+
* `create_remote_notification_method` returns a Promise that resolves to a
|
|
332
|
+
* `Result<{value: void}>` (success) or `Result<{error}>` (transport send
|
|
333
|
+
* failure). Earlier emit shapes declared notifications as `=> void` —
|
|
334
|
+
* regenerate consumer typed clients to pick up the corrected return.
|
|
329
335
|
*
|
|
330
336
|
* Consumers must import `ActionInputs`, `ActionOutputs`, `Result`,
|
|
331
337
|
* `JsonrpcErrorObject`, and (for async) `RpcClientCallOptions` into the
|
|
@@ -345,7 +351,7 @@ export const generate_actions_api_method_signature = (spec, options) => {
|
|
|
345
351
|
const input_param = has_input
|
|
346
352
|
? `input${spec.input.safeParse(undefined).success ? '?' : ''}: ActionInputs['${spec.method}']`
|
|
347
353
|
: 'input?: void';
|
|
348
|
-
const is_async = spec.kind === 'request_response' ||
|
|
354
|
+
const is_async = spec.kind === 'request_response' || spec.kind === 'remote_notification' || spec.async;
|
|
349
355
|
const options_param = is_async ? ', options?: RpcClientCallOptions' : '';
|
|
350
356
|
const result_return = `Result<{value: ActionOutputs['${spec.method}']}, {error: JsonrpcErrorObject}>`;
|
|
351
357
|
const return_type = is_async
|
|
@@ -99,14 +99,18 @@ export type ThrowingRpcCall = <TOutput = unknown>(method: string, input?: unknow
|
|
|
99
99
|
* The mapped-type generic constraint accepts both shapes without a cast:
|
|
100
100
|
* a codegen-derived typed `ActionsApi` (named-method interface, e.g.
|
|
101
101
|
* `{account_verify: (input) => Promise<Result<...>>, ...}`) and a loose
|
|
102
|
-
* `Record<string, (input?: any) => Promise<any
|
|
103
|
-
* the constraint avoids the index-signature requirement that would
|
|
102
|
+
* `Record<string, (input?: any) => Promise<any> | void>`. Using `keyof TApi`
|
|
103
|
+
* in the constraint avoids the index-signature requirement that would
|
|
104
104
|
* otherwise force consumers to `as unknown as Record<string, …>` their
|
|
105
|
-
* generated client.
|
|
105
|
+
* generated client. The `| void` arm tolerates `remote_notification`
|
|
106
|
+
* methods, whose `ActionsApi` signature is `(input) => void` even though
|
|
107
|
+
* `create_remote_notification_method` returns a Promise at runtime — the
|
|
108
|
+
* throwing wrapper is intended for `request_response` calls but must
|
|
109
|
+
* accept mixed `ActionsApi` shapes without forcing a cast at the seam.
|
|
106
110
|
*
|
|
107
111
|
* @param api - typed RPC client from `create_rpc_client` (or any object
|
|
108
|
-
* whose values are all `(input?) => Promise
|
|
109
|
-
* the consumer's generated `ActionsApi` interface)
|
|
112
|
+
* whose values are all `(input?) => Promise<...> | void` functions —
|
|
113
|
+
* notably the consumer's generated `ActionsApi` interface)
|
|
110
114
|
*/
|
|
111
|
-
export declare const create_throwing_rpc_call: <TApi extends Record<keyof TApi, (input?: any) => Promise<any
|
|
115
|
+
export declare const create_throwing_rpc_call: <TApi extends Record<keyof TApi, (input?: any) => Promise<any> | void>>(api: TApi) => ThrowingRpcCall;
|
|
112
116
|
//# sourceMappingURL=rpc_client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc_client.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/rpc_client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAQH,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,yBAAyB,CAAC;AAOpE,OAAO,KAAK,EAAC,UAAU,EAAE,qBAAqB,EAAC,MAAM,kBAAkB,CAAC;AACxE,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAGnD;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,aAAa,GAAG,SAAS,CAAC;AAM/E,8EAA8E;AAC9E,MAAM,WAAW,sBAAsB;IACtC,aAAa,EAAE,CAAC,IAAI,EAAE;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,oBAAoB,CAAA;KAAC,KAC5E;QACA,sBAAsB,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;KAC5C,GACD,SAAS,CAAC;CACb;AAED,uCAAuC;AACvC,MAAM,WAAW,sBAAsB;IACtC,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,EAAE,sBAAsB,CAAC;IACpC,kEAAkE;IAClE,OAAO,CAAC,EAAE,sBAAsB,CAAC;IACjC;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,kBAAkB,CAAC;CAC1C;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,GAC7B,SAAS,sBAAsB,KAC7B,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,GAAG,CAgB7C,CAAC;AA2DF;;;;;GAKG;AACH,MAAM,WAAW,oBAAqB,SAAQ,qBAAqB;CAAG;AAgItE;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,GAAG,OAAO,EAC/C,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,OAAO,KACX,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB
|
|
1
|
+
{"version":3,"file":"rpc_client.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/rpc_client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAQH,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,yBAAyB,CAAC;AAOpE,OAAO,KAAK,EAAC,UAAU,EAAE,qBAAqB,EAAC,MAAM,kBAAkB,CAAC;AACxE,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAGnD;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,aAAa,GAAG,SAAS,CAAC;AAM/E,8EAA8E;AAC9E,MAAM,WAAW,sBAAsB;IACtC,aAAa,EAAE,CAAC,IAAI,EAAE;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,oBAAoB,CAAA;KAAC,KAC5E;QACA,sBAAsB,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;KAC5C,GACD,SAAS,CAAC;CACb;AAED,uCAAuC;AACvC,MAAM,WAAW,sBAAsB;IACtC,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,EAAE,sBAAsB,CAAC;IACpC,kEAAkE;IAClE,OAAO,CAAC,EAAE,sBAAsB,CAAC;IACjC;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,kBAAkB,CAAC;CAC1C;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,GAC7B,SAAS,sBAAsB,KAC7B,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,GAAG,CAgB7C,CAAC;AA2DF;;;;;GAKG;AACH,MAAM,WAAW,oBAAqB,SAAQ,qBAAqB;CAAG;AAgItE;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,GAAG,OAAO,EAC/C,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,OAAO,KACX,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,eAAO,MAAM,wBAAwB,GACpC,IAAI,SAAS,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,KAAK,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,EAErE,KAAK,IAAI,KACP,eAcF,CAAC"}
|
|
@@ -193,14 +193,18 @@ const create_remote_notification_method = (peer, environment, spec, actions, tra
|
|
|
193
193
|
* The mapped-type generic constraint accepts both shapes without a cast:
|
|
194
194
|
* a codegen-derived typed `ActionsApi` (named-method interface, e.g.
|
|
195
195
|
* `{account_verify: (input) => Promise<Result<...>>, ...}`) and a loose
|
|
196
|
-
* `Record<string, (input?: any) => Promise<any
|
|
197
|
-
* the constraint avoids the index-signature requirement that would
|
|
196
|
+
* `Record<string, (input?: any) => Promise<any> | void>`. Using `keyof TApi`
|
|
197
|
+
* in the constraint avoids the index-signature requirement that would
|
|
198
198
|
* otherwise force consumers to `as unknown as Record<string, …>` their
|
|
199
|
-
* generated client.
|
|
199
|
+
* generated client. The `| void` arm tolerates `remote_notification`
|
|
200
|
+
* methods, whose `ActionsApi` signature is `(input) => void` even though
|
|
201
|
+
* `create_remote_notification_method` returns a Promise at runtime — the
|
|
202
|
+
* throwing wrapper is intended for `request_response` calls but must
|
|
203
|
+
* accept mixed `ActionsApi` shapes without forcing a cast at the seam.
|
|
200
204
|
*
|
|
201
205
|
* @param api - typed RPC client from `create_rpc_client` (or any object
|
|
202
|
-
* whose values are all `(input?) => Promise
|
|
203
|
-
* the consumer's generated `ActionsApi` interface)
|
|
206
|
+
* whose values are all `(input?) => Promise<...> | void` functions —
|
|
207
|
+
* notably the consumer's generated `ActionsApi` interface)
|
|
204
208
|
*/
|
|
205
209
|
export const create_throwing_rpc_call = (api) => {
|
|
206
210
|
const rec = api;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AAsBxD,OAAO,EAAkB,KAAK,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAEtE,OAAO,EAA+B,KAAK,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAElF,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"account_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_routes.ts"],"names":[],"mappings":"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"}
|
|
@@ -34,7 +34,7 @@ import { get_route_input } from '../http/route_spec.js';
|
|
|
34
34
|
import { get_client_ip } from '../http/proxy.js';
|
|
35
35
|
import { rate_limit_exceeded_response } from '../rate_limiter.js';
|
|
36
36
|
import { Password, PasswordProvided } from './password.js';
|
|
37
|
-
import { ERROR_AUTHENTICATION_REQUIRED, ERROR_INVALID_CREDENTIALS } from '../http/error_schemas.js';
|
|
37
|
+
import { ERROR_AUTHENTICATION_REQUIRED, ERROR_INVALID_CREDENTIALS, ERROR_INVALID_JSON_BODY, ERROR_INVALID_REQUEST_BODY, } from '../http/error_schemas.js';
|
|
38
38
|
/** Input for `GET /api/account/status`. No parameters — caller is the subject. */
|
|
39
39
|
export const AccountStatusInput = z.null();
|
|
40
40
|
/**
|
|
@@ -197,7 +197,12 @@ export const create_account_route_specs = (deps, options) => {
|
|
|
197
197
|
input: LoginInput,
|
|
198
198
|
output: LoginOutput,
|
|
199
199
|
rate_limit: 'both',
|
|
200
|
-
errors: {
|
|
200
|
+
errors: {
|
|
201
|
+
400: z.looseObject({
|
|
202
|
+
error: z.enum([ERROR_INVALID_JSON_BODY, ERROR_INVALID_REQUEST_BODY]),
|
|
203
|
+
}),
|
|
204
|
+
401: z.looseObject({ error: z.literal(ERROR_INVALID_CREDENTIALS) }),
|
|
205
|
+
},
|
|
201
206
|
handler: async (c, route) => {
|
|
202
207
|
// Per-IP rate limit check (before any DB/password work)
|
|
203
208
|
const ip = ip_rate_limiter ? get_client_ip(c) : null;
|
|
@@ -311,6 +316,11 @@ export const create_account_route_specs = (deps, options) => {
|
|
|
311
316
|
input: PasswordChangeInput,
|
|
312
317
|
output: PasswordChangeOutput,
|
|
313
318
|
rate_limit: login_account_rate_limiter ? 'both' : 'ip',
|
|
319
|
+
errors: {
|
|
320
|
+
400: z.looseObject({
|
|
321
|
+
error: z.enum([ERROR_INVALID_JSON_BODY, ERROR_INVALID_REQUEST_BODY]),
|
|
322
|
+
}),
|
|
323
|
+
},
|
|
314
324
|
handler: async (c, route) => {
|
|
315
325
|
// per-IP rate limit check (before argon2 work)
|
|
316
326
|
const ip = ip_rate_limiter ? get_client_ip(c) : null;
|
|
@@ -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;AAEpD,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;
|
|
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;AAEpD,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;AAanD,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;;;kBAG1B,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,CAyHjB,CAAC"}
|
|
@@ -14,7 +14,7 @@ import { Password } from './password.js';
|
|
|
14
14
|
import { get_route_input } from '../http/route_spec.js';
|
|
15
15
|
import { get_client_ip } from '../http/proxy.js';
|
|
16
16
|
import { rate_limit_exceeded_response } from '../rate_limiter.js';
|
|
17
|
-
import { ERROR_BOOTSTRAP_NOT_CONFIGURED, ERROR_INVALID_TOKEN, ERROR_ALREADY_BOOTSTRAPPED, ERROR_TOKEN_FILE_MISSING, } from '../http/error_schemas.js';
|
|
17
|
+
import { ERROR_BOOTSTRAP_NOT_CONFIGURED, ERROR_INVALID_TOKEN, ERROR_ALREADY_BOOTSTRAPPED, ERROR_TOKEN_FILE_MISSING, ERROR_INVALID_JSON_BODY, ERROR_INVALID_REQUEST_BODY, } from '../http/error_schemas.js';
|
|
18
18
|
import { audit_log_fire_and_forget } from './audit_log_queries.js';
|
|
19
19
|
// -- Input/output schemas ---------------------------------------------------
|
|
20
20
|
/** Input for `POST /bootstrap`. `token` is the one-shot token file contents. */
|
|
@@ -81,6 +81,9 @@ export const create_bootstrap_route_specs = (deps, options) => {
|
|
|
81
81
|
output: BootstrapOutput,
|
|
82
82
|
rate_limit: 'ip',
|
|
83
83
|
errors: {
|
|
84
|
+
400: z.looseObject({
|
|
85
|
+
error: z.enum([ERROR_INVALID_JSON_BODY, ERROR_INVALID_REQUEST_BODY]),
|
|
86
|
+
}),
|
|
84
87
|
401: z.looseObject({ error: z.literal(ERROR_INVALID_TOKEN) }),
|
|
85
88
|
403: z.looseObject({ error: z.literal(ERROR_ALREADY_BOOTSTRAPPED) }),
|
|
86
89
|
404: z.looseObject({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signup_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/signup_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAStB,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;
|
|
1
|
+
{"version":3,"file":"signup_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/signup_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAStB,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;AAQhD,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,0BAA0B,CAAC;AAE1D,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,qBAAqB,CAAC;AAEjE;;GAEG;AACH,MAAM,WAAW,kBAAmB,SAAQ,uBAAuB;IAClE,6FAA6F;IAC7F,2BAA2B,EAAE,WAAW,GAAG,IAAI,CAAC;IAChD,yFAAyF;IACzF,YAAY,EAAE,WAAW,CAAC;CAC1B;AAID,0FAA0F;AAC1F,eAAO,MAAM,WAAW;;;;kBAItB,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD,8EAA8E;AAC9E,eAAO,MAAM,YAAY;;kBAEvB,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAExD;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,GACrC,MAAM,gBAAgB,EACtB,SAAS,kBAAkB,KACzB,KAAK,CAAC,SAAS,CA4HjB,CAAC"}
|
|
@@ -16,7 +16,7 @@ import { Password } from './password.js';
|
|
|
16
16
|
import { get_route_input } from '../http/route_spec.js';
|
|
17
17
|
import { get_client_ip } from '../http/proxy.js';
|
|
18
18
|
import { rate_limit_exceeded_response } from '../rate_limiter.js';
|
|
19
|
-
import { ERROR_NO_MATCHING_INVITE, ERROR_SIGNUP_CONFLICT } from '../http/error_schemas.js';
|
|
19
|
+
import { ERROR_NO_MATCHING_INVITE, ERROR_SIGNUP_CONFLICT, ERROR_INVALID_JSON_BODY, ERROR_INVALID_REQUEST_BODY, } from '../http/error_schemas.js';
|
|
20
20
|
import { audit_log_fire_and_forget } from './audit_log_queries.js';
|
|
21
21
|
import { is_pg_unique_violation } from '../db/pg_error.js';
|
|
22
22
|
// -- Input/output schemas ---------------------------------------------------
|
|
@@ -51,6 +51,9 @@ export const create_signup_route_specs = (deps, options) => {
|
|
|
51
51
|
output: SignupOutput,
|
|
52
52
|
rate_limit: signup_account_rate_limiter ? 'both' : 'ip',
|
|
53
53
|
errors: {
|
|
54
|
+
400: z.looseObject({
|
|
55
|
+
error: z.enum([ERROR_INVALID_JSON_BODY, ERROR_INVALID_REQUEST_BODY]),
|
|
56
|
+
}),
|
|
54
57
|
403: z.looseObject({ error: z.literal(ERROR_NO_MATCHING_INVITE) }),
|
|
55
58
|
409: z.looseObject({ error: z.literal(ERROR_SIGNUP_CONFLICT) }),
|
|
56
59
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/db_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,EAAE,EAAE,MAAM,EAAC,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAmB,KAAK,SAAS,EAAC,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"db_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/db_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,EAAE,EAAE,MAAM,EAAC,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAmB,KAAK,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAYjE;;GAEG;AACH,MAAM,WAAW,SAAS;IACzB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,WAAW,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3D,+EAA+E;IAC/E,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GAAI,SAAS,cAAc,KAAG,KAAK,CAAC,SAAS,CA8N9E,CAAC"}
|
package/dist/http/db_routes.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { z } from 'zod';
|
|
10
10
|
import { get_route_params } from './route_spec.js';
|
|
11
|
-
import {
|
|
11
|
+
import { ForeignKeyError, ERROR_TABLE_NOT_FOUND, ERROR_TABLE_NO_PRIMARY_KEY, ERROR_ROW_NOT_FOUND, ERROR_FOREIGN_KEY_VIOLATION, ERROR_INVALID_ROUTE_PARAMS, ERROR_DATABASE_CONNECTION_FAILED, } from './error_schemas.js';
|
|
12
12
|
import { assert_valid_sql_identifier, VALID_SQL_IDENTIFIER } from '../db/sql_identifier.js';
|
|
13
13
|
/**
|
|
14
14
|
* Create the db API route specs.
|
|
@@ -26,7 +26,9 @@ export const create_db_route_specs = (options) => {
|
|
|
26
26
|
description: 'Database health and stats',
|
|
27
27
|
input: z.null(),
|
|
28
28
|
output: z.looseObject({ connected: z.boolean() }),
|
|
29
|
-
errors: {
|
|
29
|
+
errors: {
|
|
30
|
+
503: z.looseObject({ error: z.literal(ERROR_DATABASE_CONNECTION_FAILED) }),
|
|
31
|
+
},
|
|
30
32
|
handler: async (c, route) => {
|
|
31
33
|
try {
|
|
32
34
|
await route.db.query('SELECT 1');
|
|
@@ -46,7 +48,7 @@ export const create_db_route_specs = (options) => {
|
|
|
46
48
|
return c.json({
|
|
47
49
|
connected: false,
|
|
48
50
|
type: db_type,
|
|
49
|
-
error:
|
|
51
|
+
error: ERROR_DATABASE_CONNECTION_FAILED,
|
|
50
52
|
}, 503);
|
|
51
53
|
}
|
|
52
54
|
},
|
|
@@ -82,7 +84,10 @@ export const create_db_route_specs = (options) => {
|
|
|
82
84
|
description: 'Get table columns and rows (paginated)',
|
|
83
85
|
params: z.strictObject({ name: z.string().regex(VALID_SQL_IDENTIFIER) }),
|
|
84
86
|
input: z.null(),
|
|
85
|
-
errors: {
|
|
87
|
+
errors: {
|
|
88
|
+
400: z.looseObject({ error: z.literal(ERROR_INVALID_ROUTE_PARAMS) }),
|
|
89
|
+
404: z.looseObject({ error: z.literal(ERROR_TABLE_NOT_FOUND) }),
|
|
90
|
+
},
|
|
86
91
|
output: z.looseObject({
|
|
87
92
|
columns: z.array(z.strictObject({ column_name: z.string(), data_type: z.string(), is_nullable: z.string() })),
|
|
88
93
|
rows: z.array(z.record(z.string(), z.unknown())),
|
|
@@ -132,6 +137,9 @@ export const create_db_route_specs = (options) => {
|
|
|
132
137
|
input: z.null(),
|
|
133
138
|
output: z.looseObject({ success: z.boolean() }),
|
|
134
139
|
errors: {
|
|
140
|
+
400: z.looseObject({
|
|
141
|
+
error: z.enum([ERROR_INVALID_ROUTE_PARAMS, ERROR_TABLE_NO_PRIMARY_KEY]),
|
|
142
|
+
}),
|
|
135
143
|
404: z.looseObject({
|
|
136
144
|
error: z.enum([ERROR_TABLE_NOT_FOUND, ERROR_ROW_NOT_FOUND]),
|
|
137
145
|
}),
|
|
@@ -83,6 +83,8 @@ export declare const ERROR_TABLE_NOT_FOUND: "table_not_found";
|
|
|
83
83
|
export declare const ERROR_TABLE_NO_PRIMARY_KEY: "table_no_primary_key";
|
|
84
84
|
/** Row with the given PK value not found. */
|
|
85
85
|
export declare const ERROR_ROW_NOT_FOUND: "row_not_found";
|
|
86
|
+
/** Database health-check query failed (connectivity or query error). */
|
|
87
|
+
export declare const ERROR_DATABASE_CONNECTION_FAILED: "database_connection_failed";
|
|
86
88
|
/** Base API error — all JSON error responses have at least `{error: string}`. */
|
|
87
89
|
export declare const ApiError: z.ZodObject<{
|
|
88
90
|
error: z.ZodString;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error_schemas.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/error_schemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAI/C,0CAA0C;AAC1C,eAAO,MAAM,0BAA0B,EAAG,sBAA+B,CAAC;AAE1E,uDAAuD;AACvD,eAAO,MAAM,uBAAuB,EAAG,mBAA4B,CAAC;AAEpE,6CAA6C;AAC7C,eAAO,MAAM,0BAA0B,EAAG,sBAA+B,CAAC;AAE1E,8CAA8C;AAC9C,eAAO,MAAM,0BAA0B,EAAG,sBAA+B,CAAC;AAI1E,wCAAwC;AACxC,eAAO,MAAM,6BAA6B,EAAG,yBAAkC,CAAC;AAEhF,+CAA+C;AAC/C,eAAO,MAAM,8BAA8B,EAAG,0BAAmC,CAAC;AAElF,yCAAyC;AACzC,eAAO,MAAM,yBAAyB,EAAG,qBAA8B,CAAC;AAExE,sFAAsF;AACtF,eAAO,MAAM,yBAAyB,EAAG,qBAA8B,CAAC;AAExE,qDAAqD;AACrD,eAAO,MAAM,uBAAuB,EAAG,mBAA4B,CAAC;AAIpE,uCAAuC;AACvC,eAAO,MAAM,sBAAsB,EAAG,kBAA2B,CAAC;AAElE,wCAAwC;AACxC,eAAO,MAAM,uBAAuB,EAAG,mBAA4B,CAAC;AAEpE,sEAAsE;AACtE,eAAO,MAAM,6BAA6B,EAAG,0CAAmD,CAAC;AAEjG,uEAAuE;AACvE,eAAO,MAAM,mBAAmB,EAAG,eAAwB,CAAC;AAE5D,0CAA0C;AAC1C,eAAO,MAAM,uBAAuB,EAAG,mBAA4B,CAAC;AAIpE,0DAA0D;AAC1D,eAAO,MAAM,kCAAkC,EAAG,8BAAuC,CAAC;AAE1F,wFAAwF;AACxF,eAAO,MAAM,0BAA0B,EAAG,sBAA+B,CAAC;AAE1E,8EAA8E;AAC9E,eAAO,MAAM,mCAAmC,EAAG,+BAAwC,CAAC;AAE5F,uDAAuD;AACvD,eAAO,MAAM,8BAA8B,EAAG,0BAAmC,CAAC;AAIlF,qEAAqE;AACrE,eAAO,MAAM,0BAA0B,EAAG,sBAA+B,CAAC;AAE1E,8CAA8C;AAC9C,eAAO,MAAM,wBAAwB,EAAG,oBAA6B,CAAC;AAEtE,8DAA8D;AAC9D,eAAO,MAAM,8BAA8B,EAAG,0BAAmC,CAAC;AAIlF,0DAA0D;AAC1D,eAAO,MAAM,wBAAwB,EAAG,oBAA6B,CAAC;AAEtE,0GAA0G;AAC1G,eAAO,MAAM,qBAAqB,EAAG,iBAA0B,CAAC;AAEhE,gDAAgD;AAChD,eAAO,MAAM,sBAAsB,EAAG,kBAA2B,CAAC;AAElE,sDAAsD;AACtD,eAAO,MAAM,+BAA+B,EAAG,2BAAoC,CAAC;AAEpF,qEAAqE;AACrE,eAAO,MAAM,sBAAsB,EAAG,kBAA2B,CAAC;AAElE,6DAA6D;AAC7D,eAAO,MAAM,oCAAoC,EAAG,gCAAyC,CAAC;AAE9F,0DAA0D;AAC1D,eAAO,MAAM,iCAAiC,EAAG,6BAAsC,CAAC;AAIxF,6DAA6D;AAC7D,eAAO,MAAM,4BAA4B,EAAG,wBAAiC,CAAC;AAE9E,4DAA4D;AAC5D,eAAO,MAAM,sBAAsB,EAAG,kBAA2B,CAAC;AAElE,oEAAoE;AACpE,eAAO,MAAM,wBAAwB,EAAG,oBAA6B,CAAC;AAItE,kDAAkD;AAClD,eAAO,MAAM,2BAA2B,EAAG,uBAAgC,CAAC;AAE5E,oDAAoD;AACpD,eAAO,MAAM,qBAAqB,EAAG,iBAA0B,CAAC;AAEhE,iEAAiE;AACjE,eAAO,MAAM,0BAA0B,EAAG,sBAA+B,CAAC;AAE1E,6CAA6C;AAC7C,eAAO,MAAM,mBAAmB,EAAG,eAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"error_schemas.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/error_schemas.ts"],"names":[],"mappings":"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"}
|
|
@@ -90,6 +90,8 @@ export const ERROR_TABLE_NOT_FOUND = 'table_not_found';
|
|
|
90
90
|
export const ERROR_TABLE_NO_PRIMARY_KEY = 'table_no_primary_key';
|
|
91
91
|
/** Row with the given PK value not found. */
|
|
92
92
|
export const ERROR_ROW_NOT_FOUND = 'row_not_found';
|
|
93
|
+
/** Database health-check query failed (connectivity or query error). */
|
|
94
|
+
export const ERROR_DATABASE_CONNECTION_FAILED = 'database_connection_failed';
|
|
93
95
|
// --- Standard error shapes ---
|
|
94
96
|
// Using z.looseObject — error responses may carry extra context fields.
|
|
95
97
|
/** Base API error — all JSON error responses have at least `{error: string}`. */
|
package/dist/testing/CLAUDE.md
CHANGED
|
@@ -217,10 +217,23 @@ Tightness audit:
|
|
|
217
217
|
classifies every route × status combination as `'literal' | 'enum' | 'generic'`.
|
|
218
218
|
- `assert_error_schema_tightness(surface, options?)` — fails routes below a
|
|
219
219
|
threshold (`min_specificity`, default `'enum'`) with `allowlist` + `ignore_statuses` escape hatches.
|
|
220
|
-
- `
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
220
|
+
- `FUZ_APP_STOCK_ROUTE_TIGHTNESS_ALLOWLIST` — currently empty. Every
|
|
221
|
+
fuz_app-shipped route (account login/password/bootstrap/signup, db
|
|
222
|
+
health/tables/:name/tables/:name/rows/:id) has been tightened in place to
|
|
223
|
+
`z.enum([...])` / `z.literal(...)` against every emit-site code. Kept as a
|
|
224
|
+
forward-compatibility hook for future stock routes that need an interim
|
|
225
|
+
exemption; paths assume the standard `/api/account` + `/api/db` prefixes.
|
|
226
|
+
- `DEFAULT_ERROR_SCHEMA_TIGHTNESS` — `{ignore_statuses: [401, 403, 429], allowlist: FUZ_APP_STOCK_ROUTE_TIGHTNESS_ALLOWLIST}`.
|
|
227
|
+
Applied by `describe_standard_attack_surface_tests` when
|
|
228
|
+
`error_schema_tightness` is omitted; pass an override config or `null` to
|
|
229
|
+
opt out.
|
|
230
|
+
- **Merge semantics in `describe_standard_attack_surface_tests`**:
|
|
231
|
+
consumer-supplied `allowlist` and `ignore_statuses` are concatenated
|
|
232
|
+
underneath the defaults (stock entries first, consumer entries last),
|
|
233
|
+
so consumer allowlists are additive rather than replacing. Scalar fields
|
|
234
|
+
like `min_specificity` are overwritten by the consumer. Exported as
|
|
235
|
+
`resolve_standard_error_schema_tightness(consumer_options)` for consumers
|
|
236
|
+
calling `assert_error_schema_tightness` directly outside the suite.
|
|
224
237
|
|
|
225
238
|
Aggregate runners (called by the standard attack-surface suite):
|
|
226
239
|
|
|
@@ -20,6 +20,22 @@ export interface AdversarialTestOptions {
|
|
|
20
20
|
* @param options - the test configuration
|
|
21
21
|
*/
|
|
22
22
|
export declare const describe_adversarial_auth: (options: AdversarialTestOptions) => void;
|
|
23
|
+
/**
|
|
24
|
+
* Merge a consumer's `error_schema_tightness` option with
|
|
25
|
+
* `DEFAULT_ERROR_SCHEMA_TIGHTNESS` so `allowlist` and `ignore_statuses` are
|
|
26
|
+
* additive rather than replacing.
|
|
27
|
+
*
|
|
28
|
+
* - `undefined` → return the default as-is.
|
|
29
|
+
* - `null` → return `null` (opt out of the assertion).
|
|
30
|
+
* - object → spread the default, then consumer overrides for scalar fields
|
|
31
|
+
* (`min_specificity`), then concat stock-then-consumer for the list fields
|
|
32
|
+
* (`allowlist`, `ignore_statuses`) so consumer entries extend rather than
|
|
33
|
+
* replace.
|
|
34
|
+
*
|
|
35
|
+
* Exported for direct use when a consumer calls `assert_error_schema_tightness`
|
|
36
|
+
* outside the standard suite but still wants the additive merge.
|
|
37
|
+
*/
|
|
38
|
+
export declare const resolve_standard_error_schema_tightness: (consumer: ErrorSchemaTightnessOptions | null | undefined) => ErrorSchemaTightnessOptions | null;
|
|
23
39
|
/** Options for the standard attack surface test suite. */
|
|
24
40
|
export interface StandardAttackSurfaceOptions {
|
|
25
41
|
/** Build the app surface bundle (surface + route specs + middleware specs). */
|
|
@@ -39,9 +55,14 @@ export interface StandardAttackSurfaceOptions {
|
|
|
39
55
|
/**
|
|
40
56
|
* Error schema tightness assertion config. Defaults to
|
|
41
57
|
* `DEFAULT_ERROR_SCHEMA_TIGHTNESS` (ignores 401/403/429,
|
|
42
|
-
* `min_specificity: 'enum'
|
|
43
|
-
*
|
|
44
|
-
*
|
|
58
|
+
* `min_specificity: 'enum'`, allowlist seeded with
|
|
59
|
+
* `FUZ_APP_STOCK_ROUTE_TIGHTNESS_ALLOWLIST`).
|
|
60
|
+
*
|
|
61
|
+
* Consumer-supplied `allowlist` and `ignore_statuses` are **additive** —
|
|
62
|
+
* the suite merges them underneath the stock defaults, so project-specific
|
|
63
|
+
* entries don't need to re-list fuz_app's own stock routes. Pass a narrower
|
|
64
|
+
* config to extend either list or tighten `min_specificity`; pass `null`
|
|
65
|
+
* to skip the assertion and keep the audit log informational-only.
|
|
45
66
|
*/
|
|
46
67
|
error_schema_tightness?: ErrorSchemaTightnessOptions | null;
|
|
47
68
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attack_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/attack_surface.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAoB7B,OAAO,
|
|
1
|
+
{"version":3,"file":"attack_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/attack_surface.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAoB7B,OAAO,EAON,KAAK,4BAA4B,EACjC,KAAK,2BAA2B,EAChC,MAAM,yBAAyB,CAAC;AAoBjC,OAAO,EAA4B,KAAK,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAsClF,oFAAoF;AACpF,MAAM,WAAW,sBAAsB;IACtC,+EAA+E;IAC/E,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,yDAAyD;IACzD,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACrB;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,GAAI,SAAS,sBAAsB,KAAG,IAkH3E,CAAC;AAIF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,uCAAuC,GACnD,UAAU,2BAA2B,GAAG,IAAI,GAAG,SAAS,KACtD,2BAA2B,GAAG,IAWhC,CAAC;AAEF,0DAA0D;AAC1D,MAAM,WAAW,4BAA4B;IAC5C,+EAA+E;IAC/E,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,yDAAyD;IACzD,aAAa,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,sBAAsB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACtC,gHAAgH;IAChH,uBAAuB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACvC,yDAAyD;IACzD,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACrB,qEAAqE;IACrE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iEAAiE;IACjE,eAAe,CAAC,EAAE,4BAA4B,CAAC;IAC/C;;;;;;;;;;;OAWG;IACH,sBAAsB,CAAC,EAAE,2BAA2B,GAAG,IAAI,CAAC;CAC5D;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,sCAAsC,GAClD,SAAS,4BAA4B,KACnC,IAuEF,CAAC"}
|
|
@@ -15,7 +15,7 @@ import './assert_dev_env.js';
|
|
|
15
15
|
* @module
|
|
16
16
|
*/
|
|
17
17
|
import { test, assert, describe } from 'vitest';
|
|
18
|
-
import { assert_surface_invariants, assert_surface_security_policy, audit_error_schema_tightness, assert_error_schema_tightness, DEFAULT_ERROR_SCHEMA_TIGHTNESS, } from './surface_invariants.js';
|
|
18
|
+
import { assert_surface_invariants, assert_surface_security_policy, audit_error_schema_tightness, assert_error_schema_tightness, DEFAULT_ERROR_SCHEMA_TIGHTNESS, FUZ_APP_STOCK_ROUTE_TIGHTNESS_ALLOWLIST, } from './surface_invariants.js';
|
|
19
19
|
import { describe_adversarial_input } from './adversarial_input.js';
|
|
20
20
|
import { describe_adversarial_404 } from './adversarial_404.js';
|
|
21
21
|
import { create_test_app_from_specs, create_test_request_context, create_auth_test_apps, select_auth_app, resolve_test_path, } from './auth_apps.js';
|
|
@@ -157,6 +157,35 @@ export const describe_adversarial_auth = (options) => {
|
|
|
157
157
|
});
|
|
158
158
|
});
|
|
159
159
|
};
|
|
160
|
+
// --- Standard attack surface test suite ---
|
|
161
|
+
/**
|
|
162
|
+
* Merge a consumer's `error_schema_tightness` option with
|
|
163
|
+
* `DEFAULT_ERROR_SCHEMA_TIGHTNESS` so `allowlist` and `ignore_statuses` are
|
|
164
|
+
* additive rather than replacing.
|
|
165
|
+
*
|
|
166
|
+
* - `undefined` → return the default as-is.
|
|
167
|
+
* - `null` → return `null` (opt out of the assertion).
|
|
168
|
+
* - object → spread the default, then consumer overrides for scalar fields
|
|
169
|
+
* (`min_specificity`), then concat stock-then-consumer for the list fields
|
|
170
|
+
* (`allowlist`, `ignore_statuses`) so consumer entries extend rather than
|
|
171
|
+
* replace.
|
|
172
|
+
*
|
|
173
|
+
* Exported for direct use when a consumer calls `assert_error_schema_tightness`
|
|
174
|
+
* outside the standard suite but still wants the additive merge.
|
|
175
|
+
*/
|
|
176
|
+
export const resolve_standard_error_schema_tightness = (consumer) => {
|
|
177
|
+
if (consumer === null)
|
|
178
|
+
return null;
|
|
179
|
+
return {
|
|
180
|
+
...DEFAULT_ERROR_SCHEMA_TIGHTNESS,
|
|
181
|
+
...consumer,
|
|
182
|
+
allowlist: [...FUZ_APP_STOCK_ROUTE_TIGHTNESS_ALLOWLIST, ...(consumer?.allowlist ?? [])],
|
|
183
|
+
ignore_statuses: [
|
|
184
|
+
...(DEFAULT_ERROR_SCHEMA_TIGHTNESS.ignore_statuses ?? []),
|
|
185
|
+
...(consumer?.ignore_statuses ?? []),
|
|
186
|
+
],
|
|
187
|
+
};
|
|
188
|
+
};
|
|
160
189
|
/**
|
|
161
190
|
* Run the standard attack surface test suite.
|
|
162
191
|
*
|
|
@@ -178,7 +207,8 @@ export const describe_adversarial_auth = (options) => {
|
|
|
178
207
|
* @param options - the test configuration
|
|
179
208
|
*/
|
|
180
209
|
export const describe_standard_attack_surface_tests = (options) => {
|
|
181
|
-
const { build, snapshot_path, expected_public_routes, expected_api_middleware, roles, api_path_prefix = '/api/', security_policy,
|
|
210
|
+
const { build, snapshot_path, expected_public_routes, expected_api_middleware, roles, api_path_prefix = '/api/', security_policy, } = options;
|
|
211
|
+
const error_schema_tightness = resolve_standard_error_schema_tightness(options.error_schema_tightness);
|
|
182
212
|
const built = build();
|
|
183
213
|
const { surface } = built;
|
|
184
214
|
describe('attack surface snapshot', () => {
|
|
@@ -158,15 +158,38 @@ export interface ErrorSchemaTightnessOptions {
|
|
|
158
158
|
/** Routes to skip, in `'METHOD /path'` format. */
|
|
159
159
|
allowlist?: Array<string>;
|
|
160
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* Routes shipped by fuz_app whose error schemas require a tightness exemption.
|
|
163
|
+
*
|
|
164
|
+
* Currently empty — every fuz_app-shipped route (account login/password/
|
|
165
|
+
* bootstrap/signup, db health/tables/:name/tables/:name/rows/:id) was tightened
|
|
166
|
+
* in place to `z.enum([...])` / `z.literal(...)` against every emit-site error
|
|
167
|
+
* code.
|
|
168
|
+
*
|
|
169
|
+
* Kept as a forward-compatibility hook: when new stock routes ship with
|
|
170
|
+
* heterogeneous error surfaces that need an interim generic schema, add
|
|
171
|
+
* them here instead of forcing every consumer to hand-maintain the entry.
|
|
172
|
+
*
|
|
173
|
+
* Paths assume the standard `/api/account` + `/api/db` prefixes used by every
|
|
174
|
+
* fuz_app consumer. Merged into `DEFAULT_ERROR_SCHEMA_TIGHTNESS.allowlist` so
|
|
175
|
+
* consumers calling `assert_error_schema_tightness` directly inherit the
|
|
176
|
+
* exemptions; the standard attack-surface suite also prepends these entries
|
|
177
|
+
* underneath any consumer-supplied allowlist so project-specific entries are
|
|
178
|
+
* additive.
|
|
179
|
+
*/
|
|
180
|
+
export declare const FUZ_APP_STOCK_ROUTE_TIGHTNESS_ALLOWLIST: ReadonlyArray<string>;
|
|
161
181
|
/**
|
|
162
182
|
* Baseline error schema tightness applied by
|
|
163
183
|
* `describe_standard_attack_surface_tests` when no config is passed.
|
|
164
184
|
*
|
|
165
185
|
* Uses `min_specificity: 'enum'` (the assertion default) with `ignore_statuses`
|
|
166
186
|
* for middleware-derived status codes that are commonly generic (auth middleware
|
|
167
|
-
* produces multiple error codes at 401/403, and 429 comes from rate limiters)
|
|
168
|
-
*
|
|
169
|
-
*
|
|
187
|
+
* produces multiple error codes at 401/403, and 429 comes from rate limiters),
|
|
188
|
+
* and `allowlist` seeded with `FUZ_APP_STOCK_ROUTE_TIGHTNESS_ALLOWLIST` so
|
|
189
|
+
* fuz_app-shipped routes with heterogeneous generic schemas don't force every
|
|
190
|
+
* consumer to hand-maintain an identical allowlist. Consumers can pass a
|
|
191
|
+
* narrower config with project-specific `allowlist` entries, or pass `null`
|
|
192
|
+
* to skip the assertion entirely.
|
|
170
193
|
*/
|
|
171
194
|
export declare const DEFAULT_ERROR_SCHEMA_TIGHTNESS: ErrorSchemaTightnessOptions;
|
|
172
195
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"surface_invariants.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/surface_invariants.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"surface_invariants.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/surface_invariants.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAuB7B,OAAO,KAAK,EAAC,UAAU,EAAuB,MAAM,oBAAoB,CAAC;AAczE;;GAEG;AACH,eAAO,MAAM,mCAAmC,GAAI,SAAS,UAAU,KAAG,IAQzE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,8BAA8B,GAAI,SAAS,UAAU,KAAG,IASpE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,+BAA+B,GAAI,SAAS,UAAU,KAAG,IAQrE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,gCAAgC,GAAI,SAAS,UAAU,KAAG,IAQtE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,+BAA+B,GAAI,SAAS,UAAU,KAAG,IAQrE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,2BAA2B,GAAI,SAAS,UAAU,KAAG,IAIjE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,0BAA0B,GAAI,SAAS,UAAU,KAAG,IAOhE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,mCAAmC,GAAI,SAAS,UAAU,KAAG,IAezE,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,uCAAuC,GAAI,SAAS,UAAU,KAAG,IAgB7E,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oCAAoC,GAAI,SAAS,UAAU,KAAG,IAuC1E,CAAC;AA0CF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,sCAAsC,GAAI,SAAS,UAAU,KAAG,IAU5E,CAAC;AAIF,4DAA4D;AAC5D,MAAM,MAAM,sBAAsB,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;AAEpE,iEAAiE;AACjE,MAAM,WAAW,qBAAqB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,sBAAsB,CAAC;IACpC,qDAAqD;IACrD,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;CAClC;AA+BD;;;;;;;;;GASG;AACH,eAAO,MAAM,4BAA4B,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,qBAAqB,CAgB7F,CAAC;AAIF;;;;GAIG;AACH,MAAM,WAAW,4BAA4B;IAC5C;;;;;OAKG;IACH,wBAAwB,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IAClD;;;OAGG;IACH,yBAAyB,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1C;;;OAGG;IACH,qBAAqB,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACtC;AASD;;;;;;GAMG;AACH,eAAO,MAAM,oCAAoC,GAChD,SAAS,UAAU,EACnB,qBAAoB,KAAK,CAAC,MAAM,GAAG,MAAM,CAA8B,KACrE,IAcF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,qCAAqC,GACjD,SAAS,UAAU,EACnB,YAAW,KAAK,CAAC,MAAM,CAAM,KAC3B,IAYF,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,+BAA+B,GAAI,SAAS,UAAU,KAAG,IAQrE,CAAC;AAKF;;;;;GAKG;AACH,eAAO,MAAM,iCAAiC,GAC7C,SAAS,UAAU,EACnB,WAAU,KAAK,CAAC,MAAM,CAAiC,KACrD,IASF,CAAC;AAWF,mDAAmD;AACnD,MAAM,WAAW,2BAA2B;IAC3C,6FAA6F;IAC7F,eAAe,CAAC,EAAE,sBAAsB,CAAC;IACzC,mEAAmE;IACnE,eAAe,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAChC,kDAAkD;IAClD,SAAS,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,uCAAuC,EAAE,aAAa,CAAC,MAAM,CAAM,CAAC;AAEjF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,8BAA8B,EAAE,2BAG5C,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,6BAA6B,GACzC,SAAS,UAAU,EACnB,UAAU,2BAA2B,KACnC,IAsBF,CAAC;AAIF;;GAEG;AACH,eAAO,MAAM,yBAAyB,GAAI,SAAS,UAAU,KAAG,IAY/D,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,8BAA8B,GAC1C,SAAS,UAAU,EACnB,UAAS,4BAAiC,KACxC,IAKF,CAAC"}
|
|
@@ -379,18 +379,42 @@ const SPECIFICITY_ORDER = {
|
|
|
379
379
|
enum: 1,
|
|
380
380
|
generic: 0,
|
|
381
381
|
};
|
|
382
|
+
/**
|
|
383
|
+
* Routes shipped by fuz_app whose error schemas require a tightness exemption.
|
|
384
|
+
*
|
|
385
|
+
* Currently empty — every fuz_app-shipped route (account login/password/
|
|
386
|
+
* bootstrap/signup, db health/tables/:name/tables/:name/rows/:id) was tightened
|
|
387
|
+
* in place to `z.enum([...])` / `z.literal(...)` against every emit-site error
|
|
388
|
+
* code.
|
|
389
|
+
*
|
|
390
|
+
* Kept as a forward-compatibility hook: when new stock routes ship with
|
|
391
|
+
* heterogeneous error surfaces that need an interim generic schema, add
|
|
392
|
+
* them here instead of forcing every consumer to hand-maintain the entry.
|
|
393
|
+
*
|
|
394
|
+
* Paths assume the standard `/api/account` + `/api/db` prefixes used by every
|
|
395
|
+
* fuz_app consumer. Merged into `DEFAULT_ERROR_SCHEMA_TIGHTNESS.allowlist` so
|
|
396
|
+
* consumers calling `assert_error_schema_tightness` directly inherit the
|
|
397
|
+
* exemptions; the standard attack-surface suite also prepends these entries
|
|
398
|
+
* underneath any consumer-supplied allowlist so project-specific entries are
|
|
399
|
+
* additive.
|
|
400
|
+
*/
|
|
401
|
+
export const FUZ_APP_STOCK_ROUTE_TIGHTNESS_ALLOWLIST = [];
|
|
382
402
|
/**
|
|
383
403
|
* Baseline error schema tightness applied by
|
|
384
404
|
* `describe_standard_attack_surface_tests` when no config is passed.
|
|
385
405
|
*
|
|
386
406
|
* Uses `min_specificity: 'enum'` (the assertion default) with `ignore_statuses`
|
|
387
407
|
* for middleware-derived status codes that are commonly generic (auth middleware
|
|
388
|
-
* produces multiple error codes at 401/403, and 429 comes from rate limiters)
|
|
389
|
-
*
|
|
390
|
-
*
|
|
408
|
+
* produces multiple error codes at 401/403, and 429 comes from rate limiters),
|
|
409
|
+
* and `allowlist` seeded with `FUZ_APP_STOCK_ROUTE_TIGHTNESS_ALLOWLIST` so
|
|
410
|
+
* fuz_app-shipped routes with heterogeneous generic schemas don't force every
|
|
411
|
+
* consumer to hand-maintain an identical allowlist. Consumers can pass a
|
|
412
|
+
* narrower config with project-specific `allowlist` entries, or pass `null`
|
|
413
|
+
* to skip the assertion entirely.
|
|
391
414
|
*/
|
|
392
415
|
export const DEFAULT_ERROR_SCHEMA_TIGHTNESS = {
|
|
393
416
|
ignore_statuses: [401, 403, 429],
|
|
417
|
+
allowlist: [...FUZ_APP_STOCK_ROUTE_TIGHTNESS_ALLOWLIST],
|
|
394
418
|
};
|
|
395
419
|
/**
|
|
396
420
|
* Assert that all error schemas meet a minimum specificity threshold.
|