@fuzdev/fuz_app 0.67.0 → 0.68.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 +99 -5
- package/dist/auth/account_queries.d.ts +87 -4
- package/dist/auth/account_queries.d.ts.map +1 -1
- package/dist/auth/account_queries.js +107 -17
- package/dist/auth/account_schema.d.ts +19 -0
- package/dist/auth/account_schema.d.ts.map +1 -1
- package/dist/auth/account_schema.js +8 -0
- package/dist/auth/admin_action_specs.d.ts +168 -0
- package/dist/auth/admin_action_specs.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.js +146 -1
- package/dist/auth/admin_actions.d.ts.map +1 -1
- package/dist/auth/admin_actions.js +218 -4
- package/dist/auth/audit_log_ddl.d.ts +10 -1
- package/dist/auth/audit_log_ddl.d.ts.map +1 -1
- package/dist/auth/audit_log_ddl.js +13 -4
- package/dist/auth/audit_log_schema.d.ts +34 -1
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +73 -0
- package/dist/auth/auth_ddl.d.ts +2 -2
- package/dist/auth/auth_ddl.d.ts.map +1 -1
- package/dist/auth/auth_ddl.js +10 -2
- package/dist/auth/cell_action_specs.d.ts +1295 -0
- package/dist/auth/cell_action_specs.d.ts.map +1 -0
- package/dist/auth/cell_action_specs.js +397 -0
- package/dist/auth/cell_actions.d.ts +63 -0
- package/dist/auth/cell_actions.d.ts.map +1 -0
- package/dist/auth/cell_actions.js +546 -0
- package/dist/auth/cell_audit_action_specs.d.ts +131 -0
- package/dist/auth/cell_audit_action_specs.d.ts.map +1 -0
- package/dist/auth/cell_audit_action_specs.js +70 -0
- package/dist/auth/cell_audit_actions.d.ts +18 -0
- package/dist/auth/cell_audit_actions.d.ts.map +1 -0
- package/dist/auth/cell_audit_actions.js +59 -0
- package/dist/auth/cell_audit_events.d.ts +28 -0
- package/dist/auth/cell_audit_events.d.ts.map +1 -0
- package/dist/auth/cell_audit_events.js +42 -0
- package/dist/auth/cell_audit_metadata.d.ts +48 -0
- package/dist/auth/cell_audit_metadata.d.ts.map +1 -0
- package/dist/auth/cell_audit_metadata.js +46 -0
- package/dist/auth/cell_authorize.d.ts +88 -0
- package/dist/auth/cell_authorize.d.ts.map +1 -0
- package/dist/auth/cell_authorize.js +172 -0
- package/dist/auth/cell_data_schema.d.ts +44 -0
- package/dist/auth/cell_data_schema.d.ts.map +1 -0
- package/dist/auth/cell_data_schema.js +42 -0
- package/dist/auth/cell_field_action_specs.d.ts +244 -0
- package/dist/auth/cell_field_action_specs.d.ts.map +1 -0
- package/dist/auth/cell_field_action_specs.js +136 -0
- package/dist/auth/cell_field_actions.d.ts +34 -0
- package/dist/auth/cell_field_actions.d.ts.map +1 -0
- package/dist/auth/cell_field_actions.js +153 -0
- package/dist/auth/cell_field_audit_metadata.d.ts +30 -0
- package/dist/auth/cell_field_audit_metadata.d.ts.map +1 -0
- package/dist/auth/cell_field_audit_metadata.js +28 -0
- package/dist/auth/cell_grant_action_specs.d.ts +333 -0
- package/dist/auth/cell_grant_action_specs.d.ts.map +1 -0
- package/dist/auth/cell_grant_action_specs.js +148 -0
- package/dist/auth/cell_grant_actions.d.ts +50 -0
- package/dist/auth/cell_grant_actions.d.ts.map +1 -0
- package/dist/auth/cell_grant_actions.js +208 -0
- package/dist/auth/cell_grant_audit_metadata.d.ts +75 -0
- package/dist/auth/cell_grant_audit_metadata.d.ts.map +1 -0
- package/dist/auth/cell_grant_audit_metadata.js +54 -0
- package/dist/auth/cell_item_action_specs.d.ts +331 -0
- package/dist/auth/cell_item_action_specs.d.ts.map +1 -0
- package/dist/auth/cell_item_action_specs.js +182 -0
- package/dist/auth/cell_item_actions.d.ts +37 -0
- package/dist/auth/cell_item_actions.d.ts.map +1 -0
- package/dist/auth/cell_item_actions.js +204 -0
- package/dist/auth/cell_item_audit_metadata.d.ts +35 -0
- package/dist/auth/cell_item_audit_metadata.d.ts.map +1 -0
- package/dist/auth/cell_item_audit_metadata.js +32 -0
- package/dist/auth/cell_relation_visibility.d.ts +32 -0
- package/dist/auth/cell_relation_visibility.d.ts.map +1 -0
- package/dist/auth/cell_relation_visibility.js +57 -0
- package/dist/auth/deps.d.ts +9 -0
- package/dist/auth/deps.d.ts.map +1 -1
- package/dist/auth/role_grant_queries.d.ts +30 -0
- package/dist/auth/role_grant_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_queries.js +54 -0
- package/dist/db/CLAUDE.md +118 -0
- package/dist/db/cell_audit_queries.d.ts +26 -0
- package/dist/db/cell_audit_queries.d.ts.map +1 -0
- package/dist/db/cell_audit_queries.js +53 -0
- package/dist/db/cell_ddl.d.ts +151 -0
- package/dist/db/cell_ddl.d.ts.map +1 -0
- package/dist/db/cell_ddl.js +247 -0
- package/dist/db/cell_field_queries.d.ts +105 -0
- package/dist/db/cell_field_queries.d.ts.map +1 -0
- package/dist/db/cell_field_queries.js +113 -0
- package/dist/db/cell_grant_queries.d.ts +132 -0
- package/dist/db/cell_grant_queries.d.ts.map +1 -0
- package/dist/db/cell_grant_queries.js +145 -0
- package/dist/db/cell_history_ddl.d.ts +38 -0
- package/dist/db/cell_history_ddl.d.ts.map +1 -0
- package/dist/db/cell_history_ddl.js +61 -0
- package/dist/db/cell_item_queries.d.ts +107 -0
- package/dist/db/cell_item_queries.d.ts.map +1 -0
- package/dist/db/cell_item_queries.js +119 -0
- package/dist/db/cell_queries.d.ts +327 -0
- package/dist/db/cell_queries.d.ts.map +1 -0
- package/dist/db/cell_queries.js +431 -0
- package/dist/db/fact_ddl.d.ts +38 -0
- package/dist/db/fact_ddl.d.ts.map +1 -0
- package/dist/db/fact_ddl.js +71 -0
- package/dist/db/fact_queries.d.ts +140 -0
- package/dist/db/fact_queries.d.ts.map +1 -0
- package/dist/db/fact_queries.js +161 -0
- package/dist/db/fact_store.d.ts +112 -0
- package/dist/db/fact_store.d.ts.map +1 -0
- package/dist/db/fact_store.js +225 -0
- package/dist/server/env.d.ts +2 -0
- package/dist/server/env.d.ts.map +1 -1
- package/dist/server/env.js +6 -0
- package/dist/server/fact_write.d.ts +32 -0
- package/dist/server/fact_write.d.ts.map +1 -0
- package/dist/server/fact_write.js +56 -0
- package/dist/server/file_fact_fetcher.d.ts +42 -0
- package/dist/server/file_fact_fetcher.d.ts.map +1 -0
- package/dist/server/file_fact_fetcher.js +60 -0
- package/dist/server/file_fact_url.d.ts +53 -0
- package/dist/server/file_fact_url.d.ts.map +1 -0
- package/dist/server/file_fact_url.js +52 -0
- package/dist/server/serve_fact_route.d.ts +78 -0
- package/dist/server/serve_fact_route.d.ts.map +1 -0
- package/dist/server/serve_fact_route.js +205 -0
- package/dist/testing/CLAUDE.md +58 -5
- package/dist/testing/app_server.d.ts +12 -0
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +36 -2
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +67 -1
- package/dist/testing/cross_backend/account_lifecycle.d.ts +10 -0
- package/dist/testing/cross_backend/account_lifecycle.d.ts.map +1 -0
- package/dist/testing/cross_backend/account_lifecycle.js +76 -0
- package/dist/testing/cross_backend/capabilities.d.ts +31 -0
- package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
- package/dist/testing/cross_backend/capabilities.js +3 -0
- package/dist/testing/cross_backend/cell_cross_helpers.d.ts +39 -0
- package/dist/testing/cross_backend/cell_cross_helpers.d.ts.map +1 -0
- package/dist/testing/cross_backend/cell_cross_helpers.js +45 -0
- package/dist/testing/cross_backend/cell_crud.d.ts +4 -0
- package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -0
- package/dist/testing/cross_backend/cell_crud.js +168 -0
- package/dist/testing/cross_backend/cell_relations.d.ts +4 -0
- package/dist/testing/cross_backend/cell_relations.d.ts.map +1 -0
- package/dist/testing/cross_backend/cell_relations.js +229 -0
- package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_backend_configs.js +6 -0
- package/dist/testing/cross_backend/setup.d.ts.map +1 -1
- package/dist/testing/cross_backend/setup.js +5 -0
- package/dist/testing/cross_backend/spawn_backend.d.ts.map +1 -1
- package/dist/testing/cross_backend/spawn_backend.js +31 -3
- package/dist/testing/cross_backend/testing_server_bun.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_server_bun.js +29 -2
- package/dist/testing/entities.d.ts.map +1 -1
- package/dist/testing/entities.js +4 -0
- package/dist/testing/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/ws_round_trip.js +4 -0
- package/dist/ui/AdminAccounts.svelte +58 -0
- package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -1
- package/dist/ui/admin_accounts_state.svelte.d.ts +30 -2
- package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_accounts_state.svelte.js +45 -1
- package/dist/ui/admin_rpc_adapters.d.ts +6 -2
- package/dist/ui/admin_rpc_adapters.d.ts.map +1 -1
- package/dist/ui/admin_rpc_adapters.js +5 -1
- package/package.json +4 -2
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Dedicated cell relation / ACL / audit parity suite for the cross-backend
|
|
4
|
+
* harness — the sibling of `describe_cell_crud_cross_tests` covering every
|
|
5
|
+
* cell verb beyond plain CRUD: `cell_grant_*`, `cell_field_*`,
|
|
6
|
+
* `cell_item_*`, `cell_clone`, and `cell_audit_list`.
|
|
7
|
+
*
|
|
8
|
+
* Like the CRUD suite (and ws / sse), these verbs are live-mounted on the
|
|
9
|
+
* spine RPC path but stay off the standard declared surface, so the generic
|
|
10
|
+
* `describe_rpc_round_trip_tests` never drives them. Every success response
|
|
11
|
+
* is parsed against the verb's declared Zod **output** schema, so a TS↔Rust
|
|
12
|
+
* envelope drift fails the suite — not just a payload-field drift.
|
|
13
|
+
*
|
|
14
|
+
* Coverage (gated on `capabilities.cell_relations`):
|
|
15
|
+
*
|
|
16
|
+
* - **grant lifecycle** — owner grants an actor-shaped editor; the grantee
|
|
17
|
+
* gains edit (was 404 before the grant); `cell_grant_list` is manage-tier
|
|
18
|
+
* (owner sees it, the editor gets the IDOR 404); revoke drops the edit path.
|
|
19
|
+
* - **`cell_visibility_manage_only`** — the editor-grant holder can edit
|
|
20
|
+
* content but flipping `visibility` is 403 (the case the 5-verb CRUD cut
|
|
21
|
+
* couldn't reach without a grant principal).
|
|
22
|
+
* - **fields** — `cell_field_set` UPSERT, forward + reverse `cell_field_list`,
|
|
23
|
+
* idempotent `cell_field_delete`.
|
|
24
|
+
* - **items** — `cell_item_insert` at fractional-index positions, forward
|
|
25
|
+
* (lex-ordered) + reverse `cell_item_list`, `cell_item_move`, idempotent
|
|
26
|
+
* `cell_item_delete`.
|
|
27
|
+
* - **clone** — shallow copies item / field *edges* (shared `child_id` /
|
|
28
|
+
* `target_id`); deep clones each viewable child into a fresh cell at the
|
|
29
|
+
* same position. Both null `path` and stamp the caller as owner.
|
|
30
|
+
* - **audit** — `cell_audit_list` is manage-tier: the owner reads the cell's
|
|
31
|
+
* timeline; a viewer-grant holder who can `cell_get` the cell still gets the
|
|
32
|
+
* IDOR 404 on the timeline (D14).
|
|
33
|
+
*
|
|
34
|
+
* Only **actor-shaped** grants are exercised — role-shaped principals need a
|
|
35
|
+
* closed role registry the Rust spine deliberately lacks, so role-grant
|
|
36
|
+
* parity is out of scope here (the TS impl covers it in-process).
|
|
37
|
+
*
|
|
38
|
+
* `$lib`-free by contract (relative specifiers only) so the suite can be
|
|
39
|
+
* imported from the spawnable cross-process test files.
|
|
40
|
+
*
|
|
41
|
+
* @module
|
|
42
|
+
*/
|
|
43
|
+
import { describe, assert } from 'vitest';
|
|
44
|
+
import { fractional_index_between } from '@fuzdev/fuz_util/fractional_index.js';
|
|
45
|
+
import { CellCreateOutput, CellUpdateOutput, CellCloneOutput } from '../../auth/cell_action_specs.js';
|
|
46
|
+
import { CellGrantCreateOutput, CellGrantListOutput, CellGrantRevokeOutput, } from '../../auth/cell_grant_action_specs.js';
|
|
47
|
+
import { CellFieldDeleteOutput, CellFieldListOutput, CellFieldSetOutput, } from '../../auth/cell_field_action_specs.js';
|
|
48
|
+
import { CellItemDeleteOutput, CellItemInsertOutput, CellItemListOutput, CellItemMoveOutput, } from '../../auth/cell_item_action_specs.js';
|
|
49
|
+
import { CellAuditListOutput } from '../../auth/cell_audit_action_specs.js';
|
|
50
|
+
import { test_if } from './capabilities.js';
|
|
51
|
+
import { cross_rpc_call, error_reason, expect_output, } from './cell_cross_helpers.js';
|
|
52
|
+
import { SPINE_RPC_PATH } from './default_spine_surface.js';
|
|
53
|
+
export const describe_cell_relations_cross_tests = (options) => {
|
|
54
|
+
const { setup_test, capabilities } = options;
|
|
55
|
+
const rpc_path = options.rpc_path ?? SPINE_RPC_PATH;
|
|
56
|
+
describe('cell relations parity', () => {
|
|
57
|
+
test_if(capabilities.cell_relations, 'grant lifecycle: editor grant enables edit; grant_list is manage-tier; revoke drops it', async () => {
|
|
58
|
+
const fixture = await setup_test();
|
|
59
|
+
const owner = await fixture.create_account({ username: 'cell_grant_owner' });
|
|
60
|
+
const editor = await fixture.create_account({ username: 'cell_grant_editor' });
|
|
61
|
+
const t = fixture.fresh_transport();
|
|
62
|
+
const owner_h = owner.create_session_headers();
|
|
63
|
+
const editor_h = editor.create_session_headers();
|
|
64
|
+
const cell = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { data: { kind: 'note' } }, owner_h), CellCreateOutput).cell;
|
|
65
|
+
// Before the grant the editor can't even see the private cell.
|
|
66
|
+
const pre = await cross_rpc_call(t, rpc_path, 'cell_update', { cell_id: cell.id, data: { kind: 'note', label: 'x' } }, editor_h);
|
|
67
|
+
assert.ok(!pre.ok, 'non-grantee edited a private cell');
|
|
68
|
+
assert.strictEqual(error_reason(pre), 'cell_not_found');
|
|
69
|
+
// Owner (manage-tier) grants the editor an actor-shaped editor grant.
|
|
70
|
+
const grant = expect_output(await cross_rpc_call(t, rpc_path, 'cell_grant_create', {
|
|
71
|
+
cell_id: cell.id,
|
|
72
|
+
level: 'editor',
|
|
73
|
+
principal: { kind: 'actor', actor_id: editor.actor.id },
|
|
74
|
+
}, owner_h), CellGrantCreateOutput).grant;
|
|
75
|
+
assert.strictEqual(grant.level, 'editor');
|
|
76
|
+
assert.strictEqual(grant.actor_id, editor.actor.id);
|
|
77
|
+
assert.strictEqual(grant.role, null);
|
|
78
|
+
// The editor can now edit content.
|
|
79
|
+
const edited = expect_output(await cross_rpc_call(t, rpc_path, 'cell_update', { cell_id: cell.id, data: { kind: 'note', label: 'by editor' } }, editor_h), CellUpdateOutput);
|
|
80
|
+
assert.strictEqual(edited.cell.updated_by, editor.actor.id);
|
|
81
|
+
// grant_list is manage-tier: owner sees the grant.
|
|
82
|
+
const owner_grants = expect_output(await cross_rpc_call(t, rpc_path, 'cell_grant_list', { cell_id: cell.id }, owner_h), CellGrantListOutput);
|
|
83
|
+
assert.ok(owner_grants.grants.some((g) => g.id === grant.id), 'owner grant_list omitted the grant');
|
|
84
|
+
// The editor (non-manage) gets the IDOR 404 on grant_list.
|
|
85
|
+
const editor_grants = await cross_rpc_call(t, rpc_path, 'cell_grant_list', { cell_id: cell.id }, editor_h);
|
|
86
|
+
assert.ok(!editor_grants.ok, 'editor read the grant list');
|
|
87
|
+
assert.strictEqual(error_reason(editor_grants), 'cell_not_found');
|
|
88
|
+
// Revoke and confirm the edit path is gone.
|
|
89
|
+
const revoked = expect_output(await cross_rpc_call(t, rpc_path, 'cell_grant_revoke', { grant_id: grant.id }, owner_h), CellGrantRevokeOutput);
|
|
90
|
+
assert.strictEqual(revoked.ok, true);
|
|
91
|
+
const post = await cross_rpc_call(t, rpc_path, 'cell_update', { cell_id: cell.id, data: { kind: 'note', label: 'y' } }, editor_h);
|
|
92
|
+
assert.ok(!post.ok, 'revoked editor still edited');
|
|
93
|
+
assert.strictEqual(error_reason(post), 'cell_not_found');
|
|
94
|
+
});
|
|
95
|
+
test_if(capabilities.cell_relations, 'editor-grant holder cannot flip visibility → cell_visibility_manage_only', async () => {
|
|
96
|
+
const fixture = await setup_test();
|
|
97
|
+
const owner = await fixture.create_account({ username: 'cell_vis_owner' });
|
|
98
|
+
const editor = await fixture.create_account({ username: 'cell_vis_editor' });
|
|
99
|
+
const t = fixture.fresh_transport();
|
|
100
|
+
const owner_h = owner.create_session_headers();
|
|
101
|
+
const editor_h = editor.create_session_headers();
|
|
102
|
+
const cell = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { data: { kind: 'note' } }, owner_h), CellCreateOutput).cell;
|
|
103
|
+
expect_output(await cross_rpc_call(t, rpc_path, 'cell_grant_create', {
|
|
104
|
+
cell_id: cell.id,
|
|
105
|
+
level: 'editor',
|
|
106
|
+
principal: { kind: 'actor', actor_id: editor.actor.id },
|
|
107
|
+
}, owner_h), CellGrantCreateOutput);
|
|
108
|
+
// Content edits pass (editor tier); a visibility write is manage-tier.
|
|
109
|
+
expect_output(await cross_rpc_call(t, rpc_path, 'cell_update', { cell_id: cell.id, data: { kind: 'note', label: 'ok' } }, editor_h), CellUpdateOutput);
|
|
110
|
+
const vis = await cross_rpc_call(t, rpc_path, 'cell_update', { cell_id: cell.id, visibility: 'public' }, editor_h);
|
|
111
|
+
assert.ok(!vis.ok, 'editor flipped visibility');
|
|
112
|
+
assert.strictEqual(error_reason(vis), 'cell_visibility_manage_only');
|
|
113
|
+
});
|
|
114
|
+
test_if(capabilities.cell_relations, 'field set → forward + reverse list → idempotent delete', async () => {
|
|
115
|
+
const fixture = await setup_test();
|
|
116
|
+
const owner = await fixture.create_account({ username: 'cell_field_owner' });
|
|
117
|
+
const t = fixture.fresh_transport();
|
|
118
|
+
const h = owner.create_session_headers();
|
|
119
|
+
const source = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { data: { kind: 'note' } }, h), CellCreateOutput).cell;
|
|
120
|
+
const target = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { data: { kind: 'note' } }, h), CellCreateOutput).cell;
|
|
121
|
+
const set = expect_output(await cross_rpc_call(t, rpc_path, 'cell_field_set', { source_id: source.id, name: 'cover', target_id: target.id }, h), CellFieldSetOutput);
|
|
122
|
+
assert.strictEqual(set.field.name, 'cover');
|
|
123
|
+
assert.strictEqual(set.field.source_id, source.id);
|
|
124
|
+
assert.strictEqual(set.field.target_id, target.id);
|
|
125
|
+
const forward = expect_output(await cross_rpc_call(t, rpc_path, 'cell_field_list', { source_id: source.id }, h), CellFieldListOutput);
|
|
126
|
+
assert.ok(forward.fields.some((f) => f.name === 'cover' && f.target_id === target.id), 'forward field_list missing the field');
|
|
127
|
+
const reverse = expect_output(await cross_rpc_call(t, rpc_path, 'cell_field_list', { target_id: target.id }, h), CellFieldListOutput);
|
|
128
|
+
assert.ok(reverse.fields.some((f) => f.source_id === source.id), 'reverse field_list missing the upfield');
|
|
129
|
+
const del = expect_output(await cross_rpc_call(t, rpc_path, 'cell_field_delete', { source_id: source.id, name: 'cover' }, h), CellFieldDeleteOutput);
|
|
130
|
+
assert.strictEqual(del.deleted, true);
|
|
131
|
+
const del2 = expect_output(await cross_rpc_call(t, rpc_path, 'cell_field_delete', { source_id: source.id, name: 'cover' }, h), CellFieldDeleteOutput);
|
|
132
|
+
assert.strictEqual(del2.deleted, false);
|
|
133
|
+
});
|
|
134
|
+
test_if(capabilities.cell_relations, 'item insert → ordered forward + reverse list → move → idempotent delete', async () => {
|
|
135
|
+
const fixture = await setup_test();
|
|
136
|
+
const owner = await fixture.create_account({ username: 'cell_item_owner' });
|
|
137
|
+
const t = fixture.fresh_transport();
|
|
138
|
+
const h = owner.create_session_headers();
|
|
139
|
+
const make = async () => expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { data: { kind: 'note' } }, h), CellCreateOutput).cell.id;
|
|
140
|
+
const parent = await make();
|
|
141
|
+
const child_a = await make();
|
|
142
|
+
const child_b = await make();
|
|
143
|
+
const pos_a = fractional_index_between(null, null);
|
|
144
|
+
const ins_a = expect_output(await cross_rpc_call(t, rpc_path, 'cell_item_insert', { parent_id: parent, child_id: child_a, position: pos_a }, h), CellItemInsertOutput);
|
|
145
|
+
assert.strictEqual(ins_a.item.child_id, child_a);
|
|
146
|
+
const pos_b = fractional_index_between(pos_a, null);
|
|
147
|
+
expect_output(await cross_rpc_call(t, rpc_path, 'cell_item_insert', { parent_id: parent, child_id: child_b, position: pos_b }, h), CellItemInsertOutput);
|
|
148
|
+
// Forward list is lex-ordered by position; child_a (pos_a < pos_b) first.
|
|
149
|
+
const forward = expect_output(await cross_rpc_call(t, rpc_path, 'cell_item_list', { parent_id: parent }, h), CellItemListOutput);
|
|
150
|
+
assert.strictEqual(forward.items.length, 2);
|
|
151
|
+
assert.strictEqual(forward.items[0].child_id, child_a);
|
|
152
|
+
assert.strictEqual(forward.items[1].child_id, child_b);
|
|
153
|
+
// Reverse list — which parents contain child_a.
|
|
154
|
+
const reverse = expect_output(await cross_rpc_call(t, rpc_path, 'cell_item_list', { child_id: child_a }, h), CellItemListOutput);
|
|
155
|
+
assert.ok(reverse.items.some((i) => i.parent_id === parent), 'reverse item_list missing the parent');
|
|
156
|
+
// Move child_b ahead of child_a.
|
|
157
|
+
const new_pos = fractional_index_between(null, pos_a);
|
|
158
|
+
const moved = expect_output(await cross_rpc_call(t, rpc_path, 'cell_item_move', { parent_id: parent, position: pos_b, new_position: new_pos }, h), CellItemMoveOutput);
|
|
159
|
+
assert.strictEqual(moved.item.position, new_pos);
|
|
160
|
+
assert.strictEqual(moved.item.child_id, child_b);
|
|
161
|
+
// Idempotent delete on the slot key.
|
|
162
|
+
const del = expect_output(await cross_rpc_call(t, rpc_path, 'cell_item_delete', { parent_id: parent, position: pos_a }, h), CellItemDeleteOutput);
|
|
163
|
+
assert.strictEqual(del.deleted, true);
|
|
164
|
+
const del2 = expect_output(await cross_rpc_call(t, rpc_path, 'cell_item_delete', { parent_id: parent, position: pos_a }, h), CellItemDeleteOutput);
|
|
165
|
+
assert.strictEqual(del2.deleted, false);
|
|
166
|
+
});
|
|
167
|
+
test_if(capabilities.cell_relations, 'clone: shallow shares edges; deep clones children', async () => {
|
|
168
|
+
const fixture = await setup_test();
|
|
169
|
+
const owner = await fixture.create_account({ username: 'cell_clone_owner' });
|
|
170
|
+
const t = fixture.fresh_transport();
|
|
171
|
+
const h = owner.create_session_headers();
|
|
172
|
+
const make = async (label) => expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { data: { kind: 'note', ...(label === undefined ? {} : { label }) } }, h), CellCreateOutput).cell.id;
|
|
173
|
+
const source = await make('orig');
|
|
174
|
+
const child = await make('child');
|
|
175
|
+
const field_target = await make('target');
|
|
176
|
+
const child_pos = fractional_index_between(null, null);
|
|
177
|
+
expect_output(await cross_rpc_call(t, rpc_path, 'cell_item_insert', { parent_id: source, child_id: child, position: child_pos }, h), CellItemInsertOutput);
|
|
178
|
+
expect_output(await cross_rpc_call(t, rpc_path, 'cell_field_set', { source_id: source, name: 'link', target_id: field_target }, h), CellFieldSetOutput);
|
|
179
|
+
// Shallow clone: new owned cell, path nulled, item edge shares the
|
|
180
|
+
// same child_id, field edge shares the same target_id.
|
|
181
|
+
const shallow = expect_output(await cross_rpc_call(t, rpc_path, 'cell_clone', { source_id: source }, h), CellCloneOutput).cell;
|
|
182
|
+
assert.notStrictEqual(shallow.id, source);
|
|
183
|
+
assert.strictEqual(shallow.created_by, owner.actor.id);
|
|
184
|
+
assert.strictEqual(shallow.path, null);
|
|
185
|
+
const shallow_items = expect_output(await cross_rpc_call(t, rpc_path, 'cell_item_list', { parent_id: shallow.id }, h), CellItemListOutput);
|
|
186
|
+
assert.strictEqual(shallow_items.items.length, 1);
|
|
187
|
+
assert.strictEqual(shallow_items.items[0].child_id, child, 'shallow clone re-pointed the child');
|
|
188
|
+
const shallow_fields = expect_output(await cross_rpc_call(t, rpc_path, 'cell_field_list', { source_id: shallow.id }, h), CellFieldListOutput);
|
|
189
|
+
assert.strictEqual(shallow_fields.fields.length, 1);
|
|
190
|
+
assert.strictEqual(shallow_fields.fields[0].target_id, field_target);
|
|
191
|
+
// Deep clone: the child edge points at a NEW cloned cell, not the
|
|
192
|
+
// original child; the field edge still shares the target.
|
|
193
|
+
const deep = expect_output(await cross_rpc_call(t, rpc_path, 'cell_clone', { source_id: source, deep: true }, h), CellCloneOutput).cell;
|
|
194
|
+
const deep_items = expect_output(await cross_rpc_call(t, rpc_path, 'cell_item_list', { parent_id: deep.id }, h), CellItemListOutput);
|
|
195
|
+
assert.strictEqual(deep_items.items.length, 1);
|
|
196
|
+
assert.notStrictEqual(deep_items.items[0].child_id, child, 'deep clone reused the original child');
|
|
197
|
+
const deep_fields = expect_output(await cross_rpc_call(t, rpc_path, 'cell_field_list', { source_id: deep.id }, h), CellFieldListOutput);
|
|
198
|
+
assert.strictEqual(deep_fields.fields.length, 1);
|
|
199
|
+
assert.strictEqual(deep_fields.fields[0].target_id, field_target);
|
|
200
|
+
});
|
|
201
|
+
test_if(capabilities.cell_relations, 'audit_list is manage-tier: owner reads the timeline; viewer-grant gets 404', async () => {
|
|
202
|
+
const fixture = await setup_test();
|
|
203
|
+
const owner = await fixture.create_account({ username: 'cell_audit_owner' });
|
|
204
|
+
const viewer = await fixture.create_account({ username: 'cell_audit_viewer' });
|
|
205
|
+
const t = fixture.fresh_transport();
|
|
206
|
+
const owner_h = owner.create_session_headers();
|
|
207
|
+
const viewer_h = viewer.create_session_headers();
|
|
208
|
+
const cell = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { data: { kind: 'note' } }, owner_h), CellCreateOutput).cell;
|
|
209
|
+
// A mutation so the timeline has at least the create + update events.
|
|
210
|
+
expect_output(await cross_rpc_call(t, rpc_path, 'cell_update', { cell_id: cell.id, data: { kind: 'note', label: 'v2' } }, owner_h), CellUpdateOutput);
|
|
211
|
+
// Owner (manage-tier) reads the timeline.
|
|
212
|
+
const timeline = expect_output(await cross_rpc_call(t, rpc_path, 'cell_audit_list', { cell_id: cell.id }, owner_h), CellAuditListOutput);
|
|
213
|
+
assert.ok(timeline.events.length > 0, 'owner audit timeline empty');
|
|
214
|
+
assert.ok(timeline.events.some((e) => e.event_type === 'cell_create'), 'audit timeline missing the create event');
|
|
215
|
+
// Grant the viewer a view-only grant: they can read the cell …
|
|
216
|
+
expect_output(await cross_rpc_call(t, rpc_path, 'cell_grant_create', {
|
|
217
|
+
cell_id: cell.id,
|
|
218
|
+
level: 'viewer',
|
|
219
|
+
principal: { kind: 'actor', actor_id: viewer.actor.id },
|
|
220
|
+
}, owner_h), CellGrantCreateOutput);
|
|
221
|
+
const can_read = await cross_rpc_call(t, rpc_path, 'cell_get', { id: cell.id }, viewer_h);
|
|
222
|
+
assert.ok(can_read.ok, 'viewer-grant holder could not read the cell');
|
|
223
|
+
// … but the timeline is manage-tier, so they get the IDOR 404.
|
|
224
|
+
const denied = await cross_rpc_call(t, rpc_path, 'cell_audit_list', { cell_id: cell.id }, viewer_h);
|
|
225
|
+
assert.ok(!denied.ok, 'viewer read the audit timeline');
|
|
226
|
+
assert.strictEqual(error_reason(denied), 'cell_not_found');
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default_backend_configs.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/default_backend_configs.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAC,sBAAsB,EAAE,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAC/E,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAA2B,KAAK,gBAAgB,EAAC,MAAM,+BAA+B,CAAC;AAQ9F;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,
|
|
1
|
+
{"version":3,"file":"default_backend_configs.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/default_backend_configs.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAC,sBAAsB,EAAE,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAC/E,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAA2B,KAAK,gBAAgB,EAAC,MAAM,+BAA+B,CAAC;AAQ9F;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,mBAUpC,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,EAAE,mBAUtC,CAAC;AAeH,MAAM,WAAW,iCAAiC;IACjD,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC9C,oDAAoD;IACpD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,6CAA6C;IAC7C,QAAQ,CAAC,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAC5C,wEAAwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAClC,sEAAsE;IACtE,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC/D;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B,GAC1C,MAAM,iCAAiC,KACrC,aAmCF,CAAC;AAEF,MAAM,WAAW,mCAAmC;IACnD,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,+CAA+C;IAC/C,QAAQ,CAAC,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAC5C,wEAAwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAClC,sEAAsE;IACtE,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC/D;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,GAC5C,MAAM,mCAAmC,KACvC,aA2CF,CAAC"}
|
|
@@ -13,6 +13,9 @@ export const ts_default_capabilities = Object.freeze({
|
|
|
13
13
|
login_rate_limit: false,
|
|
14
14
|
ws: true,
|
|
15
15
|
sse: false,
|
|
16
|
+
cell_crud: true,
|
|
17
|
+
cell_relations: true,
|
|
18
|
+
account_lifecycle: true,
|
|
16
19
|
in_process_only: false,
|
|
17
20
|
});
|
|
18
21
|
/**
|
|
@@ -27,6 +30,9 @@ export const rust_default_capabilities = Object.freeze({
|
|
|
27
30
|
login_rate_limit: true,
|
|
28
31
|
ws: true,
|
|
29
32
|
sse: false,
|
|
33
|
+
cell_crud: true,
|
|
34
|
+
cell_relations: true,
|
|
35
|
+
account_lifecycle: true,
|
|
30
36
|
in_process_only: false,
|
|
31
37
|
});
|
|
32
38
|
/** Bootstrap block built from the default secrets + supplied paths. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/setup.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/setup.ts"],"names":[],"mappings":"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"}
|
|
@@ -85,6 +85,11 @@ const in_process_fetch_transport = (app) => {
|
|
|
85
85
|
* beyond what `create_test_app` already does.
|
|
86
86
|
*/
|
|
87
87
|
export const default_in_process_setup = (options) => async () => {
|
|
88
|
+
// Per-test fresh db. When `options.migration_namespaces` is set,
|
|
89
|
+
// `create_test_app` provisions an auth+extras PGlite (e.g. the cell
|
|
90
|
+
// layer); otherwise the auth-only default. Either way the factory
|
|
91
|
+
// resets + re-migrates on each `create`, so the per-test keeper
|
|
92
|
+
// bootstrap below lands on a clean DB.
|
|
88
93
|
const test_app = await create_test_app(options);
|
|
89
94
|
// Seed bootstrap-time secondaries against the same DB the keeper
|
|
90
95
|
// just landed on. Direct-insert is the only path for roles whose
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spawn_backend.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/spawn_backend.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAQ,KAAK,YAAY,EAAC,MAAM,oBAAoB,CAAC;AAI5D,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAIvD,6EAA6E;AAC7E,MAAM,WAAW,aAAa;IAC7B,yFAAyF;IACzF,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,qEAAqE;IACrE,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B;;;;;OAKG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;
|
|
1
|
+
{"version":3,"file":"spawn_backend.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/spawn_backend.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAQ,KAAK,YAAY,EAAC,MAAM,oBAAoB,CAAC;AAI5D,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAIvD,6EAA6E;AAC7E,MAAM,WAAW,aAAa;IAC7B,yFAAyF;IACzF,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,qEAAqE;IACrE,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B;;;;;OAKG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AA4ID;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,GAAU,QAAQ,aAAa,KAAG,OAAO,CAAC,aAAa,CAiHhF,CAAC"}
|
|
@@ -31,6 +31,14 @@ import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
|
31
31
|
import { dirname } from 'node:path';
|
|
32
32
|
/** Number of ms between health-probe attempts. Tuned to be cheap on busy CI runners. */
|
|
33
33
|
const HEALTH_PROBE_INTERVAL_MS = 100;
|
|
34
|
+
/**
|
|
35
|
+
* Grace window after teardown's SIGTERM before escalating to SIGKILL on the
|
|
36
|
+
* process group. A well-behaved backend exits within milliseconds; this only
|
|
37
|
+
* fires for one that ignores SIGTERM or whose graceful shutdown never
|
|
38
|
+
* completes (e.g. a runtime whose `server.stop()` promise never resolves), so
|
|
39
|
+
* the await below can't strand the whole run forever.
|
|
40
|
+
*/
|
|
41
|
+
const TEARDOWN_SIGKILL_GRACE_MS = 3_000;
|
|
34
42
|
/**
|
|
35
43
|
* Sleep helper for the probe loop. Resolves after `ms`.
|
|
36
44
|
*/
|
|
@@ -208,10 +216,30 @@ export const spawn_backend = async (config) => {
|
|
|
208
216
|
live_teardowns.delete(teardown_sync);
|
|
209
217
|
if (exit_info !== null)
|
|
210
218
|
return;
|
|
211
|
-
// Wait for the child to actually exit so callers can be sure the
|
|
212
|
-
//
|
|
219
|
+
// Wait for the child to actually exit so callers can be sure the port
|
|
220
|
+
// is free. A backend that ignores SIGTERM — or whose graceful shutdown
|
|
221
|
+
// wedges (e.g. a runtime whose `server.stop()` never resolves) — would
|
|
222
|
+
// otherwise hang this await forever and strand the run, so escalate to
|
|
223
|
+
// SIGKILL on the process group after a grace window. SIGKILL is
|
|
224
|
+
// uncatchable, so the child then exits and the `'exit'` listener
|
|
225
|
+
// resolves. Defense-in-depth: the per-runtime adapter shutdown is the
|
|
226
|
+
// primary fix; this guarantees teardown completes regardless.
|
|
213
227
|
await new Promise((resolve) => {
|
|
214
|
-
|
|
228
|
+
const kill_timer = setTimeout(() => {
|
|
229
|
+
if (child.pid !== undefined && exit_info === null) {
|
|
230
|
+
try {
|
|
231
|
+
// Negative pid → process group.
|
|
232
|
+
process.kill(-child.pid, 'SIGKILL');
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
// Already dead; ignore.
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}, TEARDOWN_SIGKILL_GRACE_MS);
|
|
239
|
+
child.once('exit', () => {
|
|
240
|
+
clearTimeout(kill_timer);
|
|
241
|
+
resolve();
|
|
242
|
+
});
|
|
215
243
|
});
|
|
216
244
|
};
|
|
217
245
|
live_teardowns.add(teardown_sync);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing_server_bun.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_server_bun.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA6B9B,OAAO,KAAK,EAAc,oBAAoB,EAAC,MAAM,0BAA0B,CAAC;AAehF,kDAAkD;AAClD,eAAO,MAAM,0BAA0B,QAAO,
|
|
1
|
+
{"version":3,"file":"testing_server_bun.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_server_bun.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA6B9B,OAAO,KAAK,EAAc,oBAAoB,EAAC,MAAM,0BAA0B,CAAC;AAehF,kDAAkD;AAClD,eAAO,MAAM,0BAA0B,QAAO,oBA0D5C,CAAC"}
|
|
@@ -43,8 +43,35 @@ export const create_bun_testing_adapter = () => ({
|
|
|
43
43
|
websocket,
|
|
44
44
|
});
|
|
45
45
|
const handle = {
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
// Bun bug (1.3.14): after a *server-initiated* WebSocket close
|
|
47
|
+
// (`ServerWebSocket.close()` / `hono/bun`'s `WSContext.close()`),
|
|
48
|
+
// `server.stop()` never resolves — Bun doesn't decrement its
|
|
49
|
+
// active-connection count for a server-closed socket, so the stop
|
|
50
|
+
// waits forever for a connection it already closed. The trigger is
|
|
51
|
+
// orthogonal to HTTP load, hono-vs-raw `Bun.serve`, in-vs-cross
|
|
52
|
+
// process, the force flag (`stop()`/`stop(false)`/`stop(true)` all
|
|
53
|
+
// hang), and the client runtime — a single server-closed WS is
|
|
54
|
+
// necessary and sufficient. Client-initiated close or leaving the
|
|
55
|
+
// socket open both stop cleanly in ~0ms. In this suite the trigger is
|
|
56
|
+
// `create_ws_auth_guard` closing the socket on `session_revoke_all`
|
|
57
|
+
// (the `ws.cross.test.ts` close-on-revoke case) — the only
|
|
58
|
+
// server-initiated WS close, which is why teardown hangs there and
|
|
59
|
+
// not under HTTP-only or client-closed WS traffic.
|
|
60
|
+
//
|
|
61
|
+
// So initiate a force-close (`true` drops active connections, no
|
|
62
|
+
// drain) but DON'T await it: awaiting hangs `start_testing_server`'s
|
|
63
|
+
// shutdown forever — `built.close()` and `exit(0)` never run, and the
|
|
64
|
+
// spawning harness blocks on a child that never exits (observed as a
|
|
65
|
+
// multi-minute hang needing SIGKILL). The force-close still tears the
|
|
66
|
+
// live sockets down; the `exit(0)` the core fires immediately after
|
|
67
|
+
// does the real teardown a few ms later. Node/Deno don't need this —
|
|
68
|
+
// their `shutdown()`/`close()` resolve normally. The `.catch` guards a
|
|
69
|
+
// future Bun that rejects rather than hangs; if a future Bun resolves
|
|
70
|
+
// the promise after a server-initiated close, revert to
|
|
71
|
+
// `await server.stop(true)` to mirror the Node/Deno adapters.
|
|
72
|
+
shutdown: () => {
|
|
73
|
+
void Promise.resolve(server.stop(true)).catch(() => { });
|
|
74
|
+
return Promise.resolve();
|
|
48
75
|
},
|
|
49
76
|
native: server,
|
|
50
77
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entities.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/entities.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAiB7B,OAAO,KAAK,EAAC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAC,MAAM,2BAA2B,CAAC;AACzE,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAC/D,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,4BAA4B,CAAC;AAE/D,sFAAsF;AACtF,MAAM,MAAM,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,YAAY,GAAG,YAAY,CAAC,CAAC,GAAG;IAC/F,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC;AAEF,sDAAsD;AACtD,eAAO,MAAM,mBAAmB,GAAI,YAAY,oBAAoB,KAAG,
|
|
1
|
+
{"version":3,"file":"entities.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/entities.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAiB7B,OAAO,KAAK,EAAC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAC,MAAM,2BAA2B,CAAC;AACzE,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAC/D,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,4BAA4B,CAAC;AAE/D,sFAAsF;AACtF,MAAM,MAAM,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,YAAY,GAAG,YAAY,CAAC,CAAC,GAAG;IAC/F,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC;AAEF,sDAAsD;AACtD,eAAO,MAAM,mBAAmB,GAAI,YAAY,oBAAoB,KAAG,OAarE,CAAC;AAEH,oFAAoF;AACpF,MAAM,MAAM,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,GAAG,YAAY,GAAG,YAAY,CAAC,CAAC,GAAG;IAC3F,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC;AAEF,oDAAoD;AACpD,eAAO,MAAM,iBAAiB,GAAI,YAAY,kBAAkB,KAAG,KAUjE,CAAC;AAEH,yFAAyF;AACzF,MAAM,MAAM,sBAAsB,GAAG,OAAO,CAC3C,IAAI,CACH,SAAS,EACT,IAAI,GAAG,UAAU,GAAG,YAAY,GAAG,UAAU,GAAG,YAAY,GAAG,YAAY,GAAG,iBAAiB,CAC/F,CACD,GAAG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC,CAAC;AAEF,wDAAwD;AACxD,eAAO,MAAM,sBAAsB,GAAI,YAAY,sBAAsB,KAAG,SAgB3E,CAAC;AAEF,8EAA8E;AAC9E,eAAO,MAAM,mBAAmB,GAC/B,cAAa,KAAK,CAAC,sBAAsB,CAAQ,KAC/C,cAID,CAAC;AAEH,0FAA0F;AAC1F,MAAM,MAAM,uBAAuB,GAAG,OAAO,CAC5C,IAAI,CAAC,aAAa,EAAE,IAAI,GAAG,UAAU,GAAG,YAAY,GAAG,mBAAmB,GAAG,iBAAiB,CAAC,CAC/F,GAAG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC,CAAC;AAEF,4DAA4D;AAC5D,eAAO,MAAM,uBAAuB,GAAI,YAAY,uBAAuB,KAAG,aAa5E,CAAC"}
|
package/dist/testing/entities.js
CHANGED
|
@@ -10,6 +10,8 @@ export const create_test_account = (overrides) => ({
|
|
|
10
10
|
created_by: null,
|
|
11
11
|
updated_at: '2024-01-01T00:00:00Z',
|
|
12
12
|
updated_by: null,
|
|
13
|
+
deleted_at: null,
|
|
14
|
+
deleted_by: null,
|
|
13
15
|
...overrides,
|
|
14
16
|
});
|
|
15
17
|
/** Create a test `Actor` with sensible defaults. */
|
|
@@ -20,6 +22,8 @@ export const create_test_actor = (overrides) => ({
|
|
|
20
22
|
created_at: '2024-01-01T00:00:00Z',
|
|
21
23
|
updated_at: null,
|
|
22
24
|
updated_by: null,
|
|
25
|
+
deleted_at: null,
|
|
26
|
+
deleted_by: null,
|
|
23
27
|
...overrides,
|
|
24
28
|
});
|
|
25
29
|
/** Create a test `RoleGrant` with sensible defaults. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/ws_round_trip.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"ws_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/ws_round_trip.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,KAAK,EAAC,OAAO,EAAO,MAAM,MAAM,CAAC;AACxC,OAAO,EACN,SAAS,EAET,KAAK,gBAAgB,EAErB,KAAK,QAAQ,EACb,MAAM,SAAS,CAAC;AACjB,OAAO,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAC/C,OAAO,EAAc,KAAK,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAE9D,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,2BAA2B,CAAC;AAC/D,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,4BAA4B,CAAC;AAEvD,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,kCAAkC,CAAC;AAE7E,OAAO,EAAqB,KAAK,uBAAuB,EAAC,MAAM,kCAAkC,CAAC;AAElG,OAAO,EAAC,yBAAyB,EAAC,MAAM,qCAAqC,CAAC;AAC9E,OAAO,EAAsB,KAAK,cAAc,EAAC,MAAM,4BAA4B,CAAC;AAEpF,OAAO,EAKN,KAAK,cAAc,EACnB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAKN,KAAK,QAAQ,EACb,MAAM,2BAA2B,CAAC;AAMnC;;;GAGG;AACH,MAAM,WAAW,MAAM;IACtB,EAAE,EAAE,SAAS,CAAC;IACd,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACrB,MAAM,EAAE,KAAK,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAC,CAAC,CAAC;CAChD;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,QAAO,MAajC,CAAC;AAEF,8CAA8C;AAC9C,MAAM,WAAW,sBAAsB;IACtC,eAAe,EAAE,cAAc,CAAC;IAChC,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B;;;OAGG;IACH,eAAe,CAAC,EAAE,cAAc,CAAC;CACjC;AAED;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,GAAI,MAAM,sBAAsB,KAAG,OAavE,CAAC;AAEF,uFAAuF;AACvF,MAAM,WAAW,WAAW;IAC3B,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,iBAAiB,EAAE,MAAM,CAAC,CAAC,EAAE,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CACtE;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,QAAO,WAatC,CAAC;AAEF;;;;GAIG;AACH,qBAAa,wBAAyB,YAAW,sBAAsB;;IACtE,QAAQ,EAAE,UAAU,GAAG,SAAS,CAAa;gBAEjC,KAAK,EAAE,aAAa,CAAC,eAAe,CAAC;IAGjD,qBAAqB,IAAI,SAAS;IAGlC,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;CAG/D;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAC/B,YAAY,WAAW,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,EAC9C,OAAO,YAAY,EACnB,IAAI,SAAS,KACX,OAAO,CAAC,IAAI,CAId,CAAC;AAMF,2CAA2C;AAC3C,MAAM,WAAW,iBAAiB;IACjC,wEAAwE;IACxE,UAAU,CAAC,EAAE,IAAI,CAAC;IAClB,yFAAyF;IACzF,eAAe,CAAC,EAAE,cAAc,CAAC;IACjC,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,sFAAsF;IACtF,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACtB;AAED,4CAA4C;AAC5C,MAAM,WAAW,0BAA0B;IAC1C;;;;;OAKG;IACH,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC/B,kEAAkE;IAClE,SAAS,CAAC,EAAE,yBAAyB,CAAC;IACtC;;;;OAIG;IACH,SAAS,CAAC,EAAE,uBAAuB,CAAC,WAAW,CAAC,CAAC;IACjD,gEAAgE;IAChE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,cAAc,CAAC,EAAE,uBAAuB,CAAC,gBAAgB,CAAC,CAAC;IAC3D,yDAAyD;IACzD,eAAe,CAAC,EAAE,uBAAuB,CAAC,iBAAiB,CAAC,CAAC;CAC7D;AAED,kEAAkE;AAClE,MAAM,WAAW,aAAa;IAC7B,SAAS,EAAE,yBAAyB,CAAC;IACrC;;;;;;;;;;OAUG;IACH,OAAO,EAAE,CAAC,QAAQ,CAAC,EAAE,iBAAiB,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CAC7D;AAiED;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,GAAI,SAAS,0BAA0B,KAAG,aA0M5E,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,eAAe,QAAO,iBAGjC,CAAC;AAYH;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,mBAAmB,GAAI,IAAI,SAAS,MAAM,EAAE,SAAS;IACjE,OAAO,EAAE,aAAa,CAAC;IACvB,KAAK,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;CACtC,KAAG,IAIH,CAAC"}
|
|
@@ -157,6 +157,8 @@ const build_multi_role_request_context = (account_id, roles) => {
|
|
|
157
157
|
created_by: null,
|
|
158
158
|
updated_at: now,
|
|
159
159
|
updated_by: null,
|
|
160
|
+
deleted_at: null,
|
|
161
|
+
deleted_by: null,
|
|
160
162
|
},
|
|
161
163
|
actor: {
|
|
162
164
|
id: actor_id,
|
|
@@ -165,6 +167,8 @@ const build_multi_role_request_context = (account_id, roles) => {
|
|
|
165
167
|
created_at: now,
|
|
166
168
|
updated_at: null,
|
|
167
169
|
updated_by: null,
|
|
170
|
+
deleted_at: null,
|
|
171
|
+
deleted_by: null,
|
|
168
172
|
},
|
|
169
173
|
role_grants: roles.map((role) => ({
|
|
170
174
|
id: create_uuid(),
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
{key: 'account', label: 'username', width: 180},
|
|
37
37
|
{key: 'role_grants', label: 'role_grants', width: 240},
|
|
38
38
|
{key: 'actor', label: 'grant', width: 200},
|
|
39
|
+
{key: 'pending_offers', label: 'manage', width: 140},
|
|
39
40
|
];
|
|
40
41
|
</script>
|
|
41
42
|
|
|
@@ -49,6 +50,22 @@
|
|
|
49
50
|
</p>
|
|
50
51
|
{/if}
|
|
51
52
|
|
|
53
|
+
{#if admin_accounts.has_rpc}
|
|
54
|
+
<label class="row gap_xs font_size_sm">
|
|
55
|
+
<input
|
|
56
|
+
type="checkbox"
|
|
57
|
+
checked={admin_accounts.show_deleted}
|
|
58
|
+
onchange={(e) => admin_accounts.set_show_deleted(e.currentTarget.checked)}
|
|
59
|
+
/>
|
|
60
|
+
show deleted
|
|
61
|
+
</label>
|
|
62
|
+
<p class="text_50 font_size_sm">
|
|
63
|
+
“delete” is a reversible soft-delete (tombstone) — enable “show deleted” to reactivate an
|
|
64
|
+
account. Permanent hard-delete (purge) is keeper/CLI-only and intentionally not available
|
|
65
|
+
here.
|
|
66
|
+
</p>
|
|
67
|
+
{/if}
|
|
68
|
+
|
|
52
69
|
{#if admin_accounts.list.loading}
|
|
53
70
|
<p class="text_50">loading accounts...</p>
|
|
54
71
|
{:else if admin_accounts.list.error}
|
|
@@ -167,6 +184,47 @@
|
|
|
167
184
|
{/if}
|
|
168
185
|
{/each}
|
|
169
186
|
{/if}
|
|
187
|
+
{:else if column.key === 'pending_offers'}
|
|
188
|
+
{#if admin_accounts.has_rpc}
|
|
189
|
+
{#if row.account.deleted_at}
|
|
190
|
+
{@const undelete_error = admin_accounts.undelete.error(row.account.id)}
|
|
191
|
+
<span
|
|
192
|
+
class="chip font_size_sm color_c"
|
|
193
|
+
title={format_datetime_local(row.account.deleted_at)}
|
|
194
|
+
>
|
|
195
|
+
deleted {format_relative_time(row.account.deleted_at)}
|
|
196
|
+
</span>
|
|
197
|
+
<button
|
|
198
|
+
type="button"
|
|
199
|
+
class="sm"
|
|
200
|
+
disabled={admin_accounts.undelete.loading(row.account.id)}
|
|
201
|
+
onclick={() => admin_accounts.submit_undelete(row.account.id)}
|
|
202
|
+
>
|
|
203
|
+
reactivate
|
|
204
|
+
</button>
|
|
205
|
+
{#if undelete_error}
|
|
206
|
+
<span class="color_c_50 font_size_sm">{undelete_error}</span>
|
|
207
|
+
{/if}
|
|
208
|
+
{:else}
|
|
209
|
+
{@const delete_error = admin_accounts.soft_delete.error(row.account.id)}
|
|
210
|
+
<ConfirmButton
|
|
211
|
+
onconfirm={() => admin_accounts.submit_delete(row.account.id)}
|
|
212
|
+
title="soft-delete @{row.account.username}"
|
|
213
|
+
class="sm"
|
|
214
|
+
label="delete"
|
|
215
|
+
pending={admin_accounts.soft_delete.loading(row.account.id)}
|
|
216
|
+
>
|
|
217
|
+
{#snippet popover_content(_popover, do_confirm)}
|
|
218
|
+
<button type="button" class="color_c bg_100" onclick={() => do_confirm()}>
|
|
219
|
+
<span class="py_sm">soft-delete @{row.account.username} (reversible)</span>
|
|
220
|
+
</button>
|
|
221
|
+
{/snippet}
|
|
222
|
+
</ConfirmButton>
|
|
223
|
+
{#if delete_error}
|
|
224
|
+
<span class="color_c_50 font_size_sm">{delete_error}</span>
|
|
225
|
+
{/if}
|
|
226
|
+
{/if}
|
|
227
|
+
{/if}
|
|
170
228
|
{/if}
|
|
171
229
|
{/snippet}
|
|
172
230
|
</Datatable>
|
|
@@ -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":"AA2MA,QAAA,MAAM,aAAa,2DAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
|
|
@@ -18,7 +18,7 @@ import { KeyedAsyncSlot } from './keyed_async_slot.svelte.js';
|
|
|
18
18
|
import type { AdminAccountEntryJson } from '../auth/account_schema.js';
|
|
19
19
|
import type { RoleName } from '../auth/role_schema.js';
|
|
20
20
|
import type { RoleGrantOfferJson } from '../auth/role_grant_offer_schema.js';
|
|
21
|
-
import type { AdminAccountListOutput, AdminSessionListOutput, AdminSessionRevokeAllInput, AdminSessionRevokeAllOutput, AdminTokenRevokeAllInput, AdminTokenRevokeAllOutput } from '../auth/admin_action_specs.js';
|
|
21
|
+
import type { AdminAccountListOutput, AccountDeleteOutput, AccountUndeleteOutput, AdminSessionListOutput, AdminSessionRevokeAllInput, AdminSessionRevokeAllOutput, AdminTokenRevokeAllInput, AdminTokenRevokeAllOutput } from '../auth/admin_action_specs.js';
|
|
22
22
|
import type { RoleGrantOfferCreateInput, RoleGrantOfferCreateOutput, RoleGrantOfferOkOutput, RoleGrantRevokeInput, RoleGrantRevokeOutput } from '../auth/role_grant_offer_action_specs.js';
|
|
23
23
|
/**
|
|
24
24
|
* Narrow RPC surface consumed by `AdminAccountsState`. Consumers adapt their
|
|
@@ -40,7 +40,9 @@ import type { RoleGrantOfferCreateInput, RoleGrantOfferCreateOutput, RoleGrantOf
|
|
|
40
40
|
* to bridge to the typed throwing Proxy.
|
|
41
41
|
*/
|
|
42
42
|
export interface AdminAccountsRpc {
|
|
43
|
-
list_accounts: () => Promise<AdminAccountListOutput>;
|
|
43
|
+
list_accounts: (include_deleted?: boolean) => Promise<AdminAccountListOutput>;
|
|
44
|
+
delete_account: (account_id: Uuid) => Promise<AccountDeleteOutput>;
|
|
45
|
+
undelete_account: (account_id: Uuid) => Promise<AccountUndeleteOutput>;
|
|
44
46
|
list_sessions: () => Promise<AdminSessionListOutput>;
|
|
45
47
|
create_role_grant: (params: RoleGrantOfferCreateInput) => Promise<RoleGrantOfferCreateOutput>;
|
|
46
48
|
revoke_role_grant: (params: RoleGrantRevokeInput) => Promise<RoleGrantRevokeOutput>;
|
|
@@ -101,8 +103,15 @@ export declare class AdminAccountsState {
|
|
|
101
103
|
}, string>;
|
|
102
104
|
readonly revoke: KeyedAsyncSlot<string & import("zod").$brand<"Uuid">, void, string>;
|
|
103
105
|
readonly retract: KeyedAsyncSlot<string & import("zod").$brand<"Uuid">, void, string>;
|
|
106
|
+
readonly soft_delete: KeyedAsyncSlot<string & import("zod").$brand<"Uuid">, void, string>;
|
|
107
|
+
readonly undelete: KeyedAsyncSlot<string & import("zod").$brand<"Uuid">, void, string>;
|
|
104
108
|
accounts: Array<AdminAccountEntryJson>;
|
|
105
109
|
grantable_roles: Array<RoleName>;
|
|
110
|
+
/**
|
|
111
|
+
* When `true`, `fetch()` includes soft-deleted (tombstoned) accounts so
|
|
112
|
+
* the admin can reactivate them. Toggled via `set_show_deleted`.
|
|
113
|
+
*/
|
|
114
|
+
show_deleted: boolean;
|
|
106
115
|
readonly account_count: number;
|
|
107
116
|
constructor(options?: AdminAccountsStateOptions);
|
|
108
117
|
/**
|
|
@@ -111,6 +120,25 @@ export declare class AdminAccountsState {
|
|
|
111
120
|
*/
|
|
112
121
|
get has_rpc(): boolean;
|
|
113
122
|
fetch(): Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* Toggle whether soft-deleted accounts appear in the listing, then
|
|
125
|
+
* re-fetch. Tombstoned rows are surfaced so an admin can reactivate them
|
|
126
|
+
* via `submit_undelete`.
|
|
127
|
+
*/
|
|
128
|
+
set_show_deleted(value: boolean): Promise<void>;
|
|
129
|
+
/**
|
|
130
|
+
* Soft-delete an account (reversible tombstone) via `account_delete`.
|
|
131
|
+
* Keyed by `account_id` so per-row spinners/errors stay independent.
|
|
132
|
+
* Refreshes the listing on success so the row drops out (active view) or
|
|
133
|
+
* flips to its tombstoned state (`show_deleted` view).
|
|
134
|
+
*/
|
|
135
|
+
submit_delete(account_id: Uuid): Promise<void>;
|
|
136
|
+
/**
|
|
137
|
+
* Reactivate a soft-deleted account via `account_undelete` (admin-only).
|
|
138
|
+
* Keyed by `account_id`; refreshes the listing on success so the row
|
|
139
|
+
* returns to active state.
|
|
140
|
+
*/
|
|
141
|
+
submit_undelete(account_id: Uuid): Promise<void>;
|
|
114
142
|
/**
|
|
115
143
|
* Offer the role to the recipient via the `role_grant_offer_create` RPC.
|
|
116
144
|
* Server returns the pending offer; the recipient must accept before
|