@fuzdev/fuz_app 0.113.0 → 0.115.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/CLAUDE.md +46 -20
- package/dist/actions/action_dispatcher.d.ts +1 -1
- package/dist/actions/action_dispatcher.d.ts.map +1 -1
- package/dist/actions/action_dispatcher.js +22 -16
- package/dist/actions/action_event.d.ts +18 -2
- package/dist/actions/action_event.d.ts.map +1 -1
- package/dist/actions/action_event.js +63 -19
- package/dist/actions/action_event_types.d.ts +22 -0
- package/dist/actions/action_event_types.d.ts.map +1 -1
- package/dist/actions/action_event_types.js +19 -0
- package/dist/actions/compile_action_registry.d.ts +4 -2
- package/dist/actions/compile_action_registry.d.ts.map +1 -1
- package/dist/actions/compile_action_registry.js +8 -3
- package/dist/actions/frontend_rpc_client.d.ts +6 -0
- package/dist/actions/frontend_rpc_client.d.ts.map +1 -1
- package/dist/actions/frontend_rpc_client.js +2 -1
- package/dist/actions/heartbeat.d.ts +8 -4
- package/dist/actions/heartbeat.d.ts.map +1 -1
- package/dist/actions/heartbeat.js +8 -4
- package/dist/actions/peer_ping.d.ts.map +1 -1
- package/dist/actions/peer_ping.js +3 -1
- package/dist/actions/perform_action.d.ts +1 -1
- package/dist/actions/perform_action.d.ts.map +1 -1
- package/dist/actions/perform_action.js +6 -4
- package/dist/actions/register_action_ws.d.ts +13 -0
- package/dist/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +23 -2
- package/dist/actions/rpc_client.d.ts +1 -1
- package/dist/actions/rpc_client.d.ts.map +1 -1
- package/dist/actions/rpc_client.js +2 -1
- package/dist/actions/socket.svelte.d.ts +53 -10
- package/dist/actions/socket.svelte.d.ts.map +1 -1
- package/dist/actions/socket.svelte.js +84 -23
- package/dist/actions/transports.d.ts +24 -0
- package/dist/actions/transports.d.ts.map +1 -1
- package/dist/actions/transports.js +30 -0
- package/dist/actions/transports_http.d.ts +11 -4
- package/dist/actions/transports_http.d.ts.map +1 -1
- package/dist/actions/transports_http.js +63 -18
- package/dist/actions/transports_ws.d.ts +28 -3
- package/dist/actions/transports_ws.d.ts.map +1 -1
- package/dist/actions/transports_ws.js +33 -4
- package/dist/actions/ws_endpoint_spec.d.ts +7 -0
- package/dist/actions/ws_endpoint_spec.d.ts.map +1 -1
- package/dist/auth/CLAUDE.md +52 -8
- package/dist/auth/admin_actions.d.ts.map +1 -1
- package/dist/auth/admin_actions.js +4 -3
- package/dist/auth/audit_log_schema.d.ts +3 -1
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +13 -3
- package/dist/auth/cell_action_specs.js +1 -1
- package/dist/auth/cell_actions.d.ts.map +1 -1
- package/dist/auth/cell_actions.js +33 -4
- package/dist/auth/cell_authorize.d.ts +3 -0
- package/dist/auth/cell_authorize.d.ts.map +1 -1
- package/dist/auth/cell_authorize.js +7 -4
- package/dist/auth/request_context.d.ts +12 -3
- package/dist/auth/request_context.d.ts.map +1 -1
- package/dist/auth/request_context.js +12 -3
- package/dist/auth/role_grant_offer_action_specs.d.ts +30 -5
- package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_action_specs.js +53 -17
- package/dist/auth/role_grant_offer_actions.d.ts +23 -12
- package/dist/auth/role_grant_offer_actions.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_actions.js +132 -38
- package/dist/auth/role_grant_offer_queries.d.ts +36 -0
- package/dist/auth/role_grant_offer_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_queries.js +76 -0
- package/dist/auth/role_schema.d.ts +12 -0
- package/dist/auth/role_schema.d.ts.map +1 -1
- package/dist/auth/role_schema.js +12 -0
- package/dist/auth/self_service_role_actions.d.ts +14 -0
- package/dist/auth/self_service_role_actions.d.ts.map +1 -1
- package/dist/auth/self_service_role_actions.js +25 -1
- package/dist/http/CLAUDE.md +7 -3
- package/dist/http/jsonrpc_helpers.d.ts +0 -5
- package/dist/http/jsonrpc_helpers.d.ts.map +1 -1
- package/dist/http/jsonrpc_helpers.js +0 -13
- package/dist/http/schema_helpers.d.ts +56 -0
- package/dist/http/schema_helpers.d.ts.map +1 -1
- package/dist/http/schema_helpers.js +91 -0
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +1 -0
- package/dist/server/serve_fact_route.d.ts +3 -1
- package/dist/server/serve_fact_route.d.ts.map +1 -1
- package/dist/server/serve_fact_route.js +7 -4
- package/dist/testing/CLAUDE.md +29 -16
- package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_gated_create.js +23 -1
- package/dist/testing/cross_backend/peer_ping_ws.d.ts.map +1 -1
- package/dist/testing/cross_backend/peer_ping_ws.js +18 -0
- package/dist/testing/cross_backend/role_grant_participation.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_participation.js +34 -0
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts +12 -1
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +13 -1
- package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts.map +1 -1
- package/dist/testing/cross_backend/test_cell_gated_create_authorize.js +9 -7
- package/dist/testing/cross_backend/ws_round_trip.d.ts +11 -3
- package/dist/testing/cross_backend/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/cross_backend/ws_round_trip.js +74 -12
- package/dist/testing/entities.d.ts +6 -3
- package/dist/testing/entities.d.ts.map +1 -1
- package/dist/testing/entities.js +4 -1
- package/dist/testing/transports/ws_client.d.ts +6 -0
- package/dist/testing/transports/ws_client.d.ts.map +1 -1
- package/dist/testing/transports/ws_transport.d.ts.map +1 -1
- package/dist/testing/transports/ws_transport.js +6 -1
- package/dist/testing/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/ws_round_trip.js +5 -0
- package/dist/ui/BootstrapForm.svelte +14 -10
- package/dist/ui/BootstrapForm.svelte.d.ts.map +1 -1
- package/dist/ui/CLAUDE.md +19 -7
- package/dist/ui/LoginForm.svelte +8 -4
- package/dist/ui/LoginForm.svelte.d.ts.map +1 -1
- package/dist/ui/RoleGrantOfferForm.svelte +29 -14
- package/dist/ui/RoleGrantOfferForm.svelte.d.ts +2 -3
- package/dist/ui/RoleGrantOfferForm.svelte.d.ts.map +1 -1
- package/dist/ui/SignupForm.svelte +14 -13
- package/dist/ui/form_state.svelte.d.ts +24 -11
- package/dist/ui/form_state.svelte.d.ts.map +1 -1
- package/dist/ui/form_state.svelte.js +38 -10
- package/dist/ui/role_grant_offers_state.svelte.d.ts +25 -15
- package/dist/ui/role_grant_offers_state.svelte.d.ts.map +1 -1
- package/dist/ui/role_grant_offers_state.svelte.js +7 -1
- package/package.json +2 -22
- package/dist/actions/request_tracker.svelte.d.ts +0 -77
- package/dist/actions/request_tracker.svelte.d.ts.map +0 -1
- package/dist/actions/request_tracker.svelte.js +0 -169
|
@@ -5,16 +5,28 @@
|
|
|
5
5
|
* `attempted` state (set on submit attempt). Errors show after a field is blurred or
|
|
6
6
|
* after a submit attempt, avoiding premature validation while the user is still typing.
|
|
7
7
|
*
|
|
8
|
-
* The `FormState.form` attachment also handles Enter key
|
|
9
|
-
*
|
|
8
|
+
* The `FormState.form` attachment also handles the Enter key: in an input
|
|
9
|
+
* followed by another input it moves focus to that input (past any button
|
|
10
|
+
* between), and in the last input it submits the form (`requestSubmit`) —
|
|
11
|
+
* so Enter in a login form's password field logs in, even when the form's
|
|
12
|
+
* button is `type="button"`, as `PendingButton` is. Hidden inputs don't count, and an Enter that commits
|
|
13
|
+
* IME composition is left alone.
|
|
14
|
+
*
|
|
15
|
+
* While a submit is in flight, make the fields `readonly` rather than `disabled`
|
|
16
|
+
* (and the button `aria-disabled`, guarding the handler against re-entry) —
|
|
17
|
+
* disabling the focused element drops focus to `<body>`. Enter still advances
|
|
18
|
+
* through and submits from `readonly` inputs.
|
|
19
|
+
*
|
|
20
|
+
* The type parameter names the form's fields, so `focus`, `show`, `touch`,
|
|
21
|
+
* and `is_touched` reject a misspelled field at compile time.
|
|
10
22
|
*
|
|
11
23
|
* All trackable inputs must have a `name` attribute — an error is thrown in dev
|
|
12
24
|
* if an input without `name` loses focus.
|
|
13
25
|
*
|
|
14
26
|
* @example
|
|
15
27
|
* ```svelte
|
|
16
|
-
* <script>
|
|
17
|
-
* const form_state = new FormState();
|
|
28
|
+
* <script lang="ts">
|
|
29
|
+
* const form_state = new FormState<'username'>();
|
|
18
30
|
* let username = $state.raw('');
|
|
19
31
|
* const username_valid = $derived(Username.safeParse(username).success);
|
|
20
32
|
* const can_submit = $derived(username.trim() && username_valid);
|
|
@@ -43,7 +55,8 @@
|
|
|
43
55
|
import { DEV } from 'esm-env';
|
|
44
56
|
import { on } from 'svelte/events';
|
|
45
57
|
import { SvelteSet } from 'svelte/reactivity';
|
|
46
|
-
|
|
58
|
+
/** Inputs Enter moves between — disabled and hidden ones are skipped. */
|
|
59
|
+
const ENTER_INPUT_SELECTOR = 'input:not(:disabled):not([type=hidden])';
|
|
47
60
|
const FORM_INPUT_SELECTOR = 'input, textarea, select';
|
|
48
61
|
export class FormState {
|
|
49
62
|
#touched = new SvelteSet();
|
|
@@ -56,8 +69,9 @@ export class FormState {
|
|
|
56
69
|
return this.#attempted;
|
|
57
70
|
}
|
|
58
71
|
/**
|
|
59
|
-
* Creates a form attachment that handles Enter key
|
|
60
|
-
*
|
|
72
|
+
* Creates a form attachment that handles the Enter key (advance focus to
|
|
73
|
+
* the next input, or submit from the last one) and tracks field touched
|
|
74
|
+
* state via delegated `focusout`.
|
|
61
75
|
*
|
|
62
76
|
* Fields are identified by their `name` attribute.
|
|
63
77
|
*
|
|
@@ -73,14 +87,28 @@ export class FormState {
|
|
|
73
87
|
const keydown_cleanup = on(form, 'keydown', (e) => {
|
|
74
88
|
if (e.key !== 'Enter')
|
|
75
89
|
return;
|
|
90
|
+
// an IME composing text commits it with Enter — not a submit; Safari
|
|
91
|
+
// reports that Enter with `isComposing` false but `keyCode` 229 — the
|
|
92
|
+
// deprecated field is the only signal there
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
94
|
+
if (e.isComposing || e.keyCode === 229)
|
|
95
|
+
return;
|
|
76
96
|
if (!(e.target instanceof HTMLInputElement))
|
|
77
97
|
return;
|
|
78
|
-
const
|
|
79
|
-
const index =
|
|
98
|
+
const inputs = Array.from(form.querySelectorAll(ENTER_INPUT_SELECTOR));
|
|
99
|
+
const index = inputs.indexOf(e.target);
|
|
80
100
|
if (index < 0)
|
|
81
101
|
return;
|
|
82
102
|
e.preventDefault();
|
|
83
|
-
|
|
103
|
+
// advance to the next input, past any button between (a show-password
|
|
104
|
+
// toggle, say); the last input submits instead
|
|
105
|
+
const next_input = inputs[index + 1];
|
|
106
|
+
if (next_input) {
|
|
107
|
+
next_input.focus();
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
form.requestSubmit();
|
|
111
|
+
}
|
|
84
112
|
});
|
|
85
113
|
const focusout_cleanup = on(form, 'focusout', (e) => {
|
|
86
114
|
const target = e.target;
|
|
@@ -26,6 +26,26 @@
|
|
|
26
26
|
*/
|
|
27
27
|
import { AsyncSlot } from './async_slot.svelte.js';
|
|
28
28
|
import type { RoleGrantOfferJson } from '../auth/role_grant_offer_schema.js';
|
|
29
|
+
/**
|
|
30
|
+
* The scope of an offer: global (both absent / `null`) or scoped (both set).
|
|
31
|
+
* `scope_kind` and `scope_id` are paired on the wire — a lone `scope_id` is an
|
|
32
|
+
* `invalid_params` refusal — so the pair is one union rather than two optional
|
|
33
|
+
* fields.
|
|
34
|
+
*/
|
|
35
|
+
export type RoleGrantOfferScope = {
|
|
36
|
+
scope_kind?: null;
|
|
37
|
+
scope_id?: null;
|
|
38
|
+
} | {
|
|
39
|
+
scope_kind: string;
|
|
40
|
+
scope_id: string;
|
|
41
|
+
};
|
|
42
|
+
/** Params for creating an offer — `RoleGrantOffersRpc.create` and `submit_create`. */
|
|
43
|
+
export type RoleGrantOfferCreateParams = {
|
|
44
|
+
to_account_id: string;
|
|
45
|
+
to_actor_id?: string | null;
|
|
46
|
+
role: string;
|
|
47
|
+
message?: string | null;
|
|
48
|
+
} & RoleGrantOfferScope;
|
|
29
49
|
/**
|
|
30
50
|
* Svelte context for `RoleGrantOffersState`.
|
|
31
51
|
* Use `role_grant_offers_state_context.set(state)` in the provider and
|
|
@@ -52,13 +72,7 @@ export interface RoleGrantOffersRpc {
|
|
|
52
72
|
}) => Promise<{
|
|
53
73
|
offers: Array<RoleGrantOfferJson>;
|
|
54
74
|
}>;
|
|
55
|
-
create: (params: {
|
|
56
|
-
to_account_id: string;
|
|
57
|
-
to_actor_id?: string | null;
|
|
58
|
-
role: string;
|
|
59
|
-
scope_id?: string | null;
|
|
60
|
-
message?: string | null;
|
|
61
|
-
}) => Promise<{
|
|
75
|
+
create: (params: RoleGrantOfferCreateParams) => Promise<{
|
|
62
76
|
offer: RoleGrantOfferJson;
|
|
63
77
|
}>;
|
|
64
78
|
accept: (offer_id: string) => Promise<{
|
|
@@ -135,15 +149,11 @@ export declare class RoleGrantOffersState {
|
|
|
135
149
|
*
|
|
136
150
|
* `to_actor_id` (optional) narrows the offer to a specific actor on
|
|
137
151
|
* `to_account_id`; omit / null for the account-grain default (any actor
|
|
138
|
-
* on the recipient account may accept).
|
|
152
|
+
* on the recipient account may accept). A scoped offer passes `scope_kind`
|
|
153
|
+
* and `scope_id` together; an unpaired scope (reachable from untyped
|
|
154
|
+
* callers) fails with `GRANT_SCOPE_PAIRED_MESSAGE` without an RPC.
|
|
139
155
|
*/
|
|
140
|
-
submit_create(params:
|
|
141
|
-
to_account_id: string;
|
|
142
|
-
to_actor_id?: string | null;
|
|
143
|
-
role: string;
|
|
144
|
-
scope_id?: string | null;
|
|
145
|
-
message?: string | null;
|
|
146
|
-
}): Promise<RoleGrantOfferJson | undefined>;
|
|
156
|
+
submit_create(params: RoleGrantOfferCreateParams): Promise<RoleGrantOfferJson | undefined>;
|
|
147
157
|
/** Accept an offer; stamps it terminal in the cache and drops any siblings the server superseded. */
|
|
148
158
|
submit_accept(offer_id: string): Promise<void>;
|
|
149
159
|
submit_decline(offer_id: string, reason?: string | null): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role_grant_offers_state.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/role_grant_offers_state.svelte.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEnD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"role_grant_offers_state.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/role_grant_offers_state.svelte.ts"],"names":[],"mappings":"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"}
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
import { create_context } from '@fuzdev/fuz_ui/context_helpers.ts';
|
|
28
28
|
import { AsyncSlot } from './async_slot.svelte.js';
|
|
29
29
|
import { is_iso8601_seconds_live } from '../timestamp.js';
|
|
30
|
+
import { GRANT_SCOPE_PAIRED_MESSAGE, is_grant_scope_paired } from '../auth/role_grant_offer_action_specs.js';
|
|
30
31
|
import { ROLE_GRANT_OFFER_ACCEPTED_NOTIFICATION_METHOD, ROLE_GRANT_OFFER_DECLINED_NOTIFICATION_METHOD, ROLE_GRANT_OFFER_RECEIVED_NOTIFICATION_METHOD, ROLE_GRANT_OFFER_RETRACTED_NOTIFICATION_METHOD, ROLE_GRANT_OFFER_SUPERSEDE_NOTIFICATION_METHOD, ROLE_GRANT_REVOKE_NOTIFICATION_METHOD } from '../auth/role_grant_offer_notifications.js';
|
|
31
32
|
/**
|
|
32
33
|
* Svelte context for `RoleGrantOffersState`.
|
|
@@ -121,10 +122,15 @@ export class RoleGrantOffersState {
|
|
|
121
122
|
*
|
|
122
123
|
* `to_actor_id` (optional) narrows the offer to a specific actor on
|
|
123
124
|
* `to_account_id`; omit / null for the account-grain default (any actor
|
|
124
|
-
* on the recipient account may accept).
|
|
125
|
+
* on the recipient account may accept). A scoped offer passes `scope_kind`
|
|
126
|
+
* and `scope_id` together; an unpaired scope (reachable from untyped
|
|
127
|
+
* callers) fails with `GRANT_SCOPE_PAIRED_MESSAGE` without an RPC.
|
|
125
128
|
*/
|
|
126
129
|
async submit_create(params) {
|
|
127
130
|
return this.create.run(async () => {
|
|
131
|
+
if (!is_grant_scope_paired(params)) {
|
|
132
|
+
throw new Error(GRANT_SCOPE_PAIRED_MESSAGE);
|
|
133
|
+
}
|
|
128
134
|
const { offer } = await this.#rpc.create(params);
|
|
129
135
|
this.#merge_offers([offer]);
|
|
130
136
|
return offer;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fuzdev/fuz_app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.115.0",
|
|
4
4
|
"description": "fullstack app library",
|
|
5
5
|
"glyph": "🗝",
|
|
6
6
|
"logo": "logo.svg",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"@electric-sql/pglite": "^0.4.5",
|
|
76
76
|
"@fuzdev/blake3-wasm": "^0.2.0",
|
|
77
77
|
"@fuzdev/fuz_code": "^0.48.0",
|
|
78
|
-
"@fuzdev/fuz_css": "^0.
|
|
78
|
+
"@fuzdev/fuz_css": "^0.64.0",
|
|
79
79
|
"@fuzdev/fuz_ui": "^0.208.0",
|
|
80
80
|
"@fuzdev/fuz_util": "^0.69.0",
|
|
81
81
|
"@fuzdev/gro": "^0.212.0",
|
|
@@ -100,8 +100,6 @@
|
|
|
100
100
|
"jsdom": "^28.1.0",
|
|
101
101
|
"magic-string": "^0.30.21",
|
|
102
102
|
"pg": "^8.20.0",
|
|
103
|
-
"prettier": "^3.9.6",
|
|
104
|
-
"prettier-plugin-svelte": "^4.1.1",
|
|
105
103
|
"svelte": "^5.56.4",
|
|
106
104
|
"svelte-check": "^4.7.1",
|
|
107
105
|
"svelte-docinfo": "^0.7.0",
|
|
@@ -115,24 +113,6 @@
|
|
|
115
113
|
"zimmerframe": "^1.1.4",
|
|
116
114
|
"zod": "^4.3.6"
|
|
117
115
|
},
|
|
118
|
-
"prettier": {
|
|
119
|
-
"plugins": [
|
|
120
|
-
"prettier-plugin-svelte"
|
|
121
|
-
],
|
|
122
|
-
"useTabs": true,
|
|
123
|
-
"printWidth": 100,
|
|
124
|
-
"singleQuote": true,
|
|
125
|
-
"bracketSpacing": true,
|
|
126
|
-
"trailingComma": "none",
|
|
127
|
-
"overrides": [
|
|
128
|
-
{
|
|
129
|
-
"files": "package.json",
|
|
130
|
-
"options": {
|
|
131
|
-
"useTabs": false
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
]
|
|
135
|
-
},
|
|
136
116
|
"sideEffects": [
|
|
137
117
|
"**/*.css"
|
|
138
118
|
],
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Request tracker — manages pending JSON-RPC requests with timeouts.
|
|
3
|
-
*
|
|
4
|
-
* Uses SvelteMap for reactive pending request tracking.
|
|
5
|
-
*
|
|
6
|
-
* @module
|
|
7
|
-
*/
|
|
8
|
-
import { type Deferred, type AsyncStatus } from '@fuzdev/fuz_util/async.ts';
|
|
9
|
-
import { SvelteMap } from 'svelte/reactivity';
|
|
10
|
-
import { type JsonrpcErrorResponse, type JsonrpcRequestId, type JsonrpcResponseOrError } from '../http/jsonrpc.js';
|
|
11
|
-
/** ISO datetime string for request creation timestamps. */
|
|
12
|
-
type Datetime = string & {
|
|
13
|
-
readonly __brand: 'Datetime';
|
|
14
|
-
};
|
|
15
|
-
export declare class RequestTrackerItem {
|
|
16
|
-
readonly id: JsonrpcRequestId;
|
|
17
|
-
readonly deferred: Deferred<JsonrpcResponseOrError>;
|
|
18
|
-
readonly created: Datetime;
|
|
19
|
-
status: AsyncStatus;
|
|
20
|
-
timeout: NodeJS.Timeout | undefined;
|
|
21
|
-
constructor(id: JsonrpcRequestId, deferred: Deferred<JsonrpcResponseOrError>, created: Datetime, status: AsyncStatus, timeout: NodeJS.Timeout | undefined);
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Reactive pending-request store with per-request timeouts. Used by transports
|
|
25
|
-
* that don't delegate request/response correlation to a `WebsocketRpcConnection`.
|
|
26
|
-
*/
|
|
27
|
-
export declare class RequestTracker {
|
|
28
|
-
readonly pending_requests: SvelteMap<JsonrpcRequestId, RequestTrackerItem>;
|
|
29
|
-
readonly request_timeout_ms: number;
|
|
30
|
-
constructor(request_timeout_ms?: number);
|
|
31
|
-
/**
|
|
32
|
-
* Track a new request keyed by `id`.
|
|
33
|
-
*
|
|
34
|
-
* @returns deferred resolved on response, or rejected via the timeout
|
|
35
|
-
* @mutates this - inserts a `RequestTrackerItem` into `pending_requests`
|
|
36
|
-
* and arms a timeout that auto-rejects after `request_timeout_ms`;
|
|
37
|
-
* clears any prior timeout for the same id
|
|
38
|
-
*/
|
|
39
|
-
track_request(id: JsonrpcRequestId): Deferred<JsonrpcResponseOrError>;
|
|
40
|
-
/**
|
|
41
|
-
* Resolve a pending request with its response.
|
|
42
|
-
*
|
|
43
|
-
* @mutates this - clears the timeout, marks status `'success'`,
|
|
44
|
-
* resolves the deferred, and removes the entry from `pending_requests`
|
|
45
|
-
*/
|
|
46
|
-
resolve_request(id: JsonrpcRequestId, response: JsonrpcResponseOrError): void;
|
|
47
|
-
/**
|
|
48
|
-
* Reject a pending request with `error_message`.
|
|
49
|
-
*
|
|
50
|
-
* @mutates this - clears the timeout, marks status `'failure'`,
|
|
51
|
-
* rejects the deferred with a `ThrownJsonrpcError`, and removes the
|
|
52
|
-
* entry from `pending_requests`
|
|
53
|
-
*/
|
|
54
|
-
reject_request(id: JsonrpcRequestId, error_message: JsonrpcErrorResponse): void;
|
|
55
|
-
/**
|
|
56
|
-
* Handles an incoming JSON-RPC message. Resolves or rejects the associated request.
|
|
57
|
-
* Ignores notifications and unknown/invalid messages.
|
|
58
|
-
*/
|
|
59
|
-
handle_message(message: any): void;
|
|
60
|
-
/**
|
|
61
|
-
* Cancel a pending request without rejecting its deferred — just
|
|
62
|
-
* cleanup. The caller's promise stays unsettled; pair with an external
|
|
63
|
-
* resolution if needed.
|
|
64
|
-
*
|
|
65
|
-
* @mutates this - clears the timeout and removes the entry from `pending_requests`
|
|
66
|
-
*/
|
|
67
|
-
cancel_request(id: JsonrpcRequestId): void;
|
|
68
|
-
/**
|
|
69
|
-
* Cancel all pending requests.
|
|
70
|
-
* @param reason - optional reason to include in rejection
|
|
71
|
-
* @mutates this - clears every timeout, marks each status `'failure'`,
|
|
72
|
-
* rejects each deferred with `internal_error`, and empties `pending_requests`
|
|
73
|
-
*/
|
|
74
|
-
cancel_all_requests(reason?: string): void;
|
|
75
|
-
}
|
|
76
|
-
export {};
|
|
77
|
-
//# sourceMappingURL=request_tracker.svelte.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"request_tracker.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/request_tracker.svelte.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAmB,KAAK,QAAQ,EAAE,KAAK,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC7F,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,OAAO,EAEN,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,MAAM,oBAAoB,CAAC;AAG5B,2DAA2D;AAC3D,KAAK,QAAQ,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAA;CAAE,CAAC;AAM1D,qBAAa,kBAAkB;IAC9B,QAAQ,CAAC,EAAE,EAAE,gBAAgB,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,sBAAsB,CAAC,CAAC;IACpD,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC;IAC3B,MAAM,EAAE,WAAW,CAAiB;IACpC,OAAO,EAAE,MAAM,CAAC,OAAO,GAAG,SAAS,CAAgB;gBAGlD,EAAE,EAAE,gBAAgB,EACpB,QAAQ,EAAE,QAAQ,CAAC,sBAAsB,CAAC,EAC1C,OAAO,EAAE,QAAQ,EACjB,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,MAAM,CAAC,OAAO,GAAG,SAAS;CAQpC;AAED;;;GAGG;AACH,qBAAa,cAAc;IAC1B,QAAQ,CAAC,gBAAgB,EAAE,SAAS,CAAC,gBAAgB,EAAE,kBAAkB,CAAC,CAAmB;IAC7F,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;gBAExB,kBAAkB,SAAU;IAIxC;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,EAAE,gBAAgB,GAAG,QAAQ,CAAC,sBAAsB,CAAC;IA6BrE;;;;;OAKG;IACH,eAAe,CAAC,EAAE,EAAE,gBAAgB,EAAE,QAAQ,EAAE,sBAAsB,GAAG,IAAI;IAkB7E;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,EAAE,gBAAgB,EAAE,aAAa,EAAE,oBAAoB,GAAG,IAAI;IAuB/E;;;OAGG;IACH,cAAc,CAAC,OAAO,EAAE,GAAG,GAAG,IAAI;IAiBlC;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,EAAE,gBAAgB,GAAG,IAAI;IAe1C;;;;;OAKG;IACH,mBAAmB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;CAiB1C"}
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Request tracker — manages pending JSON-RPC requests with timeouts.
|
|
3
|
-
*
|
|
4
|
-
* Uses SvelteMap for reactive pending request tracking.
|
|
5
|
-
*
|
|
6
|
-
* @module
|
|
7
|
-
*/
|
|
8
|
-
import { create_deferred } from '@fuzdev/fuz_util/async.ts';
|
|
9
|
-
import { SvelteMap } from 'svelte/reactivity';
|
|
10
|
-
import { JSONRPC_INTERNAL_ERROR } from '../http/jsonrpc.js';
|
|
11
|
-
import { ThrownJsonrpcError, JSONRPC_ERROR_CODES } from '../http/jsonrpc_errors.js';
|
|
12
|
-
const get_datetime_now = () => new Date().toISOString();
|
|
13
|
-
// TODO what if this uses a tracker id param that's an opaque UUID but can be used for action association?
|
|
14
|
-
// TODO name, like `TrackedRequest`? or is this implicit namespacing and generic name preferred
|
|
15
|
-
export class RequestTrackerItem {
|
|
16
|
-
id;
|
|
17
|
-
deferred;
|
|
18
|
-
created;
|
|
19
|
-
status = $state.raw();
|
|
20
|
-
timeout = $state.raw();
|
|
21
|
-
constructor(id, deferred, created, status, timeout) {
|
|
22
|
-
this.id = id;
|
|
23
|
-
this.deferred = deferred;
|
|
24
|
-
this.created = created;
|
|
25
|
-
this.status = status;
|
|
26
|
-
this.timeout = timeout;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Reactive pending-request store with per-request timeouts. Used by transports
|
|
31
|
-
* that don't delegate request/response correlation to a `WebsocketRpcConnection`.
|
|
32
|
-
*/
|
|
33
|
-
export class RequestTracker {
|
|
34
|
-
pending_requests = new SvelteMap();
|
|
35
|
-
request_timeout_ms;
|
|
36
|
-
constructor(request_timeout_ms = 120_000) {
|
|
37
|
-
this.request_timeout_ms = request_timeout_ms;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Track a new request keyed by `id`.
|
|
41
|
-
*
|
|
42
|
-
* @returns deferred resolved on response, or rejected via the timeout
|
|
43
|
-
* @mutates this - inserts a `RequestTrackerItem` into `pending_requests`
|
|
44
|
-
* and arms a timeout that auto-rejects after `request_timeout_ms`;
|
|
45
|
-
* clears any prior timeout for the same id
|
|
46
|
-
*/
|
|
47
|
-
track_request(id) {
|
|
48
|
-
const deferred = create_deferred();
|
|
49
|
-
const created = get_datetime_now();
|
|
50
|
-
// If we're tracking a request with the same id, clean up the previous one first
|
|
51
|
-
const existing_request = this.pending_requests.get(id);
|
|
52
|
-
if (existing_request?.timeout) {
|
|
53
|
-
clearTimeout(existing_request.timeout);
|
|
54
|
-
}
|
|
55
|
-
// Set up a timeout to automatically reject the request after a delay
|
|
56
|
-
const timeout = setTimeout(() => {
|
|
57
|
-
// Create a proper timeout error message
|
|
58
|
-
this.reject_request(id, {
|
|
59
|
-
jsonrpc: '2.0',
|
|
60
|
-
id,
|
|
61
|
-
error: { code: JSONRPC_INTERNAL_ERROR, message: `request timed out: ${id}` }
|
|
62
|
-
});
|
|
63
|
-
}, this.request_timeout_ms);
|
|
64
|
-
// Store the request tracker using the new class
|
|
65
|
-
this.pending_requests.set(id, new RequestTrackerItem(id, deferred, created, 'pending', timeout));
|
|
66
|
-
return deferred;
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Resolve a pending request with its response.
|
|
70
|
-
*
|
|
71
|
-
* @mutates this - clears the timeout, marks status `'success'`,
|
|
72
|
-
* resolves the deferred, and removes the entry from `pending_requests`
|
|
73
|
-
*/
|
|
74
|
-
resolve_request(id, response) {
|
|
75
|
-
const request = this.pending_requests.get(id);
|
|
76
|
-
if (!request) {
|
|
77
|
-
console.warn(`received response for unknown request: ${id}`);
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
// Clear the timeout and resolve the promise
|
|
81
|
-
if (request.timeout) {
|
|
82
|
-
clearTimeout(request.timeout);
|
|
83
|
-
request.timeout = undefined;
|
|
84
|
-
}
|
|
85
|
-
request.status = 'success';
|
|
86
|
-
request.deferred.resolve(response);
|
|
87
|
-
this.pending_requests.delete(id);
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Reject a pending request with `error_message`.
|
|
91
|
-
*
|
|
92
|
-
* @mutates this - clears the timeout, marks status `'failure'`,
|
|
93
|
-
* rejects the deferred with a `ThrownJsonrpcError`, and removes the
|
|
94
|
-
* entry from `pending_requests`
|
|
95
|
-
*/
|
|
96
|
-
reject_request(id, error_message) {
|
|
97
|
-
const request = this.pending_requests.get(id);
|
|
98
|
-
if (!request) {
|
|
99
|
-
console.warn(`received error for unknown request: ${id}`);
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
// Clear the timeout and reject the promise
|
|
103
|
-
if (request.timeout) {
|
|
104
|
-
clearTimeout(request.timeout);
|
|
105
|
-
request.timeout = undefined;
|
|
106
|
-
}
|
|
107
|
-
request.status = 'failure';
|
|
108
|
-
const error = new ThrownJsonrpcError(error_message.error.code, error_message.error.message, error_message.error.data);
|
|
109
|
-
request.deferred.reject(error);
|
|
110
|
-
this.pending_requests.delete(id);
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Handles an incoming JSON-RPC message. Resolves or rejects the associated request.
|
|
114
|
-
* Ignores notifications and unknown/invalid messages.
|
|
115
|
-
*/
|
|
116
|
-
handle_message(message) {
|
|
117
|
-
if (!message)
|
|
118
|
-
return; // ignore invalid values
|
|
119
|
-
const { id } = message;
|
|
120
|
-
// TODO maybe log a warning/error?
|
|
121
|
-
if (id == null)
|
|
122
|
-
return; // ignore notifications and errors without ids
|
|
123
|
-
// JSON-RPC responses require both an `id` and either a `result` or `error` field, but not both
|
|
124
|
-
if ('result' in message) {
|
|
125
|
-
this.resolve_request(id, message);
|
|
126
|
-
}
|
|
127
|
-
else if ('error' in message) {
|
|
128
|
-
this.reject_request(id, message);
|
|
129
|
-
}
|
|
130
|
-
// ignore other messages
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* Cancel a pending request without rejecting its deferred — just
|
|
134
|
-
* cleanup. The caller's promise stays unsettled; pair with an external
|
|
135
|
-
* resolution if needed.
|
|
136
|
-
*
|
|
137
|
-
* @mutates this - clears the timeout and removes the entry from `pending_requests`
|
|
138
|
-
*/
|
|
139
|
-
cancel_request(id) {
|
|
140
|
-
const request = this.pending_requests.get(id);
|
|
141
|
-
if (!request) {
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
if (request.timeout) {
|
|
145
|
-
clearTimeout(request.timeout);
|
|
146
|
-
request.timeout = undefined;
|
|
147
|
-
}
|
|
148
|
-
// We don't reject the promise here, just clean up the tracking
|
|
149
|
-
this.pending_requests.delete(id);
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Cancel all pending requests.
|
|
153
|
-
* @param reason - optional reason to include in rejection
|
|
154
|
-
* @mutates this - clears every timeout, marks each status `'failure'`,
|
|
155
|
-
* rejects each deferred with `internal_error`, and empties `pending_requests`
|
|
156
|
-
*/
|
|
157
|
-
cancel_all_requests(reason) {
|
|
158
|
-
for (const [id, request] of this.pending_requests.entries()) {
|
|
159
|
-
if (request.timeout) {
|
|
160
|
-
clearTimeout(request.timeout);
|
|
161
|
-
request.timeout = undefined;
|
|
162
|
-
}
|
|
163
|
-
request.status = 'failure';
|
|
164
|
-
request.deferred.reject(new ThrownJsonrpcError(JSONRPC_ERROR_CODES.internal_error, // TODO canceled error?
|
|
165
|
-
reason || 'request cancelled'));
|
|
166
|
-
this.pending_requests.delete(id);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|