@fuzdev/fuz_app 0.91.0 → 0.93.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/action_rpc.d.ts.map +1 -1
- package/dist/actions/action_rpc.js +2 -2
- package/dist/actions/perform_action.d.ts.map +1 -1
- package/dist/actions/perform_action.js +6 -6
- package/dist/actions/transports_ws.d.ts.map +1 -1
- package/dist/actions/transports_ws.js +2 -1
- package/dist/actions/transports_ws_backend.d.ts.map +1 -1
- package/dist/actions/transports_ws_backend.js +2 -1
- package/dist/auth/bootstrap_routes.d.ts.map +1 -1
- package/dist/auth/bootstrap_routes.js +2 -1
- package/dist/auth/cell_actions.d.ts.map +1 -1
- package/dist/auth/cell_actions.js +2 -4
- package/dist/db/fact_disk_storage.d.ts.map +1 -1
- package/dist/db/fact_disk_storage.js +2 -1
- package/dist/db/fact_store.d.ts.map +1 -1
- package/dist/db/fact_store.js +2 -1
- package/dist/db/migrate.d.ts.map +1 -1
- package/dist/db/migrate.js +2 -1
- package/dist/db/status.d.ts.map +1 -1
- package/dist/db/status.js +2 -1
- package/dist/http/error_schemas.d.ts +5 -3
- package/dist/http/error_schemas.d.ts.map +1 -1
- package/dist/http/error_schemas.js +5 -3
- package/dist/http/jsonrpc_errors.d.ts +21 -0
- package/dist/http/jsonrpc_errors.d.ts.map +1 -1
- package/dist/http/jsonrpc_errors.js +22 -0
- package/dist/http/route_spec.d.ts.map +1 -1
- package/dist/http/route_spec.js +11 -8
- package/dist/runtime/deno.d.ts.map +1 -1
- package/dist/runtime/deno.js +2 -1
- package/dist/server/env.d.ts.map +1 -1
- package/dist/server/env.js +2 -1
- package/dist/testing/CLAUDE.md +27 -0
- package/dist/testing/cross_backend/create_dual_spawn_global_setup.d.ts +64 -0
- package/dist/testing/cross_backend/create_dual_spawn_global_setup.d.ts.map +1 -0
- package/dist/testing/cross_backend/{create_schema_parity_global_setup.js → create_dual_spawn_global_setup.js} +1 -1
- package/dist/testing/cross_backend/default_backend_configs.d.ts +12 -0
- package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_backend_configs.js +12 -0
- package/dist/testing/cross_backend/default_spine_surface.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_spine_surface.js +9 -2
- package/dist/testing/cross_backend/login_security.d.ts +11 -0
- package/dist/testing/cross_backend/login_security.d.ts.map +1 -0
- package/dist/testing/cross_backend/login_security.js +130 -0
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts +17 -0
- 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 +7 -2
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts +11 -0
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/ts_spine_backend_config.js +23 -7
- package/dist/testing/db.d.ts.map +1 -1
- package/dist/testing/db.js +2 -1
- package/dist/ui/AdminSurface.svelte +3 -1
- package/dist/ui/AdminSurface.svelte.d.ts.map +1 -1
- package/dist/ui/async_slot.svelte.d.ts.map +1 -1
- package/dist/ui/async_slot.svelte.js +2 -1
- package/dist/ui/auth_state.svelte.d.ts.map +1 -1
- package/dist/ui/auth_state.svelte.js +4 -3
- package/dist/ui/table_state.svelte.d.ts.map +1 -1
- package/dist/ui/table_state.svelte.js +2 -1
- package/package.json +4 -3
- package/dist/testing/cross_backend/create_schema_parity_global_setup.d.ts +0 -59
- package/dist/testing/cross_backend/create_schema_parity_global_setup.d.ts.map +0 -1
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generic **dual-spawn** vitest `globalSetup` factory — spawns + bootstraps
|
|
4
|
+
* *two* backends at once and `provide`s both serialized handles, for any
|
|
5
|
+
* cross-impl gate that needs both alive together. (The per-backend
|
|
6
|
+
* `create_cross_backend_global_setup` derives one backend from the project
|
|
7
|
+
* name; this brings up a pair.)
|
|
8
|
+
*
|
|
9
|
+
* Its primary use is the **schema- / action-manifest-parity** gates (capture
|
|
10
|
+
* each backend over a `_testing_*` introspection RPC and diff with
|
|
11
|
+
* `assert_schema_snapshots_equal` / `assert_action_manifests_equal`) — which is
|
|
12
|
+
* why the default `provide_keys` are `parity_handle_*`. The login-security gate
|
|
13
|
+
* (`global_setup_login_security.ts`) reuses it with its own keys; any future
|
|
14
|
+
* two-backend gate can too.
|
|
15
|
+
*
|
|
16
|
+
* A consumer's dual-spawn `global_setup.ts` collapses to:
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* import {create_dual_spawn_global_setup} from
|
|
20
|
+
* '@fuzdev/fuz_app/testing/cross_backend/create_dual_spawn_global_setup.js';
|
|
21
|
+
* import {deno_backend_config, rust_backend_config} from './my_backend_config.js';
|
|
22
|
+
* import './cross_test_types.js'; // augments the two provide keys
|
|
23
|
+
*
|
|
24
|
+
* export default create_dual_spawn_global_setup({
|
|
25
|
+
* configs: {a: deno_backend_config, b: rust_backend_config},
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* The `.cross.test.ts` `inject`s both keys, rebuilds each with
|
|
30
|
+
* `reconstruct_bootstrapped_handle`, and asserts. Run this project in a
|
|
31
|
+
* later `groupOrder` than the single-backend projects (or with distinct
|
|
32
|
+
* ports) — it reuses both configs' ports, so it must not run concurrently
|
|
33
|
+
* with the per-backend projects.
|
|
34
|
+
*
|
|
35
|
+
* @module
|
|
36
|
+
*/
|
|
37
|
+
import type { TestProject } from 'vitest/node';
|
|
38
|
+
import type { BackendConfig } from './backend_config.js';
|
|
39
|
+
export interface DualSpawnGlobalSetupOptions {
|
|
40
|
+
/**
|
|
41
|
+
* The two backend config factories to spawn. `a` is spawned first; on
|
|
42
|
+
* its success `b` is spawned (with `a` torn down if `b` throws). Label
|
|
43
|
+
* them in the consuming test (e.g. via `assert_schema_snapshots_equal`'s labels).
|
|
44
|
+
*/
|
|
45
|
+
readonly configs: {
|
|
46
|
+
readonly a: () => BackendConfig;
|
|
47
|
+
readonly b: () => BackendConfig;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* `project.provide` keys for the two serialized handles (read by
|
|
51
|
+
* `inject` in the test). Default `parity_handle_a` / `parity_handle_b`
|
|
52
|
+
* (the primary parity use); override for other dual-spawn gates. Augment
|
|
53
|
+
* vitest's `ProvidedContext` for whichever keys you use.
|
|
54
|
+
*/
|
|
55
|
+
readonly provide_keys?: {
|
|
56
|
+
readonly a: string;
|
|
57
|
+
readonly b: string;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Build a vitest `globalSetup` default export that spawns both backends.
|
|
62
|
+
*/
|
|
63
|
+
export declare const create_dual_spawn_global_setup: ({ configs, provide_keys, }: DualSpawnGlobalSetupOptions) => ((project: TestProject) => Promise<() => Promise<void>>);
|
|
64
|
+
//# sourceMappingURL=create_dual_spawn_global_setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create_dual_spawn_global_setup.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/create_dual_spawn_global_setup.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,aAAa,CAAC;AAE7C,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAIvD,MAAM,WAAW,2BAA2B;IAC3C;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE;QAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,aAAa,CAAC;QAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,aAAa,CAAA;KAAC,CAAC;IACrF;;;;;OAKG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE;QAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAA;KAAC,CAAC;CACjE;AAED;;GAEG;AACH,eAAO,MAAM,8BAA8B,GAAI,4BAG5C,2BAA2B,KAAG,CAAC,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAmBvF,CAAC"}
|
|
@@ -4,7 +4,7 @@ import { serialize_bootstrapped_handle } from './setup.js';
|
|
|
4
4
|
/**
|
|
5
5
|
* Build a vitest `globalSetup` default export that spawns both backends.
|
|
6
6
|
*/
|
|
7
|
-
export const
|
|
7
|
+
export const create_dual_spawn_global_setup = ({ configs, provide_keys = { a: 'parity_handle_a', b: 'parity_handle_b' }, }) => {
|
|
8
8
|
return async (project) => {
|
|
9
9
|
const a = await bootstrap_backend(configs.a());
|
|
10
10
|
// Tear `a` down if `b` fails to spawn — no orphaned child process.
|
|
@@ -35,6 +35,18 @@ import '../assert_dev_env.js';
|
|
|
35
35
|
import type { BackendBootstrapConfig, BackendConfig } from './backend_config.js';
|
|
36
36
|
import type { BackendCapabilities, BackendShapeNotes } from './capabilities.js';
|
|
37
37
|
import { type TestBackendPaths } from './build_test_backend_paths.js';
|
|
38
|
+
/**
|
|
39
|
+
* Env var both spine binaries read to enable their login rate limiters
|
|
40
|
+
* (`'true'` on / unset off). The cross-language contract for the login-security
|
|
41
|
+
* cross project: the TS binary reads it via `runtime.env_get` (a test-only flag,
|
|
42
|
+
* not in `BaseServerEnv`); the Rust `testing_spine_stub` reads it via
|
|
43
|
+
* `std::env::var` — so one backend-config option drives both impls. Shared home
|
|
44
|
+
* here because both `ts_spine_backend_config` and `rust_spine_stub_backend_config`
|
|
45
|
+
* already import this module. (The spawned TS binary re-declares the literal
|
|
46
|
+
* locally — it can't import this module, which transitively pulls `vitest` —
|
|
47
|
+
* mirroring how `testing_spine_server_node.ts` re-declares `TS_SPINE_DIR_ENV`.)
|
|
48
|
+
*/
|
|
49
|
+
export declare const LOGIN_RATE_LIMIT_ENABLED_ENV = "FUZ_LOGIN_RATE_LIMIT_ENABLED";
|
|
38
50
|
/**
|
|
39
51
|
* Capabilities shared by TS-family backends — same canonical implementation,
|
|
40
52
|
* same feature set. The non-gating wiring facts (`bearer_auth` /
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default_backend_configs.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/default_backend_configs.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,EAAC,sBAAsB,EAAE,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAC/E,OAAO,KAAK,EAAC,mBAAmB,EAAE,iBAAiB,EAAC,MAAM,mBAAmB,CAAC;AAC9E,OAAO,EAA2B,KAAK,gBAAgB,EAAC,MAAM,+BAA+B,CAAC;AAQ9F;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,mBAiBpC,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,EAAE,mBAgBtC,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,EAAE,iBAInC,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,EAAE,iBAIrC,CAAC;AAeH,MAAM,WAAW,iCAAiC;IACjD,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC9C,oDAAoD;IACpD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,6CAA6C;IAC7C,QAAQ,CAAC,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAC5C,wEAAwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAClC,sEAAsE;IACtE,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC/D;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;;OAMG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B,GAC1C,MAAM,iCAAiC,KACrC,aAoCF,CAAC;AAEF,MAAM,WAAW,mCAAmC;IACnD,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,+CAA+C;IAC/C,QAAQ,CAAC,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAC5C,wEAAwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAClC,sEAAsE;IACtE,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC/D;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,GAC5C,MAAM,mCAAmC,KACvC,aA4CF,CAAC"}
|
|
1
|
+
{"version":3,"file":"default_backend_configs.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/default_backend_configs.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,EAAC,sBAAsB,EAAE,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAC/E,OAAO,KAAK,EAAC,mBAAmB,EAAE,iBAAiB,EAAC,MAAM,mBAAmB,CAAC;AAC9E,OAAO,EAA2B,KAAK,gBAAgB,EAAC,MAAM,+BAA+B,CAAC;AAQ9F;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,iCAAiC,CAAC;AAE3E;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,mBAiBpC,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,EAAE,mBAgBtC,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,EAAE,iBAInC,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,EAAE,iBAIrC,CAAC;AAeH,MAAM,WAAW,iCAAiC;IACjD,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC9C,oDAAoD;IACpD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,6CAA6C;IAC7C,QAAQ,CAAC,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAC5C,wEAAwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAClC,sEAAsE;IACtE,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC/D;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;;OAMG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B,GAC1C,MAAM,iCAAiC,KACrC,aAoCF,CAAC;AAEF,MAAM,WAAW,mCAAmC;IACnD,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,+CAA+C;IAC/C,QAAQ,CAAC,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAC5C,wEAAwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAClC,sEAAsE;IACtE,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC/D;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,GAC5C,MAAM,mCAAmC,KACvC,aA4CF,CAAC"}
|
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import '../assert_dev_env.js';
|
|
2
2
|
import { build_test_backend_paths } from './build_test_backend_paths.js';
|
|
3
3
|
import { default_test_bootstrap_token, default_test_cookie_keys, default_test_keeper_password, default_test_keeper_username, } from './default_secrets.js';
|
|
4
|
+
/**
|
|
5
|
+
* Env var both spine binaries read to enable their login rate limiters
|
|
6
|
+
* (`'true'` on / unset off). The cross-language contract for the login-security
|
|
7
|
+
* cross project: the TS binary reads it via `runtime.env_get` (a test-only flag,
|
|
8
|
+
* not in `BaseServerEnv`); the Rust `testing_spine_stub` reads it via
|
|
9
|
+
* `std::env::var` — so one backend-config option drives both impls. Shared home
|
|
10
|
+
* here because both `ts_spine_backend_config` and `rust_spine_stub_backend_config`
|
|
11
|
+
* already import this module. (The spawned TS binary re-declares the literal
|
|
12
|
+
* locally — it can't import this module, which transitively pulls `vitest` —
|
|
13
|
+
* mirroring how `testing_spine_server_node.ts` re-declares `TS_SPINE_DIR_ENV`.)
|
|
14
|
+
*/
|
|
15
|
+
export const LOGIN_RATE_LIMIT_ENABLED_ENV = 'FUZ_LOGIN_RATE_LIMIT_ENABLED';
|
|
4
16
|
/**
|
|
5
17
|
* Capabilities shared by TS-family backends — same canonical implementation,
|
|
6
18
|
* same feature set. The non-gating wiring facts (`bearer_auth` /
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default_spine_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/default_spine_surface.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAIpD,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,8CAA8C,CAAC;AACrF,OAAO,EAAqB,KAAK,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AACpF,OAAO,EAAwB,KAAK,cAAc,EAAC,MAAM,8BAA8B,CAAC;AAIxF,OAAO,EAAqB,KAAK,SAAS,EAAC,MAAM,0BAA0B,CAAC;AAC5E,OAAO,KAAK,EAAC,cAAc,EAAE,eAAe,EAAC,MAAM,uBAAuB,CAAC;AAC3E,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,oCAAoC,CAAC;AAGzE;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,cAAc,CAAC,MAAM,CAAwC,CAAC;AAElG;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,gBAAgB,CAAC;AAEpD;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,EAAE,gBAExB,CAAC;AAEH,iEAAiE;AACjE,eAAO,MAAM,cAAc,aAAa,CAAC;AAEzC;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,4BAA4B,CAAC;AAExD,+CAA+C;AAC/C,MAAM,WAAW,wBAAwB;IACxC;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAC;CACzD;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,mBAAmB,GAC/B,KAAK,gBAAgB,EACrB,UAAU,wBAAwB,KAChC,KAAK,CAAC,eAAe,CAQvB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,GAAI,KAAK,gBAAgB,KAAG,KAAK,CAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"default_spine_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/default_spine_surface.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAIpD,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,8CAA8C,CAAC;AACrF,OAAO,EAAqB,KAAK,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AACpF,OAAO,EAAwB,KAAK,cAAc,EAAC,MAAM,8BAA8B,CAAC;AAIxF,OAAO,EAAqB,KAAK,SAAS,EAAC,MAAM,0BAA0B,CAAC;AAC5E,OAAO,KAAK,EAAC,cAAc,EAAE,eAAe,EAAC,MAAM,uBAAuB,CAAC;AAC3E,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,oCAAoC,CAAC;AAGzE;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,cAAc,CAAC,MAAM,CAAwC,CAAC;AAElG;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,gBAAgB,CAAC;AAEpD;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,EAAE,gBAExB,CAAC;AAEH,iEAAiE;AACjE,eAAO,MAAM,cAAc,aAAa,CAAC;AAEzC;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,4BAA4B,CAAC;AAExD,+CAA+C;AAC/C,MAAM,WAAW,wBAAwB;IACxC;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAC;CACzD;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,mBAAmB,GAC/B,KAAK,gBAAgB,EACrB,UAAU,wBAAwB,KAChC,KAAK,CAAC,eAAe,CAQvB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,GAAI,KAAK,gBAAgB,KAAG,KAAK,CAAC,SAAS,CA+B/E,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,EAAE,GAAwD,CAAC;AAEjG;;;;;;;;;GASG;AACH,eAAO,MAAM,6BAA6B,GAAI,MAAM,MAAM,KAAG,SAC6B,CAAC;AAE3F;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,QAAO,cAM1C,CAAC"}
|
|
@@ -77,8 +77,15 @@ export const create_spine_route_specs = (ctx) => [
|
|
|
77
77
|
...prefix_route_specs('/api/account', [
|
|
78
78
|
...create_account_route_specs(ctx.deps, {
|
|
79
79
|
session_options: spine_session_options,
|
|
80
|
-
|
|
81
|
-
login_account_rate_limiter
|
|
80
|
+
// Honor the limiters the server was assembled with (on `AppServerContext`
|
|
81
|
+
// per the `ip_rate_limiter` / `login_account_rate_limiter` TSDoc) rather
|
|
82
|
+
// than hardcoding `null`. The standard spine binary passes `null` (the
|
|
83
|
+
// cross suites fire many loopback logins and must not trip a limiter), so
|
|
84
|
+
// this is behavior-preserving there; the dedicated login-security cross
|
|
85
|
+
// backend passes real limiters to exercise the 429 + `Retry-After` path
|
|
86
|
+
// and XFF-keyed bucketing over the wire (see `login_security.ts`).
|
|
87
|
+
ip_rate_limiter: ctx.ip_rate_limiter,
|
|
88
|
+
login_account_rate_limiter: ctx.login_account_rate_limiter,
|
|
82
89
|
login_fail_floor_ms: 0,
|
|
83
90
|
bootstrap_status: ctx.bootstrap_status,
|
|
84
91
|
}),
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
import type { SetupTest } from './setup.js';
|
|
3
|
+
/** Options for the login-security parity suite. */
|
|
4
|
+
export interface LoginSecurityCrossTestOptions {
|
|
5
|
+
/** Per-test fixture producer (cross-process only — see the module doc). */
|
|
6
|
+
readonly setup_test: SetupTest;
|
|
7
|
+
/** REST login route path. Default `/api/account/login` (the spine convention). */
|
|
8
|
+
readonly login_path?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare const describe_login_security_cross_tests: (options: LoginSecurityCrossTestOptions) => void;
|
|
11
|
+
//# sourceMappingURL=login_security.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login_security.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/login_security.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAoD9B,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,YAAY,CAAC;AAE1C,mDAAmD;AACnD,MAAM,WAAW,6BAA6B;IAC7C,2EAA2E;IAC3E,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,kFAAkF;IAClF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAyBD,eAAO,MAAM,mCAAmC,GAC/C,SAAS,6BAA6B,KACpC,IAoGF,CAAC"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Cross-backend parity suite for **login rate limiting + trusted-proxy
|
|
4
|
+
* (X-Forwarded-For) resolution** over real HTTP.
|
|
5
|
+
*
|
|
6
|
+
* Login throttling and client-IP resolution are wired-but-never-crossed: the
|
|
7
|
+
* in-process `describe_rate_limiting_tests` covers the limiter, and the
|
|
8
|
+
* `fuz_http` / proxy middleware unit tests cover XFF resolution, but no case
|
|
9
|
+
* exercised either over a real socket on both impls — the limiter is nulled
|
|
10
|
+
* on every standard cross backend and the resolved client IP has no
|
|
11
|
+
* wire-observable downstream there. This dedicated suite spawns a backend
|
|
12
|
+
* with the login limiters enabled + the loopback proxy trusted (see
|
|
13
|
+
* `global_setup_login_security.ts`), then pins two properties end-to-end:
|
|
14
|
+
*
|
|
15
|
+
* - **per-IP login limit fires** — the first `default_login_ip_rate_limit`
|
|
16
|
+
* failed logins from one forwarded IP each return `401`, and the next
|
|
17
|
+
* returns `429` with the canonical `{error: "rate_limit_exceeded",
|
|
18
|
+
* retry_after}` body **and** a `Retry-After: ceil(retry_after)` header. The
|
|
19
|
+
* 429 wire shape is the cross-impl contract — TS `rate_limit_exceeded_response`
|
|
20
|
+
* and Rust `route_response::rate_limit_exceeded` must agree.
|
|
21
|
+
* - **trusted-proxy / XFF resolution is honored** — distinct
|
|
22
|
+
* `X-Forwarded-For` IPs get independent buckets: after exhausting one
|
|
23
|
+
* forwarded IP to `429`, a *different* forwarded IP is unaffected (`401`,
|
|
24
|
+
* not `429`). A backend that ignored XFF and keyed on the (loopback) TCP
|
|
25
|
+
* peer would 429 the fresh-IP request too — so the `401` proves the limiter
|
|
26
|
+
* keys on the resolved `X-Forwarded-For` client IP.
|
|
27
|
+
*
|
|
28
|
+
* **Determinism without a limiter reset.** Limiter state is in-memory and the
|
|
29
|
+
* per-test `_testing_reset` wipes only the DB, never the buckets — so each
|
|
30
|
+
* case uses its own forwarded IP and its own (non-existent) username, keeping
|
|
31
|
+
* every bucket independent across cases and across the two impls (separate
|
|
32
|
+
* processes). The login floor is zeroed on both spines, so the failed-login
|
|
33
|
+
* loop stays fast.
|
|
34
|
+
*
|
|
35
|
+
* Both surfaces are flat REST (`POST /api/account/login`) on every spine, so
|
|
36
|
+
* this is an imperative suite (not a `conformance_table` row) — the sibling of
|
|
37
|
+
* `origin.ts` / `identity_parity.ts`. Cross-process only: the limiter+proxy
|
|
38
|
+
* wiring is the point, and the in-process counterparts already exist
|
|
39
|
+
* (`describe_rate_limiting_tests` + the proxy middleware tests). Cited
|
|
40
|
+
* property: `docs/security.md` §"Rate Limiting" + §"Trusted Proxy / Client IP".
|
|
41
|
+
*
|
|
42
|
+
* `$lib`-free by contract (relative specifiers only), like the sibling
|
|
43
|
+
* cross-backend suites.
|
|
44
|
+
*
|
|
45
|
+
* @module
|
|
46
|
+
*/
|
|
47
|
+
import { describe, test, assert } from 'vitest';
|
|
48
|
+
import { ERROR_RATE_LIMIT_EXCEEDED } from '../../http/error_schemas.js';
|
|
49
|
+
import { default_login_account_rate_limit, default_login_ip_rate_limit } from '../../rate_limiter.js';
|
|
50
|
+
/**
|
|
51
|
+
* RFC 5737 TEST-NET-3 (`203.0.113.0/24`) documentation addresses — guaranteed
|
|
52
|
+
* non-routable, so they never collide with a real client IP. One per bucket the
|
|
53
|
+
* suite needs; distinct across cases so no case inherits another's count (the
|
|
54
|
+
* limiter is not reset between cases). Spoofed via `X-Forwarded-For`; the
|
|
55
|
+
* loopback TCP peer is trusted, so the rightmost untrusted hop (these) is the
|
|
56
|
+
* resolved client IP.
|
|
57
|
+
*/
|
|
58
|
+
const XFF_IP_LIMIT = '203.0.113.1';
|
|
59
|
+
const XFF_SEGREGATION_EXHAUST = '203.0.113.2';
|
|
60
|
+
const XFF_SEGREGATION_FRESH = '203.0.113.3';
|
|
61
|
+
/** A non-routable, non-existent identifier — login resolves to the not-found path (records the bucket, returns 401). */
|
|
62
|
+
const PROBE_PASSWORD = 'login_security_wrong_password';
|
|
63
|
+
export const describe_login_security_cross_tests = (options) => {
|
|
64
|
+
const { setup_test } = options;
|
|
65
|
+
const login_path = options.login_path ?? '/api/account/login';
|
|
66
|
+
/** Per-IP cap shared by both impls (TS `default_login_ip_rate_limit`, Rust `DEFAULT_LOGIN_IP_RATE_LIMIT`). */
|
|
67
|
+
const ip_limit = default_login_ip_rate_limit.max_attempts;
|
|
68
|
+
// The XFF-segregation case enables both limiters (one flag drives both), so
|
|
69
|
+
// the per-account bucket is live too. After exhausting an IP, the shared
|
|
70
|
+
// username holds exactly `ip_limit` account records, so the fresh-IP probe
|
|
71
|
+
// only stays a 401 (not 429) while the account cap sits above the IP cap.
|
|
72
|
+
// Assert the ordering so a future cap change fails loud here instead of as a
|
|
73
|
+
// confusing 429-instead-of-401 mid-case.
|
|
74
|
+
assert(default_login_account_rate_limit.max_attempts > ip_limit, 'login-security XFF segregation needs the per-account cap above the per-IP cap');
|
|
75
|
+
/**
|
|
76
|
+
* POST a login with the given `X-Forwarded-For` on a fresh (cookie-jar-free)
|
|
77
|
+
* transport — wrong password against a non-existent username, so the handler
|
|
78
|
+
* clears origin + input validation, records the resolved-IP bucket, and
|
|
79
|
+
* returns `401` (or `429` once the bucket trips). Always reads the body so
|
|
80
|
+
* the cross-process socket releases.
|
|
81
|
+
*/
|
|
82
|
+
const attempt = async (fixture, forwarded_for, username) => {
|
|
83
|
+
const res = await fixture.fresh_transport()(login_path, {
|
|
84
|
+
method: 'POST',
|
|
85
|
+
headers: { 'content-type': 'application/json', 'x-forwarded-for': forwarded_for },
|
|
86
|
+
body: JSON.stringify({ username, password: PROBE_PASSWORD }),
|
|
87
|
+
});
|
|
88
|
+
const body = (await res.json().catch(() => undefined));
|
|
89
|
+
return {
|
|
90
|
+
status: res.status,
|
|
91
|
+
error: typeof body?.error === 'string' ? body.error : undefined,
|
|
92
|
+
retry_after: typeof body?.retry_after === 'number' ? body.retry_after : undefined,
|
|
93
|
+
retry_after_header: res.headers.get('retry-after'),
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
describe('login rate limiting + trusted-proxy parity', () => {
|
|
97
|
+
test(`per-IP login limit fires 429 + Retry-After after ${ip_limit} attempts from one forwarded IP`, async () => {
|
|
98
|
+
const fixture = await setup_test();
|
|
99
|
+
// The first `ip_limit` failed logins from one forwarded IP each clear the
|
|
100
|
+
// limiter (a normal 401), recording against the resolved client IP.
|
|
101
|
+
for (let i = 0; i < ip_limit; i++) {
|
|
102
|
+
const r = await attempt(fixture, XFF_IP_LIMIT, 'probe_ip_limit');
|
|
103
|
+
assert.strictEqual(r.status, 401, `attempt ${i + 1}/${ip_limit} must be a normal 401, not rate-limited`);
|
|
104
|
+
}
|
|
105
|
+
// The next request from the SAME forwarded IP trips the per-IP bucket.
|
|
106
|
+
const limited = await attempt(fixture, XFF_IP_LIMIT, 'probe_ip_limit');
|
|
107
|
+
assert.strictEqual(limited.status, 429, `attempt ${ip_limit + 1} must be 429`);
|
|
108
|
+
assert.strictEqual(limited.error, ERROR_RATE_LIMIT_EXCEEDED, '429 body must carry the canonical rate_limit_exceeded reason');
|
|
109
|
+
assert(limited.retry_after !== undefined && limited.retry_after > 0, '429 body must carry a positive numeric retry_after');
|
|
110
|
+
assert(limited.retry_after_header !== null, 'Retry-After header must be present on the 429');
|
|
111
|
+
assert.strictEqual(Number(limited.retry_after_header), Math.ceil(limited.retry_after), 'Retry-After header must equal ceil(retry_after)');
|
|
112
|
+
});
|
|
113
|
+
test('distinct X-Forwarded-For IPs get independent buckets (XFF resolution honored)', async () => {
|
|
114
|
+
const fixture = await setup_test();
|
|
115
|
+
// Exhaust one forwarded IP to its cap (own username, so the per-account
|
|
116
|
+
// bucket never confounds — these stay well under the account cap).
|
|
117
|
+
for (let i = 0; i < ip_limit; i++) {
|
|
118
|
+
await attempt(fixture, XFF_SEGREGATION_EXHAUST, 'probe_xff');
|
|
119
|
+
}
|
|
120
|
+
const exhausted = await attempt(fixture, XFF_SEGREGATION_EXHAUST, 'probe_xff');
|
|
121
|
+
assert.strictEqual(exhausted.status, 429, 'the exhausted forwarded IP must be rate-limited');
|
|
122
|
+
// A DIFFERENT forwarded IP is a fresh bucket → not limited. If the backend
|
|
123
|
+
// ignored XFF and keyed on the loopback TCP peer, this would land in the
|
|
124
|
+
// already-exhausted peer bucket and 429 — so a 401 proves the limiter keys
|
|
125
|
+
// on the resolved X-Forwarded-For client IP.
|
|
126
|
+
const fresh = await attempt(fixture, XFF_SEGREGATION_FRESH, 'probe_xff');
|
|
127
|
+
assert.strictEqual(fresh.status, 401, "a fresh forwarded IP must not inherit another IP's limit");
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
};
|
|
@@ -23,6 +23,23 @@ export interface SpineStubBackendConfigOptions {
|
|
|
23
23
|
* var. When neither is set the preset throws.
|
|
24
24
|
*/
|
|
25
25
|
readonly binary_path?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Enable the per-IP + per-account login rate limiters on the stub
|
|
28
|
+
* (`FUZ_LOGIN_RATE_LIMIT_ENABLED=true`). Off by default — the standard cross
|
|
29
|
+
* suites fire many loopback logins a live limiter would 429. Set `true` only
|
|
30
|
+
* for the dedicated login-security cross project (`global_setup_login_security.ts`).
|
|
31
|
+
* Pair with `trusted_proxies` so the limiter keys on the resolved
|
|
32
|
+
* `X-Forwarded-For` client IP. Mirrors `TsSpineBackendConfigOptions.enable_login_rate_limit`.
|
|
33
|
+
*/
|
|
34
|
+
readonly enable_login_rate_limit?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Comma-separated trusted-proxy allowlist passed as `FUZ_TRUSTED_PROXIES`
|
|
37
|
+
* (e.g. `'127.0.0.1,::1'`). Unset by default (the stub leaves XFF parsing
|
|
38
|
+
* off, keying on the raw TCP peer). The login-security project sets the
|
|
39
|
+
* loopback set so the limiter keys on the `X-Forwarded-For` client IP — the
|
|
40
|
+
* TS spine binary wires the equivalent set unconditionally.
|
|
41
|
+
*/
|
|
42
|
+
readonly trusted_proxies?: string;
|
|
26
43
|
}
|
|
27
44
|
/**
|
|
28
45
|
* Build the `BackendConfig` for `testing_spine_stub`. Resolves the binary
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rust_spine_stub_backend_config.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/rust_spine_stub_backend_config.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAuC9B,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"rust_spine_stub_backend_config.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/rust_spine_stub_backend_config.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAuC9B,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AASvD,uGAAuG;AACvG,eAAO,MAAM,uBAAuB,oCAAoC,CAAC;AAEzE;;;;;GAKG;AACH,eAAO,MAAM,wCAAwC,6CAA6C,CAAC;AAenG,kGAAkG;AAClG,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAEjD,0FAA0F;AAC1F,eAAO,MAAM,oCAAoC,sDACG,CAAC;AAErD,MAAM,WAAW,6BAA6B;IAC7C,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;OAOG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CAClC;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,8BAA8B,GAC1C,UAAS,6BAAkC,KACzC,aA+CF,CAAC"}
|
|
@@ -36,7 +36,7 @@ import '../assert_dev_env.js';
|
|
|
36
36
|
import { fileURLToPath } from 'node:url';
|
|
37
37
|
import { build_test_backend_paths } from './build_test_backend_paths.js';
|
|
38
38
|
import { SPINE_EXPECTED_SCHEMA_URL } from './default_spine_surface.js';
|
|
39
|
-
import { make_default_rust_backend_config, rust_default_capabilities, } from './default_backend_configs.js';
|
|
39
|
+
import { LOGIN_RATE_LIMIT_ENABLED_ENV, make_default_rust_backend_config, rust_default_capabilities, } from './default_backend_configs.js';
|
|
40
40
|
/** Env var naming the prebuilt `testing_spine_stub` binary. Required when `binary_path` is omitted. */
|
|
41
41
|
export const RUST_SPINE_STUB_BIN_ENV = 'FUZ_TESTING_RUST_SPINE_STUB_BIN';
|
|
42
42
|
/**
|
|
@@ -74,7 +74,7 @@ export const RUST_SPINE_STUB_DEFAULT_DATABASE_URL = 'postgres://localhost/fuz_ap
|
|
|
74
74
|
* @throws Error when no binary path is available.
|
|
75
75
|
*/
|
|
76
76
|
export const rust_spine_stub_backend_config = (options = {}) => {
|
|
77
|
-
const { port = RUST_SPINE_STUB_DEFAULT_PORT, database_url = RUST_SPINE_STUB_DEFAULT_DATABASE_URL, binary_path = process.env[RUST_SPINE_STUB_BIN_ENV], } = options;
|
|
77
|
+
const { port = RUST_SPINE_STUB_DEFAULT_PORT, database_url = RUST_SPINE_STUB_DEFAULT_DATABASE_URL, binary_path = process.env[RUST_SPINE_STUB_BIN_ENV], enable_login_rate_limit, trusted_proxies, } = options;
|
|
78
78
|
if (!binary_path) {
|
|
79
79
|
throw new Error(`rust_spine_stub_backend_config: no binary path — set ${RUST_SPINE_STUB_BIN_ENV} to a prebuilt ` +
|
|
80
80
|
'`testing_spine_stub` binary (build it with `cargo build -p testing_spine_stub --release`) ' +
|
|
@@ -105,6 +105,11 @@ export const rust_spine_stub_backend_config = (options = {}) => {
|
|
|
105
105
|
// file the TS spine reads, so the two backends share one cross-impl
|
|
106
106
|
// contract.
|
|
107
107
|
[RUST_SPINE_STUB_EXPECTED_SCHEMA_PATH_ENV]: fileURLToPath(SPINE_EXPECTED_SCHEMA_URL),
|
|
108
|
+
// Login-security project opt-ins (off by default): enable the login
|
|
109
|
+
// limiters + trust the loopback proxy so the limiter keys on the
|
|
110
|
+
// resolved `X-Forwarded-For` IP. The stub reads both directly.
|
|
111
|
+
...(enable_login_rate_limit ? { [LOGIN_RATE_LIMIT_ENABLED_ENV]: 'true' } : {}),
|
|
112
|
+
...(trusted_proxies !== undefined ? { FUZ_TRUSTED_PROXIES: trusted_proxies } : {}),
|
|
108
113
|
},
|
|
109
114
|
});
|
|
110
115
|
};
|
|
@@ -47,6 +47,17 @@ export interface TsSpineBackendConfigOptions {
|
|
|
47
47
|
readonly port?: number;
|
|
48
48
|
/** Database URL. Default `'memory://'` (in-memory PGlite). */
|
|
49
49
|
readonly database_url?: string;
|
|
50
|
+
/**
|
|
51
|
+
* Enable the per-IP + per-account login rate limiters on the spawned binary
|
|
52
|
+
* (`FUZ_LOGIN_RATE_LIMIT_ENABLED=true`). Off by default — the standard cross
|
|
53
|
+
* suites fire many loopback logins a live limiter would 429. Set `true` only
|
|
54
|
+
* for the dedicated login-security cross project (`global_setup_login_security.ts`),
|
|
55
|
+
* which drives the 429 + `Retry-After` path and XFF-keyed bucketing over the
|
|
56
|
+
* wire (`login_security.ts`). The binary always wires `trusted_proxies` for
|
|
57
|
+
* `127.0.0.1`/`::1`, so the limiter keys on the resolved `X-Forwarded-For`
|
|
58
|
+
* client IP. Mirrors `SpineStubBackendConfigOptions.enable_login_rate_limit`.
|
|
59
|
+
*/
|
|
60
|
+
readonly enable_login_rate_limit?: boolean;
|
|
50
61
|
}
|
|
51
62
|
/**
|
|
52
63
|
* `BackendConfig` for the Node TS spine binary — spawned via `gro run`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ts_spine_backend_config.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/ts_spine_backend_config.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"ts_spine_backend_config.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/ts_spine_backend_config.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAQvD,8GAA8G;AAC9G,eAAO,MAAM,gBAAgB,6BAA6B,CAAC;AAE3D;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,4BAA4B,CAAC;AAyB3D,6FAA6F;AAC7F,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,iDAAiD;AACjD,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,gDAAgD;AAChD,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,wDAAwD,CAAC;AAEzF,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,wDAAwD,CAAC;AAEzF,wDAAwD;AACxD,eAAO,MAAM,kBAAkB,uDAAuD,CAAC;AAEvF,MAAM,WAAW,2BAA2B;IAC3C,mFAAmF;IACnF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;;;;;OASG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC;CAC3C;AAWD;;;GAGG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAS,2BAAgC,KACvC,aAmBF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,GACvC,UAAS,2BAAgC,KACvC,aAmBF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAS,2BAAgC,KACvC,aAgCF,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import '../assert_dev_env.js';
|
|
2
2
|
import { build_test_backend_paths } from './build_test_backend_paths.js';
|
|
3
|
-
import { make_default_ts_backend_config, ts_default_capabilities, } from './default_backend_configs.js';
|
|
3
|
+
import { LOGIN_RATE_LIMIT_ENABLED_ENV, make_default_ts_backend_config, ts_default_capabilities, } from './default_backend_configs.js';
|
|
4
4
|
/** Env var naming the backend root dir; `{dir}/run/daemon_token` must match `bootstrap.daemon_token_path`. */
|
|
5
5
|
export const TS_SPINE_DIR_ENV = 'FUZ_TESTING_TS_SPINE_DIR';
|
|
6
6
|
/**
|
|
@@ -47,12 +47,19 @@ export const TS_SPINE_NODE_ENTRY = 'src/test/cross_backend/testing_spine_server_
|
|
|
47
47
|
export const TS_SPINE_DENO_ENTRY = 'src/test/cross_backend/testing_spine_server_deno.ts';
|
|
48
48
|
/** Entry module spawned for the Bun TS spine binary. */
|
|
49
49
|
export const TS_SPINE_BUN_ENTRY = 'src/test/cross_backend/testing_spine_server_bun.ts';
|
|
50
|
+
/**
|
|
51
|
+
* `extra_env` carrying the login-rate-limit toggle when enabled. The TS binary
|
|
52
|
+
* reads `FUZ_LOGIN_RATE_LIMIT_ENABLED` directly (a test-only flag, not in
|
|
53
|
+
* `BaseServerEnv`); the same env-var name the Rust spine-stub reads, so one
|
|
54
|
+
* backend-config option drives both impls.
|
|
55
|
+
*/
|
|
56
|
+
const login_rate_limit_env = (enable) => enable ? { [LOGIN_RATE_LIMIT_ENABLED_ENV]: 'true' } : {};
|
|
50
57
|
/**
|
|
51
58
|
* `BackendConfig` for the Node TS spine binary — spawned via `gro run`
|
|
52
59
|
* (Gro's TS loader resolves the `$lib`-free entry's relative imports).
|
|
53
60
|
*/
|
|
54
61
|
export const ts_spine_node_backend_config = (options = {}) => {
|
|
55
|
-
const { port = TS_SPINE_NODE_DEFAULT_PORT, database_url } = options;
|
|
62
|
+
const { port = TS_SPINE_NODE_DEFAULT_PORT, database_url, enable_login_rate_limit } = options;
|
|
56
63
|
const name = 'ts_spine_node';
|
|
57
64
|
const paths = build_test_backend_paths(name);
|
|
58
65
|
return {
|
|
@@ -62,7 +69,10 @@ export const ts_spine_node_backend_config = (options = {}) => {
|
|
|
62
69
|
start_command: ['gro', 'run', TS_SPINE_NODE_ENTRY],
|
|
63
70
|
database_url,
|
|
64
71
|
paths,
|
|
65
|
-
extra_env: {
|
|
72
|
+
extra_env: {
|
|
73
|
+
[TS_SPINE_DIR_ENV]: paths.root,
|
|
74
|
+
...login_rate_limit_env(enable_login_rate_limit),
|
|
75
|
+
},
|
|
66
76
|
capabilities: ts_spine_capabilities,
|
|
67
77
|
}),
|
|
68
78
|
sse_path: TS_SPINE_SSE_PATH,
|
|
@@ -75,7 +85,7 @@ export const ts_spine_node_backend_config = (options = {}) => {
|
|
|
75
85
|
* the Node path), and `Bun.serve` + `hono/bun` need no extra deps.
|
|
76
86
|
*/
|
|
77
87
|
export const ts_spine_bun_backend_config = (options = {}) => {
|
|
78
|
-
const { port = TS_SPINE_BUN_DEFAULT_PORT, database_url } = options;
|
|
88
|
+
const { port = TS_SPINE_BUN_DEFAULT_PORT, database_url, enable_login_rate_limit } = options;
|
|
79
89
|
const name = 'ts_spine_bun';
|
|
80
90
|
const paths = build_test_backend_paths(name);
|
|
81
91
|
return {
|
|
@@ -85,7 +95,10 @@ export const ts_spine_bun_backend_config = (options = {}) => {
|
|
|
85
95
|
start_command: ['bun', 'run', TS_SPINE_BUN_ENTRY],
|
|
86
96
|
database_url,
|
|
87
97
|
paths,
|
|
88
|
-
extra_env: {
|
|
98
|
+
extra_env: {
|
|
99
|
+
[TS_SPINE_DIR_ENV]: paths.root,
|
|
100
|
+
...login_rate_limit_env(enable_login_rate_limit),
|
|
101
|
+
},
|
|
89
102
|
capabilities: ts_spine_bun_capabilities,
|
|
90
103
|
}),
|
|
91
104
|
sse_path: TS_SPINE_SSE_PATH,
|
|
@@ -103,7 +116,7 @@ export const ts_spine_bun_backend_config = (options = {}) => {
|
|
|
103
116
|
* built package; this binary tests live source instead.)
|
|
104
117
|
*/
|
|
105
118
|
export const ts_spine_deno_backend_config = (options = {}) => {
|
|
106
|
-
const { port = TS_SPINE_DENO_DEFAULT_PORT, database_url } = options;
|
|
119
|
+
const { port = TS_SPINE_DENO_DEFAULT_PORT, database_url, enable_login_rate_limit } = options;
|
|
107
120
|
const name = 'ts_spine_deno';
|
|
108
121
|
const paths = build_test_backend_paths(name);
|
|
109
122
|
return {
|
|
@@ -126,7 +139,10 @@ export const ts_spine_deno_backend_config = (options = {}) => {
|
|
|
126
139
|
],
|
|
127
140
|
database_url,
|
|
128
141
|
paths,
|
|
129
|
-
extra_env: {
|
|
142
|
+
extra_env: {
|
|
143
|
+
[TS_SPINE_DIR_ENV]: paths.root,
|
|
144
|
+
...login_rate_limit_env(enable_login_rate_limit),
|
|
145
|
+
},
|
|
130
146
|
capabilities: ts_spine_capabilities,
|
|
131
147
|
}),
|
|
132
148
|
sse_path: TS_SPINE_SSE_PATH,
|
package/dist/testing/db.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/db.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/db.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA8B7B,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AAKpC;;GAEG;AACH,eAAO,MAAM,KAAK,SAA4B,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,OAAO,CAAC,EAAE,CAAC,CAAC;IAC1B,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GAAU,IAAI,EAAE,KAAG,OAAO,CAAC,IAAI,CAGvD,CAAC;AAMF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,GAAI,aAAa,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,KAAG,SAkB7E,CAAC;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,iBAAiB,GAC7B,aAAa,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,EACtC,WAAW,MAAM,KACf,SA2DF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,UAQhC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gCAAgC,UAAyC,CAAC;AAEvF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,gBAAgB,GAAU,IAAI,EAAE,KAAG,OAAO,CAAC,IAAI,CAG3D,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kBAAkB,GAC9B,WAAW,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,EACvC,iBAAiB,KAAK,CAAC,MAAM,CAAC,KAC5B,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,KAAK,IAAI,CAwBzD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAAI,WAAW,KAAK,CAAC,SAAS,CAAC,KAAG,IAMnE,CAAC"}
|
package/dist/testing/db.js
CHANGED
|
@@ -23,6 +23,7 @@ import './assert_dev_env.js';
|
|
|
23
23
|
* @module
|
|
24
24
|
*/
|
|
25
25
|
import { describe, beforeAll, beforeEach, afterAll } from 'vitest';
|
|
26
|
+
import { to_error_message } from '@fuzdev/fuz_util/error.js';
|
|
26
27
|
import { create_pglite_db } from '../db/db_pglite.js';
|
|
27
28
|
import { assert_valid_sql_identifier } from '../db/sql_identifier.js';
|
|
28
29
|
import { create_pg_db } from '../db/db_pg.js';
|
|
@@ -133,7 +134,7 @@ export const create_pg_factory = (init_schema, test_url) => {
|
|
|
133
134
|
}
|
|
134
135
|
catch (error) {
|
|
135
136
|
await pool.end();
|
|
136
|
-
const msg =
|
|
137
|
+
const msg = to_error_message(error);
|
|
137
138
|
if (msg.includes('does not exist')) {
|
|
138
139
|
const db_name = test_url.split('/').pop()?.split('?')[0] ?? 'test_db';
|
|
139
140
|
throw new Error(`Database "${db_name}" does not exist. Create it with: createdb ${db_name}`);
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import {to_error_message} from '@fuzdev/fuz_util/error.js';
|
|
12
|
+
|
|
11
13
|
import type {AppSurface} from '../http/surface.js';
|
|
12
14
|
import SurfaceExplorer from './SurfaceExplorer.svelte';
|
|
13
15
|
import {ui_fetch} from './ui_fetch.js';
|
|
@@ -27,7 +29,7 @@
|
|
|
27
29
|
}
|
|
28
30
|
surface = await res.json();
|
|
29
31
|
} catch (e) {
|
|
30
|
-
error = e
|
|
32
|
+
error = to_error_message(e, 'Unknown error');
|
|
31
33
|
} finally {
|
|
32
34
|
loading = false;
|
|
33
35
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminSurface.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminSurface.svelte"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AdminSurface.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminSurface.svelte"],"names":[],"mappings":"AAkEA,QAAA,MAAM,YAAY,2DAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"async_slot.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/async_slot.svelte.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AAEH,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"async_slot.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/async_slot.svelte.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AAEH,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,2BAA2B,CAAC;AAG3D,MAAM,WAAW,gBAAgB,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM;IAC9C;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,CAAC;IACZ;;;;;OAKG;IACH,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,CAAC,CAAC;IAC9B;;;;;;;OAOG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,UAAU;IAC1B;;;;OAIG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,qBAAa,SAAS,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,MAAM;;IAC1C,MAAM,EAAE,WAAW,CAAyB;IAC5C,IAAI,EAAE,CAAC,GAAG,SAAS,CAAwC;IAC3D,KAAK,EAAE,CAAC,GAAG,IAAI,CAAoB;IACnC,sFAAsF;IACtF,UAAU,EAAE,OAAO,CAAoB;IAEvC,mDAAmD;IACnD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAuC;IAChE,mDAAmD;IACnD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAuC;IAChE,mDAAmD;IACnD,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAuC;IAClE,mDAAmD;IACnD,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAuC;gBAcnD,OAAO,GAAE,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAM;IAUhD;;;;;;;;;;;;;;;;;;;;;OAqBG;IACG,GAAG,CACR,EAAE,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,EACvC,OAAO,GAAE,UAAe,GACtB,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IA8DzB;;;;;;;OAOG;IACH,KAAK,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI;IAM7B;;;;;;;;;;;OAWG;IACH,GAAG,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI;IASlB;;;;;;OAMG;IACH,KAAK,IAAI,IAAI;CAQb"}
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
*
|
|
59
59
|
* @module
|
|
60
60
|
*/
|
|
61
|
+
import { to_error_message } from '@fuzdev/fuz_util/error.js';
|
|
61
62
|
/**
|
|
62
63
|
* Reactive container for a single async operation.
|
|
63
64
|
*
|
|
@@ -238,4 +239,4 @@ export class AsyncSlot {
|
|
|
238
239
|
this.#has_succeeded = false;
|
|
239
240
|
}
|
|
240
241
|
}
|
|
241
|
-
const default_map_error = (e) => e
|
|
242
|
+
const default_map_error = (e) => to_error_message(e, 'Request failed');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth_state.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/auth_state.svelte.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;
|
|
1
|
+
{"version":3,"file":"auth_state.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/auth_state.svelte.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAMH,OAAO,EACN,KAAK,gBAAgB,EAErB,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACnB,MAAM,2BAA2B,CAAC;AAEnC;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;;;CAA8B,CAAC;AAE9D,qBAAa,SAAS;IACrB,SAAS,UAAqB;IAC9B,QAAQ,UAAqB;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAoB;IAC/C,OAAO,EAAE,cAAc,GAAG,IAAI,CAAoB;IAClD,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAoB;IAClD,WAAW,EAAE,KAAK,CAAC,oBAAoB,CAAC,CAAkB;IAC1D,QAAQ,CAAC,kBAAkB,EAAE,KAAK,CAAC,oBAAoB,CAAC,CAEtD;IACF,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAwD;IAErF,gEAAgE;IAChE,eAAe,UAAqB;IAEpC;;;;;;;;OAQG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IA6BpC;;;;;;;OAOG;IACG,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAwCjE;;;;;OAKG;IACG,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiCpF;;;;;;;OAOG;IACG,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA4ClF;;;;;OAKG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAW7B"}
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
*
|
|
32
32
|
* @module
|
|
33
33
|
*/
|
|
34
|
+
import { to_error_message } from '@fuzdev/fuz_util/error.js';
|
|
34
35
|
import { create_context } from '@fuzdev/fuz_ui/context_helpers.js';
|
|
35
36
|
import { ui_fetch } from './ui_fetch.js';
|
|
36
37
|
import { is_role_grant_active, } from '../auth/account_schema.js';
|
|
@@ -133,7 +134,7 @@ export class AuthState {
|
|
|
133
134
|
return false;
|
|
134
135
|
}
|
|
135
136
|
catch (e) {
|
|
136
|
-
this.verify_error = e
|
|
137
|
+
this.verify_error = to_error_message(e, 'Connection failed');
|
|
137
138
|
return false;
|
|
138
139
|
}
|
|
139
140
|
finally {
|
|
@@ -171,7 +172,7 @@ export class AuthState {
|
|
|
171
172
|
return false;
|
|
172
173
|
}
|
|
173
174
|
catch (e) {
|
|
174
|
-
this.verify_error = e
|
|
175
|
+
this.verify_error = to_error_message(e, 'Connection failed');
|
|
175
176
|
return false;
|
|
176
177
|
}
|
|
177
178
|
finally {
|
|
@@ -225,7 +226,7 @@ export class AuthState {
|
|
|
225
226
|
return false;
|
|
226
227
|
}
|
|
227
228
|
catch (e) {
|
|
228
|
-
this.verify_error = e
|
|
229
|
+
this.verify_error = to_error_message(e, 'Connection failed');
|
|
229
230
|
return false;
|
|
230
231
|
}
|
|
231
232
|
finally {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table_state.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/table_state.svelte.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;
|
|
1
|
+
{"version":3,"file":"table_state.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/table_state.svelte.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAIH,OAAO,EAAC,SAAS,EAAC,MAAM,wBAAwB,CAAC;AAGjD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,sBAAsB,CAAC;AAErD,mEAAmE;AACnE,eAAO,MAAM,eAAe,OAAO,CAAC;AAEpC,qBAAa,UAAU;IACtB,QAAQ,CAAC,IAAI,0BAAyB;IAEtC,UAAU,EAAE,MAAM,CAAkB;IACpC,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,CAAkB;IAC5C,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAkB;IACtD,KAAK,SAAiB;IACtB,MAAM,SAAiB;IACvB,KAAK,SAAmB;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAoB;IAC9C,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAoB;IAC3C,YAAY,EAAE,MAAM,GAAG,IAAI,CAAoB;IAG/C,QAAQ,CAAC,aAAa,SAAoD;IAC1E,QAAQ,CAAC,WAAW,SAAkE;IACtF,QAAQ,CAAC,QAAQ,UAA6B;IAC9C,QAAQ,CAAC,QAAQ,UAAmD;IAEpE;;;;;OAKG;IACG,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,SAAI,EAAE,KAAK,SAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBvE,OAAO,IAAI,IAAI;IAIf,OAAO,IAAI,IAAI;IAIf;;;;;;;OAOG;IACG,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;CAoChE"}
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
*
|
|
40
40
|
* @module
|
|
41
41
|
*/
|
|
42
|
+
import { to_error_message } from '@fuzdev/fuz_util/error.js';
|
|
42
43
|
import { AsyncSlot } from './async_slot.svelte.js';
|
|
43
44
|
import { parse_response_error, ui_fetch } from './ui_fetch.js';
|
|
44
45
|
import { format_value } from './ui_format.js';
|
|
@@ -124,7 +125,7 @@ export class TableState {
|
|
|
124
125
|
return true;
|
|
125
126
|
}
|
|
126
127
|
catch (e) {
|
|
127
|
-
this.delete_error = e
|
|
128
|
+
this.delete_error = to_error_message(e, 'Delete failed');
|
|
128
129
|
return false;
|
|
129
130
|
}
|
|
130
131
|
finally {
|