@fuzdev/fuz_app 0.59.0 → 0.61.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 (151) hide show
  1. package/dist/actions/CLAUDE.md +5 -5
  2. package/dist/actions/action_codegen.d.ts +1 -1
  3. package/dist/actions/action_codegen.js +2 -2
  4. package/dist/actions/action_event_helpers.d.ts +3 -3
  5. package/dist/actions/action_event_helpers.js +8 -8
  6. package/dist/actions/action_event_types.d.ts +3 -3
  7. package/dist/actions/action_event_types.js +3 -3
  8. package/dist/actions/transports_ws_auth_guard.d.ts +2 -2
  9. package/dist/actions/transports_ws_auth_guard.js +3 -3
  10. package/dist/auth/CLAUDE.md +157 -15
  11. package/dist/auth/actor_lookup_action_specs.d.ts +127 -0
  12. package/dist/auth/actor_lookup_action_specs.d.ts.map +1 -0
  13. package/dist/auth/actor_lookup_action_specs.js +93 -0
  14. package/dist/auth/actor_lookup_actions.d.ts +19 -0
  15. package/dist/auth/actor_lookup_actions.d.ts.map +1 -0
  16. package/dist/auth/actor_lookup_actions.js +32 -0
  17. package/dist/auth/actor_lookup_queries.d.ts +44 -0
  18. package/dist/auth/actor_lookup_queries.d.ts.map +1 -0
  19. package/dist/auth/actor_lookup_queries.js +42 -0
  20. package/dist/auth/actor_search_action_specs.d.ts +166 -0
  21. package/dist/auth/actor_search_action_specs.d.ts.map +1 -0
  22. package/dist/auth/actor_search_action_specs.js +139 -0
  23. package/dist/auth/actor_search_actions.d.ts +31 -0
  24. package/dist/auth/actor_search_actions.d.ts.map +1 -0
  25. package/dist/auth/actor_search_actions.js +61 -0
  26. package/dist/auth/actor_search_queries.d.ts +75 -0
  27. package/dist/auth/actor_search_queries.d.ts.map +1 -0
  28. package/dist/auth/actor_search_queries.js +91 -0
  29. package/dist/auth/admin_actions.js +2 -2
  30. package/dist/auth/all_action_spec_registries.d.ts +55 -0
  31. package/dist/auth/all_action_spec_registries.d.ts.map +1 -0
  32. package/dist/auth/all_action_spec_registries.js +59 -0
  33. package/dist/auth/audit_emitter.d.ts +1 -1
  34. package/dist/auth/audit_emitter.js +2 -2
  35. package/dist/auth/audit_log_queries.d.ts +1 -1
  36. package/dist/auth/audit_log_queries.js +3 -3
  37. package/dist/auth/audit_log_routes.d.ts +1 -1
  38. package/dist/auth/audit_log_routes.js +1 -1
  39. package/dist/auth/audit_log_schema.d.ts +5 -5
  40. package/dist/auth/audit_log_schema.js +7 -7
  41. package/dist/auth/auth_ddl.d.ts +7 -0
  42. package/dist/auth/auth_ddl.d.ts.map +1 -1
  43. package/dist/auth/auth_ddl.js +8 -0
  44. package/dist/auth/credential_type_schema.d.ts +1 -1
  45. package/dist/auth/credential_type_schema.js +3 -3
  46. package/dist/auth/grant_path_schema.d.ts +1 -1
  47. package/dist/auth/grant_path_schema.js +3 -3
  48. package/dist/auth/migrations.d.ts +4 -4
  49. package/dist/auth/migrations.d.ts.map +1 -1
  50. package/dist/auth/migrations.js +7 -6
  51. package/dist/auth/role_grant_offer_actions.js +2 -2
  52. package/dist/auth/role_grant_offer_notifications.d.ts +2 -2
  53. package/dist/auth/role_grant_offer_notifications.js +2 -2
  54. package/dist/auth/role_grant_queries.d.ts +21 -0
  55. package/dist/auth/role_grant_queries.d.ts.map +1 -1
  56. package/dist/auth/role_grant_queries.js +31 -0
  57. package/dist/auth/role_schema.d.ts +2 -2
  58. package/dist/auth/role_schema.js +3 -3
  59. package/dist/auth/self_service_role_actions.d.ts +1 -1
  60. package/dist/auth/self_service_role_actions.js +2 -2
  61. package/dist/auth/session_cookie.d.ts +1 -1
  62. package/dist/auth/session_cookie.js +1 -1
  63. package/dist/auth/session_middleware.d.ts +1 -1
  64. package/dist/auth/session_middleware.js +5 -5
  65. package/dist/rate_limiter.d.ts +5 -5
  66. package/dist/rate_limiter.js +6 -6
  67. package/dist/realtime/sse_auth_guard.d.ts +3 -3
  68. package/dist/realtime/sse_auth_guard.js +4 -4
  69. package/dist/server/app_backend.d.ts +3 -3
  70. package/dist/server/app_backend.js +4 -4
  71. package/dist/server/app_server.d.ts +1 -1
  72. package/dist/server/app_server.js +10 -10
  73. package/dist/testing/CLAUDE.md +22 -12
  74. package/dist/testing/admin_integration.js +4 -4
  75. package/dist/testing/app_server.d.ts +1 -1
  76. package/dist/testing/app_server.js +2 -2
  77. package/dist/testing/attack_surface.d.ts +4 -4
  78. package/dist/testing/attack_surface.js +6 -6
  79. package/dist/testing/audit_completeness.js +4 -4
  80. package/dist/testing/data_exposure.d.ts +2 -2
  81. package/dist/testing/data_exposure.js +7 -7
  82. package/dist/testing/db.d.ts +8 -8
  83. package/dist/testing/db.js +11 -11
  84. package/dist/testing/integration.js +4 -4
  85. package/dist/testing/integration_helpers.d.ts +6 -6
  86. package/dist/testing/integration_helpers.js +7 -7
  87. package/dist/testing/rate_limiting.js +4 -4
  88. package/dist/testing/round_trip.js +2 -2
  89. package/dist/testing/rpc_round_trip.js +2 -2
  90. package/dist/testing/schema_generators.d.ts.map +1 -1
  91. package/dist/testing/schema_generators.js +23 -2
  92. package/dist/testing/sse_round_trip.js +2 -2
  93. package/dist/testing/surface_invariants.d.ts +4 -4
  94. package/dist/testing/surface_invariants.js +5 -5
  95. package/dist/ui/AccountSessions.svelte +21 -6
  96. package/dist/ui/AccountSessions.svelte.d.ts.map +1 -1
  97. package/dist/ui/AdminAccounts.svelte +32 -25
  98. package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -1
  99. package/dist/ui/AdminAuditLog.svelte +3 -3
  100. package/dist/ui/AdminInvites.svelte +20 -15
  101. package/dist/ui/AdminOverview.svelte +19 -21
  102. package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
  103. package/dist/ui/AdminRoleGrantHistory.svelte +3 -3
  104. package/dist/ui/AdminSessions.svelte +19 -21
  105. package/dist/ui/AdminSessions.svelte.d.ts.map +1 -1
  106. package/dist/ui/AdminSettings.svelte +1 -3
  107. package/dist/ui/AdminSettings.svelte.d.ts.map +1 -1
  108. package/dist/ui/CLAUDE.md +123 -69
  109. package/dist/ui/ConfirmButton.svelte +82 -24
  110. package/dist/ui/ConfirmButton.svelte.d.ts +8 -34
  111. package/dist/ui/ConfirmButton.svelte.d.ts.map +1 -1
  112. package/dist/ui/OpenSignupToggle.svelte +6 -4
  113. package/dist/ui/OpenSignupToggle.svelte.d.ts.map +1 -1
  114. package/dist/ui/RoleGrantOfferForm.svelte +4 -4
  115. package/dist/ui/RoleGrantOfferHistory.svelte +3 -3
  116. package/dist/ui/RoleGrantOfferInbox.svelte +10 -6
  117. package/dist/ui/RoleGrantOfferInbox.svelte.d.ts.map +1 -1
  118. package/dist/ui/account_sessions_state.svelte.d.ts +17 -7
  119. package/dist/ui/account_sessions_state.svelte.d.ts.map +1 -1
  120. package/dist/ui/account_sessions_state.svelte.js +32 -33
  121. package/dist/ui/admin_accounts_state.svelte.d.ts +48 -17
  122. package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
  123. package/dist/ui/admin_accounts_state.svelte.js +58 -76
  124. package/dist/ui/admin_invites_state.svelte.d.ts +14 -7
  125. package/dist/ui/admin_invites_state.svelte.d.ts.map +1 -1
  126. package/dist/ui/admin_invites_state.svelte.js +32 -48
  127. package/dist/ui/admin_sessions_state.svelte.d.ts +15 -8
  128. package/dist/ui/admin_sessions_state.svelte.d.ts.map +1 -1
  129. package/dist/ui/admin_sessions_state.svelte.js +30 -47
  130. package/dist/ui/app_settings_state.svelte.d.ts +8 -3
  131. package/dist/ui/app_settings_state.svelte.d.ts.map +1 -1
  132. package/dist/ui/app_settings_state.svelte.js +19 -27
  133. package/dist/ui/async_slot.svelte.d.ts +173 -0
  134. package/dist/ui/async_slot.svelte.d.ts.map +1 -0
  135. package/dist/ui/async_slot.svelte.js +241 -0
  136. package/dist/ui/audit_log_state.svelte.d.ts +8 -2
  137. package/dist/ui/audit_log_state.svelte.d.ts.map +1 -1
  138. package/dist/ui/audit_log_state.svelte.js +19 -18
  139. package/dist/ui/keyed_async_slot.svelte.d.ts +139 -0
  140. package/dist/ui/keyed_async_slot.svelte.d.ts.map +1 -0
  141. package/dist/ui/keyed_async_slot.svelte.js +177 -0
  142. package/dist/ui/role_grant_offers_state.svelte.d.ts +39 -7
  143. package/dist/ui/role_grant_offers_state.svelte.d.ts.map +1 -1
  144. package/dist/ui/role_grant_offers_state.svelte.js +34 -15
  145. package/dist/ui/table_state.svelte.d.ts +10 -7
  146. package/dist/ui/table_state.svelte.d.ts.map +1 -1
  147. package/dist/ui/table_state.svelte.js +11 -8
  148. package/package.json +1 -1
  149. package/dist/ui/loadable.svelte.d.ts +0 -60
  150. package/dist/ui/loadable.svelte.d.ts.map +0 -1
  151. package/dist/ui/loadable.svelte.js +0 -80
@@ -158,8 +158,29 @@ export const generate_valid_value = (field, field_schema) => {
158
158
  return 1;
159
159
  case 'boolean':
160
160
  return true;
161
- case 'array':
162
- return [];
161
+ case 'array': {
162
+ let min_items = 0;
163
+ try {
164
+ const json = z.toJSONSchema(field_schema);
165
+ if (typeof json.minItems === 'number')
166
+ min_items = json.minItems;
167
+ }
168
+ catch {
169
+ // no constraint
170
+ }
171
+ if (min_items === 0)
172
+ return [];
173
+ const def = zod_unwrap_def(field_schema);
174
+ const element_schema = def.element;
175
+ if (!element_schema)
176
+ return [];
177
+ const element_field = {
178
+ ...field,
179
+ base_type: zod_get_base_type(element_schema),
180
+ };
181
+ const item = generate_valid_value(element_field, element_schema);
182
+ return Array.from({ length: min_items }, () => item);
183
+ }
163
184
  case 'object': {
164
185
  // Recursively generate valid nested objects
165
186
  const nested_schema = zod_unwrap_to_object(field_schema);
@@ -21,7 +21,7 @@ import { create_pglite_factory } from './db.js';
21
21
  import { find_route_spec, pick_auth_headers } from './integration_helpers.js';
22
22
  import { rpc_call, require_rpc_endpoint_path, resolve_rpc_endpoints_for_setup, } from './rpc_helpers.js';
23
23
  import { run_migrations } from '../db/migrate.js';
24
- import { AUTH_MIGRATION_NS } from '../auth/migrations.js';
24
+ import { auth_migration_ns } from '../auth/migrations.js';
25
25
  import { account_session_revoke_all_action_spec } from '../auth/account_action_specs.js';
26
26
  /**
27
27
  * Read one complete SSE frame (up to `\n\n`) from a stream reader.
@@ -138,7 +138,7 @@ export const describe_sse_route_tests = (options) => {
138
138
  const rpc_endpoints_for_setup = resolve_rpc_endpoints_for_setup(options.rpc_endpoints, options.session_options);
139
139
  const rpc_path = require_rpc_endpoint_path(rpc_endpoints_for_setup);
140
140
  const init_schema = async (db) => {
141
- await run_migrations(db, [AUTH_MIGRATION_NS]);
141
+ await run_migrations(db, [auth_migration_ns]);
142
142
  };
143
143
  const factories = options.db_factories ?? [create_pglite_factory(init_schema)];
144
144
  for (const factory of factories) {
@@ -178,13 +178,13 @@ export interface ErrorSchemaTightnessOptions {
178
178
  * them here instead of forcing every consumer to hand-maintain the entry.
179
179
  *
180
180
  * Paths assume the standard `/api/account` + `/api/db` prefixes used by every
181
- * fuz_app consumer. Merged into `DEFAULT_ERROR_SCHEMA_TIGHTNESS.allowlist` so
181
+ * fuz_app consumer. Merged into `default_error_schema_tightness.allowlist` so
182
182
  * consumers calling `assert_error_schema_tightness` directly inherit the
183
183
  * exemptions; the standard attack-surface suite also prepends these entries
184
184
  * underneath any consumer-supplied allowlist so project-specific entries are
185
185
  * additive.
186
186
  */
187
- export declare const FUZ_APP_STOCK_ROUTE_TIGHTNESS_ALLOWLIST: ReadonlyArray<string>;
187
+ export declare const fuz_app_stock_route_tightness_allowlist: ReadonlyArray<string>;
188
188
  /**
189
189
  * Baseline error schema tightness applied by
190
190
  * `describe_standard_attack_surface_tests` when no config is passed.
@@ -192,13 +192,13 @@ export declare const FUZ_APP_STOCK_ROUTE_TIGHTNESS_ALLOWLIST: ReadonlyArray<stri
192
192
  * Uses `min_specificity: 'enum'` (the assertion default) with `ignore_statuses`
193
193
  * for middleware-derived status codes that are commonly generic (auth middleware
194
194
  * produces multiple error codes at 401/403, and 429 comes from rate limiters),
195
- * and `allowlist` seeded with `FUZ_APP_STOCK_ROUTE_TIGHTNESS_ALLOWLIST` so
195
+ * and `allowlist` seeded with `fuz_app_stock_route_tightness_allowlist` so
196
196
  * fuz_app-shipped routes with heterogeneous generic schemas don't force every
197
197
  * consumer to hand-maintain an identical allowlist. Consumers can pass a
198
198
  * narrower config with project-specific `allowlist` entries, or pass `null`
199
199
  * to skip the assertion entirely.
200
200
  */
201
- export declare const DEFAULT_ERROR_SCHEMA_TIGHTNESS: ErrorSchemaTightnessOptions;
201
+ export declare const default_error_schema_tightness: ErrorSchemaTightnessOptions;
202
202
  /**
203
203
  * Assert that all error schemas meet a minimum specificity threshold.
204
204
  *
@@ -467,13 +467,13 @@ const SPECIFICITY_ORDER = {
467
467
  * them here instead of forcing every consumer to hand-maintain the entry.
468
468
  *
469
469
  * Paths assume the standard `/api/account` + `/api/db` prefixes used by every
470
- * fuz_app consumer. Merged into `DEFAULT_ERROR_SCHEMA_TIGHTNESS.allowlist` so
470
+ * fuz_app consumer. Merged into `default_error_schema_tightness.allowlist` so
471
471
  * consumers calling `assert_error_schema_tightness` directly inherit the
472
472
  * exemptions; the standard attack-surface suite also prepends these entries
473
473
  * underneath any consumer-supplied allowlist so project-specific entries are
474
474
  * additive.
475
475
  */
476
- export const FUZ_APP_STOCK_ROUTE_TIGHTNESS_ALLOWLIST = [];
476
+ export const fuz_app_stock_route_tightness_allowlist = [];
477
477
  /**
478
478
  * Baseline error schema tightness applied by
479
479
  * `describe_standard_attack_surface_tests` when no config is passed.
@@ -481,15 +481,15 @@ export const FUZ_APP_STOCK_ROUTE_TIGHTNESS_ALLOWLIST = [];
481
481
  * Uses `min_specificity: 'enum'` (the assertion default) with `ignore_statuses`
482
482
  * for middleware-derived status codes that are commonly generic (auth middleware
483
483
  * produces multiple error codes at 401/403, and 429 comes from rate limiters),
484
- * and `allowlist` seeded with `FUZ_APP_STOCK_ROUTE_TIGHTNESS_ALLOWLIST` so
484
+ * and `allowlist` seeded with `fuz_app_stock_route_tightness_allowlist` so
485
485
  * fuz_app-shipped routes with heterogeneous generic schemas don't force every
486
486
  * consumer to hand-maintain an identical allowlist. Consumers can pass a
487
487
  * narrower config with project-specific `allowlist` entries, or pass `null`
488
488
  * to skip the assertion entirely.
489
489
  */
490
- export const DEFAULT_ERROR_SCHEMA_TIGHTNESS = {
490
+ export const default_error_schema_tightness = {
491
491
  ignore_statuses: [401, 403, 429],
492
- allowlist: [...FUZ_APP_STOCK_ROUTE_TIGHTNESS_ALLOWLIST],
492
+ allowlist: [...fuz_app_stock_route_tightness_allowlist],
493
493
  };
494
494
  /**
495
495
  * Assert that all error schemas meet a minimum specificity threshold.
@@ -25,8 +25,8 @@
25
25
  void account_sessions.fetch();
26
26
 
27
27
  const handle_revoke_all = async (): Promise<void> => {
28
- await account_sessions.revoke_all();
29
- if (!account_sessions.error) {
28
+ await account_sessions.submit_revoke_all();
29
+ if (!account_sessions.revoke_all.error) {
30
30
  auth_state.verified = false;
31
31
  }
32
32
  };
@@ -48,11 +48,15 @@
48
48
  {/if}
49
49
  </h2>
50
50
 
51
- {#if account_sessions.loading}
51
+ {#if account_sessions.list.loading}
52
52
  <p class="text_50">loading sessions...</p>
53
- {:else if account_sessions.error}
54
- <p class="color_c_50">{account_sessions.error}</p>
53
+ {:else if account_sessions.list.error}
54
+ <p class="color_c_50">{account_sessions.list.error}</p>
55
55
  {:else}
56
+ {@const revoke_all_error = account_sessions.revoke_all.error}
57
+ {#if revoke_all_error}
58
+ <p class="color_c_50">{revoke_all_error}</p>
59
+ {/if}
56
60
  {#if account_sessions.active_count > 1}
57
61
  <div class="mb_md">
58
62
  <button type="button" onclick={() => handle_revoke_all()}>revoke all</button>
@@ -76,7 +80,18 @@
76
80
  {format_relative_time(row.expires_at)}
77
81
  </span>
78
82
  {:else if column.key === 'account_id'}
79
- <button type="button" onclick={() => account_sessions.revoke(row.id)}>revoke</button>
83
+ {@const revoking = account_sessions.revoke.loading(row.id)}
84
+ {@const revoke_error = account_sessions.revoke.error(row.id)}
85
+ <button
86
+ type="button"
87
+ disabled={revoking}
88
+ onclick={() => account_sessions.submit_revoke(row.id)}
89
+ >
90
+ {revoking ? 'revoking…' : 'revoke'}
91
+ </button>
92
+ {#if revoke_error}
93
+ <span class="color_c_50 font_size_sm">{revoke_error}</span>
94
+ {/if}
80
95
  {:else if column.format}
81
96
  {column.format(row[column.key], row)}
82
97
  {:else}
@@ -1 +1 @@
1
- {"version":3,"file":"AccountSessions.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AccountSessions.svelte"],"names":[],"mappings":"AAsGA,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IACtG,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAKD,QAAA,MAAM,eAAe;;kBAA+E,CAAC;AACnF,KAAK,eAAe,GAAG,YAAY,CAAC,OAAO,eAAe,CAAC,CAAC;AAC9D,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"AccountSessions.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AccountSessions.svelte"],"names":[],"mappings":"AAiHA,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IACtG,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAKD,QAAA,MAAM,eAAe;;kBAA+E,CAAC;AACnF,KAAK,eAAe,GAAG,YAAY,CAAC,OAAO,eAAe,CAAC,CAAC;AAC9D,eAAe,eAAe,CAAC"}
@@ -9,7 +9,11 @@
9
9
  * @module
10
10
  */
11
11
 
12
- import {AdminAccountsState, admin_accounts_rpc_context} from './admin_accounts_state.svelte.js';
12
+ import {
13
+ AdminAccountsState,
14
+ admin_accounts_rpc_context,
15
+ grant_key,
16
+ } from './admin_accounts_state.svelte.js';
13
17
  import ConfirmButton from './ConfirmButton.svelte';
14
18
  import Datatable from './Datatable.svelte';
15
19
  import type {DatatableColumn} from './datatable.js';
@@ -45,10 +49,10 @@
45
49
  </p>
46
50
  {/if}
47
51
 
48
- {#if admin_accounts.loading}
52
+ {#if admin_accounts.list.loading}
49
53
  <p class="text_50">loading accounts...</p>
50
- {:else if admin_accounts.error}
51
- <p class="color_c_50">{admin_accounts.error}</p>
54
+ {:else if admin_accounts.list.error}
55
+ <p class="color_c_50">{admin_accounts.list.error}</p>
52
56
  {:else}
53
57
  <Datatable {columns} rows={admin_accounts.accounts} height="400px">
54
58
  {#snippet cell(column, row)}
@@ -92,16 +96,17 @@
92
96
  {/if}
93
97
  {#if admin_accounts.has_rpc && row.actor}
94
98
  {@const actor_id = row.actor.id}
99
+ {@const revoke_error = admin_accounts.revoke.error(role_grant.id)}
95
100
  <ConfirmButton
96
- onconfirm={() => admin_accounts.revoke_role_grant(actor_id, role_grant.id)}
101
+ onconfirm={() => admin_accounts.submit_revoke(actor_id, role_grant.id)}
97
102
  title="revoke {role_grant.role}"
98
103
  class="sm"
99
- disabled={admin_accounts.revoking_ids.has(role_grant.id)}
100
- >
101
- {#snippet children(_popover, _confirm)}
102
- {admin_accounts.revoking_ids.has(role_grant.id) ? 'revoking…' : 'revoke'}
103
- {/snippet}
104
- </ConfirmButton>
104
+ label="revoke"
105
+ pending={admin_accounts.revoke.loading(role_grant.id)}
106
+ />
107
+ {#if revoke_error}
108
+ <span class="color_c_50 font_size_sm">{revoke_error}</span>
109
+ {/if}
105
110
  {/if}
106
111
  </div>
107
112
  {/each}
@@ -120,16 +125,17 @@
120
125
  </span>
121
126
  {/if}
122
127
  {#if admin_accounts.has_rpc}
128
+ {@const retract_error = admin_accounts.retract.error(offer.id)}
123
129
  <ConfirmButton
124
- onconfirm={() => admin_accounts.retract_offer(offer.id)}
130
+ onconfirm={() => admin_accounts.submit_retract(offer.id)}
125
131
  title="retract offer"
126
132
  class="sm"
127
- disabled={admin_accounts.retracting_ids.has(offer.id)}
128
- >
129
- {#snippet children(_popover, _confirm)}
130
- {admin_accounts.retracting_ids.has(offer.id) ? 'retracting…' : 'retract'}
131
- {/snippet}
132
- </ConfirmButton>
133
+ label="retract"
134
+ pending={admin_accounts.retract.loading(offer.id)}
135
+ />
136
+ {#if retract_error}
137
+ <span class="color_c_50 font_size_sm">{retract_error}</span>
138
+ {/if}
133
139
  {/if}
134
140
  </div>
135
141
  {/each}
@@ -139,24 +145,25 @@
139
145
  {:else if column.key === 'actor'}
140
146
  {#if admin_accounts.has_rpc}
141
147
  {#each admin_accounts.grantable_roles as role (role)}
148
+ {@const key = grant_key(row.account.id, role)}
149
+ {@const grant_error = admin_accounts.grant.error(key)}
142
150
  {#if !row.role_grants.some((p) => p.role === role) && !row.pending_offers.some((o) => o.role === role)}
143
151
  <ConfirmButton
144
- onconfirm={() => admin_accounts.create_role_grant(row.account.id, role)}
152
+ onconfirm={() => admin_accounts.submit_grant(row.account.id, role)}
145
153
  title="offer {role}"
146
154
  class="sm"
147
- disabled={admin_accounts.granting_keys.has(`${row.account.id}:${role}`)}
155
+ label={`+ ${role}`}
156
+ pending={admin_accounts.grant.loading(key)}
148
157
  >
149
- {#snippet children(_popover, _confirm)}
150
- {admin_accounts.granting_keys.has(`${row.account.id}:${role}`)
151
- ? 'offering…'
152
- : `+ ${role}`}
153
- {/snippet}
154
158
  {#snippet popover_content(_popover, do_confirm)}
155
159
  <button type="button" class="color_b bg_100" onclick={() => do_confirm()}>
156
160
  <span class="py_sm">offer '{role}' to @{row.account.username}</span>
157
161
  </button>
158
162
  {/snippet}
159
163
  </ConfirmButton>
164
+ {#if grant_error}
165
+ <span class="color_c_50 font_size_sm">{grant_error}</span>
166
+ {/if}
160
167
  {/if}
161
168
  {/each}
162
169
  {/if}
@@ -1 +1 @@
1
- {"version":3,"file":"AdminAccounts.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminAccounts.svelte"],"names":[],"mappings":"AA+JA,QAAA,MAAM,aAAa,2DAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"AdminAccounts.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminAccounts.svelte"],"names":[],"mappings":"AAmKA,QAAA,MAAM,aAAa,2DAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -98,10 +98,10 @@
98
98
  {/if}
99
99
  </div>
100
100
 
101
- {#if audit_log.loading}
101
+ {#if audit_log.list.loading}
102
102
  <p class="text_50">loading audit log...</p>
103
- {:else if audit_log.error}
104
- <p class="color_c_50">{audit_log.error}</p>
103
+ {:else if audit_log.list.error}
104
+ <p class="color_c_50">{audit_log.list.error}</p>
105
105
  {:else}
106
106
  <Datatable {columns} rows={audit_log.events} height="500px">
107
107
  {#snippet cell(column, row)}
@@ -25,12 +25,12 @@
25
25
  let invite_username = $state.raw('');
26
26
 
27
27
  const can_create = $derived(
28
- (invite_email.trim() || invite_username.trim()) && !admin_invites.creating,
28
+ (invite_email.trim() || invite_username.trim()) && !admin_invites.create.loading,
29
29
  );
30
30
 
31
31
  const handle_create = async (): Promise<void> => {
32
32
  if (!can_create) return;
33
- const success = await admin_invites.create_invite(
33
+ const success = await admin_invites.submit_create(
34
34
  invite_email.trim() || undefined,
35
35
  invite_username.trim() || undefined,
36
36
  );
@@ -80,7 +80,7 @@
80
80
  type="email"
81
81
  bind:value={invite_email}
82
82
  placeholder="email (optional)"
83
- disabled={admin_invites.creating}
83
+ disabled={admin_invites.create.loading}
84
84
  />
85
85
  </label>
86
86
  <label class="grow">
@@ -89,20 +89,24 @@
89
89
  type="text"
90
90
  bind:value={invite_username}
91
91
  placeholder="username (optional)"
92
- disabled={admin_invites.creating}
92
+ disabled={admin_invites.create.loading}
93
93
  />
94
94
  </label>
95
95
  </fieldset>
96
- <PendingButton pending={admin_invites.creating} disabled={!can_create} onclick={handle_create}>
96
+ <PendingButton
97
+ pending={admin_invites.create.loading}
98
+ disabled={!can_create}
99
+ onclick={handle_create}
100
+ >
97
101
  create invite
98
102
  </PendingButton>
99
103
  </form>
100
104
 
101
- {#if admin_invites.error}
102
- <p class="color_c_50">{admin_invites.error}</p>
105
+ {#if admin_invites.list.error || admin_invites.create.error}
106
+ <p class="color_c_50">{admin_invites.list.error ?? admin_invites.create.error}</p>
103
107
  {/if}
104
108
 
105
- {#if admin_invites.loading}
109
+ {#if admin_invites.list.loading}
106
110
  <p class="text_50">loading invites...</p>
107
111
  {:else}
108
112
  <Datatable {columns} rows={admin_invites.invites} height="400px">
@@ -129,16 +133,17 @@
129
133
  </span>
130
134
  {:else if column.key === 'id'}
131
135
  {#if !row.claimed_at}
136
+ {@const remove_error = admin_invites.remove.error(row.id)}
132
137
  <ConfirmButton
133
- onconfirm={() => admin_invites.delete_invite(row.id)}
138
+ onconfirm={() => admin_invites.submit_delete(row.id)}
134
139
  title="delete invite"
135
140
  class="sm"
136
- disabled={admin_invites.deleting_ids.has(row.id)}
137
- >
138
- {#snippet children(_popover, _confirm)}
139
- {admin_invites.deleting_ids.has(row.id) ? 'deleting...' : 'delete'}
140
- {/snippet}
141
- </ConfirmButton>
141
+ label="delete"
142
+ pending={admin_invites.remove.loading(row.id)}
143
+ />
144
+ {#if remove_error}
145
+ <span class="color_c_50 font_size_sm">{remove_error}</span>
146
+ {/if}
142
147
  {:else}
143
148
  <span class="text_50">-</span>
144
149
  {/if}
@@ -89,10 +89,10 @@
89
89
  <h3>accounts</h3>
90
90
  <a href={resolve('/admin/accounts' as any)} class="text_50 font_size_sm">view all &rarr;</a>
91
91
  </div>
92
- {#if accounts.loading}
92
+ {#if accounts.list.loading}
93
93
  <p class="text_50">loading...</p>
94
- {:else if accounts.error}
95
- <p class="color_c_50">{accounts.error}</p>
94
+ {:else if accounts.list.error}
95
+ <p class="color_c_50">{accounts.list.error}</p>
96
96
  {:else}
97
97
  <div class="baseline-row gap_xs">
98
98
  <strong class="font_size_lg">{accounts.account_count}</strong>
@@ -131,10 +131,10 @@
131
131
  <h3>sessions</h3>
132
132
  <a href={resolve('/admin/sessions' as any)} class="text_50 font_size_sm">view all &rarr;</a>
133
133
  </div>
134
- {#if sessions.loading}
134
+ {#if sessions.list.loading}
135
135
  <p class="text_50">loading...</p>
136
- {:else if sessions.error}
137
- <p class="color_c_50">{sessions.error}</p>
136
+ {:else if sessions.list.error}
137
+ <p class="color_c_50">{sessions.list.error}</p>
138
138
  {:else}
139
139
  <div class="baseline-row gap_xs">
140
140
  <strong class="font_size_lg">{sessions.active_count}</strong>
@@ -161,10 +161,10 @@
161
161
  <h3>invites</h3>
162
162
  <a href={resolve('/admin/invites' as any)} class="text_50 font_size_sm">view all &rarr;</a>
163
163
  </div>
164
- {#if invites.loading}
164
+ {#if invites.list.loading}
165
165
  <p class="text_50">loading...</p>
166
- {:else if invites.error}
167
- <p class="color_c_50">{invites.error}</p>
166
+ {:else if invites.list.error}
167
+ <p class="color_c_50">{invites.list.error}</p>
168
168
  {:else}
169
169
  <div class="baseline-row gap_sm">
170
170
  <span class="text_50">public signup</span>
@@ -206,10 +206,10 @@
206
206
  <h3>recent activity</h3>
207
207
  <a href={resolve('/admin/audit-log' as any)} class="text_50 font_size_sm">view all &rarr;</a>
208
208
  </div>
209
- {#if audit_log.loading}
209
+ {#if audit_log.list.loading}
210
210
  <p class="text_50">loading...</p>
211
- {:else if audit_log.error}
212
- <p class="color_c_50">{audit_log.error}</p>
211
+ {:else if audit_log.list.error}
212
+ <p class="color_c_50">{audit_log.list.error}</p>
213
213
  {:else if recent_events.length === 0}
214
214
  <p class="text_50">no events</p>
215
215
  {:else}
@@ -234,10 +234,10 @@
234
234
  <h3>security</h3>
235
235
  <a href={resolve('/admin/audit-log' as any)} class="text_50 font_size_sm">audit log &rarr;</a>
236
236
  </div>
237
- {#if audit_log.loading}
237
+ {#if audit_log.list.loading}
238
238
  <p class="text_50">loading...</p>
239
- {:else if audit_log.error}
240
- <p class="color_c_50">{audit_log.error}</p>
239
+ {:else if audit_log.list.error}
240
+ <p class="color_c_50">{audit_log.list.error}</p>
241
241
  {:else}
242
242
  <div class="baseline-row gap_xs">
243
243
  <strong class="font_size_lg" class:color_c_50={failed_logins.length > 0}>
@@ -271,10 +271,10 @@
271
271
  <div class="panel-header">
272
272
  <h3>system</h3>
273
273
  </div>
274
- {#if app_settings.loading}
274
+ {#if app_settings.list.loading}
275
275
  <p class="text_50">loading...</p>
276
- {:else if app_settings.error}
277
- <p class="color_c_50">{app_settings.error}</p>
276
+ {:else if app_settings.list.error}
277
+ <p class="color_c_50">{app_settings.list.error}</p>
278
278
  {:else}
279
279
  <div class="baseline-row gap_sm">
280
280
  <span class="text_50">public signup</span>
@@ -308,10 +308,8 @@
308
308
  await auth_state.logout();
309
309
  }}
310
310
  title="log out"
311
+ label="log out"
311
312
  >
312
- {#snippet children(_popover, _confirm)}
313
- log out
314
- {/snippet}
315
313
  {#snippet popover_button_content()}
316
314
  <span class="p_md"> log out </span>
317
315
  {/snippet}
@@ -1 +1 @@
1
- {"version":3,"file":"AdminOverview.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminOverview.svelte"],"names":[],"mappings":"AA4UA,QAAA,MAAM,aAAa,2DAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"AdminOverview.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminOverview.svelte"],"names":[],"mappings":"AA2UA,QAAA,MAAM,aAAa,2DAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -40,10 +40,10 @@
40
40
  <section>
41
41
  <h1>role_grant history</h1>
42
42
 
43
- {#if audit_log.loading}
43
+ {#if audit_log.role_grant_history.loading}
44
44
  <p class="text_50">loading role_grant history...</p>
45
- {:else if audit_log.error}
46
- <p class="color_c_50">{audit_log.error}</p>
45
+ {:else if audit_log.role_grant_history.error}
46
+ <p class="color_c_50">{audit_log.role_grant_history.error}</p>
47
47
  {:else}
48
48
  <Datatable {columns} rows={audit_log.role_grant_history_events} height="400px" row_key="id">
49
49
  {#snippet cell(column, row)}
@@ -40,10 +40,10 @@
40
40
  </p>
41
41
  {/if}
42
42
 
43
- {#if admin_sessions.loading}
43
+ {#if admin_sessions.list.loading}
44
44
  <p class="text_50">loading sessions...</p>
45
- {:else if admin_sessions.error}
46
- <p class="color_c_50">{admin_sessions.error}</p>
45
+ {:else if admin_sessions.list.error}
46
+ <p class="color_c_50">{admin_sessions.list.error}</p>
47
47
  {:else}
48
48
  <Datatable {columns} rows={admin_sessions.sessions} height="400px">
49
49
  {#snippet cell(column, row)}
@@ -63,30 +63,28 @@
63
63
  </span>
64
64
  {:else if column.key === 'account_id'}
65
65
  {#if admin_sessions.has_rpc}
66
+ {@const revoke_sessions_error = admin_sessions.revoke_sessions.error(row.account_id)}
67
+ {@const revoke_tokens_error = admin_sessions.revoke_tokens.error(row.account_id)}
66
68
  <ConfirmButton
67
- onconfirm={() => admin_sessions.revoke_all_for_account(row.account_id)}
69
+ onconfirm={() => admin_sessions.submit_revoke_sessions(row.account_id)}
68
70
  title="revoke all sessions for {row.username}"
69
71
  class="sm"
70
- disabled={admin_sessions.revoking_account_ids.has(row.account_id)}
71
- >
72
- {#snippet children(_popover, _confirm)}
73
- {admin_sessions.revoking_account_ids.has(row.account_id)
74
- ? 'revoking…'
75
- : 'revoke sessions'}
76
- {/snippet}
77
- </ConfirmButton>
72
+ label="revoke sessions"
73
+ pending={admin_sessions.revoke_sessions.loading(row.account_id)}
74
+ />
75
+ {#if revoke_sessions_error}
76
+ <span class="color_c_50 font_size_sm">{revoke_sessions_error}</span>
77
+ {/if}
78
78
  <ConfirmButton
79
- onconfirm={() => admin_sessions.revoke_all_tokens_for_account(row.account_id)}
79
+ onconfirm={() => admin_sessions.submit_revoke_tokens(row.account_id)}
80
80
  title="revoke all tokens for {row.username}"
81
81
  class="sm"
82
- disabled={admin_sessions.revoking_token_account_ids.has(row.account_id)}
83
- >
84
- {#snippet children(_popover, _confirm)}
85
- {admin_sessions.revoking_token_account_ids.has(row.account_id)
86
- ? 'revoking…'
87
- : 'revoke tokens'}
88
- {/snippet}
89
- </ConfirmButton>
82
+ label="revoke tokens"
83
+ pending={admin_sessions.revoke_tokens.loading(row.account_id)}
84
+ />
85
+ {#if revoke_tokens_error}
86
+ <span class="color_c_50 font_size_sm">{revoke_tokens_error}</span>
87
+ {/if}
90
88
  {/if}
91
89
  {:else if column.format}
92
90
  {column.format(row[column.key], row)}
@@ -1 +1 @@
1
- {"version":3,"file":"AdminSessions.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminSessions.svelte"],"names":[],"mappings":"AAwGA,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IACtG,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAKD,QAAA,MAAM,aAAa;;kBAA+E,CAAC;AACjF,KAAK,aAAa,GAAG,YAAY,CAAC,OAAO,aAAa,CAAC,CAAC;AAC1D,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"AdminSessions.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminSessions.svelte"],"names":[],"mappings":"AAoGA,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IACtG,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAKD,QAAA,MAAM,aAAa;;kBAA+E,CAAC;AACjF,KAAK,aAAa,GAAG,YAAY,CAAC,OAAO,aAAa,CAAC,CAAC;AAC1D,eAAe,aAAa,CAAC"}
@@ -30,10 +30,8 @@
30
30
  await auth_state.logout();
31
31
  }}
32
32
  title="log out"
33
+ label="log out"
33
34
  >
34
- {#snippet children(_popover, _confirm)}
35
- log out
36
- {/snippet}
37
35
  {#snippet popover_button_content()}
38
36
  <span class="p_md"> log out </span>
39
37
  {/snippet}
@@ -1 +1 @@
1
- {"version":3,"file":"AdminSettings.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminSettings.svelte"],"names":[],"mappings":"AA+CA,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IACtG,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAKD,QAAA,MAAM,aAAa;;kBAA+E,CAAC;AACjF,KAAK,aAAa,GAAG,YAAY,CAAC,OAAO,aAAa,CAAC,CAAC;AAC1D,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"AdminSettings.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminSettings.svelte"],"names":[],"mappings":"AA8CA,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IACtG,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAKD,QAAA,MAAM,aAAa;;kBAA+E,CAAC;AACjF,KAAK,aAAa,GAAG,YAAY,CAAC,OAAO,aAAa,CAAC,CAAC;AAC1D,eAAe,aAAa,CAAC"}