@fuzdev/fuz_app 0.103.0 → 0.104.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 +29 -11
- package/dist/auth/cell_action_specs.d.ts +226 -1
- package/dist/auth/cell_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_action_specs.js +79 -1
- package/dist/auth/cell_actions.d.ts +32 -9
- package/dist/auth/cell_actions.d.ts.map +1 -1
- package/dist/auth/cell_actions.js +126 -12
- package/dist/auth/cell_audit_events.d.ts +1 -1
- package/dist/auth/cell_audit_events.d.ts.map +1 -1
- package/dist/auth/cell_audit_events.js +3 -2
- package/dist/auth/cell_audit_metadata.d.ts +15 -0
- package/dist/auth/cell_audit_metadata.d.ts.map +1 -1
- package/dist/auth/cell_audit_metadata.js +11 -0
- package/dist/db/CLAUDE.md +16 -6
- package/dist/db/cell_ddl.d.ts +14 -1
- package/dist/db/cell_ddl.d.ts.map +1 -1
- package/dist/db/cell_ddl.js +20 -0
- package/dist/db/cell_queries.d.ts +45 -0
- package/dist/db/cell_queries.d.ts.map +1 -1
- package/dist/db/cell_queries.js +40 -3
- package/dist/testing/CLAUDE.md +25 -16
- package/dist/testing/cross_backend/cell_gated_create.d.ts +24 -0
- package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_gated_create.js +177 -35
- package/dist/testing/cross_backend/expected_schema.json +3 -0
- package/dist/testing/cross_backend/full_spine_mount.d.ts.map +1 -1
- package/dist/testing/cross_backend/full_spine_mount.js +7 -4
- package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts +12 -7
- package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts.map +1 -1
- package/dist/testing/cross_backend/test_cell_gated_create_authorize.js +54 -15
- package/package.json +1 -1
|
@@ -47,26 +47,49 @@ import { type CellRow } from '../db/cell_queries.js';
|
|
|
47
47
|
import type { CellData } from './cell_data_schema.js';
|
|
48
48
|
/**
|
|
49
49
|
* Input to a `CellCreateAuthorize` callback — the TS twin of the Rust
|
|
50
|
-
* `CellCreateAuthorizeInput`.
|
|
50
|
+
* `CellCreateAuthorizeInput`. **Parent-aware**: it carries the directory
|
|
51
|
+
* context (`parent_id` / the handler-resolved `root_id`) so the authorizer can
|
|
52
|
+
* resolve the governing root's policy.
|
|
51
53
|
*/
|
|
52
54
|
export interface CellCreateAuthorizeInput {
|
|
53
55
|
/** The cell's `kind` (the top-level `cell.kind` value); `null` for a typeless cell. */
|
|
54
56
|
kind: string | null;
|
|
55
57
|
/** The cell `data` (kind-free — a `kind` key is rejected upstream). For richer M3-era policies (e.g. content pre-screen). */
|
|
56
58
|
data: CellData;
|
|
59
|
+
/** The immediate container the create targets. `null` = a root creation; otherwise a contribution under that parent. */
|
|
60
|
+
parent_id: Uuid | null;
|
|
61
|
+
/** The governing root of the directory subtree, resolved by the handler from the parent (`parent.root_id ?? parent.id`). `null` for a root creation. */
|
|
62
|
+
root_id: Uuid | null;
|
|
63
|
+
/** The governing root's `data` — the handler reads it in-tx (when an authorizer is mounted) and hands it over, so a directory-aware authorizer resolves `root.data.policy[kind]` **without a DB read of its own** (pure predicate; reading in-tx avoids the single-connection PGlite deadlock a separate handle would hit). `null` for a root creation, or when no authorizer is mounted. */
|
|
64
|
+
root_data: CellData | null;
|
|
57
65
|
/** Target scope — designed-in for M2 space-scoping; **always `null` in v1** (cells carry no scope column). */
|
|
58
66
|
scope_id: Uuid | null;
|
|
59
67
|
}
|
|
60
68
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* calls) — the create handler is already async with `auth` in hand.
|
|
69
|
+
* An authorizer's decision for a `cell_create` — the TS twin of the Rust
|
|
70
|
+
* `Verdict`. Folds the moderation outcome into the authority decision (one
|
|
71
|
+
* policy resolution, not two): `{allow: false}` denies (the handler surfaces a
|
|
72
|
+
* 403 `forbidden` for a viewable parent / a root creation), `{allow: true,
|
|
73
|
+
* moderation_required}` admits — `true` → born `pending` + private, `false` →
|
|
74
|
+
* born `approved` at the author's visibility.
|
|
68
75
|
*/
|
|
69
|
-
export type
|
|
76
|
+
export type CellCreateVerdict = {
|
|
77
|
+
allow: false;
|
|
78
|
+
} | {
|
|
79
|
+
allow: true;
|
|
80
|
+
moderation_required: boolean;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Opt-in, parent-aware creation authorizer — the TS twin of the Rust
|
|
84
|
+
* `CellCreateAuthorize` trait. Gates both roots (`parent_id = null`) and
|
|
85
|
+
* contributions; answers "may *this actor* create *this kind* here?" and
|
|
86
|
+
* returns a `CellCreateVerdict`. Runs in `cell_create` after `validate_data`
|
|
87
|
+
* and after the handler resolves `root_id` from the parent (an unviewable
|
|
88
|
+
* parent already 404-masks before this runs). Omitted = today's open create
|
|
89
|
+
* (all consumers untouched). Async-capable (DB / policy calls) — a DB-backed
|
|
90
|
+
* impl closes over its own `db` (the create handler stays unaware).
|
|
91
|
+
*/
|
|
92
|
+
export type CellCreateAuthorize = (auth: RequestActorContext, input: CellCreateAuthorizeInput) => CellCreateVerdict | Promise<CellCreateVerdict>;
|
|
70
93
|
/**
|
|
71
94
|
* Dependencies for `create_cell_actions`.
|
|
72
95
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAIH,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAAW,KAAK,mBAAmB,EAAC,MAAM,sBAAsB,CAAC;AAExE,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,WAAW,CAAC;AAEhD,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAEjD,OAAO,
|
|
1
|
+
{"version":3,"file":"cell_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAIH,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAAW,KAAK,mBAAmB,EAAC,MAAM,sBAAsB,CAAC;AAExE,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,WAAW,CAAC;AAEhD,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAEjD,OAAO,EAoCN,KAAK,QAAQ,EAEb,MAAM,wBAAwB,CAAC;AAChC,OAAO,EASN,KAAK,OAAO,EACZ,MAAM,uBAAuB,CAAC;AAsB/B,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,uBAAuB,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,WAAW,wBAAwB;IACxC,uFAAuF;IACvF,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,6HAA6H;IAC7H,IAAI,EAAE,QAAQ,CAAC;IACf,wHAAwH;IACxH,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,wJAAwJ;IACxJ,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC;IACrB,6XAA6X;IAC7X,SAAS,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC3B,8GAA8G;IAC9G,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAAC,KAAK,EAAE,KAAK,CAAA;CAAC,GAAG;IAAC,KAAK,EAAE,IAAI,CAAC;IAAC,mBAAmB,EAAE,OAAO,CAAA;CAAC,CAAC;AAE7F;;;;;;;;;GASG;AACH,MAAM,MAAM,mBAAmB,GAAG,CACjC,IAAI,EAAE,mBAAmB,EACzB,KAAK,EAAE,wBAAwB,KAC3B,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAEpD;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,gBAAgB,EAAE,KAAK,GAAG,OAAO,CAAC,GAAG;IACtE,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,QAAQ,CAAC;IAC7C,gBAAgB,CAAC,EAAE,mBAAmB,CAAC;CACvC,CAAC;AAkCF,eAAO,MAAM,YAAY,GAAI,KAAK,OAAO,KAAG,QAgB1C,CAAC;AA2BH,+CAA+C;AAC/C,eAAO,MAAM,mBAAmB,GAAI,MAAM,cAAc,KAAG,KAAK,CAAC,SAAS,CAinBzE,CAAC"}
|
|
@@ -44,8 +44,8 @@ import { jsonrpc_errors, dev_only } from '../http/jsonrpc_errors.js';
|
|
|
44
44
|
import { is_pg_unique_violation } from '../db/pg_error.js';
|
|
45
45
|
import { has_role } from './request_context.js';
|
|
46
46
|
import { ROLE_ADMIN } from './role_schema.js';
|
|
47
|
-
import { cell_create_action_spec, cell_get_action_spec, cell_update_action_spec, cell_delete_action_spec, cell_list_action_spec, cell_clone_action_spec, ERROR_CELL_NOT_FOUND, ERROR_CELL_PATH_ADMIN_ONLY, ERROR_CELL_PATH_TAKEN, ERROR_CELL_VISIBILITY_MANAGE_ONLY, ERROR_CELL_GET_REQUIRES_ID_OR_PATH, ERROR_CELL_KIND_IN_DATA, ERROR_CELL_KIND_EMPTY, ERROR_CELL_LIST_CREATED_BY_REQUIRES_AUTH, ERROR_CELL_LIST_SHARED_WITH_REQUIRES_AUTH, CELL_LIST_LIMIT_DEFAULT, CELL_RELATIONS_BUNDLE_LIMIT, } from './cell_action_specs.js';
|
|
48
|
-
import { query_cell_create, query_cell_get, query_cell_get_by_path, query_cell_update, query_cell_delete, query_cell_list, query_cell_load_many, } from '../db/cell_queries.js';
|
|
47
|
+
import { cell_create_action_spec, cell_get_action_spec, cell_update_action_spec, cell_delete_action_spec, cell_list_action_spec, cell_clone_action_spec, cell_moderate_action_spec, ERROR_CELL_NOT_FOUND, ERROR_CELL_PATH_ADMIN_ONLY, ERROR_CELL_PATH_TAKEN, ERROR_CELL_VISIBILITY_MANAGE_ONLY, ERROR_CELL_GET_REQUIRES_ID_OR_PATH, ERROR_CELL_KIND_IN_DATA, ERROR_CELL_KIND_EMPTY, ERROR_CELL_CREATE_FORBIDDEN, ERROR_CELL_MODERATE_FORBIDDEN, ERROR_CELL_NOT_A_CONTRIBUTION, ERROR_CELL_LIST_CREATED_BY_REQUIRES_AUTH, ERROR_CELL_LIST_SHARED_WITH_REQUIRES_AUTH, CELL_LIST_LIMIT_DEFAULT, CELL_RELATIONS_BUNDLE_LIMIT, } from './cell_action_specs.js';
|
|
48
|
+
import { query_cell_create, query_cell_get, query_cell_get_by_path, query_cell_update, query_cell_delete, query_cell_set_moderation, query_cell_list, query_cell_load_many, } from '../db/cell_queries.js';
|
|
49
49
|
import { query_cell_grant_list_for_cell, query_cell_grants_for_caller_in_cells, } from '../db/cell_grant_queries.js';
|
|
50
50
|
import { query_cell_field_list_for_source, query_cell_field_set } from '../db/cell_field_queries.js';
|
|
51
51
|
import { query_cell_item_insert, query_cell_item_list_for_parent, } from '../db/cell_item_queries.js';
|
|
@@ -87,6 +87,9 @@ export const to_cell_json = (row) => ({
|
|
|
87
87
|
kind: row.kind,
|
|
88
88
|
visibility: row.visibility,
|
|
89
89
|
refs: row.refs,
|
|
90
|
+
parent_id: row.parent_id,
|
|
91
|
+
root_id: row.root_id,
|
|
92
|
+
moderation: row.moderation,
|
|
90
93
|
created_by: row.created_by,
|
|
91
94
|
updated_by: row.updated_by,
|
|
92
95
|
created_at: typeof row.created_at === 'string' ? row.created_at : row.created_at.toISOString(),
|
|
@@ -156,15 +159,75 @@ export const create_cell_actions = (deps) => {
|
|
|
156
159
|
// known kinds with malformed payloads surface as invalid_params.
|
|
157
160
|
const validated_data = validate_data_or_throw(input.data);
|
|
158
161
|
const kind = input.kind ?? null;
|
|
159
|
-
//
|
|
160
|
-
//
|
|
161
|
-
//
|
|
162
|
-
//
|
|
163
|
-
//
|
|
164
|
-
//
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
162
|
+
// Directory tree: resolve the governing `root_id` from the parent (and
|
|
163
|
+
// the root's `data` for the authorizer's policy read). Runs whether or
|
|
164
|
+
// not an authorizer is mounted — `root_id` is written regardless, and an
|
|
165
|
+
// unviewable parent 404-masks the attempt (the same IDOR mask the read
|
|
166
|
+
// path uses; never reveals a hidden container). A root creation
|
|
167
|
+
// (`parent_id` null/absent) has `root_id = null`. `root_data` is read
|
|
168
|
+
// **in-tx** (only when an authorizer is mounted), reusing the parent row
|
|
169
|
+
// when the parent *is* the root, else one read by `root_id` — so the
|
|
170
|
+
// authorizer stays pure + the read rides the handler's connection (no
|
|
171
|
+
// separate-handle deadlock on the single-connection PGlite).
|
|
172
|
+
const parent_id = input.parent_id ?? null;
|
|
173
|
+
let root_id = null;
|
|
174
|
+
let root_data = null;
|
|
175
|
+
if (parent_id !== null) {
|
|
176
|
+
const parent = await query_cell_get(ctx, parent_id);
|
|
177
|
+
if (!parent)
|
|
178
|
+
throw jsonrpc_errors.not_found('cell', { reason: ERROR_CELL_NOT_FOUND });
|
|
179
|
+
const parent_grants = await query_cell_grant_list_for_cell(ctx, parent.id);
|
|
180
|
+
if (!can_view_cell(auth, parent, parent_grants)) {
|
|
181
|
+
throw jsonrpc_errors.not_found('cell', { reason: ERROR_CELL_NOT_FOUND });
|
|
182
|
+
}
|
|
183
|
+
root_id = parent.root_id ?? parent.id;
|
|
184
|
+
if (authorize_create) {
|
|
185
|
+
root_data =
|
|
186
|
+
root_id === parent.id
|
|
187
|
+
? parent.data
|
|
188
|
+
: ((await query_cell_get(ctx, root_id))?.data ?? null);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Parent-aware creation authorizer (opt-in). Runs after shape validation
|
|
192
|
+
// and after the parent-resolve (a hidden parent already 404-masked).
|
|
193
|
+
// `{allow: false}` for a *viewable* parent / a root creation is a 403
|
|
194
|
+
// `forbidden` (not the 404 mask); an `{allow: true}` verdict folds the
|
|
195
|
+
// moderation outcome. Receives the **raw** `input.data` (not the
|
|
196
|
+
// `validate_data`-normalized value) to match the Rust twin, so a consumer
|
|
197
|
+
// with a transforming validator can't diverge TS↔Rust. `scope_id` is
|
|
198
|
+
// `null` in v1. Without an authorizer, create is open: `moderation` stays
|
|
199
|
+
// null and the author's visibility holds.
|
|
200
|
+
let moderation = null;
|
|
201
|
+
let visibility = input.visibility;
|
|
202
|
+
if (authorize_create) {
|
|
203
|
+
const verdict = await authorize_create(auth, {
|
|
204
|
+
kind,
|
|
205
|
+
data: input.data,
|
|
206
|
+
parent_id,
|
|
207
|
+
root_id,
|
|
208
|
+
root_data,
|
|
209
|
+
scope_id: null,
|
|
210
|
+
});
|
|
211
|
+
if (!verdict.allow) {
|
|
212
|
+
throw jsonrpc_errors.forbidden('cell creation is not permitted here', {
|
|
213
|
+
reason: ERROR_CELL_CREATE_FORBIDDEN,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
// Moderation is a *contribution* concept — only stamp it for a create
|
|
217
|
+
// with a parent. A root / unparented create stays `moderation = null`
|
|
218
|
+
// at the author's visibility even under a mounted authorizer (there is
|
|
219
|
+
// no container to be moderated under).
|
|
220
|
+
if (parent_id !== null) {
|
|
221
|
+
if (verdict.moderation_required) {
|
|
222
|
+
// Born pending: stays private until `cell_moderate` approves it.
|
|
223
|
+
moderation = 'pending';
|
|
224
|
+
visibility = 'private';
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
// Born approved + live immediately at the author's visibility.
|
|
228
|
+
moderation = 'approved';
|
|
229
|
+
}
|
|
230
|
+
}
|
|
168
231
|
}
|
|
169
232
|
let row;
|
|
170
233
|
try {
|
|
@@ -176,8 +239,11 @@ export const create_cell_actions = (deps) => {
|
|
|
176
239
|
// schema-validated runtime shape.
|
|
177
240
|
data: validated_data,
|
|
178
241
|
kind,
|
|
179
|
-
visibility
|
|
242
|
+
visibility,
|
|
180
243
|
path: input.path ?? null,
|
|
244
|
+
parent_id,
|
|
245
|
+
root_id,
|
|
246
|
+
moderation,
|
|
181
247
|
created_by: auth.actor.id,
|
|
182
248
|
});
|
|
183
249
|
}
|
|
@@ -338,6 +404,51 @@ export const create_cell_actions = (deps) => {
|
|
|
338
404
|
emit_cell_audit(ctx, 'cell_delete', existing, deps, auth);
|
|
339
405
|
return { ok: true, deleted: true };
|
|
340
406
|
};
|
|
407
|
+
const moderate_handler = async (input, ctx) => {
|
|
408
|
+
const auth = ctx.auth;
|
|
409
|
+
const existing = await query_cell_get(ctx, input.cell_id);
|
|
410
|
+
// 404 covers miss + unviewable so a pending (private) contribution
|
|
411
|
+
// doesn't leak existence to a non-viewer.
|
|
412
|
+
if (!existing)
|
|
413
|
+
throw jsonrpc_errors.not_found('cell', { reason: ERROR_CELL_NOT_FOUND });
|
|
414
|
+
const grants = await query_cell_grant_list_for_cell(ctx, existing.id);
|
|
415
|
+
if (!can_view_cell(auth, existing, grants)) {
|
|
416
|
+
throw jsonrpc_errors.not_found('cell', { reason: ERROR_CELL_NOT_FOUND });
|
|
417
|
+
}
|
|
418
|
+
// Only a contribution (with a governing root) is moderatable.
|
|
419
|
+
if (existing.root_id === null) {
|
|
420
|
+
throw jsonrpc_errors.invalid_params('cell is not a contribution (no governing root to moderate under)', { reason: ERROR_CELL_NOT_A_CONTRIBUTION });
|
|
421
|
+
}
|
|
422
|
+
// Authority is over the governing ROOT (admin / root owner), not the
|
|
423
|
+
// contribution — the author manages the contribution and could otherwise
|
|
424
|
+
// self-approve. A viewable-but-unauthorized caller (incl. the author) → 403.
|
|
425
|
+
const root = await query_cell_get(ctx, existing.root_id);
|
|
426
|
+
if (!root)
|
|
427
|
+
throw jsonrpc_errors.not_found('cell', { reason: ERROR_CELL_NOT_FOUND });
|
|
428
|
+
if (!can_manage_cell(auth, root)) {
|
|
429
|
+
throw jsonrpc_errors.forbidden('cell_moderate requires moderation authority over the governing root', { reason: ERROR_CELL_MODERATE_FORBIDDEN });
|
|
430
|
+
}
|
|
431
|
+
const approved = input.moderation === 'approved';
|
|
432
|
+
const updated = await query_cell_set_moderation(ctx, input.cell_id, input.moderation, {
|
|
433
|
+
set_visibility_public: approved,
|
|
434
|
+
updated_by: auth.actor.id,
|
|
435
|
+
});
|
|
436
|
+
// Raced with a deleter between the authz check and the UPDATE.
|
|
437
|
+
if (!updated)
|
|
438
|
+
throw jsonrpc_errors.not_found('cell', { reason: ERROR_CELL_NOT_FOUND });
|
|
439
|
+
deps.audit.emit(ctx, {
|
|
440
|
+
event_type: 'cell_moderate',
|
|
441
|
+
actor_id: auth.actor.id,
|
|
442
|
+
account_id: auth.account.id,
|
|
443
|
+
ip: ctx.client_ip,
|
|
444
|
+
metadata: {
|
|
445
|
+
cell_id: updated.id,
|
|
446
|
+
root_id: existing.root_id,
|
|
447
|
+
moderation: input.moderation,
|
|
448
|
+
},
|
|
449
|
+
});
|
|
450
|
+
return { cell: to_cell_json(updated) };
|
|
451
|
+
};
|
|
341
452
|
/**
|
|
342
453
|
* Insert one cloned cell row owned by the caller.
|
|
343
454
|
*
|
|
@@ -542,6 +653,8 @@ export const create_cell_actions = (deps) => {
|
|
|
542
653
|
ref: input.ref,
|
|
543
654
|
created_by: input.created_by,
|
|
544
655
|
path_prefix: input.path_prefix,
|
|
656
|
+
root_id: input.root_id,
|
|
657
|
+
moderation: input.moderation,
|
|
545
658
|
viewer_actor_id: caller_actor_id,
|
|
546
659
|
viewer_is_admin: auth ? has_role(auth, ROLE_ADMIN) : false,
|
|
547
660
|
caller_actor_id,
|
|
@@ -572,5 +685,6 @@ export const create_cell_actions = (deps) => {
|
|
|
572
685
|
rpc_action(cell_delete_action_spec, delete_handler),
|
|
573
686
|
rpc_action(cell_list_action_spec, list_handler),
|
|
574
687
|
rpc_action(cell_clone_action_spec, clone_handler),
|
|
688
|
+
rpc_action(cell_moderate_action_spec, moderate_handler),
|
|
575
689
|
];
|
|
576
690
|
};
|
|
@@ -20,7 +20,7 @@ import type { z } from 'zod';
|
|
|
20
20
|
/**
|
|
21
21
|
* Cell-layer `event_type → metadata schema` map for `extra_events`.
|
|
22
22
|
*
|
|
23
|
-
* Covers the
|
|
23
|
+
* Covers the seven generic cell verbs' mutation events plus the grant /
|
|
24
24
|
* field / item relation events. Read-only verbs (`cell_get`, `cell_list`,
|
|
25
25
|
* `cell_*_list`, `cell_audit_list`) emit nothing and are absent here.
|
|
26
26
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell_audit_events.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_audit_events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"cell_audit_events.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_audit_events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAqB3B;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAajE,CAAC"}
|
|
@@ -16,14 +16,14 @@
|
|
|
16
16
|
*
|
|
17
17
|
* @module
|
|
18
18
|
*/
|
|
19
|
-
import { CellAuditMetadata, CellCloneAuditMetadata } from './cell_audit_metadata.js';
|
|
19
|
+
import { CellAuditMetadata, CellCloneAuditMetadata, CellModerateAuditMetadata, } from './cell_audit_metadata.js';
|
|
20
20
|
import { CellGrantCreateAuditMetadata, CellGrantRevokeAuditMetadata, } from './cell_grant_audit_metadata.js';
|
|
21
21
|
import { CellFieldSetAuditMetadata, CellFieldDeleteAuditMetadata, } from './cell_field_audit_metadata.js';
|
|
22
22
|
import { CellItemInsertAuditMetadata, CellItemMoveAuditMetadata, CellItemDeleteAuditMetadata, } from './cell_item_audit_metadata.js';
|
|
23
23
|
/**
|
|
24
24
|
* Cell-layer `event_type → metadata schema` map for `extra_events`.
|
|
25
25
|
*
|
|
26
|
-
* Covers the
|
|
26
|
+
* Covers the seven generic cell verbs' mutation events plus the grant /
|
|
27
27
|
* field / item relation events. Read-only verbs (`cell_get`, `cell_list`,
|
|
28
28
|
* `cell_*_list`, `cell_audit_list`) emit nothing and are absent here.
|
|
29
29
|
*/
|
|
@@ -32,6 +32,7 @@ export const cell_audit_events = {
|
|
|
32
32
|
cell_update: CellAuditMetadata,
|
|
33
33
|
cell_delete: CellAuditMetadata,
|
|
34
34
|
cell_clone: CellCloneAuditMetadata,
|
|
35
|
+
cell_moderate: CellModerateAuditMetadata,
|
|
35
36
|
cell_grant_create: CellGrantCreateAuditMetadata,
|
|
36
37
|
cell_grant_revoke: CellGrantRevokeAuditMetadata,
|
|
37
38
|
cell_field_set: CellFieldSetAuditMetadata,
|
|
@@ -45,4 +45,19 @@ export declare const CellCloneAuditMetadata: z.ZodObject<{
|
|
|
45
45
|
kind: z.ZodOptional<z.ZodString>;
|
|
46
46
|
}, z.core.$loose>;
|
|
47
47
|
export type CellCloneAuditMetadata = z.infer<typeof CellCloneAuditMetadata>;
|
|
48
|
+
/**
|
|
49
|
+
* Metadata envelope for `cell_moderate`. `cell_id` is the moderated
|
|
50
|
+
* contribution; `root_id` is its governing root (the container whose
|
|
51
|
+
* moderation authority gated the call); `moderation` is the terminal decision
|
|
52
|
+
* applied (`'approved'` | `'rejected'`).
|
|
53
|
+
*/
|
|
54
|
+
export declare const CellModerateAuditMetadata: z.ZodObject<{
|
|
55
|
+
cell_id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
56
|
+
root_id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
57
|
+
moderation: z.ZodEnum<{
|
|
58
|
+
rejected: "rejected";
|
|
59
|
+
approved: "approved";
|
|
60
|
+
}>;
|
|
61
|
+
}, z.core.$loose>;
|
|
62
|
+
export type CellModerateAuditMetadata = z.infer<typeof CellModerateAuditMetadata>;
|
|
48
63
|
//# sourceMappingURL=cell_audit_metadata.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell_audit_metadata.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_audit_metadata.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAGtB;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB;;;;iBAI5B,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,sBAAsB;;;;;;iBAMjC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"cell_audit_metadata.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_audit_metadata.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAGtB;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB;;;;iBAI5B,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,sBAAsB;;;;;;iBAMjC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAE5E;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB;;;;;;;iBAIpC,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC"}
|
|
@@ -44,3 +44,14 @@ export const CellCloneAuditMetadata = z.looseObject({
|
|
|
44
44
|
item_count: z.number().int().nonnegative(),
|
|
45
45
|
kind: z.string().optional(),
|
|
46
46
|
});
|
|
47
|
+
/**
|
|
48
|
+
* Metadata envelope for `cell_moderate`. `cell_id` is the moderated
|
|
49
|
+
* contribution; `root_id` is its governing root (the container whose
|
|
50
|
+
* moderation authority gated the call); `moderation` is the terminal decision
|
|
51
|
+
* applied (`'approved'` | `'rejected'`).
|
|
52
|
+
*/
|
|
53
|
+
export const CellModerateAuditMetadata = z.looseObject({
|
|
54
|
+
cell_id: Uuid,
|
|
55
|
+
root_id: Uuid,
|
|
56
|
+
moderation: z.enum(['approved', 'rejected']),
|
|
57
|
+
});
|
package/dist/db/CLAUDE.md
CHANGED
|
@@ -53,19 +53,29 @@ The wire schemas + RPC handlers + authz predicates for this layer live in
|
|
|
53
53
|
`('private', 'public')`) — a top-level access-control column, peer to
|
|
54
54
|
`cell_grant`, not a `data` field. `cell.kind text` (nullable) is the
|
|
55
55
|
write-once capability/identity axis — likewise a top-level column, not a
|
|
56
|
-
`data` field (`idx_cell_kind` partial-active).
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
`data` field (`idx_cell_kind` partial-active). `cell.parent_id` / `root_id`
|
|
57
|
+
(nullable self-FKs to `cell.id`) are the **directory tree** — immediate
|
|
58
|
+
container + governing root (`root_id = parent.root_id ?? parent.id`, null for a
|
|
59
|
+
root), immutable in v1 (`idx_cell_root` partial-active for subtree feeds).
|
|
60
|
+
`cell.moderation text` (nullable — `pending` / `approved` / `rejected`) is the
|
|
61
|
+
approval-lifecycle marker, peer to `visibility` (partial `idx_cell_moderation_pending`
|
|
62
|
+
on `root_id WHERE moderation = 'pending'` for the mod queue), set by the create
|
|
63
|
+
authorizer's verdict + the `cell_moderate` verb, never in `data`. Nullable
|
|
64
|
+
`created_by` / `updated_by` FKs to `actor` (NULL = system origin). GIN on `data`
|
|
65
|
+
/ `refs`; global partial-unique on `path`.
|
|
59
66
|
- **`cell_history_ddl.ts`** — dormant `cell_history` table
|
|
60
67
|
(`CELL_HISTORY_MIGRATION_NS`, namespace `fuz_cell_history`), FK → `cell.id`.
|
|
61
68
|
Ships present-but-unwritten; no snapshot lifecycle yet.
|
|
62
69
|
- **`cell_queries.ts`** — `query_cell_create / get / get_by_path / update /
|
|
63
|
-
delete`, `_list_by_kind / _list_by_creator`, the
|
|
70
|
+
delete / set_moderation`, `_list_by_kind / _list_by_creator`, the
|
|
64
71
|
generic `query_cell_list` (filter + SQL-side visibility predicate mirroring
|
|
65
72
|
`can_view_cell`; the `kind` filter narrows by the `cell.kind` column, the
|
|
66
|
-
`ref` filter by `cell.refs`
|
|
73
|
+
`ref` filter by `cell.refs`, plus `root_id` / `moderation` filters for the
|
|
74
|
+
per-root feed + mod queue), and
|
|
67
75
|
`query_cell_load_many` (bulk id load, no visibility filter — feeds the
|
|
68
|
-
strict relation-read filter). `
|
|
76
|
+
strict relation-read filter). `query_cell_set_moderation` is the dedicated
|
|
77
|
+
`cell_moderate` write (`moderation` is absent from `CellUpdatePatch`, so an
|
|
78
|
+
author can't self-approve via `cell_update`). `cell.refs` derived from
|
|
69
79
|
`data` via `extract_refs` on create/update. `CellRow.grant_count` is a
|
|
70
80
|
derived projection (correlated subquery on `idx_cell_grant_cell`).
|
|
71
81
|
- **`cell_grant_queries.ts`** — resource-side ACL: `query_cell_grant_create`
|
package/dist/db/cell_ddl.d.ts
CHANGED
|
@@ -69,8 +69,17 @@ export declare const CELL_VISIBILITY_TYPE = "\nDO $$ BEGIN\n\tCREATE TYPE cell_v
|
|
|
69
69
|
* `CellCreateAuthorize`) and is **write-once**: set at INSERT and carried on
|
|
70
70
|
* no update path, so a cell's kind is fixed at birth. Content stays
|
|
71
71
|
* duck-typed in `data`; `kind` is a capability tag, not a content-type.
|
|
72
|
+
*
|
|
73
|
+
* `parent_id` / `root_id` are the **directory tree** (containment): `parent_id`
|
|
74
|
+
* is the immediate container (nullable self-FK; `NULL` = a root), `root_id` is
|
|
75
|
+
* the governing root denormalized for flat-subtree queries (`root_id =
|
|
76
|
+
* parent.root_id ?? parent.id`, so a root has `NULL`). Both are set once at
|
|
77
|
+
* create and immutable in v1 (carried on no update path). `moderation`
|
|
78
|
+
* (nullable text — `pending` / `approved` / `rejected`; `NULL` = unmoderated)
|
|
79
|
+
* is the approval-lifecycle marker, peer to `visibility` (a control field with
|
|
80
|
+
* a non-author writer — see `auth/cell_actions.ts`), never inside `data`.
|
|
72
81
|
*/
|
|
73
|
-
export declare const CELL_SCHEMA = "\nCREATE TABLE IF NOT EXISTS cell (\n\tid UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n\tdata JSONB NOT NULL,\n\tkind TEXT,\n\tvisibility cell_visibility NOT NULL DEFAULT 'private',\n\tpath TEXT,\n\trefs TEXT[],\n\tcreated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n\tupdated_at TIMESTAMPTZ,\n\tdeleted_at TIMESTAMPTZ,\n\tcreated_by UUID REFERENCES actor(id) ON DELETE SET NULL,\n\tupdated_by UUID REFERENCES actor(id) ON DELETE SET NULL\n)";
|
|
82
|
+
export declare const CELL_SCHEMA = "\nCREATE TABLE IF NOT EXISTS cell (\n\tid UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n\tdata JSONB NOT NULL,\n\tkind TEXT,\n\tvisibility cell_visibility NOT NULL DEFAULT 'private',\n\tpath TEXT,\n\trefs TEXT[],\n\tparent_id UUID REFERENCES cell(id) ON DELETE SET NULL,\n\troot_id UUID REFERENCES cell(id) ON DELETE SET NULL,\n\tmoderation TEXT,\n\tcreated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n\tupdated_at TIMESTAMPTZ,\n\tdeleted_at TIMESTAMPTZ,\n\tcreated_by UUID REFERENCES actor(id) ON DELETE SET NULL,\n\tupdated_by UUID REFERENCES actor(id) ON DELETE SET NULL\n)";
|
|
74
83
|
/**
|
|
75
84
|
* Cell indexes — all active-only, partial on `deleted_at IS NULL`.
|
|
76
85
|
*
|
|
@@ -83,6 +92,10 @@ export declare const CELL_SCHEMA = "\nCREATE TABLE IF NOT EXISTS cell (\n\tid UU
|
|
|
83
92
|
* - `idx_cell_data`: shape-driven queries (`data @> ...`).
|
|
84
93
|
* - `idx_cell_refs`: cells-by-fact discovery (cross-cell reference graph).
|
|
85
94
|
* - `idx_cell_created_by`: "cells this actor created" queries.
|
|
95
|
+
* - `idx_cell_root`: flat-subtree feed/scope queries by governing root
|
|
96
|
+
* (`cell.root_id = ?`). Active-only.
|
|
97
|
+
* - `idx_cell_moderation_pending`: the moderation queue — pending
|
|
98
|
+
* contributions per governing root. Partial on `moderation = 'pending'`.
|
|
86
99
|
*
|
|
87
100
|
* Parent↔child membership and named relations live in sibling tables;
|
|
88
101
|
* see `CELL_ITEM_INDEXES` / `CELL_FIELD_INDEXES` below.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell_ddl.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/cell_ddl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAGH,OAAO,KAAK,EAAC,SAAS,EAAE,kBAAkB,EAAC,MAAM,cAAc,CAAC;AAEhE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB,oIAI1B,CAAC;AAER
|
|
1
|
+
{"version":3,"file":"cell_ddl.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/cell_ddl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAGH,OAAO,KAAK,EAAC,SAAS,EAAE,kBAAkB,EAAC,MAAM,cAAc,CAAC;AAEhE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB,oIAI1B,CAAC;AAER;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,WAAW,gkBAgBtB,CAAC;AAEH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,MAAM,CAkBtC,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,0iBAc5B,CAAC;AAEH;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,MAAM,CAW5C,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,+RAO5B,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,MAAM,CAE5C,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,qSAO3B,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,MAAM,CAG3C,CAAC;AAEF,6EAA6E;AAC7E,eAAO,MAAM,gBAAgB,4DAA6D,CAAC;AAE3F,uBAAuB;AACvB,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,SAAS,CAuB5C,CAAC;AAEF,gDAAgD;AAChD,eAAO,MAAM,wBAAwB,aAAa,CAAC;AAEnD,wDAAwD;AACxD,eAAO,MAAM,iBAAiB,EAAE,kBAG/B,CAAC"}
|
package/dist/db/cell_ddl.js
CHANGED
|
@@ -72,6 +72,15 @@ END $$`;
|
|
|
72
72
|
* `CellCreateAuthorize`) and is **write-once**: set at INSERT and carried on
|
|
73
73
|
* no update path, so a cell's kind is fixed at birth. Content stays
|
|
74
74
|
* duck-typed in `data`; `kind` is a capability tag, not a content-type.
|
|
75
|
+
*
|
|
76
|
+
* `parent_id` / `root_id` are the **directory tree** (containment): `parent_id`
|
|
77
|
+
* is the immediate container (nullable self-FK; `NULL` = a root), `root_id` is
|
|
78
|
+
* the governing root denormalized for flat-subtree queries (`root_id =
|
|
79
|
+
* parent.root_id ?? parent.id`, so a root has `NULL`). Both are set once at
|
|
80
|
+
* create and immutable in v1 (carried on no update path). `moderation`
|
|
81
|
+
* (nullable text — `pending` / `approved` / `rejected`; `NULL` = unmoderated)
|
|
82
|
+
* is the approval-lifecycle marker, peer to `visibility` (a control field with
|
|
83
|
+
* a non-author writer — see `auth/cell_actions.ts`), never inside `data`.
|
|
75
84
|
*/
|
|
76
85
|
export const CELL_SCHEMA = `
|
|
77
86
|
CREATE TABLE IF NOT EXISTS cell (
|
|
@@ -81,6 +90,9 @@ CREATE TABLE IF NOT EXISTS cell (
|
|
|
81
90
|
visibility cell_visibility NOT NULL DEFAULT 'private',
|
|
82
91
|
path TEXT,
|
|
83
92
|
refs TEXT[],
|
|
93
|
+
parent_id UUID REFERENCES cell(id) ON DELETE SET NULL,
|
|
94
|
+
root_id UUID REFERENCES cell(id) ON DELETE SET NULL,
|
|
95
|
+
moderation TEXT,
|
|
84
96
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
85
97
|
updated_at TIMESTAMPTZ,
|
|
86
98
|
deleted_at TIMESTAMPTZ,
|
|
@@ -99,6 +111,10 @@ CREATE TABLE IF NOT EXISTS cell (
|
|
|
99
111
|
* - `idx_cell_data`: shape-driven queries (`data @> ...`).
|
|
100
112
|
* - `idx_cell_refs`: cells-by-fact discovery (cross-cell reference graph).
|
|
101
113
|
* - `idx_cell_created_by`: "cells this actor created" queries.
|
|
114
|
+
* - `idx_cell_root`: flat-subtree feed/scope queries by governing root
|
|
115
|
+
* (`cell.root_id = ?`). Active-only.
|
|
116
|
+
* - `idx_cell_moderation_pending`: the moderation queue — pending
|
|
117
|
+
* contributions per governing root. Partial on `moderation = 'pending'`.
|
|
102
118
|
*
|
|
103
119
|
* Parent↔child membership and named relations live in sibling tables;
|
|
104
120
|
* see `CELL_ITEM_INDEXES` / `CELL_FIELD_INDEXES` below.
|
|
@@ -117,6 +133,10 @@ export const CELL_INDEXES = [
|
|
|
117
133
|
WHERE refs IS NOT NULL AND deleted_at IS NULL`,
|
|
118
134
|
`CREATE INDEX IF NOT EXISTS idx_cell_created_by ON cell(created_by)
|
|
119
135
|
WHERE deleted_at IS NULL`,
|
|
136
|
+
`CREATE INDEX IF NOT EXISTS idx_cell_root ON cell(root_id)
|
|
137
|
+
WHERE deleted_at IS NULL`,
|
|
138
|
+
`CREATE INDEX IF NOT EXISTS idx_cell_moderation_pending ON cell(root_id)
|
|
139
|
+
WHERE moderation = 'pending'`,
|
|
120
140
|
];
|
|
121
141
|
/**
|
|
122
142
|
* `cell_grant` table — resource-side ACL for cells. Each row admits a
|
|
@@ -46,6 +46,9 @@ export interface CellRow {
|
|
|
46
46
|
visibility: CellVisibility;
|
|
47
47
|
path: string | null;
|
|
48
48
|
refs: Array<FactHash> | null;
|
|
49
|
+
parent_id: Uuid | null;
|
|
50
|
+
root_id: Uuid | null;
|
|
51
|
+
moderation: string | null;
|
|
49
52
|
created_at: Date;
|
|
50
53
|
updated_at: Date | null;
|
|
51
54
|
deleted_at: Date | null;
|
|
@@ -57,12 +60,19 @@ export interface CellRow {
|
|
|
57
60
|
* Input for `query_cell_create`. `refs` is derived from `data`. `kind` is
|
|
58
61
|
* the write-once capability axis (`cell.kind` column) — set here at INSERT
|
|
59
62
|
* and absent from `CellUpdatePatch`, so it can never change post-create.
|
|
63
|
+
* `parent_id` / `root_id` / `moderation` are the directory-tree + lifecycle
|
|
64
|
+
* columns the handler derives (parent containment, governing root, the
|
|
65
|
+
* create-authorizer verdict) — likewise write-once / control-gated, never on
|
|
66
|
+
* `CellUpdatePatch`.
|
|
60
67
|
*/
|
|
61
68
|
export interface CellCreateQueryInput {
|
|
62
69
|
data: Json;
|
|
63
70
|
kind?: string | null;
|
|
64
71
|
visibility?: CellVisibility;
|
|
65
72
|
path?: string | null;
|
|
73
|
+
parent_id?: Uuid | null;
|
|
74
|
+
root_id?: Uuid | null;
|
|
75
|
+
moderation?: string | null;
|
|
66
76
|
created_by?: Uuid | null;
|
|
67
77
|
}
|
|
68
78
|
/**
|
|
@@ -158,6 +168,28 @@ export declare const query_cell_update: (deps: QueryDeps, id: Uuid, patch: CellU
|
|
|
158
168
|
export declare const query_cell_delete: (deps: QueryDeps, id: Uuid, options?: {
|
|
159
169
|
deleted_by?: Uuid | null;
|
|
160
170
|
}) => Promise<boolean>;
|
|
171
|
+
/**
|
|
172
|
+
* Transition a contribution's `moderation` marker (the `cell_moderate` verb's
|
|
173
|
+
* write). On `set_visibility_public` (an approval) it also flips `visibility`
|
|
174
|
+
* to `'public'` so the approved contribution goes live; a rejection leaves
|
|
175
|
+
* visibility untouched (stays private). `updated_at` / `updated_by` are stamped.
|
|
176
|
+
*
|
|
177
|
+
* `moderation` is deliberately **not** writable through `query_cell_update`
|
|
178
|
+
* (it's absent from `CellUpdatePatch`) — gating the transition on a dedicated
|
|
179
|
+
* query (with the manage-tier authority check in the handler) is what stops an
|
|
180
|
+
* author self-approving their own pending cell.
|
|
181
|
+
*
|
|
182
|
+
* @param deps - query deps
|
|
183
|
+
* @param id - cell id
|
|
184
|
+
* @param moderation - the terminal marker to write (`'approved'` | `'rejected'`)
|
|
185
|
+
* @param options - `set_visibility_public` flips visibility on approval; `updated_by` records the moderator
|
|
186
|
+
* @returns the updated row, or `null` when no active row matched (raced delete)
|
|
187
|
+
* @mutates `cell` - sets `moderation` (+ maybe `visibility`) on one row
|
|
188
|
+
*/
|
|
189
|
+
export declare const query_cell_set_moderation: (deps: QueryDeps, id: Uuid, moderation: string, options: {
|
|
190
|
+
set_visibility_public: boolean;
|
|
191
|
+
updated_by?: Uuid | null;
|
|
192
|
+
}) => Promise<CellRow | null>;
|
|
161
193
|
/**
|
|
162
194
|
* List active cells with the given `kind`, newest first. Uses the
|
|
163
195
|
* `idx_cell_kind` index (`cell.kind = ?`).
|
|
@@ -246,6 +278,19 @@ export interface CellListParams {
|
|
|
246
278
|
* literal matching.
|
|
247
279
|
*/
|
|
248
280
|
path_prefix?: string;
|
|
281
|
+
/** Scope to a directory subtree by governing root (`cell.root_id = ?`, uses `idx_cell_root`). */
|
|
282
|
+
root_id?: Uuid;
|
|
283
|
+
/**
|
|
284
|
+
* Filter by moderation lifecycle marker (`'pending'` drives the mod queue via
|
|
285
|
+
* `idx_cell_moderation_pending`).
|
|
286
|
+
*
|
|
287
|
+
* Note: the visibility predicate still applies, so a `'pending'` (private)
|
|
288
|
+
* queue surfaces only to viewers it admits — admin / owner / grant. A
|
|
289
|
+
* non-admin container manager won't see pending children here until a
|
|
290
|
+
* manager-scoped visibility branch is added, so for now the queue is an admin
|
|
291
|
+
* surface (`cell_moderate` of a known id still works regardless).
|
|
292
|
+
*/
|
|
293
|
+
moderation?: string;
|
|
249
294
|
/**
|
|
250
295
|
* Batch-fetch by id. The visibility predicate still runs, so callers
|
|
251
296
|
* passing ids they can't view simply get fewer rows back. Order of
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/cell_queries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,0BAA0B,CAAC;AACnD,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAyB,KAAK,QAAQ,EAAC,MAAM,+BAA+B,CAAC;AAGpF,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,6BAA6B,CAAC;AAC1D,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,8BAA8B,CAAC;AAEjE;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,OAAO;IACvB,EAAE,EAAE,IAAI,CAAC;IACT,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,cAAc,CAAC;IAC3B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,IAAI,CAAC;IACjB,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACpB;AAgBD
|
|
1
|
+
{"version":3,"file":"cell_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/cell_queries.ts"],"names":[],"mappings":"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"}
|