@fuzdev/fuz_app 0.111.0 → 0.113.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 (212) hide show
  1. package/dist/actions/CLAUDE.md +15 -11
  2. package/dist/actions/action_rpc.d.ts +1 -1
  3. package/dist/actions/action_rpc.d.ts.map +1 -1
  4. package/dist/actions/action_rpc.js +4 -6
  5. package/dist/actions/peer_ping.d.ts +8 -0
  6. package/dist/actions/peer_ping.d.ts.map +1 -1
  7. package/dist/actions/peer_ping.js +8 -0
  8. package/dist/actions/peer_request.d.ts +1 -1
  9. package/dist/actions/peer_request.js +1 -1
  10. package/dist/actions/perform_action.d.ts +16 -9
  11. package/dist/actions/perform_action.d.ts.map +1 -1
  12. package/dist/actions/perform_action.js +30 -21
  13. package/dist/actions/register_action_ws.d.ts +1 -1
  14. package/dist/actions/register_action_ws.d.ts.map +1 -1
  15. package/dist/actions/register_action_ws.js +88 -21
  16. package/dist/auth/CLAUDE.md +43 -8
  17. package/dist/auth/account_actions.d.ts.map +1 -1
  18. package/dist/auth/account_actions.js +10 -1
  19. package/dist/auth/account_queries.d.ts.map +1 -1
  20. package/dist/auth/account_queries.js +36 -16
  21. package/dist/auth/account_schema.d.ts +20 -0
  22. package/dist/auth/account_schema.d.ts.map +1 -1
  23. package/dist/auth/account_schema.js +22 -1
  24. package/dist/auth/actor_search_actions.d.ts +3 -3
  25. package/dist/auth/actor_search_actions.d.ts.map +1 -1
  26. package/dist/auth/actor_search_actions.js +10 -6
  27. package/dist/auth/api_token_queries.d.ts.map +1 -1
  28. package/dist/auth/api_token_queries.js +11 -5
  29. package/dist/auth/app_settings_queries.d.ts.map +1 -1
  30. package/dist/auth/app_settings_queries.js +6 -4
  31. package/dist/auth/audit_log_queries.d.ts +6 -0
  32. package/dist/auth/audit_log_queries.d.ts.map +1 -1
  33. package/dist/auth/audit_log_queries.js +11 -5
  34. package/dist/auth/audit_log_route_schema.d.ts.map +1 -1
  35. package/dist/auth/audit_log_route_schema.js +12 -1
  36. package/dist/auth/cell_actions.d.ts.map +1 -1
  37. package/dist/auth/cell_actions.js +3 -8
  38. package/dist/auth/cell_audit_actions.d.ts.map +1 -1
  39. package/dist/auth/cell_audit_actions.js +1 -1
  40. package/dist/auth/cell_field_actions.js +1 -1
  41. package/dist/auth/cell_grant_actions.js +1 -1
  42. package/dist/auth/cell_item_actions.js +1 -1
  43. package/dist/auth/invite_queries.d.ts.map +1 -1
  44. package/dist/auth/invite_queries.js +10 -8
  45. package/dist/auth/role_grant_offer_action_specs.d.ts +2 -1
  46. package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
  47. package/dist/auth/role_grant_offer_action_specs.js +2 -1
  48. package/dist/auth/role_grant_offer_ddl.d.ts +11 -0
  49. package/dist/auth/role_grant_offer_ddl.d.ts.map +1 -1
  50. package/dist/auth/role_grant_offer_ddl.js +20 -2
  51. package/dist/auth/role_grant_offer_queries.d.ts.map +1 -1
  52. package/dist/auth/role_grant_offer_queries.js +29 -15
  53. package/dist/auth/role_grant_queries.d.ts.map +1 -1
  54. package/dist/auth/role_grant_queries.js +21 -7
  55. package/dist/auth/session_queries.d.ts.map +1 -1
  56. package/dist/auth/session_queries.js +9 -4
  57. package/dist/db/CLAUDE.md +21 -2
  58. package/dist/db/cell_audit_queries.js +2 -2
  59. package/dist/db/cell_field_queries.d.ts +1 -1
  60. package/dist/db/cell_field_queries.d.ts.map +1 -1
  61. package/dist/db/cell_field_queries.js +8 -6
  62. package/dist/db/cell_grant_queries.d.ts +1 -1
  63. package/dist/db/cell_grant_queries.d.ts.map +1 -1
  64. package/dist/db/cell_grant_queries.js +12 -10
  65. package/dist/db/cell_item_queries.d.ts +1 -1
  66. package/dist/db/cell_item_queries.d.ts.map +1 -1
  67. package/dist/db/cell_item_queries.js +9 -7
  68. package/dist/db/cell_queries.d.ts +3 -3
  69. package/dist/db/cell_queries.d.ts.map +1 -1
  70. package/dist/db/cell_queries.js +6 -4
  71. package/dist/db/fact_disk_storage.js +1 -1
  72. package/dist/db/fact_queries.d.ts.map +1 -1
  73. package/dist/db/fact_queries.js +9 -3
  74. package/dist/db/sql_columns.d.ts +80 -3
  75. package/dist/db/sql_columns.d.ts.map +1 -1
  76. package/dist/db/sql_columns.js +91 -4
  77. package/dist/http/CLAUDE.md +6 -4
  78. package/dist/http/auth_shape.d.ts +19 -6
  79. package/dist/http/auth_shape.d.ts.map +1 -1
  80. package/dist/http/auth_shape.js +19 -6
  81. package/dist/http/jsonrpc_helpers.d.ts +13 -0
  82. package/dist/http/jsonrpc_helpers.d.ts.map +1 -1
  83. package/dist/http/jsonrpc_helpers.js +13 -0
  84. package/dist/http/surface_query.d.ts +29 -4
  85. package/dist/http/surface_query.d.ts.map +1 -1
  86. package/dist/http/surface_query.js +30 -5
  87. package/dist/realtime/sse_auth_guard.d.ts +9 -3
  88. package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
  89. package/dist/realtime/sse_auth_guard.js +9 -3
  90. package/dist/server/serve_fact_route.d.ts.map +1 -1
  91. package/dist/server/serve_fact_route.js +7 -2
  92. package/dist/testing/CLAUDE.md +54 -28
  93. package/dist/testing/admin_integration.d.ts +0 -3
  94. package/dist/testing/admin_integration.d.ts.map +1 -1
  95. package/dist/testing/admin_integration.js +0 -1
  96. package/dist/testing/adversarial_404.d.ts.map +1 -1
  97. package/dist/testing/adversarial_404.js +4 -1
  98. package/dist/testing/adversarial_input.d.ts.map +1 -1
  99. package/dist/testing/adversarial_input.js +5 -4
  100. package/dist/testing/app_server.d.ts +4 -3
  101. package/dist/testing/app_server.d.ts.map +1 -1
  102. package/dist/testing/app_server.js +11 -5
  103. package/dist/testing/attack_surface.d.ts +21 -2
  104. package/dist/testing/attack_surface.d.ts.map +1 -1
  105. package/dist/testing/attack_surface.js +78 -27
  106. package/dist/testing/audit_completeness.d.ts +0 -3
  107. package/dist/testing/audit_completeness.d.ts.map +1 -1
  108. package/dist/testing/audit_completeness.js +0 -1
  109. package/dist/testing/auth_apps.d.ts +34 -2
  110. package/dist/testing/auth_apps.d.ts.map +1 -1
  111. package/dist/testing/auth_apps.js +43 -7
  112. package/dist/testing/cross_backend/account_lifecycle.d.ts +5 -4
  113. package/dist/testing/cross_backend/account_lifecycle.d.ts.map +1 -1
  114. package/dist/testing/cross_backend/account_lifecycle.js +7 -0
  115. package/dist/testing/cross_backend/action_manifest.d.ts +19 -3
  116. package/dist/testing/cross_backend/action_manifest.d.ts.map +1 -1
  117. package/dist/testing/cross_backend/action_manifest.js +10 -4
  118. package/dist/testing/cross_backend/action_manifest_parity.d.ts +9 -3
  119. package/dist/testing/cross_backend/action_manifest_parity.d.ts.map +1 -1
  120. package/dist/testing/cross_backend/action_manifest_parity.js +6 -0
  121. package/dist/testing/cross_backend/actor_lookup.d.ts +3 -2
  122. package/dist/testing/cross_backend/actor_lookup.d.ts.map +1 -1
  123. package/dist/testing/cross_backend/body_size.d.ts +4 -2
  124. package/dist/testing/cross_backend/body_size.d.ts.map +1 -1
  125. package/dist/testing/cross_backend/body_size.js +1 -1
  126. package/dist/testing/cross_backend/cell_crud.d.ts +2 -2
  127. package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -1
  128. package/dist/testing/cross_backend/cell_crud.js +10 -0
  129. package/dist/testing/cross_backend/cell_gated_create.d.ts +3 -3
  130. package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
  131. package/dist/testing/cross_backend/cell_grant_role.d.ts +2 -2
  132. package/dist/testing/cross_backend/cell_grant_role.d.ts.map +1 -1
  133. package/dist/testing/cross_backend/cell_relations.d.ts +2 -2
  134. package/dist/testing/cross_backend/cell_relations.d.ts.map +1 -1
  135. package/dist/testing/cross_backend/fact_serving.d.ts +2 -2
  136. package/dist/testing/cross_backend/fact_serving.d.ts.map +1 -1
  137. package/dist/testing/cross_backend/make_cross_backend_project.d.ts +17 -2
  138. package/dist/testing/cross_backend/make_cross_backend_project.d.ts.map +1 -1
  139. package/dist/testing/cross_backend/make_cross_backend_project.js +23 -3
  140. package/dist/testing/cross_backend/origin.d.ts +4 -2
  141. package/dist/testing/cross_backend/origin.d.ts.map +1 -1
  142. package/dist/testing/cross_backend/origin.js +1 -1
  143. package/dist/testing/cross_backend/peer_ping_ws.d.ts.map +1 -1
  144. package/dist/testing/cross_backend/peer_ping_ws.js +11 -7
  145. package/dist/testing/cross_backend/query_shape.d.ts +6 -5
  146. package/dist/testing/cross_backend/query_shape.d.ts.map +1 -1
  147. package/dist/testing/cross_backend/ready.d.ts +2 -2
  148. package/dist/testing/cross_backend/ready.d.ts.map +1 -1
  149. package/dist/testing/cross_backend/role_grant_offer_enumeration.d.ts.map +1 -1
  150. package/dist/testing/cross_backend/role_grant_offer_enumeration.js +7 -1
  151. package/dist/testing/cross_backend/setup.d.ts +26 -6
  152. package/dist/testing/cross_backend/setup.d.ts.map +1 -1
  153. package/dist/testing/cross_backend/standard.d.ts +5 -1
  154. package/dist/testing/cross_backend/standard.d.ts.map +1 -1
  155. package/dist/testing/cross_backend/standard.js +1 -5
  156. package/dist/testing/cross_backend/testing_backdoor.js +1 -1
  157. package/dist/testing/cross_backend/testing_reset_actions.d.ts +5 -0
  158. package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
  159. package/dist/testing/cross_backend/testing_reset_actions.js +3 -2
  160. package/dist/testing/cross_backend/token_lifetime.d.ts.map +1 -1
  161. package/dist/testing/cross_backend/token_lifetime.js +6 -0
  162. package/dist/testing/cross_backend/token_scope_surface.d.ts +2 -2
  163. package/dist/testing/cross_backend/token_scope_surface.d.ts.map +1 -1
  164. package/dist/testing/cross_backend/token_scope_surface.js +36 -7
  165. package/dist/testing/cross_backend/wire_shapes.d.ts +22 -0
  166. package/dist/testing/cross_backend/wire_shapes.d.ts.map +1 -0
  167. package/dist/testing/cross_backend/wire_shapes.js +41 -0
  168. package/dist/testing/data_exposure.d.ts +0 -3
  169. package/dist/testing/data_exposure.d.ts.map +1 -1
  170. package/dist/testing/data_exposure.js +0 -1
  171. package/dist/testing/db.d.ts +44 -7
  172. package/dist/testing/db.d.ts.map +1 -1
  173. package/dist/testing/db.js +62 -27
  174. package/dist/testing/integration.d.ts.map +1 -1
  175. package/dist/testing/integration.js +15 -2
  176. package/dist/testing/round_trip.d.ts +0 -3
  177. package/dist/testing/round_trip.d.ts.map +1 -1
  178. package/dist/testing/round_trip.js +0 -4
  179. package/dist/testing/rpc_attack_surface.js +2 -2
  180. package/dist/testing/rpc_round_trip.d.ts +0 -3
  181. package/dist/testing/rpc_round_trip.d.ts.map +1 -1
  182. package/dist/testing/rpc_round_trip.js +0 -1
  183. package/dist/testing/schema_generators.d.ts +15 -0
  184. package/dist/testing/schema_generators.d.ts.map +1 -1
  185. package/dist/testing/schema_generators.js +45 -11
  186. package/dist/testing/standard.d.ts +5 -1
  187. package/dist/testing/standard.d.ts.map +1 -1
  188. package/dist/testing/standard.js +2 -7
  189. package/dist/testing/surface_invariants.d.ts +7 -1
  190. package/dist/testing/surface_invariants.d.ts.map +1 -1
  191. package/dist/testing/surface_invariants.js +11 -5
  192. package/dist/timestamp.d.ts +58 -0
  193. package/dist/timestamp.d.ts.map +1 -0
  194. package/dist/timestamp.js +57 -0
  195. package/dist/ui/AdminAccounts.svelte +6 -4
  196. package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -1
  197. package/dist/ui/AdminInvites.svelte +3 -3
  198. package/dist/ui/AdminInvites.svelte.d.ts.map +1 -1
  199. package/dist/ui/AdminOverview.svelte +10 -10
  200. package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
  201. package/dist/ui/AdminRoleGrantHistory.svelte +7 -7
  202. package/dist/ui/AdminRoleGrantHistory.svelte.d.ts.map +1 -1
  203. package/dist/ui/AdminSettings.svelte +1 -1
  204. package/dist/ui/RoleGrantOfferHistory.svelte +3 -1
  205. package/dist/ui/RoleGrantOfferHistory.svelte.d.ts.map +1 -1
  206. package/dist/ui/RoleGrantOfferInbox.svelte +4 -1
  207. package/dist/ui/RoleGrantOfferInbox.svelte.d.ts.map +1 -1
  208. package/dist/ui/SurfaceExplorer.svelte +33 -17
  209. package/dist/ui/SurfaceExplorer.svelte.d.ts.map +1 -1
  210. package/dist/ui/role_grant_offers_state.svelte.d.ts.map +1 -1
  211. package/dist/ui/role_grant_offers_state.svelte.js +6 -2
  212. package/package.json +10 -10
@@ -41,19 +41,32 @@ export const detect_format = (field_schema) => {
41
41
  return null;
42
42
  };
43
43
  /**
44
- * Extract a candidate value from a JSON Schema `pattern` when the shape is
45
- * a fixed-length hex character class — covers blake3 (64-char lowercase hex),
46
- * sha256, md5, and similar digest refinements. Returns `null` when the
47
- * pattern doesn't match the expected shape.
44
+ * Extract a candidate value from a JSON Schema `pattern` when the shape is a
45
+ * fixed-length hex character class, optionally behind a literal prefix —
46
+ * covers bare digests (blake3 64-char lowercase hex, sha256, md5) and the
47
+ * algorithm-tagged form the fact store addresses bytes by
48
+ * (`^blake3:[0-9a-f]{64}$` → `blake3:` + 64 zeros).
49
+ *
50
+ * Coverage today:
51
+ * - Optional prefix: alphanumeric, starting with a letter, ending in `:`
52
+ * (`blake3:`, `sha256:`). A `_`-joined prefix over a base64url class is
53
+ * the sibling shape — see `generate_prefix_slug_pattern_value`.
54
+ * - Character class must be exactly `[0-9a-f]` or `[0-9a-fA-F]` (Zod passes
55
+ * the regex source through verbatim — no character-class reordering).
56
+ * - Fixed-length quantifier `{N}`.
57
+ *
58
+ * Returns `null` when the pattern doesn't match the expected shape; the
59
+ * caller then falls through to its other candidates.
48
60
  */
49
61
  const generate_hex_pattern_value = (pattern) => {
50
- const match = /^\^\[0-9a-f(?:A-F)?\]\{(\d+)\}\$$/.exec(pattern);
62
+ const match = /^\^(?:([A-Za-z][A-Za-z0-9]*):)?\[0-9a-f(?:A-F)?\]\{(\d+)\}\$$/.exec(pattern);
51
63
  if (!match)
52
64
  return null;
53
- const n = Number(match[1]);
65
+ const n = Number(match[2]);
54
66
  if (!Number.isInteger(n) || n <= 0)
55
67
  return null;
56
- return '0'.repeat(n);
68
+ const prefix = match[1] === undefined ? '' : `${match[1]}:`;
69
+ return prefix + '0'.repeat(n);
57
70
  };
58
71
  /**
59
72
  * Extract a candidate value from a JSON Schema `pattern` when the shape is a
@@ -78,8 +91,8 @@ const generate_hex_pattern_value = (pattern) => {
78
91
  * doesn't match the detection regex. `x`-fill would still satisfy the
79
92
  * refinement if the detection were widened.
80
93
  * - No-prefix fixed-length slugs (e.g. `^[A-Za-z0-9_-]{43}$` — daemon
81
- * token shape) are not matched here; see `generate_hex_pattern_value`
82
- * for the hex variant.
94
+ * token shape) are not matched here; `generate_hex_pattern_value` is the
95
+ * hex sibling, and it covers both the bare and the `prefix:`-tagged form.
83
96
  * Widen the detection regex when a new branded shape surfaces.
84
97
  */
85
98
  const generate_prefix_slug_pattern_value = (pattern) => {
@@ -254,22 +267,43 @@ export const generate_valid_value = (field, field_schema) => {
254
267
  /**
255
268
  * Resolve a route path with valid-ish param values so params validation passes.
256
269
  * Used when testing input on routes that also have params.
270
+ *
271
+ * Without a params schema (or for a param the schema doesn't name) each
272
+ * `:name` becomes `test_<name>` — fine for an unconstrained segment. With
273
+ * one, `test_<name>` is still preferred wherever the field schema accepts
274
+ * it, because the param name in the URL is what makes a failure message
275
+ * readable. Only a segment that refuses it falls through: a `uuid` param
276
+ * takes the nil UUID (the fast path — by far the most common shape), and
277
+ * anything else is synthesized by `generate_valid_string`, so a
278
+ * format-constrained segment (a `blake3:…` fact hash, a `tok_…` id)
279
+ * resolves to a value its schema accepts instead of a `test_<name>` that
280
+ * 400s before the route's real gates run.
281
+ *
282
+ * The synthesized value is percent-encoded, and a value containing `/` is
283
+ * refused (it would silently add a path segment and route elsewhere) —
284
+ * both fall back to `test_<name>`.
257
285
  */
258
286
  export const resolve_valid_path = (path, params_schema) => {
259
287
  if (!params_schema) {
260
288
  return path.replace(/:(\w+)/g, 'test_$1');
261
289
  }
262
290
  return path.replace(/:(\w+)/g, (_match, name) => {
291
+ const fallback = `test_${name}`;
263
292
  const field_schema = params_schema.shape[name];
264
293
  if (!field_schema)
265
- return `test_${name}`;
294
+ return fallback;
295
+ if (field_schema.safeParse(fallback).success)
296
+ return fallback;
266
297
  const base_type = zod_get_base_type(field_schema);
267
298
  if (base_type === 'uuid')
268
299
  return '00000000-0000-0000-0000-000000000000';
269
300
  const format = detect_format(field_schema);
270
301
  if (format === 'uuid')
271
302
  return '00000000-0000-0000-0000-000000000000';
272
- return `test_${name}`;
303
+ const generated = generate_valid_string(field_schema);
304
+ if (generated.includes('/'))
305
+ return fallback;
306
+ return encodeURIComponent(generated);
273
307
  });
274
308
  };
275
309
  /**
@@ -37,7 +37,11 @@ export interface StandardTestOptions {
37
37
  * in-process and cross-process tests.
38
38
  */
39
39
  surface_source: AppSurfaceSpec;
40
- /** Backend capability declarations. */
40
+ /**
41
+ * Backend capability declarations. Forwarded to the one bundled suite
42
+ * that gates on them (`describe_standard_integration_tests`); the other
43
+ * suites in the bundle read no flags and take no capability field.
44
+ */
41
45
  capabilities: BackendCapabilities;
42
46
  /** Session config — needed for cookie_name + factory-form rpc_endpoints resolution. */
43
47
  session_options: SessionOptions<string>;
@@ -1 +1 @@
1
- {"version":3,"file":"standard.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/standard.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AASvD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,2CAA2C;IAC3C,UAAU,EAAE,SAAS,CAAC;IACtB;;;OAGG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC,uFAAuF;IACvF,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;;;OAKG;IACH,kBAAkB,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IAChE;;;;;OAKG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC;;;OAGG;IACH,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB;;;;OAIG;IACH,SAAS,CAAC,EAAE,sBAAsB,CAAC;IACnC,sEAAsE;IACtE,yBAAyB,CAAC,EAAE,eAAe,CAAC;IAC5C;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,8DAA8D;IAC9D,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GAAI,SAAS,mBAAmB,KAAG,IA2DtE,CAAC"}
1
+ {"version":3,"file":"standard.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/standard.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AASvD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,2CAA2C;IAC3C,UAAU,EAAE,SAAS,CAAC;IACtB;;;OAGG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B;;;;OAIG;IACH,YAAY,EAAE,mBAAmB,CAAC;IAClC,uFAAuF;IACvF,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;;;OAKG;IACH,kBAAkB,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IAChE;;;;;OAKG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC;;;OAGG;IACH,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB;;;;OAIG;IACH,SAAS,CAAC,EAAE,sBAAsB,CAAC;IACnC,sEAAsE;IACtE,yBAAyB,CAAC,EAAE,eAAe,CAAC;IAC5C;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,8DAA8D;IAC9D,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GAAI,SAAS,mBAAmB,KAAG,IAsDtE,CAAC"}
@@ -24,20 +24,17 @@ export const describe_standard_tests = (options) => {
24
24
  });
25
25
  describe_round_trip_validation({
26
26
  setup_test: options.setup_test,
27
- surface_source: options.surface_source,
28
- capabilities: options.capabilities
27
+ surface_source: options.surface_source
29
28
  });
30
29
  describe_rpc_round_trip_tests({
31
30
  setup_test: options.setup_test,
32
31
  surface_source: options.surface_source,
33
- capabilities: options.capabilities,
34
32
  session_options: options.session_options,
35
33
  rpc_endpoints: options.rpc_endpoints
36
34
  });
37
35
  describe_data_exposure_tests({
38
36
  setup_test: options.setup_test,
39
- surface_source: options.surface_source,
40
- capabilities: options.capabilities
37
+ surface_source: options.surface_source
41
38
  });
42
39
  describe_rate_limiting_tests({
43
40
  session_options: options.session_options,
@@ -49,7 +46,6 @@ export const describe_standard_tests = (options) => {
49
46
  describe_standard_admin_integration_tests({
50
47
  setup_test: options.setup_test,
51
48
  surface_source: options.surface_source,
52
- capabilities: options.capabilities,
53
49
  session_options: options.session_options,
54
50
  roles: options.roles,
55
51
  rpc_endpoints: options.rpc_endpoints,
@@ -58,7 +54,6 @@ export const describe_standard_tests = (options) => {
58
54
  describe_audit_completeness_tests({
59
55
  setup_test: options.setup_test,
60
56
  surface_source: options.surface_source,
61
- capabilities: options.capabilities,
62
57
  session_options: options.session_options,
63
58
  rpc_endpoints: options.rpc_endpoints
64
59
  });
@@ -5,7 +5,13 @@ import type { AppSurface } from '../http/surface.js';
5
5
  */
6
6
  export declare const assert_protected_routes_declare_401: (surface: AppSurface) => void;
7
7
  /**
8
- * Every role/keeper route has 403 in `error_schemas`.
8
+ * Every route with an authority gate that can answer 403 declares one — a
9
+ * role gate, or a credential-channel gate of any kind (keeper included).
10
+ *
11
+ * The credential half is not just the keeper routes: the session-only gates
12
+ * point the same axis the other way and refuse with the same status, so a
13
+ * route carrying one and no 403 schema would have its denial body go
14
+ * unvalidated by the DEV-only error-schema check.
9
15
  */
10
16
  export declare const assert_role_routes_declare_403: (surface: AppSurface) => void;
11
17
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"surface_invariants.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/surface_invariants.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAuB7B,OAAO,KAAK,EAAE,UAAU,EAAwB,MAAM,oBAAoB,CAAC;AAe3E;;GAEG;AACH,eAAO,MAAM,mCAAmC,GAAI,SAAS,UAAU,KAAG,IAQzE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,8BAA8B,GAAI,SAAS,UAAU,KAAG,IASpE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,+BAA+B,GAAI,SAAS,UAAU,KAAG,IAQrE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,gCAAgC,GAAI,SAAS,UAAU,KAAG,IAQtE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,+BAA+B,GAAI,SAAS,UAAU,KAAG,IAQrE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,2BAA2B,GAAI,SAAS,UAAU,KAAG,IAIjE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,0BAA0B,GAAI,SAAS,UAAU,KAAG,IAOhE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,mCAAmC,GAAI,SAAS,UAAU,KAAG,IAezE,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,uCAAuC,GAAI,SAAS,UAAU,KAAG,IAO7E,CAAC;AAyBF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,oCAAoC,GAAI,SAAS,UAAU,KAAG,IAoC1E,CAAC;AAsEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,sCAAsC,GAAI,SAAS,UAAU,KAAG,IAU5E,CAAC;AAIF,4DAA4D;AAC5D,MAAM,MAAM,sBAAsB,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;AAEpE,iEAAiE;AACjE,MAAM,WAAW,qBAAqB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,sBAAsB,CAAC;IACpC,qDAAqD;IACrD,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;CAClC;AAiED;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,qBAAqB,CAgB7F,CAAC;AAIF;;;;;;GAMG;AACH,eAAO,MAAM,sCAAsC,GAAI,SAAS,UAAU,KAAG,IAS5E,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,qCAAqC,GAAI,SAAS,UAAU,KAAG,IAS3E,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,4CAA4C,GAAI,SAAS,UAAU,KAAG,IAWlF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sCAAsC,GAAI,SAAS,UAAU,KAAG,IAa5E,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,cAAc,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,yBAAyB,GAAI,SAAS,UAAU,KAAG,IAoB/D,CAAC;AAIF;;;;GAIG;AACH,MAAM,WAAW,4BAA4B;IAC5C;;;;;OAKG;IACH,wBAAwB,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IAClD;;;OAGG;IACH,yBAAyB,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1C;;;OAGG;IACH,qBAAqB,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACtC;AASD;;;;;;GAMG;AACH,eAAO,MAAM,oCAAoC,GAChD,SAAS,UAAU,EACnB,qBAAoB,KAAK,CAAC,MAAM,GAAG,MAAM,CAA8B,KACrE,IAcF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,qCAAqC,GACjD,SAAS,UAAU,EACnB,YAAW,KAAK,CAAC,MAAM,CAAM,KAC3B,IAYF,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,+BAA+B,GAAI,SAAS,UAAU,KAAG,IAQrE,CAAC;AAKF;;;;;GAKG;AACH,eAAO,MAAM,iCAAiC,GAC7C,SAAS,UAAU,EACnB,WAAU,KAAK,CAAC,MAAM,CAAiC,KACrD,IAWF,CAAC;AAWF,mDAAmD;AACnD,MAAM,WAAW,2BAA2B;IAC3C,6FAA6F;IAC7F,eAAe,CAAC,EAAE,sBAAsB,CAAC;IACzC,mEAAmE;IACnE,eAAe,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAChC,kDAAkD;IAClD,SAAS,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,uCAAuC,EAAE,aAAa,CAAC,MAAM,CAAM,CAAC;AAEjF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,8BAA8B,EAAE,2BAG5C,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,6BAA6B,GACzC,SAAS,UAAU,EACnB,UAAU,2BAA2B,KACnC,IAsBF,CAAC;AAIF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,GAAI,SAAS,UAAU,KAAG,IAY/D,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,gCAAgC,GAAI,SAAS,UAAU,KAAG,IAMtE,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,8BAA8B,GAC1C,SAAS,UAAU,EACnB,UAAS,4BAAiC,KACxC,IAKF,CAAC"}
1
+ {"version":3,"file":"surface_invariants.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/surface_invariants.ts"],"names":[],"mappings":"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"}
@@ -18,7 +18,7 @@ import './assert_dev_env.js';
18
18
  */
19
19
  import { assert } from 'vitest';
20
20
  import { middleware_applies } from '../http/schema_helpers.js';
21
- import { filter_protected_routes, filter_role_routes, filter_keeper_routes, filter_routes_with_input, filter_routes_with_params, filter_routes_with_query, filter_public_routes, format_route_key } from '../http/surface_query.js';
21
+ import { filter_protected_routes, filter_role_routes, filter_keeper_routes, filter_credential_gated_routes, filter_routes_with_input, filter_routes_with_params, filter_routes_with_query, filter_public_routes, format_route_key } from '../http/surface_query.js';
22
22
  import { PROTOCOL_ACTION_METHODS } from '../actions/action_codegen.js';
23
23
  // --- Structural invariants ---
24
24
  /**
@@ -31,13 +31,19 @@ export const assert_protected_routes_declare_401 = (surface) => {
31
31
  }
32
32
  };
33
33
  /**
34
- * Every role/keeper route has 403 in `error_schemas`.
34
+ * Every route with an authority gate that can answer 403 declares one — a
35
+ * role gate, or a credential-channel gate of any kind (keeper included).
36
+ *
37
+ * The credential half is not just the keeper routes: the session-only gates
38
+ * point the same axis the other way and refuse with the same status, so a
39
+ * route carrying one and no 403 schema would have its denial body go
40
+ * unvalidated by the DEV-only error-schema check.
35
41
  */
36
42
  export const assert_role_routes_declare_403 = (surface) => {
37
43
  const role_routes = filter_role_routes(surface);
38
- const keeper_routes = filter_keeper_routes(surface);
39
- for (const route of [...role_routes, ...keeper_routes]) {
40
- assert.ok(route.error_schemas && '403' in route.error_schemas, `${format_route_key(route)} requires role/keeper but missing 403 error schema`);
44
+ const credential_gated_routes = filter_credential_gated_routes(surface);
45
+ for (const route of [...role_routes, ...credential_gated_routes]) {
46
+ assert.ok(route.error_schemas && '403' in route.error_schemas, `${format_route_key(route)} has a role or credential gate but missing 403 error schema`);
41
47
  }
42
48
  };
43
49
  /**
@@ -0,0 +1,58 @@
1
+ /**
2
+ * The canonical wire timestamp shape shared by the two spine implementations.
3
+ *
4
+ * Every timestamp that reaches the wire is second-precision UTC —
5
+ * `YYYY-MM-DDTHH:MM:SSZ`, exactly 20 characters. Timestamps read from
6
+ * Postgres get that shape from the SQL projection
7
+ * (`iso8601_timestamp_column` in `db/sql_columns.ts`, twin of
8
+ * `fuz_db::iso8601_timestamp_column`); this module is the same shape for the
9
+ * stamps minted in TS rather than read back from a row, so the two sources
10
+ * agree and the two backends serialize the same instant to the same bytes.
11
+ *
12
+ * @module
13
+ */
14
+ /**
15
+ * The canonical wire timestamp shape as a regex — second-precision UTC,
16
+ * exactly 20 characters.
17
+ *
18
+ * Lives in library code rather than a test helper so the unit suites, the db
19
+ * suites, and the cross-backend suites all pin the same pattern the server
20
+ * renders.
21
+ */
22
+ export declare const ISO8601_SECONDS: RegExp;
23
+ /**
24
+ * The width of one wire timestamp, in milliseconds.
25
+ *
26
+ * A wire stamp is truncated, not rounded, so the instant it names lies
27
+ * anywhere in `[stamp, stamp + ISO8601_SECOND_MS)`. A predicate that must
28
+ * not fire early against a stamp — "has this expiry passed?" — adds this to
29
+ * reach the end of the second the stamp stands for.
30
+ */
31
+ export declare const ISO8601_SECOND_MS = 1000;
32
+ /**
33
+ * Whether the second a wire timestamp names has not yet ended.
34
+ *
35
+ * The one in-memory expiry rule over a truncated stamp: `stamp` stands for
36
+ * `[stamp, stamp + ISO8601_SECOND_MS)`, so this admits until that whole
37
+ * second is over and can never report an expiry the server's raw-column
38
+ * `expires_at > NOW()` gate would still accept. A stamp that does not parse
39
+ * is never live.
40
+ *
41
+ * @param stamp - a second-precision wire timestamp
42
+ * @param now_ms - the current time in epoch milliseconds
43
+ * @returns `true` while `now_ms` lies before the end of `stamp`'s second
44
+ */
45
+ export declare const is_iso8601_seconds_live: (stamp: string, now_ms: number) => boolean;
46
+ /**
47
+ * Render a `Date` as the canonical second-precision UTC wire timestamp
48
+ * (`YYYY-MM-DDTHH:MM:SSZ`).
49
+ *
50
+ * Truncates rather than rounds, mirroring Postgres'
51
+ * `to_char(…, 'YYYY-MM-DD"T"HH24:MI:SS"Z"')` and the Rust spine's
52
+ * `fuz_sys::rfc3339_now`.
53
+ *
54
+ * @param date - the instant to render
55
+ * @returns the 20-character UTC timestamp
56
+ */
57
+ export declare const to_iso8601_seconds: (date: Date) => string;
58
+ //# sourceMappingURL=timestamp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timestamp.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/timestamp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,QAA2C,CAAC;AAExE;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,OAAO,CAAC;AAEtC;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,uBAAuB,GAAI,OAAO,MAAM,EAAE,QAAQ,MAAM,KAAG,OACzB,CAAC;AAEhD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,IAAI,KAAG,MAA+C,CAAC"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * The canonical wire timestamp shape shared by the two spine implementations.
3
+ *
4
+ * Every timestamp that reaches the wire is second-precision UTC —
5
+ * `YYYY-MM-DDTHH:MM:SSZ`, exactly 20 characters. Timestamps read from
6
+ * Postgres get that shape from the SQL projection
7
+ * (`iso8601_timestamp_column` in `db/sql_columns.ts`, twin of
8
+ * `fuz_db::iso8601_timestamp_column`); this module is the same shape for the
9
+ * stamps minted in TS rather than read back from a row, so the two sources
10
+ * agree and the two backends serialize the same instant to the same bytes.
11
+ *
12
+ * @module
13
+ */
14
+ /**
15
+ * The canonical wire timestamp shape as a regex — second-precision UTC,
16
+ * exactly 20 characters.
17
+ *
18
+ * Lives in library code rather than a test helper so the unit suites, the db
19
+ * suites, and the cross-backend suites all pin the same pattern the server
20
+ * renders.
21
+ */
22
+ export const ISO8601_SECONDS = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/;
23
+ /**
24
+ * The width of one wire timestamp, in milliseconds.
25
+ *
26
+ * A wire stamp is truncated, not rounded, so the instant it names lies
27
+ * anywhere in `[stamp, stamp + ISO8601_SECOND_MS)`. A predicate that must
28
+ * not fire early against a stamp — "has this expiry passed?" — adds this to
29
+ * reach the end of the second the stamp stands for.
30
+ */
31
+ export const ISO8601_SECOND_MS = 1000;
32
+ /**
33
+ * Whether the second a wire timestamp names has not yet ended.
34
+ *
35
+ * The one in-memory expiry rule over a truncated stamp: `stamp` stands for
36
+ * `[stamp, stamp + ISO8601_SECOND_MS)`, so this admits until that whole
37
+ * second is over and can never report an expiry the server's raw-column
38
+ * `expires_at > NOW()` gate would still accept. A stamp that does not parse
39
+ * is never live.
40
+ *
41
+ * @param stamp - a second-precision wire timestamp
42
+ * @param now_ms - the current time in epoch milliseconds
43
+ * @returns `true` while `now_ms` lies before the end of `stamp`'s second
44
+ */
45
+ export const is_iso8601_seconds_live = (stamp, now_ms) => Date.parse(stamp) + ISO8601_SECOND_MS > now_ms;
46
+ /**
47
+ * Render a `Date` as the canonical second-precision UTC wire timestamp
48
+ * (`YYYY-MM-DDTHH:MM:SSZ`).
49
+ *
50
+ * Truncates rather than rounds, mirroring Postgres'
51
+ * `to_char(…, 'YYYY-MM-DD"T"HH24:MI:SS"Z"')` and the Rust spine's
52
+ * `fuz_sys::rfc3339_now`.
53
+ *
54
+ * @param date - the instant to render
55
+ * @returns the 20-character UTC timestamp
56
+ */
57
+ export const to_iso8601_seconds = (date) => `${date.toISOString().slice(0, 19)}Z`;
@@ -44,9 +44,9 @@
44
44
  <h1>accounts</h1>
45
45
  {#if admin_accounts.account_count > 0}
46
46
  <p>
47
- <span class="chip color_a"
48
- >{admin_accounts.account_count} account{admin_accounts.account_count === 1 ? '' : 's'}</span
49
- >
47
+ <span class="chip color_a">
48
+ {admin_accounts.account_count} account{admin_accounts.account_count === 1 ? '' : 's'}
49
+ </span>
50
50
  </p>
51
51
  {/if}
52
52
 
@@ -158,7 +158,9 @@
158
158
  {#each admin_accounts.grantable_roles as role (role)}
159
159
  {@const key = grant_key(row.account.id, role)}
160
160
  {@const grant_error = admin_accounts.grant.error(key)}
161
- {#if !row.role_grants.some((p) => p.role === role) && !row.pending_offers.some((o) => o.role === role)}
161
+ {#if !row.role_grants.some((p) => p.role === role) &&
162
+ !row.pending_offers.some((o) => o.role === role)
163
+ }
162
164
  <ConfirmButton
163
165
  onconfirm={() => admin_accounts.submit_grant(row.account.id, role)}
164
166
  title="offer {role}"
@@ -1 +1 @@
1
- {"version":3,"file":"AdminAccounts.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminAccounts.svelte"],"names":[],"mappings":"AAkMA,QAAA,MAAM,aAAa,2DAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"AdminAccounts.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminAccounts.svelte"],"names":[],"mappings":"AAqMA,QAAA,MAAM,aAAa,2DAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -59,9 +59,9 @@
59
59
  </section>
60
60
  {#if admin_invites.invite_count > 0}
61
61
  <p>
62
- <span class="chip color_a"
63
- >{admin_invites.unclaimed_count} unclaimed / {admin_invites.invite_count} total</span
64
- >
62
+ <span class="chip color_a">
63
+ {admin_invites.unclaimed_count} unclaimed / {admin_invites.invite_count} total
64
+ </span>
65
65
  </p>
66
66
  {/if}
67
67
 
@@ -1 +1 @@
1
- {"version":3,"file":"AdminInvites.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminInvites.svelte"],"names":[],"mappings":"AAqJA,QAAA,MAAM,YAAY,2DAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"AdminInvites.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminInvites.svelte"],"names":[],"mappings":"AAuJA,QAAA,MAAM,YAAY,2DAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -146,9 +146,9 @@
146
146
  <div class="baseline-row gap_xs font_size_sm mt_sm">
147
147
  <span class="text_50">newest session:</span>
148
148
  <strong>{most_recent.username}</strong>
149
- <span class="text_50" title={format_datetime_local(most_recent.created_at)}
150
- >{format_relative_time(most_recent.created_at)}</span
151
- >
149
+ <span class="text_50" title={format_datetime_local(most_recent.created_at)}>
150
+ {format_relative_time(most_recent.created_at)}
151
+ </span>
152
152
  </div>
153
153
  {/if}
154
154
  {/if}
@@ -214,9 +214,9 @@
214
214
  <ul class="compact-list">
215
215
  {#each recent_events as event (event.id)}
216
216
  <li>
217
- <span class="text_50 font_size_sm" title={format_datetime_local(event.created_at)}
218
- >{format_relative_time(event.created_at)}</span
219
- >
217
+ <span class="text_50 font_size_sm" title={format_datetime_local(event.created_at)}>
218
+ {format_relative_time(event.created_at)}
219
+ </span>
220
220
  <code class="font_size_sm">{event.event_type}</code>
221
221
  {#if event.outcome === 'failure'}
222
222
  <span class="chip font_size_sm color_c">fail</span>
@@ -251,9 +251,9 @@
251
251
  <ul class="compact-list">
252
252
  {#each role_grant_changes.slice(0, 4) as event (event.id)}
253
253
  <li class="font_size_sm">
254
- <span class="text_50" title={format_datetime_local(event.created_at)}
255
- >{format_relative_time(event.created_at)}</span
256
- >
254
+ <span class="text_50" title={format_datetime_local(event.created_at)}>
255
+ {format_relative_time(event.created_at)}
256
+ </span>
257
257
  <code>{event.event_type === 'role_grant_create' ? 'grant' : 'revoke'}</code>
258
258
  {#if event.metadata?.role}
259
259
  <span class="chip font_size_sm">{event.metadata.role}</span>
@@ -309,7 +309,7 @@
309
309
  label="log out"
310
310
  >
311
311
  {#snippet popover_button_content()}
312
- <span class="p_md"> log out </span>
312
+ <span class="p_md">log out</span>
313
313
  {/snippet}
314
314
  </ConfirmButton>
315
315
  </div>
@@ -1 +1 @@
1
- {"version":3,"file":"AdminOverview.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminOverview.svelte"],"names":[],"mappings":"AAyUA,QAAA,MAAM,aAAa,2DAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"AdminOverview.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminOverview.svelte"],"names":[],"mappings":"AA+UA,QAAA,MAAM,aAAa,2DAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -74,14 +74,14 @@
74
74
  {:else if column.key === 'username'}
75
75
  <!-- Prefer actor-grain id in the truncated fallback; account is
76
76
  the second fallback for events with no actor binding. -->
77
- <span class="text_50"
78
- >{row.username ?? truncate_uuid(row.actor_id ?? row.account_id ?? '?')}</span
79
- >
77
+ <span class="text_50">
78
+ {row.username ?? truncate_uuid(row.actor_id ?? row.account_id ?? '?')}
79
+ </span>
80
80
  {:else if column.key === 'target_username'}
81
- <span class="text_50"
82
- >{row.target_username ??
83
- truncate_uuid(row.target_actor_id ?? row.target_account_id ?? '?')}</span
84
- >
81
+ <span class="text_50">
82
+ {row.target_username ??
83
+ truncate_uuid(row.target_actor_id ?? row.target_account_id ?? '?')}
84
+ </span>
85
85
  {:else if column.key === 'created_at'}
86
86
  <span title={format_datetime_local(row.created_at)}>
87
87
  {format_relative_time(row.created_at)}
@@ -1 +1 @@
1
- {"version":3,"file":"AdminRoleGrantHistory.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminRoleGrantHistory.svelte"],"names":[],"mappings":"AAmGA,QAAA,MAAM,qBAAqB,2DAAwC,CAAC;AACpE,KAAK,qBAAqB,GAAG,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACtE,eAAe,qBAAqB,CAAC"}
1
+ {"version":3,"file":"AdminRoleGrantHistory.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminRoleGrantHistory.svelte"],"names":[],"mappings":"AAuGA,QAAA,MAAM,qBAAqB,2DAAwC,CAAC;AACpE,KAAK,qBAAqB,GAAG,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACtE,eAAe,qBAAqB,CAAC"}
@@ -33,7 +33,7 @@
33
33
  label="log out"
34
34
  >
35
35
  {#snippet popover_button_content()}
36
- <span class="p_md"> log out </span>
36
+ <span class="p_md">log out</span>
37
37
  {/snippet}
38
38
  </ConfirmButton>
39
39
  </section>
@@ -15,6 +15,7 @@
15
15
  import Datatable from './Datatable.svelte';
16
16
  import type { DatatableColumn } from './datatable.js';
17
17
  import { format_relative_time, format_datetime_local, truncate_uuid } from './ui_format.js';
18
+ import { is_iso8601_seconds_live } from '../timestamp.js';
18
19
  import type { RoleGrantOfferJson } from '../auth/role_grant_offer_schema.js';
19
20
  import { format_scope_context, resolve_scope_label, type FormatScope } from './format_scope.js';
20
21
 
@@ -47,7 +48,8 @@
47
48
  if (offer.declined_at) return 'declined';
48
49
  if (offer.retracted_at) return 'retracted';
49
50
  if (offer.superseded_at) return 'superseded';
50
- if (Date.parse(offer.expires_at) <= now) return 'expired';
51
+ // end-of-second comparison, see `RoleGrantOffersState.incoming`
52
+ if (!is_iso8601_seconds_live(offer.expires_at, now)) return 'expired';
51
53
  return 'pending';
52
54
  };
53
55
 
@@ -1 +1 @@
1
- {"version":3,"file":"RoleGrantOfferHistory.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/RoleGrantOfferHistory.svelte"],"names":[],"mappings":"AAmBA,OAAO,EAA6C,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAE/F,KAAK,gBAAgB,GAAI;IACxB,oFAAoF;IACpF,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,YAAY,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,MAAM,CAAC;IACjD;;;;OAIG;IACH,YAAY,CAAC,EAAE,WAAW,CAAC;IAC3B,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACvC,CAAC;AAkGH,QAAA,MAAM,qBAAqB,sDAAwC,CAAC;AACpE,KAAK,qBAAqB,GAAG,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACtE,eAAe,qBAAqB,CAAC"}
1
+ {"version":3,"file":"RoleGrantOfferHistory.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/RoleGrantOfferHistory.svelte"],"names":[],"mappings":"AAoBA,OAAO,EAA6C,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAE/F,KAAK,gBAAgB,GAAI;IACxB,oFAAoF;IACpF,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,YAAY,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,MAAM,CAAC;IACjD;;;;OAIG;IACH,YAAY,CAAC,EAAE,WAAW,CAAC;IAC3B,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACvC,CAAC;AAoGH,QAAA,MAAM,qBAAqB,sDAAwC,CAAC;AACpE,KAAK,qBAAqB,GAAG,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACtE,eAAe,qBAAqB,CAAC"}
@@ -48,7 +48,10 @@
48
48
  <section class="role-grant-offer-inbox">
49
49
  <h2>pending offers</h2>
50
50
 
51
- {#if role_grant_offers.list.error || role_grant_offers.accept.error || role_grant_offers.decline.error}
51
+ {#if role_grant_offers.list.error ||
52
+ role_grant_offers.accept.error ||
53
+ role_grant_offers.decline.error
54
+ }
52
55
  <p class="color_c_50">
53
56
  {role_grant_offers.list.error ??
54
57
  role_grant_offers.accept.error ??
@@ -1 +1 @@
1
- {"version":3,"file":"RoleGrantOfferInbox.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/RoleGrantOfferInbox.svelte"],"names":[],"mappings":"AAmBA,OAAO,EAA6C,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAE/F,KAAK,gBAAgB,GAAI;IACxB,uEAAuE;IACvE,YAAY,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,MAAM,CAAC;IACjD;;;;OAIG;IACH,YAAY,CAAC,EAAE,WAAW,CAAC;IAC3B,+DAA+D;IAC/D,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACvC,CAAC;AA+FH,QAAA,MAAM,mBAAmB,sDAAwC,CAAC;AAClE,KAAK,mBAAmB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAClE,eAAe,mBAAmB,CAAC"}
1
+ {"version":3,"file":"RoleGrantOfferInbox.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/RoleGrantOfferInbox.svelte"],"names":[],"mappings":"AAmBA,OAAO,EAA6C,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAE/F,KAAK,gBAAgB,GAAI;IACxB,uEAAuE;IACvE,YAAY,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,MAAM,CAAC;IACjD;;;;OAIG;IACH,YAAY,CAAC,EAAE,WAAW,CAAC;IAC3B,+DAA+D;IAC/D,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACvC,CAAC;AAiGH,QAAA,MAAM,mBAAmB,sDAAwC,CAAC;AAClE,KAAK,mBAAmB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAClE,eAAe,mBAAmB,CAAC"}