@fuzdev/fuz_app 0.30.0 → 0.32.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 (222) hide show
  1. package/dist/actions/CLAUDE.md +630 -0
  2. package/dist/actions/action_rpc.d.ts +29 -0
  3. package/dist/actions/action_rpc.d.ts.map +1 -1
  4. package/dist/actions/action_rpc.js +42 -6
  5. package/dist/actions/action_types.d.ts +2 -2
  6. package/dist/actions/cancel.d.ts +12 -13
  7. package/dist/actions/cancel.d.ts.map +1 -1
  8. package/dist/actions/cancel.js +10 -13
  9. package/dist/actions/heartbeat.d.ts +8 -13
  10. package/dist/actions/heartbeat.d.ts.map +1 -1
  11. package/dist/actions/heartbeat.js +5 -8
  12. package/dist/actions/register_action_ws.d.ts +3 -3
  13. package/dist/actions/register_action_ws.js +2 -2
  14. package/dist/actions/register_ws_endpoint.d.ts +4 -4
  15. package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
  16. package/dist/actions/register_ws_endpoint.js +3 -3
  17. package/dist/actions/rpc_client.d.ts +29 -0
  18. package/dist/actions/rpc_client.d.ts.map +1 -1
  19. package/dist/actions/rpc_client.js +31 -0
  20. package/dist/actions/socket.svelte.d.ts +16 -16
  21. package/dist/actions/socket.svelte.d.ts.map +1 -1
  22. package/dist/actions/socket.svelte.js +15 -15
  23. package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
  24. package/dist/auth/CLAUDE.md +945 -0
  25. package/dist/auth/account_action_specs.d.ts +216 -0
  26. package/dist/auth/account_action_specs.d.ts.map +1 -0
  27. package/dist/auth/account_action_specs.js +159 -0
  28. package/dist/auth/account_actions.d.ts +51 -0
  29. package/dist/auth/account_actions.d.ts.map +1 -0
  30. package/dist/auth/account_actions.js +119 -0
  31. package/dist/auth/account_queries.d.ts +6 -2
  32. package/dist/auth/account_queries.d.ts.map +1 -1
  33. package/dist/auth/account_queries.js +40 -4
  34. package/dist/auth/account_routes.d.ts +94 -16
  35. package/dist/auth/account_routes.d.ts.map +1 -1
  36. package/dist/auth/account_routes.js +108 -180
  37. package/dist/auth/account_schema.d.ts +85 -30
  38. package/dist/auth/account_schema.d.ts.map +1 -1
  39. package/dist/auth/account_schema.js +40 -8
  40. package/dist/auth/admin_action_specs.d.ts +674 -0
  41. package/dist/auth/admin_action_specs.d.ts.map +1 -0
  42. package/dist/auth/admin_action_specs.js +287 -0
  43. package/dist/auth/admin_actions.d.ts +69 -0
  44. package/dist/auth/admin_actions.d.ts.map +1 -0
  45. package/dist/auth/admin_actions.js +256 -0
  46. package/dist/auth/admin_rpc_actions.d.ts +49 -0
  47. package/dist/auth/admin_rpc_actions.d.ts.map +1 -0
  48. package/dist/auth/admin_rpc_actions.js +32 -0
  49. package/dist/auth/api_token.d.ts +10 -0
  50. package/dist/auth/api_token.d.ts.map +1 -1
  51. package/dist/auth/api_token.js +9 -0
  52. package/dist/auth/api_token_queries.d.ts +3 -3
  53. package/dist/auth/api_token_queries.js +3 -3
  54. package/dist/auth/app_settings_schema.d.ts +4 -3
  55. package/dist/auth/app_settings_schema.d.ts.map +1 -1
  56. package/dist/auth/app_settings_schema.js +2 -1
  57. package/dist/auth/audit_log_routes.d.ts +14 -6
  58. package/dist/auth/audit_log_routes.d.ts.map +1 -1
  59. package/dist/auth/audit_log_routes.js +22 -79
  60. package/dist/auth/audit_log_schema.d.ts +100 -29
  61. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  62. package/dist/auth/audit_log_schema.js +83 -11
  63. package/dist/auth/bootstrap_routes.d.ts +14 -0
  64. package/dist/auth/bootstrap_routes.d.ts.map +1 -1
  65. package/dist/auth/bootstrap_routes.js +10 -3
  66. package/dist/auth/cleanup.d.ts +63 -0
  67. package/dist/auth/cleanup.d.ts.map +1 -0
  68. package/dist/auth/cleanup.js +80 -0
  69. package/dist/auth/invite_schema.d.ts +11 -10
  70. package/dist/auth/invite_schema.d.ts.map +1 -1
  71. package/dist/auth/invite_schema.js +4 -3
  72. package/dist/auth/migrations.d.ts +6 -0
  73. package/dist/auth/migrations.d.ts.map +1 -1
  74. package/dist/auth/migrations.js +28 -0
  75. package/dist/auth/permit_offer_action_specs.d.ts +364 -0
  76. package/dist/auth/permit_offer_action_specs.d.ts.map +1 -0
  77. package/dist/auth/permit_offer_action_specs.js +216 -0
  78. package/dist/auth/permit_offer_actions.d.ts +96 -0
  79. package/dist/auth/permit_offer_actions.d.ts.map +1 -0
  80. package/dist/auth/permit_offer_actions.js +428 -0
  81. package/dist/auth/permit_offer_notifications.d.ts +361 -0
  82. package/dist/auth/permit_offer_notifications.d.ts.map +1 -0
  83. package/dist/auth/permit_offer_notifications.js +179 -0
  84. package/dist/auth/permit_offer_queries.d.ts +165 -0
  85. package/dist/auth/permit_offer_queries.d.ts.map +1 -0
  86. package/dist/auth/permit_offer_queries.js +390 -0
  87. package/dist/auth/permit_offer_schema.d.ts +103 -0
  88. package/dist/auth/permit_offer_schema.d.ts.map +1 -0
  89. package/dist/auth/permit_offer_schema.js +142 -0
  90. package/dist/auth/permit_queries.d.ts +77 -14
  91. package/dist/auth/permit_queries.d.ts.map +1 -1
  92. package/dist/auth/permit_queries.js +119 -24
  93. package/dist/auth/session_queries.d.ts +4 -2
  94. package/dist/auth/session_queries.d.ts.map +1 -1
  95. package/dist/auth/session_queries.js +4 -2
  96. package/dist/auth/signup_routes.d.ts +13 -0
  97. package/dist/auth/signup_routes.d.ts.map +1 -1
  98. package/dist/auth/signup_routes.js +14 -7
  99. package/dist/http/CLAUDE.md +584 -0
  100. package/dist/http/pending_effects.d.ts +29 -0
  101. package/dist/http/pending_effects.d.ts.map +1 -0
  102. package/dist/http/pending_effects.js +31 -0
  103. package/dist/http/route_spec.d.ts.map +1 -1
  104. package/dist/http/route_spec.js +4 -3
  105. package/dist/rate_limiter.d.ts +30 -0
  106. package/dist/rate_limiter.d.ts.map +1 -1
  107. package/dist/rate_limiter.js +25 -2
  108. package/dist/realtime/sse_auth_guard.d.ts +2 -0
  109. package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
  110. package/dist/realtime/sse_auth_guard.js +5 -3
  111. package/dist/server/app_server.d.ts +13 -2
  112. package/dist/server/app_server.d.ts.map +1 -1
  113. package/dist/server/app_server.js +12 -1
  114. package/dist/testing/CLAUDE.md +668 -1
  115. package/dist/testing/admin_integration.d.ts +10 -7
  116. package/dist/testing/admin_integration.d.ts.map +1 -1
  117. package/dist/testing/admin_integration.js +382 -482
  118. package/dist/testing/app_server.d.ts +7 -6
  119. package/dist/testing/app_server.d.ts.map +1 -1
  120. package/dist/testing/attack_surface.d.ts +9 -3
  121. package/dist/testing/attack_surface.d.ts.map +1 -1
  122. package/dist/testing/attack_surface.js +4 -4
  123. package/dist/testing/audit_completeness.d.ts +11 -0
  124. package/dist/testing/audit_completeness.d.ts.map +1 -1
  125. package/dist/testing/audit_completeness.js +169 -134
  126. package/dist/testing/auth_apps.d.ts.map +1 -1
  127. package/dist/testing/auth_apps.js +4 -33
  128. package/dist/testing/db.d.ts +1 -1
  129. package/dist/testing/db.d.ts.map +1 -1
  130. package/dist/testing/db.js +2 -0
  131. package/dist/testing/entities.d.ts +35 -13
  132. package/dist/testing/entities.d.ts.map +1 -1
  133. package/dist/testing/entities.js +17 -0
  134. package/dist/testing/integration.d.ts +10 -0
  135. package/dist/testing/integration.d.ts.map +1 -1
  136. package/dist/testing/integration.js +352 -340
  137. package/dist/testing/integration_helpers.d.ts +16 -5
  138. package/dist/testing/integration_helpers.d.ts.map +1 -1
  139. package/dist/testing/integration_helpers.js +24 -4
  140. package/dist/testing/rate_limiting.d.ts +7 -0
  141. package/dist/testing/rate_limiting.d.ts.map +1 -1
  142. package/dist/testing/rate_limiting.js +41 -10
  143. package/dist/testing/rpc_helpers.d.ts +153 -1
  144. package/dist/testing/rpc_helpers.d.ts.map +1 -1
  145. package/dist/testing/rpc_helpers.js +184 -8
  146. package/dist/testing/sse_round_trip.d.ts +8 -0
  147. package/dist/testing/sse_round_trip.d.ts.map +1 -1
  148. package/dist/testing/sse_round_trip.js +10 -3
  149. package/dist/testing/standard.d.ts +9 -1
  150. package/dist/testing/standard.d.ts.map +1 -1
  151. package/dist/testing/standard.js +6 -2
  152. package/dist/testing/stubs.d.ts +10 -2
  153. package/dist/testing/stubs.d.ts.map +1 -1
  154. package/dist/testing/stubs.js +17 -2
  155. package/dist/testing/surface_invariants.d.ts +7 -3
  156. package/dist/testing/surface_invariants.d.ts.map +1 -1
  157. package/dist/testing/surface_invariants.js +5 -4
  158. package/dist/testing/ws_round_trip.d.ts.map +1 -1
  159. package/dist/testing/ws_round_trip.js +9 -38
  160. package/dist/ui/AccountSessions.svelte +8 -4
  161. package/dist/ui/AccountSessions.svelte.d.ts.map +1 -1
  162. package/dist/ui/AdminAccounts.svelte +61 -33
  163. package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -1
  164. package/dist/ui/AdminAuditLog.svelte +3 -2
  165. package/dist/ui/AdminAuditLog.svelte.d.ts.map +1 -1
  166. package/dist/ui/AdminInvites.svelte +3 -2
  167. package/dist/ui/AdminInvites.svelte.d.ts.map +1 -1
  168. package/dist/ui/AdminOverview.svelte +14 -9
  169. package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
  170. package/dist/ui/AdminPermitHistory.svelte +3 -2
  171. package/dist/ui/AdminPermitHistory.svelte.d.ts.map +1 -1
  172. package/dist/ui/AdminSessions.svelte +29 -25
  173. package/dist/ui/AdminSessions.svelte.d.ts.map +1 -1
  174. package/dist/ui/CLAUDE.md +363 -0
  175. package/dist/ui/OpenSignupToggle.svelte +6 -3
  176. package/dist/ui/OpenSignupToggle.svelte.d.ts.map +1 -1
  177. package/dist/ui/PermitOfferForm.svelte +141 -0
  178. package/dist/ui/PermitOfferForm.svelte.d.ts +14 -0
  179. package/dist/ui/PermitOfferForm.svelte.d.ts.map +1 -0
  180. package/dist/ui/PermitOfferHistory.svelte +109 -0
  181. package/dist/ui/PermitOfferHistory.svelte.d.ts +11 -0
  182. package/dist/ui/PermitOfferHistory.svelte.d.ts.map +1 -0
  183. package/dist/ui/PermitOfferInbox.svelte +121 -0
  184. package/dist/ui/PermitOfferInbox.svelte.d.ts +12 -0
  185. package/dist/ui/PermitOfferInbox.svelte.d.ts.map +1 -0
  186. package/dist/ui/account_sessions_state.svelte.d.ts +53 -3
  187. package/dist/ui/account_sessions_state.svelte.d.ts.map +1 -1
  188. package/dist/ui/account_sessions_state.svelte.js +39 -16
  189. package/dist/ui/admin_accounts_state.svelte.d.ts +118 -2
  190. package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
  191. package/dist/ui/admin_accounts_state.svelte.js +99 -23
  192. package/dist/ui/admin_invites_state.svelte.d.ts +47 -1
  193. package/dist/ui/admin_invites_state.svelte.d.ts.map +1 -1
  194. package/dist/ui/admin_invites_state.svelte.js +38 -26
  195. package/dist/ui/admin_rpc_adapters.d.ts +94 -0
  196. package/dist/ui/admin_rpc_adapters.d.ts.map +1 -0
  197. package/dist/ui/admin_rpc_adapters.js +100 -0
  198. package/dist/ui/admin_sessions_state.svelte.d.ts +26 -0
  199. package/dist/ui/admin_sessions_state.svelte.d.ts.map +1 -1
  200. package/dist/ui/admin_sessions_state.svelte.js +35 -21
  201. package/dist/ui/app_settings_state.svelte.d.ts +39 -0
  202. package/dist/ui/app_settings_state.svelte.d.ts.map +1 -1
  203. package/dist/ui/app_settings_state.svelte.js +34 -18
  204. package/dist/ui/audit_log_state.svelte.d.ts +40 -3
  205. package/dist/ui/audit_log_state.svelte.d.ts.map +1 -1
  206. package/dist/ui/audit_log_state.svelte.js +36 -42
  207. package/dist/ui/auth_state.svelte.d.ts +4 -3
  208. package/dist/ui/auth_state.svelte.d.ts.map +1 -1
  209. package/dist/ui/auth_state.svelte.js +4 -1
  210. package/dist/ui/permit_offers_state.svelte.d.ts +125 -0
  211. package/dist/ui/permit_offers_state.svelte.d.ts.map +1 -0
  212. package/dist/ui/permit_offers_state.svelte.js +197 -0
  213. package/package.json +3 -3
  214. package/dist/auth/admin_routes.d.ts +0 -29
  215. package/dist/auth/admin_routes.d.ts.map +0 -1
  216. package/dist/auth/admin_routes.js +0 -226
  217. package/dist/auth/app_settings_routes.d.ts +0 -27
  218. package/dist/auth/app_settings_routes.d.ts.map +0 -1
  219. package/dist/auth/app_settings_routes.js +0 -66
  220. package/dist/auth/invite_routes.d.ts +0 -18
  221. package/dist/auth/invite_routes.d.ts.map +0 -1
  222. package/dist/auth/invite_routes.js +0 -129
@@ -4,55 +4,104 @@
4
4
  * @module
5
5
  */
6
6
  import { SvelteSet } from 'svelte/reactivity';
7
+ import { create_context } from '@fuzdev/fuz_ui/context_helpers.js';
7
8
  import { Loadable } from './loadable.svelte.js';
8
- import { parse_response_error, ui_fetch } from './ui_fetch.js';
9
+ /**
10
+ * Svelte context carrying the reactive `AdminAccountsRpc` accessor. The
11
+ * provisioner (typically the admin route shell) calls `set(() => rpc)`;
12
+ * consumers read with `const get_rpc = admin_accounts_rpc_context.get();`
13
+ * and either pass the accessor straight to `AdminAccountsState`/
14
+ * `AdminSessionsState` or wrap it with `const rpc = $derived(get_rpc());`
15
+ * for direct RPC calls. Unset context falls back to `() => null` so
16
+ * components mounted without a provisioner surface the usual "rpc adapter
17
+ * not wired" path.
18
+ */
19
+ export const admin_accounts_rpc_context = create_context(() => () => null);
9
20
  export class AdminAccountsState extends Loadable {
21
+ #get_rpc;
10
22
  accounts = $state.raw([]);
11
23
  grantable_roles = $state.raw([]);
12
24
  granting_keys = new SvelteSet();
13
25
  revoking_ids = new SvelteSet();
26
+ retracting_ids = new SvelteSet();
14
27
  account_count = $derived(this.accounts.length);
28
+ constructor(options) {
29
+ super();
30
+ this.#get_rpc = options?.get_rpc ?? (() => null);
31
+ }
32
+ /**
33
+ * True when an RPC adapter is wired. UI uses this to gate all controls
34
+ * — fetch, grant, revoke, retract all flow through the same adapter.
35
+ */
36
+ get has_rpc() {
37
+ return this.#get_rpc() !== null;
38
+ }
15
39
  async fetch() {
40
+ const rpc = this.#get_rpc();
41
+ if (!rpc) {
42
+ this.error = 'rpc adapter not wired';
43
+ return;
44
+ }
16
45
  await this.run(async () => {
17
- const response = await ui_fetch('/api/admin/accounts');
18
- if (!response.ok) {
19
- throw new Error(await parse_response_error(response, 'Failed to fetch accounts'));
20
- }
21
- const data = await response.json();
22
- this.accounts = data.accounts ?? [];
23
- this.grantable_roles = data.grantable_roles ?? [];
46
+ const { accounts, grantable_roles } = await rpc.list_accounts();
47
+ this.accounts = accounts;
48
+ this.grantable_roles = grantable_roles;
24
49
  });
25
50
  }
51
+ /**
52
+ * Offer the role to the recipient via the `permit_offer_create` RPC.
53
+ * Server returns the pending offer; the recipient must accept before
54
+ * the permit materializes. Returns the offer payload on success so
55
+ * callers can drive follow-up UX (e.g. seed `PermitOffersState.outgoing`).
56
+ *
57
+ * A re-offer from the same admin to the same `(account, role)`
58
+ * refreshes the existing pending row — the returned offer id is stable
59
+ * across those calls.
60
+ *
61
+ * No-op when the rpc adapter is absent; `error` is set to a descriptive
62
+ * message so the UI surfaces the misconfiguration.
63
+ */
26
64
  async grant_permit(account_id, role) {
65
+ const rpc = this.#get_rpc();
66
+ if (!rpc) {
67
+ this.error = 'rpc adapter not wired';
68
+ return undefined;
69
+ }
27
70
  const key = `${account_id}:${role}`;
28
71
  this.granting_keys.add(key);
29
72
  try {
30
- const response = await ui_fetch(`/api/admin/accounts/${account_id}/permits/grant`, {
31
- method: 'POST',
32
- headers: { 'Content-Type': 'application/json' },
33
- body: JSON.stringify({ role }),
34
- });
35
- if (!response.ok) {
36
- this.error = await parse_response_error(response);
37
- return;
38
- }
73
+ const { offer } = await rpc.grant_permit({ to_account_id: account_id, role });
74
+ this.error = null;
39
75
  await this.fetch();
76
+ return offer;
40
77
  }
41
78
  catch (e) {
42
79
  this.error = e instanceof Error ? e.message : 'Failed to grant permit';
80
+ return undefined;
43
81
  }
44
82
  finally {
45
83
  this.granting_keys.delete(key);
46
84
  }
47
85
  }
48
- async revoke_permit(account_id, permit_id) {
86
+ /**
87
+ * Revoke an active permit via the `permit_revoke` RPC.
88
+ *
89
+ * `actor_id` is the natural key — permits are actor-scoped, and the
90
+ * admin UI reads `row.actor.id` straight from the listing, so the state
91
+ * class takes it directly rather than deriving it from `account_id`.
92
+ * The optional `reason` is stamped on `permit.revoked_reason` and
93
+ * surfaced on the revokee's WS notification.
94
+ */
95
+ async revoke_permit(actor_id, permit_id, reason) {
96
+ const rpc = this.#get_rpc();
97
+ if (!rpc) {
98
+ this.error = 'rpc adapter not wired';
99
+ return;
100
+ }
49
101
  this.revoking_ids.add(permit_id);
50
102
  try {
51
- const response = await ui_fetch(`/api/admin/accounts/${account_id}/permits/${permit_id}/revoke`, { method: 'POST' });
52
- if (!response.ok) {
53
- this.error = await parse_response_error(response);
54
- return;
55
- }
103
+ await rpc.revoke_permit({ actor_id, permit_id, reason: reason ?? null });
104
+ this.error = null;
56
105
  await this.fetch();
57
106
  }
58
107
  catch (e) {
@@ -62,4 +111,31 @@ export class AdminAccountsState extends Loadable {
62
111
  this.revoking_ids.delete(permit_id);
63
112
  }
64
113
  }
114
+ /**
115
+ * Retract a pending offer the admin issued via the `permit_offer_retract`
116
+ * RPC. The action handles auth, audit, and the
117
+ * `permit_offer_retracted` WS notification.
118
+ *
119
+ * After success, refetches the listing so `pending_offers` drops the
120
+ * row and the "+ {role}" button un-hides.
121
+ */
122
+ async retract_offer(offer_id) {
123
+ const rpc = this.#get_rpc();
124
+ if (!rpc) {
125
+ this.error = 'rpc adapter not wired';
126
+ return;
127
+ }
128
+ this.retracting_ids.add(offer_id);
129
+ try {
130
+ await rpc.retract_offer(offer_id);
131
+ this.error = null;
132
+ await this.fetch();
133
+ }
134
+ catch (e) {
135
+ this.error = e instanceof Error ? e.message : 'Failed to retract offer';
136
+ }
137
+ finally {
138
+ this.retracting_ids.delete(offer_id);
139
+ }
140
+ }
65
141
  }
@@ -1,17 +1,63 @@
1
1
  /**
2
2
  * Reactive state for admin invite management.
3
3
  *
4
+ * Flows every operation through an injected `AdminInvitesRpc` adapter — the
5
+ * class stays decoupled from the concrete RPC client so tests can inject
6
+ * plain-function stubs. Mirrors `AdminAccountsRpc` / `AuditLogRpc`.
7
+ *
4
8
  * @module
5
9
  */
6
10
  import { SvelteSet } from 'svelte/reactivity';
7
11
  import { Loadable } from './loadable.svelte.js';
8
- import type { InviteWithUsernamesJson } from '../auth/invite_schema.js';
12
+ import type { InviteJson, InviteWithUsernamesJson } from '../auth/invite_schema.js';
13
+ /**
14
+ * Narrow RPC surface consumed by `AdminInvitesState`. Consumers adapt their
15
+ * typed RPC client to this shape. `error.data.reason` on thrown errors
16
+ * carries the `ERROR_INVITE_*` constant — handled by the caller when
17
+ * user-friendly messages are needed.
18
+ */
19
+ export interface AdminInvitesRpc {
20
+ list: () => Promise<{
21
+ invites: Array<InviteWithUsernamesJson>;
22
+ }>;
23
+ create: (params: {
24
+ email?: string | null;
25
+ username?: string | null;
26
+ }) => Promise<{
27
+ ok: true;
28
+ invite: InviteJson;
29
+ }>;
30
+ delete: (params: {
31
+ invite_id: string;
32
+ }) => Promise<{
33
+ ok: true;
34
+ }>;
35
+ }
36
+ /**
37
+ * Svelte context carrying the reactive `AdminInvitesRpc` accessor. Mirrors
38
+ * `admin_accounts_rpc_context`. Unset context falls back to `() => null`.
39
+ */
40
+ export declare const admin_invites_rpc_context: {
41
+ get: () => () => AdminInvitesRpc | null;
42
+ set: (value?: (() => AdminInvitesRpc | null) | undefined) => () => AdminInvitesRpc | null;
43
+ };
44
+ export interface AdminInvitesStateOptions {
45
+ /**
46
+ * Reactive accessor for the RPC adapter. `null` disables all operations
47
+ * (the state reports a descriptive error when mutations/fetches fire).
48
+ */
49
+ get_rpc?: () => AdminInvitesRpc | null;
50
+ }
9
51
  export declare class AdminInvitesState extends Loadable {
52
+ #private;
10
53
  invites: Array<InviteWithUsernamesJson>;
11
54
  creating: boolean;
12
55
  readonly deleting_ids: SvelteSet<string>;
13
56
  readonly invite_count: number;
14
57
  readonly unclaimed_count: number;
58
+ constructor(options?: AdminInvitesStateOptions);
59
+ /** True when an RPC adapter is wired. All ops require it. */
60
+ get has_rpc(): boolean;
15
61
  fetch(): Promise<void>;
16
62
  create_invite(email?: string, username?: string): Promise<boolean>;
17
63
  delete_invite(id: string): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"admin_invites_state.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/admin_invites_state.svelte.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAC,SAAS,EAAC,MAAM,mBAAmB,CAAC;AAE5C,OAAO,EAAC,QAAQ,EAAC,MAAM,sBAAsB,CAAC;AAE9C,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,0BAA0B,CAAC;AAEtE,qBAAa,iBAAkB,SAAQ,QAAQ;IAC9C,OAAO,EAAE,KAAK,CAAC,uBAAuB,CAAC,CAAkB;IACzD,QAAQ,UAAqB;IAC7B,QAAQ,CAAC,YAAY,EAAE,SAAS,CAAC,MAAM,CAAC,CAAmB;IAE3D,QAAQ,CAAC,YAAY,SAAiC;IACtD,QAAQ,CAAC,eAAe,SAA8D;IAEhF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAWtB,aAAa,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA2BlE,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAe9C"}
1
+ {"version":3,"file":"admin_invites_state.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/admin_invites_state.svelte.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAC,SAAS,EAAC,MAAM,mBAAmB,CAAC;AAG5C,OAAO,EAAC,QAAQ,EAAC,MAAM,sBAAsB,CAAC;AAC9C,OAAO,KAAK,EAAC,UAAU,EAAE,uBAAuB,EAAC,MAAM,0BAA0B,CAAC;AAElF;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,OAAO,CAAC;QAAC,OAAO,EAAE,KAAK,CAAC,uBAAuB,CAAC,CAAA;KAAC,CAAC,CAAC;IAC/D,MAAM,EAAE,CAAC,MAAM,EAAE;QAChB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACzB,KAAK,OAAO,CAAC;QAAC,EAAE,EAAE,IAAI,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAC,CAAC,CAAC;IAC9C,MAAM,EAAE,CAAC,MAAM,EAAE;QAAC,SAAS,EAAE,MAAM,CAAA;KAAC,KAAK,OAAO,CAAC;QAAC,EAAE,EAAE,IAAI,CAAA;KAAC,CAAC,CAAC;CAC7D;AAED;;;GAGG;AACH,eAAO,MAAM,yBAAyB;qBAAwB,eAAe,GAAG,IAAI;yBAAtB,eAAe,GAAG,IAAI,wBAAtB,eAAe,GAAG,IAAI;CAEnF,CAAC;AAEF,MAAM,WAAW,wBAAwB;IACxC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,eAAe,GAAG,IAAI,CAAC;CACvC;AAED,qBAAa,iBAAkB,SAAQ,QAAQ;;IAG9C,OAAO,EAAE,KAAK,CAAC,uBAAuB,CAAC,CAAkB;IACzD,QAAQ,UAAqB;IAC7B,QAAQ,CAAC,YAAY,EAAE,SAAS,CAAC,MAAM,CAAC,CAAmB;IAE3D,QAAQ,CAAC,YAAY,SAAiC;IACtD,QAAQ,CAAC,eAAe,SAA8D;gBAE1E,OAAO,CAAC,EAAE,wBAAwB;IAK9C,6DAA6D;IAC7D,IAAI,OAAO,IAAI,OAAO,CAErB;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAYtB,aAAa,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAoBlE,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAgB9C"}
@@ -1,45 +1,56 @@
1
1
  /**
2
2
  * Reactive state for admin invite management.
3
3
  *
4
+ * Flows every operation through an injected `AdminInvitesRpc` adapter — the
5
+ * class stays decoupled from the concrete RPC client so tests can inject
6
+ * plain-function stubs. Mirrors `AdminAccountsRpc` / `AuditLogRpc`.
7
+ *
4
8
  * @module
5
9
  */
6
10
  import { SvelteSet } from 'svelte/reactivity';
11
+ import { create_context } from '@fuzdev/fuz_ui/context_helpers.js';
7
12
  import { Loadable } from './loadable.svelte.js';
8
- import { parse_response_error, ui_fetch } from './ui_fetch.js';
13
+ /**
14
+ * Svelte context carrying the reactive `AdminInvitesRpc` accessor. Mirrors
15
+ * `admin_accounts_rpc_context`. Unset context falls back to `() => null`.
16
+ */
17
+ export const admin_invites_rpc_context = create_context(() => () => null);
9
18
  export class AdminInvitesState extends Loadable {
19
+ #get_rpc;
10
20
  invites = $state.raw([]);
11
21
  creating = $state.raw(false);
12
22
  deleting_ids = new SvelteSet();
13
23
  invite_count = $derived(this.invites.length);
14
24
  unclaimed_count = $derived(this.invites.filter((i) => !i.claimed_at).length);
25
+ constructor(options) {
26
+ super();
27
+ this.#get_rpc = options?.get_rpc ?? (() => null);
28
+ }
29
+ /** True when an RPC adapter is wired. All ops require it. */
30
+ get has_rpc() {
31
+ return this.#get_rpc() !== null;
32
+ }
15
33
  async fetch() {
34
+ const rpc = this.#get_rpc();
35
+ if (!rpc) {
36
+ this.error = 'rpc adapter not wired';
37
+ return;
38
+ }
16
39
  await this.run(async () => {
17
- const response = await ui_fetch('/api/admin/invites');
18
- if (!response.ok) {
19
- throw new Error(await parse_response_error(response, 'Failed to fetch invites'));
20
- }
21
- const data = await response.json();
22
- this.invites = data.invites ?? [];
40
+ const { invites } = await rpc.list();
41
+ this.invites = invites;
23
42
  });
24
43
  }
25
44
  async create_invite(email, username) {
45
+ const rpc = this.#get_rpc();
46
+ if (!rpc) {
47
+ this.error = 'rpc adapter not wired';
48
+ return false;
49
+ }
26
50
  this.creating = true;
27
51
  this.error = null;
28
52
  try {
29
- const body = {};
30
- if (email)
31
- body.email = email;
32
- if (username)
33
- body.username = username;
34
- const response = await ui_fetch('/api/admin/invites', {
35
- method: 'POST',
36
- headers: { 'Content-Type': 'application/json' },
37
- body: JSON.stringify(body),
38
- });
39
- if (!response.ok) {
40
- this.error = await parse_response_error(response);
41
- return false;
42
- }
53
+ await rpc.create({ email: email ?? null, username: username ?? null });
43
54
  await this.fetch();
44
55
  return true;
45
56
  }
@@ -52,13 +63,14 @@ export class AdminInvitesState extends Loadable {
52
63
  }
53
64
  }
54
65
  async delete_invite(id) {
66
+ const rpc = this.#get_rpc();
67
+ if (!rpc) {
68
+ this.error = 'rpc adapter not wired';
69
+ return;
70
+ }
55
71
  this.deleting_ids.add(id);
56
72
  try {
57
- const response = await ui_fetch(`/api/admin/invites/${id}`, { method: 'DELETE' });
58
- if (!response.ok) {
59
- this.error = await parse_response_error(response);
60
- return;
61
- }
73
+ await rpc.delete({ invite_id: id });
62
74
  await this.fetch();
63
75
  }
64
76
  catch (e) {
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Admin RPC adapter helpers for consumer UIs.
3
+ *
4
+ * Bridges a typed `rpc_call`-shaped function to the four narrow admin RPC
5
+ * interfaces the state classes consume — `AdminAccountsRpc`,
6
+ * `AdminInvitesRpc`, `AuditLogRpc`, `AppSettingsRpc`. Two calls at the
7
+ * admin shell layout wire everything:
8
+ *
9
+ * ```ts
10
+ * import {create_throwing_rpc_call} from '@fuzdev/fuz_app/actions/rpc_client.js';
11
+ * const rpc_call = create_throwing_rpc_call(api);
12
+ * provide_admin_rpc_contexts(create_admin_rpc_adapters(rpc_call));
13
+ * ```
14
+ *
15
+ * `create_throwing_rpc_call` unwraps every `Result` to throw on error, spreading
16
+ * the JSON-RPC `{code, message, data?}` onto the thrown `Error` so form
17
+ * components (e.g. `PermitOfferForm.svelte`) can match on
18
+ * `error.data?.reason` via `ERROR_OFFER_*` constants — optional chaining is
19
+ * required because JSON-RPC `data` is spec-level optional. Consumers that
20
+ * need a custom unwrap strategy can supply any function matching
21
+ * `AdminRpcCall` directly instead.
22
+ *
23
+ * No `.svelte.ts` suffix — this module holds no reactive state, only
24
+ * method-name mappings.
25
+ *
26
+ * @module
27
+ */
28
+ import type { ThrowingRpcCall } from '../actions/rpc_client.js';
29
+ import { type AdminAccountsRpc } from './admin_accounts_state.svelte.js';
30
+ import { type AdminInvitesRpc } from './admin_invites_state.svelte.js';
31
+ import { type AuditLogRpc } from './audit_log_state.svelte.js';
32
+ import { type AppSettingsRpc } from './app_settings_state.svelte.js';
33
+ /**
34
+ * Function-shaped contract for dispatching an RPC call by method name.
35
+ *
36
+ * Alias of `ThrowingRpcCall` — kept as a domain-specific name so reads of
37
+ * the admin UI code stay self-contained. Receives the method string and
38
+ * input, returns a Promise of the output — or throws on error carrying the
39
+ * JSON-RPC `{code, message, data?}` shape.
40
+ *
41
+ * The generic is load-bearing: contextual typing lets the narrow
42
+ * `Admin*Rpc` return types flow into `TOutput` so adapter methods typecheck
43
+ * without explicit casts.
44
+ */
45
+ export type AdminRpcCall = ThrowingRpcCall;
46
+ /** The four admin RPC adapters assembled from a shared `rpc_call`. */
47
+ export interface AdminRpcAdapters {
48
+ admin_accounts: AdminAccountsRpc;
49
+ admin_invites: AdminInvitesRpc;
50
+ audit_log: AuditLogRpc;
51
+ app_settings: AppSettingsRpc;
52
+ }
53
+ /**
54
+ * Build the four admin RPC adapters from a single typed `rpc_call`.
55
+ *
56
+ * Method-name mapping:
57
+ *
58
+ * | Narrow RPC method | Action spec method |
59
+ * | ----------------------------------- | ---------------------------- |
60
+ * | `admin_accounts.list_accounts` | `admin_account_list` |
61
+ * | `admin_accounts.list_sessions` | `admin_session_list` |
62
+ * | `admin_accounts.grant_permit` | `permit_offer_create` |
63
+ * | `admin_accounts.revoke_permit` | `permit_revoke` |
64
+ * | `admin_accounts.retract_offer` | `permit_offer_retract` |
65
+ * | `admin_accounts.session_revoke_all` | `admin_session_revoke_all` |
66
+ * | `admin_accounts.token_revoke_all` | `admin_token_revoke_all` |
67
+ * | `admin_invites.list` | `invite_list` |
68
+ * | `admin_invites.create` | `invite_create` |
69
+ * | `admin_invites.delete` | `invite_delete` |
70
+ * | `audit_log.list` | `audit_log_list` |
71
+ * | `audit_log.permit_history` | `audit_log_permit_history` |
72
+ * | `app_settings.get` | `app_settings_get` |
73
+ * | `app_settings.update` | `app_settings_update` |
74
+ *
75
+ * All four adapter factories call through the same `rpc_call` — consumers
76
+ * only construct one adapter closure (typically wrapping
77
+ * `create_rpc_client`'s Proxy + Result-unwrap) regardless of how many
78
+ * admin surfaces they mount.
79
+ */
80
+ export declare const create_admin_rpc_adapters: (rpc_call: AdminRpcCall) => AdminRpcAdapters;
81
+ /**
82
+ * Wire all four admin RPC contexts in a single call.
83
+ *
84
+ * Call once at the admin shell layout (e.g. `src/routes/admin/+layout.svelte`)
85
+ * with adapters built from `create_admin_rpc_adapters`. Every `Admin*.svelte`
86
+ * component that reads a context below this point sees the adapters.
87
+ *
88
+ * Each context accessor reads `adapters.{domain}` on every invocation, so
89
+ * mutating an adapter field on the same object propagates. Replacing the
90
+ * whole adapter set requires calling `provide_admin_rpc_contexts` again
91
+ * during init — in practice this is one-shot at layout mount.
92
+ */
93
+ export declare const provide_admin_rpc_contexts: (adapters: AdminRpcAdapters) => void;
94
+ //# sourceMappingURL=admin_rpc_adapters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin_rpc_adapters.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/admin_rpc_adapters.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAA6B,KAAK,gBAAgB,EAAC,MAAM,kCAAkC,CAAC;AACnG,OAAO,EAA4B,KAAK,eAAe,EAAC,MAAM,iCAAiC,CAAC;AAChG,OAAO,EAAwB,KAAK,WAAW,EAAC,MAAM,6BAA6B,CAAC;AACpF,OAAO,EAA2B,KAAK,cAAc,EAAC,MAAM,gCAAgC,CAAC;AAE7F;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,YAAY,GAAG,eAAe,CAAC;AAE3C,sEAAsE;AACtE,MAAM,WAAW,gBAAgB;IAChC,cAAc,EAAE,gBAAgB,CAAC;IACjC,aAAa,EAAE,eAAe,CAAC;IAC/B,SAAS,EAAE,WAAW,CAAC;IACvB,YAAY,EAAE,cAAc,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,yBAAyB,GAAI,UAAU,YAAY,KAAG,gBAuBjE,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,GAAI,UAAU,gBAAgB,KAAG,IAKvE,CAAC"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Admin RPC adapter helpers for consumer UIs.
3
+ *
4
+ * Bridges a typed `rpc_call`-shaped function to the four narrow admin RPC
5
+ * interfaces the state classes consume — `AdminAccountsRpc`,
6
+ * `AdminInvitesRpc`, `AuditLogRpc`, `AppSettingsRpc`. Two calls at the
7
+ * admin shell layout wire everything:
8
+ *
9
+ * ```ts
10
+ * import {create_throwing_rpc_call} from '@fuzdev/fuz_app/actions/rpc_client.js';
11
+ * const rpc_call = create_throwing_rpc_call(api);
12
+ * provide_admin_rpc_contexts(create_admin_rpc_adapters(rpc_call));
13
+ * ```
14
+ *
15
+ * `create_throwing_rpc_call` unwraps every `Result` to throw on error, spreading
16
+ * the JSON-RPC `{code, message, data?}` onto the thrown `Error` so form
17
+ * components (e.g. `PermitOfferForm.svelte`) can match on
18
+ * `error.data?.reason` via `ERROR_OFFER_*` constants — optional chaining is
19
+ * required because JSON-RPC `data` is spec-level optional. Consumers that
20
+ * need a custom unwrap strategy can supply any function matching
21
+ * `AdminRpcCall` directly instead.
22
+ *
23
+ * No `.svelte.ts` suffix — this module holds no reactive state, only
24
+ * method-name mappings.
25
+ *
26
+ * @module
27
+ */
28
+ import { admin_accounts_rpc_context } from './admin_accounts_state.svelte.js';
29
+ import { admin_invites_rpc_context } from './admin_invites_state.svelte.js';
30
+ import { audit_log_rpc_context } from './audit_log_state.svelte.js';
31
+ import { app_settings_rpc_context } from './app_settings_state.svelte.js';
32
+ /**
33
+ * Build the four admin RPC adapters from a single typed `rpc_call`.
34
+ *
35
+ * Method-name mapping:
36
+ *
37
+ * | Narrow RPC method | Action spec method |
38
+ * | ----------------------------------- | ---------------------------- |
39
+ * | `admin_accounts.list_accounts` | `admin_account_list` |
40
+ * | `admin_accounts.list_sessions` | `admin_session_list` |
41
+ * | `admin_accounts.grant_permit` | `permit_offer_create` |
42
+ * | `admin_accounts.revoke_permit` | `permit_revoke` |
43
+ * | `admin_accounts.retract_offer` | `permit_offer_retract` |
44
+ * | `admin_accounts.session_revoke_all` | `admin_session_revoke_all` |
45
+ * | `admin_accounts.token_revoke_all` | `admin_token_revoke_all` |
46
+ * | `admin_invites.list` | `invite_list` |
47
+ * | `admin_invites.create` | `invite_create` |
48
+ * | `admin_invites.delete` | `invite_delete` |
49
+ * | `audit_log.list` | `audit_log_list` |
50
+ * | `audit_log.permit_history` | `audit_log_permit_history` |
51
+ * | `app_settings.get` | `app_settings_get` |
52
+ * | `app_settings.update` | `app_settings_update` |
53
+ *
54
+ * All four adapter factories call through the same `rpc_call` — consumers
55
+ * only construct one adapter closure (typically wrapping
56
+ * `create_rpc_client`'s Proxy + Result-unwrap) regardless of how many
57
+ * admin surfaces they mount.
58
+ */
59
+ export const create_admin_rpc_adapters = (rpc_call) => ({
60
+ admin_accounts: {
61
+ list_accounts: () => rpc_call('admin_account_list', null),
62
+ list_sessions: () => rpc_call('admin_session_list', null),
63
+ grant_permit: (params) => rpc_call('permit_offer_create', params),
64
+ revoke_permit: (params) => rpc_call('permit_revoke', params),
65
+ retract_offer: (offer_id) => rpc_call('permit_offer_retract', { offer_id }),
66
+ session_revoke_all: (params) => rpc_call('admin_session_revoke_all', params),
67
+ token_revoke_all: (params) => rpc_call('admin_token_revoke_all', params),
68
+ },
69
+ admin_invites: {
70
+ list: () => rpc_call('invite_list', null),
71
+ create: (params) => rpc_call('invite_create', params),
72
+ delete: (params) => rpc_call('invite_delete', params),
73
+ },
74
+ audit_log: {
75
+ list: (options) => rpc_call('audit_log_list', options ?? {}),
76
+ permit_history: (params) => rpc_call('audit_log_permit_history', params ?? {}),
77
+ },
78
+ app_settings: {
79
+ get: () => rpc_call('app_settings_get', null),
80
+ update: (params) => rpc_call('app_settings_update', params),
81
+ },
82
+ });
83
+ /**
84
+ * Wire all four admin RPC contexts in a single call.
85
+ *
86
+ * Call once at the admin shell layout (e.g. `src/routes/admin/+layout.svelte`)
87
+ * with adapters built from `create_admin_rpc_adapters`. Every `Admin*.svelte`
88
+ * component that reads a context below this point sees the adapters.
89
+ *
90
+ * Each context accessor reads `adapters.{domain}` on every invocation, so
91
+ * mutating an adapter field on the same object propagates. Replacing the
92
+ * whole adapter set requires calling `provide_admin_rpc_contexts` again
93
+ * during init — in practice this is one-shot at layout mount.
94
+ */
95
+ export const provide_admin_rpc_contexts = (adapters) => {
96
+ admin_accounts_rpc_context.set(() => adapters.admin_accounts);
97
+ admin_invites_rpc_context.set(() => adapters.admin_invites);
98
+ audit_log_rpc_context.set(() => adapters.audit_log);
99
+ app_settings_rpc_context.set(() => adapters.app_settings);
100
+ };
@@ -1,16 +1,42 @@
1
1
  /**
2
2
  * Reactive state for admin session overview.
3
3
  *
4
+ * Both the listing and the two revoke-all mutations flow through the shared
5
+ * `AdminAccountsRpc` adapter (`list_sessions`, `session_revoke_all`,
6
+ * `token_revoke_all`). The former REST `GET /api/admin/sessions` route moved
7
+ * to the `admin_session_list` RPC method in the 2026-04-23 migration.
8
+ *
4
9
  * @module
5
10
  */
6
11
  import { SvelteSet } from 'svelte/reactivity';
7
12
  import { Loadable } from './loadable.svelte.js';
13
+ import type { AdminAccountsRpc } from './admin_accounts_state.svelte.js';
8
14
  import type { AdminSessionJson } from '../auth/audit_log_schema.js';
15
+ /**
16
+ * Options for `AdminSessionsState`.
17
+ *
18
+ * The RPC adapter drives every operation (listing + the two revoke-all
19
+ * mutations). Without it, `fetch` and the revoke controls no-op with
20
+ * `'rpc adapter not wired'` on `error`.
21
+ */
22
+ export interface AdminSessionsStateOptions {
23
+ /**
24
+ * Reactive accessor for the RPC adapter; returns `null` when unwired.
25
+ * Mirrors `AdminAccountsStateOptions.get_rpc` so a single adapter
26
+ * instance backs both states without tripping Svelte's
27
+ * `state_referenced_locally` warning.
28
+ */
29
+ get_rpc?: () => AdminAccountsRpc | null;
30
+ }
9
31
  export declare class AdminSessionsState extends Loadable {
32
+ #private;
10
33
  sessions: Array<AdminSessionJson>;
11
34
  readonly revoking_account_ids: SvelteSet<string>;
12
35
  readonly revoking_token_account_ids: SvelteSet<string>;
13
36
  readonly active_count: number;
37
+ constructor(options?: AdminSessionsStateOptions);
38
+ /** True when an RPC adapter is wired. `fetch` and the revoke controls no-op without it. */
39
+ get has_rpc(): boolean;
14
40
  fetch(): Promise<void>;
15
41
  revoke_all_for_account(account_id: string): Promise<void>;
16
42
  revoke_all_tokens_for_account(account_id: string): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"admin_sessions_state.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/admin_sessions_state.svelte.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAC,SAAS,EAAC,MAAM,mBAAmB,CAAC;AAE5C,OAAO,EAAC,QAAQ,EAAC,MAAM,sBAAsB,CAAC;AAE9C,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,6BAA6B,CAAC;AAElE,qBAAa,kBAAmB,SAAQ,QAAQ;IAC/C,QAAQ,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAkB;IACnD,QAAQ,CAAC,oBAAoB,EAAE,SAAS,CAAC,MAAM,CAAC,CAAmB;IACnE,QAAQ,CAAC,0BAA0B,EAAE,SAAS,CAAC,MAAM,CAAC,CAAmB;IAEzE,QAAQ,CAAC,YAAY,SAAkC;IAEjD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAWtB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBzD,6BAA6B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAiBtE"}
1
+ {"version":3,"file":"admin_sessions_state.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/admin_sessions_state.svelte.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAC,SAAS,EAAC,MAAM,mBAAmB,CAAC;AAE5C,OAAO,EAAC,QAAQ,EAAC,MAAM,sBAAsB,CAAC;AAC9C,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,kCAAkC,CAAC;AACvE,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,6BAA6B,CAAC;AAElE;;;;;;GAMG;AACH,MAAM,WAAW,yBAAyB;IACzC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,gBAAgB,GAAG,IAAI,CAAC;CACxC;AAED,qBAAa,kBAAmB,SAAQ,QAAQ;;IAG/C,QAAQ,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAkB;IACnD,QAAQ,CAAC,oBAAoB,EAAE,SAAS,CAAC,MAAM,CAAC,CAAmB;IACnE,QAAQ,CAAC,0BAA0B,EAAE,SAAS,CAAC,MAAM,CAAC,CAAmB;IAEzE,QAAQ,CAAC,YAAY,SAAkC;gBAE3C,OAAO,CAAC,EAAE,yBAAyB;IAK/C,2FAA2F;IAC3F,IAAI,OAAO,IAAI,OAAO,CAErB;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAYtB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBzD,6BAA6B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAiBtE"}
@@ -1,36 +1,50 @@
1
1
  /**
2
2
  * Reactive state for admin session overview.
3
3
  *
4
+ * Both the listing and the two revoke-all mutations flow through the shared
5
+ * `AdminAccountsRpc` adapter (`list_sessions`, `session_revoke_all`,
6
+ * `token_revoke_all`). The former REST `GET /api/admin/sessions` route moved
7
+ * to the `admin_session_list` RPC method in the 2026-04-23 migration.
8
+ *
4
9
  * @module
5
10
  */
6
11
  import { SvelteSet } from 'svelte/reactivity';
7
12
  import { Loadable } from './loadable.svelte.js';
8
- import { parse_response_error, ui_fetch } from './ui_fetch.js';
9
13
  export class AdminSessionsState extends Loadable {
14
+ #get_rpc;
10
15
  sessions = $state.raw([]);
11
16
  revoking_account_ids = new SvelteSet();
12
17
  revoking_token_account_ids = new SvelteSet();
13
18
  active_count = $derived(this.sessions.length);
19
+ constructor(options) {
20
+ super();
21
+ this.#get_rpc = options?.get_rpc ?? (() => null);
22
+ }
23
+ /** True when an RPC adapter is wired. `fetch` and the revoke controls no-op without it. */
24
+ get has_rpc() {
25
+ return this.#get_rpc() !== null;
26
+ }
14
27
  async fetch() {
28
+ const rpc = this.#get_rpc();
29
+ if (!rpc) {
30
+ this.error = 'rpc adapter not wired';
31
+ return;
32
+ }
15
33
  await this.run(async () => {
16
- const response = await ui_fetch('/api/admin/sessions');
17
- if (!response.ok) {
18
- throw new Error(await parse_response_error(response, 'Failed to fetch sessions'));
19
- }
20
- const data = await response.json();
21
- this.sessions = data.sessions ?? [];
34
+ const { sessions } = await rpc.list_sessions();
35
+ this.sessions = sessions;
22
36
  });
23
37
  }
24
38
  async revoke_all_for_account(account_id) {
39
+ const rpc = this.#get_rpc();
40
+ if (!rpc) {
41
+ this.error = 'rpc adapter not wired';
42
+ return;
43
+ }
25
44
  this.revoking_account_ids.add(account_id);
26
45
  try {
27
- const response = await ui_fetch(`/api/admin/accounts/${account_id}/sessions/revoke-all`, {
28
- method: 'POST',
29
- });
30
- if (!response.ok) {
31
- this.error = await parse_response_error(response);
32
- return;
33
- }
46
+ await rpc.session_revoke_all({ account_id });
47
+ this.error = null;
34
48
  await this.fetch();
35
49
  }
36
50
  catch (e) {
@@ -41,15 +55,15 @@ export class AdminSessionsState extends Loadable {
41
55
  }
42
56
  }
43
57
  async revoke_all_tokens_for_account(account_id) {
58
+ const rpc = this.#get_rpc();
59
+ if (!rpc) {
60
+ this.error = 'rpc adapter not wired';
61
+ return;
62
+ }
44
63
  this.revoking_token_account_ids.add(account_id);
45
64
  try {
46
- const response = await ui_fetch(`/api/admin/accounts/${account_id}/tokens/revoke-all`, {
47
- method: 'POST',
48
- });
49
- if (!response.ok) {
50
- this.error = await parse_response_error(response);
51
- return;
52
- }
65
+ await rpc.token_revoke_all({ account_id });
66
+ this.error = null;
53
67
  await this.fetch();
54
68
  }
55
69
  catch (e) {