@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
@@ -10,7 +10,16 @@
10
10
  * client-safe subset — rather than spelling a second column list, so the
11
11
  * drift guard on the base const (`assert_columns_match_live` in
12
12
  * `testing/db.ts`) covers every derived projection too. Twin of the Rust
13
- * spine's `*_COLUMNS` consts + `fuz_db::qualify_columns`.
13
+ * spine's `*_COLUMNS` consts + `fuz_db::qualify_columns` / `omit_columns`.
14
+ *
15
+ * A read that needs a column rendered as something other than the bare
16
+ * reference passes a `ColumnExpr` — the `expr` override, twin of the third
17
+ * argument to `fuz_db::qualify_columns`. Every timestamp that reaches the
18
+ * wire projects through `iso8601_timestamp_column` so both spines serialize
19
+ * the same instant to the same bytes; each query module builds its override
20
+ * with `iso8601_timestamp_expr` over its table const. An override costs one
21
+ * rule — a projected `ORDER BY` must qualify its column — stated on
22
+ * `qualify_columns`.
14
23
  *
15
24
  * Placement rule: a const lives in its table's query module. When two query
16
25
  * modules project the same table (the revoke cascades in
@@ -26,13 +35,68 @@
26
35
  *
27
36
  * @module
28
37
  */
38
+ /**
39
+ * Per-column expression override for a projection — return the SQL to
40
+ * project for `column`, or `undefined` to keep the bare column reference.
41
+ *
42
+ * Twin of the `expr` closure `fuz_db::qualify_columns` takes. The Rust side
43
+ * decodes rows positionally, so its overrides need no output name; TS reads
44
+ * rows by name, so `columns_sql` / `qualify_columns` alias every override
45
+ * back to its column (`… AS created_at`) — otherwise a `to_char(…)`
46
+ * projection would arrive under Postgres' derived name and read as
47
+ * `undefined`.
48
+ *
49
+ * `undefined` is the *only* decline sentinel. An empty string is a returned
50
+ * expression, not a decline: it renders ` AS created_at` and the query fails
51
+ * to parse. This is documented rather than guarded — every override in the
52
+ * repo is a literal lookup returning a fragment or nothing, and a malformed
53
+ * projection failing loud at the first query beats silently swallowing an
54
+ * expression the caller meant to supply.
55
+ */
56
+ export type ColumnExpr = (column: string) => string | undefined;
57
+ /**
58
+ * Build a `to_char({alias}.{column} AT TIME ZONE 'UTC',
59
+ * 'YYYY-MM-DD"T"HH24:MI:SS"Z"')` fragment for embedding in a SELECT
60
+ * projection, via a `ColumnExpr` override.
61
+ *
62
+ * The format literal is the spine's canonical timestamp wire shape —
63
+ * second-precision UTC ISO-8601, exactly 20 characters — byte-identical to
64
+ * `fuz_db::iso8601_timestamp_column`, so the two backends serialize the same
65
+ * instant to the same bytes. `to_iso8601_seconds` (`timestamp.ts`) is the
66
+ * same shape for stamps minted in TS rather than read from Postgres.
67
+ *
68
+ * @param alias - the table alias or name to qualify the column with, or the
69
+ * empty string for an unqualified reference
70
+ * @param column - the timestamp column name
71
+ * @returns the `to_char(…)` fragment
72
+ */
73
+ export declare const iso8601_timestamp_column: (alias: string, column: string) => string;
74
+ /**
75
+ * Build a table's timestamp override: a `ColumnExpr` factory that projects
76
+ * each of `timestamp_columns` through `iso8601_timestamp_column` and declines
77
+ * every other column. The result takes the row qualifier — the empty string
78
+ * for a bare-table read, the alias for a JOIN — so one definition per query
79
+ * module serves every read in it.
80
+ *
81
+ * `timestamp_columns` is typed against `columns` (and checked at build time),
82
+ * so a misspelled name is a compile error and a stale one throws at module
83
+ * load rather than silently leaving a column projected as a `Date`.
84
+ *
85
+ * @param columns - the table's `*_COLUMNS` const
86
+ * @param timestamp_columns - the members of `columns` that carry timestamps
87
+ * @returns `(alias) => ColumnExpr`
88
+ * @throws Error when a name in `timestamp_columns` is not in `columns`
89
+ */
90
+ export declare const iso8601_timestamp_expr: <T extends string>(columns: ReadonlyArray<T>, timestamp_columns: ReadonlyArray<NoInfer<T>>) => ((alias: string) => ColumnExpr);
29
91
  /**
30
92
  * Render a `*_COLUMNS` const as a SQL select list, in projection order.
31
93
  *
32
94
  * @param columns - the column names
95
+ * @param expr - optional per-column expression override; an overridden
96
+ * column is aliased back to its own name
33
97
  * @returns `a, b, c`
34
98
  */
35
- export declare const columns_sql: (columns: ReadonlyArray<string>) => string;
99
+ export declare const columns_sql: (columns: ReadonlyArray<string>, expr?: ColumnExpr) => string;
36
100
  /**
37
101
  * Render a `*_COLUMNS` const as a SQL select list with every column qualified
38
102
  * by a table alias, for reads that alias the table (JOINs, the `c`-aliased
@@ -43,12 +107,25 @@ export declare const columns_sql: (columns: ReadonlyArray<string>) => string;
43
107
  * alias through gets a thrown error instead of a malformed (or injectable)
44
108
  * projection.
45
109
  *
110
+ * **An `expr` override costs one rule: an `ORDER BY` in a projected SELECT
111
+ * must qualify its column.** An override is aliased back to its own name
112
+ * (`… AS created_at`), and Postgres resolves a bare `ORDER BY created_at`
113
+ * against the *output* names first, so it would sort on the `to_char(…)`
114
+ * text — collapsing sub-second order into ties and losing the btree index (a
115
+ * `Sort` node over the expression instead of an `Index Scan`). Writing
116
+ * `ORDER BY <alias>.created_at` (or `<table>.created_at`) names the input
117
+ * column and both go away. The Rust twin needs no such rule: it decodes
118
+ * positionally, so `fuz_db::qualify_columns` emits no alias and its bare
119
+ * `ORDER BY created_at` already means the column.
120
+ *
46
121
  * @param columns - the column names
47
122
  * @param alias - the table alias or name to prefix each column with
123
+ * @param expr - optional per-column expression override; an overridden
124
+ * column is aliased back to its own name
48
125
  * @returns `alias.a, alias.b, alias.c`
49
126
  * @throws Error when `alias` is not a valid SQL identifier
50
127
  */
51
- export declare const qualify_columns: (columns: ReadonlyArray<string>, alias: string) => string;
128
+ export declare const qualify_columns: (columns: ReadonlyArray<string>, alias: string, expr?: ColumnExpr) => string;
52
129
  /**
53
130
  * Drop named columns from a `*_COLUMNS` const, preserving projection order —
54
131
  * for a read that deliberately narrows the row (a client-safe listing, a
@@ -1 +1 @@
1
- {"version":3,"file":"sql_columns.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/sql_columns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAIH;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,SAAS,aAAa,CAAC,MAAM,CAAC,KAAG,MAA4B,CAAC;AAE1F;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,eAAe,GAAI,SAAS,aAAa,CAAC,MAAM,CAAC,EAAE,OAAO,MAAM,KAAG,MAG/E,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,YAAY,GAAI,CAAC,SAAS,MAAM,EAC5C,SAAS,aAAa,CAAC,CAAC,CAAC,EACzB,GAAG,SAAS,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAC3B,aAAa,CAAC,CAAC,CAOjB,CAAC"}
1
+ {"version":3,"file":"sql_columns.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/sql_columns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAIH;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;AAEhE;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,wBAAwB,GAAI,OAAO,MAAM,EAAE,QAAQ,MAAM,KAAG,MAGS,CAAC;AAEnF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,sBAAsB,GAAI,CAAC,SAAS,MAAM,EACtD,SAAS,aAAa,CAAC,CAAC,CAAC,EACzB,mBAAmB,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAC1C,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,UAAU,CAYhC,CAAC;AAqBF;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,GAAI,SAAS,aAAa,CAAC,MAAM,CAAC,EAAE,OAAO,UAAU,KAAG,MACvB,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,eAAe,GAC3B,SAAS,aAAa,CAAC,MAAM,CAAC,EAC9B,OAAO,MAAM,EACb,OAAO,UAAU,KACf,MAGF,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,YAAY,GAAI,CAAC,SAAS,MAAM,EAC5C,SAAS,aAAa,CAAC,CAAC,CAAC,EACzB,GAAG,SAAS,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAC3B,aAAa,CAAC,CAAC,CAOjB,CAAC"}
@@ -10,7 +10,16 @@
10
10
  * client-safe subset — rather than spelling a second column list, so the
11
11
  * drift guard on the base const (`assert_columns_match_live` in
12
12
  * `testing/db.ts`) covers every derived projection too. Twin of the Rust
13
- * spine's `*_COLUMNS` consts + `fuz_db::qualify_columns`.
13
+ * spine's `*_COLUMNS` consts + `fuz_db::qualify_columns` / `omit_columns`.
14
+ *
15
+ * A read that needs a column rendered as something other than the bare
16
+ * reference passes a `ColumnExpr` — the `expr` override, twin of the third
17
+ * argument to `fuz_db::qualify_columns`. Every timestamp that reaches the
18
+ * wire projects through `iso8601_timestamp_column` so both spines serialize
19
+ * the same instant to the same bytes; each query module builds its override
20
+ * with `iso8601_timestamp_expr` over its table const. An override costs one
21
+ * rule — a projected `ORDER BY` must qualify its column — stated on
22
+ * `qualify_columns`.
14
23
  *
15
24
  * Placement rule: a const lives in its table's query module. When two query
16
25
  * modules project the same table (the revoke cascades in
@@ -27,13 +36,78 @@
27
36
  * @module
28
37
  */
29
38
  import { assert_valid_sql_identifier } from './sql_identifier.js';
39
+ /**
40
+ * Build a `to_char({alias}.{column} AT TIME ZONE 'UTC',
41
+ * 'YYYY-MM-DD"T"HH24:MI:SS"Z"')` fragment for embedding in a SELECT
42
+ * projection, via a `ColumnExpr` override.
43
+ *
44
+ * The format literal is the spine's canonical timestamp wire shape —
45
+ * second-precision UTC ISO-8601, exactly 20 characters — byte-identical to
46
+ * `fuz_db::iso8601_timestamp_column`, so the two backends serialize the same
47
+ * instant to the same bytes. `to_iso8601_seconds` (`timestamp.ts`) is the
48
+ * same shape for stamps minted in TS rather than read from Postgres.
49
+ *
50
+ * @param alias - the table alias or name to qualify the column with, or the
51
+ * empty string for an unqualified reference
52
+ * @param column - the timestamp column name
53
+ * @returns the `to_char(…)` fragment
54
+ */
55
+ export const iso8601_timestamp_column = (alias, column) => alias === ''
56
+ ? `to_char(${column} AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')`
57
+ : `to_char(${alias}.${column} AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS"Z"')`;
58
+ /**
59
+ * Build a table's timestamp override: a `ColumnExpr` factory that projects
60
+ * each of `timestamp_columns` through `iso8601_timestamp_column` and declines
61
+ * every other column. The result takes the row qualifier — the empty string
62
+ * for a bare-table read, the alias for a JOIN — so one definition per query
63
+ * module serves every read in it.
64
+ *
65
+ * `timestamp_columns` is typed against `columns` (and checked at build time),
66
+ * so a misspelled name is a compile error and a stale one throws at module
67
+ * load rather than silently leaving a column projected as a `Date`.
68
+ *
69
+ * @param columns - the table's `*_COLUMNS` const
70
+ * @param timestamp_columns - the members of `columns` that carry timestamps
71
+ * @returns `(alias) => ColumnExpr`
72
+ * @throws Error when a name in `timestamp_columns` is not in `columns`
73
+ */
74
+ export const iso8601_timestamp_expr = (columns, timestamp_columns) => {
75
+ for (const t of timestamp_columns) {
76
+ if (!columns.includes(t)) {
77
+ throw new Error(`iso8601_timestamp_expr: "${t}" is not in the column list "${columns.join(', ')}"`);
78
+ }
79
+ }
80
+ return (alias) => (column) => timestamp_columns.includes(column)
81
+ ? iso8601_timestamp_column(alias, column)
82
+ : undefined;
83
+ };
84
+ /**
85
+ * Render one projection entry — the `expr` override aliased back to its
86
+ * column name, or `fallback` when the override declines the column
87
+ * (`undefined`, and only `undefined` — see `ColumnExpr`).
88
+ *
89
+ * The alias this adds is what makes the `ORDER BY` rule on `qualify_columns`
90
+ * necessary.
91
+ *
92
+ * @param column - the column name, and the output name an override is aliased to
93
+ * @param fallback - what to project when the override declines — the bare
94
+ * column, or the alias-qualified reference
95
+ * @param expr - the per-column expression override, if the read has one
96
+ * @returns the projection entry, `<expr> AS <column>` or `fallback`
97
+ */
98
+ const render_column = (column, fallback, expr) => {
99
+ const override = expr?.(column);
100
+ return override === undefined ? fallback : `${override} AS ${column}`;
101
+ };
30
102
  /**
31
103
  * Render a `*_COLUMNS` const as a SQL select list, in projection order.
32
104
  *
33
105
  * @param columns - the column names
106
+ * @param expr - optional per-column expression override; an overridden
107
+ * column is aliased back to its own name
34
108
  * @returns `a, b, c`
35
109
  */
36
- export const columns_sql = (columns) => columns.join(', ');
110
+ export const columns_sql = (columns, expr) => columns.map((c) => render_column(c, c, expr)).join(', ');
37
111
  /**
38
112
  * Render a `*_COLUMNS` const as a SQL select list with every column qualified
39
113
  * by a table alias, for reads that alias the table (JOINs, the `c`-aliased
@@ -44,14 +118,27 @@ export const columns_sql = (columns) => columns.join(', ');
44
118
  * alias through gets a thrown error instead of a malformed (or injectable)
45
119
  * projection.
46
120
  *
121
+ * **An `expr` override costs one rule: an `ORDER BY` in a projected SELECT
122
+ * must qualify its column.** An override is aliased back to its own name
123
+ * (`… AS created_at`), and Postgres resolves a bare `ORDER BY created_at`
124
+ * against the *output* names first, so it would sort on the `to_char(…)`
125
+ * text — collapsing sub-second order into ties and losing the btree index (a
126
+ * `Sort` node over the expression instead of an `Index Scan`). Writing
127
+ * `ORDER BY <alias>.created_at` (or `<table>.created_at`) names the input
128
+ * column and both go away. The Rust twin needs no such rule: it decodes
129
+ * positionally, so `fuz_db::qualify_columns` emits no alias and its bare
130
+ * `ORDER BY created_at` already means the column.
131
+ *
47
132
  * @param columns - the column names
48
133
  * @param alias - the table alias or name to prefix each column with
134
+ * @param expr - optional per-column expression override; an overridden
135
+ * column is aliased back to its own name
49
136
  * @returns `alias.a, alias.b, alias.c`
50
137
  * @throws Error when `alias` is not a valid SQL identifier
51
138
  */
52
- export const qualify_columns = (columns, alias) => {
139
+ export const qualify_columns = (columns, alias, expr) => {
53
140
  assert_valid_sql_identifier(alias);
54
- return columns_sql(columns.map((c) => `${alias}.${c}`));
141
+ return columns.map((c) => render_column(c, `${alias}.${c}`, expr)).join(', ');
55
142
  };
56
143
  /**
57
144
  * Drop named columns from a `*_COLUMNS` const, preserving projection order —
@@ -107,7 +107,9 @@ wrapper). See `auth/signup_routes.ts`.
107
107
 
108
108
  **Ordering: 401 → authz → 403 → 400 → handler.** Mirrors the RPC dispatcher
109
109
  (`actions/action_rpc.ts`) so HTTP RPC and REST fail with the same priority, and
110
- matches the Rust spine, which validates handler-side. Body validation runs last
110
+ matches the Rust spine, which validates handler-side — with the one difference
111
+ that the dispatcher carries a per-action rate-limit phase (429) between 403 and
112
+ 400 that REST has no equivalent of. Body validation runs last
111
113
  so a caller the authority gates refuse learns nothing about the route's input
112
114
  shape — a 400 would confirm the route exists and describe how to call it.
113
115
  Params and query still validate first: they are part of addressing the route,
@@ -262,11 +264,11 @@ the source specs, for tests that need to iterate over raw specs.
262
264
  No side effects, no state — filters and groupings over `AppSurface`:
263
265
 
264
266
  - `filter_protected_routes` / `filter_public_routes`
265
- - `filter_role_routes` / `filter_authenticated_routes` / `filter_keeper_routes` / `filter_routes_for_role(role)`
267
+ - `filter_role_routes` / `filter_authenticated_routes` / `filter_keeper_routes` / `filter_credential_gated_routes` (any `auth.credential_types` gate — superset of keeper) / `filter_routes_for_role(role)`
266
268
  - `filter_routes_by_prefix(prefix)` / `filter_routes_with_input` / `filter_routes_with_params` / `filter_routes_with_query` / `filter_mutation_routes` / `filter_rate_limited_routes`
267
- - `routes_by_auth_type(surface)` — `Map<RouteAuthCategory, Array<AppSurfaceRoute>>` where `RouteAuthCategory = 'none' | 'authenticated' | 'optional' | 'keeper' | 'role:<name>' | 'other'`. Multi-role specs appear under each role bucket
269
+ - `routes_by_auth_type(surface)` — `Map<RouteAuthCategory, Array<AppSurfaceRoute>>` where `RouteAuthCategory = 'none' | 'authenticated' | 'optional' | 'keeper' | 'role:<name>' | 'credential:<type>' | 'other'`. Multi-role / multi-channel specs appear under each bucket. Keeper is tested before the role bucket and the general credential bucket after it — keeper is an authority _level_ and names the route, a session gate is a _channel_ restriction over the authority the route already declares (so the audit SSE stream buckets as `role:admin`)
268
270
  - `format_route_key(route)` → `'METHOD /path'`
269
- - `surface_auth_summary(surface)` — counts per auth type, roles broken out by name
271
+ - `surface_auth_summary(surface)` — counts per auth type, roles and credential channels broken out by name; buckets exactly as `routes_by_auth_type`
270
272
 
271
273
  The per-route auth predicates these filters compose over (`is_public_auth`,
272
274
  `is_role_auth`, `is_credential_gated_auth`, `is_keeper_auth`,
@@ -189,16 +189,29 @@ export declare const is_role_auth: (auth: RouteAuth) => boolean;
189
189
  /** True iff the route declares any credential-type gate (`auth.credential_types?.length`). */
190
190
  export declare const is_credential_gated_auth: (auth: RouteAuth) => boolean;
191
191
  /**
192
- * True iff the route is the keeper bucket — credential gate restricted
193
- * to `daemon_token`. Keeper is the only credential gate today; if more
194
- * land, this filter widens. Knows the `'daemon_token'` literal directly
195
- * (the keeper composition is fuz_app's only registered credential gate).
192
+ * True iff the route is the keeper bucket — a credential gate admitting
193
+ * `daemon_token`. Knows the literal directly, because keeper is a named
194
+ * authority level rather than one channel among many: it is the ceiling
195
+ * sessions and api tokens cannot reach.
196
+ *
197
+ * Narrower than `is_credential_gated_auth`, which is true of every channel
198
+ * gate — including the session-only gates pointing the other way (the
199
+ * credential-minting actions, `POST /logout` / `POST /password`, the audit
200
+ * SSE stream). Reach for the general predicate when the question is "does a
201
+ * channel gate answer here"; reach for this one only when the question is
202
+ * about keeper specifically.
196
203
  */
197
204
  export declare const is_keeper_auth: (auth: RouteAuth) => boolean;
198
205
  /**
199
206
  * True iff the route is plain authenticated — `account === 'required'`
200
- * with no role gate and no credential gate. Account-grain authenticated
201
- * routes (logout, password change, account self-service) fall here.
207
+ * with no role gate and no credential gate: any credential the auth
208
+ * middleware resolved gets in.
209
+ *
210
+ * A channel gate takes a route out of this bucket, which is why the
211
+ * account-grain routes one might expect here are not — `POST /logout`,
212
+ * `POST /password`, and the `account_*` credential-lifecycle actions all
213
+ * declare `credential_types: ['session']` and answer to
214
+ * `is_credential_gated_auth` instead.
202
215
  */
203
216
  export declare const is_plain_authenticated_auth: (auth: RouteAuth) => boolean;
204
217
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"auth_shape.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/auth_shape.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,WAAW,6DAGtB,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,OAAO,KAAG,MAAM,GAAG,SAGtD,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,aAAa;;;;EAA2C,CAAC;AACtE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;IAMpB;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;;kBAyCF,CAAC;AACJ,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC;AAUlD;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,SAAS,KAAG,OACA,CAAC;AAElD;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,SAAS,KAAG,OAAgC,CAAC;AAE/E;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,KAAG,OAAkC,CAAC;AAEnF,wEAAwE;AACxE,eAAO,MAAM,YAAY,GAAI,MAAM,SAAS,KAAG,OAA+B,CAAC;AAE/E,8FAA8F;AAC9F,eAAO,MAAM,wBAAwB,GAAI,MAAM,SAAS,KAAG,OAC3B,CAAC;AAEjC;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,SAAS,KAAG,OACQ,CAAC;AAE1D;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,GAAI,MAAM,SAAS,KAAG,OACwB,CAAC;AAYvF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OAIhE,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,sCAAsC,GAClD,MAAM,SAAS,EACf,OAAO,WAAW,EAClB,SAAS,MAAM,KACb,IAmBF,CAAC"}
1
+ {"version":3,"file":"auth_shape.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/auth_shape.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,WAAW,6DAGtB,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,OAAO,KAAG,MAAM,GAAG,SAGtD,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,aAAa;;;;EAA2C,CAAC;AACtE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;IAMpB;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;;kBAyCF,CAAC;AACJ,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC;AAUlD;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,SAAS,KAAG,OACA,CAAC;AAElD;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,SAAS,KAAG,OAAgC,CAAC;AAE/E;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,KAAG,OAAkC,CAAC;AAEnF,wEAAwE;AACxE,eAAO,MAAM,YAAY,GAAI,MAAM,SAAS,KAAG,OAA+B,CAAC;AAE/E,8FAA8F;AAC9F,eAAO,MAAM,wBAAwB,GAAI,MAAM,SAAS,KAAG,OAC3B,CAAC;AAEjC;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,SAAS,KAAG,OACQ,CAAC;AAE1D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,2BAA2B,GAAI,MAAM,SAAS,KAAG,OACwB,CAAC;AAYvF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OAIhE,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,sCAAsC,GAClD,MAAM,SAAS,EACf,OAAO,WAAW,EAClB,SAAS,MAAM,KACb,IAmBF,CAAC"}
@@ -224,16 +224,29 @@ export const is_role_auth = (auth) => !!auth.roles?.length;
224
224
  /** True iff the route declares any credential-type gate (`auth.credential_types?.length`). */
225
225
  export const is_credential_gated_auth = (auth) => !!auth.credential_types?.length;
226
226
  /**
227
- * True iff the route is the keeper bucket — credential gate restricted
228
- * to `daemon_token`. Keeper is the only credential gate today; if more
229
- * land, this filter widens. Knows the `'daemon_token'` literal directly
230
- * (the keeper composition is fuz_app's only registered credential gate).
227
+ * True iff the route is the keeper bucket — a credential gate admitting
228
+ * `daemon_token`. Knows the literal directly, because keeper is a named
229
+ * authority level rather than one channel among many: it is the ceiling
230
+ * sessions and api tokens cannot reach.
231
+ *
232
+ * Narrower than `is_credential_gated_auth`, which is true of every channel
233
+ * gate — including the session-only gates pointing the other way (the
234
+ * credential-minting actions, `POST /logout` / `POST /password`, the audit
235
+ * SSE stream). Reach for the general predicate when the question is "does a
236
+ * channel gate answer here"; reach for this one only when the question is
237
+ * about keeper specifically.
231
238
  */
232
239
  export const is_keeper_auth = (auth) => auth.credential_types?.includes('daemon_token') ?? false;
233
240
  /**
234
241
  * True iff the route is plain authenticated — `account === 'required'`
235
- * with no role gate and no credential gate. Account-grain authenticated
236
- * routes (logout, password change, account self-service) fall here.
242
+ * with no role gate and no credential gate: any credential the auth
243
+ * middleware resolved gets in.
244
+ *
245
+ * A channel gate takes a route out of this bucket, which is why the
246
+ * account-grain routes one might expect here are not — `POST /logout`,
247
+ * `POST /password`, and the `account_*` credential-lifecycle actions all
248
+ * declare `credential_types: ['session']` and answer to
249
+ * `is_credential_gated_auth` instead.
237
250
  */
238
251
  export const is_plain_authenticated_auth = (auth) => auth.account === 'required' && !is_role_auth(auth) && !is_credential_gated_auth(auth);
239
252
  // --- Registry-time invariant 2 enforcement ---
@@ -43,6 +43,19 @@ export declare const is_jsonrpc_error_response: (message: unknown) => message is
43
43
  * Returns `null` if no valid id can be extracted.
44
44
  */
45
45
  export declare const to_jsonrpc_message_id: (message_or_id: unknown) => JsonrpcRequestId | null;
46
+ /**
47
+ * The id an error response to a rejected envelope can echo: the frame's
48
+ * `id` when the frame is an object carrying a valid one, else `null`.
49
+ *
50
+ * Unlike `to_jsonrpc_message_id`, a scalar frame (`5`, `"x"`) is never read
51
+ * as its own id — a non-object body has no id to echo, which is the Rust
52
+ * twin's `classify` answer too. Both transports use this for the
53
+ * `invalid_request` reply.
54
+ *
55
+ * @param frame - the parsed body, of any shape
56
+ * @returns the echoable id, or `null`
57
+ */
58
+ export declare const to_jsonrpc_envelope_id: (frame: unknown) => JsonrpcRequestId | null;
46
59
  /**
47
60
  * Normalizes input to JSON-RPC params format.
48
61
  * Returns `undefined` for null/undefined, wraps primitives in `{value}`.
@@ -1 +1 @@
1
- {"version":3,"file":"jsonrpc_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/jsonrpc_helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,EACN,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,cAAc,EAEnB,eAAe,EACf,MAAM,cAAc,CAAC;AAKtB,0CAA0C;AAC1C,eAAO,MAAM,sBAAsB,GAClC,QAAQ,aAAa,EACrB,QAAQ,oBAAoB,GAAG,SAAS,EACxC,IAAI,gBAAgB,KAClB,cAUF,CAAC;AAEF,mDAAmD;AACnD,eAAO,MAAM,uBAAuB,GACnC,IAAI,gBAAgB,EACpB,QAAQ,aAAa,KACnB,eAID,CAAC;AAEH,6EAA6E;AAC7E,eAAO,MAAM,2BAA2B,GACvC,QAAQ,aAAa,EACrB,QAAQ,yBAAyB,GAAG,SAAS,KAC3C,mBASF,CAAC;AAEF,iDAAiD;AACjD,eAAO,MAAM,6BAA6B,GACzC,IAAI,oBAAoB,CAAC,IAAI,CAAC,EAC9B,OAAO,oBAAoB,CAAC,OAAO,CAAC,KAClC,oBAID,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,yCAAyC,GACrD,IAAI,gBAAgB,GAAG,IAAI,EAC3B,OAAO,OAAO,KACZ,oBAyBF,CAAC;AAIF,kFAAkF;AAClF,eAAO,MAAM,qBAAqB,GAAI,IAAI,OAAO,KAAG,EAAE,IAAI,gBAGzD,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,iBAAiB,GAC7B,SAAS,OAAO,KACd,OAAO,IAAI;IAAE,OAAO,EAAE,OAAO,eAAe,CAAA;CAIF,CAAC;AAE9C,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,GAC9B,SAAS,OAAO,KACd,OAAO,IAAI,cAAc,GAAG,KAAK,CAAC,cAAc,CAGrB,CAAC;AAE/B,iEAAiE;AACjE,eAAO,MAAM,kBAAkB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,cACI,CAAC;AAEtE,wEAAwE;AACxE,eAAO,MAAM,uBAAuB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,mBACE,CAAC;AAEzE,0EAA0E;AAC1E,eAAO,MAAM,mBAAmB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,eACG,CAAC;AAEtE,uEAAuE;AACvE,eAAO,MAAM,yBAAyB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,oBACJ,CAAC;AAIrE;;;GAGG;AACH,eAAO,MAAM,qBAAqB,GAAI,eAAe,OAAO,KAAG,gBAAgB,GAAG,IAOjF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,OAAO,KAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAQxE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,QAAQ,OAAO,KAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAQrE,CAAC"}
1
+ {"version":3,"file":"jsonrpc_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/jsonrpc_helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,EACN,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,cAAc,EAEnB,eAAe,EACf,MAAM,cAAc,CAAC;AAKtB,0CAA0C;AAC1C,eAAO,MAAM,sBAAsB,GAClC,QAAQ,aAAa,EACrB,QAAQ,oBAAoB,GAAG,SAAS,EACxC,IAAI,gBAAgB,KAClB,cAUF,CAAC;AAEF,mDAAmD;AACnD,eAAO,MAAM,uBAAuB,GACnC,IAAI,gBAAgB,EACpB,QAAQ,aAAa,KACnB,eAID,CAAC;AAEH,6EAA6E;AAC7E,eAAO,MAAM,2BAA2B,GACvC,QAAQ,aAAa,EACrB,QAAQ,yBAAyB,GAAG,SAAS,KAC3C,mBASF,CAAC;AAEF,iDAAiD;AACjD,eAAO,MAAM,6BAA6B,GACzC,IAAI,oBAAoB,CAAC,IAAI,CAAC,EAC9B,OAAO,oBAAoB,CAAC,OAAO,CAAC,KAClC,oBAID,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,yCAAyC,GACrD,IAAI,gBAAgB,GAAG,IAAI,EAC3B,OAAO,OAAO,KACZ,oBAyBF,CAAC;AAIF,kFAAkF;AAClF,eAAO,MAAM,qBAAqB,GAAI,IAAI,OAAO,KAAG,EAAE,IAAI,gBAGzD,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,iBAAiB,GAC7B,SAAS,OAAO,KACd,OAAO,IAAI;IAAE,OAAO,EAAE,OAAO,eAAe,CAAA;CAIF,CAAC;AAE9C,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,GAC9B,SAAS,OAAO,KACd,OAAO,IAAI,cAAc,GAAG,KAAK,CAAC,cAAc,CAGrB,CAAC;AAE/B,iEAAiE;AACjE,eAAO,MAAM,kBAAkB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,cACI,CAAC;AAEtE,wEAAwE;AACxE,eAAO,MAAM,uBAAuB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,mBACE,CAAC;AAEzE,0EAA0E;AAC1E,eAAO,MAAM,mBAAmB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,eACG,CAAC;AAEtE,uEAAuE;AACvE,eAAO,MAAM,yBAAyB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,oBACJ,CAAC;AAIrE;;;GAGG;AACH,eAAO,MAAM,qBAAqB,GAAI,eAAe,OAAO,KAAG,gBAAgB,GAAG,IAOjF,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,GAAI,OAAO,OAAO,KAAG,gBAAgB,GAAG,IACX,CAAC;AAEjE;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,OAAO,KAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAQxE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,QAAQ,OAAO,KAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAQrE,CAAC"}
@@ -110,6 +110,19 @@ export const to_jsonrpc_message_id = (message_or_id) => {
110
110
  const maybe_id = typeof message_or_id === 'object' ? message_or_id.id : message_or_id;
111
111
  return is_jsonrpc_request_id(maybe_id) ? maybe_id : null;
112
112
  };
113
+ /**
114
+ * The id an error response to a rejected envelope can echo: the frame's
115
+ * `id` when the frame is an object carrying a valid one, else `null`.
116
+ *
117
+ * Unlike `to_jsonrpc_message_id`, a scalar frame (`5`, `"x"`) is never read
118
+ * as its own id — a non-object body has no id to echo, which is the Rust
119
+ * twin's `classify` answer too. Both transports use this for the
120
+ * `invalid_request` reply.
121
+ *
122
+ * @param frame - the parsed body, of any shape
123
+ * @returns the echoable id, or `null`
124
+ */
125
+ export const to_jsonrpc_envelope_id = (frame) => typeof frame === 'object' ? to_jsonrpc_message_id(frame) : null;
113
126
  /**
114
127
  * Normalizes input to JSON-RPC params format.
115
128
  * Returns `undefined` for null/undefined, wraps primitives in `{value}`.
@@ -27,6 +27,17 @@ export declare const filter_role_routes: (surface: AppSurface) => Array<AppSurfa
27
27
  export declare const filter_authenticated_routes: (surface: AppSurface) => Array<AppSurfaceRoute>;
28
28
  /** Filter routes that require keeper credentials (`daemon_token`). */
29
29
  export declare const filter_keeper_routes: (surface: AppSurface) => Array<AppSurfaceRoute>;
30
+ /**
31
+ * Filter routes gated to a credential channel (any non-empty
32
+ * `auth.credential_types`), whichever channel that is.
33
+ *
34
+ * Superset of `filter_keeper_routes`: keeper is the `daemon_token` gate, and
35
+ * the same axis points the other way on the session-only routes (the
36
+ * credential-minting actions, `POST /logout` / `POST /password`, the audit
37
+ * SSE stream). Adversarial runners need the whole bucket, since every route
38
+ * in it answers `credential_type_required` to a channel it does not admit.
39
+ */
40
+ export declare const filter_credential_gated_routes: (surface: AppSurface) => Array<AppSurfaceRoute>;
30
41
  /** Filter routes whose `auth.roles` includes the named role. */
31
42
  export declare const filter_routes_for_role: (surface: AppSurface, role: string) => Array<AppSurfaceRoute>;
32
43
  /**
@@ -38,11 +49,22 @@ export declare const filter_routes_for_role: (surface: AppSurface, role: string)
38
49
  * - `'role:<name>'` for each role declared on `auth.roles` (multi-role specs
39
50
  * are emitted multiple times; callers that need single-bucket grouping
40
51
  * should pre-collapse)
52
+ * - `'credential:<type>'` for each channel a role-less route's
53
+ * `credential_types` admits — the session-only gates (`POST /logout`,
54
+ * `POST /password`, the `account_*` credential-lifecycle actions), plus any
55
+ * channel a consumer registers
41
56
  * - `'authenticated'` for `account === 'required'` without role / credential gate
42
57
  * - `'optional'` when either axis is `'optional'` and no other bucket fits
43
58
  * - `'other'` as a last-resort bucket for shapes that don't match above
59
+ *
60
+ * Keeper is tested before the role bucket and the general credential bucket
61
+ * after it, which is not an inconsistency: keeper is an authority *level* — a
62
+ * ceiling no session or api token reaches — so it names the route, while a
63
+ * session gate is a *channel* restriction layered over whatever authority the
64
+ * route already declares. So the audit SSE stream, gated to the session
65
+ * channel and to the admin role, buckets as `role:admin`.
44
66
  */
45
- export type RouteAuthCategory = 'none' | 'authenticated' | 'optional' | 'keeper' | `role:${string}` | 'other';
67
+ export type RouteAuthCategory = 'none' | 'authenticated' | 'optional' | 'keeper' | `role:${string}` | `credential:${string}` | 'other';
46
68
  /**
47
69
  * Group routes by auth category (see `RouteAuthCategory`). Multi-role specs
48
70
  * appear under each of their role buckets.
@@ -65,16 +87,19 @@ export declare const format_route_key: (route: AppSurfaceRoute) => string;
65
87
  /**
66
88
  * Summarize route auth distribution across the surface.
67
89
  *
68
- * Categorical view over the flat auth record. Multi-role specs
69
- * contribute one count per role they admit.
90
+ * Categorical view over the flat auth record, bucketing exactly as
91
+ * `routes_by_auth_type`. Multi-role and multi-channel specs contribute one
92
+ * count per role / channel they admit.
70
93
  *
71
- * @returns counts by auth category, with role counts broken out by role name
94
+ * @returns counts by auth category, with role and channel counts broken out
95
+ * by name
72
96
  */
73
97
  export declare const surface_auth_summary: (surface: AppSurface) => {
74
98
  none: number;
75
99
  authenticated: number;
76
100
  optional: number;
77
101
  role: Map<string, number>;
102
+ credential: Map<string, number>;
78
103
  keeper: number;
79
104
  other: number;
80
105
  };
@@ -1 +1 @@
1
- {"version":3,"file":"surface_query.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/surface_query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAQhE,6DAA6D;AAC7D,eAAO,MAAM,uBAAuB,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CAC7B,CAAC;AAEvD,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CAC3B,CAAC;AAEtD,0EAA0E;AAC1E,eAAO,MAAM,kBAAkB,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CAC3B,CAAC;AAEpD;;;GAGG;AACH,eAAO,MAAM,2BAA2B,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CACrB,CAAC;AAEnE,sEAAsE;AACtE,eAAO,MAAM,oBAAoB,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CAC3B,CAAC;AAEtD,gEAAgE;AAChE,eAAO,MAAM,sBAAsB,GAAI,SAAS,UAAU,EAAE,MAAM,MAAM,KAAG,KAAK,CAAC,eAAe,CAC5B,CAAC;AAErE;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,iBAAiB,GAC5B,MAAM,GAAG,eAAe,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,MAAM,EAAE,GAAG,OAAO,CAAC;AAE/E;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,UAAU,KAAG,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,eAAe,CAAC,CAmC3F,CAAC;AAEF,qDAAqD;AACrD,eAAO,MAAM,uBAAuB,GACnC,SAAS,UAAU,EACnB,QAAQ,MAAM,KACZ,KAAK,CAAC,eAAe,CAA4D,CAAC;AAErF,uDAAuD;AACvD,eAAO,MAAM,wBAAwB,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CAC9B,CAAC;AAEvD,wDAAwD;AACxD,eAAO,MAAM,yBAAyB,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CAC9B,CAAC;AAExD,uDAAuD;AACvD,eAAO,MAAM,wBAAwB,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CAC9B,CAAC;AAEvD,mEAAmE;AACnE,eAAO,MAAM,sBAAsB,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CACtC,CAAC;AAE7C,gDAAgD;AAChD,eAAO,MAAM,0BAA0B,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CAC9B,CAAC;AAEzD,iEAAiE;AACjE,eAAO,MAAM,gBAAgB,GAAI,OAAO,eAAe,KAAG,MAAyC,CAAC;AAEpG;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAChC,SAAS,UAAU,KACjB;IACF,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CAqCd,CAAC"}
1
+ {"version":3,"file":"surface_query.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/surface_query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAShE,6DAA6D;AAC7D,eAAO,MAAM,uBAAuB,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CAC7B,CAAC;AAEvD,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CAC3B,CAAC;AAEtD,0EAA0E;AAC1E,eAAO,MAAM,kBAAkB,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CAC3B,CAAC;AAEpD;;;GAGG;AACH,eAAO,MAAM,2BAA2B,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CACrB,CAAC;AAEnE,sEAAsE;AACtE,eAAO,MAAM,oBAAoB,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CAC3B,CAAC;AAEtD;;;;;;;;;GASG;AACH,eAAO,MAAM,8BAA8B,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CAC3B,CAAC;AAEhE,gEAAgE;AAChE,eAAO,MAAM,sBAAsB,GAAI,SAAS,UAAU,EAAE,MAAM,MAAM,KAAG,KAAK,CAAC,eAAe,CAC5B,CAAC;AAErE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,MAAM,iBAAiB,GAC1B,MAAM,GACN,eAAe,GACf,UAAU,GACV,QAAQ,GACR,QAAQ,MAAM,EAAE,GAChB,cAAc,MAAM,EAAE,GACtB,OAAO,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,UAAU,KAAG,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,eAAe,CAAC,CAwC3F,CAAC;AAEF,qDAAqD;AACrD,eAAO,MAAM,uBAAuB,GACnC,SAAS,UAAU,EACnB,QAAQ,MAAM,KACZ,KAAK,CAAC,eAAe,CAA4D,CAAC;AAErF,uDAAuD;AACvD,eAAO,MAAM,wBAAwB,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CAC9B,CAAC;AAEvD,wDAAwD;AACxD,eAAO,MAAM,yBAAyB,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CAC9B,CAAC;AAExD,uDAAuD;AACvD,eAAO,MAAM,wBAAwB,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CAC9B,CAAC;AAEvD,mEAAmE;AACnE,eAAO,MAAM,sBAAsB,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CACtC,CAAC;AAE7C,gDAAgD;AAChD,eAAO,MAAM,0BAA0B,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CAC9B,CAAC;AAEzD,iEAAiE;AACjE,eAAO,MAAM,gBAAgB,GAAI,OAAO,eAAe,KAAG,MAAyC,CAAC;AAEpG;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,GAChC,SAAS,UAAU,KACjB;IACF,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1B,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CA4Cd,CAAC"}
@@ -13,7 +13,7 @@
13
13
  *
14
14
  * @module
15
15
  */
16
- import { is_keeper_auth, is_plain_authenticated_auth, is_public_auth, is_role_auth } from './auth_shape.js';
16
+ import { is_credential_gated_auth, is_keeper_auth, is_plain_authenticated_auth, is_public_auth, is_role_auth } from './auth_shape.js';
17
17
  /** Filter routes that require any form of authentication. */
18
18
  export const filter_protected_routes = (surface) => surface.routes.filter((r) => !is_public_auth(r.auth));
19
19
  /** Filter routes that are publicly accessible (no auth surface at all). */
@@ -27,6 +27,17 @@ export const filter_role_routes = (surface) => surface.routes.filter((r) => is_r
27
27
  export const filter_authenticated_routes = (surface) => surface.routes.filter((r) => is_plain_authenticated_auth(r.auth));
28
28
  /** Filter routes that require keeper credentials (`daemon_token`). */
29
29
  export const filter_keeper_routes = (surface) => surface.routes.filter((r) => is_keeper_auth(r.auth));
30
+ /**
31
+ * Filter routes gated to a credential channel (any non-empty
32
+ * `auth.credential_types`), whichever channel that is.
33
+ *
34
+ * Superset of `filter_keeper_routes`: keeper is the `daemon_token` gate, and
35
+ * the same axis points the other way on the session-only routes (the
36
+ * credential-minting actions, `POST /logout` / `POST /password`, the audit
37
+ * SSE stream). Adversarial runners need the whole bucket, since every route
38
+ * in it answers `credential_type_required` to a channel it does not admit.
39
+ */
40
+ export const filter_credential_gated_routes = (surface) => surface.routes.filter((r) => is_credential_gated_auth(r.auth));
30
41
  /** Filter routes whose `auth.roles` includes the named role. */
31
42
  export const filter_routes_for_role = (surface, role) => surface.routes.filter((r) => r.auth.roles?.includes(role) ?? false);
32
43
  /**
@@ -58,6 +69,11 @@ export const routes_by_auth_type = (surface) => {
58
69
  push(`role:${role}`, r);
59
70
  continue;
60
71
  }
72
+ if (is_credential_gated_auth(auth)) {
73
+ for (const credential_type of auth.credential_types)
74
+ push(`credential:${credential_type}`, r);
75
+ continue;
76
+ }
61
77
  if (is_plain_authenticated_auth(auth)) {
62
78
  push('authenticated', r);
63
79
  continue;
@@ -87,16 +103,19 @@ export const format_route_key = (route) => `${route.method} ${route.path}`;
87
103
  /**
88
104
  * Summarize route auth distribution across the surface.
89
105
  *
90
- * Categorical view over the flat auth record. Multi-role specs
91
- * contribute one count per role they admit.
106
+ * Categorical view over the flat auth record, bucketing exactly as
107
+ * `routes_by_auth_type`. Multi-role and multi-channel specs contribute one
108
+ * count per role / channel they admit.
92
109
  *
93
- * @returns counts by auth category, with role counts broken out by role name
110
+ * @returns counts by auth category, with role and channel counts broken out
111
+ * by name
94
112
  */
95
113
  export const surface_auth_summary = (surface) => {
96
114
  let none = 0;
97
115
  let authenticated = 0;
98
116
  let optional = 0;
99
117
  const role = new Map();
118
+ const credential = new Map();
100
119
  let keeper = 0;
101
120
  let other = 0;
102
121
  for (const r of surface.routes) {
@@ -115,6 +134,12 @@ export const surface_auth_summary = (surface) => {
115
134
  }
116
135
  continue;
117
136
  }
137
+ if (is_credential_gated_auth(auth)) {
138
+ for (const credential_type of auth.credential_types) {
139
+ credential.set(credential_type, (credential.get(credential_type) ?? 0) + 1);
140
+ }
141
+ continue;
142
+ }
118
143
  if (is_plain_authenticated_auth(auth)) {
119
144
  authenticated++;
120
145
  continue;
@@ -125,5 +150,5 @@ export const surface_auth_summary = (surface) => {
125
150
  }
126
151
  other++;
127
152
  }
128
- return { none, authenticated, optional, role, keeper, other };
153
+ return { none, authenticated, optional, role, credential, keeper, other };
129
154
  };
@@ -31,9 +31,15 @@ export declare const AUDIT_LOG_CHANNEL = "audit_log";
31
31
  * `session_revoke` closes only the stream tied to the specific revoked session
32
32
  * (matched by the blake3 session hash in `event.metadata.session_id`) — closing
33
33
  * all of a user's streams for a single-session revoke would be over-aggressive.
34
- * The single `token_revoke` the WS half handles is omitted: an SSE stream is
35
- * opened under a cookie session, never an API token, so no stream is keyed by a
36
- * single token id.
34
+ * The single `token_revoke` the WS half handles is omitted, and what makes that
35
+ * correct is a gate rather than an assumption: the audit route declares
36
+ * `credential_types: ['session']` (`auth/audit_log_route_schema.ts`), so a
37
+ * bearer is refused at the channel and no stream is ever keyed by a single
38
+ * token id. **A consumer that widens that gate must key its subscribers by
39
+ * api-token id and add `token_revoke` here** — otherwise a revoked token keeps
40
+ * receiving audit rows for the life of the connection. The Rust twin enforces
41
+ * the same pairing structurally, via the `RevocationScope` column on
42
+ * `AUDIT_EVENT_SPECS`.
37
43
  */
38
44
  export declare const disconnect_event_types: ReadonlySet<string>;
39
45
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"sse_auth_guard.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/realtime/sse_auth_guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEtD,OAAO,EAGN,KAAK,aAAa,EAClB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,kBAAkB,EAAE,KAAK,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACrF,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAEtE,2DAA2D;AAC3D,eAAO,MAAM,iBAAiB,cAAc,CAAC;AAE7C;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,sBAAsB,EAAE,WAAW,CAAC,MAAM,CAOrD,CAAC;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,qBAAqB,GAAI,CAAC,EACtC,UAAU,kBAAkB,CAAC,CAAC,CAAC,EAC/B,eAAe,MAAM,GAAG,IAAI,EAC5B,KAAK,MAAM,KACT,CAAC,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CA6CjC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC3B,8FAA8F;IAC9F,SAAS,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,KAAK,MAAM,IAAI,CAAC;IAC1F,kFAAkF;IAClF,GAAG,EAAE,MAAM,CAAC;IACZ,yJAAyJ;IACzJ,cAAc,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/C,yEAAyE;IACzE,QAAQ,EAAE,kBAAkB,CAAC,eAAe,CAAC,CAAC;CAC9C;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,SAAS,CAOlD,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS;IAC7C,mEAAmE;IACnE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,KAAG,WAgBH,CAAC"}
1
+ {"version":3,"file":"sse_auth_guard.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/realtime/sse_auth_guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEtD,OAAO,EAGN,KAAK,aAAa,EAClB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,kBAAkB,EAAE,KAAK,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACrF,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAEtE,2DAA2D;AAC3D,eAAO,MAAM,iBAAiB,cAAc,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,sBAAsB,EAAE,WAAW,CAAC,MAAM,CAOrD,CAAC;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,qBAAqB,GAAI,CAAC,EACtC,UAAU,kBAAkB,CAAC,CAAC,CAAC,EAC/B,eAAe,MAAM,GAAG,IAAI,EAC5B,KAAK,MAAM,KACT,CAAC,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CA6CjC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC3B,8FAA8F;IAC9F,SAAS,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,KAAK,MAAM,IAAI,CAAC;IAC1F,kFAAkF;IAClF,GAAG,EAAE,MAAM,CAAC;IACZ,yJAAyJ;IACzJ,cAAc,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/C,yEAAyE;IACzE,QAAQ,EAAE,kBAAkB,CAAC,eAAe,CAAC,CAAC;CAC9C;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,SAAS,CAOlD,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS;IAC7C,mEAAmE;IACnE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,KAAG,WAgBH,CAAC"}
@@ -29,9 +29,15 @@ export const AUDIT_LOG_CHANNEL = 'audit_log';
29
29
  * `session_revoke` closes only the stream tied to the specific revoked session
30
30
  * (matched by the blake3 session hash in `event.metadata.session_id`) — closing
31
31
  * all of a user's streams for a single-session revoke would be over-aggressive.
32
- * The single `token_revoke` the WS half handles is omitted: an SSE stream is
33
- * opened under a cookie session, never an API token, so no stream is keyed by a
34
- * single token id.
32
+ * The single `token_revoke` the WS half handles is omitted, and what makes that
33
+ * correct is a gate rather than an assumption: the audit route declares
34
+ * `credential_types: ['session']` (`auth/audit_log_route_schema.ts`), so a
35
+ * bearer is refused at the channel and no stream is ever keyed by a single
36
+ * token id. **A consumer that widens that gate must key its subscribers by
37
+ * api-token id and add `token_revoke` here** — otherwise a revoked token keeps
38
+ * receiving audit rows for the life of the connection. The Rust twin enforces
39
+ * the same pairing structurally, via the `RevocationScope` column on
40
+ * `AUDIT_EVENT_SPECS`.
35
41
  */
36
42
  export const disconnect_event_types = new Set([
37
43
  'role_grant_revoke', // role revoked — user lost access
@@ -1 +1 @@
1
- {"version":3,"file":"serve_fact_route.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/serve_fact_route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwFG;AAMH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAetD,OAAO,EAAuC,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAE5F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAM/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AA6DjD,MAAM,WAAW,+BAA+B;IAC/C;;;;;;OAMG;IACH,IAAI,EAAE,OAAO,CAAC;IACd,kFAAkF;IAClF,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;CACZ;AAgHD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iCAAiC,GAC7C,SAAS,+BAA+B,KACtC,SAgDF,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,4BAA4B,GACxC,SAAS,+BAA+B,KACtC,SA0CF,CAAC"}
1
+ {"version":3,"file":"serve_fact_route.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/serve_fact_route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwFG;AAMH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAetD,OAAO,EAAuC,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAE5F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAM/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAkEjD,MAAM,WAAW,+BAA+B;IAC/C;;;;;;OAMG;IACH,IAAI,EAAE,OAAO,CAAC;IACd,kFAAkF;IAClF,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;CACZ;AAgHD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iCAAiC,GAC7C,SAAS,+BAA+B,KACtC,SAgDF,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,4BAA4B,GACxC,SAAS,+BAA+B,KACtC,SA0CF,CAAC"}