@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.
Files changed (63) hide show
  1. package/dist/actions/action_rpc.d.ts.map +1 -1
  2. package/dist/actions/action_rpc.js +2 -2
  3. package/dist/actions/perform_action.d.ts.map +1 -1
  4. package/dist/actions/perform_action.js +6 -6
  5. package/dist/actions/transports_ws.d.ts.map +1 -1
  6. package/dist/actions/transports_ws.js +2 -1
  7. package/dist/actions/transports_ws_backend.d.ts.map +1 -1
  8. package/dist/actions/transports_ws_backend.js +2 -1
  9. package/dist/auth/bootstrap_routes.d.ts.map +1 -1
  10. package/dist/auth/bootstrap_routes.js +2 -1
  11. package/dist/auth/cell_actions.d.ts.map +1 -1
  12. package/dist/auth/cell_actions.js +2 -4
  13. package/dist/db/fact_disk_storage.d.ts.map +1 -1
  14. package/dist/db/fact_disk_storage.js +2 -1
  15. package/dist/db/fact_store.d.ts.map +1 -1
  16. package/dist/db/fact_store.js +2 -1
  17. package/dist/db/migrate.d.ts.map +1 -1
  18. package/dist/db/migrate.js +2 -1
  19. package/dist/db/status.d.ts.map +1 -1
  20. package/dist/db/status.js +2 -1
  21. package/dist/http/error_schemas.d.ts +5 -3
  22. package/dist/http/error_schemas.d.ts.map +1 -1
  23. package/dist/http/error_schemas.js +5 -3
  24. package/dist/http/jsonrpc_errors.d.ts +21 -0
  25. package/dist/http/jsonrpc_errors.d.ts.map +1 -1
  26. package/dist/http/jsonrpc_errors.js +22 -0
  27. package/dist/http/route_spec.d.ts.map +1 -1
  28. package/dist/http/route_spec.js +11 -8
  29. package/dist/runtime/deno.d.ts.map +1 -1
  30. package/dist/runtime/deno.js +2 -1
  31. package/dist/server/env.d.ts.map +1 -1
  32. package/dist/server/env.js +2 -1
  33. package/dist/testing/CLAUDE.md +27 -0
  34. package/dist/testing/cross_backend/create_dual_spawn_global_setup.d.ts +64 -0
  35. package/dist/testing/cross_backend/create_dual_spawn_global_setup.d.ts.map +1 -0
  36. package/dist/testing/cross_backend/{create_schema_parity_global_setup.js → create_dual_spawn_global_setup.js} +1 -1
  37. package/dist/testing/cross_backend/default_backend_configs.d.ts +12 -0
  38. package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
  39. package/dist/testing/cross_backend/default_backend_configs.js +12 -0
  40. package/dist/testing/cross_backend/default_spine_surface.d.ts.map +1 -1
  41. package/dist/testing/cross_backend/default_spine_surface.js +9 -2
  42. package/dist/testing/cross_backend/login_security.d.ts +11 -0
  43. package/dist/testing/cross_backend/login_security.d.ts.map +1 -0
  44. package/dist/testing/cross_backend/login_security.js +130 -0
  45. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts +17 -0
  46. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
  47. package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +7 -2
  48. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts +11 -0
  49. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
  50. package/dist/testing/cross_backend/ts_spine_backend_config.js +23 -7
  51. package/dist/testing/db.d.ts.map +1 -1
  52. package/dist/testing/db.js +2 -1
  53. package/dist/ui/AdminSurface.svelte +3 -1
  54. package/dist/ui/AdminSurface.svelte.d.ts.map +1 -1
  55. package/dist/ui/async_slot.svelte.d.ts.map +1 -1
  56. package/dist/ui/async_slot.svelte.js +2 -1
  57. package/dist/ui/auth_state.svelte.d.ts.map +1 -1
  58. package/dist/ui/auth_state.svelte.js +4 -3
  59. package/dist/ui/table_state.svelte.d.ts.map +1 -1
  60. package/dist/ui/table_state.svelte.js +2 -1
  61. package/package.json +4 -3
  62. package/dist/testing/cross_backend/create_schema_parity_global_setup.d.ts +0 -59
  63. 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 create_schema_parity_global_setup = ({ configs, provide_keys = { a: 'parity_handle_a', b: 'parity_handle_b' }, }) => {
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,CAwB/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"}
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
- ip_rate_limiter: null,
81
- login_account_rate_limiter: null,
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;AAQvD,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;CAC9B;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,8BAA8B,GAC1C,UAAS,6BAAkC,KACzC,aAwCF,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;AAOvD,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;CAC/B;AAED;;;GAGG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAS,2BAAgC,KACvC,aAgBF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,GACvC,UAAS,2BAAgC,KACvC,aAgBF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAS,2BAAgC,KACvC,aA6BF,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: { [TS_SPINE_DIR_ENV]: paths.root },
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: { [TS_SPINE_DIR_ENV]: paths.root },
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: { [TS_SPINE_DIR_ENV]: paths.root },
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,
@@ -1 +1 @@
1
- {"version":3,"file":"db.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/db.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA6B7B,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"}
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"}
@@ -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 = error instanceof Error ? error.message : String(error);
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 instanceof Error ? e.message : 'Unknown error';
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":"AA8DA,QAAA,MAAM,YAAY,2DAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
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;AAE3D,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"}
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 instanceof Error ? e.message : 'Request failed';
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;AAKH,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"}
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 instanceof Error ? e.message : 'Connection failed';
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 instanceof Error ? e.message : 'Connection failed';
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 instanceof Error ? e.message : 'Connection failed';
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;AAEH,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"}
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 instanceof Error ? e.message : 'Delete failed';
128
+ this.delete_error = to_error_message(e, 'Delete failed');
128
129
  return false;
129
130
  }
130
131
  finally {