@fuzdev/fuz_app 0.102.0 → 0.104.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/auth/CLAUDE.md +37 -10
  2. package/dist/auth/cell_action_specs.d.ts +261 -35
  3. package/dist/auth/cell_action_specs.d.ts.map +1 -1
  4. package/dist/auth/cell_action_specs.js +102 -9
  5. package/dist/auth/cell_actions.d.ts +51 -0
  6. package/dist/auth/cell_actions.d.ts.map +1 -1
  7. package/dist/auth/cell_actions.js +168 -22
  8. package/dist/auth/cell_audit_events.d.ts +1 -1
  9. package/dist/auth/cell_audit_events.d.ts.map +1 -1
  10. package/dist/auth/cell_audit_events.js +3 -2
  11. package/dist/auth/cell_audit_metadata.d.ts +15 -0
  12. package/dist/auth/cell_audit_metadata.d.ts.map +1 -1
  13. package/dist/auth/cell_audit_metadata.js +11 -0
  14. package/dist/auth/cell_data_schema.d.ts +14 -16
  15. package/dist/auth/cell_data_schema.d.ts.map +1 -1
  16. package/dist/auth/cell_data_schema.js +14 -16
  17. package/dist/db/CLAUDE.md +19 -6
  18. package/dist/db/cell_ddl.d.ts +24 -2
  19. package/dist/db/cell_ddl.d.ts.map +1 -1
  20. package/dist/db/cell_ddl.js +33 -1
  21. package/dist/db/cell_queries.d.ts +59 -8
  22. package/dist/db/cell_queries.d.ts.map +1 -1
  23. package/dist/db/cell_queries.js +50 -12
  24. package/dist/testing/CLAUDE.md +37 -7
  25. package/dist/testing/cross_backend/capabilities.d.ts +14 -0
  26. package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
  27. package/dist/testing/cross_backend/capabilities.js +3 -0
  28. package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -1
  29. package/dist/testing/cross_backend/cell_crud.js +55 -13
  30. package/dist/testing/cross_backend/cell_gated_create.d.ts +28 -0
  31. package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -0
  32. package/dist/testing/cross_backend/cell_gated_create.js +213 -0
  33. package/dist/testing/cross_backend/cell_grant_role.js +4 -4
  34. package/dist/testing/cross_backend/cell_relations.d.ts.map +1 -1
  35. package/dist/testing/cross_backend/cell_relations.js +30 -24
  36. package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
  37. package/dist/testing/cross_backend/default_backend_configs.js +7 -0
  38. package/dist/testing/cross_backend/expected_schema.json +4 -0
  39. package/dist/testing/cross_backend/fact_serving.js +2 -2
  40. package/dist/testing/cross_backend/full_spine_mount.d.ts.map +1 -1
  41. package/dist/testing/cross_backend/full_spine_mount.js +9 -1
  42. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
  43. package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +6 -3
  44. package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts +22 -0
  45. package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts.map +1 -0
  46. package/dist/testing/cross_backend/test_cell_gated_create_authorize.js +75 -0
  47. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
  48. package/dist/testing/cross_backend/ts_spine_backend_config.js +5 -2
  49. package/package.json +1 -1
@@ -100,9 +100,9 @@ export const describe_cell_relations_cross_tests = (options) => {
100
100
  const t = fixture.fresh_transport();
101
101
  const owner_h = owner.create_session_headers();
102
102
  const editor_h = editor.create_session_headers();
103
- const cell = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { data: { kind: 'note' } }, owner_h), CellCreateOutput).cell;
103
+ const cell = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { kind: 'note', data: {} }, owner_h), CellCreateOutput).cell;
104
104
  // Before the grant the editor can't even see the private cell.
105
- const pre = await cross_rpc_call(t, rpc_path, 'cell_update', { cell_id: cell.id, data: { kind: 'note', label: 'x' } }, editor_h);
105
+ const pre = await cross_rpc_call(t, rpc_path, 'cell_update', { cell_id: cell.id, data: { label: 'x' } }, editor_h);
106
106
  assert.ok(!pre.ok, 'non-grantee edited a private cell');
107
107
  assert.strictEqual(error_reason(pre), 'cell_not_found');
108
108
  // Owner (manage-tier) grants the editor an actor-shaped editor grant.
@@ -115,7 +115,7 @@ export const describe_cell_relations_cross_tests = (options) => {
115
115
  assert.strictEqual(grant.actor_id, editor.actor.id);
116
116
  assert.strictEqual(grant.role, null);
117
117
  // The editor can now edit content.
118
- const edited = expect_output(await cross_rpc_call(t, rpc_path, 'cell_update', { cell_id: cell.id, data: { kind: 'note', label: 'by editor' } }, editor_h), CellUpdateOutput);
118
+ const edited = expect_output(await cross_rpc_call(t, rpc_path, 'cell_update', { cell_id: cell.id, data: { label: 'by editor' } }, editor_h), CellUpdateOutput);
119
119
  assert.strictEqual(edited.cell.updated_by, editor.actor.id);
120
120
  // grant_list is manage-tier: owner sees the grant.
121
121
  const owner_grants = expect_output(await cross_rpc_call(t, rpc_path, 'cell_grant_list', { cell_id: cell.id }, owner_h), CellGrantListOutput);
@@ -127,7 +127,7 @@ export const describe_cell_relations_cross_tests = (options) => {
127
127
  // Revoke and confirm the edit path is gone.
128
128
  const revoked = expect_output(await cross_rpc_call(t, rpc_path, 'cell_grant_revoke', { grant_id: grant.id }, owner_h), CellGrantRevokeOutput);
129
129
  assert.strictEqual(revoked.ok, true);
130
- const post = await cross_rpc_call(t, rpc_path, 'cell_update', { cell_id: cell.id, data: { kind: 'note', label: 'y' } }, editor_h);
130
+ const post = await cross_rpc_call(t, rpc_path, 'cell_update', { cell_id: cell.id, data: { label: 'y' } }, editor_h);
131
131
  assert.ok(!post.ok, 'revoked editor still edited');
132
132
  assert.strictEqual(error_reason(post), 'cell_not_found');
133
133
  });
@@ -138,14 +138,14 @@ export const describe_cell_relations_cross_tests = (options) => {
138
138
  const t = fixture.fresh_transport();
139
139
  const owner_h = owner.create_session_headers();
140
140
  const editor_h = editor.create_session_headers();
141
- const cell = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { data: { kind: 'note' } }, owner_h), CellCreateOutput).cell;
141
+ const cell = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { kind: 'note', data: {} }, owner_h), CellCreateOutput).cell;
142
142
  expect_output(await cross_rpc_call(t, rpc_path, 'cell_grant_create', {
143
143
  cell_id: cell.id,
144
144
  level: 'editor',
145
145
  principal: { kind: 'actor', actor_id: editor.actor.id },
146
146
  }, owner_h), CellGrantCreateOutput);
147
147
  // Content edits pass (editor tier); a visibility write is manage-tier.
148
- expect_output(await cross_rpc_call(t, rpc_path, 'cell_update', { cell_id: cell.id, data: { kind: 'note', label: 'ok' } }, editor_h), CellUpdateOutput);
148
+ expect_output(await cross_rpc_call(t, rpc_path, 'cell_update', { cell_id: cell.id, data: { label: 'ok' } }, editor_h), CellUpdateOutput);
149
149
  const vis = await cross_rpc_call(t, rpc_path, 'cell_update', { cell_id: cell.id, visibility: 'public' }, editor_h);
150
150
  assert.ok(!vis.ok, 'editor flipped visibility');
151
151
  assert.strictEqual(error_reason(vis), 'cell_visibility_manage_only');
@@ -155,8 +155,8 @@ export const describe_cell_relations_cross_tests = (options) => {
155
155
  const owner = await fixture.create_account({ username: 'cell_field_owner' });
156
156
  const t = fixture.fresh_transport();
157
157
  const h = owner.create_session_headers();
158
- const source = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { data: { kind: 'note' } }, h), CellCreateOutput).cell;
159
- const target = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { data: { kind: 'note' } }, h), CellCreateOutput).cell;
158
+ const source = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { kind: 'note', data: {} }, h), CellCreateOutput).cell;
159
+ const target = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { kind: 'note', data: {} }, h), CellCreateOutput).cell;
160
160
  const set = expect_output(await cross_rpc_call(t, rpc_path, 'cell_field_set', { source_id: source.id, name: 'cover', target_id: target.id }, h), CellFieldSetOutput);
161
161
  assert.strictEqual(set.field.name, 'cover');
162
162
  assert.strictEqual(set.field.source_id, source.id);
@@ -175,7 +175,7 @@ export const describe_cell_relations_cross_tests = (options) => {
175
175
  const owner = await fixture.create_account({ username: 'cell_item_owner' });
176
176
  const t = fixture.fresh_transport();
177
177
  const h = owner.create_session_headers();
178
- const make = async () => expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { data: { kind: 'note' } }, h), CellCreateOutput).cell.id;
178
+ const make = async () => expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { kind: 'note', data: {} }, h), CellCreateOutput).cell.id;
179
179
  const parent = await make();
180
180
  const child_a = await make();
181
181
  const child_b = await make();
@@ -208,7 +208,7 @@ export const describe_cell_relations_cross_tests = (options) => {
208
208
  const owner = await fixture.create_account({ username: 'cell_clone_owner' });
209
209
  const t = fixture.fresh_transport();
210
210
  const h = owner.create_session_headers();
211
- const make = async (label) => expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { data: { kind: 'note', ...(label === undefined ? {} : { label }) } }, h), CellCreateOutput).cell.id;
211
+ const make = async (label) => expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { kind: 'note', data: { ...(label === undefined ? {} : { label }) } }, h), CellCreateOutput).cell.id;
212
212
  const source = await make('orig');
213
213
  const child = await make('child');
214
214
  const field_target = await make('target');
@@ -244,9 +244,9 @@ export const describe_cell_relations_cross_tests = (options) => {
244
244
  const t = fixture.fresh_transport();
245
245
  const owner_h = owner.create_session_headers();
246
246
  const viewer_h = viewer.create_session_headers();
247
- const cell = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { data: { kind: 'note' } }, owner_h), CellCreateOutput).cell;
247
+ const cell = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { kind: 'note', data: {} }, owner_h), CellCreateOutput).cell;
248
248
  // A mutation so the timeline has at least the create + update events.
249
- expect_output(await cross_rpc_call(t, rpc_path, 'cell_update', { cell_id: cell.id, data: { kind: 'note', label: 'v2' } }, owner_h), CellUpdateOutput);
249
+ expect_output(await cross_rpc_call(t, rpc_path, 'cell_update', { cell_id: cell.id, data: { label: 'v2' } }, owner_h), CellUpdateOutput);
250
250
  // Owner (manage-tier) reads the timeline.
251
251
  const timeline = expect_output(await cross_rpc_call(t, rpc_path, 'cell_audit_list', { cell_id: cell.id }, owner_h), CellAuditListOutput);
252
252
  assert.ok(timeline.events.length > 0, 'owner audit timeline empty');
@@ -273,14 +273,16 @@ export const describe_cell_relations_cross_tests = (options) => {
273
273
  // items and fields. The private child is owned by `owner`, so only
274
274
  // `owner` (and admin) can view it.
275
275
  const parent = await create_cell(t, rpc_path, owner_h, {
276
- data: { kind: 'collection' },
276
+ kind: 'collection',
277
+ data: {},
277
278
  visibility: 'public',
278
279
  });
279
280
  const pub_child = await create_cell(t, rpc_path, owner_h, {
280
- data: { kind: 'note' },
281
+ kind: 'note',
282
+ data: {},
281
283
  visibility: 'public',
282
284
  });
283
- const priv_child = await create_cell(t, rpc_path, owner_h, { data: { kind: 'note' } });
285
+ const priv_child = await create_cell(t, rpc_path, owner_h, { kind: 'note', data: {} });
284
286
  await wire_children(t, rpc_path, owner_h, parent, pub_child, priv_child);
285
287
  // Anonymous: only the public child surfaces in the bundle …
286
288
  const anon = fixture.fresh_transport({ origin: null });
@@ -301,9 +303,9 @@ export const describe_cell_relations_cross_tests = (options) => {
301
303
  const owner_h = owner.create_session_headers();
302
304
  const viewer_h = viewer.create_session_headers();
303
305
  // Private parent; one child the viewer will be granted, one they won't.
304
- const parent = await create_cell(t, rpc_path, owner_h, { data: { kind: 'collection' } });
305
- const shared_child = await create_cell(t, rpc_path, owner_h, { data: { kind: 'note' } });
306
- const priv_child = await create_cell(t, rpc_path, owner_h, { data: { kind: 'note' } });
306
+ const parent = await create_cell(t, rpc_path, owner_h, { kind: 'collection', data: {} });
307
+ const shared_child = await create_cell(t, rpc_path, owner_h, { kind: 'note', data: {} });
308
+ const priv_child = await create_cell(t, rpc_path, owner_h, { kind: 'note', data: {} });
307
309
  await wire_children(t, rpc_path, owner_h, parent, shared_child, priv_child);
308
310
  // Grant the viewer on parent AND shared_child — but not priv_child.
309
311
  for (const cell_id of [parent, shared_child]) {
@@ -325,14 +327,16 @@ export const describe_cell_relations_cross_tests = (options) => {
325
327
  // The keeper holds [keeper, admin], so its headers are the admin probe.
326
328
  const admin_h = fixture.create_session_headers();
327
329
  const parent = await create_cell(t, rpc_path, owner_h, {
328
- data: { kind: 'collection' },
330
+ kind: 'collection',
331
+ data: {},
329
332
  visibility: 'public',
330
333
  });
331
334
  const pub_child = await create_cell(t, rpc_path, owner_h, {
332
- data: { kind: 'note' },
335
+ kind: 'note',
336
+ data: {},
333
337
  visibility: 'public',
334
338
  });
335
- const priv_child = await create_cell(t, rpc_path, owner_h, { data: { kind: 'note' } });
339
+ const priv_child = await create_cell(t, rpc_path, owner_h, { kind: 'note', data: {} });
336
340
  await wire_children(t, rpc_path, owner_h, parent, pub_child, priv_child);
337
341
  // The cloner can read the public parent but not the private child.
338
342
  const clone = expect_output(await cross_rpc_call(t, rpc_path, 'cell_clone', { source_id: parent }, cloner_h), CellCloneOutput).cell;
@@ -362,14 +366,16 @@ export const describe_cell_relations_cross_tests = (options) => {
362
366
  const cloner_h = cloner.create_session_headers();
363
367
  const admin_h = fixture.create_session_headers();
364
368
  const parent = await create_cell(t, rpc_path, owner_h, {
365
- data: { kind: 'collection' },
369
+ kind: 'collection',
370
+ data: {},
366
371
  visibility: 'public',
367
372
  });
368
373
  const pub_child = await create_cell(t, rpc_path, owner_h, {
369
- data: { kind: 'note' },
374
+ kind: 'note',
375
+ data: {},
370
376
  visibility: 'public',
371
377
  });
372
- const priv_child = await create_cell(t, rpc_path, owner_h, { data: { kind: 'note' } });
378
+ const priv_child = await create_cell(t, rpc_path, owner_h, { kind: 'note', data: {} });
373
379
  await wire_children(t, rpc_path, owner_h, parent, pub_child, priv_child);
374
380
  const clone = expect_output(await cross_rpc_call(t, rpc_path, 'cell_clone', { source_id: parent, deep: true }, cloner_h), CellCloneOutput).cell;
375
381
  // Deep clone copies viewable children into fresh cells; the
@@ -1 +1 @@
1
- {"version":3,"file":"default_backend_configs.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/default_backend_configs.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,EAAC,sBAAsB,EAAE,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAC/E,OAAO,KAAK,EAAC,mBAAmB,EAAE,iBAAiB,EAAC,MAAM,mBAAmB,CAAC;AAC9E,OAAO,EAA2B,KAAK,gBAAgB,EAAC,MAAM,+BAA+B,CAAC;AAQ9F;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,iCAAiC,CAAC;AAE3E;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,mBAuBpC,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,EAAE,mBAmBtC,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,EAAE,iBAInC,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,EAAE,iBAIrC,CAAC;AAeH,MAAM,WAAW,iCAAiC;IACjD,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC9C,oDAAoD;IACpD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,6CAA6C;IAC7C,QAAQ,CAAC,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAC5C,wEAAwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAClC,sEAAsE;IACtE,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC/D;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;;OAMG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B,GAC1C,MAAM,iCAAiC,KACrC,aAoCF,CAAC;AAEF,MAAM,WAAW,mCAAmC;IACnD,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,+CAA+C;IAC/C,QAAQ,CAAC,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAC5C,wEAAwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAClC,sEAAsE;IACtE,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC/D;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,GAC5C,MAAM,mCAAmC,KACvC,aA4CF,CAAC"}
1
+ {"version":3,"file":"default_backend_configs.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/default_backend_configs.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,EAAC,sBAAsB,EAAE,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAC/E,OAAO,KAAK,EAAC,mBAAmB,EAAE,iBAAiB,EAAC,MAAM,mBAAmB,CAAC;AAC9E,OAAO,EAA2B,KAAK,gBAAgB,EAAC,MAAM,+BAA+B,CAAC;AAQ9F;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,iCAAiC,CAAC;AAE3E;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,mBA2BpC,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,EAAE,mBAsBtC,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,EAAE,iBAInC,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,EAAE,iBAIrC,CAAC;AAeH,MAAM,WAAW,iCAAiC;IACjD,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC9C,oDAAoD;IACpD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,6CAA6C;IAC7C,QAAQ,CAAC,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAC5C,wEAAwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAClC,sEAAsE;IACtE,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC/D;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;;OAMG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B,GAC1C,MAAM,iCAAiC,KACrC,aAoCF,CAAC;AAEF,MAAM,WAAW,mCAAmC;IACnD,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,+CAA+C;IAC/C,QAAQ,CAAC,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAC5C,wEAAwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAClC,sEAAsE;IACtE,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC/D;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,GAC5C,MAAM,mCAAmC,KACvC,aA4CF,CAAC"}
@@ -41,6 +41,10 @@ export const ts_default_capabilities = Object.freeze({
41
41
  // the HTTP RPC endpoint (the no-transport case). The TS spine config opts in;
42
42
  // a generic consumer enables it once it wires that mount.
43
43
  peer_request: false,
44
+ // Off by default — the `cell_gated_create` test policy is a fuz_app test
45
+ // fixture, mounted only on the spine binaries (`ts_spine_*`), not generic
46
+ // consumers.
47
+ cell_gated_create: false,
44
48
  });
45
49
  /**
46
50
  * Capabilities for the Rust family. The non-gating wiring facts live in
@@ -66,6 +70,9 @@ export const rust_default_capabilities = Object.freeze({
66
70
  // Server-initiated requests landed Rust-first canonical — the Rust spine
67
71
  // drives the `peer/ping` round-trip, so the family opts into the suite.
68
72
  peer_request: true,
73
+ // Off by default — the `testing_spine_stub` preset opts in (it mounts the
74
+ // `TestCellGatedCreateAuthorize` policy); a generic Rust consumer doesn't.
75
+ cell_gated_create: false,
69
76
  });
70
77
  /**
71
78
  * Shape notes for TS-family backends — wiring facts, not gating flags.
@@ -54,8 +54,12 @@
54
54
  "data",
55
55
  "deleted_at",
56
56
  "id",
57
+ "kind",
58
+ "moderation",
59
+ "parent_id",
57
60
  "path",
58
61
  "refs",
62
+ "root_id",
59
63
  "updated_at",
60
64
  "updated_by",
61
65
  "visibility"
@@ -80,8 +80,8 @@ export const describe_fact_serving_cross_tests = (options) => {
80
80
  * disambiguate or the authorization phase rejects with `actor_required`.
81
81
  */
82
82
  const create_cell_with_ref = async (transport, headers, hash, visibility, acting) => expect_output(await cross_rpc_call(transport, rpc_path, 'cell_create', acting === undefined
83
- ? { data: { kind: 'doc', cover: hash }, visibility }
84
- : { data: { kind: 'doc', cover: hash }, visibility, acting }, headers), CellCreateOutput).cell.id;
83
+ ? { kind: 'doc', data: { cover: hash }, visibility }
84
+ : { kind: 'doc', data: { cover: hash }, visibility, acting }, headers), CellCreateOutput).cell.id;
85
85
  const cell_fact_path = (cell_id, hash) => `/api/cells/${cell_id}/facts/${hash}`;
86
86
  describe('fact serving parity (cell-scoped per-reference reads)', () => {
87
87
  test_if(capabilities.fact_serving, 'cell-scoped admit: anon reads a fact through a public referencing cell', async () => {
@@ -1 +1 @@
1
- {"version":3,"file":"full_spine_mount.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/full_spine_mount.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,6BAA6B,CAAC;AAE3D,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,4BAA4B,CAAC;AACjE,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,8CAA8C,CAAC;AAKrF,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,oCAAoC,CAAC;AAQzE,2FAA2F;AAC3F,MAAM,WAAW,qBAAqB;IACrC;;;;;;OAMG;IACH,QAAQ,CAAC,kBAAkB,EAAE,gBAAgB,CAAC;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAC;CACzD;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,4BAA4B,GACxC,MAAM,OAAO,EACb,SAAS,qBAAqB,KAC5B,KAAK,CAAC,SAAS,CA0BjB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,GACpC,KAAK,gBAAgB,EACrB,SAAS,qBAAqB,KAC5B,KAAK,CAAC,eAAe,CAEvB,CAAC"}
1
+ {"version":3,"file":"full_spine_mount.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/full_spine_mount.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,6BAA6B,CAAC;AAE3D,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,4BAA4B,CAAC;AACjE,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,8CAA8C,CAAC;AAKrF,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,oCAAoC,CAAC;AASzE,2FAA2F;AAC3F,MAAM,WAAW,qBAAqB;IACrC;;;;;;OAMG;IACH,QAAQ,CAAC,kBAAkB,EAAE,gBAAgB,CAAC;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAC;CACzD;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,4BAA4B,GACxC,MAAM,OAAO,EACb,SAAS,qBAAqB,KAC5B,KAAK,CAAC,SAAS,CAoCjB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,GACpC,KAAK,gBAAgB,EACrB,SAAS,qBAAqB,KAC5B,KAAK,CAAC,eAAe,CAEvB,CAAC"}
@@ -5,6 +5,7 @@ import { create_all_cell_actions } from '../../auth/all_cell_actions.js';
5
5
  import { create_actor_lookup_actions } from '../../auth/actor_lookup_actions.js';
6
6
  import { create_actor_search_actions } from '../../auth/actor_search_actions.js';
7
7
  import { create_testing_action_manifest_action, create_testing_actions, } from './testing_reset_actions.js';
8
+ import { test_cell_gated_create_authorize } from './test_cell_gated_create_authorize.js';
8
9
  import { spine_roles, spine_session_options } from './default_spine_surface.js';
9
10
  import { SPINE_RPC_PATH } from './spine_surface_constants.js';
10
11
  /**
@@ -28,7 +29,14 @@ export const build_full_spine_rpc_actions = (deps, options) => {
28
29
  session_options: spine_session_options,
29
30
  daemon_token_state: options.daemon_token_state,
30
31
  }),
31
- ...create_all_cell_actions(deps, { roles: spine_roles }),
32
+ // Mount the directory-model `cell_gated_create` test policy (twin of the
33
+ // Rust stub's `TestCellGatedCreateAuthorize`) so the cross-backend
34
+ // authorizer-parity suite has a known gate: `kind: 'space'` roots are
35
+ // admin-only, contributions are gated by the root's `data.policy`, and
36
+ // plain parentless creates stay open (the other cell suites are
37
+ // unaffected). The handler hands the governing root's `data` to the
38
+ // (pure) authorizer.
39
+ ...create_all_cell_actions({ ...deps, authorize_create: test_cell_gated_create_authorize }, { roles: spine_roles }),
32
40
  ...create_actor_lookup_actions(deps),
33
41
  ...create_actor_search_actions(deps),
34
42
  // `peer/ping` is mounted on the HTTP RPC endpoint too (not just WS) so an
@@ -1 +1 @@
1
- {"version":3,"file":"rust_spine_stub_backend_config.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/rust_spine_stub_backend_config.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAuC9B,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AASvD,uGAAuG;AACvG,eAAO,MAAM,uBAAuB,oCAAoC,CAAC;AAEzE;;;;;GAKG;AACH,eAAO,MAAM,wCAAwC,6CAA6C,CAAC;AAenG,kGAAkG;AAClG,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAEjD,0FAA0F;AAC1F,eAAO,MAAM,oCAAoC,sDACG,CAAC;AAErD,MAAM,WAAW,6BAA6B;IAC7C,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;OAOG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CAClC;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,8BAA8B,GAC1C,UAAS,6BAAkC,KACzC,aAiDF,CAAC"}
1
+ {"version":3,"file":"rust_spine_stub_backend_config.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/rust_spine_stub_backend_config.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAuC9B,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AASvD,uGAAuG;AACvG,eAAO,MAAM,uBAAuB,oCAAoC,CAAC;AAEzE;;;;;GAKG;AACH,eAAO,MAAM,wCAAwC,6CAA6C,CAAC;AAkBnG,kGAAkG;AAClG,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAEjD,0FAA0F;AAC1F,eAAO,MAAM,oCAAoC,sDACG,CAAC;AAErD,MAAM,WAAW,6BAA6B;IAC7C,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;OAOG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CAClC;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,8BAA8B,GAC1C,UAAS,6BAAkC,KACzC,aAiDF,CAAC"}
@@ -49,14 +49,17 @@ export const RUST_SPINE_STUB_EXPECTED_SCHEMA_PATH_ENV = 'FUZ_RUST_SPINE_STUB_EXP
49
49
  /**
50
50
  * Capabilities for the Rust `testing_spine_stub` — `rust_default_capabilities`
51
51
  * plus `sse` (the stub serves `GET /api/admin/audit/stream` over the spine
52
- * `fuz_realtime::SseRegistry` + audit listener) and `ready` (it live-mounts
53
- * `/ready` over the env-supplied fixture path). Named (not inline) so every
54
- * spine preset is greppable, mirroring `ts_spine_capabilities`.
52
+ * `fuz_realtime::SseRegistry` + audit listener), `ready` (it live-mounts
53
+ * `/ready` over the env-supplied fixture path), and `cell_gated_create` (it
54
+ * mounts the `TestCellGatedCreateAuthorize` policy on its cell layer). Named
55
+ * (not inline) so every spine preset is greppable, mirroring
56
+ * `ts_spine_capabilities`.
55
57
  */
56
58
  const rust_spine_stub_capabilities = Object.freeze({
57
59
  ...rust_default_capabilities,
58
60
  sse: true,
59
61
  ready: true,
62
+ cell_gated_create: true,
60
63
  });
61
64
  /** Default listening port — slots beside zzz's 1175/1176; matches the binary's `DEFAULT_PORT`. */
62
65
  export const RUST_SPINE_STUB_DEFAULT_PORT = 1177;
@@ -0,0 +1,22 @@
1
+ import '../assert_dev_env.js';
2
+ import type { CellCreateAuthorize } from '../../auth/cell_actions.js';
3
+ /** The admin-only root kind the test policy gates (talk's `space` analog). */
4
+ export declare const SPACE_CELL_KIND = "space";
5
+ /**
6
+ * The app-role a space policy references as `min_role` (besides admin) — the
7
+ * `participant` role both reference spines register. Mirrors the Rust policy's
8
+ * `"participant"` literal.
9
+ */
10
+ export declare const PARTICIPATION_ROLE = "participant";
11
+ /** A space's per-kind contribution rule (the shape stamped into `space.data.policy`). */
12
+ export interface ContributionRule {
13
+ min_role?: string;
14
+ moderation_required?: boolean;
15
+ }
16
+ /**
17
+ * The directory-model test authorizer — a **pure** function of the input (it
18
+ * reads the governing root's policy off `input.root_data`, supplied by the
19
+ * handler). Byte-equivalent with the Rust `TestCellGatedCreateAuthorize`.
20
+ */
21
+ export declare const test_cell_gated_create_authorize: CellCreateAuthorize;
22
+ //# sourceMappingURL=test_cell_gated_create_authorize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test_cell_gated_create_authorize.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/test_cell_gated_create_authorize.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA+B9B,OAAO,KAAK,EACX,mBAAmB,EAGnB,MAAM,4BAA4B,CAAC;AAGpC,8EAA8E;AAC9E,eAAO,MAAM,eAAe,UAAU,CAAC;AAEvC;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,gBAAyB,CAAC;AAEzD,yFAAyF;AACzF,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAYD;;;;GAIG;AACH,eAAO,MAAM,gCAAgC,EAAE,mBAoB9C,CAAC"}
@@ -0,0 +1,75 @@
1
+ import '../assert_dev_env.js';
2
+ /**
3
+ * Test `CellCreateAuthorize` policy mounted on **both** reference spines — the
4
+ * TS spine binary's full mount (`full_spine_mount.ts`) and the Rust
5
+ * `testing_spine_stub` — so the cross-backend `cell_gated_create` suite proves
6
+ * the parent-aware cell-creation authorizer agrees TS↔Rust. The twin of the
7
+ * Rust `TestCellGatedCreateAuthorize`, **directory model**.
8
+ *
9
+ * Admin bypasses everything (`{allow: true, moderation_required: false}`). For
10
+ * a non-admin:
11
+ *
12
+ * - **Root creation** (`root_id` null): `kind: 'space'` is admin-only (denied);
13
+ * every other parentless kind stays open, so the plain-create `cell_crud` /
14
+ * `cell_relations` suites are unaffected.
15
+ * - **Contribution** (`root_id` set): the governing root's
16
+ * `data.policy[kind] = {min_role?, moderation_required?}` decides — a missing
17
+ * entry denies, a present `min_role` the actor lacks denies, and otherwise it
18
+ * admits with the entry's `moderation_required` folded into the verdict. The
19
+ * root's `data` arrives in `input.root_data` (the handler read it in-tx), so
20
+ * the predicate is **pure** — no DB read of its own (which also dodges the
21
+ * single-connection PGlite deadlock a separate handle would hit).
22
+ *
23
+ * **`$lib`-free by contract** — reached by the spawned TS spine binary under
24
+ * Gro's loader (no `$lib` alias). Keep every import relative.
25
+ *
26
+ * @module
27
+ */
28
+ import { has_role } from '../../auth/request_context.js';
29
+ import { ROLE_ADMIN } from '../../auth/role_schema.js';
30
+ import { SPINE_PARTICIPANT_ROLE } from './spine_surface_constants.js';
31
+ /** The admin-only root kind the test policy gates (talk's `space` analog). */
32
+ export const SPACE_CELL_KIND = 'space';
33
+ /**
34
+ * The app-role a space policy references as `min_role` (besides admin) — the
35
+ * `participant` role both reference spines register. Mirrors the Rust policy's
36
+ * `"participant"` literal.
37
+ */
38
+ export const PARTICIPATION_ROLE = SPINE_PARTICIPANT_ROLE;
39
+ /** Pull the governing root's `data.policy[kind]` rule, defensively (loose `data`). */
40
+ const get_contribution_rule = (data, kind) => {
41
+ if (data === null || typeof data !== 'object')
42
+ return undefined;
43
+ const policy = data.policy;
44
+ if (policy === null || typeof policy !== 'object')
45
+ return undefined;
46
+ const rule = policy[kind];
47
+ if (rule === null || typeof rule !== 'object')
48
+ return undefined;
49
+ return rule;
50
+ };
51
+ /**
52
+ * The directory-model test authorizer — a **pure** function of the input (it
53
+ * reads the governing root's policy off `input.root_data`, supplied by the
54
+ * handler). Byte-equivalent with the Rust `TestCellGatedCreateAuthorize`.
55
+ */
56
+ export const test_cell_gated_create_authorize = (auth, input) => {
57
+ // Admin bypass — admins create roots + any kind, live immediately.
58
+ if (has_role(auth, ROLE_ADMIN))
59
+ return { allow: true, moderation_required: false };
60
+ // Root creation (no governing root): `space` is admin-only; other parentless
61
+ // kinds stay open (the plain-create suites).
62
+ if (input.root_id === null) {
63
+ return input.kind === SPACE_CELL_KIND
64
+ ? { allow: false }
65
+ : { allow: true, moderation_required: false };
66
+ }
67
+ // Contribution: resolve the governing root's per-kind policy.
68
+ const rule = get_contribution_rule(input.root_data, input.kind ?? '');
69
+ if (!rule)
70
+ return { allow: false };
71
+ const admitted = rule.min_role === undefined || has_role(auth, rule.min_role);
72
+ return admitted
73
+ ? { allow: true, moderation_required: rule.moderation_required ?? false }
74
+ : { allow: false };
75
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"ts_spine_backend_config.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/ts_spine_backend_config.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AASvD,8GAA8G;AAC9G,eAAO,MAAM,gBAAgB,6BAA6B,CAAC;AA+B3D,6FAA6F;AAC7F,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,iDAAiD;AACjD,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,gDAAgD;AAChD,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,wDAAwD,CAAC;AAEzF,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,wDAAwD,CAAC;AAEzF,wDAAwD;AACxD,eAAO,MAAM,kBAAkB,uDAAuD,CAAC;AAEvF,MAAM,WAAW,2BAA2B;IAC3C,mFAAmF;IACnF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;;;;;OASG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC;CAC3C;AAWD;;;GAGG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAS,2BAAgC,KACvC,aAmBF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,GACvC,UAAS,2BAAgC,KACvC,aAmBF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAS,2BAAgC,KACvC,aAgCF,CAAC"}
1
+ {"version":3,"file":"ts_spine_backend_config.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/ts_spine_backend_config.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AASvD,8GAA8G;AAC9G,eAAO,MAAM,gBAAgB,6BAA6B,CAAC;AAkC3D,6FAA6F;AAC7F,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,iDAAiD;AACjD,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,gDAAgD;AAChD,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,wDAAwD,CAAC;AAEzF,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,wDAAwD,CAAC;AAEzF,wDAAwD;AACxD,eAAO,MAAM,kBAAkB,uDAAuD,CAAC;AAEvF,MAAM,WAAW,2BAA2B;IAC3C,mFAAmF;IACnF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;;;;;OASG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC;CAC3C;AAWD;;;GAGG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAS,2BAAgC,KACvC,aAmBF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,GACvC,UAAS,2BAAgC,KACvC,aAmBF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAS,2BAAgC,KACvC,aAgCF,CAAC"}
@@ -7,14 +7,17 @@ export const TS_SPINE_DIR_ENV = 'FUZ_TESTING_TS_SPINE_DIR';
7
7
  /**
8
8
  * Capabilities for the TS spine binary — `ts_default_capabilities` plus `sse`
9
9
  * (the binary wires `audit_log_sse`), `ready` (the binary live-mounts the
10
- * `/ready` deploy gate in `build_spine_app`), and `peer_request` (the backend
11
- * WS transport's `request_connection` path drives server→client `peer/ping`).
10
+ * `/ready` deploy gate in `build_spine_app`), `peer_request` (the backend
11
+ * WS transport's `request_connection` path drives server→client `peer/ping`),
12
+ * and `cell_gated_create` (the binary's `full_spine_mount` mounts the
13
+ * `test_cell_gated_create_authorize` policy).
12
14
  */
13
15
  const ts_spine_capabilities = Object.freeze({
14
16
  ...ts_default_capabilities,
15
17
  sse: true,
16
18
  ready: true,
17
19
  peer_request: true,
20
+ cell_gated_create: true,
18
21
  });
19
22
  /**
20
23
  * Capabilities for the **Bun** spine binary — `ts_spine_capabilities` with
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fuzdev/fuz_app",
3
- "version": "0.102.0",
3
+ "version": "0.104.0",
4
4
  "description": "fullstack app library",
5
5
  "glyph": "🗝",
6
6
  "logo": "logo.svg",