@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
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
10
|
import { Hono, type Context } from 'hono';
|
|
11
|
+
import type { UpgradeWebSocket } from 'hono/ws';
|
|
11
12
|
import { z } from 'zod';
|
|
12
13
|
import { type SessionOptions } from '../auth/session_cookie.js';
|
|
13
14
|
import type { BootstrapAccountSuccess } from '../auth/bootstrap_account.js';
|
|
@@ -25,6 +26,8 @@ import { type AppSurfaceSpec, type RpcEndpointSpec } from '../http/surface.js';
|
|
|
25
26
|
import { type RouteSpec } from '../http/route_spec.js';
|
|
26
27
|
import type { MiddlewareSpec } from '../http/middleware_spec.js';
|
|
27
28
|
import { type BootstrapStatus } from '../auth/bootstrap_routes.js';
|
|
29
|
+
import type { WsEndpointSpec } from '../actions/ws_endpoint_spec.js';
|
|
30
|
+
import { BackendWebsocketTransport } from '../actions/transports_ws_backend.js';
|
|
28
31
|
/**
|
|
29
32
|
* Context passed to `on_effect_error` when a pending effect rejects.
|
|
30
33
|
*/
|
|
@@ -34,6 +37,46 @@ export interface EffectErrorContext {
|
|
|
34
37
|
/** URL path of the request that spawned the effect. */
|
|
35
38
|
path: string;
|
|
36
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Bootstrap configuration for `AppServerOptions.bootstrap`.
|
|
42
|
+
*
|
|
43
|
+
* Discriminated union over three deployment intents. Distinct from
|
|
44
|
+
* `BootstrapRouteOptions` in `auth/bootstrap_routes.ts` — that one is
|
|
45
|
+
* per-factory runtime state (mutable `bootstrap_status` ref, rate
|
|
46
|
+
* limiter); this one is the consumer-facing server option that
|
|
47
|
+
* `create_app_server` reads at startup to decide whether to mount the
|
|
48
|
+
* routes and where.
|
|
49
|
+
*
|
|
50
|
+
* Three modes:
|
|
51
|
+
* - `disabled` — no route mounted, nothing in `/api/surface`.
|
|
52
|
+
* Equivalent to omitting `bootstrap` entirely; the explicit mode is
|
|
53
|
+
* for documentation and reviewable intent at the wiring layer.
|
|
54
|
+
* - `surface_only` — route present, permanent 403 via
|
|
55
|
+
* `check_bootstrap_status`. For tests asserting on the
|
|
56
|
+
* disabled-but-present wire shape.
|
|
57
|
+
* - `live` — route mounted, real token verification. Success path
|
|
58
|
+
* reachable. `token_path` is required (non-nullable).
|
|
59
|
+
*/
|
|
60
|
+
export type BootstrapServerOptions = BootstrapDisabledOptions | BootstrapSurfaceOnlyOptions | BootstrapLiveOptions;
|
|
61
|
+
export interface BootstrapDisabledOptions {
|
|
62
|
+
mode: 'disabled';
|
|
63
|
+
}
|
|
64
|
+
export interface BootstrapSurfaceOnlyOptions {
|
|
65
|
+
mode: 'surface_only';
|
|
66
|
+
/** Route prefix for surface generation. Default `'/api/account'`. */
|
|
67
|
+
route_prefix?: string;
|
|
68
|
+
}
|
|
69
|
+
export interface BootstrapLiveOptions {
|
|
70
|
+
mode: 'live';
|
|
71
|
+
token_path: string;
|
|
72
|
+
/** Route prefix for bootstrap routes. Default `'/api/account'`. */
|
|
73
|
+
route_prefix?: string;
|
|
74
|
+
/**
|
|
75
|
+
* Called after successful bootstrap (account + session created).
|
|
76
|
+
* Use for app-specific post-bootstrap work like generating API tokens.
|
|
77
|
+
*/
|
|
78
|
+
on_bootstrap?: (result: BootstrapAccountSuccess, c: Context) => Promise<void>;
|
|
79
|
+
}
|
|
37
80
|
/**
|
|
38
81
|
* Configuration for `create_app_server()`.
|
|
39
82
|
*
|
|
@@ -108,16 +151,7 @@ export interface AppServerOptions {
|
|
|
108
151
|
/** Daemon token state for keeper auth. Omit to disable. */
|
|
109
152
|
daemon_token_state?: DaemonTokenState;
|
|
110
153
|
/** Bootstrap options. Omit to skip bootstrap status check and routes. */
|
|
111
|
-
bootstrap?:
|
|
112
|
-
token_path: string | null;
|
|
113
|
-
/** Route prefix for bootstrap routes. Default `'/api/account'`. */
|
|
114
|
-
route_prefix?: string;
|
|
115
|
-
/**
|
|
116
|
-
* Called after successful bootstrap (account + session created).
|
|
117
|
-
* Use for app-specific post-bootstrap work like generating API tokens.
|
|
118
|
-
*/
|
|
119
|
-
on_bootstrap?: (result: BootstrapAccountSuccess, c: Context) => Promise<void>;
|
|
120
|
-
};
|
|
154
|
+
bootstrap?: BootstrapServerOptions;
|
|
121
155
|
/**
|
|
122
156
|
* Set to `false` to disable the auto-created surface route (`GET /api/surface`).
|
|
123
157
|
* Default: auto-created (authenticated).
|
|
@@ -163,6 +197,50 @@ export interface AppServerOptions {
|
|
|
163
197
|
* `create_standard_rpc_actions(ctx.deps, {app_settings: ctx.app_settings})`.
|
|
164
198
|
*/
|
|
165
199
|
rpc_endpoints?: Array<RpcEndpointSpec> | ((context: AppServerContext) => Array<RpcEndpointSpec>);
|
|
200
|
+
/**
|
|
201
|
+
* Hono adapter's `upgradeWebSocket` helper. Required whenever
|
|
202
|
+
* `ws_endpoints` resolves to a non-empty array — `create_app_server`
|
|
203
|
+
* throws at assembly otherwise. Omit (along with `ws_endpoints`)
|
|
204
|
+
* when the consumer doesn't mount any WS endpoints. The same
|
|
205
|
+
* adapter helper services every `WsEndpointSpec` mounted from
|
|
206
|
+
* `ws_endpoints` — one adapter per app.
|
|
207
|
+
*
|
|
208
|
+
* For Node, `import {upgradeWebSocket} from '@hono/node-ws'`. For
|
|
209
|
+
* Deno, `import {upgradeWebSocket} from 'hono/deno'`. Test harnesses
|
|
210
|
+
* use `create_stub_upgrade` from `$lib/testing/ws_round_trip.ts`.
|
|
211
|
+
*/
|
|
212
|
+
upgradeWebSocket?: UpgradeWebSocket;
|
|
213
|
+
/**
|
|
214
|
+
* WebSocket endpoint specs — single source of truth for both surface
|
|
215
|
+
* generation *and* live dispatch. Each entry is auto-mounted via
|
|
216
|
+
* `register_ws_endpoint` against the assembled Hono app, so
|
|
217
|
+
* consumers no longer call `register_ws_endpoint` themselves.
|
|
218
|
+
*
|
|
219
|
+
* Accepts either an array (evaluated eagerly) or a factory
|
|
220
|
+
* `(ctx: AppServerContext) => ReadonlyArray<WsEndpointSpec>`
|
|
221
|
+
* (evaluated after the server context is assembled). Use the factory
|
|
222
|
+
* form when action lists depend on `ctx.deps` /
|
|
223
|
+
* `ctx.action_*_rate_limiter` — e.g. when spreading
|
|
224
|
+
* `create_standard_rpc_actions(ctx.deps, ...)` over WS.
|
|
225
|
+
*
|
|
226
|
+
* When non-empty, `upgradeWebSocket` must be supplied (throws
|
|
227
|
+
* otherwise). A factory returning `[]` does NOT trip the check —
|
|
228
|
+
* feature-flag gated WS surfaces stay safe.
|
|
229
|
+
*
|
|
230
|
+
* Duplicate `path` values across two `WsEndpointSpec`s throw at
|
|
231
|
+
* mount time (Hono would silently shadow them otherwise).
|
|
232
|
+
*
|
|
233
|
+
* Each spec's `auth_guard?` defaults to `true` — the factory
|
|
234
|
+
* composes `create_ws_auth_guard` + `create_ws_logout_closer`
|
|
235
|
+
* against the mounted transport and appends them to
|
|
236
|
+
* `deps.audit.on_event_chain`. Wiring is deduped by transport
|
|
237
|
+
* **reference identity** so two specs sharing one
|
|
238
|
+
* `BackendWebsocketTransport` instance get a single pair of
|
|
239
|
+
* listeners; wrapped / proxied transports dedupe as separate
|
|
240
|
+
* entries (set `auth_guard: false` on duplicates and compose
|
|
241
|
+
* against the underlying transport once).
|
|
242
|
+
*/
|
|
243
|
+
ws_endpoints?: ReadonlyArray<WsEndpointSpec> | ((context: AppServerContext) => ReadonlyArray<WsEndpointSpec>);
|
|
166
244
|
/**
|
|
167
245
|
* Env schema for surface generation. Defaults to `BaseServerEnv` —
|
|
168
246
|
* pass an extended schema (typically `BaseServerEnv.extend({...})`)
|
|
@@ -241,6 +319,19 @@ export interface AppServer {
|
|
|
241
319
|
* Use `require_audit_sse(server)` to assert the invariant.
|
|
242
320
|
*/
|
|
243
321
|
audit_sse: AuditLogSse | null;
|
|
322
|
+
/**
|
|
323
|
+
* Path-keyed map of mounted WS endpoints. Each value is the
|
|
324
|
+
* `BackendWebsocketTransport` `create_app_server` registered
|
|
325
|
+
* connections against — supplied via `WsEndpointSpec.transport` or
|
|
326
|
+
* auto-created when omitted. Retain for broadcast / fan-out:
|
|
327
|
+
*
|
|
328
|
+
* ```ts
|
|
329
|
+
* app_server.ws_endpoints['/api/ws'].send_to_account(account_id, msg);
|
|
330
|
+
* ```
|
|
331
|
+
*
|
|
332
|
+
* Empty when no `ws_endpoints` were mounted.
|
|
333
|
+
*/
|
|
334
|
+
ws_endpoints: Readonly<Record<string, BackendWebsocketTransport>>;
|
|
244
335
|
/** Close the database connection. Propagated from `AppBackend`. */
|
|
245
336
|
close: () => Promise<void>;
|
|
246
337
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAC,IAAI,EAAE,KAAK,OAAO,EAAC,MAAM,MAAM,CAAC;AAGxC,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,EAEN,KAAK,cAAc,EAEnB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,8BAA8B,CAAC;AAC1E,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAGN,KAAK,WAAW,EAChB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,gCAAgC,CAAC;AAEhE,OAAO,EAKN,KAAK,WAAW,EAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAGjD,OAAO,oBAAoB,CAAC;AAE5B,OAAO,EAA2B,KAAK,kBAAkB,EAAC,MAAM,aAAa,CAAC;AAE9E,OAAO,EAEN,KAAK,cAAc,EAEnB,KAAK,eAAe,EACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAGN,KAAK,eAAe,EACpB,MAAM,6BAA6B,CAAC;AASrC;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAClC,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;CACb;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAChC,2DAA2D;IAC3D,OAAO,EAAE,UAAU,CAAC;IACpB,6CAA6C;IAC7C,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,sCAAsC;IACtC,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAE/B,6BAA6B;IAC7B,KAAK,EAAE;QACN,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QAC/B,iBAAiB,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,MAAM,GAAG,SAAS,CAAC;KACtD,CAAC;IAEF;;;;;OAKG;IACH,eAAe,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACrC;;;;;OAKG;IACH,0BAA0B,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAChD;;;;;OAKG;IACH,2BAA2B,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACjD;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5C;;;;;;;;OAQG;IACH,sBAAsB,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5C;;;;;;;;OAQG;IACH,2BAA2B,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACjD;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,2DAA2D;IAC3D,kBAAkB,CAAC,EAAE,gBAAgB,CAAC;IAEtC,yEAAyE;IACzE,SAAS,CAAC,EAAE
|
|
1
|
+
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_server.ts"],"names":[],"mappings":"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"}
|
|
@@ -32,6 +32,9 @@ import { fuz_auth_guard_resolver } from '../auth/auth_guard_resolver.js';
|
|
|
32
32
|
import { create_fuz_authorization_handler } from '../auth/request_context.js';
|
|
33
33
|
import { ERROR_PAYLOAD_TOO_LARGE } from '../http/error_schemas.js';
|
|
34
34
|
import { create_rpc_endpoint } from '../actions/action_rpc.js';
|
|
35
|
+
import { register_ws_endpoint } from '../actions/register_ws_endpoint.js';
|
|
36
|
+
import { create_ws_auth_guard, create_ws_logout_closer, } from '../actions/transports_ws_auth_guard.js';
|
|
37
|
+
import { BackendWebsocketTransport } from '../actions/transports_ws_backend.js';
|
|
35
38
|
/**
|
|
36
39
|
* Assert that `audit_sse` was wired by `create_app_server` and return it
|
|
37
40
|
* as a non-null `AuditLogSse`. Throws a labelled error when the
|
|
@@ -121,13 +124,24 @@ export const create_app_server = async (options) => {
|
|
|
121
124
|
middleware_specs = options.transform_middleware(middleware_specs);
|
|
122
125
|
}
|
|
123
126
|
// Bootstrap status + app settings
|
|
124
|
-
|
|
127
|
+
// - undefined / 'disabled': no route mounted; placeholder status.
|
|
128
|
+
// - 'surface_only': route mounted but permanently unavailable; status placeholder.
|
|
129
|
+
// - 'live': real disk + lock check via `check_bootstrap_status`.
|
|
130
|
+
const bootstrap_status = options.bootstrap?.mode === 'live'
|
|
125
131
|
? await check_bootstrap_status(deps, { token_path: options.bootstrap.token_path })
|
|
126
132
|
: { available: false, token_path: null };
|
|
127
133
|
const app_settings = await query_app_settings_load({ db: deps.db });
|
|
128
134
|
// Surface route ref — factory manages the circular ref
|
|
129
135
|
const surface_ref = {
|
|
130
|
-
surface: {
|
|
136
|
+
surface: {
|
|
137
|
+
middleware: [],
|
|
138
|
+
routes: [],
|
|
139
|
+
rpc_endpoints: [],
|
|
140
|
+
ws_endpoints: [],
|
|
141
|
+
env: [],
|
|
142
|
+
events: [],
|
|
143
|
+
diagnostics: [],
|
|
144
|
+
},
|
|
131
145
|
};
|
|
132
146
|
// Route specs (consumer routes + factory-managed routes)
|
|
133
147
|
const context = {
|
|
@@ -146,12 +160,15 @@ export const create_app_server = async (options) => {
|
|
|
146
160
|
const consumer_routes = options.create_route_specs(context);
|
|
147
161
|
// Factory-managed routes appended after consumer routes
|
|
148
162
|
const factory_routes = [];
|
|
149
|
-
// Bootstrap routes
|
|
150
|
-
|
|
163
|
+
// Bootstrap routes — mounted for 'surface_only' and 'live'; omitted for
|
|
164
|
+
// 'disabled' / undefined. The route handler short-circuits to 403 when
|
|
165
|
+
// `bootstrap_status.available === false`, which is the steady state for
|
|
166
|
+
// 'surface_only' and the post-bootstrap state for 'live'.
|
|
167
|
+
if (options.bootstrap && options.bootstrap.mode !== 'disabled') {
|
|
151
168
|
const bootstrap_routes = create_bootstrap_route_specs(deps, {
|
|
152
169
|
session_options: options.session_options,
|
|
153
170
|
bootstrap_status,
|
|
154
|
-
on_bootstrap: options.bootstrap.on_bootstrap,
|
|
171
|
+
on_bootstrap: options.bootstrap.mode === 'live' ? options.bootstrap.on_bootstrap : undefined,
|
|
155
172
|
ip_rate_limiter,
|
|
156
173
|
});
|
|
157
174
|
const prefix = options.bootstrap.route_prefix ?? '/api/account';
|
|
@@ -173,6 +190,15 @@ export const create_app_server = async (options) => {
|
|
|
173
190
|
}));
|
|
174
191
|
}
|
|
175
192
|
}
|
|
193
|
+
// WS endpoint resolution — done here (alongside RPC) so the captured
|
|
194
|
+
// array threads into surface generation below. Actual mount happens
|
|
195
|
+
// after `apply_route_specs` because `register_ws_endpoint` mutates the
|
|
196
|
+
// live Hono `app` (origin / auth / role / authorization middleware +
|
|
197
|
+
// the `app.get(path, ...)` upgrade route), and `app` does not exist
|
|
198
|
+
// until the assembly phase below.
|
|
199
|
+
const resolved_ws_endpoints = typeof options.ws_endpoints === 'function'
|
|
200
|
+
? options.ws_endpoints(context)
|
|
201
|
+
: options.ws_endpoints;
|
|
176
202
|
// Surface route (default: enabled)
|
|
177
203
|
if (options.surface_route !== false) {
|
|
178
204
|
factory_routes.push(create_surface_route_spec(surface_ref));
|
|
@@ -192,6 +218,7 @@ export const create_app_server = async (options) => {
|
|
|
192
218
|
env_schema: options.env_schema ?? BaseServerEnv,
|
|
193
219
|
event_specs: all_event_specs,
|
|
194
220
|
rpc_endpoints: resolved_rpc_endpoints,
|
|
221
|
+
ws_endpoints: resolved_ws_endpoints,
|
|
195
222
|
});
|
|
196
223
|
// Config-level diagnostics (concatenated after spec-level from generate_app_surface)
|
|
197
224
|
const config_diagnostics = [];
|
|
@@ -215,7 +242,7 @@ export const create_app_server = async (options) => {
|
|
|
215
242
|
config_diagnostics.push({
|
|
216
243
|
level: 'warning',
|
|
217
244
|
category: 'security',
|
|
218
|
-
message: 'Session cookie httpOnly=false — cookie accessible to
|
|
245
|
+
message: 'Session cookie httpOnly=false — cookie accessible to JS',
|
|
219
246
|
});
|
|
220
247
|
}
|
|
221
248
|
}
|
|
@@ -291,6 +318,77 @@ export const create_app_server = async (options) => {
|
|
|
291
318
|
apply_middleware_specs(app, middleware_specs);
|
|
292
319
|
const authorize = create_fuz_authorization_handler({ db: deps.db });
|
|
293
320
|
apply_route_specs(app, route_specs, fuz_auth_guard_resolver, log, deps.db, authorize);
|
|
321
|
+
// WS endpoint auto-mount — must run after `app` exists and
|
|
322
|
+
// `apply_route_specs` has registered the request routes. Each spec
|
|
323
|
+
// becomes a `register_ws_endpoint` call, plus optional `auth_guard`
|
|
324
|
+
// wiring onto the audit chain. `post_route_middleware` and static
|
|
325
|
+
// serving register after this loop, so WS upgrade routes sit
|
|
326
|
+
// adjacent to the consumer routes and ahead of the static fallback —
|
|
327
|
+
// matches the "WS mount is route registration" mental model.
|
|
328
|
+
const mounted_ws_endpoints = {};
|
|
329
|
+
if (resolved_ws_endpoints?.length) {
|
|
330
|
+
if (options.upgradeWebSocket === undefined) {
|
|
331
|
+
throw new Error('create_app_server: ws_endpoints resolved non-empty but upgradeWebSocket is missing. ' +
|
|
332
|
+
"Pass the Hono adapter's upgradeWebSocket helper as a top-level option.");
|
|
333
|
+
}
|
|
334
|
+
// Cross-surface collision: `register_ws_endpoint` mounts a `GET path`
|
|
335
|
+
// upgrade route. If a `RouteSpec` already registered `GET path`,
|
|
336
|
+
// Hono's last-wins semantics would silently shadow the consumer's
|
|
337
|
+
// GET route — fail fast instead.
|
|
338
|
+
const route_spec_get_paths = new Set();
|
|
339
|
+
for (const r of route_specs) {
|
|
340
|
+
if (r.method === 'GET')
|
|
341
|
+
route_spec_get_paths.add(r.path);
|
|
342
|
+
}
|
|
343
|
+
const seen_paths = new Set();
|
|
344
|
+
// Dedupe `auth_guard` wiring by transport reference — two specs
|
|
345
|
+
// sharing one transport instance get a single pair of listeners,
|
|
346
|
+
// otherwise revocation events would fire `close_sockets_for_*`
|
|
347
|
+
// twice per event (idempotent on the transport but log-spammy).
|
|
348
|
+
// Cross-spec OR-semantics: any spec with `auth_guard !== false`
|
|
349
|
+
// wires the guard for that transport; once wired, sibling specs
|
|
350
|
+
// (even with explicit `auth_guard: false`) cannot opt out. To
|
|
351
|
+
// disable, every spec sharing the transport must pass `auth_guard: false`.
|
|
352
|
+
const guarded_transports = new WeakSet();
|
|
353
|
+
for (const endpoint of resolved_ws_endpoints) {
|
|
354
|
+
if (seen_paths.has(endpoint.path)) {
|
|
355
|
+
throw new Error(`create_app_server: duplicate ws_endpoints path: ${endpoint.path}`);
|
|
356
|
+
}
|
|
357
|
+
if (route_spec_get_paths.has(endpoint.path)) {
|
|
358
|
+
throw new Error(`create_app_server: ws_endpoints path collides with a GET RouteSpec: ${endpoint.path}`);
|
|
359
|
+
}
|
|
360
|
+
seen_paths.add(endpoint.path);
|
|
361
|
+
const endpoint_transport = endpoint.transport ?? new BackendWebsocketTransport();
|
|
362
|
+
register_ws_endpoint({
|
|
363
|
+
app,
|
|
364
|
+
path: endpoint.path,
|
|
365
|
+
upgradeWebSocket: options.upgradeWebSocket,
|
|
366
|
+
allowed_origins: endpoint.allowed_origins,
|
|
367
|
+
db: deps.db,
|
|
368
|
+
actions: endpoint.actions,
|
|
369
|
+
transport: endpoint_transport,
|
|
370
|
+
heartbeat: endpoint.heartbeat,
|
|
371
|
+
artificial_delay: endpoint.artificial_delay,
|
|
372
|
+
on_socket_open: endpoint.on_socket_open,
|
|
373
|
+
on_socket_close: endpoint.on_socket_close,
|
|
374
|
+
log,
|
|
375
|
+
required_roles: endpoint.required_roles,
|
|
376
|
+
action_ip_rate_limiter,
|
|
377
|
+
action_account_rate_limiter,
|
|
378
|
+
});
|
|
379
|
+
mounted_ws_endpoints[endpoint.path] = endpoint_transport;
|
|
380
|
+
if (endpoint.auth_guard !== false && !guarded_transports.has(endpoint_transport)) {
|
|
381
|
+
guarded_transports.add(endpoint_transport);
|
|
382
|
+
deps.audit.on_event_chain.push(create_ws_auth_guard(endpoint_transport, log));
|
|
383
|
+
deps.audit.on_event_chain.push(create_ws_logout_closer(endpoint_transport, log));
|
|
384
|
+
}
|
|
385
|
+
if (endpoint.extra_audit_handlers?.length) {
|
|
386
|
+
for (const handler of endpoint.extra_audit_handlers) {
|
|
387
|
+
deps.audit.on_event_chain.push(handler);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
294
392
|
// Post-route middleware (before static serving)
|
|
295
393
|
if (options.post_route_middleware) {
|
|
296
394
|
apply_middleware_specs(app, options.post_route_middleware);
|
|
@@ -309,6 +407,7 @@ export const create_app_server = async (options) => {
|
|
|
309
407
|
app_settings,
|
|
310
408
|
migration_results: backend.migration_results,
|
|
311
409
|
audit_sse,
|
|
410
|
+
ws_endpoints: mounted_ws_endpoints,
|
|
312
411
|
close: backend.close,
|
|
313
412
|
};
|
|
314
413
|
};
|
package/dist/server/env.d.ts
CHANGED
|
@@ -26,12 +26,12 @@ export declare const BaseServerEnv: z.ZodObject<{
|
|
|
26
26
|
PORT: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
27
27
|
HOST: z.ZodDefault<z.ZodString>;
|
|
28
28
|
DATABASE_URL: z.ZodString;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
SECRET_FUZ_COOKIE_KEYS: z.ZodString;
|
|
30
|
+
FUZ_ALLOWED_ORIGINS: z.ZodString;
|
|
31
|
+
PUBLIC_FUZ_API_URL: z.ZodDefault<z.ZodString>;
|
|
32
|
+
PUBLIC_FUZ_WEBSOCKET_URL: z.ZodOptional<z.ZodString>;
|
|
33
|
+
PUBLIC_FUZ_CONTACT_EMAIL: z.ZodOptional<z.ZodUnion<readonly [z.ZodEmail, z.ZodLiteral<"">]>>;
|
|
34
|
+
FUZ_BOOTSTRAP_TOKEN_PATH: z.ZodOptional<z.ZodString>;
|
|
35
35
|
SMTP_HOST: z.ZodOptional<z.ZodString>;
|
|
36
36
|
SMTP_USER: z.ZodOptional<z.ZodUnion<readonly [z.ZodEmail, z.ZodLiteral<"">]>>;
|
|
37
37
|
SMTP_PASSWORD: z.ZodOptional<z.ZodString>;
|
|
@@ -51,7 +51,7 @@ export interface ServerEnvOptions {
|
|
|
51
51
|
*/
|
|
52
52
|
export interface ServerEnvOptionsError {
|
|
53
53
|
ok: false;
|
|
54
|
-
field: '
|
|
54
|
+
field: 'SECRET_FUZ_COOKIE_KEYS' | 'FUZ_ALLOWED_ORIGINS';
|
|
55
55
|
errors: Array<string>;
|
|
56
56
|
}
|
|
57
57
|
export type ServerEnvOptionsResult = ServerEnvOptions | ServerEnvOptionsError;
|
package/dist/server/env.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,EAA2B,KAAK,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAG1E;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;kBAkCxB,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,EAAE,EAAE,IAAI,CAAC;IACT,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACrC,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,EAA2B,KAAK,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAG1E;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;kBAkCxB,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,EAAE,EAAE,IAAI,CAAC;IACT,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACrC,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,wBAAwB,GAAG,qBAAqB,CAAC;IACxD,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACtB;AAED,MAAM,MAAM,sBAAsB,GAAG,gBAAgB,GAAG,qBAAqB,CAAC;AAE9E;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAAI,KAAK,aAAa,KAAG,sBA4BxD,CAAC"}
|
package/dist/server/env.js
CHANGED
|
@@ -27,20 +27,20 @@ export const BaseServerEnv = z.strictObject({
|
|
|
27
27
|
description: 'Database URL (postgres://, file://, or memory://)',
|
|
28
28
|
sensitivity: 'secret',
|
|
29
29
|
}),
|
|
30
|
-
|
|
30
|
+
SECRET_FUZ_COOKIE_KEYS: z.string().min(32).meta({
|
|
31
31
|
description: 'Cookie signing keys, separated by __ for rotation',
|
|
32
32
|
sensitivity: 'secret',
|
|
33
33
|
}),
|
|
34
|
-
|
|
34
|
+
FUZ_ALLOWED_ORIGINS: z.string().min(1, 'FUZ_ALLOWED_ORIGINS is required').meta({
|
|
35
35
|
description: 'Comma-separated origin patterns for API verification',
|
|
36
36
|
}),
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
PUBLIC_FUZ_API_URL: z.string().default('/api').meta({ description: 'Public API base URL' }),
|
|
38
|
+
PUBLIC_FUZ_WEBSOCKET_URL: z.string().optional().meta({ description: 'Public WebSocket URL' }),
|
|
39
|
+
PUBLIC_FUZ_CONTACT_EMAIL: z
|
|
40
40
|
.union([z.email(), z.literal('')])
|
|
41
41
|
.optional()
|
|
42
42
|
.meta({ description: 'Public contact email address' }),
|
|
43
|
-
|
|
43
|
+
FUZ_BOOTSTRAP_TOKEN_PATH: z
|
|
44
44
|
.string()
|
|
45
45
|
.optional()
|
|
46
46
|
.meta({ description: 'Path to one-shot admin bootstrap token', sensitivity: 'secret' }),
|
|
@@ -64,32 +64,32 @@ export const BaseServerEnv = z.strictObject({
|
|
|
64
64
|
* @returns `{ok: true, keyring, allowed_origins, bootstrap_token_path}` or `{ok: false, field, errors}`
|
|
65
65
|
*/
|
|
66
66
|
export const validate_server_env = (env) => {
|
|
67
|
-
const keyring_result = create_validated_keyring(env.
|
|
67
|
+
const keyring_result = create_validated_keyring(env.SECRET_FUZ_COOKIE_KEYS);
|
|
68
68
|
if (!keyring_result.ok) {
|
|
69
|
-
return { ok: false, field: '
|
|
69
|
+
return { ok: false, field: 'SECRET_FUZ_COOKIE_KEYS', errors: keyring_result.errors };
|
|
70
70
|
}
|
|
71
71
|
let allowed_origins;
|
|
72
72
|
try {
|
|
73
|
-
allowed_origins = parse_allowed_origins(env.
|
|
73
|
+
allowed_origins = parse_allowed_origins(env.FUZ_ALLOWED_ORIGINS);
|
|
74
74
|
}
|
|
75
75
|
catch (err) {
|
|
76
76
|
return {
|
|
77
77
|
ok: false,
|
|
78
|
-
field: '
|
|
79
|
-
errors: [err instanceof Error ? err.message : 'Invalid
|
|
78
|
+
field: 'FUZ_ALLOWED_ORIGINS',
|
|
79
|
+
errors: [err instanceof Error ? err.message : 'Invalid FUZ_ALLOWED_ORIGINS'],
|
|
80
80
|
};
|
|
81
81
|
}
|
|
82
82
|
if (allowed_origins.length === 0) {
|
|
83
83
|
return {
|
|
84
84
|
ok: false,
|
|
85
|
-
field: '
|
|
86
|
-
errors: ['
|
|
85
|
+
field: 'FUZ_ALLOWED_ORIGINS',
|
|
86
|
+
errors: ['FUZ_ALLOWED_ORIGINS contains no valid patterns'],
|
|
87
87
|
};
|
|
88
88
|
}
|
|
89
89
|
return {
|
|
90
90
|
ok: true,
|
|
91
91
|
keyring: keyring_result.keyring,
|
|
92
92
|
allowed_origins,
|
|
93
|
-
bootstrap_token_path: env.
|
|
93
|
+
bootstrap_token_path: env.FUZ_BOOTSTRAP_TOKEN_PATH ?? null,
|
|
94
94
|
};
|
|
95
95
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"startup.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/startup.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAGpD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,oBAAoB,CAAC;AAEnD;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAC/B,SAAS,UAAU,EACnB,KAAK,MAAM,EACX,aAAa,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAClC,
|
|
1
|
+
{"version":3,"file":"startup.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/startup.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAGpD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,oBAAoB,CAAC;AAEnD;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAC/B,SAAS,UAAU,EACnB,KAAK,MAAM,EACX,aAAa,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAClC,IAkDF,CAAC"}
|
package/dist/server/startup.js
CHANGED
|
@@ -17,6 +17,18 @@ import { format_env_display_value } from '../env/mask.js';
|
|
|
17
17
|
*/
|
|
18
18
|
export const log_startup_summary = (surface, log, env_values) => {
|
|
19
19
|
log.info(`Surface: ${surface.routes.length} routes, ${surface.middleware.length} middleware layers`);
|
|
20
|
+
// Endpoint surfaces — logged when non-empty so operators can confirm
|
|
21
|
+
// auto-mount picked up the expected actions (and so a factory that
|
|
22
|
+
// silently returns `[]` is loud at boot instead of a method_not_found
|
|
23
|
+
// at first call).
|
|
24
|
+
if (surface.rpc_endpoints.length) {
|
|
25
|
+
const rpc_method_count = surface.rpc_endpoints.reduce((sum, ep) => sum + ep.methods.length, 0);
|
|
26
|
+
log.info(`RPC: ${surface.rpc_endpoints.length} endpoint(s), ${rpc_method_count} method(s)`);
|
|
27
|
+
}
|
|
28
|
+
if (surface.ws_endpoints.length) {
|
|
29
|
+
const ws_method_count = surface.ws_endpoints.reduce((sum, ep) => sum + ep.methods.length, 0);
|
|
30
|
+
log.info(`WS: ${surface.ws_endpoints.length} endpoint(s), ${ws_method_count} method(s)`);
|
|
31
|
+
}
|
|
20
32
|
if (surface.env.length) {
|
|
21
33
|
const required = surface.env.filter((e) => !e.optional);
|
|
22
34
|
const secret = surface.env.filter((e) => e.sensitivity === 'secret');
|
package/dist/server/static.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Static file serving middleware for SvelteKit static builds.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* -
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
4
|
+
* Multi-step static serving:
|
|
5
|
+
* - Step 1: Exact path match (handles /, assets, images)
|
|
6
|
+
* - Step 2: `.html` fallback for clean URLs (`/about` → `/about.html`)
|
|
7
|
+
* - Step 3 (optional): SPA fallback for client-side routes
|
|
8
8
|
*
|
|
9
9
|
* @module
|
|
10
10
|
*/
|
package/dist/server/static.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Static file serving middleware for SvelteKit static builds.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* -
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
4
|
+
* Multi-step static serving:
|
|
5
|
+
* - Step 1: Exact path match (handles /, assets, images)
|
|
6
|
+
* - Step 2: `.html` fallback for clean URLs (`/about` → `/about.html`)
|
|
7
|
+
* - Step 3 (optional): SPA fallback for client-side routes
|
|
8
8
|
*
|
|
9
9
|
* @module
|
|
10
10
|
*/
|
|
@@ -21,16 +21,16 @@ const is_spa_route_default = (path) => !path.startsWith('/api/');
|
|
|
21
21
|
export const create_static_middleware = (serve_static, options) => {
|
|
22
22
|
const root = options?.root ?? './build';
|
|
23
23
|
const handlers = [];
|
|
24
|
-
//
|
|
24
|
+
// Step 1: exact path match
|
|
25
25
|
handlers.push(serve_static({ root }));
|
|
26
|
-
//
|
|
26
|
+
// Step 2: .html fallback for clean URLs (/about → /about.html)
|
|
27
27
|
handlers.push(async (c, next) => {
|
|
28
28
|
const path = c.req.path;
|
|
29
29
|
if (path === '/' || path.includes('.'))
|
|
30
30
|
return next();
|
|
31
31
|
return serve_static({ root, rewriteRequestPath: () => `${path}.html` })(c, next);
|
|
32
32
|
});
|
|
33
|
-
//
|
|
33
|
+
// Step 3: optional SPA fallback for client-side routes
|
|
34
34
|
if (options?.spa_fallback) {
|
|
35
35
|
const fallback = options.spa_fallback;
|
|
36
36
|
const is_spa_route = options.is_spa_route ?? is_spa_route_default;
|