@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.
- package/dist/actions/CLAUDE.md +15 -11
- package/dist/actions/action_rpc.d.ts +1 -1
- package/dist/actions/action_rpc.d.ts.map +1 -1
- package/dist/actions/action_rpc.js +4 -6
- package/dist/actions/peer_ping.d.ts +8 -0
- package/dist/actions/peer_ping.d.ts.map +1 -1
- package/dist/actions/peer_ping.js +8 -0
- package/dist/actions/peer_request.d.ts +1 -1
- package/dist/actions/peer_request.js +1 -1
- package/dist/actions/perform_action.d.ts +16 -9
- package/dist/actions/perform_action.d.ts.map +1 -1
- package/dist/actions/perform_action.js +30 -21
- package/dist/actions/register_action_ws.d.ts +1 -1
- package/dist/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +88 -21
- package/dist/auth/CLAUDE.md +43 -8
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +10 -1
- package/dist/auth/account_queries.d.ts.map +1 -1
- package/dist/auth/account_queries.js +36 -16
- package/dist/auth/account_schema.d.ts +20 -0
- package/dist/auth/account_schema.d.ts.map +1 -1
- package/dist/auth/account_schema.js +22 -1
- package/dist/auth/actor_search_actions.d.ts +3 -3
- package/dist/auth/actor_search_actions.d.ts.map +1 -1
- package/dist/auth/actor_search_actions.js +10 -6
- package/dist/auth/api_token_queries.d.ts.map +1 -1
- package/dist/auth/api_token_queries.js +11 -5
- package/dist/auth/app_settings_queries.d.ts.map +1 -1
- package/dist/auth/app_settings_queries.js +6 -4
- package/dist/auth/audit_log_queries.d.ts +6 -0
- package/dist/auth/audit_log_queries.d.ts.map +1 -1
- package/dist/auth/audit_log_queries.js +11 -5
- package/dist/auth/audit_log_route_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_route_schema.js +12 -1
- package/dist/auth/cell_actions.d.ts.map +1 -1
- package/dist/auth/cell_actions.js +3 -8
- package/dist/auth/cell_audit_actions.d.ts.map +1 -1
- package/dist/auth/cell_audit_actions.js +1 -1
- package/dist/auth/cell_field_actions.js +1 -1
- package/dist/auth/cell_grant_actions.js +1 -1
- package/dist/auth/cell_item_actions.js +1 -1
- package/dist/auth/invite_queries.d.ts.map +1 -1
- package/dist/auth/invite_queries.js +10 -8
- package/dist/auth/role_grant_offer_action_specs.d.ts +2 -1
- package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_action_specs.js +2 -1
- package/dist/auth/role_grant_offer_ddl.d.ts +11 -0
- package/dist/auth/role_grant_offer_ddl.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_ddl.js +20 -2
- package/dist/auth/role_grant_offer_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_queries.js +29 -15
- package/dist/auth/role_grant_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_queries.js +21 -7
- package/dist/auth/session_queries.d.ts.map +1 -1
- package/dist/auth/session_queries.js +9 -4
- package/dist/db/CLAUDE.md +21 -2
- package/dist/db/cell_audit_queries.js +2 -2
- package/dist/db/cell_field_queries.d.ts +1 -1
- package/dist/db/cell_field_queries.d.ts.map +1 -1
- package/dist/db/cell_field_queries.js +8 -6
- package/dist/db/cell_grant_queries.d.ts +1 -1
- package/dist/db/cell_grant_queries.d.ts.map +1 -1
- package/dist/db/cell_grant_queries.js +12 -10
- package/dist/db/cell_item_queries.d.ts +1 -1
- package/dist/db/cell_item_queries.d.ts.map +1 -1
- package/dist/db/cell_item_queries.js +9 -7
- package/dist/db/cell_queries.d.ts +3 -3
- package/dist/db/cell_queries.d.ts.map +1 -1
- package/dist/db/cell_queries.js +6 -4
- package/dist/db/fact_disk_storage.js +1 -1
- package/dist/db/fact_queries.d.ts.map +1 -1
- package/dist/db/fact_queries.js +9 -3
- package/dist/db/sql_columns.d.ts +80 -3
- package/dist/db/sql_columns.d.ts.map +1 -1
- package/dist/db/sql_columns.js +91 -4
- package/dist/http/CLAUDE.md +6 -4
- package/dist/http/auth_shape.d.ts +19 -6
- package/dist/http/auth_shape.d.ts.map +1 -1
- package/dist/http/auth_shape.js +19 -6
- package/dist/http/jsonrpc_helpers.d.ts +13 -0
- package/dist/http/jsonrpc_helpers.d.ts.map +1 -1
- package/dist/http/jsonrpc_helpers.js +13 -0
- package/dist/http/surface_query.d.ts +29 -4
- package/dist/http/surface_query.d.ts.map +1 -1
- package/dist/http/surface_query.js +30 -5
- package/dist/realtime/sse_auth_guard.d.ts +9 -3
- package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
- package/dist/realtime/sse_auth_guard.js +9 -3
- package/dist/server/serve_fact_route.d.ts.map +1 -1
- package/dist/server/serve_fact_route.js +7 -2
- package/dist/testing/CLAUDE.md +54 -28
- package/dist/testing/admin_integration.d.ts +0 -3
- package/dist/testing/admin_integration.d.ts.map +1 -1
- package/dist/testing/admin_integration.js +0 -1
- package/dist/testing/adversarial_404.d.ts.map +1 -1
- package/dist/testing/adversarial_404.js +4 -1
- package/dist/testing/adversarial_input.d.ts.map +1 -1
- package/dist/testing/adversarial_input.js +5 -4
- package/dist/testing/app_server.d.ts +4 -3
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +11 -5
- package/dist/testing/attack_surface.d.ts +21 -2
- package/dist/testing/attack_surface.d.ts.map +1 -1
- package/dist/testing/attack_surface.js +78 -27
- package/dist/testing/audit_completeness.d.ts +0 -3
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +0 -1
- package/dist/testing/auth_apps.d.ts +34 -2
- package/dist/testing/auth_apps.d.ts.map +1 -1
- package/dist/testing/auth_apps.js +43 -7
- package/dist/testing/cross_backend/account_lifecycle.d.ts +5 -4
- package/dist/testing/cross_backend/account_lifecycle.d.ts.map +1 -1
- package/dist/testing/cross_backend/account_lifecycle.js +7 -0
- package/dist/testing/cross_backend/action_manifest.d.ts +19 -3
- package/dist/testing/cross_backend/action_manifest.d.ts.map +1 -1
- package/dist/testing/cross_backend/action_manifest.js +10 -4
- package/dist/testing/cross_backend/action_manifest_parity.d.ts +9 -3
- package/dist/testing/cross_backend/action_manifest_parity.d.ts.map +1 -1
- package/dist/testing/cross_backend/action_manifest_parity.js +6 -0
- package/dist/testing/cross_backend/actor_lookup.d.ts +3 -2
- package/dist/testing/cross_backend/actor_lookup.d.ts.map +1 -1
- package/dist/testing/cross_backend/body_size.d.ts +4 -2
- package/dist/testing/cross_backend/body_size.d.ts.map +1 -1
- package/dist/testing/cross_backend/body_size.js +1 -1
- package/dist/testing/cross_backend/cell_crud.d.ts +2 -2
- package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_crud.js +10 -0
- package/dist/testing/cross_backend/cell_gated_create.d.ts +3 -3
- package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_grant_role.d.ts +2 -2
- package/dist/testing/cross_backend/cell_grant_role.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_relations.d.ts +2 -2
- package/dist/testing/cross_backend/cell_relations.d.ts.map +1 -1
- package/dist/testing/cross_backend/fact_serving.d.ts +2 -2
- package/dist/testing/cross_backend/fact_serving.d.ts.map +1 -1
- package/dist/testing/cross_backend/make_cross_backend_project.d.ts +17 -2
- package/dist/testing/cross_backend/make_cross_backend_project.d.ts.map +1 -1
- package/dist/testing/cross_backend/make_cross_backend_project.js +23 -3
- package/dist/testing/cross_backend/origin.d.ts +4 -2
- package/dist/testing/cross_backend/origin.d.ts.map +1 -1
- package/dist/testing/cross_backend/origin.js +1 -1
- package/dist/testing/cross_backend/peer_ping_ws.d.ts.map +1 -1
- package/dist/testing/cross_backend/peer_ping_ws.js +11 -7
- package/dist/testing/cross_backend/query_shape.d.ts +6 -5
- package/dist/testing/cross_backend/query_shape.d.ts.map +1 -1
- package/dist/testing/cross_backend/ready.d.ts +2 -2
- package/dist/testing/cross_backend/ready.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_offer_enumeration.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_offer_enumeration.js +7 -1
- package/dist/testing/cross_backend/setup.d.ts +26 -6
- package/dist/testing/cross_backend/setup.d.ts.map +1 -1
- package/dist/testing/cross_backend/standard.d.ts +5 -1
- package/dist/testing/cross_backend/standard.d.ts.map +1 -1
- package/dist/testing/cross_backend/standard.js +1 -5
- package/dist/testing/cross_backend/testing_backdoor.js +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +5 -0
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.js +3 -2
- package/dist/testing/cross_backend/token_lifetime.d.ts.map +1 -1
- package/dist/testing/cross_backend/token_lifetime.js +6 -0
- package/dist/testing/cross_backend/token_scope_surface.d.ts +2 -2
- package/dist/testing/cross_backend/token_scope_surface.d.ts.map +1 -1
- package/dist/testing/cross_backend/token_scope_surface.js +36 -7
- package/dist/testing/cross_backend/wire_shapes.d.ts +22 -0
- package/dist/testing/cross_backend/wire_shapes.d.ts.map +1 -0
- package/dist/testing/cross_backend/wire_shapes.js +41 -0
- package/dist/testing/data_exposure.d.ts +0 -3
- package/dist/testing/data_exposure.d.ts.map +1 -1
- package/dist/testing/data_exposure.js +0 -1
- package/dist/testing/db.d.ts +44 -7
- package/dist/testing/db.d.ts.map +1 -1
- package/dist/testing/db.js +62 -27
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +15 -2
- package/dist/testing/round_trip.d.ts +0 -3
- package/dist/testing/round_trip.d.ts.map +1 -1
- package/dist/testing/round_trip.js +0 -4
- package/dist/testing/rpc_attack_surface.js +2 -2
- package/dist/testing/rpc_round_trip.d.ts +0 -3
- package/dist/testing/rpc_round_trip.d.ts.map +1 -1
- package/dist/testing/rpc_round_trip.js +0 -1
- package/dist/testing/schema_generators.d.ts +15 -0
- package/dist/testing/schema_generators.d.ts.map +1 -1
- package/dist/testing/schema_generators.js +45 -11
- package/dist/testing/standard.d.ts +5 -1
- package/dist/testing/standard.d.ts.map +1 -1
- package/dist/testing/standard.js +2 -7
- package/dist/testing/surface_invariants.d.ts +7 -1
- package/dist/testing/surface_invariants.d.ts.map +1 -1
- package/dist/testing/surface_invariants.js +11 -5
- package/dist/timestamp.d.ts +58 -0
- package/dist/timestamp.d.ts.map +1 -0
- package/dist/timestamp.js +57 -0
- package/dist/ui/AdminAccounts.svelte +6 -4
- package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -1
- package/dist/ui/AdminInvites.svelte +3 -3
- package/dist/ui/AdminInvites.svelte.d.ts.map +1 -1
- package/dist/ui/AdminOverview.svelte +10 -10
- package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
- package/dist/ui/AdminRoleGrantHistory.svelte +7 -7
- package/dist/ui/AdminRoleGrantHistory.svelte.d.ts.map +1 -1
- package/dist/ui/AdminSettings.svelte +1 -1
- package/dist/ui/RoleGrantOfferHistory.svelte +3 -1
- package/dist/ui/RoleGrantOfferHistory.svelte.d.ts.map +1 -1
- package/dist/ui/RoleGrantOfferInbox.svelte +4 -1
- package/dist/ui/RoleGrantOfferInbox.svelte.d.ts.map +1 -1
- package/dist/ui/SurfaceExplorer.svelte +33 -17
- package/dist/ui/SurfaceExplorer.svelte.d.ts.map +1 -1
- package/dist/ui/role_grant_offers_state.svelte.d.ts.map +1 -1
- package/dist/ui/role_grant_offers_state.svelte.js +6 -2
- package/package.json +10 -10
package/dist/db/sql_columns.d.ts
CHANGED
|
@@ -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
|
|
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
|
|
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"}
|
package/dist/db/sql_columns.js
CHANGED
|
@@ -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
|
|
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 —
|
package/dist/http/CLAUDE.md
CHANGED
|
@@ -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
|
|
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
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
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
|
|
201
|
-
*
|
|
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
|
|
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"}
|
package/dist/http/auth_shape.js
CHANGED
|
@@ -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
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
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
|
|
236
|
-
*
|
|
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
|
|
69
|
-
*
|
|
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
|
|
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;
|
|
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
|
|
91
|
-
*
|
|
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
|
|
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
|
|
35
|
-
*
|
|
36
|
-
*
|
|
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
|
|
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
|
|
33
|
-
*
|
|
34
|
-
*
|
|
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;
|
|
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"}
|