@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,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cell PG schema.
|
|
3
|
+
*
|
|
4
|
+
* The universal content primitive: a single `cell` table whose `data` JSONB
|
|
5
|
+
* is interpreted by view shape. Parent→child membership and named
|
|
6
|
+
* relationships live in two sibling tables — `cell_item` (ordered
|
|
7
|
+
* children, fractional-indexing keyed) and `cell_field` (named edges).
|
|
8
|
+
* `refs text[]` carries `blake3:` fact hashes auto-extracted from `data`
|
|
9
|
+
* by application code on every write.
|
|
10
|
+
*
|
|
11
|
+
* Soft delete via `deleted_at`. Most indexes are partial on
|
|
12
|
+
* `deleted_at IS NULL` so active-cell queries skip tombstones.
|
|
13
|
+
*
|
|
14
|
+
* `path` is the global namespace axis — a partial unique index enforces
|
|
15
|
+
* uniqueness across all active rows (PostgreSQL UNIQUE constraints don't
|
|
16
|
+
* support WHERE clauses, so it's expressed as a partial unique index). It
|
|
17
|
+
* additionally filters on `deleted_at IS NULL` so a soft-deleted cell
|
|
18
|
+
* doesn't block reuse of its path. Path writes are admin-only at the
|
|
19
|
+
* action layer; user-namespaced paths are a future extension.
|
|
20
|
+
*
|
|
21
|
+
* **Ownership columns** (`created_by`, `updated_by`) are nullable FKs to
|
|
22
|
+
* `actor`: NULL = system origin (well-known cells, daemon/agent cells).
|
|
23
|
+
* The non-admin authz path treats NULL `created_by` as admin-only via an
|
|
24
|
+
* explicit equality check (`auth/cell_authorize.ts`).
|
|
25
|
+
*
|
|
26
|
+
* **Timestamp naming** (`created_at`, `updated_at`) aligns with fuz_app's
|
|
27
|
+
* `_at`-everywhere convention used by `account`, `actor`, `audit_log`,
|
|
28
|
+
* `role_grant`, etc.
|
|
29
|
+
*
|
|
30
|
+
* **Single-migration shape**: `cell_v0` creates the canonical
|
|
31
|
+
* cell + cell_grant + cell_field + cell_item layout in one shot from the
|
|
32
|
+
* live exported constants.
|
|
33
|
+
*
|
|
34
|
+
* @module
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* `cell_visibility` enum — access-control axis for a cell. Lives as a
|
|
38
|
+
* top-level column (not inside `data`) because visibility is access
|
|
39
|
+
* control, not content metadata. `cell_grant` is the other ACL surface;
|
|
40
|
+
* keeping visibility as a peer column (not a JSON field) co-locates
|
|
41
|
+
* access-control state and lets the planner reason about it directly.
|
|
42
|
+
*
|
|
43
|
+
* Ships with two states (`'private'`, `'public'`); a third (unlisted /
|
|
44
|
+
* public-link) folds in via `ALTER TYPE` when public-link sharing lands.
|
|
45
|
+
*
|
|
46
|
+
* Wrapped in a `DO` block so the migration can replay idempotently —
|
|
47
|
+
* `CREATE TYPE` has no `IF NOT EXISTS` variant in PostgreSQL.
|
|
48
|
+
*/
|
|
49
|
+
export const CELL_VISIBILITY_TYPE = `
|
|
50
|
+
DO $$ BEGIN
|
|
51
|
+
CREATE TYPE cell_visibility AS ENUM ('private', 'public');
|
|
52
|
+
EXCEPTION WHEN duplicate_object THEN NULL;
|
|
53
|
+
END $$`;
|
|
54
|
+
/**
|
|
55
|
+
* `cell` table — universal content primitive: identity + content only.
|
|
56
|
+
* Parent→child membership lives in `cell_item`; named relations live in
|
|
57
|
+
* `cell_field`. Includes the `created_by` / `updated_by` ownership columns.
|
|
58
|
+
*
|
|
59
|
+
* `visibility` is the access-control axis — `private` (default) is
|
|
60
|
+
* restricted to admin / owner / `cell_grant`-admitted callers; `public`
|
|
61
|
+
* admits everyone, including unauthenticated visitors. Lives as a
|
|
62
|
+
* top-level column so the auth predicate reads off the row directly
|
|
63
|
+
* rather than reaching into `data`.
|
|
64
|
+
*
|
|
65
|
+
* `path` is the global namespace axis (no tenant/hub scoping) — globally
|
|
66
|
+
* unique on active rows via `idx_cell_path_unique`.
|
|
67
|
+
*/
|
|
68
|
+
export const CELL_SCHEMA = `
|
|
69
|
+
CREATE TABLE IF NOT EXISTS cell (
|
|
70
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
71
|
+
data JSONB NOT NULL,
|
|
72
|
+
visibility cell_visibility NOT NULL DEFAULT 'private',
|
|
73
|
+
path TEXT,
|
|
74
|
+
refs TEXT[],
|
|
75
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
76
|
+
updated_at TIMESTAMPTZ,
|
|
77
|
+
deleted_at TIMESTAMPTZ,
|
|
78
|
+
created_by UUID REFERENCES actor(id) ON DELETE SET NULL,
|
|
79
|
+
updated_by UUID REFERENCES actor(id) ON DELETE SET NULL
|
|
80
|
+
)`;
|
|
81
|
+
/**
|
|
82
|
+
* Cell indexes — all active-only, partial on `deleted_at IS NULL`.
|
|
83
|
+
*
|
|
84
|
+
* - `idx_cell_active`: active-cell list/scan ordered by creation.
|
|
85
|
+
* - `idx_cell_path_unique`: global `path` uniqueness + read-side path
|
|
86
|
+
* lookup. Partial on path + active so reused paths after soft delete are
|
|
87
|
+
* allowed.
|
|
88
|
+
* - `idx_cell_data`: shape-driven queries (`data ? 'kind'`, `data @> ...`).
|
|
89
|
+
* - `idx_cell_refs`: cells-by-fact discovery (cross-cell reference graph).
|
|
90
|
+
* - `idx_cell_created_by`: "cells this actor created" queries.
|
|
91
|
+
*
|
|
92
|
+
* Parent↔child membership and named relations live in sibling tables;
|
|
93
|
+
* see `CELL_ITEM_INDEXES` / `CELL_FIELD_INDEXES` below.
|
|
94
|
+
*/
|
|
95
|
+
export const CELL_INDEXES = [
|
|
96
|
+
`CREATE INDEX IF NOT EXISTS idx_cell_active ON cell(created_at)
|
|
97
|
+
WHERE deleted_at IS NULL`,
|
|
98
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS idx_cell_path_unique
|
|
99
|
+
ON cell(path)
|
|
100
|
+
WHERE path IS NOT NULL AND deleted_at IS NULL`,
|
|
101
|
+
`CREATE INDEX IF NOT EXISTS idx_cell_data ON cell USING gin(data)
|
|
102
|
+
WHERE deleted_at IS NULL`,
|
|
103
|
+
`CREATE INDEX IF NOT EXISTS idx_cell_refs ON cell USING gin(refs)
|
|
104
|
+
WHERE refs IS NOT NULL AND deleted_at IS NULL`,
|
|
105
|
+
`CREATE INDEX IF NOT EXISTS idx_cell_created_by ON cell(created_by)
|
|
106
|
+
WHERE deleted_at IS NULL`,
|
|
107
|
+
];
|
|
108
|
+
/**
|
|
109
|
+
* `cell_grant` table — resource-side ACL for cells. Each row admits a
|
|
110
|
+
* principal (actor or `(role, scope_id)`) at a `level` (`viewer` or
|
|
111
|
+
* `editor`). Owner is implicit (`cell.created_by`); the table never carries
|
|
112
|
+
* owner rows.
|
|
113
|
+
*
|
|
114
|
+
* The single-principal arm is actor-grain (`actor_id` FK); the other arm is
|
|
115
|
+
* role-shaped (`(role, scope_id)`). The CHECK enforces exactly one arm.
|
|
116
|
+
*/
|
|
117
|
+
export const CELL_GRANT_SCHEMA = `
|
|
118
|
+
CREATE TABLE IF NOT EXISTS cell_grant (
|
|
119
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
120
|
+
cell_id UUID NOT NULL REFERENCES cell(id) ON DELETE CASCADE,
|
|
121
|
+
level TEXT NOT NULL CHECK (level IN ('viewer', 'editor')),
|
|
122
|
+
actor_id UUID REFERENCES actor(id) ON DELETE CASCADE,
|
|
123
|
+
role TEXT,
|
|
124
|
+
scope_id UUID,
|
|
125
|
+
granted_by UUID REFERENCES actor(id) ON DELETE SET NULL,
|
|
126
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
127
|
+
CHECK (
|
|
128
|
+
(actor_id IS NOT NULL AND role IS NULL AND scope_id IS NULL) OR
|
|
129
|
+
(actor_id IS NULL AND role IS NOT NULL)
|
|
130
|
+
)
|
|
131
|
+
)`;
|
|
132
|
+
/**
|
|
133
|
+
* `cell_grant` indexes.
|
|
134
|
+
*
|
|
135
|
+
* - `idx_cell_grant_cell`: forward lookup ("who has access to this cell?").
|
|
136
|
+
* - `idx_cell_grant_actor`: reverse lookup ("which cells does this actor have access to?").
|
|
137
|
+
* - `idx_cell_grant_role_scope`: reverse lookup for role-shaped principals.
|
|
138
|
+
* - `idx_cell_grant_unique_actor`: prevents duplicate actor-shaped grants for the same cell.
|
|
139
|
+
* Re-granting updates `level` via UPSERT on this index.
|
|
140
|
+
* - `idx_cell_grant_unique_role_scope`: same, for role-shaped grants.
|
|
141
|
+
* `NULLS NOT DISTINCT` so two rows with the same `(cell_id, role)` and
|
|
142
|
+
* `scope_id IS NULL` collide — without it, default NULL-distinct
|
|
143
|
+
* semantics would let duplicate null-scope role grants slip past the
|
|
144
|
+
* re-share UPSERT path. Requires PostgreSQL 15+ (pglite tracks PG 16).
|
|
145
|
+
*/
|
|
146
|
+
export const CELL_GRANT_INDEXES = [
|
|
147
|
+
`CREATE INDEX IF NOT EXISTS idx_cell_grant_cell ON cell_grant(cell_id)`,
|
|
148
|
+
`CREATE INDEX IF NOT EXISTS idx_cell_grant_actor
|
|
149
|
+
ON cell_grant(actor_id) WHERE actor_id IS NOT NULL`,
|
|
150
|
+
`CREATE INDEX IF NOT EXISTS idx_cell_grant_role_scope
|
|
151
|
+
ON cell_grant(role, scope_id) WHERE role IS NOT NULL`,
|
|
152
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS idx_cell_grant_unique_actor
|
|
153
|
+
ON cell_grant(cell_id, actor_id) WHERE actor_id IS NOT NULL`,
|
|
154
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS idx_cell_grant_unique_role_scope
|
|
155
|
+
ON cell_grant(cell_id, role, scope_id) NULLS NOT DISTINCT
|
|
156
|
+
WHERE role IS NOT NULL`,
|
|
157
|
+
];
|
|
158
|
+
/**
|
|
159
|
+
* `cell_field` table — named relation (`(source_id, name) → target_id`).
|
|
160
|
+
* One target per name per source — JSON-object keys are unique. Multiplicity
|
|
161
|
+
* is expressed by composition (`foo.tags = collection_cell` whose `items[]`
|
|
162
|
+
* are the tags), not by allowing duplicate `(source_id, name)` rows.
|
|
163
|
+
*/
|
|
164
|
+
export const CELL_FIELD_SCHEMA = `
|
|
165
|
+
CREATE TABLE IF NOT EXISTS cell_field (
|
|
166
|
+
source_id UUID NOT NULL REFERENCES cell(id) ON DELETE CASCADE,
|
|
167
|
+
name TEXT NOT NULL,
|
|
168
|
+
target_id UUID NOT NULL REFERENCES cell(id) ON DELETE CASCADE,
|
|
169
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
170
|
+
PRIMARY KEY (source_id, name)
|
|
171
|
+
)`;
|
|
172
|
+
/**
|
|
173
|
+
* `cell_field` indexes.
|
|
174
|
+
*
|
|
175
|
+
* - PK on `(source_id, name)` covers forward lookup ("what does this cell
|
|
176
|
+
* point to via field X?") and the per-source fields list.
|
|
177
|
+
* - `idx_cell_field_target` covers reverse lookup ("which cells link to
|
|
178
|
+
* this target?").
|
|
179
|
+
*
|
|
180
|
+
* Soft-delete is filtered by JOIN at the read boundary; no partial indexes
|
|
181
|
+
* here on `deleted_at` (would force index churn on cell soft-delete
|
|
182
|
+
* toggles, and the join filter is sufficient).
|
|
183
|
+
*/
|
|
184
|
+
export const CELL_FIELD_INDEXES = [
|
|
185
|
+
`CREATE INDEX IF NOT EXISTS idx_cell_field_target ON cell_field(target_id)`,
|
|
186
|
+
];
|
|
187
|
+
/**
|
|
188
|
+
* `cell_item` table — ordered child membership keyed by an opaque
|
|
189
|
+
* fractional-indexing string. `(parent_id, position)` PK enforces one cell
|
|
190
|
+
* per slot; the same `child_id` may appear at multiple positions (the
|
|
191
|
+
* primitive is JSON-array-shaped — ordered multiset, not set). Domain
|
|
192
|
+
* dedup rules ride on top in helpers.
|
|
193
|
+
*/
|
|
194
|
+
export const CELL_ITEM_SCHEMA = `
|
|
195
|
+
CREATE TABLE IF NOT EXISTS cell_item (
|
|
196
|
+
parent_id UUID NOT NULL REFERENCES cell(id) ON DELETE CASCADE,
|
|
197
|
+
position TEXT NOT NULL,
|
|
198
|
+
child_id UUID NOT NULL REFERENCES cell(id) ON DELETE CASCADE,
|
|
199
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
200
|
+
PRIMARY KEY (parent_id, position)
|
|
201
|
+
)`;
|
|
202
|
+
/**
|
|
203
|
+
* `cell_item` indexes.
|
|
204
|
+
*
|
|
205
|
+
* - PK on `(parent_id, position)` covers ordered scans for the per-parent
|
|
206
|
+
* items list (`SELECT ... ORDER BY position`).
|
|
207
|
+
* - `idx_cell_item_child` covers reverse lookup ("which parents contain
|
|
208
|
+
* this child?").
|
|
209
|
+
*/
|
|
210
|
+
export const CELL_ITEM_INDEXES = [
|
|
211
|
+
`CREATE INDEX IF NOT EXISTS idx_cell_item_parent_position ON cell_item(parent_id, position)`,
|
|
212
|
+
`CREATE INDEX IF NOT EXISTS idx_cell_item_child ON cell_item(child_id)`,
|
|
213
|
+
];
|
|
214
|
+
/** Tables created by `CELL_MIGRATION_NS`, in drop order (children first). */
|
|
215
|
+
export const CELL_DROP_TABLES = ['cell_field', 'cell_item', 'cell_grant', 'cell'];
|
|
216
|
+
/** Cell migrations. */
|
|
217
|
+
export const CELL_MIGRATIONS = [
|
|
218
|
+
{
|
|
219
|
+
name: 'cell_v0',
|
|
220
|
+
up: async (db) => {
|
|
221
|
+
await db.query(CELL_VISIBILITY_TYPE);
|
|
222
|
+
await db.query(CELL_SCHEMA);
|
|
223
|
+
for (const sql of CELL_INDEXES) {
|
|
224
|
+
await db.query(sql);
|
|
225
|
+
}
|
|
226
|
+
await db.query(CELL_GRANT_SCHEMA);
|
|
227
|
+
for (const sql of CELL_GRANT_INDEXES) {
|
|
228
|
+
await db.query(sql);
|
|
229
|
+
}
|
|
230
|
+
await db.query(CELL_FIELD_SCHEMA);
|
|
231
|
+
for (const sql of CELL_FIELD_INDEXES) {
|
|
232
|
+
await db.query(sql);
|
|
233
|
+
}
|
|
234
|
+
await db.query(CELL_ITEM_SCHEMA);
|
|
235
|
+
for (const sql of CELL_ITEM_INDEXES) {
|
|
236
|
+
await db.query(sql);
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
];
|
|
241
|
+
/** Namespace identifier for cell migrations. */
|
|
242
|
+
export const CELL_MIGRATION_NAMESPACE = 'fuz_cell';
|
|
243
|
+
/** Migration namespace consumed by `run_migrations`. */
|
|
244
|
+
export const CELL_MIGRATION_NS = {
|
|
245
|
+
namespace: CELL_MIGRATION_NAMESPACE,
|
|
246
|
+
migrations: CELL_MIGRATIONS,
|
|
247
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raw queries against the `cell_field` table.
|
|
3
|
+
*
|
|
4
|
+
* Named-relation primitive: each row is `(source_id, name) → target_id`,
|
|
5
|
+
* a JSON-object-shaped edge from one cell to another. `(source_id, name)`
|
|
6
|
+
* is unique — one target per name per source. Multiplicity by composition
|
|
7
|
+
* (target a collection cell whose `items[]` are the multi-valued tags),
|
|
8
|
+
* not by allowing duplicate field rows.
|
|
9
|
+
*
|
|
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.
|
|
12
|
+
*
|
|
13
|
+
* `query_cell_field_set` upserts on the `(source_id, name)` PK so
|
|
14
|
+
* re-pointing a name updates `target_id` in place — JSON-object semantics
|
|
15
|
+
* (`obj.foo = bar` overwrites whatever was there).
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
import type { QueryDeps } from './query_deps.js';
|
|
20
|
+
import type { Uuid } from '@fuzdev/fuz_util/id.js';
|
|
21
|
+
/** Row shape returned by `cell_field` SELECTs. */
|
|
22
|
+
export interface CellFieldRow {
|
|
23
|
+
source_id: Uuid;
|
|
24
|
+
name: string;
|
|
25
|
+
target_id: Uuid;
|
|
26
|
+
created_at: Date;
|
|
27
|
+
}
|
|
28
|
+
/** Input for `query_cell_field_set`. */
|
|
29
|
+
export interface CellFieldSetQueryInput {
|
|
30
|
+
source_id: Uuid;
|
|
31
|
+
name: string;
|
|
32
|
+
target_id: Uuid;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Insert or update a field row.
|
|
36
|
+
*
|
|
37
|
+
* UPSERT on `(source_id, name)` — re-setting the same name updates
|
|
38
|
+
* `target_id` and bumps `created_at` (timestamp reflects last write).
|
|
39
|
+
* Idempotent at the row level: caller can re-issue with the same input
|
|
40
|
+
* without checking existence first.
|
|
41
|
+
*
|
|
42
|
+
* @param deps - query deps
|
|
43
|
+
* @param input - source, name, target
|
|
44
|
+
* @returns the inserted-or-updated row
|
|
45
|
+
* @mutates `cell_field` - inserts or updates one row
|
|
46
|
+
*/
|
|
47
|
+
export declare const query_cell_field_set: (deps: QueryDeps, input: CellFieldSetQueryInput) => Promise<CellFieldRow>;
|
|
48
|
+
/**
|
|
49
|
+
* Fetch one field row by primary key.
|
|
50
|
+
*
|
|
51
|
+
* Does NOT JOIN cell — the caller decides whether to filter by
|
|
52
|
+
* `deleted_at`. Used by handlers that need the row's current target_id
|
|
53
|
+
* for audit envelopes before issuing the delete.
|
|
54
|
+
*
|
|
55
|
+
* @param deps - query deps
|
|
56
|
+
* @param source_id - source cell id
|
|
57
|
+
* @param name - field name
|
|
58
|
+
* @returns the row or `null` when not found
|
|
59
|
+
*/
|
|
60
|
+
export declare const query_cell_field_get: (deps: QueryDeps, source_id: Uuid, name: string) => Promise<CellFieldRow | null>;
|
|
61
|
+
/**
|
|
62
|
+
* Delete a field row by primary key. Returns the deleted row so callers
|
|
63
|
+
* can audit the prior target_id without a pre-fetch.
|
|
64
|
+
*
|
|
65
|
+
* @returns the deleted row, or `null` when no row matched (idempotent
|
|
66
|
+
* delete: a 200 response is correct even when nothing was deleted)
|
|
67
|
+
* @mutates `cell_field` - deletes one row
|
|
68
|
+
*/
|
|
69
|
+
export declare const query_cell_field_delete: (deps: QueryDeps, source_id: Uuid, name: string) => Promise<CellFieldRow | null>;
|
|
70
|
+
/**
|
|
71
|
+
* Forward fields list (`source.fields[]`).
|
|
72
|
+
*
|
|
73
|
+
* Filters target by `deleted_at IS NULL` so relations to tombstoned cells
|
|
74
|
+
* don't surface; the source filter is the caller's responsibility (gated
|
|
75
|
+
* upstream by `can_view_cell(source)`).
|
|
76
|
+
*
|
|
77
|
+
* @param deps - query deps
|
|
78
|
+
* @param source_id - source cell id
|
|
79
|
+
* @returns matching rows, oldest first by name (lex order)
|
|
80
|
+
*/
|
|
81
|
+
export declare const query_cell_field_list_for_source: (deps: QueryDeps, source_id: Uuid, options?: {
|
|
82
|
+
limit?: number;
|
|
83
|
+
name_after?: string;
|
|
84
|
+
}) => Promise<Array<CellFieldRow>>;
|
|
85
|
+
/**
|
|
86
|
+
* Reverse fields list (`target.upfields[]`).
|
|
87
|
+
*
|
|
88
|
+
* Returns rows whose `target_id = $1`, joined to `cell` on `source_id` so
|
|
89
|
+
* relations from tombstoned sources don't surface. The caller-side
|
|
90
|
+
* authz filter (per-source `can_view_cell`) runs after the SQL fetch
|
|
91
|
+
* — see the 2-layer authz contract on `cell_field_list({target_id})`.
|
|
92
|
+
*
|
|
93
|
+
* Bounded by `limit` (the wire `cell_field_list` cap) so a heavily
|
|
94
|
+
* inbound-linked target can't force an unbounded fetch + per-source authz
|
|
95
|
+
* pass on the public, IP-rate-limited reverse endpoint.
|
|
96
|
+
*
|
|
97
|
+
* @param deps - query deps
|
|
98
|
+
* @param target_id - target cell id
|
|
99
|
+
* @param options - `limit` caps the row count
|
|
100
|
+
* @returns matching rows, oldest first by source created_at
|
|
101
|
+
*/
|
|
102
|
+
export declare const query_cell_field_list_for_target: (deps: QueryDeps, target_id: Uuid, options?: {
|
|
103
|
+
limit?: number;
|
|
104
|
+
}) => Promise<Array<CellFieldRow>>;
|
|
105
|
+
//# sourceMappingURL=cell_field_queries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cell_field_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/cell_field_queries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAGjD,kDAAkD;AAClD,MAAM,WAAW,YAAY;IAC5B,SAAS,EAAE,IAAI,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,IAAI,CAAC;IAChB,UAAU,EAAE,IAAI,CAAC;CACjB;AAED,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,CAM7B,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gCAAgC,GAC5C,MAAM,SAAS,EACf,WAAW,IAAI,EACf,UAAU;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAC,KAC7C,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAa7B,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,gCAAgC,GAC5C,MAAM,SAAS,EACf,WAAW,IAAI,EACf,UAAU;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAC,KACxB,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAS5B,CAAC"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raw queries against the `cell_field` table.
|
|
3
|
+
*
|
|
4
|
+
* Named-relation primitive: each row is `(source_id, name) → target_id`,
|
|
5
|
+
* a JSON-object-shaped edge from one cell to another. `(source_id, name)`
|
|
6
|
+
* is unique — one target per name per source. Multiplicity by composition
|
|
7
|
+
* (target a collection cell whose `items[]` are the multi-valued tags),
|
|
8
|
+
* not by allowing duplicate field rows.
|
|
9
|
+
*
|
|
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.
|
|
12
|
+
*
|
|
13
|
+
* `query_cell_field_set` upserts on the `(source_id, name)` PK so
|
|
14
|
+
* re-pointing a name updates `target_id` in place — JSON-object semantics
|
|
15
|
+
* (`obj.foo = bar` overwrites whatever was there).
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
import { assert_row } from './assert_row.js';
|
|
20
|
+
/**
|
|
21
|
+
* Insert or update a field row.
|
|
22
|
+
*
|
|
23
|
+
* UPSERT on `(source_id, name)` — re-setting the same name updates
|
|
24
|
+
* `target_id` and bumps `created_at` (timestamp reflects last write).
|
|
25
|
+
* Idempotent at the row level: caller can re-issue with the same input
|
|
26
|
+
* without checking existence first.
|
|
27
|
+
*
|
|
28
|
+
* @param deps - query deps
|
|
29
|
+
* @param input - source, name, target
|
|
30
|
+
* @returns the inserted-or-updated row
|
|
31
|
+
* @mutates `cell_field` - inserts or updates one row
|
|
32
|
+
*/
|
|
33
|
+
export const query_cell_field_set = async (deps, input) => {
|
|
34
|
+
const row = await deps.db.query_one(`INSERT INTO cell_field (source_id, name, target_id)
|
|
35
|
+
VALUES ($1, $2, $3)
|
|
36
|
+
ON CONFLICT (source_id, name)
|
|
37
|
+
DO UPDATE SET target_id = EXCLUDED.target_id, created_at = NOW()
|
|
38
|
+
RETURNING *`, [input.source_id, input.name, input.target_id]);
|
|
39
|
+
return assert_row(row, 'INSERT INTO cell_field');
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Fetch one field row by primary key.
|
|
43
|
+
*
|
|
44
|
+
* Does NOT JOIN cell — the caller decides whether to filter by
|
|
45
|
+
* `deleted_at`. Used by handlers that need the row's current target_id
|
|
46
|
+
* for audit envelopes before issuing the delete.
|
|
47
|
+
*
|
|
48
|
+
* @param deps - query deps
|
|
49
|
+
* @param source_id - source cell id
|
|
50
|
+
* @param name - field name
|
|
51
|
+
* @returns the row or `null` when not found
|
|
52
|
+
*/
|
|
53
|
+
export const query_cell_field_get = async (deps, source_id, name) => {
|
|
54
|
+
const row = await deps.db.query_one(`SELECT * FROM cell_field WHERE source_id = $1 AND name = $2`, [source_id, name]);
|
|
55
|
+
return row ?? null;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Delete a field row by primary key. Returns the deleted row so callers
|
|
59
|
+
* can audit the prior target_id without a pre-fetch.
|
|
60
|
+
*
|
|
61
|
+
* @returns the deleted row, or `null` when no row matched (idempotent
|
|
62
|
+
* delete: a 200 response is correct even when nothing was deleted)
|
|
63
|
+
* @mutates `cell_field` - deletes one row
|
|
64
|
+
*/
|
|
65
|
+
export const query_cell_field_delete = async (deps, source_id, name) => {
|
|
66
|
+
const row = await deps.db.query_one(`DELETE FROM cell_field WHERE source_id = $1 AND name = $2 RETURNING *`, [source_id, name]);
|
|
67
|
+
return row ?? null;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Forward fields list (`source.fields[]`).
|
|
71
|
+
*
|
|
72
|
+
* Filters target by `deleted_at IS NULL` so relations to tombstoned cells
|
|
73
|
+
* don't surface; the source filter is the caller's responsibility (gated
|
|
74
|
+
* upstream by `can_view_cell(source)`).
|
|
75
|
+
*
|
|
76
|
+
* @param deps - query deps
|
|
77
|
+
* @param source_id - source cell id
|
|
78
|
+
* @returns matching rows, oldest first by name (lex order)
|
|
79
|
+
*/
|
|
80
|
+
export const query_cell_field_list_for_source = async (deps, source_id, options) => {
|
|
81
|
+
const limit = options?.limit ?? null;
|
|
82
|
+
const name_after = options?.name_after ?? null;
|
|
83
|
+
return deps.db.query(`SELECT f.* FROM cell_field f
|
|
84
|
+
JOIN cell t ON t.id = f.target_id
|
|
85
|
+
WHERE f.source_id = $1
|
|
86
|
+
AND t.deleted_at IS NULL
|
|
87
|
+
AND ($3::text IS NULL OR f.name > $3)
|
|
88
|
+
ORDER BY f.name ASC
|
|
89
|
+
LIMIT $2`, [source_id, limit, name_after]);
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Reverse fields list (`target.upfields[]`).
|
|
93
|
+
*
|
|
94
|
+
* Returns rows whose `target_id = $1`, joined to `cell` on `source_id` so
|
|
95
|
+
* relations from tombstoned sources don't surface. The caller-side
|
|
96
|
+
* authz filter (per-source `can_view_cell`) runs after the SQL fetch
|
|
97
|
+
* — see the 2-layer authz contract on `cell_field_list({target_id})`.
|
|
98
|
+
*
|
|
99
|
+
* Bounded by `limit` (the wire `cell_field_list` cap) so a heavily
|
|
100
|
+
* inbound-linked target can't force an unbounded fetch + per-source authz
|
|
101
|
+
* pass on the public, IP-rate-limited reverse endpoint.
|
|
102
|
+
*
|
|
103
|
+
* @param deps - query deps
|
|
104
|
+
* @param target_id - target cell id
|
|
105
|
+
* @param options - `limit` caps the row count
|
|
106
|
+
* @returns matching rows, oldest first by source created_at
|
|
107
|
+
*/
|
|
108
|
+
export const query_cell_field_list_for_target = async (deps, target_id, options) => deps.db.query(`SELECT f.* FROM cell_field f
|
|
109
|
+
JOIN cell s ON s.id = f.source_id
|
|
110
|
+
WHERE f.target_id = $1
|
|
111
|
+
AND s.deleted_at IS NULL
|
|
112
|
+
ORDER BY f.created_at ASC
|
|
113
|
+
LIMIT $2`, [target_id, options?.limit ?? null]);
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raw queries against the `cell_grant` table.
|
|
3
|
+
*
|
|
4
|
+
* Resource-side ACL for cells: each row admits a principal at a `level`
|
|
5
|
+
* (`viewer` | `editor`). Principal is discriminated by which columns are
|
|
6
|
+
* set — `actor_id` (single actor) xor `(role, scope_id?)` (any holder
|
|
7
|
+
* of a matching role_grant). Owner is implicit on `cell.created_by` and never
|
|
8
|
+
* appears in this table.
|
|
9
|
+
*
|
|
10
|
+
* Convention: `deps: QueryDeps` first, no audit side effects, mutations
|
|
11
|
+
* return the affected row (or `null` for not-found).
|
|
12
|
+
*
|
|
13
|
+
* `query_cell_grant_create` upserts on the relevant partial unique index so
|
|
14
|
+
* re-granting the same principal updates `level` rather than producing
|
|
15
|
+
* duplicate rows. The two principal shapes use different indexes:
|
|
16
|
+
*
|
|
17
|
+
* - Actor-shaped: `idx_cell_grant_unique_actor` on `(cell_id, actor_id)`.
|
|
18
|
+
* - Role-shaped: `idx_cell_grant_unique_role_scope` on `(cell_id, role, scope_id)`
|
|
19
|
+
* with `NULLS NOT DISTINCT` so two `(role, NULL)` grants on the same cell
|
|
20
|
+
* collide.
|
|
21
|
+
*
|
|
22
|
+
* @module
|
|
23
|
+
*/
|
|
24
|
+
import type { QueryDeps } from './query_deps.js';
|
|
25
|
+
import type { Uuid } from '@fuzdev/fuz_util/id.js';
|
|
26
|
+
import type { CellGrantLevel } from '../auth/cell_grant_action_specs.js';
|
|
27
|
+
/** Row shape returned by `cell_grant` SELECTs. */
|
|
28
|
+
export interface CellGrantRow {
|
|
29
|
+
id: Uuid;
|
|
30
|
+
cell_id: Uuid;
|
|
31
|
+
level: CellGrantLevel;
|
|
32
|
+
actor_id: Uuid | null;
|
|
33
|
+
role: string | null;
|
|
34
|
+
scope_id: Uuid | null;
|
|
35
|
+
granted_by: Uuid | null;
|
|
36
|
+
created_at: Date;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Discriminated principal input for `query_cell_grant_create`. Wire
|
|
40
|
+
* and query shapes are aligned — actor-shaped principals carry a
|
|
41
|
+
* pre-resolved `actor_id`; pickers run `actor_search` to convert a
|
|
42
|
+
* typed name to an id upstream of the handler.
|
|
43
|
+
*/
|
|
44
|
+
export type CellGrantPrincipalQueryInput = {
|
|
45
|
+
kind: 'actor';
|
|
46
|
+
actor_id: Uuid;
|
|
47
|
+
} | {
|
|
48
|
+
kind: 'role';
|
|
49
|
+
role: string;
|
|
50
|
+
scope_id: Uuid | null;
|
|
51
|
+
};
|
|
52
|
+
/** Input for `query_cell_grant_create`. */
|
|
53
|
+
export interface CellGrantCreateQueryInput {
|
|
54
|
+
cell_id: Uuid;
|
|
55
|
+
level: CellGrantLevel;
|
|
56
|
+
principal: CellGrantPrincipalQueryInput;
|
|
57
|
+
granted_by: Uuid | null;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Insert a grant, or update the existing row's `level` + `granted_by` when
|
|
61
|
+
* one already exists for the same `(cell_id, principal)` pair.
|
|
62
|
+
*
|
|
63
|
+
* Idempotent re-share: caller doesn't need to check existence first. The
|
|
64
|
+
* UPSERT path runs even when the existing row's level matches — handlers
|
|
65
|
+
* reading the row's prior state for audit ("create vs. update") must do
|
|
66
|
+
* so before this call.
|
|
67
|
+
*
|
|
68
|
+
* @param deps - query deps
|
|
69
|
+
* @param input - cell, level, principal, grantor
|
|
70
|
+
* @returns the inserted-or-updated row
|
|
71
|
+
* @mutates `cell_grant` - inserts or updates one row
|
|
72
|
+
*/
|
|
73
|
+
export declare const query_cell_grant_create: (deps: QueryDeps, input: CellGrantCreateQueryInput) => Promise<CellGrantRow>;
|
|
74
|
+
/**
|
|
75
|
+
* Fetch a grant by id.
|
|
76
|
+
*
|
|
77
|
+
* @param deps - query deps
|
|
78
|
+
* @param grant_id - grant id
|
|
79
|
+
* @returns the row or `null` when not found
|
|
80
|
+
*/
|
|
81
|
+
export declare const query_cell_grant_get: (deps: QueryDeps, grant_id: Uuid) => Promise<CellGrantRow | null>;
|
|
82
|
+
/**
|
|
83
|
+
* Delete a grant by id, returning the deleted row.
|
|
84
|
+
*
|
|
85
|
+
* Returning the row lets the caller audit the principal + level after the
|
|
86
|
+
* delete and (for self-revoke) recompute `still_admitted` against the
|
|
87
|
+
* remaining grants on the cell without a second fetch.
|
|
88
|
+
*
|
|
89
|
+
* @param deps - query deps
|
|
90
|
+
* @param grant_id - grant id
|
|
91
|
+
* @returns the deleted row or `null` when no row matched
|
|
92
|
+
* @mutates `cell_grant` - deletes one row
|
|
93
|
+
*/
|
|
94
|
+
export declare const query_cell_grant_delete: (deps: QueryDeps, grant_id: Uuid) => Promise<CellGrantRow | null>;
|
|
95
|
+
/**
|
|
96
|
+
* List all grants on a cell, oldest first.
|
|
97
|
+
*
|
|
98
|
+
* Used by `cell_grant_list` (RPC) and by handlers that need grants
|
|
99
|
+
* alongside the cell row for the authorize predicate.
|
|
100
|
+
*
|
|
101
|
+
* @param deps - query deps
|
|
102
|
+
* @param cell_id - cell id
|
|
103
|
+
* @returns matching rows
|
|
104
|
+
*/
|
|
105
|
+
export declare const query_cell_grant_list_for_cell: (deps: QueryDeps, cell_id: Uuid) => Promise<Array<CellGrantRow>>;
|
|
106
|
+
/**
|
|
107
|
+
* List all grants across a set of cells, ordered by cell then creation.
|
|
108
|
+
* Used by the strict relation-read filter to test `can_view_cell` per
|
|
109
|
+
* target in memory — the caller groups the flat result by `cell_id`.
|
|
110
|
+
* Returns **every** grant on each cell (not caller-filtered), because
|
|
111
|
+
* `can_view_cell` needs the full grant list to decide admission.
|
|
112
|
+
*
|
|
113
|
+
* @param deps - query deps
|
|
114
|
+
* @param cell_ids - cells to fetch grants for (duplicates are harmless)
|
|
115
|
+
* @returns matching grant rows (group by `cell_id` caller-side)
|
|
116
|
+
*/
|
|
117
|
+
export declare const query_cell_grant_list_for_cells: (deps: QueryDeps, cell_ids: ReadonlyArray<Uuid>) => Promise<Array<CellGrantRow>>;
|
|
118
|
+
/**
|
|
119
|
+
* Load grants that admit the caller (by actor or role-scoped role_grants) across
|
|
120
|
+
* multiple cells. Used to enrich `cell_list` responses with context about what
|
|
121
|
+
* granted access. Returns grants for the given cells that match the caller's
|
|
122
|
+
* identity or role_grant set.
|
|
123
|
+
*
|
|
124
|
+
* @param cell_ids - cells to fetch grants for
|
|
125
|
+
* @param caller_actor_id - actor id of the caller (null for unauth)
|
|
126
|
+
* @param role_grant_roles - active role_grant roles (parallel array)
|
|
127
|
+
* @param role_grant_scope_ids - active role_grant scope ids (parallel array, parallel to roles)
|
|
128
|
+
* @returns matching grants (may include grants the caller doesn't match; caller's
|
|
129
|
+
* list handler must filter when returning to the API)
|
|
130
|
+
*/
|
|
131
|
+
export declare const query_cell_grants_for_caller_in_cells: (deps: QueryDeps, cell_ids: Array<Uuid>, caller_actor_id: Uuid | null, role_grant_roles: Array<string>, role_grant_scope_ids: Array<Uuid | null>) => Promise<Array<CellGrantRow>>;
|
|
132
|
+
//# sourceMappingURL=cell_grant_queries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cell_grant_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/cell_grant_queries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AACjD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,oCAAoC,CAAC;AAGvE,kDAAkD;AAClD,MAAM,WAAW,YAAY;IAC5B,EAAE,EAAE,IAAI,CAAC;IACT,OAAO,EAAE,IAAI,CAAC;IACd,KAAK,EAAE,cAAc,CAAC;IACtB,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,IAAI,CAAC;CACjB;AAED;;;;;GAKG;AACH,MAAM,MAAM,4BAA4B,GACrC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,IAAI,CAAA;CAAC,GAC/B;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAA;CAAC,CAAC;AAEvD,2CAA2C;AAC3C,MAAM,WAAW,yBAAyB;IACzC,OAAO,EAAE,IAAI,CAAC;IACd,KAAK,EAAE,cAAc,CAAC;IACtB,SAAS,EAAE,4BAA4B,CAAC;IACxC,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;CACxB;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,uBAAuB,GACnC,MAAM,SAAS,EACf,OAAO,yBAAyB,KAC9B,OAAO,CAAC,YAAY,CAsBtB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAChC,MAAM,SAAS,EACf,UAAU,IAAI,KACZ,OAAO,CAAC,YAAY,GAAG,IAAI,CAK7B,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,uBAAuB,GACnC,MAAM,SAAS,EACf,UAAU,IAAI,KACZ,OAAO,CAAC,YAAY,GAAG,IAAI,CAM7B,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,8BAA8B,GAC1C,MAAM,SAAS,EACf,SAAS,IAAI,KACX,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAM5B,CAAC;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,+BAA+B,GAC3C,MAAM,SAAS,EACf,UAAU,aAAa,CAAC,IAAI,CAAC,KAC3B,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAQ7B,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qCAAqC,GACjD,MAAM,SAAS,EACf,UAAU,KAAK,CAAC,IAAI,CAAC,EACrB,iBAAiB,IAAI,GAAG,IAAI,EAC5B,kBAAkB,KAAK,CAAC,MAAM,CAAC,EAC/B,sBAAsB,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,KACtC,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAmB7B,CAAC"}
|