@fuzdev/fuz_app 0.148.0 → 0.149.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 +2 -2
- package/dist/actions/action_event.d.ts.map +1 -1
- package/dist/actions/action_event.js +14 -1
- package/dist/actions/perform_action.d.ts +17 -1
- package/dist/actions/perform_action.d.ts.map +1 -1
- package/dist/actions/perform_action.js +22 -2
- package/dist/auth/CLAUDE.md +3 -2
- package/dist/auth/account_action_specs.d.ts +12 -0
- package/dist/auth/account_action_specs.d.ts.map +1 -1
- package/dist/auth/account_action_specs.js +16 -2
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +7 -4
- package/dist/auth/admin_action_specs.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.js +29 -8
- package/dist/auth/audit_emitter.d.ts +14 -0
- package/dist/auth/audit_emitter.d.ts.map +1 -1
- package/dist/auth/audit_emitter.js +5 -1
- package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_action_specs.js +5 -3
- package/dist/http/CLAUDE.md +14 -2
- package/dist/http/error_schemas.d.ts +8 -0
- package/dist/http/error_schemas.d.ts.map +1 -1
- package/dist/http/error_schemas.js +12 -0
- package/dist/http/jsonrpc_errors.d.ts +40 -15
- package/dist/http/jsonrpc_errors.d.ts.map +1 -1
- package/dist/http/jsonrpc_errors.js +90 -35
- package/dist/schema_meta.d.ts +60 -0
- package/dist/schema_meta.d.ts.map +1 -1
- package/dist/schema_meta.js +64 -0
- package/dist/testing/CLAUDE.md +162 -61
- package/dist/testing/adversarial_input_cases.d.ts +67 -7
- package/dist/testing/adversarial_input_cases.d.ts.map +1 -1
- package/dist/testing/adversarial_input_cases.js +210 -165
- package/dist/testing/audit_drift_guard.d.ts +3 -2
- package/dist/testing/audit_drift_guard.d.ts.map +1 -1
- package/dist/testing/audit_drift_guard.js +5 -3
- package/dist/testing/cross_backend/audit_cross_helpers.d.ts +6 -0
- package/dist/testing/cross_backend/audit_cross_helpers.d.ts.map +1 -1
- package/dist/testing/cross_backend/audit_cross_helpers.js +11 -0
- package/dist/testing/cross_backend/capabilities.d.ts +27 -4
- package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
- package/dist/testing/cross_backend/capabilities.js +10 -6
- package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_crud.js +117 -2
- package/dist/testing/cross_backend/cross_rpc.d.ts +3 -1
- package/dist/testing/cross_backend/cross_rpc.d.ts.map +1 -1
- package/dist/testing/cross_backend/cross_rpc.js +2 -2
- package/dist/testing/cross_backend/default_backend_configs.d.ts +9 -7
- package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_backend_configs.js +3 -3
- package/dist/testing/cross_backend/rpc_adversarial.d.ts +9 -0
- package/dist/testing/cross_backend/rpc_adversarial.d.ts.map +1 -1
- package/dist/testing/cross_backend/rpc_adversarial.js +16 -6
- package/dist/testing/cross_backend/rust_spine_consumer_surface.d.ts +1 -1
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +6 -3
- package/dist/testing/cross_backend/setup.d.ts +2 -2
- package/dist/testing/cross_backend/spine_suites.d.ts +85 -5
- package/dist/testing/cross_backend/spine_suites.d.ts.map +1 -1
- package/dist/testing/cross_backend/spine_suites.js +291 -20
- package/dist/testing/cross_backend/spine_surface_constants.d.ts +15 -0
- package/dist/testing/cross_backend/spine_surface_constants.d.ts.map +1 -1
- package/dist/testing/cross_backend/spine_surface_constants.js +24 -0
- package/dist/testing/cross_backend/testing_backdoor.d.ts +27 -4
- package/dist/testing/cross_backend/testing_backdoor.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_backdoor.js +136 -41
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +30 -15
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.js +52 -35
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/ts_spine_backend_config.js +6 -3
- package/dist/testing/rpc_attack_surface.d.ts +10 -0
- package/dist/testing/rpc_attack_surface.d.ts.map +1 -1
- package/dist/testing/rpc_attack_surface.js +15 -3
- package/dist/testing/rpc_envelope_cases.d.ts +5 -2
- package/dist/testing/rpc_envelope_cases.d.ts.map +1 -1
- package/dist/testing/rpc_envelope_cases.js +4 -1
- package/dist/testing/rpc_round_trip.js +3 -3
- package/dist/testing/schema_generators.d.ts +99 -2
- package/dist/testing/schema_generators.d.ts.map +1 -1
- package/dist/testing/schema_generators.js +244 -76
- package/dist/testing/stubs.d.ts +4 -3
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +6 -4
- package/dist/zod.d.ts +9 -0
- package/dist/zod.d.ts.map +1 -1
- package/dist/zod.js +20 -0
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import '../assert_dev_env.js';
|
|
2
2
|
import { build_test_backend_paths } from './build_test_backend_paths.js';
|
|
3
3
|
import { action_rate_limit_env, make_default_ts_backend_config, ts_default_capabilities } from './default_backend_configs.js';
|
|
4
|
-
import { LOGIN_RATE_LIMIT_ENABLED_ENV, SPINE_SSE_PATH, TS_SPINE_DIR_ENV } from './spine_surface_constants.js';
|
|
4
|
+
import { LOGIN_RATE_LIMIT_ENABLED_ENV, SPINE_SSE_PATH, TESTING_BACKDOOR_METHODS, TS_SPINE_DIR_ENV } from './spine_surface_constants.js';
|
|
5
5
|
/**
|
|
6
6
|
* Capabilities for the TS spine binary — `ts_default_capabilities` with every `MountFlag` stated, all `true`: `ws`
|
|
7
7
|
* (the binary mounts `/api/ws` through `create_app_server`'s `ws_endpoints`),
|
|
@@ -16,7 +16,9 @@ import { LOGIN_RATE_LIMIT_ENABLED_ENV, SPINE_SSE_PATH, TS_SPINE_DIR_ENV } from '
|
|
|
16
16
|
* the account actions among them),
|
|
17
17
|
* `cell_gated_create` (the binary's `full_spine_mount` mounts the
|
|
18
18
|
* `test_cell_gated_create_authorize` policy), and `ws_role_gated` (the binary
|
|
19
|
-
* mounts the role-gated WS endpoint at `SPINE_ROLE_GATED_WS_PATH`)
|
|
19
|
+
* mounts the role-gated WS endpoint at `SPINE_ROLE_GATED_WS_PATH`), and every
|
|
20
|
+
* `TESTING_BACKDOOR_METHODS` entry as `testing_backdoors` (`full_spine_mount`
|
|
21
|
+
* mounts them all, on `/api/rpc` and `/api/ws`). The Node
|
|
20
22
|
* binary's preset; `ws_refusal_body` stays `false`, as `@hono/node-ws` drops a
|
|
21
23
|
* refused upgrade's body.
|
|
22
24
|
*/
|
|
@@ -32,7 +34,8 @@ const ts_spine_capabilities = Object.freeze({
|
|
|
32
34
|
peer_request: true,
|
|
33
35
|
ws_account_actions: true,
|
|
34
36
|
cell_gated_create: true,
|
|
35
|
-
ws_role_gated: true
|
|
37
|
+
ws_role_gated: true,
|
|
38
|
+
testing_backdoors: TESTING_BACKDOOR_METHODS
|
|
36
39
|
});
|
|
37
40
|
/**
|
|
38
41
|
* Capabilities for the **Deno** spine binary — `ts_spine_capabilities` with
|
|
@@ -6,6 +6,16 @@ export interface RpcAttackSurfaceOptions {
|
|
|
6
6
|
build: () => AppSurfaceSpec;
|
|
7
7
|
/** All roles in the app (e.g. `['admin', 'keeper']`). */
|
|
8
8
|
roles: Array<string>;
|
|
9
|
+
/**
|
|
10
|
+
* Valid params per method (method name → params) to build its adversarial
|
|
11
|
+
* params cases from, in place of the generated base — for an input whose
|
|
12
|
+
* valid shape the generator can't reach. Each must pass the method's input
|
|
13
|
+
* schema; one that doesn't throws at collection, naming the method. The
|
|
14
|
+
* cases are sent as a single-actor principal, so an override omits
|
|
15
|
+
* `acting`. The cross-process twin is
|
|
16
|
+
* `RpcAdversarialCrossTestOptions.input_overrides`.
|
|
17
|
+
*/
|
|
18
|
+
input_overrides?: ReadonlyMap<string, Record<string, unknown>>;
|
|
9
19
|
}
|
|
10
20
|
/**
|
|
11
21
|
* Run the standard RPC attack surface test suite.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc_attack_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/rpc_attack_surface.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAkB7B,OAAO,KAAK,EAGX,cAAc,EACd,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"rpc_attack_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/rpc_attack_surface.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAkB7B,OAAO,KAAK,EAGX,cAAc,EACd,MAAM,oBAAoB,CAAC;AA+B5B,uDAAuD;AACvD,MAAM,WAAW,uBAAuB;IACvC,+FAA+F;IAC/F,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,yDAAyD;IACzD,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACrB;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC/D;AA2VD;;;;;;;;;GASG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,uBAAuB,KAAG,IAOpF,CAAC"}
|
|
@@ -19,6 +19,7 @@ import { create_auth_test_apps, create_test_app_from_specs, create_test_request_
|
|
|
19
19
|
import { generate_valid_body } from './schema_generators.js';
|
|
20
20
|
import { create_rpc_post_init, create_rpc_get_url, assert_jsonrpc_error_response } from './rpc_helpers.js';
|
|
21
21
|
import { assert_rpc_envelope_refusal, create_rpc_envelope_cases, generate_rpc_params_test_cases, to_rpc_envelope_request } from './rpc_envelope_cases.js';
|
|
22
|
+
import { expected_invalid_reason } from './adversarial_input_cases.js';
|
|
22
23
|
// --- Helpers ---
|
|
23
24
|
/** Filter RPC methods that require any form of authentication. */
|
|
24
25
|
const filter_protected_rpc_methods = (endpoint) => endpoint.methods.filter((m) => !is_public_auth(m.auth));
|
|
@@ -201,8 +202,10 @@ const describe_rpc_adversarial_envelopes = (options) => {
|
|
|
201
202
|
*
|
|
202
203
|
* For each method with a non-null input schema, generates test cases
|
|
203
204
|
* from the schema (wrong types, missing fields, format violations)
|
|
204
|
-
* and wraps them in valid JSON-RPC envelopes
|
|
205
|
-
* `
|
|
205
|
+
* and wraps them in valid JSON-RPC envelopes, via `generate_rpc_params_test_cases`;
|
|
206
|
+
* a method's `input_overrides` entry, when given, is the base they're built from.
|
|
207
|
+
* Each owes 400 `invalid_params` with the broken field's declared
|
|
208
|
+
* `invalid_reason` as `data.reason`, or no reason (`expected_invalid_reason`).
|
|
206
209
|
*/
|
|
207
210
|
const describe_rpc_adversarial_params = (options) => {
|
|
208
211
|
const { build, roles } = options;
|
|
@@ -226,7 +229,10 @@ const describe_rpc_adversarial_params = (options) => {
|
|
|
226
229
|
});
|
|
227
230
|
continue;
|
|
228
231
|
}
|
|
229
|
-
const test_cases = generate_rpc_params_test_cases(action.spec.input
|
|
232
|
+
const test_cases = generate_rpc_params_test_cases(action.spec.input, {
|
|
233
|
+
base: options.input_overrides?.get(method.name),
|
|
234
|
+
label: method.name
|
|
235
|
+
});
|
|
230
236
|
if (test_cases.length === 0)
|
|
231
237
|
continue;
|
|
232
238
|
total_cases += test_cases.length;
|
|
@@ -238,6 +244,12 @@ const describe_rpc_adversarial_params = (options) => {
|
|
|
238
244
|
assert.strictEqual(res.status, 400, `Expected 400 for ${method.name} [${tc.label}], got ${res.status}`);
|
|
239
245
|
const body = await res.json();
|
|
240
246
|
assert_jsonrpc_error_response(body, JSONRPC_ERROR_CODES.invalid_params);
|
|
247
|
+
// the broken field's declared reason, or none
|
|
248
|
+
const data = body.error.data;
|
|
249
|
+
const reason = typeof data === 'object' && data !== null
|
|
250
|
+
? data.reason
|
|
251
|
+
: undefined;
|
|
252
|
+
assert.strictEqual(reason, expected_invalid_reason(action.spec.input, tc), `data.reason for ${method.name} [${tc.label}]`);
|
|
241
253
|
});
|
|
242
254
|
}
|
|
243
255
|
});
|
|
@@ -2,7 +2,7 @@ import './assert_dev_env.js';
|
|
|
2
2
|
import type { z } from 'zod';
|
|
3
3
|
import type { AppSurfaceRpcEndpoint, AppSurfaceRpcMethod } from '../http/surface.js';
|
|
4
4
|
import type { JsonrpcErrorCode, JsonrpcRequestId } from '../http/jsonrpc.js';
|
|
5
|
-
import { type InputTestCase } from './adversarial_input_cases.js';
|
|
5
|
+
import { type InputTestCase, type InputTestCaseOptions } from './adversarial_input_cases.js';
|
|
6
6
|
/** The request id every envelope case sends where it sends one. */
|
|
7
7
|
export declare const RPC_ENVELOPE_PROBE_ID = "envelope_probe";
|
|
8
8
|
/**
|
|
@@ -105,7 +105,10 @@ export declare const assert_rpc_envelope_refusal: (status: number, body: unknown
|
|
|
105
105
|
* cover it. Every case returned owes 400 `invalid_params`.
|
|
106
106
|
*
|
|
107
107
|
* @param input - the method's input schema
|
|
108
|
+
* @param options - a valid base `params` to seed the cases (`InputTestCaseOptions.base`),
|
|
109
|
+
* and the method name for error messages
|
|
108
110
|
* @returns the cases, each a `params` object
|
|
111
|
+
* @throws Error if the base `params`, seeded or generated, fails the input schema
|
|
109
112
|
*/
|
|
110
|
-
export declare const generate_rpc_params_test_cases: (input: z.ZodType) => Array<InputTestCase>;
|
|
113
|
+
export declare const generate_rpc_params_test_cases: (input: z.ZodType, options?: InputTestCaseOptions) => Array<InputTestCase>;
|
|
111
114
|
//# sourceMappingURL=rpc_envelope_cases.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc_envelope_cases.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/rpc_envelope_cases.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAe7B,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,KAAK,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAGrF,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAK7E,OAAO,
|
|
1
|
+
{"version":3,"file":"rpc_envelope_cases.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/rpc_envelope_cases.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAe7B,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,KAAK,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAGrF,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAK7E,OAAO,EAEN,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,MAAM,8BAA8B,CAAC;AAItC,mEAAmE;AACnE,eAAO,MAAM,qBAAqB,mBAAmB,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,EAAE,YAAY,GAAG,WAAW,CAAC;IACxC,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,gEAAgE;IAChE,QAAQ,CAAC,OAAO,EACb;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAChD;QAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACpD,QAAQ,CAAC,MAAM,EAAE;QAChB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;QAChC,mFAAmF;QACnF,QAAQ,CAAC,EAAE,EAAE,gBAAgB,GAAG,IAAI,CAAC;QACrC,mEAAmE;QACnE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;KAC/B,CAAC;CACF;AAED,+CAA+C;AAC/C,MAAM,WAAW,sBAAsB;IACtC,oDAAoD;IACpD,QAAQ,CAAC,YAAY,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC9C;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,aAAa,CAAC,MAAM,CAAgC,CAAC;AAEzF,wFAAwF;AACxF,eAAO,MAAM,2BAA2B,GAAI,QAAQ,MAAM,KAAG,MACL,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,yBAAyB,GACrC,UAAU,qBAAqB,EAC/B,UAAS,sBAA2B,KAClC,KAAK,CAAC,eAAe,CAmKvB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,GACnC,eAAe,MAAM,EACrB,GAAG,eAAe,EAClB,UAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,KAClC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,WAAW,CAAA;CAUgD,CAAC;AAEpF;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,GACvC,QAAQ,MAAM,EACd,MAAM,OAAO,EACb,GAAG,eAAe,KAChB,IAUF,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,8BAA8B,GAC1C,OAAO,CAAC,CAAC,OAAO,EAChB,UAAU,oBAAoB,KAC5B,KAAK,CAAC,aAAa,CAGpB,CAAC"}
|
|
@@ -235,6 +235,9 @@ export const assert_rpc_envelope_refusal = (status, body, c) => {
|
|
|
235
235
|
* cover it. Every case returned owes 400 `invalid_params`.
|
|
236
236
|
*
|
|
237
237
|
* @param input - the method's input schema
|
|
238
|
+
* @param options - a valid base `params` to seed the cases (`InputTestCaseOptions.base`),
|
|
239
|
+
* and the method name for error messages
|
|
238
240
|
* @returns the cases, each a `params` object
|
|
241
|
+
* @throws Error if the base `params`, seeded or generated, fails the input schema
|
|
239
242
|
*/
|
|
240
|
-
export const generate_rpc_params_test_cases = (input) => generate_input_test_cases(input).filter((tc) => tc.expected_error !== ERROR_INVALID_JSON_BODY);
|
|
243
|
+
export const generate_rpc_params_test_cases = (input, options) => generate_input_test_cases(input, options).filter((tc) => tc.expected_error !== ERROR_INVALID_JSON_BODY);
|
|
@@ -28,7 +28,7 @@ import './assert_dev_env.js';
|
|
|
28
28
|
import { describe, test, beforeAll, assert } from 'vitest';
|
|
29
29
|
import { ROLE_ADMIN } from '../auth/role_schema.js';
|
|
30
30
|
import { JSONRPC_INTERNAL_ERROR, JSONRPC_METHOD_NOT_FOUND, JsonrpcErrorResponse } from '../http/jsonrpc.js';
|
|
31
|
-
import {
|
|
31
|
+
import { jsonrpc_error_code_to_http_status } from '../http/jsonrpc_errors.js';
|
|
32
32
|
import { generate_valid_body } from './schema_generators.js';
|
|
33
33
|
import { create_rpc_post_init, create_rpc_get_url, assert_jsonrpc_error_response, assert_jsonrpc_success_response, resolve_rpc_endpoints_for_setup, find_rpc_action, find_rpc_method } from './rpc_helpers.js';
|
|
34
34
|
import { is_unauthenticated_refusal, pick_auth_principal, round_trip_uses_fresh_account, to_principal_headers } from './integration_helpers.js';
|
|
@@ -112,7 +112,7 @@ const read_rpc_body = async (res) => {
|
|
|
112
112
|
}
|
|
113
113
|
};
|
|
114
114
|
/** The HTTP status an `internal_error` maps to, read off the shared status table. */
|
|
115
|
-
const INTERNAL_ERROR_HTTP_STATUS =
|
|
115
|
+
const INTERNAL_ERROR_HTTP_STATUS = jsonrpc_error_code_to_http_status(JSONRPC_INTERNAL_ERROR);
|
|
116
116
|
/**
|
|
117
117
|
* Guard against a backend that crashes on schema-valid input. A response is
|
|
118
118
|
* a crash when any of these holds:
|
|
@@ -125,7 +125,7 @@ const INTERNAL_ERROR_HTTP_STATUS = JSONRPC_ERROR_CODE_TO_HTTP_STATUS[JSONRPC_INT
|
|
|
125
125
|
*
|
|
126
126
|
* Any other ≥500 that carries a well-formed envelope with a declared code
|
|
127
127
|
* passes like a 4xx: `service_unavailable` (503), `timeout` (504), or a
|
|
128
|
-
* consumer's own code
|
|
128
|
+
* consumer's own code declared with `register_jsonrpc_error_code` (a 502).
|
|
129
129
|
* Those are server conditions a handler declares on purpose — a dependency
|
|
130
130
|
* the test environment doesn't provide — while an undeclared 500 or -32603
|
|
131
131
|
* is a handler that failed where it owed an answer.
|
|
@@ -11,12 +11,109 @@ import './assert_dev_env.js';
|
|
|
11
11
|
import { z } from 'zod';
|
|
12
12
|
import { type ZodFieldInfo } from '../zod.js';
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* The JSON Schema of a field's input side — where its format, pattern and
|
|
15
|
+
* bounds live — or `null` when JSON Schema can't render the field or any part
|
|
16
|
+
* of it (`z.date()`, `z.custom()`, a union with a `z.date()` branch).
|
|
17
|
+
*
|
|
18
|
+
* Rendered with `io: 'input'`, so a field carrying a `.transform()` or a pipe
|
|
19
|
+
* (`Username`) still yields the constraints its input is checked against;
|
|
20
|
+
* the default output side throws on a transform. A pipe yields its input
|
|
21
|
+
* side only, so constraints of the stage after it are not here. A
|
|
22
|
+
* `.nullable()` / `.nullish()` field renders as
|
|
23
|
+
* `anyOf: [{…}, {type: 'null'}]`; its one non-null branch is returned, so
|
|
24
|
+
* the bounds and pattern read the same as on the bare field. A union of two
|
|
25
|
+
* or more non-null branches returns the whole schema — its branches' bounds
|
|
26
|
+
* are no single field's bounds.
|
|
27
|
+
*
|
|
28
|
+
* A `.refine()` is never here: JSON Schema can't express one, so a bound a
|
|
29
|
+
* refinement enforces (a code-point length, a cross-field rule) generates no
|
|
30
|
+
* boundary case.
|
|
31
|
+
*
|
|
32
|
+
* @param field_schema - the field to read
|
|
33
|
+
* @returns the field's input-side JSON Schema, or `null` when it can't be rendered
|
|
34
|
+
*/
|
|
35
|
+
export declare const to_field_json_schema: (field_schema: z.ZodType) => Record<string, unknown> | null;
|
|
36
|
+
/**
|
|
37
|
+
* The format constraint a field's input-side JSON Schema carries: its
|
|
38
|
+
* `format` (e.g. `'uuid'`, `'email'`), `'pattern'` for a regex, or `null`.
|
|
39
|
+
* A union (`anyOf` of several non-null branches) answers its first branch
|
|
40
|
+
* carrying one — a value violating every branch is what the caller needs,
|
|
41
|
+
* and the generators check the field refuses the candidate.
|
|
42
|
+
*
|
|
43
|
+
* @param json - a field's JSON Schema, as `to_field_json_schema` returns it
|
|
44
|
+
* @returns the format, `'pattern'`, or `null`
|
|
45
|
+
*/
|
|
46
|
+
export declare const detect_json_schema_format: (json: Record<string, unknown>) => string | null;
|
|
47
|
+
/**
|
|
48
|
+
* Detect format constraints on a field from its input-side JSON Schema
|
|
49
|
+
* (`to_field_json_schema`). Returns the format string (e.g. `'uuid'`,
|
|
50
|
+
* `'email'`), `'pattern'` for a regex, or `null`.
|
|
16
51
|
*/
|
|
17
52
|
export declare const detect_format: (field_schema: z.ZodType) => string | null;
|
|
18
53
|
/** Generate a valid-ish value for a field based on its base type. */
|
|
19
54
|
export declare const generate_valid_value: (field: ZodFieldInfo, field_schema: z.ZodType) => unknown;
|
|
55
|
+
/**
|
|
56
|
+
* Generate a valid object for an object schema: its required and defaulted
|
|
57
|
+
* fields first; when `schema` still refuses it, each optional field alone on
|
|
58
|
+
* top of those, in declaration order, taking the first that parses; and
|
|
59
|
+
* failing that, the optional fields added one at a time, in declaration
|
|
60
|
+
* order, until it parses. The fill covers a `.refine()` over optional fields
|
|
61
|
+
* (at least one of, exactly one of, both of), which the required-only object
|
|
62
|
+
* can't satisfy, and the single-field pass keeps an unrelated optional field
|
|
63
|
+
* declared ahead of the refined ones (an `acting`, a note) out of the object.
|
|
64
|
+
*
|
|
65
|
+
* Shared by the top-level body generators and `generate_valid_value`'s
|
|
66
|
+
* nested-object case, so a refined object validates the same way at any depth.
|
|
67
|
+
*
|
|
68
|
+
* @param object_schema - the unwrapped object whose fields are generated
|
|
69
|
+
* @param schema - the schema the object must satisfy, refinements included
|
|
70
|
+
* (`object_schema` or a schema wrapping it)
|
|
71
|
+
* @returns the generated object and its last `safeParse` result — the caller
|
|
72
|
+
* decides whether a failure throws
|
|
73
|
+
*/
|
|
74
|
+
export declare const generate_valid_object: (object_schema: z.ZodObject, schema: z.ZodType) => {
|
|
75
|
+
value: Record<string, unknown>;
|
|
76
|
+
result: z.ZodSafeParseResult<unknown>;
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* A valid object that carries `field`, built from `base`, a valid object of
|
|
80
|
+
* `object_schema` — the body a case breaking `field` changes, so the case
|
|
81
|
+
* breaks that field and nothing else. `base` itself when it carries the
|
|
82
|
+
* field. Otherwise the field takes a generated value and joins `base`, with
|
|
83
|
+
* the optional fields `base` omits filled as `generate_valid_object` fills
|
|
84
|
+
* them (a pair rule: adding `scope_kind` needs `scope_id`); failing that, it
|
|
85
|
+
* replaces `base`'s optional fields, refilled the same way around it (an
|
|
86
|
+
* exactly-one-of rule: adding `slug` means dropping `id`).
|
|
87
|
+
*
|
|
88
|
+
* @param object_schema - the unwrapped object `base` is an instance of
|
|
89
|
+
* @param schema - the schema the object must satisfy, refinements included
|
|
90
|
+
* @param base - a valid object, the generated or seeded base
|
|
91
|
+
* @param field - the field the object must carry
|
|
92
|
+
* @returns the object, or `null` when no object the generator can build
|
|
93
|
+
* carries the field and passes `schema`
|
|
94
|
+
*/
|
|
95
|
+
export declare const generate_valid_object_with_field: (object_schema: z.ZodObject, schema: z.ZodType, base: Record<string, unknown>, field: ZodFieldInfo) => Record<string, unknown> | null;
|
|
96
|
+
/**
|
|
97
|
+
* A string a URL-sourced field accepts — a route param or a query value, which
|
|
98
|
+
* arrive as strings whatever the schema parses them into. The nil UUID for a
|
|
99
|
+
* UUID field; else the first the field accepts of `test_value`, the generated
|
|
100
|
+
* valid value written as a string, `1`, `0`, `true` and `false`, the field's
|
|
101
|
+
* JSON Schema `default` or lower bound written as a string (a preprocessed
|
|
102
|
+
* integer whose minimum is above one), and a string synthesized from the
|
|
103
|
+
* field's constraints.
|
|
104
|
+
*
|
|
105
|
+
* @param field - the field's info, from `zod_extract_fields`
|
|
106
|
+
* @param field_schema - the field's schema
|
|
107
|
+
* @returns the string, or `null` when no candidate passes
|
|
108
|
+
*/
|
|
109
|
+
export declare const generate_valid_string_param: (field: ZodFieldInfo, field_schema: z.ZodType) => string | null;
|
|
110
|
+
/**
|
|
111
|
+
* Format Zod issues as `path: message` pairs for a generator's error message.
|
|
112
|
+
*
|
|
113
|
+
* @param error - the failed parse's error
|
|
114
|
+
* @returns the issues, comma-separated
|
|
115
|
+
*/
|
|
116
|
+
export declare const format_zod_issues: (error: z.ZodError) => string;
|
|
20
117
|
/**
|
|
21
118
|
* The `Content-Type` a well-formed request to a route sends: `application/json`,
|
|
22
119
|
* or `application/octet-stream` for a `raw_body` route. Every `POST` route
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema_generators.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/schema_generators.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAKN,KAAK,YAAY,EACjB,MAAM,WAAW,CAAC;AAInB
|
|
1
|
+
{"version":3,"file":"schema_generators.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/schema_generators.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAKN,KAAK,YAAY,EACjB,MAAM,WAAW,CAAC;AAInB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,oBAAoB,GAAI,cAAc,CAAC,CAAC,OAAO,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAcxF,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,yBAAyB,GAAI,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,MAAM,GAAG,IAWlF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,cAAc,CAAC,CAAC,OAAO,KAAG,MAAM,GAAG,IAGhE,CAAC;AAqHF,qEAAqE;AACrE,eAAO,MAAM,oBAAoB,GAAI,OAAO,YAAY,EAAE,cAAc,CAAC,CAAC,OAAO,KAAG,OAkGnF,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,qBAAqB,GACjC,eAAe,CAAC,CAAC,SAAS,EAC1B,QAAQ,CAAC,CAAC,OAAO,KACf;IAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,MAAM,EAAE,CAAC,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAA;CASzE,CAAC;AAsCF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,gCAAgC,GAC5C,eAAe,CAAC,CAAC,SAAS,EAC1B,QAAQ,CAAC,CAAC,OAAO,EACjB,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,YAAY,KACjB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IA0B5B,CAAC;AAIF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,2BAA2B,GACvC,OAAO,YAAY,EACnB,cAAc,CAAC,CAAC,OAAO,KACrB,MAAM,GAAG,IA6BX,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,CAAC,CAAC,QAAQ,KAAG,MACkB,CAAC;AAEzE;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,GAAI,OAAO;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,KAAG,MACL,CAAC;AAElE;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,EAAE,gBAAgB,CAAC,CAAC,SAAS,KAAG,MAiB9E,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAC/B,cAAc,CAAC,CAAC,OAAO,KACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAY5B,CAAC"}
|