@fuzdev/fuz_app 0.60.0 → 0.62.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 +28 -22
- package/dist/auth/CLAUDE.md +4 -4
- package/dist/server/app_server.d.ts +54 -6
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +32 -4
- package/dist/testing/CLAUDE.md +8 -8
- package/dist/ui/AccountSessions.svelte +21 -6
- package/dist/ui/AccountSessions.svelte.d.ts.map +1 -1
- package/dist/ui/AdminAccounts.svelte +32 -25
- package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -1
- package/dist/ui/AdminAuditLog.svelte +3 -3
- package/dist/ui/AdminInvites.svelte +20 -15
- package/dist/ui/AdminOverview.svelte +19 -21
- package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
- package/dist/ui/AdminRoleGrantHistory.svelte +3 -3
- package/dist/ui/AdminSessions.svelte +19 -21
- package/dist/ui/AdminSessions.svelte.d.ts.map +1 -1
- package/dist/ui/AdminSettings.svelte +1 -3
- package/dist/ui/AdminSettings.svelte.d.ts.map +1 -1
- package/dist/ui/CLAUDE.md +123 -69
- package/dist/ui/ConfirmButton.svelte +82 -24
- package/dist/ui/ConfirmButton.svelte.d.ts +8 -34
- package/dist/ui/ConfirmButton.svelte.d.ts.map +1 -1
- package/dist/ui/OpenSignupToggle.svelte +6 -4
- package/dist/ui/OpenSignupToggle.svelte.d.ts.map +1 -1
- package/dist/ui/RoleGrantOfferForm.svelte +4 -4
- package/dist/ui/RoleGrantOfferHistory.svelte +3 -3
- package/dist/ui/RoleGrantOfferInbox.svelte +10 -6
- package/dist/ui/RoleGrantOfferInbox.svelte.d.ts.map +1 -1
- package/dist/ui/account_sessions_state.svelte.d.ts +17 -7
- package/dist/ui/account_sessions_state.svelte.d.ts.map +1 -1
- package/dist/ui/account_sessions_state.svelte.js +32 -33
- package/dist/ui/admin_accounts_state.svelte.d.ts +48 -17
- package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_accounts_state.svelte.js +58 -76
- package/dist/ui/admin_invites_state.svelte.d.ts +14 -7
- package/dist/ui/admin_invites_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_invites_state.svelte.js +32 -48
- package/dist/ui/admin_sessions_state.svelte.d.ts +15 -8
- package/dist/ui/admin_sessions_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_sessions_state.svelte.js +30 -47
- package/dist/ui/app_settings_state.svelte.d.ts +8 -3
- package/dist/ui/app_settings_state.svelte.d.ts.map +1 -1
- package/dist/ui/app_settings_state.svelte.js +19 -27
- package/dist/ui/async_slot.svelte.d.ts +173 -0
- package/dist/ui/async_slot.svelte.d.ts.map +1 -0
- package/dist/ui/async_slot.svelte.js +241 -0
- package/dist/ui/audit_log_state.svelte.d.ts +8 -2
- package/dist/ui/audit_log_state.svelte.d.ts.map +1 -1
- package/dist/ui/audit_log_state.svelte.js +19 -18
- package/dist/ui/keyed_async_slot.svelte.d.ts +139 -0
- package/dist/ui/keyed_async_slot.svelte.d.ts.map +1 -0
- package/dist/ui/keyed_async_slot.svelte.js +177 -0
- package/dist/ui/role_grant_offers_state.svelte.d.ts +39 -7
- package/dist/ui/role_grant_offers_state.svelte.d.ts.map +1 -1
- package/dist/ui/role_grant_offers_state.svelte.js +34 -15
- package/dist/ui/table_state.svelte.d.ts +10 -7
- package/dist/ui/table_state.svelte.d.ts.map +1 -1
- package/dist/ui/table_state.svelte.js +11 -8
- package/package.json +1 -1
- package/dist/ui/loadable.svelte.d.ts +0 -60
- package/dist/ui/loadable.svelte.d.ts.map +0 -1
- package/dist/ui/loadable.svelte.js +0 -80
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keyed sibling of `AsyncSlot` — fans the per-instance supersession
|
|
3
|
+
* machinery out across an open set of keys.
|
|
4
|
+
*
|
|
5
|
+
* Each key gets its own lazily-created `AsyncSlot`, so concurrent
|
|
6
|
+
* `run(key_a, ...)` and `run(key_b, ...)` calls are independent: a
|
|
7
|
+
* second `run()` on `key_a` aborts only `key_a`'s in-flight call,
|
|
8
|
+
* leaving `key_b` running. The keyed shape replaces the
|
|
9
|
+
* `AsyncSlot` + `SvelteSet<id>` pair that state classes previously
|
|
10
|
+
* carried for per-row in-flight tracking, with two genuine wins:
|
|
11
|
+
*
|
|
12
|
+
* - **Cross-key supersession is correct** — clicking row B while row
|
|
13
|
+
* A is in flight no longer aborts A; each row has its own
|
|
14
|
+
* AbortController.
|
|
15
|
+
* - **Per-key error surfacing** — `error(key)` carries the failure
|
|
16
|
+
* for that key only, instead of the last-error-wins shape of a
|
|
17
|
+
* shared slot.
|
|
18
|
+
*
|
|
19
|
+
* The backing `SvelteMap` keeps entries even after a `run()` resolves
|
|
20
|
+
* — components can read `error(key)` to render an inline per-row
|
|
21
|
+
* failure indicator. Call `delete(key)` to dismiss an entry, or
|
|
22
|
+
* `reset()` to clear everything (e.g. on page leave).
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* class AdminInvitesState {
|
|
27
|
+
* readonly remove = new KeyedAsyncSlot<Uuid>();
|
|
28
|
+
*
|
|
29
|
+
* async submit_delete(id: Uuid): Promise<void> {
|
|
30
|
+
* const ok = await this.remove.run(id, () => this.#rpc().delete({invite_id: id}));
|
|
31
|
+
* if (ok !== undefined) await this.fetch();
|
|
32
|
+
* }
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* // In a template:
|
|
36
|
+
* // <button disabled={state.remove.loading(row.id)}>
|
|
37
|
+
* // {state.remove.loading(row.id) ? 'deleting…' : 'delete'}
|
|
38
|
+
* // </button>
|
|
39
|
+
* // {#if state.remove.error(row.id)}<p>{state.remove.error(row.id)}</p>{/if}
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @module
|
|
43
|
+
*/
|
|
44
|
+
import { SvelteMap } from 'svelte/reactivity';
|
|
45
|
+
import { AsyncSlot } from './async_slot.svelte.js';
|
|
46
|
+
/**
|
|
47
|
+
* Reactive container for many concurrent async operations keyed by `K`.
|
|
48
|
+
*
|
|
49
|
+
* @typeParam K - The key type. Map identity is SameValueZero — branded
|
|
50
|
+
* strings (`Uuid`) work directly. For composite keys, stringify at
|
|
51
|
+
* the call site (e.g. `` `${account_id}:${role}` ``).
|
|
52
|
+
* @typeParam T - The success payload type. Use `void` for write-only
|
|
53
|
+
* actions whose response isn't worth retaining.
|
|
54
|
+
* @typeParam E - The shape of per-key `error(key)`. Defaults to
|
|
55
|
+
* `string` (set by the default `map_error`).
|
|
56
|
+
*/
|
|
57
|
+
export class KeyedAsyncSlot {
|
|
58
|
+
#slots = new SvelteMap();
|
|
59
|
+
#options;
|
|
60
|
+
constructor(options = {}) {
|
|
61
|
+
this.#options = options;
|
|
62
|
+
}
|
|
63
|
+
/** Total number of keys with state (pending OR resolved). Reactive. */
|
|
64
|
+
get size() {
|
|
65
|
+
return this.#slots.size;
|
|
66
|
+
}
|
|
67
|
+
/** Reactive — true once `run(key, ...)` has been called and the entry hasn't been deleted. */
|
|
68
|
+
has(key) {
|
|
69
|
+
return this.#slots.has(key);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Direct access to the underlying `AsyncSlot` for `key`, or
|
|
73
|
+
* `undefined` if no `run()` has been issued for it yet. Reactive on
|
|
74
|
+
* map population and on the slot's `$state.raw` fields.
|
|
75
|
+
*
|
|
76
|
+
* Prefer the sugar getters ({@link loading}, {@link error}) for
|
|
77
|
+
* templates; reach for `get(key)` when you need `error_data`, `data`,
|
|
78
|
+
* or to call `abort()` / `set()` / `reset()` on the underlying slot.
|
|
79
|
+
*/
|
|
80
|
+
get(key) {
|
|
81
|
+
return this.#slots.get(key);
|
|
82
|
+
}
|
|
83
|
+
/** Reactive — `false` for keys that have never been used. */
|
|
84
|
+
loading(key) {
|
|
85
|
+
return this.#slots.get(key)?.loading ?? false;
|
|
86
|
+
}
|
|
87
|
+
/** Reactive — `null` when the key has no entry or hasn't failed. */
|
|
88
|
+
error(key) {
|
|
89
|
+
return this.#slots.get(key)?.error ?? null;
|
|
90
|
+
}
|
|
91
|
+
/** Reactive — `false` for keys that have never been used. */
|
|
92
|
+
failed(key) {
|
|
93
|
+
return this.#slots.get(key)?.failed ?? false;
|
|
94
|
+
}
|
|
95
|
+
/** Reactive — `false` for keys that have never been used. */
|
|
96
|
+
succeeded(key) {
|
|
97
|
+
return this.#slots.get(key)?.succeeded ?? false;
|
|
98
|
+
}
|
|
99
|
+
/** Reactive iterator over every key with state. */
|
|
100
|
+
keys() {
|
|
101
|
+
return this.#slots.keys();
|
|
102
|
+
}
|
|
103
|
+
/** Reactive iterator over every slot. */
|
|
104
|
+
values() {
|
|
105
|
+
return this.#slots.values();
|
|
106
|
+
}
|
|
107
|
+
/** Reactive iterator over `[key, slot]` pairs. */
|
|
108
|
+
entries() {
|
|
109
|
+
return this.#slots.entries();
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Run an async operation for `key`. Lazily creates an `AsyncSlot`
|
|
113
|
+
* for the key on first use, inheriting the constructor's
|
|
114
|
+
* `map_error` / `preserve_error_on_retry` options.
|
|
115
|
+
*
|
|
116
|
+
* Supersession is scoped to `key`: a second `run(key, ...)` aborts
|
|
117
|
+
* the first's signal AND drops its commit. Calls on different keys
|
|
118
|
+
* are fully independent (each has its own `AbortController`).
|
|
119
|
+
*
|
|
120
|
+
* @returns the resolved value on success; `undefined` on failure,
|
|
121
|
+
* abort, or supersession.
|
|
122
|
+
*/
|
|
123
|
+
async run(key, fn, options) {
|
|
124
|
+
let slot = this.#slots.get(key);
|
|
125
|
+
if (!slot) {
|
|
126
|
+
slot = new AsyncSlot(this.#options);
|
|
127
|
+
this.#slots.set(key, slot);
|
|
128
|
+
}
|
|
129
|
+
return slot.run(fn, options);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Abort the in-flight run for `key`, if any. No-op when the key has
|
|
133
|
+
* no entry. The slot stays in the map at its prior resolved status —
|
|
134
|
+
* call {@link delete} to remove the entry entirely.
|
|
135
|
+
*/
|
|
136
|
+
abort(key, reason) {
|
|
137
|
+
this.#slots.get(key)?.abort(reason);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Abort every in-flight run. Resolved entries stay in the map —
|
|
141
|
+
* call {@link reset} to clear them too.
|
|
142
|
+
*/
|
|
143
|
+
abort_all(reason) {
|
|
144
|
+
for (const slot of this.#slots.values()) {
|
|
145
|
+
slot.abort(reason);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Abort the in-flight run for `key` (if any) and remove the entry
|
|
150
|
+
* from the map. After `delete(key)`, `has(key)` returns `false` and
|
|
151
|
+
* the sugar getters report the no-entry defaults — typically how a
|
|
152
|
+
* UI dismisses a per-row error indicator.
|
|
153
|
+
*
|
|
154
|
+
* @returns `true` if the key had an entry.
|
|
155
|
+
*
|
|
156
|
+
* @mutates `this`
|
|
157
|
+
*/
|
|
158
|
+
delete(key) {
|
|
159
|
+
const slot = this.#slots.get(key);
|
|
160
|
+
if (!slot)
|
|
161
|
+
return false;
|
|
162
|
+
slot.abort();
|
|
163
|
+
return this.#slots.delete(key);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Abort every in-flight run and clear the map. The keyed slot looks
|
|
167
|
+
* like a fresh instance afterwards.
|
|
168
|
+
*
|
|
169
|
+
* @mutates `this`
|
|
170
|
+
*/
|
|
171
|
+
reset() {
|
|
172
|
+
for (const slot of this.#slots.values()) {
|
|
173
|
+
slot.abort();
|
|
174
|
+
}
|
|
175
|
+
this.#slots.clear();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -13,9 +13,18 @@
|
|
|
13
13
|
* delivery is pull-only via `subscribe()` — the consumer plumbs their
|
|
14
14
|
* `FrontendWebsocketClient` / `ActionPeer` receiver to `apply_notification`.
|
|
15
15
|
*
|
|
16
|
+
* Holds six `AsyncSlot`s — one per RPC verb. The cache `#offers` lives on
|
|
17
|
+
* the class (multiple ops write into it via `#merge_offers` /
|
|
18
|
+
* `#remove_offer`); the `create` slot is typed `AsyncSlot<RoleGrantOfferJson>`
|
|
19
|
+
* so `submit_create` can return the new offer via the slot's
|
|
20
|
+
* supersession-safe `data` path, but the other slots' `data` is unused
|
|
21
|
+
* (no single op owns the cache). Method names use the `submit_*` prefix
|
|
22
|
+
* to avoid slot-name collisions; the `history` view stayed natural by
|
|
23
|
+
* naming the fetch slot `list_history`.
|
|
24
|
+
*
|
|
16
25
|
* @module
|
|
17
26
|
*/
|
|
18
|
-
import {
|
|
27
|
+
import { AsyncSlot } from './async_slot.svelte.js';
|
|
19
28
|
import type { RoleGrantOfferJson } from '../auth/role_grant_offer_schema.js';
|
|
20
29
|
/**
|
|
21
30
|
* Svelte context for `RoleGrantOffersState`.
|
|
@@ -81,8 +90,31 @@ export interface RoleGrantOffersStateOptions {
|
|
|
81
90
|
*/
|
|
82
91
|
actor_id: () => string | null;
|
|
83
92
|
}
|
|
84
|
-
export declare class RoleGrantOffersState
|
|
93
|
+
export declare class RoleGrantOffersState {
|
|
85
94
|
#private;
|
|
95
|
+
readonly list: AsyncSlot<void, string>;
|
|
96
|
+
readonly list_history: AsyncSlot<void, string>;
|
|
97
|
+
readonly create: AsyncSlot<{
|
|
98
|
+
id: string & import("zod").$brand<"Uuid">;
|
|
99
|
+
from_actor_id: string & import("zod").$brand<"Uuid">;
|
|
100
|
+
to_account_id: string & import("zod").$brand<"Uuid">;
|
|
101
|
+
to_actor_id: (string & import("zod").$brand<"Uuid">) | null;
|
|
102
|
+
role: string;
|
|
103
|
+
scope_kind: string | null;
|
|
104
|
+
scope_id: (string & import("zod").$brand<"Uuid">) | null;
|
|
105
|
+
message: string | null;
|
|
106
|
+
created_at: string;
|
|
107
|
+
expires_at: string;
|
|
108
|
+
accepted_at: string | null;
|
|
109
|
+
declined_at: string | null;
|
|
110
|
+
decline_reason: string | null;
|
|
111
|
+
retracted_at: string | null;
|
|
112
|
+
superseded_at: string | null;
|
|
113
|
+
resulting_role_grant_id: (string & import("zod").$brand<"Uuid">) | null;
|
|
114
|
+
}, string>;
|
|
115
|
+
readonly accept: AsyncSlot<void, string>;
|
|
116
|
+
readonly decline: AsyncSlot<void, string>;
|
|
117
|
+
readonly retract: AsyncSlot<void, string>;
|
|
86
118
|
/** Pending offers for the current account, soonest-expiring first. */
|
|
87
119
|
readonly incoming: Array<RoleGrantOfferJson>;
|
|
88
120
|
/** Pending offers from the current actor, newest-created first. */
|
|
@@ -105,7 +137,7 @@ export declare class RoleGrantOffersState extends Loadable {
|
|
|
105
137
|
* `to_account_id`; omit / null for the account-grain default (any actor
|
|
106
138
|
* on the recipient account may accept).
|
|
107
139
|
*/
|
|
108
|
-
|
|
140
|
+
submit_create(params: {
|
|
109
141
|
to_account_id: string;
|
|
110
142
|
to_actor_id?: string | null;
|
|
111
143
|
role: string;
|
|
@@ -113,9 +145,9 @@ export declare class RoleGrantOffersState extends Loadable {
|
|
|
113
145
|
message?: string | null;
|
|
114
146
|
}): Promise<RoleGrantOfferJson | undefined>;
|
|
115
147
|
/** Accept an offer; stamps it terminal in the cache and drops any siblings the server superseded. */
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
148
|
+
submit_accept(offer_id: string): Promise<void>;
|
|
149
|
+
submit_decline(offer_id: string, reason?: string | null): Promise<void>;
|
|
150
|
+
submit_retract(offer_id: string): Promise<void>;
|
|
119
151
|
/**
|
|
120
152
|
* Wire a notification subscription. The handler dispatches each matching
|
|
121
153
|
* notification into `apply_notification`; the returned disposer unwires.
|
|
@@ -129,7 +161,7 @@ export declare class RoleGrantOffersState extends Loadable {
|
|
|
129
161
|
* @mutates `this`
|
|
130
162
|
*/
|
|
131
163
|
apply_notification(notification: RoleGrantOfferNotification): void;
|
|
132
|
-
/** Clear the cache and reset
|
|
164
|
+
/** Clear the cache and reset every slot. */
|
|
133
165
|
reset(): void;
|
|
134
166
|
}
|
|
135
167
|
//# sourceMappingURL=role_grant_offers_state.svelte.d.ts.map
|
|
@@ -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
|
|
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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"}
|
|
@@ -13,10 +13,19 @@
|
|
|
13
13
|
* delivery is pull-only via `subscribe()` — the consumer plumbs their
|
|
14
14
|
* `FrontendWebsocketClient` / `ActionPeer` receiver to `apply_notification`.
|
|
15
15
|
*
|
|
16
|
+
* Holds six `AsyncSlot`s — one per RPC verb. The cache `#offers` lives on
|
|
17
|
+
* the class (multiple ops write into it via `#merge_offers` /
|
|
18
|
+
* `#remove_offer`); the `create` slot is typed `AsyncSlot<RoleGrantOfferJson>`
|
|
19
|
+
* so `submit_create` can return the new offer via the slot's
|
|
20
|
+
* supersession-safe `data` path, but the other slots' `data` is unused
|
|
21
|
+
* (no single op owns the cache). Method names use the `submit_*` prefix
|
|
22
|
+
* to avoid slot-name collisions; the `history` view stayed natural by
|
|
23
|
+
* naming the fetch slot `list_history`.
|
|
24
|
+
*
|
|
16
25
|
* @module
|
|
17
26
|
*/
|
|
18
27
|
import { create_context } from '@fuzdev/fuz_ui/context_helpers.js';
|
|
19
|
-
import {
|
|
28
|
+
import { AsyncSlot } from './async_slot.svelte.js';
|
|
20
29
|
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';
|
|
21
30
|
/**
|
|
22
31
|
* Svelte context for `RoleGrantOffersState`.
|
|
@@ -28,10 +37,16 @@ const is_terminal = (o) => o.accepted_at !== null ||
|
|
|
28
37
|
o.declined_at !== null ||
|
|
29
38
|
o.retracted_at !== null ||
|
|
30
39
|
o.superseded_at !== null;
|
|
31
|
-
export class RoleGrantOffersState
|
|
40
|
+
export class RoleGrantOffersState {
|
|
32
41
|
#rpc;
|
|
33
42
|
#get_account_id;
|
|
34
43
|
#get_actor_id;
|
|
44
|
+
list = new AsyncSlot();
|
|
45
|
+
list_history = new AsyncSlot();
|
|
46
|
+
create = new AsyncSlot();
|
|
47
|
+
accept = new AsyncSlot();
|
|
48
|
+
decline = new AsyncSlot();
|
|
49
|
+
retract = new AsyncSlot();
|
|
35
50
|
#offers = $state.raw(new Map());
|
|
36
51
|
/** Pending offers for the current account, soonest-expiring first. */
|
|
37
52
|
incoming = $derived.by(() => {
|
|
@@ -79,21 +94,20 @@ export class RoleGrantOffersState extends Loadable {
|
|
|
79
94
|
});
|
|
80
95
|
incoming_count = $derived(this.incoming.length);
|
|
81
96
|
constructor(options) {
|
|
82
|
-
super();
|
|
83
97
|
this.#rpc = options.rpc;
|
|
84
98
|
this.#get_account_id = options.account_id;
|
|
85
99
|
this.#get_actor_id = options.actor_id;
|
|
86
100
|
}
|
|
87
101
|
/** Seed the cache with the recipient-side pending inbox. */
|
|
88
102
|
async fetch() {
|
|
89
|
-
await this.run(async () => {
|
|
103
|
+
await this.list.run(async () => {
|
|
90
104
|
const { offers } = await this.#rpc.list();
|
|
91
105
|
this.#merge_offers(offers);
|
|
92
106
|
});
|
|
93
107
|
}
|
|
94
108
|
/** Seed both-directions history (includes terminal rows). */
|
|
95
109
|
async fetch_history(options) {
|
|
96
|
-
await this.run(async () => {
|
|
110
|
+
await this.list_history.run(async () => {
|
|
97
111
|
const { offers } = await this.#rpc.history(options);
|
|
98
112
|
this.#merge_offers(offers);
|
|
99
113
|
});
|
|
@@ -105,16 +119,16 @@ export class RoleGrantOffersState extends Loadable {
|
|
|
105
119
|
* `to_account_id`; omit / null for the account-grain default (any actor
|
|
106
120
|
* on the recipient account may accept).
|
|
107
121
|
*/
|
|
108
|
-
async
|
|
109
|
-
return this.run(async () => {
|
|
122
|
+
async submit_create(params) {
|
|
123
|
+
return this.create.run(async () => {
|
|
110
124
|
const { offer } = await this.#rpc.create(params);
|
|
111
125
|
this.#merge_offers([offer]);
|
|
112
126
|
return offer;
|
|
113
127
|
});
|
|
114
128
|
}
|
|
115
129
|
/** Accept an offer; stamps it terminal in the cache and drops any siblings the server superseded. */
|
|
116
|
-
async
|
|
117
|
-
await this.run(async () => {
|
|
130
|
+
async submit_accept(offer_id) {
|
|
131
|
+
await this.accept.run(async () => {
|
|
118
132
|
const result = await this.#rpc.accept(offer_id);
|
|
119
133
|
this.#merge_offers([result.offer]);
|
|
120
134
|
// siblings are authoritatively superseded server-side; the
|
|
@@ -125,14 +139,14 @@ export class RoleGrantOffersState extends Loadable {
|
|
|
125
139
|
}
|
|
126
140
|
});
|
|
127
141
|
}
|
|
128
|
-
async
|
|
129
|
-
await this.run(async () => {
|
|
142
|
+
async submit_decline(offer_id, reason) {
|
|
143
|
+
await this.decline.run(async () => {
|
|
130
144
|
await this.#rpc.decline(offer_id, reason);
|
|
131
145
|
this.#remove_offer(offer_id);
|
|
132
146
|
});
|
|
133
147
|
}
|
|
134
|
-
async
|
|
135
|
-
await this.run(async () => {
|
|
148
|
+
async submit_retract(offer_id) {
|
|
149
|
+
await this.retract.run(async () => {
|
|
136
150
|
await this.#rpc.retract(offer_id);
|
|
137
151
|
this.#remove_offer(offer_id);
|
|
138
152
|
});
|
|
@@ -178,9 +192,14 @@ export class RoleGrantOffersState extends Loadable {
|
|
|
178
192
|
return;
|
|
179
193
|
}
|
|
180
194
|
}
|
|
181
|
-
/** Clear the cache and reset
|
|
195
|
+
/** Clear the cache and reset every slot. */
|
|
182
196
|
reset() {
|
|
183
|
-
|
|
197
|
+
this.list.reset();
|
|
198
|
+
this.list_history.reset();
|
|
199
|
+
this.create.reset();
|
|
200
|
+
this.accept.reset();
|
|
201
|
+
this.decline.reset();
|
|
202
|
+
this.retract.reset();
|
|
184
203
|
this.#offers = new Map();
|
|
185
204
|
}
|
|
186
205
|
#merge_offers(offers) {
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Reactive state for database table pagination and data fetching.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Holds one `AsyncSlot` — `list` (the paginated row fetch). Per-row delete
|
|
5
|
+
* uses plain try/catch + scalar `deleting` / `delete_error` fields (no slot
|
|
6
|
+
* — `delete_error` must persist past `list.run()` retries so the failure
|
|
7
|
+
* message stays visible while the user refetches).
|
|
6
8
|
*
|
|
7
9
|
* @example
|
|
8
10
|
* ```ts
|
|
@@ -26,10 +28,10 @@
|
|
|
26
28
|
* table.fetch('accounts');
|
|
27
29
|
* </script>
|
|
28
30
|
*
|
|
29
|
-
* {#if table.loading}
|
|
31
|
+
* {#if table.list.loading}
|
|
30
32
|
* <p>loading…</p>
|
|
31
|
-
* {:else if table.error}
|
|
32
|
-
* <p>{table.error}</p>
|
|
33
|
+
* {:else if table.list.error}
|
|
34
|
+
* <p>{table.list.error}</p>
|
|
33
35
|
* {:else}
|
|
34
36
|
* <p>showing {table.showing_start}–{table.showing_end} of {table.total}</p>
|
|
35
37
|
* {/if}
|
|
@@ -37,11 +39,12 @@
|
|
|
37
39
|
*
|
|
38
40
|
* @module
|
|
39
41
|
*/
|
|
40
|
-
import {
|
|
42
|
+
import { AsyncSlot } from './async_slot.svelte.js';
|
|
41
43
|
import type { ColumnInfo } from '../http/db_routes.js';
|
|
42
44
|
/** Maximum number of rows that can be fetched in a single page. */
|
|
43
45
|
export declare const TABLE_LIMIT_MAX = 1000;
|
|
44
|
-
export declare class TableState
|
|
46
|
+
export declare class TableState {
|
|
47
|
+
readonly list: AsyncSlot<void, string>;
|
|
45
48
|
table_name: string;
|
|
46
49
|
columns: Array<ColumnInfo>;
|
|
47
50
|
rows: Array<Record<string, unknown>>;
|
|
@@ -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
|
|
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,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Reactive state for database table pagination and data fetching.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Holds one `AsyncSlot` — `list` (the paginated row fetch). Per-row delete
|
|
5
|
+
* uses plain try/catch + scalar `deleting` / `delete_error` fields (no slot
|
|
6
|
+
* — `delete_error` must persist past `list.run()` retries so the failure
|
|
7
|
+
* message stays visible while the user refetches).
|
|
6
8
|
*
|
|
7
9
|
* @example
|
|
8
10
|
* ```ts
|
|
@@ -26,10 +28,10 @@
|
|
|
26
28
|
* table.fetch('accounts');
|
|
27
29
|
* </script>
|
|
28
30
|
*
|
|
29
|
-
* {#if table.loading}
|
|
31
|
+
* {#if table.list.loading}
|
|
30
32
|
* <p>loading…</p>
|
|
31
|
-
* {:else if table.error}
|
|
32
|
-
* <p>{table.error}</p>
|
|
33
|
+
* {:else if table.list.error}
|
|
34
|
+
* <p>{table.list.error}</p>
|
|
33
35
|
* {:else}
|
|
34
36
|
* <p>showing {table.showing_start}–{table.showing_end} of {table.total}</p>
|
|
35
37
|
* {/if}
|
|
@@ -37,12 +39,13 @@
|
|
|
37
39
|
*
|
|
38
40
|
* @module
|
|
39
41
|
*/
|
|
40
|
-
import {
|
|
42
|
+
import { AsyncSlot } from './async_slot.svelte.js';
|
|
41
43
|
import { parse_response_error, ui_fetch } from './ui_fetch.js';
|
|
42
44
|
import { format_value } from './ui_format.js';
|
|
43
45
|
/** Maximum number of rows that can be fetched in a single page. */
|
|
44
46
|
export const TABLE_LIMIT_MAX = 1000;
|
|
45
|
-
export class TableState
|
|
47
|
+
export class TableState {
|
|
48
|
+
list = new AsyncSlot();
|
|
46
49
|
table_name = $state.raw('');
|
|
47
50
|
columns = $state.raw([]);
|
|
48
51
|
rows = $state.raw([]);
|
|
@@ -67,7 +70,7 @@ export class TableState extends Loadable {
|
|
|
67
70
|
this.table_name = table_name;
|
|
68
71
|
this.offset = offset;
|
|
69
72
|
this.limit = Math.max(1, Math.min(TABLE_LIMIT_MAX, limit));
|
|
70
|
-
await this.run(async () => {
|
|
73
|
+
await this.list.run(async () => {
|
|
71
74
|
const response = await ui_fetch(`/api/db/tables/${table_name}?offset=${this.offset}&limit=${this.limit}`);
|
|
72
75
|
if (!response.ok) {
|
|
73
76
|
throw new Error(await parse_response_error(response, 'Failed to fetch table'));
|
package/package.json
CHANGED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Base reactive state class with loading/error management.
|
|
3
|
-
*
|
|
4
|
-
* Provides the common loading/error pattern shared by all state classes.
|
|
5
|
-
* Subclasses add domain-specific `$state` fields and methods that call
|
|
6
|
-
* the protected `run` helper for async operations.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```ts
|
|
10
|
-
* class ItemsState extends Loadable {
|
|
11
|
-
* items: Array<Item> = $state.raw([]);
|
|
12
|
-
*
|
|
13
|
-
* async fetch(): Promise<void> {
|
|
14
|
-
* await this.run(async () => {
|
|
15
|
-
* const response = await fetch('/api/items');
|
|
16
|
-
* if (!response.ok) throw new Error('failed to fetch');
|
|
17
|
-
* this.items = await response.json();
|
|
18
|
-
* });
|
|
19
|
-
* }
|
|
20
|
-
* }
|
|
21
|
-
* ```
|
|
22
|
-
*
|
|
23
|
-
* @example
|
|
24
|
-
* ```ts
|
|
25
|
-
* // structured errors via map_error
|
|
26
|
-
* class FormState extends Loadable<{field: string; message: string}> {
|
|
27
|
-
* async submit(data: FormData): Promise<void> {
|
|
28
|
-
* await this.run(
|
|
29
|
-
* () => post_form(data),
|
|
30
|
-
* (e) => ({field: 'form', message: e instanceof Error ? e.message : 'unknown'}),
|
|
31
|
-
* );
|
|
32
|
-
* }
|
|
33
|
-
* }
|
|
34
|
-
* ```
|
|
35
|
-
*
|
|
36
|
-
* @module
|
|
37
|
-
*/
|
|
38
|
-
export declare class Loadable<TError = string> {
|
|
39
|
-
loading: boolean;
|
|
40
|
-
error: TError | null;
|
|
41
|
-
/** The raw caught value from the last failed `run()`, for programmatic inspection. */
|
|
42
|
-
error_data: unknown;
|
|
43
|
-
/**
|
|
44
|
-
* Run an async operation with loading/error handling.
|
|
45
|
-
*
|
|
46
|
-
* Sets `loading` to `true`, clears `error` and `error_data`, runs `fn`, catches errors.
|
|
47
|
-
* Pass `map_error` to produce structured errors instead of strings.
|
|
48
|
-
*
|
|
49
|
-
* @returns the result or `undefined` if the operation failed
|
|
50
|
-
* @mutates `this`
|
|
51
|
-
*/
|
|
52
|
-
protected run<T>(fn: () => Promise<T>, map_error?: (e: unknown) => TError): Promise<T | undefined>;
|
|
53
|
-
/**
|
|
54
|
-
* Reset loading and error state. Subclasses override to clear data.
|
|
55
|
-
*
|
|
56
|
-
* @mutates `this`
|
|
57
|
-
*/
|
|
58
|
-
reset(): void;
|
|
59
|
-
}
|
|
60
|
-
//# sourceMappingURL=loadable.svelte.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"loadable.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/loadable.svelte.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,qBAAa,QAAQ,CAAC,MAAM,GAAG,MAAM;IACpC,OAAO,UAAqB;IAC5B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAoB;IAExC,sFAAsF;IACtF,UAAU,EAAE,OAAO,CAAoB;IAEvC;;;;;;;;OAQG;cACa,GAAG,CAAC,CAAC,EACpB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,MAAM,GAChC,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAiBzB;;;;OAIG;IACH,KAAK,IAAI,IAAI;CAKb"}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Base reactive state class with loading/error management.
|
|
3
|
-
*
|
|
4
|
-
* Provides the common loading/error pattern shared by all state classes.
|
|
5
|
-
* Subclasses add domain-specific `$state` fields and methods that call
|
|
6
|
-
* the protected `run` helper for async operations.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```ts
|
|
10
|
-
* class ItemsState extends Loadable {
|
|
11
|
-
* items: Array<Item> = $state.raw([]);
|
|
12
|
-
*
|
|
13
|
-
* async fetch(): Promise<void> {
|
|
14
|
-
* await this.run(async () => {
|
|
15
|
-
* const response = await fetch('/api/items');
|
|
16
|
-
* if (!response.ok) throw new Error('failed to fetch');
|
|
17
|
-
* this.items = await response.json();
|
|
18
|
-
* });
|
|
19
|
-
* }
|
|
20
|
-
* }
|
|
21
|
-
* ```
|
|
22
|
-
*
|
|
23
|
-
* @example
|
|
24
|
-
* ```ts
|
|
25
|
-
* // structured errors via map_error
|
|
26
|
-
* class FormState extends Loadable<{field: string; message: string}> {
|
|
27
|
-
* async submit(data: FormData): Promise<void> {
|
|
28
|
-
* await this.run(
|
|
29
|
-
* () => post_form(data),
|
|
30
|
-
* (e) => ({field: 'form', message: e instanceof Error ? e.message : 'unknown'}),
|
|
31
|
-
* );
|
|
32
|
-
* }
|
|
33
|
-
* }
|
|
34
|
-
* ```
|
|
35
|
-
*
|
|
36
|
-
* @module
|
|
37
|
-
*/
|
|
38
|
-
export class Loadable {
|
|
39
|
-
loading = $state.raw(false);
|
|
40
|
-
error = $state.raw(null);
|
|
41
|
-
/** The raw caught value from the last failed `run()`, for programmatic inspection. */
|
|
42
|
-
error_data = $state.raw(null);
|
|
43
|
-
/**
|
|
44
|
-
* Run an async operation with loading/error handling.
|
|
45
|
-
*
|
|
46
|
-
* Sets `loading` to `true`, clears `error` and `error_data`, runs `fn`, catches errors.
|
|
47
|
-
* Pass `map_error` to produce structured errors instead of strings.
|
|
48
|
-
*
|
|
49
|
-
* @returns the result or `undefined` if the operation failed
|
|
50
|
-
* @mutates `this`
|
|
51
|
-
*/
|
|
52
|
-
async run(fn, map_error) {
|
|
53
|
-
this.loading = true;
|
|
54
|
-
this.error = null;
|
|
55
|
-
this.error_data = null;
|
|
56
|
-
try {
|
|
57
|
-
return await fn();
|
|
58
|
-
}
|
|
59
|
-
catch (e) {
|
|
60
|
-
this.error = map_error
|
|
61
|
-
? map_error(e)
|
|
62
|
-
: (e instanceof Error ? e.message : 'Request failed');
|
|
63
|
-
this.error_data = e;
|
|
64
|
-
return undefined;
|
|
65
|
-
}
|
|
66
|
-
finally {
|
|
67
|
-
this.loading = false;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Reset loading and error state. Subclasses override to clear data.
|
|
72
|
-
*
|
|
73
|
-
* @mutates `this`
|
|
74
|
-
*/
|
|
75
|
-
reset() {
|
|
76
|
-
this.loading = false;
|
|
77
|
-
this.error = null;
|
|
78
|
-
this.error_data = null;
|
|
79
|
-
}
|
|
80
|
-
}
|