@fuzdev/fuz_app 0.140.0 → 0.142.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (311) hide show
  1. package/dist/actions/CLAUDE.md +72 -22
  2. package/dist/actions/action_rpc.d.ts +5 -1
  3. package/dist/actions/action_rpc.d.ts.map +1 -1
  4. package/dist/actions/action_rpc.js +9 -1
  5. package/dist/actions/connection_closer.d.ts +44 -4
  6. package/dist/actions/connection_closer.d.ts.map +1 -1
  7. package/dist/actions/connection_closer.js +33 -3
  8. package/dist/actions/perform_action.d.ts +10 -0
  9. package/dist/actions/perform_action.d.ts.map +1 -1
  10. package/dist/actions/perform_action.js +23 -9
  11. package/dist/actions/register_action_ws.d.ts +42 -6
  12. package/dist/actions/register_action_ws.d.ts.map +1 -1
  13. package/dist/actions/register_action_ws.js +87 -19
  14. package/dist/actions/register_ws_endpoint.d.ts +15 -6
  15. package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
  16. package/dist/actions/register_ws_endpoint.js +10 -4
  17. package/dist/actions/socket.svelte.d.ts +19 -4
  18. package/dist/actions/socket.svelte.d.ts.map +1 -1
  19. package/dist/actions/socket.svelte.js +31 -4
  20. package/dist/actions/transports.d.ts +30 -1
  21. package/dist/actions/transports.d.ts.map +1 -1
  22. package/dist/actions/transports.js +30 -1
  23. package/dist/actions/transports_ws_auth_guard.d.ts +13 -6
  24. package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
  25. package/dist/actions/transports_ws_auth_guard.js +24 -10
  26. package/dist/actions/transports_ws_backend.d.ts +51 -5
  27. package/dist/actions/transports_ws_backend.d.ts.map +1 -1
  28. package/dist/actions/transports_ws_backend.js +62 -9
  29. package/dist/actions/ws_endpoint_spec.d.ts +11 -10
  30. package/dist/actions/ws_endpoint_spec.d.ts.map +1 -1
  31. package/dist/auth/CLAUDE.md +44 -22
  32. package/dist/auth/account_actions.d.ts.map +1 -1
  33. package/dist/auth/account_actions.js +12 -1
  34. package/dist/auth/account_route_schema.d.ts +1 -0
  35. package/dist/auth/account_route_schema.d.ts.map +1 -1
  36. package/dist/auth/account_route_schema.js +12 -0
  37. package/dist/auth/account_routes.d.ts +7 -2
  38. package/dist/auth/account_routes.d.ts.map +1 -1
  39. package/dist/auth/account_routes.js +240 -187
  40. package/dist/auth/admin_action_specs.d.ts +6 -1
  41. package/dist/auth/admin_action_specs.d.ts.map +1 -1
  42. package/dist/auth/admin_action_specs.js +6 -1
  43. package/dist/auth/admin_actions.d.ts.map +1 -1
  44. package/dist/auth/admin_actions.js +14 -2
  45. package/dist/auth/api_token.d.ts +20 -0
  46. package/dist/auth/api_token.d.ts.map +1 -1
  47. package/dist/auth/api_token.js +21 -1
  48. package/dist/auth/api_token_queries.d.ts +22 -4
  49. package/dist/auth/api_token_queries.d.ts.map +1 -1
  50. package/dist/auth/api_token_queries.js +11 -6
  51. package/dist/auth/audit_log_routes.d.ts +10 -0
  52. package/dist/auth/audit_log_routes.d.ts.map +1 -1
  53. package/dist/auth/audit_log_routes.js +20 -0
  54. package/dist/auth/audit_log_schema.d.ts +30 -5
  55. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  56. package/dist/auth/audit_log_schema.js +30 -0
  57. package/dist/auth/bearer_auth.d.ts +4 -2
  58. package/dist/auth/bearer_auth.d.ts.map +1 -1
  59. package/dist/auth/bearer_auth.js +19 -8
  60. package/dist/auth/bootstrap_route_schema.d.ts +1 -0
  61. package/dist/auth/bootstrap_route_schema.d.ts.map +1 -1
  62. package/dist/auth/bootstrap_route_schema.js +1 -0
  63. package/dist/auth/bootstrap_routes.d.ts.map +1 -1
  64. package/dist/auth/bootstrap_routes.js +85 -76
  65. package/dist/auth/cell_action_specs.d.ts +7 -3
  66. package/dist/auth/cell_action_specs.d.ts.map +1 -1
  67. package/dist/auth/cell_action_specs.js +9 -5
  68. package/dist/auth/cell_actions.d.ts +3 -3
  69. package/dist/auth/cell_actions.d.ts.map +1 -1
  70. package/dist/auth/cell_actions.js +67 -38
  71. package/dist/auth/cell_authorize.d.ts +69 -8
  72. package/dist/auth/cell_authorize.d.ts.map +1 -1
  73. package/dist/auth/cell_authorize.js +89 -9
  74. package/dist/auth/cell_field_action_specs.d.ts +5 -4
  75. package/dist/auth/cell_field_action_specs.d.ts.map +1 -1
  76. package/dist/auth/cell_field_action_specs.js +7 -7
  77. package/dist/auth/cell_field_actions.d.ts +7 -5
  78. package/dist/auth/cell_field_actions.d.ts.map +1 -1
  79. package/dist/auth/cell_field_actions.js +24 -22
  80. package/dist/auth/cell_grant_action_specs.d.ts +6 -0
  81. package/dist/auth/cell_grant_action_specs.d.ts.map +1 -1
  82. package/dist/auth/cell_grant_action_specs.js +6 -0
  83. package/dist/auth/cell_grant_actions.d.ts +3 -1
  84. package/dist/auth/cell_grant_actions.d.ts.map +1 -1
  85. package/dist/auth/cell_grant_actions.js +16 -2
  86. package/dist/auth/cell_item_action_specs.d.ts +5 -4
  87. package/dist/auth/cell_item_action_specs.d.ts.map +1 -1
  88. package/dist/auth/cell_item_action_specs.js +7 -7
  89. package/dist/auth/cell_item_actions.d.ts +6 -5
  90. package/dist/auth/cell_item_actions.d.ts.map +1 -1
  91. package/dist/auth/cell_item_actions.js +23 -22
  92. package/dist/auth/cell_relation_list_limit.d.ts +20 -0
  93. package/dist/auth/cell_relation_list_limit.d.ts.map +1 -0
  94. package/dist/auth/cell_relation_list_limit.js +27 -0
  95. package/dist/auth/cell_relation_visibility.d.ts +4 -3
  96. package/dist/auth/cell_relation_visibility.d.ts.map +1 -1
  97. package/dist/auth/cell_relation_visibility.js +6 -5
  98. package/dist/auth/cell_steward.d.ts +45 -0
  99. package/dist/auth/cell_steward.d.ts.map +1 -0
  100. package/dist/auth/cell_steward.js +53 -0
  101. package/dist/auth/cleanup.d.ts +9 -9
  102. package/dist/auth/cleanup.js +9 -9
  103. package/dist/auth/credential_deadline.d.ts +95 -0
  104. package/dist/auth/credential_deadline.d.ts.map +1 -0
  105. package/dist/auth/credential_deadline.js +139 -0
  106. package/dist/auth/daemon_token_middleware.d.ts.map +1 -1
  107. package/dist/auth/daemon_token_middleware.js +4 -1
  108. package/dist/auth/middleware.d.ts +90 -12
  109. package/dist/auth/middleware.d.ts.map +1 -1
  110. package/dist/auth/middleware.js +115 -16
  111. package/dist/auth/request_context.d.ts +8 -2
  112. package/dist/auth/request_context.d.ts.map +1 -1
  113. package/dist/auth/request_context.js +20 -4
  114. package/dist/auth/resolved_auth.d.ts +13 -2
  115. package/dist/auth/resolved_auth.d.ts.map +1 -1
  116. package/dist/auth/resolved_auth.js +5 -4
  117. package/dist/auth/role_grant_offer_action_specs.d.ts +5 -0
  118. package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
  119. package/dist/auth/role_grant_offer_action_specs.js +6 -1
  120. package/dist/auth/role_grant_offer_actions.d.ts.map +1 -1
  121. package/dist/auth/role_grant_offer_actions.js +23 -16
  122. package/dist/auth/role_grant_offer_queries.d.ts +8 -6
  123. package/dist/auth/role_grant_offer_queries.d.ts.map +1 -1
  124. package/dist/auth/role_grant_offer_queries.js +16 -8
  125. package/dist/auth/session_cookie.d.ts +4 -0
  126. package/dist/auth/session_cookie.d.ts.map +1 -1
  127. package/dist/auth/session_cookie.js +3 -2
  128. package/dist/auth/session_middleware.d.ts +1 -1
  129. package/dist/auth/session_middleware.d.ts.map +1 -1
  130. package/dist/auth/session_middleware.js +4 -1
  131. package/dist/auth/session_queries.d.ts +19 -2
  132. package/dist/auth/session_queries.d.ts.map +1 -1
  133. package/dist/auth/session_queries.js +8 -2
  134. package/dist/auth/signup_route_schema.d.ts.map +1 -1
  135. package/dist/auth/signup_route_schema.js +1 -0
  136. package/dist/auth/signup_routes.d.ts +4 -3
  137. package/dist/auth/signup_routes.d.ts.map +1 -1
  138. package/dist/auth/signup_routes.js +157 -134
  139. package/dist/auth/token_scope.d.ts +7 -1
  140. package/dist/auth/token_scope.d.ts.map +1 -1
  141. package/dist/auth/token_scope.js +8 -2
  142. package/dist/db/CLAUDE.md +32 -19
  143. package/dist/db/cell_ddl.d.ts +1 -2
  144. package/dist/db/cell_ddl.d.ts.map +1 -1
  145. package/dist/db/cell_ddl.js +1 -2
  146. package/dist/db/cell_field_queries.d.ts +20 -18
  147. package/dist/db/cell_field_queries.d.ts.map +1 -1
  148. package/dist/db/cell_field_queries.js +28 -22
  149. package/dist/db/cell_item_queries.d.ts +20 -14
  150. package/dist/db/cell_item_queries.d.ts.map +1 -1
  151. package/dist/db/cell_item_queries.js +28 -18
  152. package/dist/db/cell_moderation.d.ts +67 -0
  153. package/dist/db/cell_moderation.d.ts.map +1 -0
  154. package/dist/db/cell_moderation.js +66 -0
  155. package/dist/db/cell_queries.d.ts +44 -14
  156. package/dist/db/cell_queries.d.ts.map +1 -1
  157. package/dist/db/cell_queries.js +68 -45
  158. package/dist/db/cell_view_sql.d.ts +102 -0
  159. package/dist/db/cell_view_sql.d.ts.map +1 -0
  160. package/dist/db/cell_view_sql.js +112 -0
  161. package/dist/db/db_pg.d.ts +1 -1
  162. package/dist/db/db_pg.js +1 -1
  163. package/dist/hono_context.d.ts +23 -0
  164. package/dist/hono_context.d.ts.map +1 -1
  165. package/dist/hono_context.js +19 -0
  166. package/dist/http/CLAUDE.md +41 -15
  167. package/dist/http/auth_shape.d.ts +5 -2
  168. package/dist/http/auth_shape.d.ts.map +1 -1
  169. package/dist/http/auth_shape.js +5 -2
  170. package/dist/http/ip_canonical.d.ts +30 -3
  171. package/dist/http/ip_canonical.d.ts.map +1 -1
  172. package/dist/http/ip_canonical.js +50 -3
  173. package/dist/http/middleware_spec.d.ts +16 -0
  174. package/dist/http/middleware_spec.d.ts.map +1 -1
  175. package/dist/http/origin.d.ts +113 -18
  176. package/dist/http/origin.d.ts.map +1 -1
  177. package/dist/http/origin.js +247 -127
  178. package/dist/http/route_spec.d.ts +21 -0
  179. package/dist/http/route_spec.d.ts.map +1 -1
  180. package/dist/http/schema_helpers.d.ts +22 -0
  181. package/dist/http/schema_helpers.d.ts.map +1 -1
  182. package/dist/http/schema_helpers.js +24 -0
  183. package/dist/http/surface.d.ts +6 -1
  184. package/dist/http/surface.d.ts.map +1 -1
  185. package/dist/http/surface.js +12 -5
  186. package/dist/primitive_schemas.d.ts +10 -1
  187. package/dist/primitive_schemas.d.ts.map +1 -1
  188. package/dist/primitive_schemas.js +11 -2
  189. package/dist/rate_limiter.d.ts +179 -12
  190. package/dist/rate_limiter.d.ts.map +1 -1
  191. package/dist/rate_limiter.js +352 -42
  192. package/dist/realtime/sse_auth_guard.d.ts +22 -6
  193. package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
  194. package/dist/realtime/sse_auth_guard.js +31 -12
  195. package/dist/realtime/subscriber_registry.d.ts +10 -1
  196. package/dist/realtime/subscriber_registry.d.ts.map +1 -1
  197. package/dist/realtime/subscriber_registry.js +10 -1
  198. package/dist/server/app_server.d.ts +20 -7
  199. package/dist/server/app_server.d.ts.map +1 -1
  200. package/dist/server/app_server.js +8 -3
  201. package/dist/server/env.d.ts +2 -1
  202. package/dist/server/env.d.ts.map +1 -1
  203. package/dist/server/env.js +7 -17
  204. package/dist/server/serve_fact_route.d.ts +8 -4
  205. package/dist/server/serve_fact_route.d.ts.map +1 -1
  206. package/dist/server/serve_fact_route.js +16 -6
  207. package/dist/testing/CLAUDE.md +164 -25
  208. package/dist/testing/adversarial_headers.d.ts.map +1 -1
  209. package/dist/testing/adversarial_headers.js +13 -6
  210. package/dist/testing/connection_closer_helpers.d.ts +6 -1
  211. package/dist/testing/connection_closer_helpers.d.ts.map +1 -1
  212. package/dist/testing/connection_closer_helpers.js +4 -0
  213. package/dist/testing/cross_backend/account_lifecycle.d.ts.map +1 -1
  214. package/dist/testing/cross_backend/account_lifecycle.js +37 -2
  215. package/dist/testing/cross_backend/body_size.d.ts.map +1 -1
  216. package/dist/testing/cross_backend/body_size.js +14 -65
  217. package/dist/testing/cross_backend/capabilities.d.ts +26 -2
  218. package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
  219. package/dist/testing/cross_backend/capabilities.js +9 -3
  220. package/dist/testing/cross_backend/cell_gated_create.d.ts +38 -0
  221. package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
  222. package/dist/testing/cross_backend/cell_gated_create.js +401 -5
  223. package/dist/testing/cross_backend/cell_relations.d.ts.map +1 -1
  224. package/dist/testing/cross_backend/cell_relations.js +145 -2
  225. package/dist/testing/cross_backend/conformance_case.d.ts +11 -3
  226. package/dist/testing/cross_backend/conformance_case.d.ts.map +1 -1
  227. package/dist/testing/cross_backend/conformance_case.js +6 -0
  228. package/dist/testing/cross_backend/conformance_table.d.ts.map +1 -1
  229. package/dist/testing/cross_backend/conformance_table.js +20 -19
  230. package/dist/testing/cross_backend/credential_deadline.d.ts +25 -0
  231. package/dist/testing/cross_backend/credential_deadline.d.ts.map +1 -0
  232. package/dist/testing/cross_backend/credential_deadline.js +185 -0
  233. package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
  234. package/dist/testing/cross_backend/default_backend_configs.js +13 -2
  235. package/dist/testing/cross_backend/expected_schema.json +0 -1
  236. package/dist/testing/cross_backend/full_spine_mount.d.ts +3 -0
  237. package/dist/testing/cross_backend/full_spine_mount.d.ts.map +1 -1
  238. package/dist/testing/cross_backend/full_spine_mount.js +7 -1
  239. package/dist/testing/cross_backend/login_security.d.ts +16 -0
  240. package/dist/testing/cross_backend/login_security.d.ts.map +1 -1
  241. package/dist/testing/cross_backend/login_security.js +154 -0
  242. package/dist/testing/cross_backend/narrowed_api_token.d.ts +23 -0
  243. package/dist/testing/cross_backend/narrowed_api_token.d.ts.map +1 -0
  244. package/dist/testing/cross_backend/narrowed_api_token.js +40 -0
  245. package/dist/testing/cross_backend/origin.d.ts.map +1 -1
  246. package/dist/testing/cross_backend/origin.js +6 -26
  247. package/dist/testing/cross_backend/origin_conformance.d.ts +152 -0
  248. package/dist/testing/cross_backend/origin_conformance.d.ts.map +1 -0
  249. package/dist/testing/cross_backend/origin_conformance.js +136 -0
  250. package/dist/testing/cross_backend/origin_conformance.json +1679 -0
  251. package/dist/testing/cross_backend/origin_scope.d.ts +16 -0
  252. package/dist/testing/cross_backend/origin_scope.d.ts.map +1 -0
  253. package/dist/testing/cross_backend/origin_scope.js +140 -0
  254. package/dist/testing/cross_backend/query_shape.d.ts.map +1 -1
  255. package/dist/testing/cross_backend/query_shape.js +24 -2
  256. package/dist/testing/cross_backend/role_grant_cross_helpers.d.ts +35 -0
  257. package/dist/testing/cross_backend/role_grant_cross_helpers.d.ts.map +1 -0
  258. package/dist/testing/cross_backend/role_grant_cross_helpers.js +64 -0
  259. package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts +8 -0
  260. package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts.map +1 -1
  261. package/dist/testing/cross_backend/role_grant_offer_notification_ws.js +44 -33
  262. package/dist/testing/cross_backend/role_grant_participation.d.ts.map +1 -1
  263. package/dist/testing/cross_backend/role_grant_participation.js +64 -0
  264. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
  265. package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +6 -4
  266. package/dist/testing/cross_backend/spine_surface_constants.d.ts +20 -1
  267. package/dist/testing/cross_backend/spine_surface_constants.d.ts.map +1 -1
  268. package/dist/testing/cross_backend/spine_surface_constants.js +23 -1
  269. package/dist/testing/cross_backend/testing_backdoor.d.ts.map +1 -1
  270. package/dist/testing/cross_backend/testing_backdoor.js +20 -7
  271. package/dist/testing/cross_backend/testing_reset_actions.d.ts +180 -5
  272. package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
  273. package/dist/testing/cross_backend/testing_reset_actions.js +154 -5
  274. package/dist/testing/cross_backend/token_scope_surface.d.ts.map +1 -1
  275. package/dist/testing/cross_backend/token_scope_surface.js +25 -21
  276. package/dist/testing/cross_backend/transport_composition.d.ts +60 -0
  277. package/dist/testing/cross_backend/transport_composition.d.ts.map +1 -0
  278. package/dist/testing/cross_backend/transport_composition.js +364 -0
  279. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
  280. package/dist/testing/cross_backend/ts_spine_backend_config.js +21 -6
  281. package/dist/testing/cross_backend/ws_action_rate_limit.d.ts.map +1 -1
  282. package/dist/testing/cross_backend/ws_action_rate_limit.js +21 -1
  283. package/dist/testing/cross_backend/ws_role_gated.d.ts +38 -0
  284. package/dist/testing/cross_backend/ws_role_gated.d.ts.map +1 -0
  285. package/dist/testing/cross_backend/ws_role_gated.js +142 -0
  286. package/dist/testing/entities.d.ts +23 -0
  287. package/dist/testing/entities.d.ts.map +1 -1
  288. package/dist/testing/entities.js +37 -0
  289. package/dist/testing/integration.d.ts.map +1 -1
  290. package/dist/testing/integration.js +3 -1
  291. package/dist/testing/middleware.d.ts +10 -0
  292. package/dist/testing/middleware.d.ts.map +1 -1
  293. package/dist/testing/middleware.js +31 -7
  294. package/dist/testing/schema_introspect.js +1 -1
  295. package/dist/testing/schema_parity.d.ts +4 -3
  296. package/dist/testing/schema_parity.d.ts.map +1 -1
  297. package/dist/testing/schema_parity.js +4 -3
  298. package/dist/testing/schema_ready_fixture.d.ts +2 -4
  299. package/dist/testing/schema_ready_fixture.d.ts.map +1 -1
  300. package/dist/testing/schema_ready_fixture.js +2 -4
  301. package/dist/testing/stubs.d.ts +16 -3
  302. package/dist/testing/stubs.d.ts.map +1 -1
  303. package/dist/testing/stubs.js +20 -7
  304. package/dist/testing/surface_invariants.js +2 -2
  305. package/dist/ui/auth_state.svelte.d.ts +10 -0
  306. package/dist/ui/auth_state.svelte.d.ts.map +1 -1
  307. package/dist/ui/auth_state.svelte.js +22 -4
  308. package/package.json +1 -1
  309. package/dist/db/cell_history_ddl.d.ts +0 -38
  310. package/dist/db/cell_history_ddl.d.ts.map +0 -1
  311. package/dist/db/cell_history_ddl.js +0 -61
@@ -16,11 +16,23 @@ import '../assert_dev_env.js';
16
16
  * - **grant lifecycle** — owner grants an actor-shaped editor; the grantee
17
17
  * gains edit (was 404 before the grant); `cell_grant_list` is manage-tier
18
18
  * (owner sees it, the editor gets the IDOR 404); revoke drops the edit path.
19
+ * - **no owner principal** — a grant naming the cell's owner is refused
20
+ * `cell_grant_principal_is_owner`, with the owner's actor id sent lowercase
21
+ * or uppercase.
19
22
  * - **`cell_visibility_manage_only`** — the editor-grant holder can edit
20
23
  * content but flipping `visibility` is 403 (the case the 5-verb CRUD cut
21
24
  * couldn't reach without a grant principal).
22
25
  * - **fields** — `cell_field_set` UPSERT, forward + reverse `cell_field_list`,
23
26
  * idempotent `cell_field_delete`.
27
+ * - **relation-list caps** — past the default page of inbound edges, a
28
+ * reverse `cell_item_list` / `cell_field_list` with no `limit` answers the
29
+ * default page, an explicit `limit` caps it in either direction, and the
30
+ * forward list defaults the same — the same counts on both spines. A page is
31
+ * visible edges: a page's worth of private ends older than one public end
32
+ * still leaves the public one on an anonymous page, in all four lists. An
33
+ * out-of-range or mistyped `limit` (`0`, `-1`, `1.5`, past the max, `null`,
34
+ * a string) is `invalid_params` in every direction, and the reverse lists
35
+ * read oldest edge first.
24
36
  * - **items** — `cell_item_insert` at fractional-index positions, forward
25
37
  * (lex-ordered) + reverse `cell_item_list`, `cell_item_move`, idempotent
26
38
  * `cell_item_delete`.
@@ -55,13 +67,15 @@ import '../assert_dev_env.js';
55
67
  * @module
56
68
  */
57
69
  import { describe, assert } from 'vitest';
58
- import { fractional_index_between } from '@fuzdev/fuz_util/fractional_index.ts';
70
+ import { fractional_index_between, fractional_indices_between } from '@fuzdev/fuz_util/fractional_index.ts';
59
71
  import { CellCreateOutput, CellUpdateOutput, CellCloneOutput, CellGetOutput, CellListOutput } from '../../auth/cell_action_specs.js';
60
72
  import { AuditLogListOutput } from '../../auth/admin_action_specs.js';
61
- import { CellGrantCreateOutput, CellGrantListOutput, CellGrantRevokeOutput } from '../../auth/cell_grant_action_specs.js';
73
+ import { ERROR_CELL_GRANT_PRINCIPAL_IS_OWNER, CellGrantCreateOutput, CellGrantListOutput, CellGrantRevokeOutput } from '../../auth/cell_grant_action_specs.js';
62
74
  import { CellFieldDeleteOutput, CellFieldListOutput, CellFieldSetOutput } from '../../auth/cell_field_action_specs.js';
63
75
  import { CellItemDeleteOutput, CellItemInsertOutput, CellItemListOutput, CellItemMoveOutput } from '../../auth/cell_item_action_specs.js';
64
76
  import { CellAuditListOutput } from '../../auth/cell_audit_action_specs.js';
77
+ import { JSONRPC_INVALID_PARAMS } from '../../http/jsonrpc.js';
78
+ import { CELL_RELATION_LIST_LIMIT_DEFAULT, CELL_RELATION_LIST_LIMIT_MAX } from '../../auth/cell_relation_list_limit.js';
65
79
  import { test_if } from './capabilities.js';
66
80
  import { cross_rpc_call, error_reason, expect_output } from './cell_cross_helpers.js';
67
81
  import { SPINE_RPC_PATH } from './spine_surface_constants.js';
@@ -134,6 +148,20 @@ export const describe_cell_relations_cross_tests = (options) => {
134
148
  assert.ok(!post.ok, 'revoked editor still edited');
135
149
  assert.strictEqual(error_reason(post), 'cell_not_found');
136
150
  });
151
+ test_if(capabilities.cell_relations, 'a grant naming the owner is refused cell_grant_principal_is_owner, whatever the id case', async () => {
152
+ const fixture = await setup_test();
153
+ const owner = await fixture.create_account({ username: 'cell_grant_self_owner' });
154
+ const t = fixture.fresh_transport();
155
+ const owner_h = owner.create_session_headers();
156
+ const cell = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { kind: 'note', data: {} }, owner_h), CellCreateOutput).cell;
157
+ // both spines canonicalize the id before comparing it with
158
+ // `created_by`, so the uppercased owner id is the owner too
159
+ for (const actor_id of [owner.actor.id, owner.actor.id.toUpperCase()]) {
160
+ const res = await cross_rpc_call(t, rpc_path, 'cell_grant_create', { cell_id: cell.id, level: 'viewer', principal: { kind: 'actor', actor_id } }, owner_h);
161
+ assert.ok(!res.ok, `a grant to the owner must be refused (${actor_id})`);
162
+ assert.strictEqual(error_reason(res), ERROR_CELL_GRANT_PRINCIPAL_IS_OWNER, actor_id);
163
+ }
164
+ });
137
165
  test_if(capabilities.cell_relations, "cell_list shared_with: 'me' lists grant-admitted cells, excludes owned, enriches cell_grants", async () => {
138
166
  const fixture = await setup_test();
139
167
  const owner = await fixture.create_account({ username: 'cell_shared_owner' });
@@ -219,6 +247,121 @@ export const describe_cell_relations_cross_tests = (options) => {
219
247
  const del2 = expect_output(await cross_rpc_call(t, rpc_path, 'cell_field_delete', { source_id: source.id, name: 'cover' }, h), CellFieldDeleteOutput);
220
248
  assert.strictEqual(del2.deleted, false);
221
249
  });
250
+ test_if(capabilities.cell_relations, 'relation lists default to a capped page and honor limit, forward and reverse', async () => {
251
+ const fixture = await setup_test();
252
+ const owner = await fixture.create_account({ username: 'cell_list_cap_owner' });
253
+ const t = fixture.fresh_transport();
254
+ const h = owner.create_session_headers();
255
+ // one past the default page, so an omitted limit shows the cap
256
+ const inbound = CELL_RELATION_LIST_LIMIT_DEFAULT + 1;
257
+ const make = async () => expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { kind: 'note', data: {} }, h), CellCreateOutput).cell.id;
258
+ const hub = await make();
259
+ const parent = await make();
260
+ const source = await make();
261
+ const positions = fractional_indices_between(null, null, inbound);
262
+ for (let i = 0; i < inbound; i++) {
263
+ expect_output(await cross_rpc_call(t, rpc_path, 'cell_item_insert', { parent_id: parent, child_id: hub, position: positions[i] }, h), CellItemInsertOutput);
264
+ expect_output(await cross_rpc_call(t, rpc_path, 'cell_field_set', { source_id: source, name: `link_${i}`, target_id: hub }, h), CellFieldSetOutput);
265
+ }
266
+ const items = async (params) => expect_output(await cross_rpc_call(t, rpc_path, 'cell_item_list', params, h), CellItemListOutput).items.length;
267
+ const fields = async (params) => expect_output(await cross_rpc_call(t, rpc_path, 'cell_field_list', params, h), CellFieldListOutput).fields.length;
268
+ for (const [label, count] of [
269
+ ['reverse items, no limit', await items({ child_id: hub })],
270
+ ['reverse fields, no limit', await fields({ target_id: hub })],
271
+ ['forward items, no limit', await items({ parent_id: parent })],
272
+ ['forward fields, no limit', await fields({ source_id: source })]
273
+ ]) {
274
+ assert.strictEqual(count, CELL_RELATION_LIST_LIMIT_DEFAULT, label);
275
+ }
276
+ assert.strictEqual(await items({ child_id: hub, limit: 3 }), 3, 'reverse items, limit 3');
277
+ assert.strictEqual(await fields({ target_id: hub, limit: 3 }), 3, 'reverse fields, limit 3');
278
+ assert.strictEqual(await items({ child_id: hub, limit: inbound }), inbound, 'reverse items, limit past the default');
279
+ assert.strictEqual(await fields({ target_id: hub, limit: inbound }), inbound, 'reverse fields, limit past the default');
280
+ });
281
+ test_if(capabilities.cell_relations, 'a relation page is visible edges: hidden ends never crowd out a visible one', async () => {
282
+ const fixture = await setup_test();
283
+ const owner = await fixture.create_account({ username: 'cell_list_pad_owner' });
284
+ const t = fixture.fresh_transport();
285
+ const h = owner.create_session_headers();
286
+ const anon = fixture.fresh_transport({ origin: null });
287
+ const make = async (visibility) => expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { kind: 'note', data: {}, visibility }, h), CellCreateOutput).cell.id;
288
+ const call = async (method, params) => cross_rpc_call(t, rpc_path, method, params, h);
289
+ // A full default page of private edges, older (and, forward, named
290
+ // earlier) than one public edge, in all four lists.
291
+ const target = await make('public');
292
+ const child = await make('public');
293
+ const source = await make('public');
294
+ const parent = await make('public');
295
+ const padding = CELL_RELATION_LIST_LIMIT_DEFAULT + 1;
296
+ const positions = fractional_indices_between(null, null, padding + 1);
297
+ for (let i = 0; i <= padding; i++) {
298
+ const end = await make(i < padding ? 'private' : 'public');
299
+ const name = `link_${String(i).padStart(3, '0')}`;
300
+ expect_output(await call('cell_field_set', { source_id: end, name: 'link', target_id: target }), CellFieldSetOutput);
301
+ expect_output(await call('cell_item_insert', { parent_id: end, child_id: child, position: 'a0' }), CellItemInsertOutput);
302
+ expect_output(await call('cell_field_set', { source_id: source, name, target_id: end }), CellFieldSetOutput);
303
+ expect_output(await call('cell_item_insert', {
304
+ parent_id: parent,
305
+ child_id: end,
306
+ position: positions[i]
307
+ }), CellItemInsertOutput);
308
+ }
309
+ const anon_fields = async (params) => expect_output(await cross_rpc_call(anon, rpc_path, 'cell_field_list', params, {}), CellFieldListOutput).fields;
310
+ const anon_items = async (params) => expect_output(await cross_rpc_call(anon, rpc_path, 'cell_item_list', params, {}), CellItemListOutput).items;
311
+ assert.strictEqual((await anon_fields({ target_id: target })).length, 1, 'reverse fields: the public source, past a page of private ones');
312
+ assert.strictEqual((await anon_items({ child_id: child })).length, 1, 'reverse items: the public parent, past a page of private ones');
313
+ assert.strictEqual((await anon_fields({ source_id: source })).length, 1, 'forward fields: the public target, past a page of private ones');
314
+ assert.strictEqual((await anon_items({ parent_id: parent })).length, 1, 'forward items: the public child, past a page of private ones');
315
+ // the owner views every end: a full default page
316
+ const owned = expect_output(await call('cell_field_list', { target_id: target }), CellFieldListOutput).fields;
317
+ assert.strictEqual(owned.length, CELL_RELATION_LIST_LIMIT_DEFAULT, "the owner's page");
318
+ });
319
+ test_if(capabilities.cell_relations, 'relation-list limit: out-of-range and mistyped values refused alike, forward and reverse', async () => {
320
+ const fixture = await setup_test();
321
+ const owner = await fixture.create_account({ username: 'cell_list_limit_owner' });
322
+ const t = fixture.fresh_transport();
323
+ const h = owner.create_session_headers();
324
+ const cell = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { kind: 'note', data: {} }, h), CellCreateOutput).cell.id;
325
+ const directions = [
326
+ ['cell_field_list', { source_id: cell }],
327
+ ['cell_field_list', { target_id: cell }],
328
+ ['cell_item_list', { parent_id: cell }],
329
+ ['cell_item_list', { child_id: cell }]
330
+ ];
331
+ for (const [method, base] of directions) {
332
+ for (const limit of [0, -1, 1.5, CELL_RELATION_LIST_LIMIT_MAX + 1, null, '5']) {
333
+ const res = await cross_rpc_call(t, rpc_path, method, { ...base, limit }, h);
334
+ const label = `${method} ${JSON.stringify(base)} limit ${JSON.stringify(limit)}`;
335
+ assert.ok(!res.ok, `${label} must be refused`);
336
+ assert.strictEqual(res.error?.code, JSONRPC_INVALID_PARAMS, label);
337
+ assert.strictEqual(error_reason(res), undefined, label);
338
+ }
339
+ for (const limit of [1, CELL_RELATION_LIST_LIMIT_MAX]) {
340
+ const res = await cross_rpc_call(t, rpc_path, method, { ...base, limit }, h);
341
+ assert.ok(res.ok, `${method} ${JSON.stringify(base)} limit ${limit}`);
342
+ }
343
+ }
344
+ });
345
+ test_if(capabilities.cell_relations, 'reverse relation lists read oldest edge first, so a page of one is the oldest', async () => {
346
+ const fixture = await setup_test();
347
+ const owner = await fixture.create_account({ username: 'cell_list_order_owner' });
348
+ const t = fixture.fresh_transport();
349
+ const h = owner.create_session_headers();
350
+ const make = async () => expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { kind: 'note', data: {} }, h), CellCreateOutput).cell.id;
351
+ const hub = await make();
352
+ // each edge its own call, so each has its own `created_at`, in order
353
+ const ends = [await make(), await make(), await make()];
354
+ for (const end of ends) {
355
+ expect_output(await cross_rpc_call(t, rpc_path, 'cell_item_insert', { parent_id: end, child_id: hub, position: 'a0' }, h), CellItemInsertOutput);
356
+ expect_output(await cross_rpc_call(t, rpc_path, 'cell_field_set', { source_id: end, name: 'link', target_id: hub }, h), CellFieldSetOutput);
357
+ }
358
+ const parents = async (limit) => expect_output(await cross_rpc_call(t, rpc_path, 'cell_item_list', { child_id: hub, limit }, h), CellItemListOutput).items.map((i) => i.parent_id);
359
+ const sources = async (limit) => expect_output(await cross_rpc_call(t, rpc_path, 'cell_field_list', { target_id: hub, limit }, h), CellFieldListOutput).fields.map((f) => f.source_id);
360
+ assert.deepStrictEqual(await parents(), ends, 'reverse items, oldest first');
361
+ assert.deepStrictEqual(await parents(1), [ends[0]], 'reverse items, a page of one');
362
+ assert.deepStrictEqual(await sources(), ends, 'reverse fields, oldest first');
363
+ assert.deepStrictEqual(await sources(1), [ends[0]], 'reverse fields, a page of one');
364
+ });
222
365
  test_if(capabilities.cell_relations, 'item insert → ordered forward + reverse list → move → idempotent delete', async () => {
223
366
  const fixture = await setup_test();
224
367
  const owner = await fixture.create_account({ username: 'cell_item_owner' });
@@ -66,6 +66,11 @@ import { z } from 'zod';
66
66
  * be replayed from a browser — wire-indistinguishable from sending no
67
67
  * credential (the `token` principal is the honored counterpart: it suppresses
68
68
  * `Origin`, so the same token authenticates).
69
+ * - `malformed_bearer` — the keeper's bearer api-token with its last character
70
+ * dropped, non-browser context, fresh jar. A value that cannot be a token
71
+ * fails each spine's shape precheck and soft-fails before any database work,
72
+ * so the request arrives anonymous — wire-indistinguishable from sending no
73
+ * credential.
69
74
  * - `scoped_token` — the keeper authenticated via a bearer api-token minted
70
75
  * with a **narrowed** `TokenScope` (`{kind: 'methods', methods:
71
76
  * [SCOPED_TOKEN_ADMITTED_METHOD]}`), non-browser context. Minted through the
@@ -92,12 +97,13 @@ import { z } from 'zod';
92
97
  export declare const ConformancePrincipal: z.ZodEnum<{
93
98
  keeper: "keeper";
94
99
  token: "token";
95
- anonymous: "anonymous";
96
100
  daemon: "daemon";
97
101
  invalid_daemon: "invalid_daemon";
98
102
  daemon_browser: "daemon_browser";
99
103
  bearer_browser: "bearer_browser";
104
+ malformed_bearer: "malformed_bearer";
100
105
  scoped_token: "scoped_token";
106
+ anonymous: "anonymous";
101
107
  fresh_non_admin: "fresh_non_admin";
102
108
  role_holder: "role_holder";
103
109
  wrong_role: "wrong_role";
@@ -122,12 +128,13 @@ export declare const ConformanceCaseRequest: z.ZodObject<{
122
128
  as: z.ZodEnum<{
123
129
  keeper: "keeper";
124
130
  token: "token";
125
- anonymous: "anonymous";
126
131
  daemon: "daemon";
127
132
  invalid_daemon: "invalid_daemon";
128
133
  daemon_browser: "daemon_browser";
129
134
  bearer_browser: "bearer_browser";
135
+ malformed_bearer: "malformed_bearer";
130
136
  scoped_token: "scoped_token";
137
+ anonymous: "anonymous";
131
138
  fresh_non_admin: "fresh_non_admin";
132
139
  role_holder: "role_holder";
133
140
  wrong_role: "wrong_role";
@@ -179,12 +186,13 @@ export declare const ConformanceCase: z.ZodObject<{
179
186
  as: z.ZodEnum<{
180
187
  keeper: "keeper";
181
188
  token: "token";
182
- anonymous: "anonymous";
183
189
  daemon: "daemon";
184
190
  invalid_daemon: "invalid_daemon";
185
191
  daemon_browser: "daemon_browser";
186
192
  bearer_browser: "bearer_browser";
193
+ malformed_bearer: "malformed_bearer";
187
194
  scoped_token: "scoped_token";
195
+ anonymous: "anonymous";
188
196
  fresh_non_admin: "fresh_non_admin";
189
197
  role_holder: "role_holder";
190
198
  wrong_role: "wrong_role";
@@ -1 +1 @@
1
- {"version":3,"file":"conformance_case.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/conformance_case.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;EAa/B,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE;;;;;;;;;GASG;AACH,eAAO,MAAM,4BAA4B,mBAAmB,CAAC;AAE7D,6CAA6C;AAC7C,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;kBAgBjC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAE5E,8DAA8D;AAC9D,eAAO,MAAM,0BAA0B;;;;;;;kBA+DrC,CAAC;AACH,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAEpF;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB;;;kBAK/B,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAS1B,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC"}
1
+ {"version":3,"file":"conformance_case.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/conformance_case.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqEG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;EAc/B,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE;;;;;;;;;GASG;AACH,eAAO,MAAM,4BAA4B,mBAAmB,CAAC;AAE7D,6CAA6C;AAC7C,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;kBAgBjC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAE5E,8DAA8D;AAC9D,eAAO,MAAM,0BAA0B;;;;;;;kBA+DrC,CAAC;AACH,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAEpF;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB;;;kBAK/B,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAS1B,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC"}
@@ -66,6 +66,11 @@ import { z } from 'zod';
66
66
  * be replayed from a browser — wire-indistinguishable from sending no
67
67
  * credential (the `token` principal is the honored counterpart: it suppresses
68
68
  * `Origin`, so the same token authenticates).
69
+ * - `malformed_bearer` — the keeper's bearer api-token with its last character
70
+ * dropped, non-browser context, fresh jar. A value that cannot be a token
71
+ * fails each spine's shape precheck and soft-fails before any database work,
72
+ * so the request arrives anonymous — wire-indistinguishable from sending no
73
+ * credential.
69
74
  * - `scoped_token` — the keeper authenticated via a bearer api-token minted
70
75
  * with a **narrowed** `TokenScope` (`{kind: 'methods', methods:
71
76
  * [SCOPED_TOKEN_ADMITTED_METHOD]}`), non-browser context. Minted through the
@@ -96,6 +101,7 @@ export const ConformancePrincipal = z.enum([
96
101
  'daemon_browser',
97
102
  'token',
98
103
  'bearer_browser',
104
+ 'malformed_bearer',
99
105
  'scoped_token',
100
106
  'anonymous',
101
107
  'fresh_non_admin',
@@ -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;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"}
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,EAKN,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;AAIzD;;;;;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;AAmPD;;;;;;;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"}
@@ -27,9 +27,10 @@ import '../assert_dev_env.js';
27
27
  import { assert, describe, test } from 'vitest';
28
28
  import { DAEMON_TOKEN_HEADER } from '../../auth/daemon_token.js';
29
29
  import { find_auth_route, rest_auth_route_suffixes } from '../integration_helpers.js';
30
- import { find_rpc_action, headers_to_record, rpc_call, rpc_call_for_spec, resolve_rpc_endpoints_for_setup } from '../rpc_helpers.js';
30
+ import { find_rpc_action, headers_to_record, rpc_call, resolve_rpc_endpoints_for_setup } from '../rpc_helpers.js';
31
31
  import { account_token_create_action_spec } from '../../auth/account_action_specs.js';
32
32
  import { SCOPED_TOKEN_ADMITTED_METHOD } from './conformance_case.js';
33
+ import { mint_narrowed_api_token } from './narrowed_api_token.js';
33
34
  import { xfail_until } from './xfail.js';
34
35
  /**
35
36
  * A deliberately malformed `X-Daemon-Token` value for the `invalid_daemon`
@@ -61,26 +62,13 @@ const mint_scoped_token = async (fixture, resolved_rpc_endpoints) => {
61
62
  `on the surface to mint its narrowed token, but the method is not registered on ` +
62
63
  `rpc_endpoints.`);
63
64
  }
64
- // Spec-bound rather than a loose `rpc_call`: `params` type-checks against
65
- // `TokenCreateInput` (so dropping `scope` is a compile error, not a runtime
66
- // 400 in every test at once), and `result.token` comes back typed and
67
- // output-validated — no cast, no hand-rolled presence check.
68
- const res = await rpc_call_for_spec({
65
+ return mint_narrowed_api_token({
69
66
  app: fixture.transport,
70
- path: found.path,
71
- spec: account_token_create_action_spec,
72
- params: {
73
- name: 'conformance scoped token',
74
- scope: { kind: 'methods', methods: [SCOPED_TOKEN_ADMITTED_METHOD] },
75
- lifetime: { kind: 'eternal' }
76
- },
77
- headers: fixture.create_session_headers()
67
+ rpc_path: found.path,
68
+ session_headers: fixture.create_session_headers(),
69
+ methods: [SCOPED_TOKEN_ADMITTED_METHOD],
70
+ name: 'conformance scoped token'
78
71
  });
79
- if (!res.ok) {
80
- throw new Error(`conformance: minting the 'scoped_token' principal failed with ${res.status}: ` +
81
- JSON.stringify(res.error));
82
- }
83
- return res.result.token;
84
72
  };
85
73
  /**
86
74
  * Map a `ConformancePrincipal` onto the transport + headers it
@@ -159,6 +147,19 @@ const resolve_principal = async (fixture, as, principals, resolved_rpc_endpoints
159
147
  transport: fixture.fresh_transport(),
160
148
  headers: fixture.create_bearer_headers()
161
149
  };
150
+ case 'malformed_bearer': {
151
+ // The keeper's valid token minus its last character — prefixed, one char
152
+ // short — over the same no-Origin, empty-jar transport as `token`, so the
153
+ // shape precheck (not the browser-context discard) is what refuses it.
154
+ const valid = fixture.create_bearer_headers().authorization;
155
+ if (!valid)
156
+ throw new Error(`conformance: principal 'malformed_bearer' found no bearer header`);
157
+ return {
158
+ transport: fixture.fresh_transport({ origin: null }),
159
+ headers: { authorization: valid.slice(0, -1) },
160
+ suppress_default_origin: true
161
+ };
162
+ }
162
163
  case 'scoped_token': {
163
164
  // A narrowed bearer, otherwise identical to `token` — empty jar + no
164
165
  // Origin, so the credential is honored rather than browser-discarded
@@ -0,0 +1,25 @@
1
+ import '../assert_dev_env.js';
2
+ import { type BackendCapabilities } from './capabilities.js';
3
+ import type { SetupTest } from './setup.js';
4
+ /** Configuration for `describe_credential_deadline_cross_tests`. */
5
+ export interface CredentialDeadlineCrossTestOptions {
6
+ /** Per-test fixture producer (`default_cross_process_setup(handle)`). */
7
+ readonly setup_test: SetupTest;
8
+ /** Backend capability flags — the WebSocket cases gate on `ws`, the stream case on `sse`. */
9
+ readonly capabilities: BackendCapabilities;
10
+ /** Base URL the backend is reachable at. */
11
+ readonly base_url: string;
12
+ /** The WebSocket endpoint path (e.g. `/api/ws`). */
13
+ readonly ws_path: string;
14
+ /** The audit-log stream path. Default `/api/admin/audit/stream`. */
15
+ readonly sse_path?: string;
16
+ /** JSON-RPC endpoint path. Default `SPINE_RPC_PATH`. */
17
+ readonly rpc_path?: string;
18
+ }
19
+ /**
20
+ * Register the credential-deadline suite: a WebSocket on a session or an API
21
+ * token closes with 4001 "Credential expired" at its credential's expiry, an
22
+ * audit stream on a session ends there, and the expiry seam never extends.
23
+ */
24
+ export declare const describe_credential_deadline_cross_tests: (options: CredentialDeadlineCrossTestOptions) => void;
25
+ //# sourceMappingURL=credential_deadline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential_deadline.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/credential_deadline.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAmD9B,OAAO,EAAE,KAAK,mBAAmB,EAAW,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAmC,MAAM,YAAY,CAAC;AAsB7E,oEAAoE;AACpE,MAAM,WAAW,kCAAkC;IAClD,yEAAyE;IACzE,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,6FAA6F;IAC7F,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,4CAA4C;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,oDAAoD;IACpD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;GAIG;AACH,eAAO,MAAM,wCAAwC,GACpD,SAAS,kCAAkC,KACzC,IAoKF,CAAC"}
@@ -0,0 +1,185 @@
1
+ import '../assert_dev_env.js';
2
+ /**
3
+ * Cross-process **credential deadline** suite — a long-lived connection
4
+ * closes when the credential it was opened with expires.
5
+ *
6
+ * A WebSocket or an audit stream is authorized once, at open, and closed on
7
+ * revocation by identity — but expiry writes nothing a revocation listener
8
+ * could hear. So each spine carries the credential's deadline onto the
9
+ * connection (`ResolvedAuth.credential_deadline`: the earlier of a session's
10
+ * cookie and row expiry, an API token's row expiry) and closes it there: a
11
+ * socket with `WS_CLOSE_SESSION_REVOKED` and
12
+ * `WS_CLOSE_CREDENTIAL_EXPIRED_REASON`, a stream by ending it as a revoked one
13
+ * ends.
14
+ *
15
+ * Each case pulls a fresh account's credential expiries in to
16
+ * `expiry_seconds` from now with the daemon-token
17
+ * `_testing_shorten_credential_expiry` seam, **before** opening the
18
+ * connection (the deadline is read when the credential resolves), then checks
19
+ * the connection is open short of the deadline and closed at it:
20
+ *
21
+ * 1. WebSocket × session — 4001, "Credential expired".
22
+ * 2. WebSocket × API token — the same.
23
+ * 3. Audit stream × session — the stream ends.
24
+ * 4. The seam only shortens: a second call asking for a later expiry moves
25
+ * nothing, and the socket still closes at the first deadline.
26
+ *
27
+ * The fourth reachable combination, API token × audit stream, is not here:
28
+ * the stock audit stream admits sessions only on both spines. Each spine
29
+ * covers it below the wire — the Rust stream route on a widened credential
30
+ * gate, the TS deadline wait shared by both transports.
31
+ *
32
+ * Gated on `capabilities.ws` (cases 1, 2, 4) and `capabilities.sse` (case 3);
33
+ * cross-process only — the seam is mounted on the spine test binaries.
34
+ *
35
+ * @module
36
+ */
37
+ import { assert, describe } from 'vitest';
38
+ import { heartbeat_action_spec } from '../../actions/heartbeat.js';
39
+ import { WS_CLOSE_CREDENTIAL_EXPIRED_REASON, WS_CLOSE_SESSION_REVOKED } from '../../actions/transports.js';
40
+ import { ROLE_ADMIN } from '../../auth/role_schema.js';
41
+ import { SSE_CONNECTED_COMMENT } from '../../realtime/sse_constants.js';
42
+ import { rpc_call_non_browser } from '../rpc_helpers.js';
43
+ import { create_sse_transport } from '../transports/sse_transport.js';
44
+ import { create_admitted_ws_transport } from '../transports/ws_transport.js';
45
+ import { test_if } from './capabilities.js';
46
+ import { SPINE_RPC_PATH } from './spine_surface_constants.js';
47
+ /** Default audit-log stream path. */
48
+ const DEFAULT_SSE_PATH = '/api/admin/audit/stream';
49
+ /** The shortened lifetime each case gives its credentials, in seconds. */
50
+ const EXPIRY_SECONDS = 2;
51
+ /**
52
+ * How long past the deadline a close may take before the case fails — wide
53
+ * for a loaded machine, and bounded so a missing close fails rather than
54
+ * hangs.
55
+ */
56
+ const CLOSE_GRACE_MS = 4000;
57
+ /**
58
+ * The deadline counts from the seam's call, and the connection opens after
59
+ * it; a close sooner than this after the call came before the deadline.
60
+ */
61
+ const EARLIEST_CLOSE_MS = EXPIRY_SECONDS * 1000 - 300;
62
+ /**
63
+ * Register the credential-deadline suite: a WebSocket on a session or an API
64
+ * token closes with 4001 "Credential expired" at its credential's expiry, an
65
+ * audit stream on a session ends there, and the expiry seam never extends.
66
+ */
67
+ export const describe_credential_deadline_cross_tests = (options) => {
68
+ const { setup_test, capabilities, base_url, ws_path, sse_path = DEFAULT_SSE_PATH, rpc_path = SPINE_RPC_PATH } = options;
69
+ /**
70
+ * Pull `account`'s credential expiries in to `seconds` from now through the
71
+ * daemon-token seam, returning when it was called.
72
+ */
73
+ const shorten = async (fixture, account, seconds) => {
74
+ const called_at = performance.now();
75
+ const result = await rpc_call_non_browser({
76
+ app: fixture.fresh_transport({ origin: null }),
77
+ path: rpc_path,
78
+ method: '_testing_shorten_credential_expiry',
79
+ params: { account_id: account.account.id, seconds },
80
+ headers: fixture.create_daemon_token_headers()
81
+ });
82
+ if (!result.ok) {
83
+ assert.fail(`_testing_shorten_credential_expiry failed: ${JSON.stringify(result)}`);
84
+ }
85
+ const counts = result.result;
86
+ return { called_at, ...counts };
87
+ };
88
+ const cookie_of = (account) => {
89
+ const { cookie } = account.create_session_headers();
90
+ assert.ok(cookie, 'expected a session cookie');
91
+ return cookie;
92
+ };
93
+ /**
94
+ * Assert `client` is open short of the deadline, then closes with 4001
95
+ * "Credential expired" at it.
96
+ */
97
+ const assert_closes_at_deadline = async (client, called_at) => {
98
+ // open and dispatching before the deadline
99
+ assert.deepStrictEqual(await client.request(1, heartbeat_action_spec.method, {}), {});
100
+ assert.ok(await client.wait_for_close(EXPIRY_SECONDS * 1000 + CLOSE_GRACE_MS), 'the socket did not close at its credential deadline');
101
+ const elapsed = performance.now() - called_at;
102
+ assert.ok(elapsed >= EARLIEST_CLOSE_MS, `closed ${Math.round(elapsed)}ms in, before the deadline`);
103
+ assert.strictEqual(client.close_code, WS_CLOSE_SESSION_REVOKED);
104
+ assert.strictEqual(client.close_reason, WS_CLOSE_CREDENTIAL_EXPIRED_REASON);
105
+ };
106
+ describe('credential deadline (cross-process)', () => {
107
+ test_if(capabilities.ws, 'a session socket closes with 4001 "Credential expired" at the deadline', async () => {
108
+ const fixture = await setup_test();
109
+ const account = await fixture.create_account();
110
+ const { called_at, sessions } = await shorten(fixture, account, EXPIRY_SECONDS);
111
+ assert.ok(sessions >= 1, 'the seam shortened the account session');
112
+ const client = await create_admitted_ws_transport({
113
+ base_url,
114
+ ws_path,
115
+ cookies: [cookie_of(account)],
116
+ origin: base_url
117
+ });
118
+ try {
119
+ await assert_closes_at_deadline(client, called_at);
120
+ }
121
+ finally {
122
+ await client.close();
123
+ }
124
+ });
125
+ test_if(capabilities.ws, 'an API token socket closes with 4001 "Credential expired" at the deadline', async () => {
126
+ const fixture = await setup_test();
127
+ const account = await fixture.create_account();
128
+ const { called_at, api_tokens } = await shorten(fixture, account, EXPIRY_SECONDS);
129
+ assert.ok(api_tokens >= 1, 'the seam shortened the account API token');
130
+ // a bearer outside a browser context: no Origin, no cookie
131
+ const client = await create_admitted_ws_transport({
132
+ base_url,
133
+ ws_path,
134
+ cookies: [],
135
+ origin: null,
136
+ headers: { Authorization: `Bearer ${account.api_token}` }
137
+ });
138
+ try {
139
+ await assert_closes_at_deadline(client, called_at);
140
+ }
141
+ finally {
142
+ await client.close();
143
+ }
144
+ });
145
+ test_if(capabilities.sse, 'a session audit stream ends at the deadline', async () => {
146
+ const fixture = await setup_test();
147
+ const account = await fixture.create_account({ roles: [ROLE_ADMIN] });
148
+ const { called_at } = await shorten(fixture, account, EXPIRY_SECONDS);
149
+ const sse = await create_sse_transport({
150
+ base_url,
151
+ sse_path,
152
+ cookies: [cookie_of(account)],
153
+ origin: base_url
154
+ });
155
+ try {
156
+ assert.strictEqual((await sse.read_frame()) + '\n\n', SSE_CONNECTED_COMMENT, 'the stream opened');
157
+ assert.ok(await sse.wait_for_close(EXPIRY_SECONDS * 1000 + CLOSE_GRACE_MS), 'the stream did not end at its credential deadline');
158
+ const elapsed = performance.now() - called_at;
159
+ assert.ok(elapsed >= EARLIEST_CLOSE_MS, `ended ${Math.round(elapsed)}ms in, before the deadline`);
160
+ }
161
+ finally {
162
+ await sse.close();
163
+ }
164
+ });
165
+ test_if(capabilities.ws, 'the expiry seam only shortens: a later expiry moves nothing', async () => {
166
+ const fixture = await setup_test();
167
+ const account = await fixture.create_account();
168
+ const { called_at } = await shorten(fixture, account, EXPIRY_SECONDS);
169
+ // asks for a minute; the credentials stay due at the first deadline
170
+ await shorten(fixture, account, 60);
171
+ const client = await create_admitted_ws_transport({
172
+ base_url,
173
+ ws_path,
174
+ cookies: [cookie_of(account)],
175
+ origin: base_url
176
+ });
177
+ try {
178
+ await assert_closes_at_deadline(client, called_at);
179
+ }
180
+ finally {
181
+ await client.close();
182
+ }
183
+ });
184
+ });
185
+ };
@@ -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":"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"}
1
+ {"version":3,"file":"default_backend_configs.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/default_backend_configs.ts"],"names":[],"mappings":"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"}
@@ -53,7 +53,13 @@ export const ts_default_capabilities = Object.freeze({
53
53
  // Off by default — the `cell_gated_create` test policy is a fuz_app test
54
54
  // fixture, mounted only on the spine binaries (`ts_spine_*`), not generic
55
55
  // consumers.
56
- cell_gated_create: false
56
+ cell_gated_create: false,
57
+ // Off by default — the second, role-gated WS endpoint is a spine-binary
58
+ // mount (`SPINE_ROLE_GATED_WS_PATH`); fuz_app's spine configs opt in.
59
+ ws_role_gated: false,
60
+ // Off by default: `@hono/node-ws` drops a refused upgrade's body, so a
61
+ // Node-served consumer sends none. A Deno- or Bun-served one may opt in.
62
+ ws_refusal_body: false
57
63
  });
58
64
  /**
59
65
  * Capabilities for the Rust family. The non-gating wiring facts live in
@@ -88,7 +94,12 @@ export const rust_default_capabilities = Object.freeze({
88
94
  ws_self_revocation_reply: false,
89
95
  // Off by default — the `testing_spine_stub` preset opts in (it mounts the
90
96
  // `TestCellGatedCreateAuthorize` policy); a generic Rust consumer doesn't.
91
- cell_gated_create: false
97
+ cell_gated_create: false,
98
+ // Off by default — the `testing_spine_stub` preset opts in (it mounts the
99
+ // role-gated WS endpoint at `SPINE_ROLE_GATED_WS_PATH`).
100
+ ws_role_gated: false,
101
+ // axum sends a refused upgrade's response as built, body included.
102
+ ws_refusal_body: true
92
103
  });
93
104
  /**
94
105
  * Shape notes for TS-family backends — wiring facts, not gating flags.
@@ -76,7 +76,6 @@
76
76
  "role",
77
77
  "scope_id"
78
78
  ],
79
- "cell_history": ["action_id", "cell_id", "created_at", "fact_hash", "id"],
80
79
  "cell_item": ["child_id", "created_at", "parent_id", "position"],
81
80
  "fact": ["bytes", "content_type", "created_at", "external_url", "hash", "size"],
82
81
  "fact_ref": ["source_hash", "target_hash"],
@@ -14,6 +14,9 @@ import '../assert_dev_env.js';
14
14
  * - the `_testing_*` daemon-token backdoors (`create_testing_actions`),
15
15
  * - the full cell verb set (CRUD + grant + field + item + audit),
16
16
  * - the opt-in `actor_lookup` / `actor_search` resolvers,
17
+ * - the `_testing_origin_check` probe the origin conformance table drives,
18
+ * - the `_testing_shorten_credential_expiry` seam the credential-deadline
19
+ * suite drives,
17
20
  * - the `_testing_action_manifest` backdoor, appended last (it dumps the live
18
21
  * method set for the cross-impl manifest-parity gate, so it must enumerate
19
22
  * every method above it).
@@ -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,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAG5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8CAA8C,CAAC;AAYvF,6EAA6E;AAC7E,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,6EAA6E;AAC7E,MAAM,WAAW,cAAc;IAC9B,gFAAgF;IAChF,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACnC;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,OAAO,EACb,SAAS,qBAAqB,KAC5B,cAUF,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAG5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8CAA8C,CAAC;AAcvF,6EAA6E;AAC7E,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,CA0CjB,CAAC;AAEF,6EAA6E;AAC7E,MAAM,WAAW,cAAc;IAC9B,gFAAgF;IAChF,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACnC;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,OAAO,EACb,SAAS,qBAAqB,KAC5B,cAUF,CAAC"}