@fuzdev/fuz_app 0.108.0 → 0.110.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 (122) hide show
  1. package/README.md +4 -0
  2. package/dist/actions/CLAUDE.md +1 -1
  3. package/dist/auth/CLAUDE.md +7 -6
  4. package/dist/auth/account_action_specs.d.ts +18 -0
  5. package/dist/auth/account_action_specs.d.ts.map +1 -1
  6. package/dist/auth/account_action_specs.js +14 -1
  7. package/dist/auth/account_actions.d.ts.map +1 -1
  8. package/dist/auth/account_actions.js +6 -2
  9. package/dist/auth/audit_emitter.d.ts +9 -5
  10. package/dist/auth/audit_emitter.d.ts.map +1 -1
  11. package/dist/auth/audit_log_schema.d.ts +7 -1
  12. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  13. package/dist/auth/audit_log_schema.js +16 -1
  14. package/dist/auth/bootstrap_account.d.ts +7 -2
  15. package/dist/auth/bootstrap_account.d.ts.map +1 -1
  16. package/dist/auth/bootstrap_account.js +9 -4
  17. package/dist/auth/bootstrap_routes.d.ts +12 -4
  18. package/dist/auth/bootstrap_routes.d.ts.map +1 -1
  19. package/dist/auth/bootstrap_routes.js +26 -7
  20. package/dist/auth/daemon_token_middleware.d.ts +7 -69
  21. package/dist/auth/daemon_token_middleware.d.ts.map +1 -1
  22. package/dist/auth/daemon_token_middleware.js +8 -104
  23. package/dist/auth/deps.d.ts +9 -5
  24. package/dist/auth/deps.d.ts.map +1 -1
  25. package/dist/auth/middleware.js +1 -1
  26. package/dist/auth/migrations.d.ts +7 -0
  27. package/dist/auth/migrations.d.ts.map +1 -1
  28. package/dist/auth/migrations.js +25 -8
  29. package/dist/auth/request_context.d.ts +1 -3
  30. package/dist/auth/request_context.d.ts.map +1 -1
  31. package/dist/auth/request_context.js +2 -5
  32. package/dist/auth/session_cookie.d.ts +18 -36
  33. package/dist/auth/session_cookie.d.ts.map +1 -1
  34. package/dist/auth/session_cookie.js +16 -26
  35. package/dist/auth/session_middleware.d.ts +1 -1
  36. package/dist/auth/session_middleware.d.ts.map +1 -1
  37. package/dist/auth/session_middleware.js +1 -4
  38. package/dist/auth/session_queries.d.ts +14 -30
  39. package/dist/auth/session_queries.d.ts.map +1 -1
  40. package/dist/auth/session_queries.js +15 -45
  41. package/dist/auth/token_lifetime.d.ts +51 -0
  42. package/dist/auth/token_lifetime.d.ts.map +1 -0
  43. package/dist/auth/token_lifetime.js +54 -0
  44. package/dist/db/CLAUDE.md +9 -5
  45. package/dist/db/cell_audit_queries.d.ts +4 -2
  46. package/dist/db/cell_audit_queries.d.ts.map +1 -1
  47. package/dist/db/cell_audit_queries.js +4 -2
  48. package/dist/db/pg_error.d.ts +12 -0
  49. package/dist/db/pg_error.d.ts.map +1 -1
  50. package/dist/db/pg_error.js +15 -1
  51. package/dist/dev/setup.d.ts +5 -9
  52. package/dist/dev/setup.d.ts.map +1 -1
  53. package/dist/dev/setup.js +18 -21
  54. package/dist/http/CLAUDE.md +78 -18
  55. package/dist/http/db_routes.d.ts +96 -3
  56. package/dist/http/db_routes.d.ts.map +1 -1
  57. package/dist/http/db_routes.js +263 -46
  58. package/dist/http/error_schemas.d.ts +12 -1
  59. package/dist/http/error_schemas.d.ts.map +1 -1
  60. package/dist/http/error_schemas.js +12 -1
  61. package/dist/http/pending_effects.d.ts +5 -5
  62. package/dist/http/pending_effects.d.ts.map +1 -1
  63. package/dist/http/pending_effects.js +1 -1
  64. package/dist/runtime/deno.d.ts.map +1 -1
  65. package/dist/runtime/deno.js +27 -1
  66. package/dist/runtime/deps.d.ts +38 -4
  67. package/dist/runtime/deps.d.ts.map +1 -1
  68. package/dist/runtime/fs.d.ts +24 -5
  69. package/dist/runtime/fs.d.ts.map +1 -1
  70. package/dist/runtime/fs.js +38 -7
  71. package/dist/runtime/mock.d.ts +7 -0
  72. package/dist/runtime/mock.d.ts.map +1 -1
  73. package/dist/runtime/mock.js +41 -1
  74. package/dist/runtime/node.d.ts.map +1 -1
  75. package/dist/runtime/node.js +7 -1
  76. package/dist/runtime/secure_file.d.ts +71 -0
  77. package/dist/runtime/secure_file.d.ts.map +1 -0
  78. package/dist/runtime/secure_file.js +125 -0
  79. package/dist/server/app_backend.d.ts +6 -5
  80. package/dist/server/app_backend.d.ts.map +1 -1
  81. package/dist/server/app_backend.js +2 -3
  82. package/dist/testing/CLAUDE.md +36 -1
  83. package/dist/testing/admin_integration.d.ts.map +1 -1
  84. package/dist/testing/admin_integration.js +10 -2
  85. package/dist/testing/app_server.d.ts.map +1 -1
  86. package/dist/testing/app_server.js +13 -8
  87. package/dist/testing/audit_completeness.d.ts.map +1 -1
  88. package/dist/testing/audit_completeness.js +10 -2
  89. package/dist/testing/cross_backend/cell_grant_role.d.ts.map +1 -1
  90. package/dist/testing/cross_backend/cell_grant_role.js +34 -1
  91. package/dist/testing/cross_backend/cell_relations.d.ts.map +1 -1
  92. package/dist/testing/cross_backend/cell_relations.js +53 -1
  93. package/dist/testing/cross_backend/conformance_table.d.ts.map +1 -1
  94. package/dist/testing/cross_backend/conformance_table.js +2 -1
  95. package/dist/testing/cross_backend/query_shape.d.ts +10 -0
  96. package/dist/testing/cross_backend/query_shape.d.ts.map +1 -0
  97. package/dist/testing/cross_backend/query_shape.js +128 -0
  98. package/dist/testing/cross_backend/setup.js +1 -1
  99. package/dist/testing/cross_backend/token_lifetime.d.ts +15 -0
  100. package/dist/testing/cross_backend/token_lifetime.d.ts.map +1 -0
  101. package/dist/testing/cross_backend/token_lifetime.js +99 -0
  102. package/dist/testing/cross_backend/token_scope_surface.js +1 -1
  103. package/dist/testing/daemon_token_rotation.d.ts +83 -0
  104. package/dist/testing/daemon_token_rotation.d.ts.map +1 -0
  105. package/dist/testing/daemon_token_rotation.js +99 -0
  106. package/dist/testing/integration.d.ts.map +1 -1
  107. package/dist/testing/integration.js +15 -3
  108. package/dist/testing/stubs.d.ts.map +1 -1
  109. package/dist/testing/stubs.js +4 -4
  110. package/dist/ui/AccountSessions.svelte +0 -5
  111. package/dist/ui/AccountSessions.svelte.d.ts.map +1 -1
  112. package/dist/ui/AdminOverview.svelte +7 -4
  113. package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
  114. package/dist/ui/AdminSessions.svelte +0 -5
  115. package/dist/ui/AdminSessions.svelte.d.ts.map +1 -1
  116. package/dist/ui/CLAUDE.md +9 -4
  117. package/dist/ui/table_state.svelte.d.ts +18 -5
  118. package/dist/ui/table_state.svelte.d.ts.map +1 -1
  119. package/dist/ui/table_state.svelte.js +22 -7
  120. package/dist/ui/ui_format.d.ts.map +1 -1
  121. package/dist/ui/ui_format.js +4 -0
  122. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/app_server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/app_server.ts"],"names":[],"mappings":"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"}
@@ -157,8 +157,9 @@ export const bootstrap_test_keeper = async (options) => {
157
157
  /** Silent logger for tests — suppresses all output. */
158
158
  const test_log = new Logger('test', { level: 'off' });
159
159
  const default_test_fs_stubs = {
160
- stat: async () => null,
161
- read_text_file: async () => '',
160
+ read_secure_file: async (path) => {
161
+ throw new Error(`ENOENT: no such file or directory: ${path}`);
162
+ },
162
163
  delete_file: async () => { }
163
164
  };
164
165
  /**
@@ -375,14 +376,18 @@ export const create_test_app_for_bootstrap = async (options) => {
375
376
  if (options.db) {
376
377
  await options.db.query('UPDATE bootstrap_lock SET bootstrapped = false WHERE bootstrapped = true');
377
378
  }
378
- // Token-aware fs stubs: the bootstrap route's filesystem operations resolve
379
- // against the configured token_path; everything else returns no-op defaults.
379
+ // Token-aware fs stubs: the bootstrap route's secure read resolves against
380
+ // the configured token_path (both the boot-time availability probe and the
381
+ // request-time read go through it); a deleted or unknown path throws like
382
+ // the real runtimes' ENOENT.
380
383
  let token_file_deleted = false;
381
384
  const fs_stubs = {
382
- stat: async (path) => path === bootstrap.token_path && !token_file_deleted
383
- ? { is_file: true, is_directory: false }
384
- : null,
385
- read_text_file: async (path) => path === bootstrap.token_path && !token_file_deleted ? bootstrap_token : '',
385
+ read_secure_file: async (path) => {
386
+ if (path === bootstrap.token_path && !token_file_deleted) {
387
+ return new TextEncoder().encode(bootstrap_token);
388
+ }
389
+ throw new Error(`ENOENT: no such file or directory: ${path}`);
390
+ },
386
391
  delete_file: async (path) => {
387
392
  if (path === bootstrap.token_path)
388
393
  token_file_deleted = true;
@@ -1 +1 @@
1
- {"version":3,"file":"audit_completeness.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_completeness.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA4B7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAUhE,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AA2B1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,0BAA0B,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C;;;;;OAKG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;;;OAMG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC,yEAAyE;IACzE,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;OAGG;IACH,aAAa,EAAE,uBAAuB,CAAC;CACvC;AAgGD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,4BAA4B,KAAG,IAymBzF,CAAC"}
1
+ {"version":3,"file":"audit_completeness.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_completeness.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA4B7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAUhE,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AA2B1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,0BAA0B,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C;;;;;OAKG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;;;OAMG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC,yEAAyE;IACzE,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;OAGG;IACH,aAAa,EAAE,uBAAuB,CAAC;CACvC;AAgGD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,4BAA4B,KAAG,IAinBzF,CAAC"}
@@ -169,7 +169,11 @@ export const describe_audit_completeness_tests = (options) => {
169
169
  app: { request: fixture.transport },
170
170
  path: rpc_path,
171
171
  spec: account_token_create_action_spec,
172
- params: { name: 'audit-test', scope: { kind: 'full' } },
172
+ params: {
173
+ name: 'audit-test',
174
+ scope: { kind: 'full' },
175
+ lifetime: { kind: 'eternal' }
176
+ },
173
177
  headers: fixture.create_session_headers()
174
178
  });
175
179
  assert.ok(res.ok, `account_token_create failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
@@ -561,7 +565,11 @@ export const describe_audit_completeness_tests = (options) => {
561
565
  'role_grant_offer_decline',
562
566
  'role_grant_offer_retract',
563
567
  'role_grant_offer_expire',
564
- 'role_grant_offer_supersede'
568
+ 'role_grant_offer_supersede',
569
+ // `db_admin_row_delete` fires from the generic db-admin browser
570
+ // (`http/db_routes.ts`), which the standard test app doesn't mount —
571
+ // covered in `db_routes.db.test.ts`.
572
+ 'db_admin_row_delete'
565
573
  ]);
566
574
  test('all audit event types are covered or explicitly excluded', () => {
567
575
  const all_covered = new Set([...COVERED_EVENT_TYPES, ...EXCLUDED_EVENT_TYPES]);
@@ -1 +1 @@
1
- {"version":3,"file":"cell_grant_role.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/cell_grant_role.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA8C9B,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAG3D,+EAA+E;AAC/E,eAAO,MAAM,gBAAgB,gBAAyB,CAAC;AAEvD,oFAAoF;AACpF,eAAO,MAAM,yBAAyB,qBAAqB,CAAC;AAK5D,eAAO,MAAM,oCAAoC,GAAI,SAAS,wBAAwB,KAAG,IAuIxF,CAAC"}
1
+ {"version":3,"file":"cell_grant_role.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/cell_grant_role.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAmD9B,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAG3D,+EAA+E;AAC/E,eAAO,MAAM,gBAAgB,gBAAyB,CAAC;AAEvD,oFAAoF;AACpF,eAAO,MAAM,yBAAyB,qBAAqB,CAAC;AAK5D,eAAO,MAAM,oCAAoC,GAAI,SAAS,wBAAwB,KAAG,IAsOxF,CAAC"}
@@ -34,7 +34,7 @@ import '../assert_dev_env.js';
34
34
  * @module
35
35
  */
36
36
  import { describe, assert } from 'vitest';
37
- import { CellCreateOutput, CellGetOutput, CellUpdateOutput } from '../../auth/cell_action_specs.js';
37
+ import { CellCreateOutput, CellGetOutput, CellListOutput, CellUpdateOutput } from '../../auth/cell_action_specs.js';
38
38
  import { CellGrantCreateOutput, ERROR_CELL_GRANT_UNKNOWN_ROLE } from '../../auth/cell_grant_action_specs.js';
39
39
  import { test_if } from './capabilities.js';
40
40
  import { cross_rpc_call, error_reason, expect_output } from './cell_cross_helpers.js';
@@ -71,6 +71,39 @@ export const describe_cell_grant_role_cross_tests = (options) => {
71
71
  assert.ok(!stranger_view.ok, 'non-holder must not see the cell');
72
72
  assert.strictEqual(error_reason(stranger_view), 'cell_not_found');
73
73
  });
74
+ test_if(capabilities.cell_relations, "cell_list shared_with:'me' admits through the role branch of the grant predicate", async () => {
75
+ // The role-admit arm of the shared-with predicate (the
76
+ // `caller_role_grants` branch of the CTE in the Rust
77
+ // `query_cell_list` / the TS list query) — pinned nowhere else:
78
+ // the actor-shaped `shared_with:'me'` case in `cell_relations.ts`
79
+ // runs the CTE but its role branch never matches, and the
80
+ // storage-half copy in `fuz_cell/tests` is a textually separate
81
+ // EXISTS the CTE can silently drift from. An ungranted control
82
+ // cell keeps the semi-join non-vacuous (dropping it must change
83
+ // the result), and a non-holder proves the branch keys on the
84
+ // role, not on authentication.
85
+ const fixture = await setup_test();
86
+ const t = fixture.transport;
87
+ const owner = await fixture.create_account({ username: 'cell_role_list_owner' });
88
+ const owner_h = owner.create_session_headers();
89
+ const holder = fixture.extra_accounts[CELL_ROLE_HOLDER_USERNAME];
90
+ assert.ok(holder, `fixture must seed the ${CELL_ROLE_HOLDER_USERNAME} extra account`);
91
+ const stranger = await fixture.create_account({ username: 'cell_role_list_stranger' });
92
+ const granted = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { kind: 'note', data: { label: 'role-shared' } }, owner_h), CellCreateOutput);
93
+ // control: same owner, same visibility, no grant — must stay out
94
+ const ungranted = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { kind: 'note', data: { label: 'unshared control' } }, owner_h), CellCreateOutput);
95
+ expect_output(await cross_rpc_call(t, rpc_path, 'cell_grant_create', {
96
+ cell_id: granted.cell.id,
97
+ level: 'viewer',
98
+ principal: { kind: 'role', role: CELL_EDITOR_ROLE }
99
+ }, owner_h), CellGrantCreateOutput);
100
+ const holder_list = expect_output(await cross_rpc_call(t, rpc_path, 'cell_list', { shared_with: 'me' }, holder.create_session_headers()), CellListOutput);
101
+ const holder_ids = holder_list.cells.map((c) => c.id);
102
+ assert.ok(holder_ids.includes(granted.cell.id), 'the role-granted cell must reach a holder of the role via shared_with:me');
103
+ assert.ok(!holder_ids.includes(ungranted.cell.id), 'the ungranted control cell must not — the semi-join is load-bearing');
104
+ const stranger_list = expect_output(await cross_rpc_call(t, rpc_path, 'cell_list', { shared_with: 'me' }, stranger.create_session_headers()), CellListOutput);
105
+ assert.ok(!stranger_list.cells.some((c) => c.id === granted.cell.id), 'a non-holder must not be admitted through the role branch');
106
+ });
74
107
  test_if(capabilities.cell_relations, 'role-shaped grant for an unregistered role is rejected at create', async () => {
75
108
  const fixture = await setup_test();
76
109
  const t = fixture.transport;
@@ -1 +1 @@
1
- {"version":3,"file":"cell_relations.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/cell_relations.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAqF9B,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAsF3D,eAAO,MAAM,mCAAmC,GAAI,SAAS,wBAAwB,KAAG,IA2wBvF,CAAC"}
1
+ {"version":3,"file":"cell_relations.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/cell_relations.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA4F9B,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAsF3D,eAAO,MAAM,mCAAmC,GAAI,SAAS,wBAAwB,KAAG,IAi2BvF,CAAC"}
@@ -27,6 +27,12 @@ import '../assert_dev_env.js';
27
27
  * - **clone** — shallow copies item / field *edges* (shared `child_id` /
28
28
  * `target_id`); deep clones each viewable child into a fresh cell at the
29
29
  * same position. Both null `path` and stamp the caller as owner.
30
+ * - **`shared_with: 'me'`** — the grant-driven `cell_list` branch: a grantee
31
+ * lists exactly the cells that admit them via `cell_grant`, their own cells
32
+ * excluded, with the `cell_grants` enrichment keyed by cell id. Plus the two
33
+ * guards — null-auth is `cell_list_shared_with_requires_auth`, and any value
34
+ * other than `'me'` is `invalid_params` on both spines. The cross twin of
35
+ * the in-process `auth/cell_actions.list.db.test.ts` §"shared_with: me".
30
36
  * - **audit** — `cell_audit_list` is manage-tier: the owner reads the cell's
31
37
  * timeline; a viewer-grant holder who can `cell_get` the cell still gets the
32
38
  * IDOR 404 on the timeline (D14).
@@ -53,7 +59,7 @@ import '../assert_dev_env.js';
53
59
  */
54
60
  import { describe, assert } from 'vitest';
55
61
  import { fractional_index_between } from '@fuzdev/fuz_util/fractional_index.ts';
56
- import { CellCreateOutput, CellUpdateOutput, CellCloneOutput, CellGetOutput } from '../../auth/cell_action_specs.js';
62
+ import { CellCreateOutput, CellUpdateOutput, CellCloneOutput, CellGetOutput, CellListOutput } from '../../auth/cell_action_specs.js';
57
63
  import { AuditLogListOutput } from '../../auth/admin_action_specs.js';
58
64
  import { CellGrantCreateOutput, CellGrantListOutput, CellGrantRevokeOutput } from '../../auth/cell_grant_action_specs.js';
59
65
  import { CellFieldDeleteOutput, CellFieldListOutput, CellFieldSetOutput } from '../../auth/cell_field_action_specs.js';
@@ -131,6 +137,52 @@ export const describe_cell_relations_cross_tests = (options) => {
131
137
  assert.ok(!post.ok, 'revoked editor still edited');
132
138
  assert.strictEqual(error_reason(post), 'cell_not_found');
133
139
  });
140
+ test_if(capabilities.cell_relations, "cell_list shared_with: 'me' lists grant-admitted cells, excludes owned, enriches cell_grants", async () => {
141
+ const fixture = await setup_test();
142
+ const owner = await fixture.create_account({ username: 'cell_shared_owner' });
143
+ const viewer = await fixture.create_account({ username: 'cell_shared_viewer' });
144
+ const t = fixture.fresh_transport();
145
+ const owner_h = owner.create_session_headers();
146
+ const viewer_h = viewer.create_session_headers();
147
+ const shared = await create_cell(t, rpc_path, owner_h, { kind: 'note', data: {} });
148
+ // A cell the viewer owns — owner ≠ sharee, so it must NOT appear.
149
+ await create_cell(t, rpc_path, viewer_h, { kind: 'note', data: {} });
150
+ // The grant-admit control: owner-owned but NOT granted to the viewer.
151
+ // Without it the semi-join is untested — the shared-with SQL's two
152
+ // conjuncts (created_by IS DISTINCT FROM caller AND id IN grant-set)
153
+ // would select the same set, so dropping the grant clause changes
154
+ // nothing. This ungranted cell is exactly what dropping it would leak.
155
+ await create_cell(t, rpc_path, owner_h, { kind: 'note', data: {} });
156
+ const grant = expect_output(await cross_rpc_call(t, rpc_path, 'cell_grant_create', {
157
+ cell_id: shared,
158
+ level: 'viewer',
159
+ principal: { kind: 'actor', actor_id: viewer.actor.id }
160
+ }, owner_h), CellGrantCreateOutput).grant;
161
+ const listed = expect_output(await cross_rpc_call(t, rpc_path, 'cell_list', { shared_with: 'me' }, viewer_h), CellListOutput);
162
+ assert.deepStrictEqual(listed.cells.map((c) => c.id), [shared], 'shared_with list is not exactly the grant-admitted cell');
163
+ // The enrichment: the caller's own admit grants, keyed by cell id.
164
+ assert.ok(listed.cell_grants, 'shared_with list omitted the cell_grants enrichment');
165
+ const grants = listed.cell_grants[shared];
166
+ assert.ok(grants, 'cell_grants missing the shared cell key');
167
+ assert.strictEqual(grants.length, 1);
168
+ assert.strictEqual(grants[0].id, grant.id);
169
+ });
170
+ test_if(capabilities.cell_relations, 'null-auth cell_list shared_with → cell_list_shared_with_requires_auth', async () => {
171
+ const fixture = await setup_test();
172
+ const anon = fixture.fresh_transport({ origin: null });
173
+ const bad = await cross_rpc_call(anon, rpc_path, 'cell_list', { shared_with: 'me' }, {});
174
+ assert.ok(!bad.ok, 'anon shared_with filter accepted');
175
+ assert.strictEqual(error_reason(bad), 'cell_list_shared_with_requires_auth');
176
+ });
177
+ test_if(capabilities.cell_relations, "cell_list shared_with other than 'me' → invalid_params", async () => {
178
+ const fixture = await setup_test();
179
+ // TS rejects at the schema (`z.literal('me')`), Rust in the handler
180
+ // guard — both must land on -32602, so a caller can't widen the
181
+ // grant-driven branch to another actor on either spine.
182
+ const bad = await cross_rpc_call(fixture.fresh_transport(), rpc_path, 'cell_list', { shared_with: 'not-me' }, fixture.create_session_headers());
183
+ assert.ok(!bad.ok, "cell_list accepted a shared_with other than 'me'");
184
+ assert.strictEqual(bad.error?.code, -32602);
185
+ });
134
186
  test_if(capabilities.cell_relations, 'editor-grant holder cannot flip visibility → cell_visibility_manage_only', async () => {
135
187
  const fixture = await setup_test();
136
188
  const owner = await fixture.create_account({ username: 'cell_vis_owner' });
@@ -1 +1 @@
1
- {"version":3,"file":"conformance_table.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/conformance_table.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA8B9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAOnE,OAAO,EAMN,KAAK,uBAAuB,EAC5B,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAEN,KAAK,eAAe,EAEpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,YAAY,CAAC;AAGzD;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B;IAC1C,iEAAiE;IACjE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,gEAAgE;IAChE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,sDAAsD;AACtD,MAAM,WAAW,uBAAuB;IACvC,8CAA8C;IAC9C,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAC/C,+DAA+D;IAC/D,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,oEAAoE;IACpE,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC,0EAA0E;IAC1E,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,kEAAkE;IAClE,QAAQ,CAAC,aAAa,EAAE,uBAAuB,CAAC;IAChD,4EAA4E;IAC5E,QAAQ,CAAC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACjD,4EAA4E;IAC5E,QAAQ,CAAC,UAAU,CAAC,EAAE,0BAA0B,CAAC;IACjD,iEAAiE;IACjE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAoPD;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,EAAE,aAAa,CAAC,MAAM,CAIrD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,6BAA6B,GACzC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,OAAO,MAAM,KACX,IAQF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GACnC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,UAAU,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,EACvC,OAAO,MAAM,KACX,IAYF,CAAC;AAwKF;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,GAAI,SAAS,uBAAuB,KAAG,IAkEnF,CAAC"}
1
+ {"version":3,"file":"conformance_table.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/conformance_table.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA8B9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAOnE,OAAO,EAMN,KAAK,uBAAuB,EAC5B,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAEN,KAAK,eAAe,EAEpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,YAAY,CAAC;AAGzD;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B;IAC1C,iEAAiE;IACjE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,gEAAgE;IAChE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,sDAAsD;AACtD,MAAM,WAAW,uBAAuB;IACvC,8CAA8C;IAC9C,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAC/C,+DAA+D;IAC/D,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,oEAAoE;IACpE,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC,0EAA0E;IAC1E,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,kEAAkE;IAClE,QAAQ,CAAC,aAAa,EAAE,uBAAuB,CAAC;IAChD,4EAA4E;IAC5E,QAAQ,CAAC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACjD,4EAA4E;IAC5E,QAAQ,CAAC,UAAU,CAAC,EAAE,0BAA0B,CAAC;IACjD,iEAAiE;IACjE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAqPD;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,EAAE,aAAa,CAAC,MAAM,CAIrD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,6BAA6B,GACzC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,OAAO,MAAM,KACX,IAQF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GACnC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,UAAU,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,EACvC,OAAO,MAAM,KACX,IAYF,CAAC;AAwKF;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,GAAI,SAAS,uBAAuB,KAAG,IAkEnF,CAAC"}
@@ -71,7 +71,8 @@ const mint_scoped_token = async (fixture, resolved_rpc_endpoints) => {
71
71
  spec: account_token_create_action_spec,
72
72
  params: {
73
73
  name: 'conformance scoped token',
74
- scope: { kind: 'methods', methods: [SCOPED_TOKEN_ADMITTED_METHOD] }
74
+ scope: { kind: 'methods', methods: [SCOPED_TOKEN_ADMITTED_METHOD] },
75
+ lifetime: { kind: 'eternal' }
75
76
  },
76
77
  headers: fixture.create_session_headers()
77
78
  });
@@ -0,0 +1,10 @@
1
+ import '../assert_dev_env.js';
2
+ import type { RpcPathCrossSuiteOptions } from './setup.js';
3
+ /**
4
+ * Options for the query-shape parity suite. The standard RPC-dispatched
5
+ * cross-suite shape (`setup_test` / `capabilities` / `rpc_path`); aliases the
6
+ * shared `RpcPathCrossSuiteOptions` rather than minting a duplicate.
7
+ */
8
+ export type QueryShapeCrossTestOptions = RpcPathCrossSuiteOptions;
9
+ export declare const describe_query_shape_cross_tests: (options: QueryShapeCrossTestOptions) => void;
10
+ //# sourceMappingURL=query_shape.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query_shape.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/query_shape.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA0C9B,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAG3D;;;;GAIG;AACH,MAAM,MAAM,0BAA0B,GAAG,wBAAwB,CAAC;AAQlE,eAAO,MAAM,gCAAgC,GAAI,SAAS,0BAA0B,KAAG,IAwHtF,CAAC"}
@@ -0,0 +1,128 @@
1
+ import '../assert_dev_env.js';
2
+ /**
3
+ * Cross-backend query-string shape parity suite.
4
+ *
5
+ * The TS pipeline reads queries through Hono's `c.req.query()` — a duplicated
6
+ * key resolves to its **first** occurrence — and only routes that declare a
7
+ * `z.strictObject` query schema 400 unknown keys, in the pipeline's
8
+ * params → query → 401 → authz → 403 phase order. A derived-serde axum
9
+ * extractor diverges on every axis (a plain-text pre-handler "duplicate
10
+ * field" 400 observable anonymously, silently ignored unknown keys, query
11
+ * rejection ahead of the 401), so each spine surface that reads a query is
12
+ * pinned here on both backends:
13
+ *
14
+ * - **`GET /api/account/status`** — NO query schema: a duplicated `acting`
15
+ * reads first-wins (the persona resolves), unknown keys are ignored, and an
16
+ * anonymous caller with junk query hears 401, never a query 400.
17
+ * - **the JSON-RPC GET endpoint** — `method` / `id` / `params` read
18
+ * first-wins; a duplicated `method` must dispatch the first one on every
19
+ * backend (a last-wins reader dispatches a different method per backend on
20
+ * the same request).
21
+ * - **`GET /api/facts/:hash`** (gated on `capabilities.fact_serving`) —
22
+ * strict query schema: unknown keys and a malformed `acting` are 400
23
+ * `invalid_query_params` even for an anonymous caller (query precedes the
24
+ * 401 guard), while a duplicated `acting` takes the first occurrence.
25
+ *
26
+ * The per-surface in-process TS pins live beside each module's own tests
27
+ * (`account_status.test.ts`, `action_rpc.test.ts`,
28
+ * `serve_fact_route.db.test.ts`); this suite is the cross-impl gate, so it is
29
+ * cross-process only — like `login_security.ts` and `testing_backdoor.ts`.
30
+ *
31
+ * `$lib`-free by contract (relative specifiers only), like the sibling
32
+ * cross-backend suites.
33
+ *
34
+ * @module
35
+ */
36
+ import { describe, test, assert } from 'vitest';
37
+ import { account_verify_action_spec } from '../../auth/account_action_specs.js';
38
+ import { ERROR_INVALID_QUERY_PARAMS } from '../../http/error_schemas.js';
39
+ import { test_if } from './capabilities.js';
40
+ import { SPINE_RPC_PATH } from './spine_surface_constants.js';
41
+ /** The status route every spine serves (bundled into the account family). */
42
+ const STATUS_PATH = '/api/account/status';
43
+ /** A well-formed blake3 hash that references no stored fact. */
44
+ const MISSING_HASH = `blake3:${'a'.repeat(64)}`;
45
+ export const describe_query_shape_cross_tests = (options) => {
46
+ const { setup_test, capabilities } = options;
47
+ const rpc_path = options.rpc_path ?? SPINE_RPC_PATH;
48
+ describe('query-shape parity (duplicate keys, unknown keys, phase order)', () => {
49
+ test_if(capabilities.account_status, 'status: duplicated acting takes the first occurrence (persona resolves)', async () => {
50
+ const fixture = await setup_test();
51
+ // First occurrence is the keeper's real actor; the second is a
52
+ // mismatching-but-well-formed uuid. First-wins resolves the real
53
+ // persona; last-wins would fall through to `actor: null`, and a
54
+ // derived-serde extractor would 400 before the handler ran.
55
+ const nil = '00000000-0000-0000-0000-000000000000';
56
+ const res = await fixture.transport(`${STATUS_PATH}?acting=${fixture.actor.id}&acting=${nil}`, { method: 'GET', headers: fixture.create_session_headers() });
57
+ assert.strictEqual(res.status, 200);
58
+ const body = (await res.json());
59
+ assert.strictEqual(body.actor?.id, fixture.actor.id, 'first acting occurrence must win');
60
+ });
61
+ test_if(capabilities.account_status, 'status: anonymous with duplicated/unknown query keys → 401, never a query 400', async () => {
62
+ const fixture = await setup_test();
63
+ const anon = fixture.fresh_transport();
64
+ // The status route has no query schema — an anonymous prober with a
65
+ // junk query must hear the same 401 as a clean one (a pre-handler
66
+ // query reject here is a backend fingerprint).
67
+ const dup = await anon(`${STATUS_PATH}?acting=a&acting=b`, { method: 'GET' });
68
+ assert.strictEqual(dup.status, 401);
69
+ const unknown = await anon(`${STATUS_PATH}?foo=1&bar=2`, { method: 'GET' });
70
+ assert.strictEqual(unknown.status, 401);
71
+ });
72
+ test_if(capabilities.account_status, 'status: unknown query keys are ignored for an authed caller (no strict schema)', async () => {
73
+ const fixture = await setup_test();
74
+ const res = await fixture.transport(`${STATUS_PATH}?foo=1`, {
75
+ method: 'GET',
76
+ headers: fixture.create_session_headers()
77
+ });
78
+ assert.strictEqual(res.status, 200);
79
+ });
80
+ test('rpc GET: duplicated method and id keys take the first occurrence', async () => {
81
+ const fixture = await setup_test();
82
+ const method = account_verify_action_spec.method;
83
+ // First-wins dispatches `account_verify` with id 7; a last-wins
84
+ // reader dispatches the missing method (method_not_found) with id 8.
85
+ const res = await fixture.transport(`${rpc_path}?method=${method}&method=definitely_missing_method&id=7&id=8`, { method: 'GET', headers: fixture.create_session_headers() });
86
+ assert.strictEqual(res.status, 200);
87
+ const body = (await res.json());
88
+ assert.strictEqual(body.id, 7, 'first id occurrence must win');
89
+ assert.ok(body.result !== undefined, 'first method occurrence must dispatch');
90
+ });
91
+ test_if(capabilities.fact_serving, 'bare-hash facts: unknown query key → 400 invalid_query_params, even anonymous', async () => {
92
+ const fixture = await setup_test();
93
+ // Authed admin (the keeper): the strict query schema refuses the
94
+ // unknown key before the handler can 404 the missing fact.
95
+ const as_admin = await fixture.transport(`/api/facts/${MISSING_HASH}?foo=1`, {
96
+ method: 'GET',
97
+ headers: fixture.create_session_headers()
98
+ });
99
+ assert.strictEqual(as_admin.status, 400);
100
+ const body = (await as_admin.json());
101
+ assert.strictEqual(body.error, ERROR_INVALID_QUERY_PARAMS);
102
+ // Anonymous: query validation precedes the 401 guard (phase
103
+ // order), with the clean anonymous request as the 401 control.
104
+ const anon = fixture.fresh_transport();
105
+ const anon_bad = await anon(`/api/facts/${MISSING_HASH}?foo=1`, { method: 'GET' });
106
+ assert.strictEqual(anon_bad.status, 400);
107
+ const anon_clean = await anon(`/api/facts/${MISSING_HASH}`, { method: 'GET' });
108
+ assert.strictEqual(anon_clean.status, 401);
109
+ });
110
+ test_if(capabilities.fact_serving, 'bare-hash facts: duplicated acting takes the first occurrence; malformed acting → 400', async () => {
111
+ const fixture = await setup_test();
112
+ // First occurrence is the keeper's real actor; the second is
113
+ // garbage. First-wins passes the query phase and reaches the
114
+ // handler's masked 404 (the hash references nothing) — a
115
+ // derived-serde extractor would 400 the duplication instead.
116
+ const dup = await fixture.transport(`/api/facts/${MISSING_HASH}?acting=${fixture.actor.id}&acting=not-a-uuid`, { method: 'GET', headers: fixture.create_session_headers() });
117
+ assert.strictEqual(dup.status, 404, 'duplicated acting must resolve first-wins');
118
+ // A malformed single acting is refused at the query phase.
119
+ const bad = await fixture.transport(`/api/facts/${MISSING_HASH}?acting=not-a-uuid`, {
120
+ method: 'GET',
121
+ headers: fixture.create_session_headers()
122
+ });
123
+ assert.strictEqual(bad.status, 400);
124
+ const body = (await bad.json());
125
+ assert.strictEqual(body.error, ERROR_INVALID_QUERY_PARAMS);
126
+ });
127
+ });
128
+ };
@@ -375,7 +375,7 @@ const mint_account = async (handle, options) => {
375
375
  // point of the default-deny design. A minted-account fixture stands in for an
376
376
  // ordinary account credential, so it takes full account authority; the
377
377
  // narrowed credential is the conformance table's `scoped_token` principal.
378
- const token_result = await rpc_via_transport(transport, handle.config.rpc_path, 'account_token_create', { scope: { kind: 'full' } }, handle.config.name);
378
+ const token_result = await rpc_via_transport(transport, handle.config.rpc_path, 'account_token_create', { scope: { kind: 'full' }, lifetime: { kind: 'eternal' } }, handle.config.name);
379
379
  const token_parsed = TokenCreateResponseShape.safeParse(token_result);
380
380
  if (!token_parsed.success) {
381
381
  throw new Error(`account_token_create(${handle.config.name}) returned unexpected result: ${JSON.stringify(token_result)}`);
@@ -0,0 +1,15 @@
1
+ import '../assert_dev_env.js';
2
+ import type { SetupTest } from './setup.js';
3
+ /**
4
+ * Options for the token-lifetime parity suite. Ungated — the mint + list pair
5
+ * is on every spine's standard surface, so there is no capability flag (the
6
+ * sibling of `identity_parity.ts` rather than the cell suites).
7
+ */
8
+ export interface TokenLifetimeCrossTestOptions {
9
+ /** Per-test fixture-producing function (fresh keeper + db per call). */
10
+ readonly setup_test: SetupTest;
11
+ /** RPC endpoint path the methods are mounted on. Default `/api/rpc`. */
12
+ readonly rpc_path?: string;
13
+ }
14
+ export declare const describe_token_lifetime_cross_tests: (options: TokenLifetimeCrossTestOptions) => void;
15
+ //# sourceMappingURL=token_lifetime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token_lifetime.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/token_lifetime.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA+C9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C;;;;GAIG;AACH,MAAM,WAAW,6BAA6B;IAC7C,wEAAwE;IACxE,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AAaD,eAAO,MAAM,mCAAmC,GAC/C,SAAS,6BAA6B,KACpC,IAiHF,CAAC"}
@@ -0,0 +1,99 @@
1
+ import '../assert_dev_env.js';
2
+ /**
3
+ * Cross-backend parity suite for the **API token lifetime** axis of
4
+ * `account_token_create` — the round-trip that keeps a one-sided landing
5
+ * impossible.
6
+ *
7
+ * ## Why this suite exists
8
+ *
9
+ * The action-manifest parity gate captures `{method, side_effects, auth}` and
10
+ * **no param schemas**, while the TS spine validates params against a Zod
11
+ * `strictObject` and the Rust spine hand-parses with `params.get(...)`. So a
12
+ * spine that silently ignored the `lifetime` field would answer the same mint
13
+ * request with a **200 and an eternal token** — same method, same status,
14
+ * nothing red anywhere. The forcing function has to observe the *stored*
15
+ * state: mint with a TTL, then read `expires_at` back off the wire.
16
+ *
17
+ * ## What it pins
18
+ *
19
+ * - **A TTL mint stores a bounded expiry** — `account_token_create` with
20
+ * `{kind:'ttl', days}` returns a non-null `expires_at` (create output *and*
21
+ * `account_token_list`), landing within a generous window around
22
+ * `now + days`. A spine that dropped the field returns `null` and fails.
23
+ * - **An eternal mint stores NULL** — the control: `{kind:'eternal'}` yields
24
+ * `expires_at: null` on both reads.
25
+ * - **An omitted lifetime is refused** — `lifetime` is required; a mint
26
+ * without it must 400 `invalid_params` on both spines. This is the direct
27
+ * pin on the silently-ignoring-unknown-shapes hazard.
28
+ * - **Out-of-range days are refused** — `days: 0` 400s identically, so the
29
+ * two hand-rolled bounds checks can't drift.
30
+ *
31
+ * `$lib`-free by contract (relative specifiers only), like the sibling
32
+ * cross-backend suites. Runs both legs — in-process (`gro test`) and
33
+ * cross-process — through the shared `{setup_test}` protocol.
34
+ *
35
+ * @module
36
+ */
37
+ import { describe, test, assert } from 'vitest';
38
+ import { account_token_create_action_spec, account_token_list_action_spec, TokenCreateOutput, TokenListOutput } from '../../auth/account_action_specs.js';
39
+ import { cross_rpc_call, error_reason, expect_output } from './cell_cross_helpers.js';
40
+ import { SPINE_RPC_PATH } from './spine_surface_constants.js';
41
+ /** TTL used by the bounded-mint case. */
42
+ const TTL_DAYS = 30;
43
+ /**
44
+ * Window slack around `now + days` for the expiry assertion — generous (1h)
45
+ * because the TS spine computes the expiry from the handler clock while the
46
+ * Rust spine binds a `SystemTime` computed at dispatch; only gross divergence
47
+ * (wrong unit, wrong sign, silently-dropped field) should fail.
48
+ */
49
+ const EXPIRY_SLACK_MS = 60 * 60 * 1000;
50
+ export const describe_token_lifetime_cross_tests = (options) => {
51
+ const { setup_test } = options;
52
+ const rpc_path = options.rpc_path ?? SPINE_RPC_PATH;
53
+ describe('api token lifetime parity', () => {
54
+ test('a ttl mint stores a bounded expiry (create output + list round-trip)', async () => {
55
+ const fixture = await setup_test();
56
+ const before = Date.now();
57
+ const create_res = await cross_rpc_call(fixture.transport, rpc_path, account_token_create_action_spec.method, {
58
+ name: 'ttl-token',
59
+ scope: { kind: 'full' },
60
+ lifetime: { kind: 'ttl', days: TTL_DAYS }
61
+ }, fixture.create_session_headers());
62
+ const created = expect_output(create_res, TokenCreateOutput);
63
+ assert.ok(created.expires_at !== null, 'a ttl mint must return a non-null expires_at — null means the spine dropped the field');
64
+ const expires_ms = Date.parse(created.expires_at);
65
+ assert.ok(!Number.isNaN(expires_ms), `expires_at must parse as a date: ${created.expires_at}`);
66
+ const expected = before + TTL_DAYS * 86_400_000;
67
+ assert.ok(Math.abs(expires_ms - expected) < EXPIRY_SLACK_MS, `expires_at must land near now + ${TTL_DAYS}d (got ${created.expires_at})`);
68
+ // The read-back — the half a silently-ignoring spine can't fake.
69
+ const list_res = await cross_rpc_call(fixture.transport, rpc_path, account_token_list_action_spec.method, undefined, fixture.create_session_headers());
70
+ const listed = expect_output(list_res, TokenListOutput);
71
+ const row = listed.tokens.find((t) => t.id === created.id);
72
+ assert.ok(row, 'the minted token must appear in account_token_list');
73
+ assert.ok(row.expires_at !== null, 'account_token_list must report the stored expiry, not null');
74
+ });
75
+ test('an eternal mint stores NULL expiry', async () => {
76
+ const fixture = await setup_test();
77
+ const create_res = await cross_rpc_call(fixture.transport, rpc_path, account_token_create_action_spec.method, { name: 'eternal-token', scope: { kind: 'full' }, lifetime: { kind: 'eternal' } }, fixture.create_session_headers());
78
+ const created = expect_output(create_res, TokenCreateOutput);
79
+ assert.strictEqual(created.expires_at, null, 'an eternal mint must return expires_at: null');
80
+ const list_res = await cross_rpc_call(fixture.transport, rpc_path, account_token_list_action_spec.method, undefined, fixture.create_session_headers());
81
+ const listed = expect_output(list_res, TokenListOutput);
82
+ const row = listed.tokens.find((t) => t.id === created.id);
83
+ assert.ok(row, 'the minted token must appear in account_token_list');
84
+ assert.strictEqual(row.expires_at, null, 'the stored token must be eternal');
85
+ });
86
+ test('an omitted lifetime is refused with invalid_params on every spine', async () => {
87
+ const fixture = await setup_test();
88
+ const res = await cross_rpc_call(fixture.transport, rpc_path, account_token_create_action_spec.method, { name: 'no-lifetime', scope: { kind: 'full' } }, fixture.create_session_headers());
89
+ assert.ok(!res.ok, 'a mint without lifetime must be refused, never defaulted');
90
+ assert.strictEqual(res.error?.code, -32602, `expected invalid_params, got ${JSON.stringify(res.error)} (reason: ${String(error_reason(res))})`);
91
+ });
92
+ test('out-of-range ttl days are refused identically', async () => {
93
+ const fixture = await setup_test();
94
+ const res = await cross_rpc_call(fixture.transport, rpc_path, account_token_create_action_spec.method, { name: 'zero-days', scope: { kind: 'full' }, lifetime: { kind: 'ttl', days: 0 } }, fixture.create_session_headers());
95
+ assert.ok(!res.ok, 'days: 0 must be refused');
96
+ assert.strictEqual(res.error?.code, -32602, `expected invalid_params: ${JSON.stringify(res.error)}`);
97
+ });
98
+ });
99
+ };
@@ -87,7 +87,7 @@ const surface_denial_body = (surface) => ({
87
87
  * narrowed token on an account that lacks the gating role.
88
88
  */
89
89
  const mint_narrowed_token = async (transport, rpc_path, session_headers) => {
90
- const res = await cross_rpc_call(transport, rpc_path, account_token_create_action_spec.method, { scope: { kind: 'methods', methods: [ADMITTED_METHOD] } }, session_headers);
90
+ const res = await cross_rpc_call(transport, rpc_path, account_token_create_action_spec.method, { scope: { kind: 'methods', methods: [ADMITTED_METHOD] }, lifetime: { kind: 'eternal' } }, session_headers);
91
91
  assert.ok(res.ok, `minting a narrowed token failed: ${JSON.stringify(res.error)}`);
92
92
  const token = res.result?.token;
93
93
  assert.ok(typeof token === 'string' && token.length > 0, 'mint returned no token');