@fuzdev/fuz_app 0.67.1 → 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.
Files changed (164) hide show
  1. package/dist/auth/CLAUDE.md +99 -5
  2. package/dist/auth/account_queries.d.ts +87 -4
  3. package/dist/auth/account_queries.d.ts.map +1 -1
  4. package/dist/auth/account_queries.js +107 -17
  5. package/dist/auth/account_schema.d.ts +19 -0
  6. package/dist/auth/account_schema.d.ts.map +1 -1
  7. package/dist/auth/account_schema.js +8 -0
  8. package/dist/auth/admin_action_specs.d.ts +168 -0
  9. package/dist/auth/admin_action_specs.d.ts.map +1 -1
  10. package/dist/auth/admin_action_specs.js +146 -1
  11. package/dist/auth/admin_actions.d.ts.map +1 -1
  12. package/dist/auth/admin_actions.js +218 -4
  13. package/dist/auth/audit_log_ddl.d.ts +10 -1
  14. package/dist/auth/audit_log_ddl.d.ts.map +1 -1
  15. package/dist/auth/audit_log_ddl.js +13 -4
  16. package/dist/auth/audit_log_schema.d.ts +34 -1
  17. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  18. package/dist/auth/audit_log_schema.js +73 -0
  19. package/dist/auth/auth_ddl.d.ts +2 -2
  20. package/dist/auth/auth_ddl.d.ts.map +1 -1
  21. package/dist/auth/auth_ddl.js +10 -2
  22. package/dist/auth/cell_action_specs.d.ts +1295 -0
  23. package/dist/auth/cell_action_specs.d.ts.map +1 -0
  24. package/dist/auth/cell_action_specs.js +397 -0
  25. package/dist/auth/cell_actions.d.ts +63 -0
  26. package/dist/auth/cell_actions.d.ts.map +1 -0
  27. package/dist/auth/cell_actions.js +546 -0
  28. package/dist/auth/cell_audit_action_specs.d.ts +131 -0
  29. package/dist/auth/cell_audit_action_specs.d.ts.map +1 -0
  30. package/dist/auth/cell_audit_action_specs.js +70 -0
  31. package/dist/auth/cell_audit_actions.d.ts +18 -0
  32. package/dist/auth/cell_audit_actions.d.ts.map +1 -0
  33. package/dist/auth/cell_audit_actions.js +59 -0
  34. package/dist/auth/cell_audit_events.d.ts +28 -0
  35. package/dist/auth/cell_audit_events.d.ts.map +1 -0
  36. package/dist/auth/cell_audit_events.js +42 -0
  37. package/dist/auth/cell_audit_metadata.d.ts +48 -0
  38. package/dist/auth/cell_audit_metadata.d.ts.map +1 -0
  39. package/dist/auth/cell_audit_metadata.js +46 -0
  40. package/dist/auth/cell_authorize.d.ts +88 -0
  41. package/dist/auth/cell_authorize.d.ts.map +1 -0
  42. package/dist/auth/cell_authorize.js +172 -0
  43. package/dist/auth/cell_data_schema.d.ts +44 -0
  44. package/dist/auth/cell_data_schema.d.ts.map +1 -0
  45. package/dist/auth/cell_data_schema.js +42 -0
  46. package/dist/auth/cell_field_action_specs.d.ts +244 -0
  47. package/dist/auth/cell_field_action_specs.d.ts.map +1 -0
  48. package/dist/auth/cell_field_action_specs.js +136 -0
  49. package/dist/auth/cell_field_actions.d.ts +34 -0
  50. package/dist/auth/cell_field_actions.d.ts.map +1 -0
  51. package/dist/auth/cell_field_actions.js +153 -0
  52. package/dist/auth/cell_field_audit_metadata.d.ts +30 -0
  53. package/dist/auth/cell_field_audit_metadata.d.ts.map +1 -0
  54. package/dist/auth/cell_field_audit_metadata.js +28 -0
  55. package/dist/auth/cell_grant_action_specs.d.ts +333 -0
  56. package/dist/auth/cell_grant_action_specs.d.ts.map +1 -0
  57. package/dist/auth/cell_grant_action_specs.js +148 -0
  58. package/dist/auth/cell_grant_actions.d.ts +50 -0
  59. package/dist/auth/cell_grant_actions.d.ts.map +1 -0
  60. package/dist/auth/cell_grant_actions.js +208 -0
  61. package/dist/auth/cell_grant_audit_metadata.d.ts +75 -0
  62. package/dist/auth/cell_grant_audit_metadata.d.ts.map +1 -0
  63. package/dist/auth/cell_grant_audit_metadata.js +54 -0
  64. package/dist/auth/cell_item_action_specs.d.ts +331 -0
  65. package/dist/auth/cell_item_action_specs.d.ts.map +1 -0
  66. package/dist/auth/cell_item_action_specs.js +182 -0
  67. package/dist/auth/cell_item_actions.d.ts +37 -0
  68. package/dist/auth/cell_item_actions.d.ts.map +1 -0
  69. package/dist/auth/cell_item_actions.js +204 -0
  70. package/dist/auth/cell_item_audit_metadata.d.ts +35 -0
  71. package/dist/auth/cell_item_audit_metadata.d.ts.map +1 -0
  72. package/dist/auth/cell_item_audit_metadata.js +32 -0
  73. package/dist/auth/cell_relation_visibility.d.ts +32 -0
  74. package/dist/auth/cell_relation_visibility.d.ts.map +1 -0
  75. package/dist/auth/cell_relation_visibility.js +57 -0
  76. package/dist/auth/deps.d.ts +9 -0
  77. package/dist/auth/deps.d.ts.map +1 -1
  78. package/dist/auth/role_grant_queries.d.ts +30 -0
  79. package/dist/auth/role_grant_queries.d.ts.map +1 -1
  80. package/dist/auth/role_grant_queries.js +54 -0
  81. package/dist/db/CLAUDE.md +118 -0
  82. package/dist/db/cell_audit_queries.d.ts +26 -0
  83. package/dist/db/cell_audit_queries.d.ts.map +1 -0
  84. package/dist/db/cell_audit_queries.js +53 -0
  85. package/dist/db/cell_ddl.d.ts +151 -0
  86. package/dist/db/cell_ddl.d.ts.map +1 -0
  87. package/dist/db/cell_ddl.js +247 -0
  88. package/dist/db/cell_field_queries.d.ts +105 -0
  89. package/dist/db/cell_field_queries.d.ts.map +1 -0
  90. package/dist/db/cell_field_queries.js +113 -0
  91. package/dist/db/cell_grant_queries.d.ts +132 -0
  92. package/dist/db/cell_grant_queries.d.ts.map +1 -0
  93. package/dist/db/cell_grant_queries.js +145 -0
  94. package/dist/db/cell_history_ddl.d.ts +38 -0
  95. package/dist/db/cell_history_ddl.d.ts.map +1 -0
  96. package/dist/db/cell_history_ddl.js +61 -0
  97. package/dist/db/cell_item_queries.d.ts +107 -0
  98. package/dist/db/cell_item_queries.d.ts.map +1 -0
  99. package/dist/db/cell_item_queries.js +119 -0
  100. package/dist/db/cell_queries.d.ts +327 -0
  101. package/dist/db/cell_queries.d.ts.map +1 -0
  102. package/dist/db/cell_queries.js +431 -0
  103. package/dist/db/fact_ddl.d.ts +38 -0
  104. package/dist/db/fact_ddl.d.ts.map +1 -0
  105. package/dist/db/fact_ddl.js +71 -0
  106. package/dist/db/fact_queries.d.ts +140 -0
  107. package/dist/db/fact_queries.d.ts.map +1 -0
  108. package/dist/db/fact_queries.js +161 -0
  109. package/dist/db/fact_store.d.ts +112 -0
  110. package/dist/db/fact_store.d.ts.map +1 -0
  111. package/dist/db/fact_store.js +225 -0
  112. package/dist/server/env.d.ts +2 -0
  113. package/dist/server/env.d.ts.map +1 -1
  114. package/dist/server/env.js +6 -0
  115. package/dist/server/fact_write.d.ts +32 -0
  116. package/dist/server/fact_write.d.ts.map +1 -0
  117. package/dist/server/fact_write.js +56 -0
  118. package/dist/server/file_fact_fetcher.d.ts +42 -0
  119. package/dist/server/file_fact_fetcher.d.ts.map +1 -0
  120. package/dist/server/file_fact_fetcher.js +60 -0
  121. package/dist/server/file_fact_url.d.ts +53 -0
  122. package/dist/server/file_fact_url.d.ts.map +1 -0
  123. package/dist/server/file_fact_url.js +52 -0
  124. package/dist/server/serve_fact_route.d.ts +78 -0
  125. package/dist/server/serve_fact_route.d.ts.map +1 -0
  126. package/dist/server/serve_fact_route.js +205 -0
  127. package/dist/testing/CLAUDE.md +58 -5
  128. package/dist/testing/app_server.d.ts +12 -0
  129. package/dist/testing/app_server.d.ts.map +1 -1
  130. package/dist/testing/app_server.js +36 -2
  131. package/dist/testing/audit_completeness.d.ts.map +1 -1
  132. package/dist/testing/audit_completeness.js +67 -1
  133. package/dist/testing/cross_backend/account_lifecycle.d.ts +10 -0
  134. package/dist/testing/cross_backend/account_lifecycle.d.ts.map +1 -0
  135. package/dist/testing/cross_backend/account_lifecycle.js +76 -0
  136. package/dist/testing/cross_backend/capabilities.d.ts +31 -0
  137. package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
  138. package/dist/testing/cross_backend/capabilities.js +3 -0
  139. package/dist/testing/cross_backend/cell_cross_helpers.d.ts +39 -0
  140. package/dist/testing/cross_backend/cell_cross_helpers.d.ts.map +1 -0
  141. package/dist/testing/cross_backend/cell_cross_helpers.js +45 -0
  142. package/dist/testing/cross_backend/cell_crud.d.ts +4 -0
  143. package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -0
  144. package/dist/testing/cross_backend/cell_crud.js +168 -0
  145. package/dist/testing/cross_backend/cell_relations.d.ts +4 -0
  146. package/dist/testing/cross_backend/cell_relations.d.ts.map +1 -0
  147. package/dist/testing/cross_backend/cell_relations.js +229 -0
  148. package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
  149. package/dist/testing/cross_backend/default_backend_configs.js +6 -0
  150. package/dist/testing/cross_backend/setup.d.ts.map +1 -1
  151. package/dist/testing/cross_backend/setup.js +5 -0
  152. package/dist/testing/entities.d.ts.map +1 -1
  153. package/dist/testing/entities.js +4 -0
  154. package/dist/testing/ws_round_trip.d.ts.map +1 -1
  155. package/dist/testing/ws_round_trip.js +4 -0
  156. package/dist/ui/AdminAccounts.svelte +58 -0
  157. package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -1
  158. package/dist/ui/admin_accounts_state.svelte.d.ts +30 -2
  159. package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
  160. package/dist/ui/admin_accounts_state.svelte.js +45 -1
  161. package/dist/ui/admin_rpc_adapters.d.ts +6 -2
  162. package/dist/ui/admin_rpc_adapters.d.ts.map +1 -1
  163. package/dist/ui/admin_rpc_adapters.js +5 -1
  164. package/package.json +2 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cell_action_specs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_action_specs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,397 @@
1
+ /**
2
+ * Cell RPC action specs — the declarative contract for the six generic
3
+ * cell verbs. App vocabulary (galleries, posts, events) lives in
4
+ * client-side helpers; the wire stays generic.
5
+ *
6
+ * @module
7
+ */
8
+ import { z } from 'zod';
9
+ import { Uuid } from '@fuzdev/fuz_util/id.js';
10
+ import { FactHashSchema } from '@fuzdev/fuz_util/fact_hash.js';
11
+ import { ActingActor } from '../http/auth_shape.js';
12
+ import { all_cell_grant_action_specs, cell_grant_create_action_spec, cell_grant_revoke_action_spec, cell_grant_list_action_spec, GrantJson, } from './cell_grant_action_specs.js';
13
+ import { all_cell_field_action_specs, cell_field_set_action_spec, cell_field_delete_action_spec, cell_field_list_action_spec, FieldJson, } from './cell_field_action_specs.js';
14
+ import { all_cell_item_action_specs, cell_item_insert_action_spec, cell_item_move_action_spec, cell_item_delete_action_spec, cell_item_list_action_spec, ItemJson, } from './cell_item_action_specs.js';
15
+ import { all_cell_audit_action_specs, cell_audit_list_action_spec, } from './cell_audit_action_specs.js';
16
+ import { CellData } from './cell_data_schema.js';
17
+ /**
18
+ * Cell visibility — the coarse-grained access-control axis for a cell.
19
+ * Sibling to `cell_grant` (the fine-grained allowlist of actor- /
20
+ * role-shaped principals at `viewer` / `editor` levels). Together they
21
+ * form the cell-layer access-control surface:
22
+ *
23
+ * - `cell.visibility = 'public'` admits everyone, including
24
+ * unauthenticated visitors. `cell_grant` rows still apply for edit-
25
+ * level admit; read is universal.
26
+ * - `cell.visibility = 'private'` (default) restricts read to admin /
27
+ * owner (`created_by`) / `cell_grant`-admitted callers.
28
+ *
29
+ * Stored as a top-level PG enum column (`cell.visibility`) — NOT inside
30
+ * `cell.data`, which is content metadata only. `can_view_cell` reads
31
+ * the column directly.
32
+ */
33
+ export const CellVisibility = z.enum(['private', 'public']);
34
+ // Re-exported so the codegen's `cell_specs.*` qualifier resolves them
35
+ // under the single cell-layer namespace. `cell_action_specs.ts` is the
36
+ // registry/aggregator module for the cell layer; the grant / field /
37
+ // item specs live in their own files for source-file separation but
38
+ // ride the same namespace.
39
+ export { cell_grant_create_action_spec, cell_grant_revoke_action_spec, cell_grant_list_action_spec };
40
+ export { cell_field_set_action_spec, cell_field_delete_action_spec, cell_field_list_action_spec };
41
+ export { cell_item_insert_action_spec, cell_item_move_action_spec, cell_item_delete_action_spec, cell_item_list_action_spec, };
42
+ export { cell_audit_list_action_spec };
43
+ // -- Error reasons ----------------------------------------------------------
44
+ /** Error reason — cell id did not resolve, or caller can't view it. */
45
+ export const ERROR_CELL_NOT_FOUND = 'cell_not_found';
46
+ /** Error reason — caller is not an admin and supplied a `path` write. */
47
+ export const ERROR_CELL_PATH_ADMIN_ONLY = 'cell_path_admin_only';
48
+ /**
49
+ * Error reason — a `path` write collided with an existing active cell's
50
+ * path. `path` is globally unique on active rows (`idx_cell_path_unique`);
51
+ * the create / update handlers translate the unique-index violation into
52
+ * this `conflict` (409) reason rather than leaking a raw internal error.
53
+ * Soft-deleted rows free their path (the index is partial on
54
+ * `deleted_at IS NULL`), so reusing a deleted cell's path does not collide.
55
+ */
56
+ export const ERROR_CELL_PATH_TAKEN = 'cell_path_taken';
57
+ /**
58
+ * Error reason — caller tried to write `cell.visibility` without the
59
+ * manage tier (`can_manage_cell` = admin / owner). Editor-grant holders
60
+ * may edit `data` but cannot flip a cell's visibility — that is a
61
+ * manage-tier-only operation.
62
+ */
63
+ export const ERROR_CELL_VISIBILITY_MANAGE_ONLY = 'cell_visibility_manage_only';
64
+ /** Error reason — input shape for `cell_get` lacked both `id` and `path`. */
65
+ export const ERROR_CELL_GET_REQUIRES_ID_OR_PATH = 'cell_get_requires_id_or_path';
66
+ /**
67
+ * Error reason — `cell_clone` `with_data_patch` would change `data.kind`.
68
+ * Per-kind shape validation can pass coincidentally (e.g., one kind's
69
+ * schema accepts most of another's fields), so we reject the cross-kind
70
+ * patch explicitly to prevent incoherent clones.
71
+ */
72
+ export const ERROR_CELL_CLONE_KIND_MISMATCH = 'cell_clone_kind_mismatch';
73
+ /**
74
+ * Error reason — null-auth `cell_list` caller passed a `created_by`
75
+ * filter. The filter is a soft account-id enumeration vector ("does
76
+ * account X have any public cells?"), so we require an authenticated
77
+ * caller to use it.
78
+ */
79
+ export const ERROR_CELL_LIST_CREATED_BY_REQUIRES_AUTH = 'cell_list_created_by_requires_auth';
80
+ /**
81
+ * Error reason — null-auth `cell_list` caller passed `shared_with: 'me'`.
82
+ * The filter resolves to the caller's account + role_grants, which only
83
+ * exist for an authenticated session.
84
+ */
85
+ export const ERROR_CELL_LIST_SHARED_WITH_REQUIRES_AUTH = 'cell_list_shared_with_requires_auth';
86
+ // -- Shared schemas ---------------------------------------------------------
87
+ /**
88
+ * Wire form for `cell.path`.
89
+ *
90
+ * At the spec level we only enforce that the value is a non-empty string
91
+ * with a sane upper bound — the cell layer is generic and doesn't impose
92
+ * a path grammar. App-side curation (well-known names like `/map/main`,
93
+ * `/site/events`) is admin-driven.
94
+ */
95
+ export const CELL_PATH_LENGTH_MAX = 256;
96
+ /**
97
+ * Branded so the type system distinguishes a validated path from any other
98
+ * string. Construct via `CellPath.parse(s)` at external boundaries; the
99
+ * RPC dispatcher does this automatically when the wire schema (`CellCreateInput`,
100
+ * `CellGetInput`, `CellUpdateInput`, `CellListInput`) is parsed at the entry
101
+ * point. Frontend callers handing a raw string to `api.cell_*` cast at the
102
+ * callsite (`as CellPath`) — the runtime check still runs server-side.
103
+ */
104
+ export const CellPath = z.string().min(1).max(CELL_PATH_LENGTH_MAX).brand('CellPath');
105
+ /**
106
+ * Soft cap on the size of a `cell.list` request page. Larger pages chew
107
+ * memory both server- and client-side; combined with the visibility
108
+ * predicate's filter cost, 200 is a safe ceiling.
109
+ */
110
+ export const CELL_LIST_LIMIT_MAX = 200;
111
+ export const CELL_LIST_LIMIT_DEFAULT = 50;
112
+ /**
113
+ * Hard cap on bundled relation arrays in `cell_get` (per-relation
114
+ * `LIMIT`). Beyond this the response sets `*_truncated: true` and the
115
+ * client paginates via `cell_item_list({parent_id, position_after})` /
116
+ * `cell_field_list({source_id, name_after})`.
117
+ */
118
+ export const CELL_RELATIONS_BUNDLE_LIMIT = 500;
119
+ /**
120
+ * Wire form for a cell row. `data` is the typed-but-permissive `CellData`
121
+ * shape (kind / label / summary typed-and-optional, additional fields
122
+ * pass through). Per-kind shape validation is sub-API and handled by
123
+ * the app's `validate_data` deps callback (see `cell_actions.ts`).
124
+ *
125
+ * `visibility` is the access-control axis — a top-level column on the
126
+ * row, not a field inside `data`. `cell_grant` and `visibility` are the
127
+ * two ACL surfaces; both live as peers, not embedded in content.
128
+ *
129
+ * `path` is the global namespace axis (no tenant/hub scoping).
130
+ *
131
+ * Relations (`items`, `fields`) are NOT carried on the cell row — they
132
+ * live in the `cell_item` / `cell_field` sibling tables. Bundled arrays
133
+ * appear on `CellGetOutput`; other read verbs (`cell_list`) do not bundle.
134
+ */
135
+ export const CellJson = z.strictObject({
136
+ id: Uuid,
137
+ path: CellPath.nullable(),
138
+ data: CellData,
139
+ visibility: CellVisibility.meta({
140
+ description: "Access-control tag. `'public'` admits everyone (including unauthenticated visitors); `'private'` (default) admits admin / owner / `cell_grant`-admitted callers. Top-level column, not inside `data`.",
141
+ }),
142
+ refs: z.array(FactHashSchema).nullable(),
143
+ created_by: Uuid.nullable(),
144
+ updated_by: Uuid.nullable(),
145
+ created_at: z.string(),
146
+ updated_at: z.string().nullable(),
147
+ deleted_at: z.string().nullable(),
148
+ grant_count: z.number().int().nonnegative().meta({
149
+ description: 'Number of `cell_grant` rows naming this cell. Non-leaky scalar (no actor/role identity); surfaces "Shared with N" badges. Derived in SQL via a correlated subquery on `idx_cell_grant_cell`.',
150
+ }),
151
+ });
152
+ // -- cell_create ------------------------------------------------------------
153
+ /**
154
+ * Input for `cell_create`. `created_by` is NOT on the wire — the handler
155
+ * stamps it from auth.actor.id. `path` is admin-only; non-admin callers
156
+ * supplying `path` get `ERROR_CELL_PATH_ADMIN_ONLY` (forbidden).
157
+ */
158
+ export const CellCreateInput = z.strictObject({
159
+ data: CellData.meta({
160
+ description: 'Cell data. Base fields (kind / label / summary) typed; extras loose. Per-kind shape is sub-API.',
161
+ }),
162
+ visibility: CellVisibility.optional().meta({
163
+ description: "Access-control tag. Top-level column (not in `data`). Default `'private'` when omitted.",
164
+ }),
165
+ path: CellPath.nullish().meta({
166
+ description: 'Admin-only named lookup alias. Globally unique on active rows.',
167
+ }),
168
+ acting: ActingActor,
169
+ });
170
+ export const CellCreateOutput = z.strictObject({ cell: CellJson });
171
+ // -- cell_get ---------------------------------------------------------------
172
+ /**
173
+ * Input for `cell_get`. Pass `id` OR `path` (exactly one expected; both
174
+ * accepted, `id` takes precedence). The handler responds with 404 when no
175
+ * row matches OR when `can_view_cell` rejects the caller — same code so
176
+ * private-cell existence doesn't leak.
177
+ */
178
+ export const CellGetInput = z
179
+ .strictObject({
180
+ id: Uuid.optional(),
181
+ path: CellPath.optional(),
182
+ acting: ActingActor,
183
+ })
184
+ .refine((v) => v.id !== undefined || v.path !== undefined, {
185
+ message: ERROR_CELL_GET_REQUIRES_ID_OR_PATH,
186
+ });
187
+ /**
188
+ * Output for `cell_get`. Bundles relation arrays (`fields` + `items`)
189
+ * server-side via JOINs so the common "show this cell with its
190
+ * children" flow needs one round-trip. Targets are filtered to those the
191
+ * caller may view (strict target-visibility). Per-relation `LIMIT
192
+ * CELL_RELATIONS_BUNDLE_LIMIT`; clients paginate via
193
+ * `cell_item_list({parent_id, position_after})` /
194
+ * `cell_field_list({source_id})` when truncated.
195
+ */
196
+ export const CellGetOutput = z.strictObject({
197
+ cell: CellJson,
198
+ fields: z.array(FieldJson),
199
+ fields_truncated: z.boolean(),
200
+ items: z.array(ItemJson),
201
+ items_truncated: z.boolean(),
202
+ can_edit: z.boolean(),
203
+ can_grant: z.boolean(),
204
+ });
205
+ // -- cell_update ------------------------------------------------------------
206
+ /**
207
+ * Input for `cell_update`. Fields left undefined keep their existing value.
208
+ * `path` writes are admin-only (handler-enforced); non-admin callers
209
+ * supplying `path` get `ERROR_CELL_PATH_ADMIN_ONLY` even if no other field
210
+ * is changing. `visibility` writes require the manage tier
211
+ * (`can_manage_cell` = admin / owner) — editor-grant holders editing
212
+ * `data` cannot flip visibility (`ERROR_CELL_VISIBILITY_MANAGE_ONLY`).
213
+ */
214
+ export const CellUpdateInput = z.strictObject({
215
+ cell_id: Uuid.meta({ description: 'Cell to update.' }),
216
+ data: CellData.optional(),
217
+ visibility: CellVisibility.optional().meta({
218
+ description: 'Access-control tag. Top-level column (not in `data`). Manage-tier write.',
219
+ }),
220
+ path: CellPath.nullable().optional().meta({ description: 'Admin-only path write.' }),
221
+ acting: ActingActor,
222
+ });
223
+ export const CellUpdateOutput = z.strictObject({ cell: CellJson });
224
+ // -- cell_delete ------------------------------------------------------------
225
+ export const CellDeleteInput = z.strictObject({
226
+ cell_id: Uuid.meta({ description: 'Cell to soft-delete.' }),
227
+ acting: ActingActor,
228
+ });
229
+ export const CellDeleteOutput = z.strictObject({
230
+ ok: z.literal(true),
231
+ deleted: z.boolean(),
232
+ });
233
+ // -- cell_list --------------------------------------------------------------
234
+ /**
235
+ * Input for `cell_list`. Filters are optional and combine with AND. The
236
+ * handler applies the SQL-side visibility predicate from
237
+ * `query_cell_list` so the page-window stays correct under pagination —
238
+ * post-filtering in JS would silently truncate pages.
239
+ *
240
+ * `ids` is the batch-read filter — pass a list of cell ids to fetch them
241
+ * in one round-trip (avoids N+1 when rendering a collection's `items[]`).
242
+ * The visibility predicate still runs, so callers passing ids they can't
243
+ * view simply get fewer rows back. Capped at `CELL_LIST_LIMIT_MAX`.
244
+ *
245
+ * `shared_with: 'me'` narrows to cells that admit the caller via a
246
+ * `cell_grant` row (actor-shaped or role-shaped principal) AND that
247
+ * the caller does not own. Authenticated only; combine with
248
+ * `data_kind` / `path_prefix` etc. to scope further. Combining with
249
+ * `created_by: <my-actor-id>` produces an empty result by definition
250
+ * (owner is implicit, never appears as a grant principal); we don't
251
+ * reject the combination at the schema layer because SQL emptiness is
252
+ * correct.
253
+ */
254
+ export const CellListInput = z
255
+ .strictObject({
256
+ ids: z
257
+ .array(Uuid)
258
+ .max(CELL_LIST_LIMIT_MAX)
259
+ .optional()
260
+ .meta({ description: 'Batch-fetch by id. Visibility predicate still applies.' }),
261
+ data_kind: z.string().min(1).optional().meta({ description: 'Match `data.kind = ?`.' }),
262
+ visibility: CellVisibility.optional().meta({
263
+ description: "Match `cell.visibility = ?`. The SQL-side auth-narrow already filters to public-or-admitted; this is an additional narrowing filter (e.g. `visibility: 'public'` on the discovery feed so authed callers don't see their own private entries mixed in).",
264
+ }),
265
+ ref: FactHashSchema.optional().meta({ description: 'Match cells referencing this fact hash.' }),
266
+ created_by: Uuid.optional().meta({ description: 'Filter to a specific creator.' }),
267
+ path_prefix: CellPath.optional().meta({
268
+ description: 'Match cells whose path starts with this.',
269
+ }),
270
+ shared_with: z.literal('me').optional().meta({
271
+ description: 'Narrow to cells admitting the caller via `cell_grant`, excluding cells the caller owns. Self-only — a `Uuid` form would need cross-actor role_grant loading.',
272
+ }),
273
+ order_by: z.enum(['created_at', 'updated_at']).optional(),
274
+ order_direction: z.enum(['asc', 'desc']).optional(),
275
+ limit: z.number().int().positive().max(CELL_LIST_LIMIT_MAX).optional(),
276
+ offset: z.number().int().nonnegative().optional(),
277
+ acting: ActingActor,
278
+ })
279
+ .default({});
280
+ export const CellListOutput = z.strictObject({
281
+ cells: z.array(CellJson),
282
+ cell_grants: z.record(z.string(), z.array(GrantJson)).optional(),
283
+ });
284
+ // -- cell_clone -------------------------------------------------------------
285
+ /**
286
+ * Input for `cell_clone`. Source must be view-admitted by `can_view_cell`
287
+ * (404 otherwise — IDOR mask). The clone is owned by the caller; `path`
288
+ * is always nulled (admin-only paths can't auto-clone). Provenance lives
289
+ * only in the `cell_clone` audit row's `source_id` — no provenance
290
+ * fields are stamped into `data`.
291
+ */
292
+ export const CellCloneInput = z.strictObject({
293
+ source_id: Uuid.meta({ description: 'Cell to clone.' }),
294
+ deep: z.boolean().optional().meta({
295
+ description: 'Recurse into `items[]` (depth=1 — clones direct children only). Default false.',
296
+ }),
297
+ // TODO: cap `with_data_patch` size/depth once a consumer measures the
298
+ // upper bound. `z.json()` is unbounded — a multi-MB patch is in scope
299
+ // today, gated only by the JSON-RPC body limit. Realistic patches are
300
+ // O(few KB); a `.refine` on serialized size would tighten the surface
301
+ // without disturbing the patch-wins-last semantics.
302
+ with_data_patch: CellData.optional().meta({
303
+ description: "Optional shallow patch merged into the new root cell's `data` (patch-last semantics).",
304
+ }),
305
+ acting: ActingActor,
306
+ });
307
+ export const CellCloneOutput = z.strictObject({ cell: CellJson });
308
+ // -- Action specs -----------------------------------------------------------
309
+ export const cell_create_action_spec = {
310
+ method: 'cell_create',
311
+ kind: 'request_response',
312
+ initiator: 'frontend',
313
+ auth: { account: 'required', actor: 'required' },
314
+ side_effects: true,
315
+ input: CellCreateInput,
316
+ output: CellCreateOutput,
317
+ async: true,
318
+ rate_limit: 'account',
319
+ description: 'Create a cell. Handler stamps `created_by` from auth.actor.id; `path` writes are admin-only. Per-account rate-limited to bound write-spam.',
320
+ };
321
+ export const cell_get_action_spec = {
322
+ method: 'cell_get',
323
+ kind: 'request_response',
324
+ initiator: 'frontend',
325
+ auth: { account: 'optional', actor: 'optional' },
326
+ side_effects: false,
327
+ input: CellGetInput,
328
+ output: CellGetOutput,
329
+ async: true,
330
+ rate_limit: 'ip',
331
+ description: 'Fetch a cell by id or path. Per-row authz via `can_view_cell`; unauthed callers get only `cell.visibility === "public"` cells. 404 on miss or unauthorized — no existence leak. Bundled relations are filtered to viewable targets. Per-IP rate-limited as the defense-in-depth complement to `cell_list`: an id-walker that learns ids from a side channel can pivot from the enumeration entry point to per-row reads.',
332
+ };
333
+ export const cell_update_action_spec = {
334
+ method: 'cell_update',
335
+ kind: 'request_response',
336
+ initiator: 'frontend',
337
+ auth: { account: 'required', actor: 'required' },
338
+ side_effects: true,
339
+ input: CellUpdateInput,
340
+ output: CellUpdateOutput,
341
+ async: true,
342
+ description: 'Update a cell. Per-row `can_edit_cell` (admin / owner / editor-grant). `visibility` writes require the manage tier (admin / owner). `path` writes are admin-only. Stamps `updated_by`.',
343
+ };
344
+ export const cell_delete_action_spec = {
345
+ method: 'cell_delete',
346
+ kind: 'request_response',
347
+ initiator: 'frontend',
348
+ auth: { account: 'required', actor: 'required' },
349
+ side_effects: true,
350
+ input: CellDeleteInput,
351
+ output: CellDeleteOutput,
352
+ async: true,
353
+ description: 'Soft-delete a cell. Per-row `can_edit_cell` (admin / owner / editor-grant).',
354
+ };
355
+ export const cell_list_action_spec = {
356
+ method: 'cell_list',
357
+ kind: 'request_response',
358
+ initiator: 'frontend',
359
+ auth: { account: 'optional', actor: 'optional' },
360
+ side_effects: false,
361
+ input: CellListInput,
362
+ output: CellListOutput,
363
+ async: true,
364
+ rate_limit: 'ip',
365
+ description: 'List cells with optional filters. SQL-side visibility predicate: admin sees all; authed see owned + public + grant-admitted; null auth sees public-only. `created_by` filter is rejected for null auth (account-id enumeration guard). Per-IP rate-limited to bound the public-enumeration surface (paired with `actor_lookup` it would be a scrape primitive).',
366
+ };
367
+ export const cell_clone_action_spec = {
368
+ method: 'cell_clone',
369
+ kind: 'request_response',
370
+ initiator: 'frontend',
371
+ auth: { account: 'required', actor: 'required' },
372
+ side_effects: true,
373
+ input: CellCloneInput,
374
+ output: CellCloneOutput,
375
+ async: true,
376
+ rate_limit: 'account',
377
+ description: 'Clone a cell (optionally deep). New owner is the caller; `path` is always nulled. Provenance recorded only in the `cell_clone` audit row. Per-account rate-limited — `deep: true` walks `cell_item` rows and fans out, so unbounded clone is a write-amplification vector.',
378
+ };
379
+ /**
380
+ * All cell-layer action specs — composed by app registries. Bundles the
381
+ * six generic verbs (this module), the three `cell_grant_*` specs, the
382
+ * three `cell_field_*` specs, the four `cell_item_*` specs, and the
383
+ * `cell_audit_list` spec so codegen + UI clients see a single cell
384
+ * namespace.
385
+ */
386
+ export const all_cell_action_specs = [
387
+ cell_create_action_spec,
388
+ cell_get_action_spec,
389
+ cell_update_action_spec,
390
+ cell_delete_action_spec,
391
+ cell_list_action_spec,
392
+ cell_clone_action_spec,
393
+ ...all_cell_grant_action_specs,
394
+ ...all_cell_field_action_specs,
395
+ ...all_cell_item_action_specs,
396
+ ...all_cell_audit_action_specs,
397
+ ];
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Generic cell RPC action handlers.
3
+ *
4
+ * Six `request_response` actions bound to the specs in
5
+ * `./cell_action_specs.ts`:
6
+ *
7
+ * - Mutations: `cell_create`, `cell_update`, `cell_delete`, `cell_clone`.
8
+ * - Reads: `cell_get`, `cell_list`.
9
+ *
10
+ * Authorization model:
11
+ *
12
+ * - `cell_create` is authenticated at the spec level. The handler stamps
13
+ * `created_by` from `auth.actor.id`. `path` writes are admin-only —
14
+ * non-admin callers supplying `path` get `ERROR_CELL_PATH_ADMIN_ONLY`.
15
+ * - `cell_get` is `optional` auth at the spec level. Per-row authorization
16
+ * via `can_view_cell(auth, cell)`. Misses + unauthorized reads both 404,
17
+ * so private-cell existence doesn't leak through the wire. Bundled
18
+ * relations are filtered to viewable targets (strict target-visibility).
19
+ * - `cell_update` / `cell_delete` are authenticated at the spec level
20
+ * with per-row `can_edit_cell` enforcement. `path` writes on update
21
+ * are admin-only; `visibility` writes require the manage tier
22
+ * (`can_manage_cell`).
23
+ * - `cell_list` is `optional` auth at the spec level. The SQL-side
24
+ * visibility predicate in `query_cell_list` admits null auth to
25
+ * public-only rows and authed callers to owned + public + grant-admitted
26
+ * rows; admin sees all. SQL-side because post-filtering in JS would
27
+ * silently truncate pages. The handler rejects the `created_by` filter
28
+ * for null auth (account-id enumeration guard).
29
+ *
30
+ * Mutations emit `cell_create` / `cell_update` / `cell_delete` audit
31
+ * events via `deps.audit.emit(...)`. The `AuditLogConfig` threaded through
32
+ * the consumer's `audit_factory` (see `create_app_backend`) must declare
33
+ * the cell event types (see `./cell_audit_metadata.ts`).
34
+ *
35
+ * App vocabulary (e.g., collection / entry kinds) lives in client-side
36
+ * helpers and per-app `validate_data` deps — this layer is generic-only
37
+ * by construction.
38
+ *
39
+ * @module
40
+ */
41
+ import { type RpcAction } from '../actions/action_rpc.js';
42
+ import type { RouteFactoryDeps } from './deps.js';
43
+ import { type CellJson } from './cell_action_specs.js';
44
+ import { type CellRow } from '../db/cell_queries.js';
45
+ import type { CellData } from './cell_data_schema.js';
46
+ /**
47
+ * Dependencies for `create_cell_actions`.
48
+ *
49
+ * `validate_data` is the optional sub-API hook for per-kind shape
50
+ * validation (e.g., a collection/entry registry). It runs on every
51
+ * incoming `data` payload (create, update, clone-merged) and may throw
52
+ * a `ZodError` — the handler converts that into the standard
53
+ * `invalid_params` JSON-RPC error so per-kind validation failures
54
+ * surface to clients with code -32602 (not -32603 / internal). When
55
+ * omitted, payloads pass through as-is.
56
+ */
57
+ export type CellActionDeps = Pick<RouteFactoryDeps, 'log' | 'audit'> & {
58
+ validate_data?: (data: CellData) => CellData;
59
+ };
60
+ export declare const to_cell_json: (row: CellRow) => CellJson;
61
+ /** Create the six generic cell RPC actions. */
62
+ export declare const create_cell_actions: (deps: CellActionDeps) => Array<RpcAction>;
63
+ //# sourceMappingURL=cell_actions.d.ts.map
@@ -0,0 +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;AAKlC,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,WAAW,CAAC;AAGhD,OAAO,EA6BN,KAAK,QAAQ,EAEb,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAQN,KAAK,OAAO,EACZ,MAAM,uBAAuB,CAAC;AAkB/B,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,uBAAuB,CAAC;AAEpD;;;;;;;;;;GAUG;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;CAC7C,CAAC;AAgBF,eAAO,MAAM,YAAY,GAAI,KAAK,OAAO,KAAG,QAY1C,CAAC;AA2BH,+CAA+C;AAC/C,eAAO,MAAM,mBAAmB,GAAI,MAAM,cAAc,KAAG,KAAK,CAAC,SAAS,CA4ezE,CAAC"}