@fuzdev/fuz_app 0.38.1 → 0.40.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/auth/CLAUDE.md +124 -36
- package/dist/auth/account_actions.d.ts +5 -3
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +5 -6
- package/dist/auth/account_routes.d.ts.map +1 -1
- package/dist/auth/account_routes.js +7 -7
- package/dist/auth/admin_action_specs.d.ts +6 -138
- package/dist/auth/admin_action_specs.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.js +4 -2
- package/dist/auth/admin_actions.d.ts +4 -3
- package/dist/auth/admin_actions.d.ts.map +1 -1
- package/dist/auth/admin_actions.js +8 -9
- package/dist/auth/audit_log_queries.d.ts +32 -20
- package/dist/auth/audit_log_queries.d.ts.map +1 -1
- package/dist/auth/audit_log_queries.js +52 -40
- package/dist/auth/audit_log_schema.d.ts +105 -84
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +84 -12
- package/dist/auth/bootstrap_routes.d.ts.map +1 -1
- package/dist/auth/bootstrap_routes.js +3 -3
- package/dist/auth/cleanup.d.ts +9 -1
- package/dist/auth/cleanup.d.ts.map +1 -1
- package/dist/auth/cleanup.js +2 -2
- package/dist/auth/deps.d.ts +13 -1
- package/dist/auth/deps.d.ts.map +1 -1
- package/dist/auth/permit_offer_actions.d.ts +16 -2
- package/dist/auth/permit_offer_actions.d.ts.map +1 -1
- package/dist/auth/permit_offer_actions.js +26 -8
- package/dist/auth/role_schema.d.ts +10 -1
- package/dist/auth/role_schema.d.ts.map +1 -1
- package/dist/auth/role_schema.js +10 -1
- package/dist/auth/self_service_role_actions.d.ts +136 -0
- package/dist/auth/self_service_role_actions.d.ts.map +1 -0
- package/dist/auth/self_service_role_actions.js +198 -0
- package/dist/auth/signup_routes.d.ts.map +1 -1
- package/dist/auth/signup_routes.js +2 -2
- package/dist/auth/standard_rpc_actions.d.ts +1 -1
- package/dist/auth/standard_rpc_actions.js +1 -1
- package/dist/http/jsonrpc_errors.d.ts +27 -75
- package/dist/http/jsonrpc_errors.d.ts.map +1 -1
- package/dist/http/jsonrpc_errors.js +16 -9
- package/dist/server/app_backend.d.ts +26 -7
- package/dist/server/app_backend.d.ts.map +1 -1
- package/dist/server/app_backend.js +29 -7
- package/dist/server/app_server.d.ts +6 -7
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +16 -29
- package/dist/ui/AdminAccounts.svelte +19 -0
- package/dist/ui/AdminAccounts.svelte.d.ts +2 -17
- package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -1
- package/dist/ui/AdminPermitHistory.svelte +23 -2
- package/dist/ui/AdminPermitHistory.svelte.d.ts +2 -17
- package/dist/ui/AdminPermitHistory.svelte.d.ts.map +1 -1
- package/dist/ui/CLAUDE.md +11 -0
- package/dist/ui/PermitOfferHistory.svelte +11 -5
- package/dist/ui/PermitOfferHistory.svelte.d.ts +7 -1
- package/dist/ui/PermitOfferHistory.svelte.d.ts.map +1 -1
- package/dist/ui/PermitOfferInbox.svelte +12 -7
- package/dist/ui/PermitOfferInbox.svelte.d.ts +8 -3
- package/dist/ui/PermitOfferInbox.svelte.d.ts.map +1 -1
- package/dist/ui/admin_rpc_adapters.d.ts +16 -1
- package/dist/ui/admin_rpc_adapters.d.ts.map +1 -1
- package/dist/ui/admin_rpc_adapters.js +12 -1
- package/dist/ui/format_scope.d.ts +45 -0
- package/dist/ui/format_scope.d.ts.map +1 -0
- package/dist/ui/format_scope.js +34 -0
- package/dist/ui/ui_format.d.ts +2 -3
- package/dist/ui/ui_format.d.ts.map +1 -1
- package/dist/ui/ui_format.js +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAC,IAAI,EAAE,KAAK,OAAO,EAAC,MAAM,MAAM,CAAC;AAGxC,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,EAEN,KAAK,cAAc,EAEnB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,8BAA8B,CAAC;AAC1E,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAGN,KAAK,WAAW,EAChB,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,gCAAgC,CAAC;AAEhE,OAAO,EAGN,KAAK,WAAW,EAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAC9D,OAAO,
|
|
1
|
+
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_server.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,8 +16,6 @@ import { SESSION_COOKIE_OPTIONS, } from '../auth/session_cookie.js';
|
|
|
16
16
|
import { create_audit_log_sse, AUDIT_LOG_EVENT_SPECS, } from '../realtime/sse_auth_guard.js';
|
|
17
17
|
import { query_app_settings_load } from '../auth/app_settings_queries.js';
|
|
18
18
|
import { create_rate_limiter, DEFAULT_LOGIN_ACCOUNT_RATE_LIMIT, } from '../rate_limiter.js';
|
|
19
|
-
import { run_migrations } from '../db/migrate.js';
|
|
20
|
-
import { AUTH_MIGRATION_NAMESPACE } from '../auth/migrations.js';
|
|
21
19
|
// Side-effect import: augments Hono's ContextVariableMap so consumers
|
|
22
20
|
// that import app_server get type-safe c.get('auth_session_id') etc.
|
|
23
21
|
import '../hono_context.js';
|
|
@@ -37,9 +35,10 @@ export const DEFAULT_MAX_BODY_SIZE = 1024 * 1024;
|
|
|
37
35
|
/**
|
|
38
36
|
* Create a fully assembled Hono app with auth, middleware, and routes.
|
|
39
37
|
*
|
|
40
|
-
* Handles the
|
|
41
|
-
*
|
|
42
|
-
*
|
|
38
|
+
* Handles the assembly lifecycle: proxy middleware → auth middleware →
|
|
39
|
+
* bootstrap status → route specs → surface generation → Hono app assembly →
|
|
40
|
+
* static serving. Database migrations belong to the backend lifecycle —
|
|
41
|
+
* pass `migration_namespaces` to `create_app_backend`.
|
|
43
42
|
*
|
|
44
43
|
* @param options - server configuration
|
|
45
44
|
* @returns assembled Hono app, backend, surface build, and bootstrap status
|
|
@@ -47,19 +46,7 @@ export const DEFAULT_MAX_BODY_SIZE = 1024 * 1024;
|
|
|
47
46
|
export const create_app_server = async (options) => {
|
|
48
47
|
const { backend } = options;
|
|
49
48
|
const { log } = backend.deps;
|
|
50
|
-
//
|
|
51
|
-
let all_migration_results = backend.migration_results;
|
|
52
|
-
if (options.migration_namespaces?.length) {
|
|
53
|
-
// guard against namespace collision with fuz_app's internal migrations
|
|
54
|
-
for (const ns of options.migration_namespaces) {
|
|
55
|
-
if (ns.namespace === AUTH_MIGRATION_NAMESPACE) {
|
|
56
|
-
throw new Error(`Migration namespace "${AUTH_MIGRATION_NAMESPACE}" is reserved by fuz_app — choose a different namespace`);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
const consumer_results = await run_migrations(backend.deps.db, options.migration_namespaces);
|
|
60
|
-
all_migration_results = [...backend.migration_results, ...consumer_results];
|
|
61
|
-
}
|
|
62
|
-
// 2. Rate limiter defaults (undefined = default, null = disable)
|
|
49
|
+
// Rate limiter defaults (undefined = default, null = disable)
|
|
63
50
|
const ip_rate_limiter = options.ip_rate_limiter === undefined ? create_rate_limiter() : options.ip_rate_limiter;
|
|
64
51
|
const login_account_rate_limiter = options.login_account_rate_limiter === undefined
|
|
65
52
|
? create_rate_limiter(DEFAULT_LOGIN_ACCOUNT_RATE_LIMIT)
|
|
@@ -70,7 +57,7 @@ export const create_app_server = async (options) => {
|
|
|
70
57
|
const bearer_ip_rate_limiter = options.bearer_ip_rate_limiter === undefined
|
|
71
58
|
? create_rate_limiter()
|
|
72
59
|
: options.bearer_ip_rate_limiter;
|
|
73
|
-
//
|
|
60
|
+
// Factory-managed audit SSE (shallow copy deps, no mutation of backend.deps)
|
|
74
61
|
const audit_sse = options.audit_log_sse
|
|
75
62
|
? create_audit_log_sse({
|
|
76
63
|
log,
|
|
@@ -86,9 +73,9 @@ export const create_app_server = async (options) => {
|
|
|
86
73
|
},
|
|
87
74
|
}
|
|
88
75
|
: backend.deps;
|
|
89
|
-
//
|
|
76
|
+
// Proxy middleware
|
|
90
77
|
const proxy_spec = create_proxy_middleware_spec({ ...options.proxy, log });
|
|
91
|
-
//
|
|
78
|
+
// Auth middleware
|
|
92
79
|
const auth_middleware = await create_auth_middleware_specs(deps, {
|
|
93
80
|
allowed_origins: options.allowed_origins,
|
|
94
81
|
session_options: options.session_options,
|
|
@@ -99,16 +86,16 @@ export const create_app_server = async (options) => {
|
|
|
99
86
|
if (options.transform_middleware) {
|
|
100
87
|
middleware_specs = options.transform_middleware(middleware_specs);
|
|
101
88
|
}
|
|
102
|
-
//
|
|
89
|
+
// Bootstrap status + app settings
|
|
103
90
|
const bootstrap_status = options.bootstrap
|
|
104
91
|
? await check_bootstrap_status(deps, { token_path: options.bootstrap.token_path })
|
|
105
92
|
: { available: false, token_path: null };
|
|
106
93
|
const app_settings = await query_app_settings_load({ db: deps.db });
|
|
107
|
-
//
|
|
94
|
+
// Surface route ref — factory manages the circular ref
|
|
108
95
|
const surface_ref = {
|
|
109
96
|
surface: { middleware: [], routes: [], rpc_endpoints: [], env: [], events: [], diagnostics: [] },
|
|
110
97
|
};
|
|
111
|
-
//
|
|
98
|
+
// Route specs (consumer routes + factory-managed routes)
|
|
112
99
|
const context = {
|
|
113
100
|
deps,
|
|
114
101
|
backend,
|
|
@@ -149,7 +136,7 @@ export const create_app_server = async (options) => {
|
|
|
149
136
|
factory_routes.push(create_surface_route_spec(surface_ref));
|
|
150
137
|
}
|
|
151
138
|
const route_specs = [...consumer_routes, ...factory_routes];
|
|
152
|
-
//
|
|
139
|
+
// Surface + logging
|
|
153
140
|
const surface_middleware = options.post_route_middleware
|
|
154
141
|
? [...middleware_specs, ...options.post_route_middleware]
|
|
155
142
|
: middleware_specs;
|
|
@@ -210,7 +197,7 @@ export const create_app_server = async (options) => {
|
|
|
210
197
|
// Backfill the surface ref — factory owns this lifecycle
|
|
211
198
|
surface_ref.surface = surface_spec.surface;
|
|
212
199
|
log_startup_summary(surface_spec.surface, log, options.env_values);
|
|
213
|
-
//
|
|
200
|
+
// Hono app assembly
|
|
214
201
|
const app = new Hono();
|
|
215
202
|
// Pending effects — collects fire-and-forget promises (audit logs, usage tracking).
|
|
216
203
|
// In test mode, effects are awaited before the response returns.
|
|
@@ -255,11 +242,11 @@ export const create_app_server = async (options) => {
|
|
|
255
242
|
}
|
|
256
243
|
apply_middleware_specs(app, middleware_specs);
|
|
257
244
|
apply_route_specs(app, route_specs, fuz_auth_guard_resolver, log, deps.db);
|
|
258
|
-
//
|
|
245
|
+
// Post-route middleware (before static serving)
|
|
259
246
|
if (options.post_route_middleware) {
|
|
260
247
|
apply_middleware_specs(app, options.post_route_middleware);
|
|
261
248
|
}
|
|
262
|
-
//
|
|
249
|
+
// Static file serving
|
|
263
250
|
if (options.static_serving) {
|
|
264
251
|
const { serve_static, spa_fallback } = options.static_serving;
|
|
265
252
|
for (const mw of create_static_middleware(serve_static, { spa_fallback })) {
|
|
@@ -271,7 +258,7 @@ export const create_app_server = async (options) => {
|
|
|
271
258
|
surface_spec,
|
|
272
259
|
bootstrap_status,
|
|
273
260
|
app_settings,
|
|
274
|
-
migration_results:
|
|
261
|
+
migration_results: backend.migration_results,
|
|
275
262
|
audit_sse,
|
|
276
263
|
close: backend.close,
|
|
277
264
|
};
|
|
@@ -5,9 +5,16 @@
|
|
|
5
5
|
import type {DatatableColumn} from './datatable.js';
|
|
6
6
|
import type {AdminAccountEntryJson} from '../auth/account_schema.js';
|
|
7
7
|
import {format_relative_time, format_datetime_local} from './ui_format.js';
|
|
8
|
+
import {format_scope_context, resolve_scope_label} from './format_scope.js';
|
|
8
9
|
|
|
9
10
|
const get_rpc = admin_accounts_rpc_context.get();
|
|
10
11
|
const admin_accounts = new AdminAccountsState({get_rpc});
|
|
12
|
+
const get_format_scope = format_scope_context.get();
|
|
13
|
+
const format_scope = $derived(get_format_scope());
|
|
14
|
+
|
|
15
|
+
// `null` global label: global permits render no scope chip — the implicit default in admin tables.
|
|
16
|
+
const scope_label = (scope_id: string | null, role: string): string | null =>
|
|
17
|
+
resolve_scope_label(scope_id, role, format_scope, null);
|
|
11
18
|
|
|
12
19
|
void admin_accounts.fetch();
|
|
13
20
|
|
|
@@ -57,8 +64,14 @@
|
|
|
57
64
|
{/if}
|
|
58
65
|
{:else if column.key === 'permits'}
|
|
59
66
|
{#each row.permits as permit (permit.id)}
|
|
67
|
+
{@const scope = scope_label(permit.scope_id, permit.role)}
|
|
60
68
|
<div class="row">
|
|
61
69
|
<span class="chip color_b">{permit.role}</span>
|
|
70
|
+
{#if scope !== null}
|
|
71
|
+
<span class="text_50 font_size_sm" title={permit.scope_id ?? undefined}>
|
|
72
|
+
{scope}
|
|
73
|
+
</span>
|
|
74
|
+
{/if}
|
|
62
75
|
{#if permit.expires_at}
|
|
63
76
|
<span class="text_50 font_size_sm" title={format_datetime_local(permit.expires_at)}>
|
|
64
77
|
expires {format_relative_time(permit.expires_at)}
|
|
@@ -80,6 +93,7 @@
|
|
|
80
93
|
</div>
|
|
81
94
|
{/each}
|
|
82
95
|
{#each row.pending_offers as offer (offer.id)}
|
|
96
|
+
{@const offer_scope = scope_label(offer.scope_id, offer.role)}
|
|
83
97
|
<div class="row">
|
|
84
98
|
<span
|
|
85
99
|
class="chip"
|
|
@@ -87,6 +101,11 @@
|
|
|
87
101
|
>
|
|
88
102
|
{offer.role} (pending from @{offer.from_username})
|
|
89
103
|
</span>
|
|
104
|
+
{#if offer_scope !== null}
|
|
105
|
+
<span class="text_50 font_size_sm" title={offer.scope_id ?? undefined}>
|
|
106
|
+
{offer_scope}
|
|
107
|
+
</span>
|
|
108
|
+
{/if}
|
|
90
109
|
{#if admin_accounts.has_rpc}
|
|
91
110
|
<ConfirmButton
|
|
92
111
|
onconfirm={() => admin_accounts.retract_offer(offer.id)}
|
|
@@ -1,19 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
$$bindings?: Bindings;
|
|
4
|
-
} & Exports;
|
|
5
|
-
(internal: unknown, props: {
|
|
6
|
-
$$events?: Events;
|
|
7
|
-
$$slots?: Slots;
|
|
8
|
-
}): Exports & {
|
|
9
|
-
$set?: any;
|
|
10
|
-
$on?: any;
|
|
11
|
-
};
|
|
12
|
-
z_$$bindings?: Bindings;
|
|
13
|
-
}
|
|
14
|
-
declare const AdminAccounts: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
15
|
-
[evt: string]: CustomEvent<any>;
|
|
16
|
-
}, {}, {}, string>;
|
|
17
|
-
type AdminAccounts = InstanceType<typeof AdminAccounts>;
|
|
1
|
+
declare const AdminAccounts: import("svelte").Component<Record<string, never>, {}, "">;
|
|
2
|
+
type AdminAccounts = ReturnType<typeof AdminAccounts>;
|
|
18
3
|
export default AdminAccounts;
|
|
19
4
|
//# sourceMappingURL=AdminAccounts.svelte.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminAccounts.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminAccounts.svelte"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AdminAccounts.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminAccounts.svelte"],"names":[],"mappings":"AAoJA,QAAA,MAAM,aAAa,2DAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
|
|
@@ -4,19 +4,28 @@
|
|
|
4
4
|
import Datatable from './Datatable.svelte';
|
|
5
5
|
import type {DatatableColumn} from './datatable.js';
|
|
6
6
|
import type {PermitHistoryEventJson} from '../auth/audit_log_schema.js';
|
|
7
|
+
import {format_scope_context, resolve_scope_label} from './format_scope.js';
|
|
7
8
|
|
|
8
9
|
const get_rpc = audit_log_rpc_context.get();
|
|
9
10
|
const audit_log = new AuditLogState({get_rpc});
|
|
11
|
+
const get_format_scope = format_scope_context.get();
|
|
12
|
+
const format_scope = $derived(get_format_scope());
|
|
10
13
|
|
|
11
14
|
void audit_log.fetch_permit_history();
|
|
12
15
|
|
|
13
16
|
const columns: Array<DatatableColumn<PermitHistoryEventJson>> = [
|
|
14
17
|
{key: 'event_type', label: 'action', width: 100},
|
|
15
|
-
{key: 'metadata', label: 'role', width:
|
|
18
|
+
{key: 'metadata', label: 'role', width: 160},
|
|
16
19
|
{key: 'username', label: 'by', width: 140},
|
|
17
20
|
{key: 'target_username', label: 'target', width: 140},
|
|
18
21
|
{key: 'created_at', label: 'time', width: 100},
|
|
19
22
|
];
|
|
23
|
+
|
|
24
|
+
// Metadata is `Record<string, unknown>`; narrow before reusing `resolve_scope_label`.
|
|
25
|
+
const scope_label_from_metadata = (scope_id: unknown, role: string): string | null => {
|
|
26
|
+
if (typeof scope_id !== 'string' || scope_id === '') return null;
|
|
27
|
+
return resolve_scope_label(scope_id, role, format_scope, null);
|
|
28
|
+
};
|
|
20
29
|
</script>
|
|
21
30
|
|
|
22
31
|
<section>
|
|
@@ -39,7 +48,19 @@
|
|
|
39
48
|
</span>
|
|
40
49
|
{:else if column.key === 'metadata'}
|
|
41
50
|
{#if row.metadata}
|
|
42
|
-
|
|
51
|
+
{@const role = typeof row.metadata.role === 'string' ? row.metadata.role : ''}
|
|
52
|
+
<code>{role}</code>
|
|
53
|
+
{@const scope = scope_label_from_metadata(row.metadata.scope_id, role)}
|
|
54
|
+
{#if scope !== null}
|
|
55
|
+
<span
|
|
56
|
+
class="text_50 font_size_sm"
|
|
57
|
+
title={typeof row.metadata.scope_id === 'string'
|
|
58
|
+
? row.metadata.scope_id
|
|
59
|
+
: undefined}
|
|
60
|
+
>
|
|
61
|
+
{scope}
|
|
62
|
+
</span>
|
|
63
|
+
{/if}
|
|
43
64
|
{/if}
|
|
44
65
|
{:else if column.key === 'username'}
|
|
45
66
|
<span class="text_50">{row.username ?? truncate_uuid(row.account_id ?? '?')}</span>
|
|
@@ -1,19 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
$$bindings?: Bindings;
|
|
4
|
-
} & Exports;
|
|
5
|
-
(internal: unknown, props: {
|
|
6
|
-
$$events?: Events;
|
|
7
|
-
$$slots?: Slots;
|
|
8
|
-
}): Exports & {
|
|
9
|
-
$set?: any;
|
|
10
|
-
$on?: any;
|
|
11
|
-
};
|
|
12
|
-
z_$$bindings?: Bindings;
|
|
13
|
-
}
|
|
14
|
-
declare const AdminPermitHistory: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
15
|
-
[evt: string]: CustomEvent<any>;
|
|
16
|
-
}, {}, {}, string>;
|
|
17
|
-
type AdminPermitHistory = InstanceType<typeof AdminPermitHistory>;
|
|
1
|
+
declare const AdminPermitHistory: import("svelte").Component<Record<string, never>, {}, "">;
|
|
2
|
+
type AdminPermitHistory = ReturnType<typeof AdminPermitHistory>;
|
|
18
3
|
export default AdminPermitHistory;
|
|
19
4
|
//# sourceMappingURL=AdminPermitHistory.svelte.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminPermitHistory.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminPermitHistory.svelte"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AdminPermitHistory.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminPermitHistory.svelte"],"names":[],"mappings":"AAuFA,QAAA,MAAM,kBAAkB,2DAAwC,CAAC;AACjE,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChE,eAAe,kBAAkB,CAAC"}
|
package/dist/ui/CLAUDE.md
CHANGED
|
@@ -303,6 +303,17 @@ provisioner pattern.
|
|
|
303
303
|
directly. Consumed by `PermitOfferInbox`, `PermitOfferForm`,
|
|
304
304
|
`PermitOfferHistory`. Wiring is ctor-bound (RPC + account/actor
|
|
305
305
|
getters), so there's no separate `permit_offers_rpc_context`.
|
|
306
|
+
- `format_scope_context` — `() => FormatScope` (getter shape, matching
|
|
307
|
+
the RPC contexts above). `FormatScope = ({scope_id, role}) => string |
|
|
308
|
+
null`; default returns `null` so callers fall back to the raw uuid.
|
|
309
|
+
Provisioned by `provide_admin_rpc_contexts(adapters, {format_scope})`.
|
|
310
|
+
Consumed by `AdminAccounts`, `AdminPermitHistory`, `PermitOfferInbox`,
|
|
311
|
+
`PermitOfferHistory` via the `resolve_scope_label(scope_id, role,
|
|
312
|
+
format_scope, global_label)` helper — `global_label = null` renders no
|
|
313
|
+
chip (admin tables); `'global'` renders an explicit label (offer
|
|
314
|
+
surfaces). `PermitOfferInbox` / `PermitOfferHistory` accept a
|
|
315
|
+
`format_scope?: FormatScope` prop — same shape as the context, prop
|
|
316
|
+
wins when supplied.
|
|
306
317
|
- `sidebar_state_context` — `() => SidebarState`. Provisioned by
|
|
307
318
|
`AppShell`.
|
|
308
319
|
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import type {DatatableColumn} from './datatable.js';
|
|
17
17
|
import {format_relative_time, format_datetime_local, truncate_uuid} from './ui_format.js';
|
|
18
18
|
import type {PermitOfferJson} from '../auth/permit_offer_schema.js';
|
|
19
|
+
import {format_scope_context, resolve_scope_label, type FormatScope} from './format_scope.js';
|
|
19
20
|
|
|
20
21
|
const {
|
|
21
22
|
current_actor_id,
|
|
@@ -26,11 +27,18 @@
|
|
|
26
27
|
/** Used to label a row as sent vs received. When `null`, direction shows as `-`. */
|
|
27
28
|
current_actor_id: string | null;
|
|
28
29
|
format_actor?: (from_actor_id: string) => string;
|
|
29
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Display label for an offer's scope. Bypasses `format_scope_context`
|
|
32
|
+
* when supplied — return `null` to fall back to a truncated uuid (or
|
|
33
|
+
* `'global'` for null scope_id). Omit to use the context value directly.
|
|
34
|
+
*/
|
|
35
|
+
format_scope?: FormatScope;
|
|
30
36
|
format_role?: (role: string) => string;
|
|
31
37
|
} = $props();
|
|
32
38
|
|
|
33
39
|
const permit_offers = permit_offers_state_context.get();
|
|
40
|
+
const get_format_scope = format_scope_context.get();
|
|
41
|
+
const format_scope_from_context = $derived(get_format_scope());
|
|
34
42
|
|
|
35
43
|
const now = $state.raw(Date.now());
|
|
36
44
|
|
|
@@ -59,10 +67,8 @@
|
|
|
59
67
|
}
|
|
60
68
|
};
|
|
61
69
|
|
|
62
|
-
const scope_label = (scope_id: string | null, role: string): string =>
|
|
63
|
-
|
|
64
|
-
return scope_id === null ? 'global' : truncate_uuid(scope_id);
|
|
65
|
-
};
|
|
70
|
+
const scope_label = (scope_id: string | null, role: string): string =>
|
|
71
|
+
resolve_scope_label(scope_id, role, format_scope ?? format_scope_from_context, 'global');
|
|
66
72
|
|
|
67
73
|
const columns: Array<DatatableColumn<PermitOfferJson>> = [
|
|
68
74
|
{key: 'from_actor_id', label: 'direction', width: 110},
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
import { type FormatScope } from './format_scope.js';
|
|
1
2
|
type $$ComponentProps = {
|
|
2
3
|
/** Used to label a row as sent vs received. When `null`, direction shows as `-`. */
|
|
3
4
|
current_actor_id: string | null;
|
|
4
5
|
format_actor?: (from_actor_id: string) => string;
|
|
5
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Display label for an offer's scope. Bypasses `format_scope_context`
|
|
8
|
+
* when supplied — return `null` to fall back to a truncated uuid (or
|
|
9
|
+
* `'global'` for null scope_id). Omit to use the context value directly.
|
|
10
|
+
*/
|
|
11
|
+
format_scope?: FormatScope;
|
|
6
12
|
format_role?: (role: string) => string;
|
|
7
13
|
};
|
|
8
14
|
declare const PermitOfferHistory: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PermitOfferHistory.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/PermitOfferHistory.svelte"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PermitOfferHistory.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/PermitOfferHistory.svelte"],"names":[],"mappings":"AAmBA,OAAO,EAA4C,KAAK,WAAW,EAAC,MAAM,mBAAmB,CAAC;AAE7F,KAAK,gBAAgB,GAAI;IACxB,oFAAoF;IACpF,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,YAAY,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,MAAM,CAAC;IACjD;;;;OAIG;IACH,YAAY,CAAC,EAAE,WAAW,CAAC;IAC3B,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACvC,CAAC;AAkGH,QAAA,MAAM,kBAAkB,sDAAwC,CAAC;AACjE,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChE,eAAe,kBAAkB,CAAC"}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import ConfirmButton from './ConfirmButton.svelte';
|
|
17
17
|
import {format_relative_time, format_datetime_local, truncate_uuid} from './ui_format.js';
|
|
18
18
|
import {PERMIT_OFFER_MESSAGE_LENGTH_MAX} from '../auth/permit_offer_schema.js';
|
|
19
|
+
import {format_scope_context, resolve_scope_label, type FormatScope} from './format_scope.js';
|
|
19
20
|
|
|
20
21
|
const {
|
|
21
22
|
format_actor = truncate_uuid,
|
|
@@ -24,18 +25,22 @@
|
|
|
24
25
|
}: {
|
|
25
26
|
/** Display label for `from_actor_id`. Defaults to a truncated uuid. */
|
|
26
27
|
format_actor?: (from_actor_id: string) => string;
|
|
27
|
-
/**
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Display label for an offer's scope. Bypasses `format_scope_context`
|
|
30
|
+
* when supplied — return `null` to fall back to a truncated uuid (or
|
|
31
|
+
* `'global'` for null scope_id). Omit to use the context value directly.
|
|
32
|
+
*/
|
|
33
|
+
format_scope?: FormatScope;
|
|
34
|
+
/** Display label for a role constant. Defaults to identity. */
|
|
30
35
|
format_role?: (role: string) => string;
|
|
31
36
|
} = $props();
|
|
32
37
|
|
|
33
38
|
const permit_offers = permit_offers_state_context.get();
|
|
39
|
+
const get_format_scope = format_scope_context.get();
|
|
40
|
+
const format_scope_from_context = $derived(get_format_scope());
|
|
34
41
|
|
|
35
|
-
const scope_label = (scope_id: string | null, role: string): string =>
|
|
36
|
-
|
|
37
|
-
return scope_id === null ? 'global' : truncate_uuid(scope_id);
|
|
38
|
-
};
|
|
42
|
+
const scope_label = (scope_id: string | null, role: string): string =>
|
|
43
|
+
resolve_scope_label(scope_id, role, format_scope ?? format_scope_from_context, 'global');
|
|
39
44
|
|
|
40
45
|
const decline_reasons: SvelteMap<string, string> = new SvelteMap();
|
|
41
46
|
</script>
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
import { type FormatScope } from './format_scope.js';
|
|
1
2
|
type $$ComponentProps = {
|
|
2
3
|
/** Display label for `from_actor_id`. Defaults to a truncated uuid. */
|
|
3
4
|
format_actor?: (from_actor_id: string) => string;
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Display label for an offer's scope. Bypasses `format_scope_context`
|
|
7
|
+
* when supplied — return `null` to fall back to a truncated uuid (or
|
|
8
|
+
* `'global'` for null scope_id). Omit to use the context value directly.
|
|
9
|
+
*/
|
|
10
|
+
format_scope?: FormatScope;
|
|
11
|
+
/** Display label for a role constant. Defaults to identity. */
|
|
7
12
|
format_role?: (role: string) => string;
|
|
8
13
|
};
|
|
9
14
|
declare const PermitOfferInbox: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PermitOfferInbox.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/PermitOfferInbox.svelte"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PermitOfferInbox.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/PermitOfferInbox.svelte"],"names":[],"mappings":"AAmBA,OAAO,EAA4C,KAAK,WAAW,EAAC,MAAM,mBAAmB,CAAC;AAE7F,KAAK,gBAAgB,GAAI;IACxB,uEAAuE;IACvE,YAAY,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,MAAM,CAAC;IACjD;;;;OAIG;IACH,YAAY,CAAC,EAAE,WAAW,CAAC;IAC3B,+DAA+D;IAC/D,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACvC,CAAC;AA2FH,QAAA,MAAM,gBAAgB,sDAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
|
|
@@ -39,6 +39,7 @@ import { type AdminAccountsRpc } from './admin_accounts_state.svelte.js';
|
|
|
39
39
|
import { type AdminInvitesRpc } from './admin_invites_state.svelte.js';
|
|
40
40
|
import { type AuditLogRpc } from './audit_log_state.svelte.js';
|
|
41
41
|
import { type AppSettingsRpc } from './app_settings_state.svelte.js';
|
|
42
|
+
import { type FormatScope } from './format_scope.js';
|
|
42
43
|
/**
|
|
43
44
|
* Function-shaped contract for dispatching an RPC call by method name.
|
|
44
45
|
*
|
|
@@ -87,6 +88,14 @@ export interface AdminRpcAdapters {
|
|
|
87
88
|
* admin surfaces they mount.
|
|
88
89
|
*/
|
|
89
90
|
export declare const create_admin_rpc_adapters: (rpc_call: AdminRpcCall) => AdminRpcAdapters;
|
|
91
|
+
/** Optional knobs alongside the adapters when wiring admin contexts. */
|
|
92
|
+
export interface ProvideAdminRpcContextsOptions {
|
|
93
|
+
/**
|
|
94
|
+
* Render `{scope_id, role}` as a human label across permit-display
|
|
95
|
+
* components. Omit (or return `null`) to fall back to the raw uuid.
|
|
96
|
+
*/
|
|
97
|
+
format_scope?: FormatScope;
|
|
98
|
+
}
|
|
90
99
|
/**
|
|
91
100
|
* Wire all four admin RPC contexts in a single call.
|
|
92
101
|
*
|
|
@@ -98,6 +107,12 @@ export declare const create_admin_rpc_adapters: (rpc_call: AdminRpcCall) => Admi
|
|
|
98
107
|
* mutating an adapter field on the same object propagates. Replacing the
|
|
99
108
|
* whole adapter set requires calling `provide_admin_rpc_contexts` again
|
|
100
109
|
* during init — in practice this is one-shot at layout mount.
|
|
110
|
+
*
|
|
111
|
+
* Pass `options.format_scope` to render permit/offer `scope_id` values as
|
|
112
|
+
* human labels across `AdminAccounts`, `AdminPermitHistory`,
|
|
113
|
+
* `PermitOfferInbox`, `PermitOfferForm`, and `PermitOfferHistory`.
|
|
114
|
+
* Components that accept a `format_scope` prop honor the prop first; the
|
|
115
|
+
* context is the fallback.
|
|
101
116
|
*/
|
|
102
|
-
export declare const provide_admin_rpc_contexts: (adapters: AdminRpcAdapters) => void;
|
|
117
|
+
export declare const provide_admin_rpc_contexts: (adapters: AdminRpcAdapters, options?: ProvideAdminRpcContextsOptions) => void;
|
|
103
118
|
//# sourceMappingURL=admin_rpc_adapters.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin_rpc_adapters.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/admin_rpc_adapters.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;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;
|
|
1
|
+
{"version":3,"file":"admin_rpc_adapters.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/admin_rpc_adapters.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;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;AAC7F,OAAO,EAAuB,KAAK,WAAW,EAAC,MAAM,mBAAmB,CAAC;AAEzE;;;;;;;;;;;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,wEAAwE;AACxE,MAAM,WAAW,8BAA8B;IAC9C;;;OAGG;IACH,YAAY,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,0BAA0B,GACtC,UAAU,gBAAgB,EAC1B,UAAU,8BAA8B,KACtC,IASF,CAAC"}
|
|
@@ -38,6 +38,7 @@ import { admin_accounts_rpc_context } from './admin_accounts_state.svelte.js';
|
|
|
38
38
|
import { admin_invites_rpc_context } from './admin_invites_state.svelte.js';
|
|
39
39
|
import { audit_log_rpc_context } from './audit_log_state.svelte.js';
|
|
40
40
|
import { app_settings_rpc_context } from './app_settings_state.svelte.js';
|
|
41
|
+
import { format_scope_context } from './format_scope.js';
|
|
41
42
|
/**
|
|
42
43
|
* Build the four admin RPC adapters from a single typed `rpc_call`.
|
|
43
44
|
*
|
|
@@ -100,10 +101,20 @@ export const create_admin_rpc_adapters = (rpc_call) => ({
|
|
|
100
101
|
* mutating an adapter field on the same object propagates. Replacing the
|
|
101
102
|
* whole adapter set requires calling `provide_admin_rpc_contexts` again
|
|
102
103
|
* during init — in practice this is one-shot at layout mount.
|
|
104
|
+
*
|
|
105
|
+
* Pass `options.format_scope` to render permit/offer `scope_id` values as
|
|
106
|
+
* human labels across `AdminAccounts`, `AdminPermitHistory`,
|
|
107
|
+
* `PermitOfferInbox`, `PermitOfferForm`, and `PermitOfferHistory`.
|
|
108
|
+
* Components that accept a `format_scope` prop honor the prop first; the
|
|
109
|
+
* context is the fallback.
|
|
103
110
|
*/
|
|
104
|
-
export const provide_admin_rpc_contexts = (adapters) => {
|
|
111
|
+
export const provide_admin_rpc_contexts = (adapters, options) => {
|
|
105
112
|
admin_accounts_rpc_context.set(() => adapters.admin_accounts);
|
|
106
113
|
admin_invites_rpc_context.set(() => adapters.admin_invites);
|
|
107
114
|
audit_log_rpc_context.set(() => adapters.audit_log);
|
|
108
115
|
app_settings_rpc_context.set(() => adapters.app_settings);
|
|
116
|
+
if (options?.format_scope) {
|
|
117
|
+
const { format_scope } = options;
|
|
118
|
+
format_scope_context.set(() => format_scope);
|
|
119
|
+
}
|
|
109
120
|
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared `format_scope` callback contract for permit-display components.
|
|
3
|
+
*
|
|
4
|
+
* Permits and offers carry a `scope_id` that names a consumer-owned resource
|
|
5
|
+
* (e.g. a classroom uuid). The default render is the raw uuid. Consumers wire
|
|
6
|
+
* a `FormatScope` via context to render a human label without per-page
|
|
7
|
+
* lookup or forking the components.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Render a `{scope_id, role}` pair as a human label. Return `null` to fall
|
|
13
|
+
* back to the raw scope uuid (or a caller-chosen `global_label` when
|
|
14
|
+
* `scope_id` is `null`).
|
|
15
|
+
*
|
|
16
|
+
* Returning `null` for unknown scope ids (stale cache, revoked resource) is
|
|
17
|
+
* the recommended pattern — components show the raw uuid rather than a
|
|
18
|
+
* misleading blank.
|
|
19
|
+
*/
|
|
20
|
+
export type FormatScope = (args: {
|
|
21
|
+
scope_id: string | null;
|
|
22
|
+
role: string;
|
|
23
|
+
}) => string | null;
|
|
24
|
+
/** Default `FormatScope` — always returns `null` so callers fall back to the raw uuid. */
|
|
25
|
+
export declare const default_format_scope: FormatScope;
|
|
26
|
+
/**
|
|
27
|
+
* Svelte context carrying a getter for the consumer's `FormatScope`.
|
|
28
|
+
* Provisioned by `provide_admin_rpc_contexts` from its `format_scope` option.
|
|
29
|
+
* Default getter returns `default_format_scope` so unprovisioned trees render
|
|
30
|
+
* the raw uuid.
|
|
31
|
+
*/
|
|
32
|
+
export declare const format_scope_context: {
|
|
33
|
+
get: () => () => FormatScope;
|
|
34
|
+
set: (value?: (() => FormatScope) | undefined) => () => FormatScope;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Resolve a scope label across the context → raw-uuid fallback chain.
|
|
38
|
+
*
|
|
39
|
+
* `global_label` is returned for `scope_id === null`. Callers pass `null`
|
|
40
|
+
* to render no chip (admin tables — global is the implicit default) or
|
|
41
|
+
* `'global'` for explicit labels (offer surfaces). The return type
|
|
42
|
+
* propagates `null` only when `global_label` is `null`.
|
|
43
|
+
*/
|
|
44
|
+
export declare const resolve_scope_label: <G extends string | null>(scope_id: string | null, role: string, format_scope: FormatScope, global_label: G) => string | G;
|
|
45
|
+
//# sourceMappingURL=format_scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format_scope.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/format_scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH;;;;;;;;GAQG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAC,KAAK,MAAM,GAAG,IAAI,CAAC;AAE3F,0FAA0F;AAC1F,eAAO,MAAM,oBAAoB,EAAE,WAAwB,CAAC;AAE5D;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB;qBAAwB,WAAW;yBAAX,WAAW,wBAAX,WAAW;CAEnE,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,GAAI,CAAC,SAAS,MAAM,GAAG,IAAI,EAC1D,UAAU,MAAM,GAAG,IAAI,EACvB,MAAM,MAAM,EACZ,cAAc,WAAW,EACzB,cAAc,CAAC,KACb,MAAM,GAAG,CAGX,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared `format_scope` callback contract for permit-display components.
|
|
3
|
+
*
|
|
4
|
+
* Permits and offers carry a `scope_id` that names a consumer-owned resource
|
|
5
|
+
* (e.g. a classroom uuid). The default render is the raw uuid. Consumers wire
|
|
6
|
+
* a `FormatScope` via context to render a human label without per-page
|
|
7
|
+
* lookup or forking the components.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
import { create_context } from '@fuzdev/fuz_ui/context_helpers.js';
|
|
12
|
+
import { truncate_uuid } from './ui_format.js';
|
|
13
|
+
/** Default `FormatScope` — always returns `null` so callers fall back to the raw uuid. */
|
|
14
|
+
export const default_format_scope = () => null;
|
|
15
|
+
/**
|
|
16
|
+
* Svelte context carrying a getter for the consumer's `FormatScope`.
|
|
17
|
+
* Provisioned by `provide_admin_rpc_contexts` from its `format_scope` option.
|
|
18
|
+
* Default getter returns `default_format_scope` so unprovisioned trees render
|
|
19
|
+
* the raw uuid.
|
|
20
|
+
*/
|
|
21
|
+
export const format_scope_context = create_context(() => () => default_format_scope);
|
|
22
|
+
/**
|
|
23
|
+
* Resolve a scope label across the context → raw-uuid fallback chain.
|
|
24
|
+
*
|
|
25
|
+
* `global_label` is returned for `scope_id === null`. Callers pass `null`
|
|
26
|
+
* to render no chip (admin tables — global is the implicit default) or
|
|
27
|
+
* `'global'` for explicit labels (offer surfaces). The return type
|
|
28
|
+
* propagates `null` only when `global_label` is `null`.
|
|
29
|
+
*/
|
|
30
|
+
export const resolve_scope_label = (scope_id, role, format_scope, global_label) => {
|
|
31
|
+
if (scope_id === null)
|
|
32
|
+
return global_label;
|
|
33
|
+
return format_scope({ scope_id, role }) ?? truncate_uuid(scope_id);
|
|
34
|
+
};
|
package/dist/ui/ui_format.d.ts
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module
|
|
8
8
|
*/
|
|
9
|
-
import type { AuditEventType } from '../auth/audit_log_schema.js';
|
|
10
9
|
/**
|
|
11
10
|
* Format a timestamp as a relative time string.
|
|
12
11
|
*
|
|
@@ -55,9 +54,9 @@ export declare const format_datetime_local: (timestamp: string | number | Date)
|
|
|
55
54
|
/**
|
|
56
55
|
* Format audit event metadata for display based on event type.
|
|
57
56
|
*
|
|
58
|
-
* @param event_type - the audit event type
|
|
57
|
+
* @param event_type - the audit event type (builtin or consumer-registered)
|
|
59
58
|
* @param metadata - the metadata object (may be null)
|
|
60
59
|
* @returns human-readable summary string
|
|
61
60
|
*/
|
|
62
|
-
export declare const format_audit_metadata: (event_type:
|
|
61
|
+
export declare const format_audit_metadata: (event_type: string, metadata: Record<string, unknown> | null) => string;
|
|
63
62
|
//# sourceMappingURL=ui_format.d.ts.map
|