@fuzdev/fuz_app 0.141.0 → 0.142.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/actions/CLAUDE.md +10 -5
- package/dist/actions/connection_closer.d.ts +19 -2
- package/dist/actions/connection_closer.d.ts.map +1 -1
- package/dist/actions/connection_closer.js +25 -2
- package/dist/actions/perform_action.d.ts +10 -0
- package/dist/actions/perform_action.d.ts.map +1 -1
- package/dist/actions/perform_action.js +23 -9
- package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
- package/dist/actions/register_ws_endpoint.js +5 -2
- package/dist/auth/CLAUDE.md +33 -13
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +12 -1
- package/dist/auth/account_route_schema.d.ts.map +1 -1
- package/dist/auth/account_route_schema.js +8 -0
- package/dist/auth/account_routes.d.ts +7 -2
- package/dist/auth/account_routes.d.ts.map +1 -1
- package/dist/auth/account_routes.js +240 -187
- package/dist/auth/admin_action_specs.d.ts +6 -1
- package/dist/auth/admin_action_specs.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.js +6 -1
- package/dist/auth/admin_actions.d.ts.map +1 -1
- package/dist/auth/admin_actions.js +14 -2
- package/dist/auth/bootstrap_routes.d.ts.map +1 -1
- package/dist/auth/bootstrap_routes.js +85 -76
- package/dist/auth/cell_action_specs.d.ts +7 -3
- package/dist/auth/cell_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_action_specs.js +9 -5
- package/dist/auth/cell_actions.d.ts +3 -3
- package/dist/auth/cell_actions.d.ts.map +1 -1
- package/dist/auth/cell_actions.js +67 -38
- package/dist/auth/cell_authorize.d.ts +69 -8
- package/dist/auth/cell_authorize.d.ts.map +1 -1
- package/dist/auth/cell_authorize.js +89 -9
- package/dist/auth/cell_field_action_specs.d.ts +5 -4
- package/dist/auth/cell_field_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_field_action_specs.js +7 -7
- package/dist/auth/cell_field_actions.d.ts +7 -5
- package/dist/auth/cell_field_actions.d.ts.map +1 -1
- package/dist/auth/cell_field_actions.js +24 -22
- package/dist/auth/cell_grant_action_specs.d.ts +6 -0
- package/dist/auth/cell_grant_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_grant_action_specs.js +6 -0
- package/dist/auth/cell_grant_actions.d.ts +3 -1
- package/dist/auth/cell_grant_actions.d.ts.map +1 -1
- package/dist/auth/cell_grant_actions.js +16 -2
- package/dist/auth/cell_item_action_specs.d.ts +5 -4
- package/dist/auth/cell_item_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_item_action_specs.js +7 -7
- package/dist/auth/cell_item_actions.d.ts +6 -5
- package/dist/auth/cell_item_actions.d.ts.map +1 -1
- package/dist/auth/cell_item_actions.js +23 -22
- package/dist/auth/cell_relation_list_limit.d.ts +20 -0
- package/dist/auth/cell_relation_list_limit.d.ts.map +1 -0
- package/dist/auth/cell_relation_list_limit.js +27 -0
- package/dist/auth/cell_relation_visibility.d.ts +4 -3
- package/dist/auth/cell_relation_visibility.d.ts.map +1 -1
- package/dist/auth/cell_relation_visibility.js +6 -5
- package/dist/auth/cell_steward.d.ts +45 -0
- package/dist/auth/cell_steward.d.ts.map +1 -0
- package/dist/auth/cell_steward.js +53 -0
- package/dist/auth/request_context.d.ts +4 -0
- package/dist/auth/request_context.d.ts.map +1 -1
- package/dist/auth/request_context.js +6 -1
- package/dist/auth/role_grant_offer_action_specs.d.ts +5 -0
- package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_action_specs.js +6 -1
- package/dist/auth/role_grant_offer_actions.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_actions.js +23 -16
- package/dist/auth/role_grant_offer_queries.d.ts +8 -6
- package/dist/auth/role_grant_offer_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_queries.js +16 -8
- package/dist/auth/signup_routes.d.ts +4 -3
- package/dist/auth/signup_routes.d.ts.map +1 -1
- package/dist/auth/signup_routes.js +157 -134
- package/dist/auth/token_scope.d.ts +7 -1
- package/dist/auth/token_scope.d.ts.map +1 -1
- package/dist/auth/token_scope.js +8 -2
- package/dist/db/CLAUDE.md +32 -19
- package/dist/db/cell_ddl.d.ts +1 -2
- package/dist/db/cell_ddl.d.ts.map +1 -1
- package/dist/db/cell_ddl.js +1 -2
- package/dist/db/cell_field_queries.d.ts +20 -18
- package/dist/db/cell_field_queries.d.ts.map +1 -1
- package/dist/db/cell_field_queries.js +28 -22
- package/dist/db/cell_item_queries.d.ts +20 -14
- package/dist/db/cell_item_queries.d.ts.map +1 -1
- package/dist/db/cell_item_queries.js +28 -18
- package/dist/db/cell_moderation.d.ts +67 -0
- package/dist/db/cell_moderation.d.ts.map +1 -0
- package/dist/db/cell_moderation.js +66 -0
- package/dist/db/cell_queries.d.ts +44 -14
- package/dist/db/cell_queries.d.ts.map +1 -1
- package/dist/db/cell_queries.js +68 -45
- package/dist/db/cell_view_sql.d.ts +102 -0
- package/dist/db/cell_view_sql.d.ts.map +1 -0
- package/dist/db/cell_view_sql.js +112 -0
- package/dist/db/db_pg.d.ts +1 -1
- package/dist/db/db_pg.js +1 -1
- package/dist/http/auth_shape.d.ts +5 -2
- package/dist/http/auth_shape.d.ts.map +1 -1
- package/dist/http/auth_shape.js +5 -2
- package/dist/http/ip_canonical.d.ts +30 -3
- package/dist/http/ip_canonical.d.ts.map +1 -1
- package/dist/http/ip_canonical.js +50 -3
- package/dist/primitive_schemas.d.ts +10 -1
- package/dist/primitive_schemas.d.ts.map +1 -1
- package/dist/primitive_schemas.js +11 -2
- package/dist/rate_limiter.d.ts +179 -12
- package/dist/rate_limiter.d.ts.map +1 -1
- package/dist/rate_limiter.js +352 -42
- package/dist/server/serve_fact_route.d.ts +8 -4
- package/dist/server/serve_fact_route.d.ts.map +1 -1
- package/dist/server/serve_fact_route.js +15 -6
- package/dist/testing/CLAUDE.md +10 -3
- package/dist/testing/cross_backend/account_lifecycle.d.ts.map +1 -1
- package/dist/testing/cross_backend/account_lifecycle.js +37 -2
- package/dist/testing/cross_backend/cell_gated_create.d.ts +38 -0
- package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_gated_create.js +401 -5
- package/dist/testing/cross_backend/cell_relations.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_relations.js +145 -2
- package/dist/testing/cross_backend/expected_schema.json +0 -1
- package/dist/testing/cross_backend/login_security.d.ts +16 -0
- package/dist/testing/cross_backend/login_security.d.ts.map +1 -1
- package/dist/testing/cross_backend/login_security.js +154 -0
- package/dist/testing/cross_backend/query_shape.d.ts.map +1 -1
- package/dist/testing/cross_backend/query_shape.js +24 -2
- package/dist/testing/cross_backend/role_grant_participation.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_participation.js +64 -0
- package/dist/testing/cross_backend/spine_surface_constants.d.ts +1 -1
- package/dist/testing/cross_backend/spine_surface_constants.js +1 -1
- package/dist/testing/cross_backend/ws_action_rate_limit.d.ts.map +1 -1
- package/dist/testing/cross_backend/ws_action_rate_limit.js +21 -1
- package/dist/testing/schema_introspect.js +1 -1
- package/dist/testing/schema_parity.d.ts +4 -3
- package/dist/testing/schema_parity.d.ts.map +1 -1
- package/dist/testing/schema_parity.js +4 -3
- package/dist/testing/schema_ready_fixture.d.ts +2 -4
- package/dist/testing/schema_ready_fixture.d.ts.map +1 -1
- package/dist/testing/schema_ready_fixture.js +2 -4
- package/dist/ui/auth_state.svelte.d.ts +10 -0
- package/dist/ui/auth_state.svelte.d.ts.map +1 -1
- package/dist/ui/auth_state.svelte.js +22 -4
- package/package.json +1 -1
- package/dist/db/cell_history_ddl.d.ts +0 -38
- package/dist/db/cell_history_ddl.d.ts.map +0 -1
- package/dist/db/cell_history_ddl.js +0 -61
|
@@ -8,7 +8,10 @@
|
|
|
8
8
|
* not by allowing duplicate field rows.
|
|
9
9
|
*
|
|
10
10
|
* Reads filter both endpoints by `cell.deleted_at IS NULL` so relations
|
|
11
|
-
* dangling off a soft-deleted cell don't surface to the live graph
|
|
11
|
+
* dangling off a soft-deleted cell don't surface to the live graph, and the
|
|
12
|
+
* list reads apply the view predicate (`db/cell_view_sql.ts`) to the far end
|
|
13
|
+
* before `LIMIT` — a page is up to `limit` edges whose far end the caller can
|
|
14
|
+
* view.
|
|
12
15
|
*
|
|
13
16
|
* `query_cell_field_set` upserts on the `(source_id, name)` PK so
|
|
14
17
|
* re-pointing a name updates `target_id` in place — JSON-object semantics
|
|
@@ -18,6 +21,7 @@
|
|
|
18
21
|
*/
|
|
19
22
|
import type { QueryDeps } from './query_deps.js';
|
|
20
23
|
import type { Uuid } from '../primitive_schemas.js';
|
|
24
|
+
import { type CellViewer } from './cell_view_sql.js';
|
|
21
25
|
/** Row shape returned by `cell_field` SELECTs. */
|
|
22
26
|
export interface CellFieldRow {
|
|
23
27
|
source_id: Uuid;
|
|
@@ -77,38 +81,36 @@ export declare const query_cell_field_delete: (deps: QueryDeps, source_id: Uuid,
|
|
|
77
81
|
/**
|
|
78
82
|
* Forward fields list (`source.fields[]`).
|
|
79
83
|
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
84
|
+
* Up to `limit` fields whose target is live and one `viewer` may view
|
|
85
|
+
* (`can_view_cell_sql`, applied before the limit); `name_after` pages on.
|
|
86
|
+
* The source-view gate is the caller's responsibility.
|
|
83
87
|
*
|
|
84
88
|
* @param deps - query deps
|
|
89
|
+
* @param viewer - who is reading; `CELL_VIEWER_UNRESTRICTED` filters nothing
|
|
85
90
|
* @param source_id - source cell id
|
|
86
|
-
* @param options - `limit` caps the row count; `name_after`
|
|
87
|
-
* name (keyset pagination)
|
|
91
|
+
* @param options - `limit` caps the row count (omitted = unbounded); `name_after`
|
|
92
|
+
* resumes after a name (keyset pagination)
|
|
88
93
|
* @returns matching rows in lex `name` order
|
|
89
94
|
*/
|
|
90
|
-
export declare const query_cell_field_list_for_source: (deps: QueryDeps, source_id: Uuid, options?: {
|
|
95
|
+
export declare const query_cell_field_list_for_source: (deps: QueryDeps, viewer: CellViewer, source_id: Uuid, options?: {
|
|
91
96
|
limit?: number;
|
|
92
97
|
name_after?: string;
|
|
93
98
|
}) => Promise<Array<CellFieldRow>>;
|
|
94
99
|
/**
|
|
95
100
|
* Reverse fields list (`target.upfields[]`).
|
|
96
101
|
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
* Bounded by `limit` (the wire `cell_field_list` cap) so a heavily
|
|
103
|
-
* inbound-linked target can't force an unbounded fetch + per-source authz
|
|
104
|
-
* pass on the public, IP-rate-limited reverse endpoint.
|
|
102
|
+
* Up to `limit` fields whose source is live and one `viewer` may view —
|
|
103
|
+
* filtered before the limit (`can_view_cell_sql`), so sources the caller
|
|
104
|
+
* can't see never crowd out ones it can. The target-view gate is the
|
|
105
|
+
* caller's responsibility.
|
|
105
106
|
*
|
|
106
107
|
* @param deps - query deps
|
|
108
|
+
* @param viewer - who is reading; `CELL_VIEWER_UNRESTRICTED` filters nothing
|
|
107
109
|
* @param target_id - target cell id
|
|
108
|
-
* @param options - `limit` caps the row count
|
|
109
|
-
* @returns matching rows, oldest field first (by the field row's `created_at
|
|
110
|
+
* @param options - `limit` caps the row count (omitted = unbounded)
|
|
111
|
+
* @returns matching rows, oldest field first (by the field row's `created_at`, then its key)
|
|
110
112
|
*/
|
|
111
|
-
export declare const query_cell_field_list_for_target: (deps: QueryDeps, target_id: Uuid, options?: {
|
|
113
|
+
export declare const query_cell_field_list_for_target: (deps: QueryDeps, viewer: CellViewer, target_id: Uuid, options?: {
|
|
112
114
|
limit?: number;
|
|
113
115
|
}) => Promise<Array<CellFieldRow>>;
|
|
114
116
|
//# sourceMappingURL=cell_field_queries.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell_field_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/cell_field_queries.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"cell_field_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/cell_field_queries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAGpD,OAAO,EAIN,KAAK,UAAU,EACf,MAAM,oBAAoB,CAAC;AAE5B,kDAAkD;AAClD,MAAM,WAAW,YAAY;IAC5B,SAAS,EAAE,IAAI,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,IAAI,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,2DAKuB,CAAC;AAKvD,wCAAwC;AACxC,MAAM,WAAW,sBAAsB;IACtC,SAAS,EAAE,IAAI,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,IAAI,CAAC;CAChB;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB,GAChC,MAAM,SAAS,EACf,OAAO,sBAAsB,KAC3B,OAAO,CAAC,YAAY,CAUtB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,GAChC,MAAM,SAAS,EACf,WAAW,IAAI,EACf,MAAM,MAAM,KACV,OAAO,CAAC,YAAY,GAAG,IAAI,CAM7B,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,GACnC,MAAM,SAAS,EACf,WAAW,IAAI,EACf,MAAM,MAAM,KACV,OAAO,CAAC,YAAY,GAAG,IAAI,CAO7B,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,gCAAgC,GAC5C,MAAM,SAAS,EACf,QAAQ,UAAU,EAClB,WAAW,IAAI,EACf,UAAU;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,KAC/C,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAe7B,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,gCAAgC,GAC5C,MAAM,SAAS,EACf,QAAQ,UAAU,EAClB,WAAW,IAAI,EACf,UAAU;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,KAC1B,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAW5B,CAAC"}
|
|
@@ -8,7 +8,10 @@
|
|
|
8
8
|
* not by allowing duplicate field rows.
|
|
9
9
|
*
|
|
10
10
|
* Reads filter both endpoints by `cell.deleted_at IS NULL` so relations
|
|
11
|
-
* dangling off a soft-deleted cell don't surface to the live graph
|
|
11
|
+
* dangling off a soft-deleted cell don't surface to the live graph, and the
|
|
12
|
+
* list reads apply the view predicate (`db/cell_view_sql.ts`) to the far end
|
|
13
|
+
* before `LIMIT` — a page is up to `limit` edges whose far end the caller can
|
|
14
|
+
* view.
|
|
12
15
|
*
|
|
13
16
|
* `query_cell_field_set` upserts on the `(source_id, name)` PK so
|
|
14
17
|
* re-pointing a name updates `target_id` in place — JSON-object semantics
|
|
@@ -18,6 +21,7 @@
|
|
|
18
21
|
*/
|
|
19
22
|
import { assert_row } from './assert_row.js';
|
|
20
23
|
import { columns_sql, iso8601_timestamp_expr, qualify_columns } from './sql_columns.js';
|
|
24
|
+
import { can_view_cell_sql, caller_role_grants_cte, cell_viewer_params } from './cell_view_sql.js';
|
|
21
25
|
/**
|
|
22
26
|
* The full `cell_field` column set, named explicitly so a row read fails
|
|
23
27
|
* loud on schema drift (see `CELL_COLUMNS` in `db/cell_queries.ts`). Column
|
|
@@ -85,47 +89,49 @@ export const query_cell_field_delete = async (deps, source_id, name) => {
|
|
|
85
89
|
/**
|
|
86
90
|
* Forward fields list (`source.fields[]`).
|
|
87
91
|
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
92
|
+
* Up to `limit` fields whose target is live and one `viewer` may view
|
|
93
|
+
* (`can_view_cell_sql`, applied before the limit); `name_after` pages on.
|
|
94
|
+
* The source-view gate is the caller's responsibility.
|
|
91
95
|
*
|
|
92
96
|
* @param deps - query deps
|
|
97
|
+
* @param viewer - who is reading; `CELL_VIEWER_UNRESTRICTED` filters nothing
|
|
93
98
|
* @param source_id - source cell id
|
|
94
|
-
* @param options - `limit` caps the row count; `name_after`
|
|
95
|
-
* name (keyset pagination)
|
|
99
|
+
* @param options - `limit` caps the row count (omitted = unbounded); `name_after`
|
|
100
|
+
* resumes after a name (keyset pagination)
|
|
96
101
|
* @returns matching rows in lex `name` order
|
|
97
102
|
*/
|
|
98
|
-
export const query_cell_field_list_for_source = async (deps, source_id, options) => {
|
|
103
|
+
export const query_cell_field_list_for_source = async (deps, viewer, source_id, options) => {
|
|
99
104
|
const limit = options?.limit ?? null;
|
|
100
105
|
const name_after = options?.name_after ?? null;
|
|
101
|
-
return deps.db.query(`
|
|
106
|
+
return deps.db.query(`WITH ${caller_role_grants_cte('$6', '$7')}
|
|
107
|
+
SELECT ${qualify_columns(CELL_FIELD_COLUMNS, 'f', cell_field_expr('f'))} FROM cell_field f
|
|
102
108
|
JOIN cell t ON t.id = f.target_id
|
|
103
109
|
WHERE f.source_id = $1
|
|
104
110
|
AND t.deleted_at IS NULL
|
|
111
|
+
AND ${can_view_cell_sql('t', { is_admin: '$4', actor_id: '$5' })}
|
|
105
112
|
AND ($3::text IS NULL OR f.name > $3)
|
|
106
113
|
ORDER BY f.name ASC
|
|
107
|
-
LIMIT $2`, [source_id, limit, name_after]);
|
|
114
|
+
LIMIT $2`, [source_id, limit, name_after, ...cell_viewer_params(viewer)]);
|
|
108
115
|
};
|
|
109
116
|
/**
|
|
110
117
|
* Reverse fields list (`target.upfields[]`).
|
|
111
118
|
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
* Bounded by `limit` (the wire `cell_field_list` cap) so a heavily
|
|
118
|
-
* inbound-linked target can't force an unbounded fetch + per-source authz
|
|
119
|
-
* pass on the public, IP-rate-limited reverse endpoint.
|
|
119
|
+
* Up to `limit` fields whose source is live and one `viewer` may view —
|
|
120
|
+
* filtered before the limit (`can_view_cell_sql`), so sources the caller
|
|
121
|
+
* can't see never crowd out ones it can. The target-view gate is the
|
|
122
|
+
* caller's responsibility.
|
|
120
123
|
*
|
|
121
124
|
* @param deps - query deps
|
|
125
|
+
* @param viewer - who is reading; `CELL_VIEWER_UNRESTRICTED` filters nothing
|
|
122
126
|
* @param target_id - target cell id
|
|
123
|
-
* @param options - `limit` caps the row count
|
|
124
|
-
* @returns matching rows, oldest field first (by the field row's `created_at
|
|
127
|
+
* @param options - `limit` caps the row count (omitted = unbounded)
|
|
128
|
+
* @returns matching rows, oldest field first (by the field row's `created_at`, then its key)
|
|
125
129
|
*/
|
|
126
|
-
export const query_cell_field_list_for_target = async (deps, target_id, options) => deps.db.query(`
|
|
130
|
+
export const query_cell_field_list_for_target = async (deps, viewer, target_id, options) => deps.db.query(`WITH ${caller_role_grants_cte('$5', '$6')}
|
|
131
|
+
SELECT ${qualify_columns(CELL_FIELD_COLUMNS, 'f', cell_field_expr('f'))} FROM cell_field f
|
|
127
132
|
JOIN cell s ON s.id = f.source_id
|
|
128
133
|
WHERE f.target_id = $1
|
|
129
134
|
AND s.deleted_at IS NULL
|
|
130
|
-
|
|
131
|
-
|
|
135
|
+
AND ${can_view_cell_sql('s', { is_admin: '$3', actor_id: '$4' })}
|
|
136
|
+
ORDER BY f.created_at ASC, f.source_id ASC, f.name ASC
|
|
137
|
+
LIMIT $2`, [target_id, options?.limit ?? null, ...cell_viewer_params(viewer)]);
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
* domain dedup rules ride on top).
|
|
10
10
|
*
|
|
11
11
|
* Reads filter both endpoints by `cell.deleted_at IS NULL` so items
|
|
12
|
-
* dangling off a soft-deleted cell don't surface
|
|
12
|
+
* dangling off a soft-deleted cell don't surface, and the list reads apply
|
|
13
|
+
* the view predicate (`db/cell_view_sql.ts`) to the far end before `LIMIT` —
|
|
14
|
+
* a page is up to `limit` edges whose far end the caller can view.
|
|
13
15
|
*
|
|
14
16
|
* `query_cell_item_insert` returns the inserted row OR throws the
|
|
15
17
|
* underlying `23505` (Postgres unique violation) on a `(parent_id,
|
|
@@ -21,6 +23,7 @@
|
|
|
21
23
|
*/
|
|
22
24
|
import type { QueryDeps } from './query_deps.js';
|
|
23
25
|
import type { Uuid } from '../primitive_schemas.js';
|
|
26
|
+
import { type CellViewer } from './cell_view_sql.js';
|
|
24
27
|
/** Row shape returned by `cell_item` SELECTs. */
|
|
25
28
|
export interface CellItemRow {
|
|
26
29
|
parent_id: Uuid;
|
|
@@ -86,33 +89,36 @@ export declare const query_cell_item_delete: (deps: QueryDeps, parent_id: Uuid,
|
|
|
86
89
|
/**
|
|
87
90
|
* Forward items list (`parent.items[]`), ordered by lex `position`.
|
|
88
91
|
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
+
* Up to `limit` items whose child is live and one `viewer` may view
|
|
93
|
+
* (`can_view_cell_sql`, applied before the limit); `position_after` pages on.
|
|
94
|
+
* The parent-view gate is the caller's responsibility.
|
|
92
95
|
*
|
|
93
96
|
* @param deps - query deps
|
|
97
|
+
* @param viewer - who is reading; `CELL_VIEWER_UNRESTRICTED` filters nothing
|
|
94
98
|
* @param parent_id - parent cell id
|
|
95
|
-
* @param options - `limit` caps the row count (
|
|
99
|
+
* @param options - `limit` caps the row count (omitted = unbounded);
|
|
96
100
|
* `position_after` resumes after a position (keyset pagination)
|
|
97
101
|
* @returns matching rows in lex `position` order
|
|
98
102
|
*/
|
|
99
|
-
export declare const query_cell_item_list_for_parent: (deps: QueryDeps, parent_id: Uuid, options?: {
|
|
103
|
+
export declare const query_cell_item_list_for_parent: (deps: QueryDeps, viewer: CellViewer, parent_id: Uuid, options?: {
|
|
100
104
|
limit?: number;
|
|
101
105
|
position_after?: string;
|
|
102
106
|
}) => Promise<Array<CellItemRow>>;
|
|
103
107
|
/**
|
|
104
108
|
* Reverse items list (`child.lists[]`).
|
|
105
109
|
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
+
* Up to `limit` items whose parent is live and one `viewer` may view —
|
|
111
|
+
* filtered before the limit (`can_view_cell_sql`), so parents the caller
|
|
112
|
+
* can't see never crowd out ones it can. The child-view gate is the
|
|
113
|
+
* caller's responsibility.
|
|
110
114
|
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
115
|
+
* @param deps - query deps
|
|
116
|
+
* @param viewer - who is reading; `CELL_VIEWER_UNRESTRICTED` filters nothing
|
|
117
|
+
* @param child_id - child cell id
|
|
118
|
+
* @param options - `limit` caps the row count (omitted = unbounded)
|
|
119
|
+
* @returns matching rows, oldest item first (by the item row's `created_at`, then its key)
|
|
114
120
|
*/
|
|
115
|
-
export declare const query_cell_item_list_for_child: (deps: QueryDeps, child_id: Uuid, options?: {
|
|
121
|
+
export declare const query_cell_item_list_for_child: (deps: QueryDeps, viewer: CellViewer, child_id: Uuid, options?: {
|
|
116
122
|
limit?: number;
|
|
117
123
|
}) => Promise<Array<CellItemRow>>;
|
|
118
124
|
//# sourceMappingURL=cell_item_queries.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell_item_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/cell_item_queries.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"cell_item_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/cell_item_queries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAGpD,OAAO,EAIN,KAAK,UAAU,EACf,MAAM,oBAAoB,CAAC;AAE5B,iDAAiD;AACjD,MAAM,WAAW,WAAW;IAC3B,SAAS,EAAE,IAAI,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,IAAI,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,8DAKuB,CAAC;AAKtD,0CAA0C;AAC1C,MAAM,WAAW,wBAAwB;IACxC,SAAS,EAAE,IAAI,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,IAAI,CAAC;CACf;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,SAAS,EACf,OAAO,wBAAwB,KAC7B,OAAO,CAAC,WAAW,CAQrB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAC/B,MAAM,SAAS,EACf,WAAW,IAAI,EACf,UAAU,MAAM,KACd,OAAO,CAAC,WAAW,GAAG,IAAI,CAM5B,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB,GAChC,MAAM,SAAS,EACf,WAAW,IAAI,EACf,cAAc,MAAM,EACpB,cAAc,MAAM,KAClB,OAAO,CAAC,WAAW,GAAG,IAAI,CAS5B,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,SAAS,EACf,WAAW,IAAI,EACf,UAAU,MAAM,KACd,OAAO,CAAC,WAAW,GAAG,IAAI,CAO5B,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,+BAA+B,GAC3C,MAAM,SAAS,EACf,QAAQ,UAAU,EAClB,WAAW,IAAI,EACf,UAAU;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,KACnD,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAe5B,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,8BAA8B,GAC1C,MAAM,SAAS,EACf,QAAQ,UAAU,EAClB,UAAU,IAAI,EACd,UAAU;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,KAC1B,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAW3B,CAAC"}
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
* domain dedup rules ride on top).
|
|
10
10
|
*
|
|
11
11
|
* Reads filter both endpoints by `cell.deleted_at IS NULL` so items
|
|
12
|
-
* dangling off a soft-deleted cell don't surface
|
|
12
|
+
* dangling off a soft-deleted cell don't surface, and the list reads apply
|
|
13
|
+
* the view predicate (`db/cell_view_sql.ts`) to the far end before `LIMIT` —
|
|
14
|
+
* a page is up to `limit` edges whose far end the caller can view.
|
|
13
15
|
*
|
|
14
16
|
* `query_cell_item_insert` returns the inserted row OR throws the
|
|
15
17
|
* underlying `23505` (Postgres unique violation) on a `(parent_id,
|
|
@@ -21,6 +23,7 @@
|
|
|
21
23
|
*/
|
|
22
24
|
import { assert_row } from './assert_row.js';
|
|
23
25
|
import { columns_sql, iso8601_timestamp_expr, qualify_columns } from './sql_columns.js';
|
|
26
|
+
import { can_view_cell_sql, caller_role_grants_cte, cell_viewer_params } from './cell_view_sql.js';
|
|
24
27
|
/**
|
|
25
28
|
* The full `cell_item` column set, named explicitly so a row read fails
|
|
26
29
|
* loud on schema drift (see `CELL_COLUMNS` in `db/cell_queries.ts`). Column
|
|
@@ -98,42 +101,49 @@ export const query_cell_item_delete = async (deps, parent_id, position) => {
|
|
|
98
101
|
/**
|
|
99
102
|
* Forward items list (`parent.items[]`), ordered by lex `position`.
|
|
100
103
|
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
+
* Up to `limit` items whose child is live and one `viewer` may view
|
|
105
|
+
* (`can_view_cell_sql`, applied before the limit); `position_after` pages on.
|
|
106
|
+
* The parent-view gate is the caller's responsibility.
|
|
104
107
|
*
|
|
105
108
|
* @param deps - query deps
|
|
109
|
+
* @param viewer - who is reading; `CELL_VIEWER_UNRESTRICTED` filters nothing
|
|
106
110
|
* @param parent_id - parent cell id
|
|
107
|
-
* @param options - `limit` caps the row count (
|
|
111
|
+
* @param options - `limit` caps the row count (omitted = unbounded);
|
|
108
112
|
* `position_after` resumes after a position (keyset pagination)
|
|
109
113
|
* @returns matching rows in lex `position` order
|
|
110
114
|
*/
|
|
111
|
-
export const query_cell_item_list_for_parent = async (deps, parent_id, options) => {
|
|
115
|
+
export const query_cell_item_list_for_parent = async (deps, viewer, parent_id, options) => {
|
|
112
116
|
const limit = options?.limit ?? null;
|
|
113
117
|
const position_after = options?.position_after ?? null;
|
|
114
|
-
return deps.db.query(`
|
|
118
|
+
return deps.db.query(`WITH ${caller_role_grants_cte('$6', '$7')}
|
|
119
|
+
SELECT ${qualify_columns(CELL_ITEM_COLUMNS, 'i', cell_item_expr('i'))} FROM cell_item i
|
|
115
120
|
JOIN cell c ON c.id = i.child_id
|
|
116
121
|
WHERE i.parent_id = $1
|
|
117
122
|
AND c.deleted_at IS NULL
|
|
123
|
+
AND ${can_view_cell_sql('c', { is_admin: '$4', actor_id: '$5' })}
|
|
118
124
|
AND ($3::text IS NULL OR i.position > $3)
|
|
119
125
|
ORDER BY i.position ASC
|
|
120
|
-
LIMIT $2`, [parent_id, limit, position_after]);
|
|
126
|
+
LIMIT $2`, [parent_id, limit, position_after, ...cell_viewer_params(viewer)]);
|
|
121
127
|
};
|
|
122
128
|
/**
|
|
123
129
|
* Reverse items list (`child.lists[]`).
|
|
124
130
|
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
131
|
+
* Up to `limit` items whose parent is live and one `viewer` may view —
|
|
132
|
+
* filtered before the limit (`can_view_cell_sql`), so parents the caller
|
|
133
|
+
* can't see never crowd out ones it can. The child-view gate is the
|
|
134
|
+
* caller's responsibility.
|
|
129
135
|
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
136
|
+
* @param deps - query deps
|
|
137
|
+
* @param viewer - who is reading; `CELL_VIEWER_UNRESTRICTED` filters nothing
|
|
138
|
+
* @param child_id - child cell id
|
|
139
|
+
* @param options - `limit` caps the row count (omitted = unbounded)
|
|
140
|
+
* @returns matching rows, oldest item first (by the item row's `created_at`, then its key)
|
|
133
141
|
*/
|
|
134
|
-
export const query_cell_item_list_for_child = async (deps, child_id, options) => deps.db.query(`
|
|
142
|
+
export const query_cell_item_list_for_child = async (deps, viewer, child_id, options) => deps.db.query(`WITH ${caller_role_grants_cte('$5', '$6')}
|
|
143
|
+
SELECT ${qualify_columns(CELL_ITEM_COLUMNS, 'i', cell_item_expr('i'))} FROM cell_item i
|
|
135
144
|
JOIN cell p ON p.id = i.parent_id
|
|
136
145
|
WHERE i.child_id = $1
|
|
137
146
|
AND p.deleted_at IS NULL
|
|
138
|
-
|
|
139
|
-
|
|
147
|
+
AND ${can_view_cell_sql('p', { is_admin: '$3', actor_id: '$4' })}
|
|
148
|
+
ORDER BY i.created_at ASC, i.parent_id ASC, i.position ASC
|
|
149
|
+
LIMIT $2`, [child_id, options?.limit ?? null, ...cell_viewer_params(viewer)]);
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `cell.moderation` read rules, in pure and SQL form side by side.
|
|
3
|
+
*
|
|
4
|
+
* `moderation` is free `TEXT` with no CHECK, so each rule is an allowlist over
|
|
5
|
+
* the literals: a marker this module doesn't name (a typo, a future state)
|
|
6
|
+
* admits nothing.
|
|
7
|
+
*
|
|
8
|
+
* **Audience admission** — `cell_moderation_admits_audience` and
|
|
9
|
+
* `cell_moderation_admits_audience_sql`: unmoderated (`null`) or `'approved'`.
|
|
10
|
+
* A cell's effective audience is its visibility (or grants) **and** its
|
|
11
|
+
* admission, so the public and grant read arms of `can_view_cell` require it,
|
|
12
|
+
* as do the `editor` grant arm of `can_edit_cell` and the matching arms of
|
|
13
|
+
* `query_cell_list` (general and `shared_with` shapes). The owner and
|
|
14
|
+
* global-admin arms don't: an author keeps reading and editing their own
|
|
15
|
+
* pending or rejected contribution, and an admin reads everything.
|
|
16
|
+
*
|
|
17
|
+
* **Stewardship** — `cell_moderation_is_stewardable` and
|
|
18
|
+
* `cell_moderation_is_stewardable_sql`: exactly `'pending'` or `'rejected'`, the
|
|
19
|
+
* states in which the governing root's manager reads a contribution its
|
|
20
|
+
* audience can't (`can_steward_cell`). An unknown marker is neither admitted
|
|
21
|
+
* nor stewardable, so only its owner and admins read it.
|
|
22
|
+
*
|
|
23
|
+
* The pure and SQL forms are written once each here, next to each other, over
|
|
24
|
+
* the same literal constants; every predicate and query composes them rather
|
|
25
|
+
* than restating the rule. Twin of the Rust `fuz_cell::cell_moderation`.
|
|
26
|
+
*
|
|
27
|
+
* @module
|
|
28
|
+
*/
|
|
29
|
+
/** `cell.moderation` for a gated contribution awaiting a decision. */
|
|
30
|
+
export declare const CELL_MODERATION_PENDING = "pending";
|
|
31
|
+
/** `cell.moderation` for an approved contribution. */
|
|
32
|
+
export declare const CELL_MODERATION_APPROVED = "approved";
|
|
33
|
+
/** `cell.moderation` for a rejected contribution. */
|
|
34
|
+
export declare const CELL_MODERATION_REJECTED = "rejected";
|
|
35
|
+
/**
|
|
36
|
+
* Whether a cell's `moderation` marker admits it to an audience beyond its
|
|
37
|
+
* owner — unmoderated (`null`) or `'approved'`; `'pending'`, `'rejected'`, and
|
|
38
|
+
* any unknown marker do not.
|
|
39
|
+
*
|
|
40
|
+
* @param moderation - the cell's `moderation` column
|
|
41
|
+
* @returns whether the public and grant read arms may admit the cell
|
|
42
|
+
*/
|
|
43
|
+
export declare const cell_moderation_admits_audience: (moderation: string | null) => boolean;
|
|
44
|
+
/**
|
|
45
|
+
* SQL form of `cell_moderation_admits_audience` over the cell alias `alias`
|
|
46
|
+
* (a parenthesized boolean expression).
|
|
47
|
+
*
|
|
48
|
+
* @param alias - the `cell` table alias in the enclosing query
|
|
49
|
+
* @returns the SQL predicate
|
|
50
|
+
*/
|
|
51
|
+
export declare const cell_moderation_admits_audience_sql: (alias: string) => string;
|
|
52
|
+
/**
|
|
53
|
+
* Whether a cell's `moderation` marker is one its governing root's manager
|
|
54
|
+
* stewards — exactly `'pending'` or `'rejected'`.
|
|
55
|
+
*
|
|
56
|
+
* @param moderation - the cell's `moderation` column
|
|
57
|
+
* @returns whether the steward read arm may admit the cell
|
|
58
|
+
*/
|
|
59
|
+
export declare const cell_moderation_is_stewardable: (moderation: string | null) => boolean;
|
|
60
|
+
/**
|
|
61
|
+
* SQL form of `cell_moderation_is_stewardable` over the cell alias `alias`.
|
|
62
|
+
*
|
|
63
|
+
* @param alias - the `cell` table alias in the enclosing query
|
|
64
|
+
* @returns the SQL predicate
|
|
65
|
+
*/
|
|
66
|
+
export declare const cell_moderation_is_stewardable_sql: (alias: string) => string;
|
|
67
|
+
//# sourceMappingURL=cell_moderation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cell_moderation.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/cell_moderation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,sEAAsE;AACtE,eAAO,MAAM,uBAAuB,YAAY,CAAC;AAEjD,sDAAsD;AACtD,eAAO,MAAM,wBAAwB,aAAa,CAAC;AAEnD,qDAAqD;AACrD,eAAO,MAAM,wBAAwB,aAAa,CAAC;AAEnD;;;;;;;GAOG;AACH,eAAO,MAAM,+BAA+B,GAAI,YAAY,MAAM,GAAG,IAAI,KAAG,OACb,CAAC;AAEhE;;;;;;GAMG;AACH,eAAO,MAAM,mCAAmC,GAAI,OAAO,MAAM,KAAG,MACmB,CAAC;AAExF;;;;;;GAMG;AACH,eAAO,MAAM,8BAA8B,GAAI,YAAY,MAAM,GAAG,IAAI,KAAG,OACO,CAAC;AAEnF;;;;;GAKG;AACH,eAAO,MAAM,kCAAkC,GAAI,OAAO,MAAM,KAAG,MACoB,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `cell.moderation` read rules, in pure and SQL form side by side.
|
|
3
|
+
*
|
|
4
|
+
* `moderation` is free `TEXT` with no CHECK, so each rule is an allowlist over
|
|
5
|
+
* the literals: a marker this module doesn't name (a typo, a future state)
|
|
6
|
+
* admits nothing.
|
|
7
|
+
*
|
|
8
|
+
* **Audience admission** — `cell_moderation_admits_audience` and
|
|
9
|
+
* `cell_moderation_admits_audience_sql`: unmoderated (`null`) or `'approved'`.
|
|
10
|
+
* A cell's effective audience is its visibility (or grants) **and** its
|
|
11
|
+
* admission, so the public and grant read arms of `can_view_cell` require it,
|
|
12
|
+
* as do the `editor` grant arm of `can_edit_cell` and the matching arms of
|
|
13
|
+
* `query_cell_list` (general and `shared_with` shapes). The owner and
|
|
14
|
+
* global-admin arms don't: an author keeps reading and editing their own
|
|
15
|
+
* pending or rejected contribution, and an admin reads everything.
|
|
16
|
+
*
|
|
17
|
+
* **Stewardship** — `cell_moderation_is_stewardable` and
|
|
18
|
+
* `cell_moderation_is_stewardable_sql`: exactly `'pending'` or `'rejected'`, the
|
|
19
|
+
* states in which the governing root's manager reads a contribution its
|
|
20
|
+
* audience can't (`can_steward_cell`). An unknown marker is neither admitted
|
|
21
|
+
* nor stewardable, so only its owner and admins read it.
|
|
22
|
+
*
|
|
23
|
+
* The pure and SQL forms are written once each here, next to each other, over
|
|
24
|
+
* the same literal constants; every predicate and query composes them rather
|
|
25
|
+
* than restating the rule. Twin of the Rust `fuz_cell::cell_moderation`.
|
|
26
|
+
*
|
|
27
|
+
* @module
|
|
28
|
+
*/
|
|
29
|
+
/** `cell.moderation` for a gated contribution awaiting a decision. */
|
|
30
|
+
export const CELL_MODERATION_PENDING = 'pending';
|
|
31
|
+
/** `cell.moderation` for an approved contribution. */
|
|
32
|
+
export const CELL_MODERATION_APPROVED = 'approved';
|
|
33
|
+
/** `cell.moderation` for a rejected contribution. */
|
|
34
|
+
export const CELL_MODERATION_REJECTED = 'rejected';
|
|
35
|
+
/**
|
|
36
|
+
* Whether a cell's `moderation` marker admits it to an audience beyond its
|
|
37
|
+
* owner — unmoderated (`null`) or `'approved'`; `'pending'`, `'rejected'`, and
|
|
38
|
+
* any unknown marker do not.
|
|
39
|
+
*
|
|
40
|
+
* @param moderation - the cell's `moderation` column
|
|
41
|
+
* @returns whether the public and grant read arms may admit the cell
|
|
42
|
+
*/
|
|
43
|
+
export const cell_moderation_admits_audience = (moderation) => moderation === null || moderation === CELL_MODERATION_APPROVED;
|
|
44
|
+
/**
|
|
45
|
+
* SQL form of `cell_moderation_admits_audience` over the cell alias `alias`
|
|
46
|
+
* (a parenthesized boolean expression).
|
|
47
|
+
*
|
|
48
|
+
* @param alias - the `cell` table alias in the enclosing query
|
|
49
|
+
* @returns the SQL predicate
|
|
50
|
+
*/
|
|
51
|
+
export const cell_moderation_admits_audience_sql = (alias) => `(${alias}.moderation IS NULL OR ${alias}.moderation = '${CELL_MODERATION_APPROVED}')`;
|
|
52
|
+
/**
|
|
53
|
+
* Whether a cell's `moderation` marker is one its governing root's manager
|
|
54
|
+
* stewards — exactly `'pending'` or `'rejected'`.
|
|
55
|
+
*
|
|
56
|
+
* @param moderation - the cell's `moderation` column
|
|
57
|
+
* @returns whether the steward read arm may admit the cell
|
|
58
|
+
*/
|
|
59
|
+
export const cell_moderation_is_stewardable = (moderation) => moderation === CELL_MODERATION_PENDING || moderation === CELL_MODERATION_REJECTED;
|
|
60
|
+
/**
|
|
61
|
+
* SQL form of `cell_moderation_is_stewardable` over the cell alias `alias`.
|
|
62
|
+
*
|
|
63
|
+
* @param alias - the `cell` table alias in the enclosing query
|
|
64
|
+
* @returns the SQL predicate
|
|
65
|
+
*/
|
|
66
|
+
export const cell_moderation_is_stewardable_sql = (alias) => `${alias}.moderation IN ('${CELL_MODERATION_PENDING}', '${CELL_MODERATION_REJECTED}')`;
|
|
@@ -85,6 +85,15 @@ export declare const CELL_COLUMNS: readonly ["id", "data", "kind", "visibility",
|
|
|
85
85
|
* actually produces one.
|
|
86
86
|
*/
|
|
87
87
|
export declare const cell_row_projection: (alias: string) => string;
|
|
88
|
+
/**
|
|
89
|
+
* The columns a view check reads (`can_view_cell`) — the `CELL_COLUMNS`
|
|
90
|
+
* subset `query_cell_view_load_many` projects, typed against `CELL_COLUMNS`
|
|
91
|
+
* so the base const's drift guard covers it. Twin of `fuz_cell`'s
|
|
92
|
+
* `CELL_VIEW_COLUMNS`.
|
|
93
|
+
*/
|
|
94
|
+
export declare const CELL_VIEW_COLUMNS: readonly ["id", "visibility", "moderation", "created_by"];
|
|
95
|
+
/** A cell read for its view check alone — the `CELL_VIEW_COLUMNS` projection. */
|
|
96
|
+
export type CellViewRow = Pick<CellRow, (typeof CELL_VIEW_COLUMNS)[number]>;
|
|
88
97
|
/**
|
|
89
98
|
* Input for `query_cell_create`. `refs` is derived from `data`. `kind` is
|
|
90
99
|
* the write-once capability axis (`cell.kind` column) — set here at INSERT
|
|
@@ -158,17 +167,28 @@ export declare const query_cell_get: (deps: QueryDeps, id: Uuid, options?: {
|
|
|
158
167
|
*/
|
|
159
168
|
export declare const query_cell_get_by_path: (deps: QueryDeps, path: string) => Promise<CellRow | null>;
|
|
160
169
|
/**
|
|
161
|
-
* Bulk-load active cell rows by id, **no visibility filter applied
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
* to tombstones never surface.
|
|
170
|
+
* Bulk-load active cell rows by id, **no visibility filter applied** — the
|
|
171
|
+
* full rows, for a caller that copies or returns them (the deep-clone walk).
|
|
172
|
+
* A caller that needs only the view check reads `query_cell_view_load_many`.
|
|
173
|
+
* Soft-deleted rows are excluded so relations to tombstones never surface.
|
|
166
174
|
*
|
|
167
175
|
* @param deps - query deps
|
|
168
176
|
* @param ids - cell ids to load (duplicates are harmless)
|
|
169
177
|
* @returns active rows in arbitrary order (caller indexes by `id`)
|
|
170
178
|
*/
|
|
171
179
|
export declare const query_cell_load_many: (deps: QueryDeps, ids: ReadonlyArray<Uuid>) => Promise<Array<CellRow>>;
|
|
180
|
+
/**
|
|
181
|
+
* Bulk-load the active cells among `ids` for a view check — the
|
|
182
|
+
* `CELL_VIEW_COLUMNS` alone, never the payload. Backs the strict
|
|
183
|
+
* relation-read filter (`auth/cell_relation_visibility.ts`'s
|
|
184
|
+
* `filter_visible_target_ids`), which runs `can_view_cell` per row in memory
|
|
185
|
+
* rather than in SQL. Soft-deleted rows and ids with no row are absent.
|
|
186
|
+
*
|
|
187
|
+
* @param deps - query deps
|
|
188
|
+
* @param ids - cell ids to load (duplicates are harmless)
|
|
189
|
+
* @returns active rows in arbitrary order (caller indexes by `id`)
|
|
190
|
+
*/
|
|
191
|
+
export declare const query_cell_view_load_many: (deps: QueryDeps, ids: ReadonlyArray<Uuid>) => Promise<Array<CellViewRow>>;
|
|
172
192
|
/**
|
|
173
193
|
* Update a cell. Fields left `undefined` in the patch keep their existing
|
|
174
194
|
* value; explicit `null` writes `NULL`. `refs` is re-derived from `data`
|
|
@@ -247,15 +267,24 @@ export declare const query_cell_list_by_creator: (deps: QueryDeps, actor_id: Uui
|
|
|
247
267
|
* dimension — keep it simple) plus an optional viewer-aware visibility
|
|
248
268
|
* predicate.
|
|
249
269
|
*
|
|
250
|
-
* The visibility predicate mirrors `can_view_cell` in SQL
|
|
270
|
+
* The visibility predicate mirrors `can_view_cell || can_steward_cell` in SQL
|
|
271
|
+
* form:
|
|
251
272
|
*
|
|
252
273
|
* ```
|
|
253
274
|
* (viewer_is_admin
|
|
254
|
-
* OR cell.visibility = 'public'
|
|
255
275
|
* OR (viewer_actor_id IS NOT NULL AND created_by = viewer_actor_id)
|
|
256
|
-
* OR (
|
|
276
|
+
* OR (<moderation admits an audience> AND (
|
|
277
|
+
* cell.visibility = 'public'
|
|
278
|
+
* OR (viewer_actor_id IS NOT NULL AND <grant admits caller>)))
|
|
279
|
+
* OR (viewer_actor_id IS NOT NULL AND <pending or rejected contribution>
|
|
280
|
+
* AND <viewer owns its live governing root>))
|
|
257
281
|
* ```
|
|
258
282
|
*
|
|
283
|
+
* The moderation rules are `cell_moderation_admits_audience_sql` and
|
|
284
|
+
* `cell_moderation_is_stewardable_sql` (`db/cell_moderation.ts`). The last arm
|
|
285
|
+
* is the steward arm (`auth/cell_steward.ts`) — a root owner reads the
|
|
286
|
+
* moderation queue under their root; a global admin is already admitted.
|
|
287
|
+
*
|
|
259
288
|
* The grants branch closes parity with `can_view_cell`: a SQL `EXISTS`
|
|
260
289
|
* over `cell_grant`, parameterized by the caller's `actor_id` and the
|
|
261
290
|
* parallel `(role[], scope_id[])` projection of `auth.role_grants`. The
|
|
@@ -277,7 +306,9 @@ export declare const query_cell_list_by_creator: (deps: QueryDeps, actor_id: Uui
|
|
|
277
306
|
* owner-excluding, but the explicit `created_by IS DISTINCT FROM caller`
|
|
278
307
|
* guards against any future deviation. The shared_with branch does NOT
|
|
279
308
|
* bypass for admin: an admin asking "what's shared with me" wants their
|
|
280
|
-
* own grant footprint, not every cell.
|
|
309
|
+
* own grant footprint, not every cell. A grant admits there only while the
|
|
310
|
+
* cell's moderation admits an audience, as in `can_view_cell`, and the steward
|
|
311
|
+
* arm doesn't apply — a grant footprint is not a moderation queue.
|
|
281
312
|
*
|
|
282
313
|
* Soft-deleted rows are excluded by default; opt-in via `include_deleted`.
|
|
283
314
|
*
|
|
@@ -313,11 +344,10 @@ export interface CellListParams {
|
|
|
313
344
|
* Filter by moderation lifecycle marker (`'pending'` drives the mod queue via
|
|
314
345
|
* `idx_cell_moderation_pending`).
|
|
315
346
|
*
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
* surface (`cell_moderate` of a known id still works regardless).
|
|
347
|
+
* The visibility predicate still applies, so `{root_id, moderation:
|
|
348
|
+
* 'pending'}` is the queue for the callers who moderate it — a global admin,
|
|
349
|
+
* and the governing root's owner through the steward arm — while each author
|
|
350
|
+
* sees only their own entries.
|
|
321
351
|
*/
|
|
322
352
|
moderation?: string;
|
|
323
353
|
/**
|
|
@@ -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,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"cell_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/cell_queries.ts"],"names":[],"mappings":"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"}
|