@fuzdev/fuz_app 0.63.0 → 0.65.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 +525 -827
- package/dist/actions/broadcast_api.d.ts +1 -1
- package/dist/actions/broadcast_api.js +1 -1
- package/dist/actions/cancel.d.ts +2 -2
- package/dist/actions/cancel.js +3 -3
- package/dist/actions/connection_closer.d.ts +65 -0
- package/dist/actions/connection_closer.d.ts.map +1 -0
- package/dist/actions/connection_closer.js +38 -0
- package/dist/actions/register_action_ws.d.ts +2 -2
- package/dist/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +23 -2
- package/dist/actions/register_ws_endpoint.d.ts +12 -10
- package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
- package/dist/actions/register_ws_endpoint.js +5 -5
- package/dist/actions/transports_ws_auth_guard.d.ts +25 -10
- package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
- package/dist/actions/transports_ws_auth_guard.js +24 -9
- package/dist/actions/ws_endpoint_spec.d.ts +119 -0
- package/dist/actions/ws_endpoint_spec.d.ts.map +1 -0
- package/dist/actions/ws_endpoint_spec.js +13 -0
- package/dist/auth/CLAUDE.md +592 -1808
- package/dist/auth/account_action_specs.d.ts +1 -1
- package/dist/auth/account_actions.d.ts +13 -0
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +31 -1
- package/dist/auth/account_routes.d.ts +12 -2
- package/dist/auth/account_routes.d.ts.map +1 -1
- package/dist/auth/account_routes.js +55 -8
- package/dist/auth/account_schema.d.ts +4 -4
- package/dist/auth/account_schema.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.d.ts +8 -8
- package/dist/auth/admin_actions.d.ts +11 -0
- package/dist/auth/admin_actions.d.ts.map +1 -1
- package/dist/auth/admin_actions.js +25 -0
- package/dist/auth/api_token_queries.js +1 -1
- package/dist/auth/audit_emitter.d.ts +56 -12
- package/dist/auth/audit_emitter.d.ts.map +1 -1
- package/dist/auth/audit_emitter.js +38 -12
- package/dist/auth/audit_log_ddl.d.ts +1 -1
- package/dist/auth/audit_log_ddl.d.ts.map +1 -1
- package/dist/auth/audit_log_ddl.js +1 -1
- package/dist/auth/audit_log_schema.d.ts +5 -3
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +5 -3
- package/dist/auth/bootstrap_account.d.ts.map +1 -1
- package/dist/auth/bootstrap_account.js +1 -5
- package/dist/auth/bootstrap_routes.d.ts +8 -2
- package/dist/auth/bootstrap_routes.d.ts.map +1 -1
- package/dist/auth/bootstrap_routes.js +15 -11
- package/dist/auth/invite_schema.d.ts +2 -2
- package/dist/auth/keyring.d.ts +6 -6
- package/dist/auth/keyring.js +8 -8
- package/dist/auth/role_grant_offer_actions.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_actions.js +4 -2
- package/dist/auth/signup_routes.d.ts +1 -1
- package/dist/auth/standard_rpc_actions.d.ts +1 -0
- package/dist/auth/standard_rpc_actions.d.ts.map +1 -1
- package/dist/auth/standard_rpc_actions.js +1 -0
- package/dist/db/create_db.d.ts.map +1 -1
- package/dist/db/create_db.js +13 -0
- package/dist/dev/setup.d.ts +2 -2
- package/dist/dev/setup.js +3 -3
- package/dist/http/CLAUDE.md +225 -483
- package/dist/http/error_schemas.d.ts +0 -4
- package/dist/http/error_schemas.d.ts.map +1 -1
- package/dist/http/error_schemas.js +0 -4
- package/dist/http/ip_canonical.d.ts +100 -0
- package/dist/http/ip_canonical.d.ts.map +1 -0
- package/dist/http/ip_canonical.js +195 -0
- package/dist/http/origin.d.ts +14 -6
- package/dist/http/origin.d.ts.map +1 -1
- package/dist/http/origin.js +14 -32
- package/dist/http/pending_effects.d.ts +1 -1
- package/dist/http/pending_effects.js +1 -1
- package/dist/http/proxy.d.ts +13 -5
- package/dist/http/proxy.d.ts.map +1 -1
- package/dist/http/proxy.js +15 -23
- package/dist/http/surface.d.ts +50 -0
- package/dist/http/surface.d.ts.map +1 -1
- package/dist/http/surface.js +27 -1
- package/dist/primitive_schemas.d.ts +20 -4
- package/dist/primitive_schemas.d.ts.map +1 -1
- package/dist/primitive_schemas.js +25 -4
- package/dist/realtime/sse_auth_guard.d.ts +16 -4
- package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
- package/dist/realtime/sse_auth_guard.js +15 -3
- package/dist/runtime/mock.js +1 -1
- package/dist/server/app_backend.d.ts +66 -19
- package/dist/server/app_backend.d.ts.map +1 -1
- package/dist/server/app_backend.js +57 -34
- package/dist/server/app_server.d.ts +101 -10
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +105 -6
- package/dist/server/env.d.ts +7 -7
- package/dist/server/env.d.ts.map +1 -1
- package/dist/server/env.js +14 -14
- package/dist/server/startup.d.ts.map +1 -1
- package/dist/server/startup.js +12 -0
- package/dist/server/static.d.ts +4 -4
- package/dist/server/static.js +7 -7
- package/dist/testing/CLAUDE.md +269 -59
- package/dist/testing/admin_integration.d.ts +18 -23
- package/dist/testing/admin_integration.d.ts.map +1 -1
- package/dist/testing/admin_integration.js +159 -202
- package/dist/testing/adversarial_headers.d.ts +6 -0
- package/dist/testing/adversarial_headers.d.ts.map +1 -1
- package/dist/testing/adversarial_headers.js +13 -5
- package/dist/testing/app_server.d.ts +148 -60
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +143 -54
- package/dist/testing/attack_surface.d.ts +8 -7
- package/dist/testing/attack_surface.d.ts.map +1 -1
- package/dist/testing/attack_surface.js +12 -8
- package/dist/testing/audit_completeness.d.ts +23 -22
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +199 -158
- package/dist/testing/audit_drift_guard.d.ts +116 -0
- package/dist/testing/audit_drift_guard.d.ts.map +1 -0
- package/dist/testing/audit_drift_guard.js +134 -0
- package/dist/testing/bootstrap_success.d.ts +28 -0
- package/dist/testing/bootstrap_success.d.ts.map +1 -0
- package/dist/testing/bootstrap_success.js +144 -0
- package/dist/testing/connection_closer_helpers.d.ts +44 -0
- package/dist/testing/connection_closer_helpers.d.ts.map +1 -0
- package/dist/testing/connection_closer_helpers.js +48 -0
- package/dist/testing/cross_backend/capabilities.d.ts +64 -0
- package/dist/testing/cross_backend/capabilities.d.ts.map +1 -0
- package/dist/testing/cross_backend/capabilities.js +47 -0
- package/dist/testing/cross_backend/setup.d.ts +215 -0
- package/dist/testing/cross_backend/setup.d.ts.map +1 -0
- package/dist/testing/cross_backend/setup.js +101 -0
- package/dist/testing/data_exposure.d.ts +14 -15
- package/dist/testing/data_exposure.d.ts.map +1 -1
- package/dist/testing/data_exposure.js +127 -146
- package/dist/testing/db_entities.d.ts +11 -1
- package/dist/testing/db_entities.d.ts.map +1 -1
- package/dist/testing/db_entities.js +13 -1
- package/dist/testing/integration.d.ts +35 -21
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +231 -293
- package/dist/testing/integration_helpers.d.ts +16 -6
- package/dist/testing/integration_helpers.d.ts.map +1 -1
- package/dist/testing/integration_helpers.js +7 -7
- package/dist/testing/mock_fs.d.ts.map +1 -1
- package/dist/testing/mock_fs.js +0 -2
- package/dist/testing/rate_limiting.d.ts.map +1 -1
- package/dist/testing/rate_limiting.js +13 -4
- package/dist/testing/role_grant_helpers.d.ts +31 -0
- package/dist/testing/role_grant_helpers.d.ts.map +1 -0
- package/dist/testing/role_grant_helpers.js +46 -0
- package/dist/testing/round_trip.d.ts +21 -16
- package/dist/testing/round_trip.d.ts.map +1 -1
- package/dist/testing/round_trip.js +65 -86
- package/dist/testing/rpc_helpers.d.ts +2 -1
- package/dist/testing/rpc_helpers.d.ts.map +1 -1
- package/dist/testing/rpc_round_trip.d.ts +24 -21
- package/dist/testing/rpc_round_trip.d.ts.map +1 -1
- package/dist/testing/rpc_round_trip.js +91 -106
- package/dist/testing/schema_introspect.d.ts +106 -0
- package/dist/testing/schema_introspect.d.ts.map +1 -0
- package/dist/testing/schema_introspect.js +123 -0
- package/dist/testing/schema_parity.d.ts +144 -0
- package/dist/testing/schema_parity.d.ts.map +1 -0
- package/dist/testing/schema_parity.js +233 -0
- package/dist/testing/sse_round_trip.d.ts.map +1 -1
- package/dist/testing/sse_round_trip.js +12 -6
- package/dist/testing/standard.d.ts +57 -25
- package/dist/testing/standard.d.ts.map +1 -1
- package/dist/testing/standard.js +62 -5
- package/dist/testing/stubs.d.ts +22 -3
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +28 -21
- package/dist/testing/surface_invariants.d.ts +66 -1
- package/dist/testing/surface_invariants.d.ts.map +1 -1
- package/dist/testing/surface_invariants.js +103 -1
- package/dist/testing/transports/surface_source.d.ts +51 -0
- package/dist/testing/transports/surface_source.d.ts.map +1 -0
- package/dist/testing/transports/surface_source.js +19 -0
- package/dist/ui/SurfaceExplorer.svelte +161 -2
- package/dist/ui/SurfaceExplorer.svelte.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/http/surface.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ import type { RouteSpec } from './route_spec.js';
|
|
|
13
13
|
import type { RouteAuth } from './auth_shape.js';
|
|
14
14
|
import type { RateLimitKey, RouteErrorSchemas } from './error_schemas.js';
|
|
15
15
|
import type { RpcAction } from '../actions/action_rpc.js';
|
|
16
|
+
import type { ActionKind } from '../actions/action_spec.js';
|
|
17
|
+
import type { WsEndpointSpec } from '../actions/ws_endpoint_spec.js';
|
|
16
18
|
import type { Sensitivity } from '../sensitivity.js';
|
|
17
19
|
/** A route in the generated attack surface (JSON-serializable). */
|
|
18
20
|
export interface AppSurfaceRoute {
|
|
@@ -79,6 +81,45 @@ export interface AppSurfaceRpcEndpoint {
|
|
|
79
81
|
path: string;
|
|
80
82
|
methods: Array<AppSurfaceRpcMethod>;
|
|
81
83
|
}
|
|
84
|
+
/** A method within a WebSocket endpoint in the generated attack surface (JSON-serializable). */
|
|
85
|
+
export interface AppSurfaceWsMethod {
|
|
86
|
+
name: string;
|
|
87
|
+
/** `request_response` (inbound dispatch) or `remote_notification` (server → client). */
|
|
88
|
+
kind: ActionKind;
|
|
89
|
+
/**
|
|
90
|
+
* Per-action auth shape. `null` for `remote_notification` (server →
|
|
91
|
+
* client) — notifications have no inbound dispatch and therefore no
|
|
92
|
+
* auth axis. `request_response` always carries a `RouteAuth`.
|
|
93
|
+
*/
|
|
94
|
+
auth: RouteAuth | null;
|
|
95
|
+
/** JSON Schema of the input schema. `null` for nullary inputs. */
|
|
96
|
+
input_schema: unknown;
|
|
97
|
+
/** JSON Schema of the output schema. */
|
|
98
|
+
output_schema: unknown;
|
|
99
|
+
description: string;
|
|
100
|
+
side_effects: boolean;
|
|
101
|
+
/** Rate limit key declared on the action spec. `null` when not rate-limited. */
|
|
102
|
+
rate_limit_key: RateLimitKey | null;
|
|
103
|
+
}
|
|
104
|
+
/** A WebSocket endpoint in the generated attack surface (JSON-serializable). */
|
|
105
|
+
export interface AppSurfaceWsEndpoint {
|
|
106
|
+
path: string;
|
|
107
|
+
/**
|
|
108
|
+
* Upgrade-time origin allowlist, one entry per `WsEndpointSpec.allowed_origins`
|
|
109
|
+
* regex stringified via `RegExp.prototype.toString()` (`'/<source>/<flags>'`).
|
|
110
|
+
* Empty array when no origins were declared (any-origin); reviewers read this
|
|
111
|
+
* as the exact pattern matched at the upgrade gate, not a wildcard
|
|
112
|
+
* approximation. Reconstruct via `new RegExp(source, flags)` if needed.
|
|
113
|
+
*/
|
|
114
|
+
allowed_origins: ReadonlyArray<string>;
|
|
115
|
+
/**
|
|
116
|
+
* Upgrade-time role gate — empty array when no `required_roles` was
|
|
117
|
+
* declared (any-authenticated). Documents the coarse gate; per-action
|
|
118
|
+
* `auth` on each method covers per-message authorization.
|
|
119
|
+
*/
|
|
120
|
+
required_roles: ReadonlyArray<string>;
|
|
121
|
+
methods: Array<AppSurfaceWsMethod>;
|
|
122
|
+
}
|
|
82
123
|
/** Assembly-time diagnostic collected during surface generation or server assembly. */
|
|
83
124
|
export interface AppSurfaceDiagnostic {
|
|
84
125
|
level: 'warning' | 'info';
|
|
@@ -91,6 +132,7 @@ export interface AppSurface {
|
|
|
91
132
|
middleware: Array<AppSurfaceMiddleware>;
|
|
92
133
|
routes: Array<AppSurfaceRoute>;
|
|
93
134
|
rpc_endpoints: Array<AppSurfaceRpcEndpoint>;
|
|
135
|
+
ws_endpoints: Array<AppSurfaceWsEndpoint>;
|
|
94
136
|
env: Array<AppSurfaceEnv>;
|
|
95
137
|
events: Array<AppSurfaceEvent>;
|
|
96
138
|
diagnostics: Array<AppSurfaceDiagnostic>;
|
|
@@ -106,6 +148,7 @@ export interface AppSurfaceSpec {
|
|
|
106
148
|
route_specs: Array<RouteSpec>;
|
|
107
149
|
middleware_specs: Array<MiddlewareSpec>;
|
|
108
150
|
rpc_endpoints: Array<RpcEndpointSpec>;
|
|
151
|
+
ws_endpoints: Array<WsEndpointSpec>;
|
|
109
152
|
}
|
|
110
153
|
/** An RPC endpoint definition for surface generation. */
|
|
111
154
|
export interface RpcEndpointSpec {
|
|
@@ -119,6 +162,13 @@ export interface GenerateAppSurfaceOptions {
|
|
|
119
162
|
env_schema?: z.ZodObject;
|
|
120
163
|
event_specs?: Array<EventSpec>;
|
|
121
164
|
rpc_endpoints?: Array<RpcEndpointSpec>;
|
|
165
|
+
/**
|
|
166
|
+
* Mounted WS endpoints (the same array `create_app_server.ws_endpoints`
|
|
167
|
+
* auto-mounts). Each entry's actions surface into
|
|
168
|
+
* `AppSurface.ws_endpoints[i].methods` for attack-surface tests +
|
|
169
|
+
* startup logging.
|
|
170
|
+
*/
|
|
171
|
+
ws_endpoints?: ReadonlyArray<WsEndpointSpec>;
|
|
122
172
|
}
|
|
123
173
|
/**
|
|
124
174
|
* Collect error schemas from all middleware that applies to a route path.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"surface.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/surface.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAC,YAAY,EAAE,iBAAiB,EAAC,MAAM,oBAAoB,CAAC;AACxE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"surface.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/surface.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAC,YAAY,EAAE,iBAAiB,EAAC,MAAM,oBAAoB,CAAC;AACxE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,0BAA0B,CAAC;AACxD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,2BAA2B,CAAC;AAC1D,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,gCAAgC,CAAC;AAQnE,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,mBAAmB,CAAC;AAKnD,mEAAmE;AACnE,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,CAAC;IAChB,qBAAqB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,WAAW,EAAE,OAAO,CAAC;IACrB,uEAAuE;IACvE,WAAW,EAAE,OAAO,CAAC;IACrB,oFAAoF;IACpF,cAAc,EAAE,YAAY,GAAG,IAAI,CAAC;IACpC,uFAAuF;IACvF,aAAa,EAAE,OAAO,CAAC;IACvB,8FAA8F;IAC9F,YAAY,EAAE,OAAO,CAAC;IACtB,wFAAwF;IACxF,YAAY,EAAE,OAAO,CAAC;IACtB,iEAAiE;IACjE,aAAa,EAAE,OAAO,CAAC;IACvB,mGAAmG;IACnG,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC9C;AAED,wEAAwE;AACxE,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,mGAAmG;IACnG,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC9C;AAED,sEAAsE;AACtE,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,gFAAgF;IAChF,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IAChC,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;CAClB;AAED,wEAAwE;AACxE,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;CACvB;AAED,2FAA2F;AAC3F,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,CAAC;IAChB,qFAAqF;IACrF,YAAY,EAAE,OAAO,CAAC;IACtB,uDAAuD;IACvD,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,gFAAgF;IAChF,cAAc,EAAE,YAAY,GAAG,IAAI,CAAC;CACpC;AAED,2EAA2E;AAC3E,MAAM,WAAW,qBAAqB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,KAAK,CAAC,mBAAmB,CAAC,CAAC;CACpC;AAED,gGAAgG;AAChG,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,wFAAwF;IACxF,IAAI,EAAE,UAAU,CAAC;IACjB;;;;OAIG;IACH,IAAI,EAAE,SAAS,GAAG,IAAI,CAAC;IACvB,kEAAkE;IAClE,YAAY,EAAE,OAAO,CAAC;IACtB,wCAAwC;IACxC,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,gFAAgF;IAChF,cAAc,EAAE,YAAY,GAAG,IAAI,CAAC;CACpC;AAED,gFAAgF;AAChF,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,eAAe,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACvC;;;;OAIG;IACH,cAAc,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACtC,OAAO,EAAE,KAAK,CAAC,kBAAkB,CAAC,CAAC;CACnC;AAED,uFAAuF;AACvF,MAAM,WAAW,oBAAoB;IACpC,KAAK,EAAE,SAAS,GAAG,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,oDAAoD;AACpD,MAAM,WAAW,UAAU;IAC1B,UAAU,EAAE,KAAK,CAAC,oBAAoB,CAAC,CAAC;IACxC,MAAM,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;IAC/B,aAAa,EAAE,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAC5C,YAAY,EAAE,KAAK,CAAC,oBAAoB,CAAC,CAAC;IAC1C,GAAG,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;IAC1B,MAAM,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;IAC/B,WAAW,EAAE,KAAK,CAAC,oBAAoB,CAAC,CAAC;CACzC;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,UAAU,CAAC;IACpB,WAAW,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC9B,gBAAgB,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;IACxC,aAAa,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;IACtC,YAAY,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;CACpC;AAED,yDAAyD;AACzD,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAC1B;AAED,0CAA0C;AAC1C,MAAM,WAAW,yBAAyB;IACzC,WAAW,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC9B,gBAAgB,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;IACxC,UAAU,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;IACzB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC/B,aAAa,CAAC,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;IACvC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;CAC7C;AAID;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,GACrC,YAAY,KAAK,CAAC,cAAc,CAAC,EACjC,YAAY,MAAM,KAChB,iBAAiB,GAAG,IAQtB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAAI,QAAQ,CAAC,CAAC,SAAS,KAAG,KAAK,CAAC,aAAa,CAe9E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAAI,aAAa,KAAK,CAAC,SAAS,CAAC,KAAG,KAAK,CAAC,eAAe,CAOtF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS,yBAAyB,KAAG,UAoHzE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,uBAAuB,GAAI,SAAS,yBAAyB,KAAG,cAS5E,CAAC"}
|
package/dist/http/surface.js
CHANGED
|
@@ -60,7 +60,7 @@ export const events_to_surface = (event_specs) => {
|
|
|
60
60
|
* and optional env/event metadata.
|
|
61
61
|
*/
|
|
62
62
|
export const generate_app_surface = (options) => {
|
|
63
|
-
const { route_specs, middleware_specs, env_schema, event_specs, rpc_endpoints } = options;
|
|
63
|
+
const { route_specs, middleware_specs, env_schema, event_specs, rpc_endpoints, ws_endpoints } = options;
|
|
64
64
|
const diagnostics = [];
|
|
65
65
|
// Spec-level diagnostics: check for non-strict input schemas
|
|
66
66
|
for (const r of route_specs) {
|
|
@@ -141,6 +141,31 @@ export const generate_app_surface = (options) => {
|
|
|
141
141
|
})),
|
|
142
142
|
}))
|
|
143
143
|
: [],
|
|
144
|
+
ws_endpoints: ws_endpoints?.length
|
|
145
|
+
? ws_endpoints.map((ep) => ({
|
|
146
|
+
path: ep.path,
|
|
147
|
+
allowed_origins: ep.allowed_origins.map((re) => re.toString()),
|
|
148
|
+
required_roles: ep.required_roles ?? [],
|
|
149
|
+
// `local_call` specs are frontend-side helpers — registry-only
|
|
150
|
+
// on the backend, never dispatched over WS. Drop them from the
|
|
151
|
+
// surface so attack-surface tests reflect dispatchable methods
|
|
152
|
+
// only. Notifications are kept (server → client emit).
|
|
153
|
+
methods: ep.actions
|
|
154
|
+
.filter((a) => a.spec.kind !== 'local_call')
|
|
155
|
+
.map((a) => ({
|
|
156
|
+
name: a.spec.method,
|
|
157
|
+
kind: a.spec.kind,
|
|
158
|
+
// `request_response` carries a `RouteAuth`; notifications
|
|
159
|
+
// have `auth: null` (server-pushed, no inbound dispatch).
|
|
160
|
+
auth: a.spec.auth,
|
|
161
|
+
input_schema: schema_to_surface(a.spec.input),
|
|
162
|
+
output_schema: schema_to_surface(a.spec.output),
|
|
163
|
+
description: a.spec.description,
|
|
164
|
+
side_effects: a.spec.side_effects,
|
|
165
|
+
rate_limit_key: a.spec.kind === 'request_response' ? (a.spec.rate_limit ?? null) : null,
|
|
166
|
+
})),
|
|
167
|
+
}))
|
|
168
|
+
: [],
|
|
144
169
|
env: env_schema ? env_schema_to_surface(env_schema) : [],
|
|
145
170
|
events: event_specs?.length ? events_to_surface(event_specs) : [],
|
|
146
171
|
};
|
|
@@ -155,5 +180,6 @@ export const create_app_surface_spec = (options) => {
|
|
|
155
180
|
route_specs: options.route_specs,
|
|
156
181
|
middleware_specs: options.middleware_specs,
|
|
157
182
|
rpc_endpoints: options.rpc_endpoints ?? [],
|
|
183
|
+
ws_endpoints: options.ws_endpoints ? [...options.ws_endpoints] : [],
|
|
158
184
|
};
|
|
159
185
|
};
|
|
@@ -21,11 +21,27 @@ export declare const USERNAME_LENGTH_MIN = 3;
|
|
|
21
21
|
export declare const USERNAME_LENGTH_MAX = 39;
|
|
22
22
|
/** Maximum length for username input on login/lookup — more permissive than `USERNAME_LENGTH_MAX` for forward-compatibility if the creation limit is raised. */
|
|
23
23
|
export declare const USERNAME_PROVIDED_LENGTH_MAX = 255;
|
|
24
|
-
/**
|
|
25
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Username for account creation — starts with letter, alphanumeric/dash/underscore middle, ends with alphanumeric. No @ or . allowed.
|
|
26
|
+
*
|
|
27
|
+
* Canonicalized to lowercase at parse time. The regex rejects whitespace
|
|
28
|
+
* outright, so `.trim()` is unnecessary here. Storage is canonical across
|
|
29
|
+
* every creation site (bootstrap, signup, admin-create, invite acceptance)
|
|
30
|
+
* because the schema is the single source of truth — eliminates the
|
|
31
|
+
* per-caller `trim().toLowerCase()` ritual and keeps the
|
|
32
|
+
* `LOWER(username) = LOWER($1)` lookup contract simple.
|
|
33
|
+
*/
|
|
34
|
+
export declare const Username: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
26
35
|
export type Username = z.infer<typeof Username>;
|
|
27
|
-
/**
|
|
28
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Username submitted for login or lookup — minimal validation for forward-compatibility if format rules change.
|
|
38
|
+
*
|
|
39
|
+
* Canonicalized via `.trim().toLowerCase()` at parse time so login's
|
|
40
|
+
* per-account rate-limit key and DB lookup see a uniform value
|
|
41
|
+
* regardless of casing or surrounding whitespace. Mirrors the storage
|
|
42
|
+
* canonicalization on `Username` so submission and storage agree.
|
|
43
|
+
*/
|
|
44
|
+
export declare const UsernameProvided: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
29
45
|
export type UsernameProvided = z.infer<typeof UsernameProvided>;
|
|
30
46
|
/**
|
|
31
47
|
* Email validation. Lives here rather than `@fuzdev/fuz_util` because every
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"primitive_schemas.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/primitive_schemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAItB,2EAA2E;AAC3E,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,wDAAwD;AACxD,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,gKAAgK;AAChK,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAEhD
|
|
1
|
+
{"version":3,"file":"primitive_schemas.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/primitive_schemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAItB,2EAA2E;AAC3E,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,wDAAwD;AACxD,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,gKAAgK;AAChK,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAEhD;;;;;;;;;GASG;AACH,eAAO,MAAM,QAAQ,wDAKc,CAAC;AACpC,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,QAAQ,CAAC,CAAC;AAEhD;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,wDAIa,CAAC;AAC3C,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE;;;;;;GAMG;AACH,eAAO,MAAM,KAAK,YAAY,CAAC;AAC/B,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,KAAK,CAAC,CAAC"}
|
|
@@ -22,14 +22,35 @@ export const USERNAME_LENGTH_MIN = 3;
|
|
|
22
22
|
export const USERNAME_LENGTH_MAX = 39;
|
|
23
23
|
/** Maximum length for username input on login/lookup — more permissive than `USERNAME_LENGTH_MAX` for forward-compatibility if the creation limit is raised. */
|
|
24
24
|
export const USERNAME_PROVIDED_LENGTH_MAX = 255;
|
|
25
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* Username for account creation — starts with letter, alphanumeric/dash/underscore middle, ends with alphanumeric. No @ or . allowed.
|
|
27
|
+
*
|
|
28
|
+
* Canonicalized to lowercase at parse time. The regex rejects whitespace
|
|
29
|
+
* outright, so `.trim()` is unnecessary here. Storage is canonical across
|
|
30
|
+
* every creation site (bootstrap, signup, admin-create, invite acceptance)
|
|
31
|
+
* because the schema is the single source of truth — eliminates the
|
|
32
|
+
* per-caller `trim().toLowerCase()` ritual and keeps the
|
|
33
|
+
* `LOWER(username) = LOWER($1)` lookup contract simple.
|
|
34
|
+
*/
|
|
26
35
|
export const Username = z
|
|
27
36
|
.string()
|
|
28
37
|
.min(USERNAME_LENGTH_MIN)
|
|
29
38
|
.max(USERNAME_LENGTH_MAX)
|
|
30
|
-
.regex(/^[a-zA-Z][0-9a-zA-Z_-]*[0-9a-zA-Z]$/)
|
|
31
|
-
|
|
32
|
-
|
|
39
|
+
.regex(/^[a-zA-Z][0-9a-zA-Z_-]*[0-9a-zA-Z]$/)
|
|
40
|
+
.transform((s) => s.toLowerCase());
|
|
41
|
+
/**
|
|
42
|
+
* Username submitted for login or lookup — minimal validation for forward-compatibility if format rules change.
|
|
43
|
+
*
|
|
44
|
+
* Canonicalized via `.trim().toLowerCase()` at parse time so login's
|
|
45
|
+
* per-account rate-limit key and DB lookup see a uniform value
|
|
46
|
+
* regardless of casing or surrounding whitespace. Mirrors the storage
|
|
47
|
+
* canonicalization on `Username` so submission and storage agree.
|
|
48
|
+
*/
|
|
49
|
+
export const UsernameProvided = z
|
|
50
|
+
.string()
|
|
51
|
+
.min(1)
|
|
52
|
+
.max(USERNAME_PROVIDED_LENGTH_MAX)
|
|
53
|
+
.transform((s) => s.trim().toLowerCase());
|
|
33
54
|
/**
|
|
34
55
|
* Email validation. Lives here rather than `@fuzdev/fuz_util` because every
|
|
35
56
|
* current consumer pairs it with `Username` (signup, invites, audit log) —
|
|
@@ -58,7 +58,7 @@ export interface AuditLogSse {
|
|
|
58
58
|
subscribe: (stream: SseStream<SseNotification>, options?: SubscribeOptions) => () => void;
|
|
59
59
|
/** Logger — pass as part of `stream` option to `create_audit_log_route_specs`. */
|
|
60
60
|
log: Logger;
|
|
61
|
-
/** Combined broadcast + guard callback.
|
|
61
|
+
/** Combined broadcast + guard callback. Wired by `create_app_server`'s `audit_log_sse` option, or compose inside the consumer's `audit_factory` body. */
|
|
62
62
|
on_audit_event: (event: AuditLogEvent) => void;
|
|
63
63
|
/** The underlying registry — exposed for subscriber count monitoring. */
|
|
64
64
|
registry: SubscriberRegistry<SseNotification>;
|
|
@@ -86,7 +86,15 @@ export declare const AUDIT_LOG_SSE_MAX_PER_SCOPE = 10;
|
|
|
86
86
|
*
|
|
87
87
|
* Combines `SubscriberRegistry`, `create_sse_auth_guard`, and the broadcast
|
|
88
88
|
* call into a single object. The result satisfies `AuditLogRouteOptions['stream']`
|
|
89
|
-
* and provides the `on_audit_event`
|
|
89
|
+
* and provides the `on_audit_event` listener for the audit emitter's chain.
|
|
90
|
+
*
|
|
91
|
+
* Most consumers pass `audit_log_sse: true` to `create_app_server` and never
|
|
92
|
+
* touch this directly — the factory builds an `AuditLogSse`, appends
|
|
93
|
+
* `audit_sse.on_audit_event` to `backend.deps.audit.on_event_chain`, and
|
|
94
|
+
* exposes it via `AppServerContext.audit_sse`. Reach for the manual path
|
|
95
|
+
* (compose inside `audit_factory` body, or
|
|
96
|
+
* `audit.on_event_chain.push(audit_sse.on_audit_event)` post-assembly) only
|
|
97
|
+
* when wiring outside `create_app_server`.
|
|
90
98
|
*
|
|
91
99
|
* @param options - factory options
|
|
92
100
|
* @returns audit log SSE setup (stream options + `on_audit_event` + registry)
|
|
@@ -95,8 +103,12 @@ export declare const AUDIT_LOG_SSE_MAX_PER_SCOPE = 10;
|
|
|
95
103
|
* ```ts
|
|
96
104
|
* const audit_sse = create_audit_log_sse({log});
|
|
97
105
|
*
|
|
98
|
-
* //
|
|
99
|
-
*
|
|
106
|
+
* // Inside the audit_factory body on CreateAppBackendOptions:
|
|
107
|
+
* audit_factory: ({db, log}) => create_audit_emitter({
|
|
108
|
+
* db,
|
|
109
|
+
* log,
|
|
110
|
+
* on_audit_event: audit_sse.on_audit_event,
|
|
111
|
+
* }),
|
|
100
112
|
*
|
|
101
113
|
* // In create_route_specs:
|
|
102
114
|
* create_audit_log_route_specs({stream: audit_sse});
|
|
@@ -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,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,EAGN,KAAK,aAAa,EAClB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAC,kBAAkB,EAAE,KAAK,gBAAgB,EAAC,MAAM,0BAA0B,CAAC;AACnF,OAAO,KAAK,EAAC,SAAS,EAAE,eAAe,EAAE,SAAS,EAAC,MAAM,UAAU,CAAC;AAEpE,2DAA2D;AAC3D,eAAO,MAAM,iBAAiB,cAAc,CAAC;AAE7C;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,EAAE,WAAW,CAAC,MAAM,CAKrD,CAAC;AAEH;;;;;;;;;;;;;;;;;GAiBG;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,
|
|
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,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,EAGN,KAAK,aAAa,EAClB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAC,kBAAkB,EAAE,KAAK,gBAAgB,EAAC,MAAM,0BAA0B,CAAC;AACnF,OAAO,KAAK,EAAC,SAAS,EAAE,eAAe,EAAE,SAAS,EAAC,MAAM,UAAU,CAAC;AAEpE,2DAA2D;AAC3D,eAAO,MAAM,iBAAiB,cAAc,CAAC;AAE7C;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,EAAE,WAAW,CAAC,MAAM,CAKrD,CAAC;AAEH;;;;;;;;;;;;;;;;;GAiBG;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"}
|
|
@@ -120,7 +120,15 @@ export const AUDIT_LOG_SSE_MAX_PER_SCOPE = 10;
|
|
|
120
120
|
*
|
|
121
121
|
* Combines `SubscriberRegistry`, `create_sse_auth_guard`, and the broadcast
|
|
122
122
|
* call into a single object. The result satisfies `AuditLogRouteOptions['stream']`
|
|
123
|
-
* and provides the `on_audit_event`
|
|
123
|
+
* and provides the `on_audit_event` listener for the audit emitter's chain.
|
|
124
|
+
*
|
|
125
|
+
* Most consumers pass `audit_log_sse: true` to `create_app_server` and never
|
|
126
|
+
* touch this directly — the factory builds an `AuditLogSse`, appends
|
|
127
|
+
* `audit_sse.on_audit_event` to `backend.deps.audit.on_event_chain`, and
|
|
128
|
+
* exposes it via `AppServerContext.audit_sse`. Reach for the manual path
|
|
129
|
+
* (compose inside `audit_factory` body, or
|
|
130
|
+
* `audit.on_event_chain.push(audit_sse.on_audit_event)` post-assembly) only
|
|
131
|
+
* when wiring outside `create_app_server`.
|
|
124
132
|
*
|
|
125
133
|
* @param options - factory options
|
|
126
134
|
* @returns audit log SSE setup (stream options + `on_audit_event` + registry)
|
|
@@ -129,8 +137,12 @@ export const AUDIT_LOG_SSE_MAX_PER_SCOPE = 10;
|
|
|
129
137
|
* ```ts
|
|
130
138
|
* const audit_sse = create_audit_log_sse({log});
|
|
131
139
|
*
|
|
132
|
-
* //
|
|
133
|
-
*
|
|
140
|
+
* // Inside the audit_factory body on CreateAppBackendOptions:
|
|
141
|
+
* audit_factory: ({db, log}) => create_audit_emitter({
|
|
142
|
+
* db,
|
|
143
|
+
* log,
|
|
144
|
+
* on_audit_event: audit_sse.on_audit_event,
|
|
145
|
+
* }),
|
|
134
146
|
*
|
|
135
147
|
* // In create_route_specs:
|
|
136
148
|
* create_audit_log_route_specs({stream: audit_sse});
|
package/dist/runtime/mock.js
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { Logger } from '@fuzdev/fuz_util/log.js';
|
|
14
14
|
import type { AppDeps } from '../auth/deps.js';
|
|
15
|
-
import
|
|
16
|
-
import type { DbType } from '../db/db.js';
|
|
15
|
+
import { type AuditEmitter } from '../auth/audit_emitter.js';
|
|
16
|
+
import type { DbType, Db } from '../db/db.js';
|
|
17
17
|
import type { Keyring } from '../auth/keyring.js';
|
|
18
18
|
import type { PasswordHashDeps } from '../auth/password.js';
|
|
19
19
|
import type { StatResult } from '../runtime/deps.js';
|
|
@@ -33,6 +33,49 @@ export interface AppBackend {
|
|
|
33
33
|
/** Close the database connection. Bound to the actual driver. */
|
|
34
34
|
close: () => Promise<void>;
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Callback that builds the bound `AuditEmitter` after the backend's pool
|
|
38
|
+
* `Db` and `Logger` exist. Required on `CreateAppBackendOptions` so the
|
|
39
|
+
* consumer owns subscriber-chain composition and `AuditLogConfig`
|
|
40
|
+
* selection without the factory holding a default.
|
|
41
|
+
*
|
|
42
|
+
* The factory is invoked exactly once during `create_app_backend`, after
|
|
43
|
+
* `create_db` resolves and migrations run. The emitter it returns lands
|
|
44
|
+
* on `AppDeps.audit` and is captured by every query/handler that reaches
|
|
45
|
+
* `deps.audit.emit(...)`.
|
|
46
|
+
*
|
|
47
|
+
* The canonical body is a one-liner over `create_audit_emitter`:
|
|
48
|
+
*
|
|
49
|
+
* ```ts
|
|
50
|
+
* audit_factory: ({db, log}) => create_audit_emitter({
|
|
51
|
+
* db,
|
|
52
|
+
* log,
|
|
53
|
+
* on_audit_event,
|
|
54
|
+
* audit_log_config,
|
|
55
|
+
* })
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* Returning an emitter built against a different `db` than the one passed
|
|
59
|
+
* in would route audit writes to a different pool than handlers query —
|
|
60
|
+
* the callback shape exists specifically to make that mistake structurally
|
|
61
|
+
* impossible.
|
|
62
|
+
*/
|
|
63
|
+
export type AuditFactory = (params: {
|
|
64
|
+
db: Db;
|
|
65
|
+
log: Logger;
|
|
66
|
+
}) => AuditEmitter;
|
|
67
|
+
/**
|
|
68
|
+
* Trivial `AuditFactory` for consumers that don't compose `on_audit_event`
|
|
69
|
+
* or `audit_log_config`. Equivalent to
|
|
70
|
+
* `({db, log}) => create_audit_emitter({db, log})` — exported so the
|
|
71
|
+
* default case stays a single-symbol reference rather than five tokens
|
|
72
|
+
* of boilerplate at every consumer.
|
|
73
|
+
*
|
|
74
|
+
* Use the inline form when you need to thread `on_audit_event` /
|
|
75
|
+
* `audit_log_config` / `emit_decorator`; the factory composes those
|
|
76
|
+
* three fields itself so there's nothing this constant can pass through.
|
|
77
|
+
*/
|
|
78
|
+
export declare const default_audit_factory: AuditFactory;
|
|
36
79
|
/**
|
|
37
80
|
* Input for `create_app_backend()`.
|
|
38
81
|
*
|
|
@@ -55,21 +98,25 @@ export interface CreateAppBackendOptions {
|
|
|
55
98
|
/** Structured logger instance. Omit for default (`new Logger('server')`). */
|
|
56
99
|
log?: Logger;
|
|
57
100
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
101
|
+
* Build the bound `AuditEmitter` once the backend's pool `Db` + `Logger`
|
|
102
|
+
* exist. Required — the factory owns subscriber-chain composition and
|
|
103
|
+
* `AuditLogConfig` selection without `create_app_backend` holding a
|
|
104
|
+
* default. Typical body:
|
|
105
|
+
*
|
|
106
|
+
* ```ts
|
|
107
|
+
* audit_factory: ({db, log}) => create_audit_emitter({
|
|
108
|
+
* db,
|
|
109
|
+
* log,
|
|
110
|
+
* on_audit_event,
|
|
111
|
+
* audit_log_config,
|
|
112
|
+
* })
|
|
113
|
+
* ```
|
|
114
|
+
*
|
|
115
|
+
* Additional listeners (factory-managed audit SSE, per-endpoint WS
|
|
116
|
+
* auth guards) are appended at `create_app_server` time via
|
|
117
|
+
* `audit.on_event_chain.push(...)`.
|
|
71
118
|
*/
|
|
72
|
-
|
|
119
|
+
audit_factory: AuditFactory;
|
|
73
120
|
/**
|
|
74
121
|
* Additional migration namespaces to run after the builtin auth namespace.
|
|
75
122
|
* The shared `schema_version` table records one row per applied migration
|
|
@@ -87,10 +134,10 @@ export interface CreateAppBackendOptions {
|
|
|
87
134
|
* Initialize the backend: database + auth migrations + deps.
|
|
88
135
|
*
|
|
89
136
|
* Calls `create_db` → `run_migrations` (auth namespace, then any
|
|
90
|
-
* `migration_namespaces` from options in order)
|
|
91
|
-
* with the provided keyring and password deps.
|
|
137
|
+
* `migration_namespaces` from options in order) → `audit_factory({db, log})`
|
|
138
|
+
* and bundles the result with the provided keyring and password deps.
|
|
92
139
|
*
|
|
93
|
-
* @param options - keyring, password deps, optional database URL, and optional `migration_namespaces`
|
|
140
|
+
* @param options - keyring, password deps, `audit_factory`, optional database URL, and optional `migration_namespaces`
|
|
94
141
|
* @returns app backend with deps, database metadata, and combined migration results
|
|
95
142
|
* @throws Error if `migration_namespaces` contains a namespace in `reserved_migration_namespaces`
|
|
96
143
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app_backend.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAE/C,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"app_backend.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAE/C,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAuB,KAAK,YAAY,EAAC,MAAM,0BAA0B,CAAC;AACjF,OAAO,KAAK,EAAC,MAAM,EAAE,EAAE,EAAC,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAiB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAC,MAAM,kBAAkB,CAAC;AAI/F;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,oIAAoI;IACpI,QAAQ,CAAC,iBAAiB,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAC3D,iEAAiE;IACjE,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE;IAAC,EAAE,EAAE,EAAE,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAC,KAAK,YAAY,CAAC;AAE3E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,EAAE,YAA6D,CAAC;AAElG;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB;IACvC,+DAA+D;IAC/D,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACnD,2BAA2B;IAC3B,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAClD,qBAAqB;IACrB,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,0EAA0E;IAC1E,YAAY,EAAE,MAAM,CAAC;IACrB,wCAAwC;IACxC,OAAO,EAAE,OAAO,CAAC;IACjB,iFAAiF;IACjF,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,6EAA6E;IAC7E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;;;;;;;;;;;;;OAkBG;IACH,aAAa,EAAE,YAAY,CAAC;IAC5B;;;;;;;;;;OAUG;IACH,oBAAoB,CAAC,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;CACzD;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,GAAU,SAAS,uBAAuB,KAAG,OAAO,CAAC,UAAU,CAiD7F,CAAC"}
|
|
@@ -15,52 +15,75 @@ import { create_audit_emitter } from '../auth/audit_emitter.js';
|
|
|
15
15
|
import { run_migrations } from '../db/migrate.js';
|
|
16
16
|
import { auth_migration_ns, reserved_migration_namespaces } from '../auth/migrations.js';
|
|
17
17
|
import { create_db } from '../db/create_db.js';
|
|
18
|
+
/**
|
|
19
|
+
* Trivial `AuditFactory` for consumers that don't compose `on_audit_event`
|
|
20
|
+
* or `audit_log_config`. Equivalent to
|
|
21
|
+
* `({db, log}) => create_audit_emitter({db, log})` — exported so the
|
|
22
|
+
* default case stays a single-symbol reference rather than five tokens
|
|
23
|
+
* of boilerplate at every consumer.
|
|
24
|
+
*
|
|
25
|
+
* Use the inline form when you need to thread `on_audit_event` /
|
|
26
|
+
* `audit_log_config` / `emit_decorator`; the factory composes those
|
|
27
|
+
* three fields itself so there's nothing this constant can pass through.
|
|
28
|
+
*/
|
|
29
|
+
export const default_audit_factory = ({ db, log }) => create_audit_emitter({ db, log });
|
|
18
30
|
/**
|
|
19
31
|
* Initialize the backend: database + auth migrations + deps.
|
|
20
32
|
*
|
|
21
33
|
* Calls `create_db` → `run_migrations` (auth namespace, then any
|
|
22
|
-
* `migration_namespaces` from options in order)
|
|
23
|
-
* with the provided keyring and password deps.
|
|
34
|
+
* `migration_namespaces` from options in order) → `audit_factory({db, log})`
|
|
35
|
+
* and bundles the result with the provided keyring and password deps.
|
|
24
36
|
*
|
|
25
|
-
* @param options - keyring, password deps, optional database URL, and optional `migration_namespaces`
|
|
37
|
+
* @param options - keyring, password deps, `audit_factory`, optional database URL, and optional `migration_namespaces`
|
|
26
38
|
* @returns app backend with deps, database metadata, and combined migration results
|
|
27
39
|
* @throws Error if `migration_namespaces` contains a namespace in `reserved_migration_namespaces`
|
|
28
40
|
*/
|
|
29
41
|
export const create_app_backend = async (options) => {
|
|
30
|
-
const { database_url, keyring, password, stat, read_text_file, delete_file } = options;
|
|
42
|
+
const { database_url, keyring, password, stat, read_text_file, delete_file, audit_factory } = options;
|
|
31
43
|
const log = options.log ?? new Logger('server');
|
|
32
44
|
const { db, close, db_type, db_name } = await create_db(database_url);
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
45
|
+
// Everything after `create_db` can throw — reserved-namespace check,
|
|
46
|
+
// `run_migrations` (seven MigrationError kinds), `audit_factory`.
|
|
47
|
+
// Without this guard the pool leaks because `close` is only returned
|
|
48
|
+
// on the success path. Cleanup errors are logged and swallowed so the
|
|
49
|
+
// caller sees the original failure, not a teardown-shaped one.
|
|
50
|
+
try {
|
|
51
|
+
if (options.migration_namespaces?.length) {
|
|
52
|
+
for (const ns of options.migration_namespaces) {
|
|
53
|
+
if (reserved_migration_namespaces.includes(ns.namespace)) {
|
|
54
|
+
throw new Error(`Migration namespace "${ns.namespace}" is reserved by fuz_app — choose a different namespace`);
|
|
55
|
+
}
|
|
37
56
|
}
|
|
38
57
|
}
|
|
58
|
+
const migration_results = await run_migrations(db, [
|
|
59
|
+
auth_migration_ns,
|
|
60
|
+
...(options.migration_namespaces ?? []),
|
|
61
|
+
]);
|
|
62
|
+
const audit = audit_factory({ db, log });
|
|
63
|
+
return {
|
|
64
|
+
db_type,
|
|
65
|
+
db_name,
|
|
66
|
+
migration_results,
|
|
67
|
+
close,
|
|
68
|
+
deps: {
|
|
69
|
+
keyring,
|
|
70
|
+
password,
|
|
71
|
+
db,
|
|
72
|
+
stat,
|
|
73
|
+
read_text_file,
|
|
74
|
+
delete_file,
|
|
75
|
+
log,
|
|
76
|
+
audit,
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
try {
|
|
82
|
+
await close();
|
|
83
|
+
}
|
|
84
|
+
catch (close_err) {
|
|
85
|
+
log.error('create_app_backend: failed to close db after init error:', close_err);
|
|
86
|
+
}
|
|
87
|
+
throw err;
|
|
39
88
|
}
|
|
40
|
-
const migration_results = await run_migrations(db, [
|
|
41
|
-
auth_migration_ns,
|
|
42
|
-
...(options.migration_namespaces ?? []),
|
|
43
|
-
]);
|
|
44
|
-
const audit = create_audit_emitter({
|
|
45
|
-
db,
|
|
46
|
-
log,
|
|
47
|
-
on_audit_event: options.on_audit_event,
|
|
48
|
-
audit_log_config: options.audit_log_config,
|
|
49
|
-
});
|
|
50
|
-
return {
|
|
51
|
-
db_type,
|
|
52
|
-
db_name,
|
|
53
|
-
migration_results,
|
|
54
|
-
close,
|
|
55
|
-
deps: {
|
|
56
|
-
keyring,
|
|
57
|
-
password,
|
|
58
|
-
db,
|
|
59
|
-
stat,
|
|
60
|
-
read_text_file,
|
|
61
|
-
delete_file,
|
|
62
|
-
log,
|
|
63
|
-
audit,
|
|
64
|
-
},
|
|
65
|
-
};
|
|
66
89
|
};
|