devframe 0.5.4 → 0.6.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/dist/{_shared-CUFqO4kJ.mjs → _shared-bWRzeSa0.mjs} +2 -3
  2. package/dist/adapters/build.d.mts +1 -1
  3. package/dist/adapters/build.mjs +7 -7
  4. package/dist/adapters/cli.d.mts +1 -1
  5. package/dist/adapters/cli.mjs +2 -2
  6. package/dist/adapters/dev.d.mts +8 -2
  7. package/dist/adapters/dev.mjs +1 -1
  8. package/dist/adapters/embedded.d.mts +1 -1
  9. package/dist/adapters/mcp.d.mts +1 -1
  10. package/dist/adapters/mcp.mjs +4 -4
  11. package/dist/client/index.d.mts +148 -6
  12. package/dist/client/index.mjs +257 -44
  13. package/dist/constants.d.mts +17 -1
  14. package/dist/constants.mjs +17 -1
  15. package/dist/context-3x_bgBgZ.mjs +48 -0
  16. package/dist/{context-DTRcO_UH.d.mts → context-BEVByy_Z.d.mts} +1 -1
  17. package/dist/{dev-Cv43GfqM.mjs → dev-BaX8fLH2.mjs} +39 -13
  18. package/dist/{devframe-BuR6n9ZD.d.mts → devframe-D-gr9sBx.d.mts} +365 -15
  19. package/dist/{diagnostics-GitRGKbr.mjs → diagnostics-BXwBQmoN.mjs} +1 -1
  20. package/dist/{dump-9lKIJTLh.mjs → dump-DoXkj4ku.mjs} +1 -1
  21. package/dist/helpers/vite.d.mts +1 -1
  22. package/dist/helpers/vite.mjs +11 -11
  23. package/dist/{host-h3-Dgpgr1Ul.mjs → host-h3-C5SZlk03.mjs} +134 -6
  24. package/dist/{index-C7M1hnvL.d.mts → index-CxaPKDXX.d.mts} +2 -2
  25. package/dist/{index-DH2sBIwd.d.mts → index-DXHWuwJk.d.mts} +1 -1
  26. package/dist/index.d.mts +4 -4
  27. package/dist/index.mjs +1 -1
  28. package/dist/{launch-editor-BbNhtg7b.mjs → launch-editor-DPfltGA4.mjs} +88 -13
  29. package/dist/node/auth.d.mts +43 -23
  30. package/dist/node/auth.mjs +84 -37
  31. package/dist/node/hub-internals.d.mts +2 -2
  32. package/dist/node/hub-internals.mjs +2 -2
  33. package/dist/node/index.d.mts +22 -6
  34. package/dist/node/index.mjs +4 -4
  35. package/dist/recipes/open-helpers.d.mts +1 -1
  36. package/dist/recipes/open-helpers.mjs +3 -3
  37. package/dist/revoke-ENfxWkFK.mjs +79 -0
  38. package/dist/rpc/dump.d.mts +1 -1
  39. package/dist/rpc/dump.mjs +1 -1
  40. package/dist/rpc/index.d.mts +3 -3
  41. package/dist/rpc/index.mjs +4 -4
  42. package/dist/rpc/transports/ws-client.d.mts +1 -1
  43. package/dist/rpc/transports/ws-client.mjs +2 -2
  44. package/dist/rpc/transports/ws-server.d.mts +2 -2
  45. package/dist/rpc/transports/ws-server.mjs +154 -61
  46. package/dist/{serialization-BD_qXGjd.mjs → serialization-DpLXCy13.mjs} +1 -1
  47. package/dist/{server-BBaBJaUL.mjs → server-Dl6TU5LE.mjs} +19 -14
  48. package/dist/server-rX781kz-.d.mts +90 -0
  49. package/dist/{shared-state-u0y123fi.mjs → shared-state-D4PPieA0.mjs} +4 -4
  50. package/dist/{shared-state-BlBNYziY.mjs → storage-l2ezeend.mjs} +128 -6
  51. package/dist/types/index.d.mts +4 -4
  52. package/dist/{types-BkkQ0Txg.d.mts → types-DZEx4ffs.d.mts} +9 -1
  53. package/dist/utils/crypto-token.d.mts +24 -0
  54. package/dist/utils/crypto-token.mjs +45 -0
  55. package/dist/utils/events.d.mts +1 -1
  56. package/dist/utils/hash.mjs +1 -1
  57. package/dist/utils/launch-editor.mjs +1 -1
  58. package/dist/utils/open.mjs +1 -1
  59. package/dist/utils/scope.d.mts +11 -0
  60. package/dist/utils/scope.mjs +15 -0
  61. package/dist/utils/shared-state.d.mts +1 -1
  62. package/dist/utils/shared-state.mjs +1 -1
  63. package/dist/utils/streaming-channel.d.mts +1 -1
  64. package/dist/utils/structured-clone.mjs +1 -1
  65. package/dist/{ws-client-CVYX9niP.d.mts → ws-client-D0z0pOhn.d.mts} +1 -1
  66. package/dist/ws-server-Bc2wBHl-.d.mts +119 -0
  67. package/package.json +12 -7
  68. package/skills/devframe/SKILL.md +123 -23
  69. package/skills/devframe/templates/counter-devframe.ts +14 -4
  70. package/skills/devframe/templates/spa-devframe.ts +13 -3
  71. package/skills/devframe/templates/vite-client.ts +3 -2
  72. package/dist/context-DaKmhhHY.mjs +0 -164
  73. package/dist/revoke-CL0LSAN9.mjs +0 -803
  74. package/dist/server-wHlpcdZ9.d.mts +0 -55
  75. package/dist/utils/human-id.d.mts +0 -8
  76. package/dist/utils/human-id.mjs +0 -769
  77. package/dist/ws-server-C1LjmRnp.d.mts +0 -61
  78. /package/dist/{define-CW9gLnyG.mjs → define-BLWPsH6y.mjs} +0 -0
  79. /package/dist/{diagnostics-reporter-CBBZwoMv.mjs → diagnostics-reporter-CsIG85Q5.mjs} +0 -0
  80. /package/dist/{hash-bwOD8RgU.mjs → hash-DFc5WwhO.mjs} +0 -0
  81. /package/dist/{open-DiQn6zCH.mjs → open-Dusa2Zzd.mjs} +0 -0
  82. /package/dist/{structured-clone-PdCZwt7F.mjs → structured-clone-CeZOHvkd.mjs} +0 -0
  83. /package/dist/{structured-clone-jegjz0hM.mjs → structured-clone-XpHLZ8nr.mjs} +0 -0
  84. /package/dist/{transports-DTFoMUbE.mjs → transports-BmDVn4SF.mjs} +0 -0
@@ -1,8 +1,8 @@
1
- import { $ as AgentResource, K as DevframeHost, P as SharedState, Q as AgentManifest, U as DevframeRpcClientFunctions, W as DevframeRpcServerFunctions, X as DevframeDiagnosticsLogger, Y as DevframeDiagnosticsHost$1, Z as AgentHandle, at as DevframeAgentHostEvents, d as DevframeNodeContext, et as AgentResourceContent, f as DevframeNodeRpcSession, g as RpcSharedStateHost, it as DevframeAgentHost$1, m as RpcFunctionsHost$1, nt as AgentTool, ot as EventEmitter, p as RpcBroadcastOptions, rt as AgentToolInput, tt as AgentResourceInput, y as RpcStreamingHost, z as DevframeViewHost$1 } from "../devframe-BuR6n9ZD.mjs";
2
- import { _ as RpcFunctionDefinitionAny } from "../types-BkkQ0Txg.mjs";
3
- import { S as RpcFunctionsCollectorBase } from "../index-C7M1hnvL.mjs";
4
- import { t as DevframeNodeRpcSessionMeta } from "../ws-server-C1LjmRnp.mjs";
5
- import { n as StartedServer, r as startHttpAndWs, t as StartHttpAndWsOptions } from "../server-wHlpcdZ9.mjs";
1
+ import { G as DevframeScopedNodeContext, J as DevframeSettings, L as SharedState, S as RpcStreamingHost, _ as RpcFunctionsHost$1, _t as AgentToolInput, bt as EventEmitter, dt as AgentHandle, ft as AgentManifest, g as RpcBroadcastOptions, gt as AgentTool, h as DevframeNodeRpcSession, ht as AgentResourceInput, it as DevframeRpcServerFunctions, lt as DevframeDiagnosticsHost$1, m as DevframeNodeContext, mt as AgentResourceContent, nt as DevframeViewHost$1, ot as DevframeHost, pt as AgentResource, rt as DevframeRpcClientFunctions, ut as DevframeDiagnosticsLogger, vt as DevframeAgentHost$1, y as RpcSharedStateHost, yt as DevframeAgentHostEvents } from "../devframe-D-gr9sBx.mjs";
2
+ import { v as RpcFunctionDefinitionAny } from "../types-DZEx4ffs.mjs";
3
+ import { S as RpcFunctionsCollectorBase } from "../index-CxaPKDXX.mjs";
4
+ import { t as DevframeNodeRpcSessionMeta } from "../ws-server-Bc2wBHl-.mjs";
5
+ import { n as StartedServer, r as startHttpAndWs, t as StartHttpAndWsOptions } from "../server-rX781kz-.mjs";
6
6
  import { BirpcGroup } from "birpc";
7
7
  import { AsyncLocalStorage } from "node:async_hooks";
8
8
 
@@ -156,6 +156,22 @@ declare function createRpcSharedStateServerHost(rpc: RpcFunctionsHost$1): RpcSha
156
156
  */
157
157
  declare function createRpcStreamingServerHost(rpc: RpcFunctionsHost$1): RpcStreamingHost;
158
158
  //#endregion
159
+ //#region src/node/scope.d.ts
160
+ /**
161
+ * Build a namespace-scoped view of a {@link DevframeNodeContext}. Every
162
+ * RPC id, shared-state key, and streaming channel passed through the
163
+ * returned `rpc` surface is auto-namespaced with `<namespace>:`.
164
+ */
165
+ declare function createScopedNodeContext<NS extends string = string>(context: DevframeNodeContext, namespace: NS): DevframeScopedNodeContext<NS>;
166
+ //#endregion
167
+ //#region src/node/settings.d.ts
168
+ /**
169
+ * Build the node-side `settings` surface for a scope namespace. `project`
170
+ * persists under the host's `workspace` storage dir, `global` under its
171
+ * `global` dir. Each is a file-backed, client-synced key-value store.
172
+ */
173
+ declare function createNodeSettings<T extends Record<string, any> = Record<string, any>>(context: DevframeNodeContext, namespace: string): DevframeSettings<T>;
174
+ //#endregion
159
175
  //#region src/node/storage.d.ts
160
176
  interface CreateStorageOptions<T extends object> {
161
177
  filepath: string;
@@ -169,4 +185,4 @@ declare function createStorage<T extends object>(options: CreateStorageOptions<T
169
185
  declare function isObject(value: unknown): value is Record<string, any>;
170
186
  declare function normalizeHttpServerUrl(host: string, port: number | string): string;
171
187
  //#endregion
172
- export { CreateH3DevframeHostOptions, CreateHostContextOptions, CreateStorageOptions, DevframeAgentHost, DevframeDiagnosticsHost, DevframeViewHost, RpcFunctionsHost, StartHttpAndWsOptions, StartedServer, createH3DevframeHost, createHostContext, createRpcSharedStateServerHost, createRpcStreamingServerHost, createStorage, isObject, normalizeHttpServerUrl, startHttpAndWs };
188
+ export { CreateH3DevframeHostOptions, CreateHostContextOptions, CreateStorageOptions, DevframeAgentHost, DevframeDiagnosticsHost, DevframeViewHost, RpcFunctionsHost, StartHttpAndWsOptions, StartedServer, createH3DevframeHost, createHostContext, createNodeSettings, createRpcSharedStateServerHost, createRpcStreamingServerHost, createScopedNodeContext, createStorage, isObject, normalizeHttpServerUrl, startHttpAndWs };
@@ -1,6 +1,6 @@
1
- import { a as createRpcStreamingServerHost, c as DevframeAgentHost, i as RpcFunctionsHost, n as createHostContext, o as createRpcSharedStateServerHost, r as DevframeViewHost, s as DevframeDiagnosticsHost, t as createH3DevframeHost } from "../host-h3-Dgpgr1Ul.mjs";
2
- import { r as createStorage } from "../context-DaKmhhHY.mjs";
3
- import { t as startHttpAndWs } from "../server-BBaBJaUL.mjs";
1
+ import { t as createStorage } from "../storage-l2ezeend.mjs";
2
+ import { a as DevframeViewHost, c as createRpcSharedStateServerHost, i as createNodeSettings, l as DevframeDiagnosticsHost, n as createHostContext, o as RpcFunctionsHost, r as createScopedNodeContext, s as createRpcStreamingServerHost, t as createH3DevframeHost, u as DevframeAgentHost } from "../host-h3-C5SZlk03.mjs";
3
+ import { t as startHttpAndWs } from "../server-Dl6TU5LE.mjs";
4
4
  import { isIP } from "node:net";
5
5
  //#region src/node/utils.ts
6
6
  function isObject(value) {
@@ -10,4 +10,4 @@ function normalizeHttpServerUrl(host, port) {
10
10
  return `http://${host === "127.0.0.1" ? "localhost" : isIP(host) === 6 ? `[${host}]` : host}:${port}`;
11
11
  }
12
12
  //#endregion
13
- export { DevframeAgentHost, DevframeDiagnosticsHost, DevframeViewHost, RpcFunctionsHost, createH3DevframeHost, createHostContext, createRpcSharedStateServerHost, createRpcStreamingServerHost, createStorage, isObject, normalizeHttpServerUrl, startHttpAndWs };
13
+ export { DevframeAgentHost, DevframeDiagnosticsHost, DevframeViewHost, RpcFunctionsHost, createH3DevframeHost, createHostContext, createNodeSettings, createRpcSharedStateServerHost, createRpcStreamingServerHost, createScopedNodeContext, createStorage, isObject, normalizeHttpServerUrl, startHttpAndWs };
@@ -1,4 +1,4 @@
1
- import { T as Thenable, h as RpcFunctionAgentOptions, s as RpcDump, x as RpcFunctionSetupResult } from "../types-BkkQ0Txg.mjs";
1
+ import { E as Thenable, S as RpcFunctionSetupResult, c as RpcDump, g as RpcFunctionAgentOptions } from "../types-DZEx4ffs.mjs";
2
2
  import * as v from "valibot";
3
3
 
4
4
  //#region src/recipes/open-helpers.d.ts
@@ -1,6 +1,6 @@
1
- import { t as launchEditor } from "../launch-editor-BbNhtg7b.mjs";
2
- import { n as defineRpcFunction } from "../define-CW9gLnyG.mjs";
3
- import { t as open } from "../open-DiQn6zCH.mjs";
1
+ import { t as launchEditor } from "../launch-editor-DPfltGA4.mjs";
2
+ import { n as defineRpcFunction } from "../define-BLWPsH6y.mjs";
3
+ import { t as open } from "../open-Dusa2Zzd.mjs";
4
4
  import * as v from "valibot";
5
5
  //#region src/recipes/open-helpers.ts
6
6
  /**
@@ -0,0 +1,79 @@
1
+ //#region src/utils/crypto-token.ts
2
+ const HEX = "0123456789abcdef";
3
+ /**
4
+ * Generate a high-entropy, URL-safe (hex) random token suitable for use as a
5
+ * bearer credential — e.g. the persistent client auth token or an ephemeral
6
+ * remote-dock token. Defaults to 16 bytes (128 bits) of entropy.
7
+ */
8
+ function randomToken(byteLength = 16) {
9
+ const bytes = new Uint8Array(byteLength);
10
+ globalThis.crypto.getRandomValues(bytes);
11
+ let out = "";
12
+ for (let i = 0; i < bytes.length; i++) out += HEX[bytes[i] >> 4] + HEX[bytes[i] & 15];
13
+ return out;
14
+ }
15
+ /**
16
+ * Generate a uniformly-distributed string of decimal digits using rejection
17
+ * sampling to avoid modulo bias. Intended for short, human-typed one-time
18
+ * codes (e.g. a 6-digit authentication code). Leading zeros are preserved.
19
+ */
20
+ function randomDigits(length) {
21
+ const limit = 250;
22
+ const buf = new Uint8Array(1);
23
+ let out = "";
24
+ while (out.length < length) {
25
+ globalThis.crypto.getRandomValues(buf);
26
+ if (buf[0] < limit) out += String(buf[0] % 10);
27
+ }
28
+ return out;
29
+ }
30
+ /**
31
+ * Constant-time string equality. Compares every character so the comparison
32
+ * time does not depend on the position of the first mismatch, mitigating
33
+ * timing side-channels when verifying secrets.
34
+ *
35
+ * Length is treated as public (it short-circuits on differing lengths), which
36
+ * is appropriate for fixed-length codes and tokens.
37
+ */
38
+ function timingSafeEqual(a, b) {
39
+ if (a.length !== b.length) return false;
40
+ let mismatch = 0;
41
+ for (let i = 0; i < a.length; i++) mismatch |= a.charCodeAt(i) ^ b.charCodeAt(i);
42
+ return mismatch === 0;
43
+ }
44
+ //#endregion
45
+ //#region src/node/auth/revoke.ts
46
+ /**
47
+ * Flip `isTrusted` to false on any live WS clients connected with `token`
48
+ * and broadcast the `auth:revoked` event so they can react.
49
+ *
50
+ * Shared between persisted-auth revocation and remote-dock token revocation.
51
+ */
52
+ async function revokeActiveConnectionsForToken(context, token) {
53
+ const rpcHost = context.rpc;
54
+ if (!rpcHost?._rpcGroup) return;
55
+ const affectedSessionIds = /* @__PURE__ */ new Set();
56
+ for (const client of rpcHost._rpcGroup.clients) if (client.$meta.clientAuthToken === token) {
57
+ affectedSessionIds.add(client.$meta.id);
58
+ client.$meta.isTrusted = false;
59
+ client.$meta.clientAuthToken = void 0;
60
+ }
61
+ if (affectedSessionIds.size === 0) return;
62
+ await rpcHost.broadcast({
63
+ method: "devframe:auth:revoked",
64
+ args: [],
65
+ filter: (client) => affectedSessionIds.has(client.$meta.id)
66
+ });
67
+ }
68
+ /**
69
+ * Revoke an auth token: remove from storage and notify all connected clients
70
+ * using this token that they are no longer trusted.
71
+ */
72
+ async function revokeAuthToken(context, storage, token) {
73
+ storage.mutate((state) => {
74
+ delete state.trusted[token];
75
+ });
76
+ await revokeActiveConnectionsForToken(context, token);
77
+ }
78
+ //#endregion
79
+ export { timingSafeEqual as a, randomToken as i, revokeAuthToken as n, randomDigits as r, revokeActiveConnectionsForToken as t };
@@ -1,2 +1,2 @@
1
- import { a as getDefinitionsWithDumps, c as StaticRpcDumpManifest, d as StaticRpcDumpManifestValue, f as StaticRpcDumpSerialization, i as dumpFunctions, l as StaticRpcDumpManifestQueryEntry, n as serializeDumpError, o as StaticRpcDumpCollection, p as collectStaticRpcDump, r as createClientFromDump, s as StaticRpcDumpFile, t as reviveDumpError, u as StaticRpcDumpManifestStaticEntry } from "../index-DH2sBIwd.mjs";
1
+ import { a as getDefinitionsWithDumps, c as StaticRpcDumpManifest, d as StaticRpcDumpManifestValue, f as StaticRpcDumpSerialization, i as dumpFunctions, l as StaticRpcDumpManifestQueryEntry, n as serializeDumpError, o as StaticRpcDumpCollection, p as collectStaticRpcDump, r as createClientFromDump, s as StaticRpcDumpFile, t as reviveDumpError, u as StaticRpcDumpManifestStaticEntry } from "../index-DXHWuwJk.mjs";
2
2
  export { StaticRpcDumpCollection, StaticRpcDumpFile, StaticRpcDumpManifest, StaticRpcDumpManifestQueryEntry, StaticRpcDumpManifestStaticEntry, StaticRpcDumpManifestValue, StaticRpcDumpSerialization, collectStaticRpcDump, createClientFromDump, dumpFunctions, getDefinitionsWithDumps, reviveDumpError, serializeDumpError };
package/dist/rpc/dump.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { a as dumpFunctions, c as serializeDumpError, i as createClientFromDump, o as getDefinitionsWithDumps, s as reviveDumpError, t as collectStaticRpcDump } from "../dump-9lKIJTLh.mjs";
1
+ import { a as dumpFunctions, c as serializeDumpError, i as createClientFromDump, o as getDefinitionsWithDumps, s as reviveDumpError, t as collectStaticRpcDump } from "../dump-DoXkj4ku.mjs";
2
2
  export { collectStaticRpcDump, createClientFromDump, dumpFunctions, getDefinitionsWithDumps, reviveDumpError, serializeDumpError };
@@ -1,3 +1,3 @@
1
- import { C as RpcFunctionsCollector, S as RpcFunctionType, T as Thenable, _ as RpcFunctionDefinitionAny, a as RpcDefinitionsFilter, b as RpcFunctionDefinitionToFunction, c as RpcDumpClientOptions, d as RpcDumpGetter, f as RpcDumpRecord, g as RpcFunctionDefinition, h as RpcFunctionAgentOptions, i as RpcArgsSchema, l as RpcDumpCollectionOptions, m as RpcDumpStore, n as BirpcReturn, o as RpcDefinitionsToFunctions, p as RpcDumpRecordError, r as EntriesToObject, s as RpcDump, t as BirpcFn, u as RpcDumpDefinition, v as RpcFunctionDefinitionAnyWithContext, w as RpcReturnSchema, x as RpcFunctionSetupResult, y as RpcFunctionDefinitionBase } from "../types-BkkQ0Txg.mjs";
2
- import { C as RpcCacheManager, S as RpcFunctionsCollectorBase, _ as strictJsonStringify, a as StaticRpcDumpManifestStaticEntry, b as createDefineWrapperWithContext, c as collectStaticRpcDump, d as getDefinitionsWithDumps, f as reviveDumpError, g as STRUCTURED_CLONE_PREFIX, h as validateDefinitions, i as StaticRpcDumpManifestQueryEntry, l as createClientFromDump, m as validateDefinition, n as StaticRpcDumpFile, o as StaticRpcDumpManifestValue, p as serializeDumpError, r as StaticRpcDumpManifest, s as StaticRpcDumpSerialization, t as StaticRpcDumpCollection, u as dumpFunctions, v as getRpcHandler, w as RpcCacheOptions, x as defineRpcFunction, y as getRpcResolvedSetupResult } from "../index-C7M1hnvL.mjs";
3
- export { BirpcFn, BirpcReturn, EntriesToObject, RpcArgsSchema, RpcCacheManager, RpcCacheOptions, RpcDefinitionsFilter, RpcDefinitionsToFunctions, RpcDump, RpcDumpClientOptions, RpcDumpCollectionOptions, RpcDumpDefinition, RpcDumpGetter, RpcDumpRecord, RpcDumpRecordError, RpcDumpStore, RpcFunctionAgentOptions, RpcFunctionDefinition, RpcFunctionDefinitionAny, RpcFunctionDefinitionAnyWithContext, RpcFunctionDefinitionBase, RpcFunctionDefinitionToFunction, RpcFunctionSetupResult, RpcFunctionType, RpcFunctionsCollector, RpcFunctionsCollectorBase, RpcReturnSchema, STRUCTURED_CLONE_PREFIX, StaticRpcDumpCollection, StaticRpcDumpFile, StaticRpcDumpManifest, StaticRpcDumpManifestQueryEntry, StaticRpcDumpManifestStaticEntry, StaticRpcDumpManifestValue, StaticRpcDumpSerialization, Thenable, collectStaticRpcDump, createClientFromDump, createDefineWrapperWithContext, defineRpcFunction, dumpFunctions, getDefinitionsWithDumps, getRpcHandler, getRpcResolvedSetupResult, reviveDumpError, serializeDumpError, strictJsonStringify, validateDefinition, validateDefinitions };
1
+ import { C as RpcFunctionType, E as Thenable, S as RpcFunctionSetupResult, T as RpcReturnSchema, _ as RpcFunctionDefinition, a as RpcDefinitionsFilter, b as RpcFunctionDefinitionBase, c as RpcDump, d as RpcDumpDefinition, f as RpcDumpGetter, g as RpcFunctionAgentOptions, h as RpcDumpStore, i as RpcArgsSchema, l as RpcDumpClientOptions, m as RpcDumpRecordError, n as BirpcReturn, o as RpcDefinitionsToFunctions, p as RpcDumpRecord, r as EntriesToObject, s as RpcDefinitionsToFunctionsWithNamespace, t as BirpcFn, u as RpcDumpCollectionOptions, v as RpcFunctionDefinitionAny, w as RpcFunctionsCollector, x as RpcFunctionDefinitionToFunction, y as RpcFunctionDefinitionAnyWithContext } from "../types-DZEx4ffs.mjs";
2
+ import { C as RpcCacheManager, S as RpcFunctionsCollectorBase, _ as strictJsonStringify, a as StaticRpcDumpManifestStaticEntry, b as createDefineWrapperWithContext, c as collectStaticRpcDump, d as getDefinitionsWithDumps, f as reviveDumpError, g as STRUCTURED_CLONE_PREFIX, h as validateDefinitions, i as StaticRpcDumpManifestQueryEntry, l as createClientFromDump, m as validateDefinition, n as StaticRpcDumpFile, o as StaticRpcDumpManifestValue, p as serializeDumpError, r as StaticRpcDumpManifest, s as StaticRpcDumpSerialization, t as StaticRpcDumpCollection, u as dumpFunctions, v as getRpcHandler, w as RpcCacheOptions, x as defineRpcFunction, y as getRpcResolvedSetupResult } from "../index-CxaPKDXX.mjs";
3
+ export { BirpcFn, BirpcReturn, EntriesToObject, RpcArgsSchema, RpcCacheManager, RpcCacheOptions, RpcDefinitionsFilter, RpcDefinitionsToFunctions, RpcDefinitionsToFunctionsWithNamespace, RpcDump, RpcDumpClientOptions, RpcDumpCollectionOptions, RpcDumpDefinition, RpcDumpGetter, RpcDumpRecord, RpcDumpRecordError, RpcDumpStore, RpcFunctionAgentOptions, RpcFunctionDefinition, RpcFunctionDefinitionAny, RpcFunctionDefinitionAnyWithContext, RpcFunctionDefinitionBase, RpcFunctionDefinitionToFunction, RpcFunctionSetupResult, RpcFunctionType, RpcFunctionsCollector, RpcFunctionsCollectorBase, RpcReturnSchema, STRUCTURED_CLONE_PREFIX, StaticRpcDumpCollection, StaticRpcDumpFile, StaticRpcDumpManifest, StaticRpcDumpManifestQueryEntry, StaticRpcDumpManifestStaticEntry, StaticRpcDumpManifestValue, StaticRpcDumpSerialization, Thenable, collectStaticRpcDump, createClientFromDump, createDefineWrapperWithContext, defineRpcFunction, dumpFunctions, getDefinitionsWithDumps, getRpcHandler, getRpcResolvedSetupResult, reviveDumpError, serializeDumpError, strictJsonStringify, validateDefinition, validateDefinitions };
@@ -1,7 +1,7 @@
1
- import { a as dumpFunctions$1, c as serializeDumpError$1, d as hash, i as createClientFromDump$1, l as validateDefinition, n as getRpcHandler, o as getDefinitionsWithDumps$1, r as getRpcResolvedSetupResult, s as reviveDumpError$1, t as collectStaticRpcDump$1, u as validateDefinitions } from "../dump-9lKIJTLh.mjs";
2
- import { t as diagnostics } from "../diagnostics-GitRGKbr.mjs";
3
- import { n as defineRpcFunction, t as createDefineWrapperWithContext } from "../define-CW9gLnyG.mjs";
4
- import { n as strictJsonStringify, t as STRUCTURED_CLONE_PREFIX } from "../serialization-BD_qXGjd.mjs";
1
+ import { a as dumpFunctions$1, c as serializeDumpError$1, d as hash, i as createClientFromDump$1, l as validateDefinition, n as getRpcHandler, o as getDefinitionsWithDumps$1, r as getRpcResolvedSetupResult, s as reviveDumpError$1, t as collectStaticRpcDump$1, u as validateDefinitions } from "../dump-DoXkj4ku.mjs";
2
+ import { t as diagnostics } from "../diagnostics-BXwBQmoN.mjs";
3
+ import { n as defineRpcFunction, t as createDefineWrapperWithContext } from "../define-BLWPsH6y.mjs";
4
+ import { n as strictJsonStringify, t as STRUCTURED_CLONE_PREFIX } from "../serialization-DpLXCy13.mjs";
5
5
  //#region src/rpc/cache.ts
6
6
  /**
7
7
  * @experimental API is expected to change.
@@ -1,2 +1,2 @@
1
- import { n as createWsRpcChannel, t as WsRpcChannelOptions } from "../../ws-client-CVYX9niP.mjs";
1
+ import { n as createWsRpcChannel, t as WsRpcChannelOptions } from "../../ws-client-D0z0pOhn.mjs";
2
2
  export { WsRpcChannelOptions, createWsRpcChannel };
@@ -1,5 +1,5 @@
1
- import { n as strictJsonStringify } from "../../serialization-BD_qXGjd.mjs";
2
- import { n as structuredCloneStringify, t as structuredCloneParse } from "../../structured-clone-jegjz0hM.mjs";
1
+ import { n as strictJsonStringify } from "../../serialization-DpLXCy13.mjs";
2
+ import { n as structuredCloneStringify, t as structuredCloneParse } from "../../structured-clone-XpHLZ8nr.mjs";
3
3
  //#region src/rpc/transports/ws-client.ts
4
4
  function NOOP() {}
5
5
  const EMPTY_DEFS = /* @__PURE__ */ new Map();
@@ -1,2 +1,2 @@
1
- import { n as WsRpcTransportOptions, r as attachWsRpcTransport, t as DevframeNodeRpcSessionMeta } from "../../ws-server-C1LjmRnp.mjs";
2
- export { DevframeNodeRpcSessionMeta, WsRpcTransportOptions, attachWsRpcTransport };
1
+ import { a as isAllowedOrigin, i as attachWsRpcTransport, n as WsRpcTransport, o as isLoopbackHostname, r as WsRpcTransportOptions, t as DevframeNodeRpcSessionMeta } from "../../ws-server-Bc2wBHl-.mjs";
2
+ export { DevframeNodeRpcSessionMeta, WsRpcTransport, WsRpcTransportOptions, attachWsRpcTransport, isAllowedOrigin, isLoopbackHostname };
@@ -1,74 +1,167 @@
1
- import { n as strictJsonStringify } from "../../serialization-BD_qXGjd.mjs";
2
- import { n as structuredCloneStringify, t as structuredCloneParse } from "../../structured-clone-jegjz0hM.mjs";
3
- import { createServer } from "node:https";
4
- import { WebSocketServer } from "ws";
1
+ import { n as strictJsonStringify } from "../../serialization-DpLXCy13.mjs";
2
+ import { n as structuredCloneStringify, t as structuredCloneParse } from "../../structured-clone-XpHLZ8nr.mjs";
3
+ import { createServer } from "node:http";
4
+ import { createServer as createServer$1 } from "node:https";
5
+ import crossws from "crossws/adapters/node";
5
6
  //#region src/rpc/transports/ws-server.ts
6
7
  let sessionId = 0;
7
8
  const EMPTY_DEFS = /* @__PURE__ */ new Map();
8
9
  function NOOP() {}
10
+ /** Compare two URL paths ignoring a trailing slash. */
11
+ function pathMatches(a, b) {
12
+ const strip = (p) => p.length > 1 && p.endsWith("/") ? p.slice(0, -1) : p;
13
+ return strip(a) === strip(b);
14
+ }
15
+ function isLoopbackHostname(hostname) {
16
+ const h = hostname.replace(/^\[|\]$/g, "");
17
+ return h === "localhost" || h === "127.0.0.1" || h === "::1" || h.endsWith(".localhost") || h.startsWith("127.");
18
+ }
9
19
  /**
10
- * Attach a WebSocket transport to an existing RPC group. Either pass an
11
- * existing `WebSocketServer` via `wss`, or let this helper create one from
12
- * `port` / `host` / `https`.
20
+ * Default origin policy for a localhost dev tool: allow requests with no
21
+ * `Origin` header (native, non-browser clients), allow any loopback origin
22
+ * (so cross-port localhost dev setups keep working), and allow explicitly
23
+ * configured origins. Everything else — a real remote page in the dev's
24
+ * browser — is rejected.
13
25
  */
14
- function attachWsRpcTransport(rpcGroup, options = {}) {
15
- const { wss: externalWss, port, host = "localhost", https, onConnected = NOOP, onDisconnected = NOOP, definitions = EMPTY_DEFS, serialize: serializeOverride, deserialize: deserializeOverride } = options;
16
- let wss;
17
- if (externalWss) wss = externalWss;
18
- else if (https) {
19
- const httpsServer = createServer(https);
20
- wss = new WebSocketServer({ server: httpsServer });
21
- httpsServer.listen(port, host);
22
- } else wss = new WebSocketServer({
23
- port,
24
- host
25
- });
26
- wss.on("connection", (ws, req) => {
27
- const meta = {
28
- id: sessionId++,
29
- ws,
30
- subscribedStates: /* @__PURE__ */ new Set()
31
- };
32
- const pendingRequestMethods = /* @__PURE__ */ new Map();
33
- const channel = {
34
- post: (data) => {
35
- ws.send(data);
36
- },
37
- on: (fn) => {
38
- ws.on("message", (data) => {
39
- fn(data.toString());
40
- });
41
- },
42
- serialize: serializeOverride ?? ((msg) => {
43
- let method;
44
- if (msg.t === "q") method = msg.m;
45
- else {
46
- method = pendingRequestMethods.get(msg.i);
47
- pendingRequestMethods.delete(msg.i);
26
+ function isAllowedOrigin(origin, allowedOrigins) {
27
+ if (!origin) return true;
28
+ if (allowedOrigins.includes(origin)) return true;
29
+ try {
30
+ return isLoopbackHostname(new URL(origin).hostname);
31
+ } catch {
32
+ return false;
33
+ }
34
+ }
35
+ /**
36
+ * Route `upgrade` events on a server to the crossws adapter, optionally
37
+ * filtered to a single `path`. Non-matching requests are left untouched so
38
+ * other upgrade listeners (e.g. a Vite dev server's HMR socket) can claim
39
+ * them, unless `destroyUnmatched` is set. Returns a detach function that
40
+ * removes the listener.
41
+ */
42
+ function routeUpgrades(server, ws, path, destroyUnmatched, allowedOrigins) {
43
+ const listener = (req, socket, head) => {
44
+ socket.on("error", () => {});
45
+ if (path) {
46
+ let pathname = req.url ?? "/";
47
+ try {
48
+ pathname = new URL(req.url ?? "/", "http://localhost").pathname;
49
+ } catch {}
50
+ if (!pathMatches(pathname, path)) {
51
+ if (destroyUnmatched) {
52
+ socket.write("HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n");
53
+ socket.destroy();
48
54
  }
49
- if (!(msg.t === "s" && "e" in msg) && !!method && definitions.get(method)?.jsonSerializable === true) return strictJsonStringify(msg, method ?? "");
50
- return `s:${structuredCloneStringify(msg)}`;
51
- }),
52
- deserialize: deserializeOverride ?? ((raw) => {
53
- const msg = raw.startsWith("s:") ? structuredCloneParse(raw.slice(2)) : JSON.parse(raw);
54
- if (msg.t === "q" && msg.i && msg.m) pendingRequestMethods.set(msg.i, msg.m);
55
- return msg;
56
- }),
57
- meta
58
- };
59
- rpcGroup.updateChannels((channels) => {
60
- channels.push(channel);
61
- });
62
- ws.on("close", () => {
55
+ return;
56
+ }
57
+ }
58
+ if (allowedOrigins !== false && !isAllowedOrigin(req.headers.origin, allowedOrigins ?? [])) {
59
+ socket.write("HTTP/1.1 403 Forbidden\r\nConnection: close\r\n\r\n");
60
+ socket.destroy();
61
+ return;
62
+ }
63
+ ws.handleUpgrade(req, socket, head);
64
+ };
65
+ server.on("upgrade", listener);
66
+ return () => server.off("upgrade", listener);
67
+ }
68
+ /**
69
+ * Attach a WebSocket transport to an existing RPC group, powered by
70
+ * [crossws](https://crossws.h3.dev). Either attach to an existing HTTP(S)
71
+ * `server` (sharing its port, optionally scoped to a `path`), or let this
72
+ * helper create a standalone server from `port` / `host` / `https`.
73
+ *
74
+ * Returns the crossws node adapter plus `detach` (remove the upgrade
75
+ * listener from a shared `server`) and `close` (full deterministic
76
+ * teardown).
77
+ */
78
+ function attachWsRpcTransport(rpcGroup, options = {}) {
79
+ const { server, port, host = "localhost", path, destroyUnmatched = false, https, allowedOrigins, onConnected = NOOP, onDisconnected = NOOP, definitions = EMPTY_DEFS, serialize: serializeOverride, deserialize: deserializeOverride } = options;
80
+ const states = /* @__PURE__ */ new WeakMap();
81
+ const ws = crossws({ hooks: {
82
+ open: (peer) => {
83
+ const meta = {
84
+ id: sessionId++,
85
+ peer,
86
+ subscribedStates: /* @__PURE__ */ new Set()
87
+ };
88
+ const pendingRequestMethods = /* @__PURE__ */ new Map();
89
+ const state = {
90
+ meta,
91
+ channel: void 0
92
+ };
93
+ const channel = {
94
+ post: (data) => {
95
+ peer.send(data);
96
+ },
97
+ on: (fn) => {
98
+ state.onMessage = fn;
99
+ },
100
+ serialize: serializeOverride ?? ((msg) => {
101
+ let method;
102
+ if (msg.t === "q") method = msg.m;
103
+ else {
104
+ method = pendingRequestMethods.get(msg.i);
105
+ pendingRequestMethods.delete(msg.i);
106
+ }
107
+ if (!(msg.t === "s" && "e" in msg) && !!method && definitions.get(method)?.jsonSerializable === true) return strictJsonStringify(msg, method ?? "");
108
+ return `s:${structuredCloneStringify(msg)}`;
109
+ }),
110
+ deserialize: deserializeOverride ?? ((raw) => {
111
+ const msg = raw.startsWith("s:") ? structuredCloneParse(raw.slice(2)) : JSON.parse(raw);
112
+ if (msg.t === "q" && msg.i && msg.m) pendingRequestMethods.set(msg.i, msg.m);
113
+ return msg;
114
+ }),
115
+ meta
116
+ };
117
+ state.channel = channel;
118
+ states.set(peer, state);
63
119
  rpcGroup.updateChannels((channels) => {
64
- const index = channels.indexOf(channel);
120
+ channels.push(channel);
121
+ });
122
+ onConnected(peer, meta);
123
+ },
124
+ message: (peer, message) => {
125
+ states.get(peer)?.onMessage?.(message.text());
126
+ },
127
+ close: (peer) => {
128
+ const state = states.get(peer);
129
+ if (!state) return;
130
+ states.delete(peer);
131
+ rpcGroup.updateChannels((channels) => {
132
+ const index = channels.indexOf(state.channel);
65
133
  if (index >= 0) channels.splice(index, 1);
66
134
  });
67
- onDisconnected(ws, meta);
135
+ onDisconnected(peer, state.meta);
136
+ }
137
+ } });
138
+ let detach = NOOP;
139
+ let ownedServer;
140
+ if (server) detach = routeUpgrades(server, ws, path, destroyUnmatched, allowedOrigins);
141
+ else if (https) {
142
+ ownedServer = createServer$1(https);
143
+ detach = routeUpgrades(ownedServer, ws, path, true, allowedOrigins);
144
+ ownedServer.listen(port, host);
145
+ } else {
146
+ ownedServer = createServer((_req, res) => {
147
+ res.writeHead(426, { "content-type": "text/plain" });
148
+ res.end("Upgrade Required");
68
149
  });
69
- onConnected(ws, req, meta);
70
- });
71
- return { wss };
150
+ detach = routeUpgrades(ownedServer, ws, path, true, allowedOrigins);
151
+ ownedServer.listen(port, host);
152
+ }
153
+ return {
154
+ ws,
155
+ detach,
156
+ async close() {
157
+ detach();
158
+ ws.closeAll(void 0, void 0, true);
159
+ if (ownedServer) {
160
+ const srv = ownedServer;
161
+ await new Promise((r) => srv.close(() => r()));
162
+ }
163
+ }
164
+ };
72
165
  }
73
166
  //#endregion
74
- export { attachWsRpcTransport };
167
+ export { attachWsRpcTransport, isAllowedOrigin, isLoopbackHostname };
@@ -1,4 +1,4 @@
1
- import { t as diagnostics } from "./diagnostics-GitRGKbr.mjs";
1
+ import { t as diagnostics } from "./diagnostics-BXwBQmoN.mjs";
2
2
  //#region src/rpc/serialization.ts
3
3
  /**
4
4
  * Wire format used by the WS RPC transport.
@@ -1,9 +1,8 @@
1
1
  import { createRpcServer } from "./rpc/server.mjs";
2
2
  import { attachWsRpcTransport } from "./rpc/transports/ws-server.mjs";
3
- import { t as getInternalContext } from "./context-DaKmhhHY.mjs";
4
- import { WebSocketServer } from "ws";
5
- import { AsyncLocalStorage } from "node:async_hooks";
3
+ import { t as getInternalContext } from "./context-3x_bgBgZ.mjs";
6
4
  import { createServer } from "node:http";
5
+ import { AsyncLocalStorage } from "node:async_hooks";
7
6
  import { H3, toNodeHandler } from "h3";
8
7
  //#region src/node/server.ts
9
8
  /**
@@ -15,8 +14,8 @@ async function startHttpAndWs(options) {
15
14
  const { context, port } = options;
16
15
  const bindHost = options.host ?? "localhost";
17
16
  const app = options.app ?? new H3();
18
- const httpServer = createServer(toNodeHandler(app));
19
- const wss = new WebSocketServer({ server: httpServer });
17
+ const ownsHttpServer = !options.server;
18
+ const httpServer = options.server ?? createServer(toNodeHandler(app));
20
19
  const rpcHost = context.rpc;
21
20
  const asyncStorage = new AsyncLocalStorage();
22
21
  const rpcGroup = createRpcServer(rpcHost.functions, { rpcOptions: { resolver(name, fn) {
@@ -31,9 +30,16 @@ async function startHttpAndWs(options) {
31
30
  });
32
31
  };
33
32
  } } });
34
- attachWsRpcTransport(rpcGroup, {
35
- wss,
36
- onDisconnected: (_ws, meta) => {
33
+ const separateWsPort = ownsHttpServer && options.wsPort != null && options.wsPort !== port ? options.wsPort : void 0;
34
+ const { ws, close: closeWs } = attachWsRpcTransport(rpcGroup, {
35
+ ...separateWsPort != null ? {
36
+ port: separateWsPort,
37
+ host: bindHost
38
+ } : { server: httpServer },
39
+ path: options.path,
40
+ destroyUnmatched: ownsHttpServer,
41
+ allowedOrigins: options.allowedOrigins,
42
+ onDisconnected: (_peer, meta) => {
37
43
  rpcHost._emitSessionDisconnected(meta);
38
44
  }
39
45
  });
@@ -49,14 +55,14 @@ async function startHttpAndWs(options) {
49
55
  return { isTrusted: true };
50
56
  }
51
57
  });
52
- await new Promise((resolveListen) => {
58
+ if (ownsHttpServer) await new Promise((resolveListen) => {
53
59
  httpServer.listen(port, bindHost, () => resolveListen());
54
60
  });
55
61
  const address = httpServer.address();
56
62
  const resolvedPort = typeof address === "object" && address ? address.port : port;
57
63
  const origin = `http://${bindHost}:${resolvedPort}`;
58
64
  const internal = getInternalContext(context);
59
- const wsUrl = origin.replace(/^http/, "ws");
65
+ const wsUrl = `ws://${bindHost}:${separateWsPort ?? resolvedPort}${options.path ?? ""}`;
60
66
  internal.wsEndpoint = { url: wsUrl };
61
67
  if (options.onReady) await options.onReady({
62
68
  origin,
@@ -67,12 +73,11 @@ async function startHttpAndWs(options) {
67
73
  origin,
68
74
  port: resolvedPort,
69
75
  app,
70
- wss,
76
+ ws,
71
77
  rpcGroup,
72
78
  async close() {
73
- for (const ws of wss.clients) ws.terminate();
74
- await new Promise((r) => wss.close(() => r()));
75
- await new Promise((r) => httpServer.close(() => r()));
79
+ await closeWs();
80
+ if (ownsHttpServer) await new Promise((r) => httpServer.close(() => r()));
76
81
  if (getInternalContext(context).wsEndpoint?.url === wsUrl) getInternalContext(context).wsEndpoint = void 0;
77
82
  }
78
83
  };
@@ -0,0 +1,90 @@
1
+ import { it as DevframeRpcServerFunctions, m as DevframeNodeContext, rt as DevframeRpcClientFunctions } from "./devframe-D-gr9sBx.mjs";
2
+ import { BirpcGroup } from "birpc";
3
+ import { NodeAdapter } from "crossws/adapters/node";
4
+ import { Server } from "node:http";
5
+ import { H3 } from "h3";
6
+
7
+ //#region src/node/server.d.ts
8
+ interface StartHttpAndWsOptions {
9
+ context: DevframeNodeContext;
10
+ host?: string;
11
+ port: number;
12
+ /**
13
+ * Optional h3 app to mount on. When omitted a fresh one is created;
14
+ * when provided, callers can add their own routes (static handlers,
15
+ * auth middleware, etc.) first.
16
+ */
17
+ app?: H3;
18
+ /**
19
+ * Bind the WS endpoint to a single upgrade route (e.g. `/__devframe_ws`) instead of
20
+ * claiming every upgrade on the port. This lets the socket share a server
21
+ * with other upgrade handlers (Vite HMR, a host framework's own sockets)
22
+ * and is what the SPA's `__connection.json` points at. When omitted, the WS
23
+ * server handles every upgrade on the port (legacy behaviour).
24
+ */
25
+ path?: string;
26
+ /**
27
+ * Bind the WS endpoint on its own port instead of sharing the HTTP server's.
28
+ * The HTTP/SPA server still listens on `port`; the socket gets a dedicated
29
+ * `ws` server on `wsPort` (same `host`). Use this for the "different port"
30
+ * connection scenario. Ignored when a `server` is supplied.
31
+ */
32
+ wsPort?: number;
33
+ /**
34
+ * Mount the WS endpoint onto an existing HTTP server, sharing its port,
35
+ * rather than creating and listening on a fresh one. Use this to embed
36
+ * devframe's RPC socket inside a host server (e.g. a Vite dev server) — pair
37
+ * it with `path` so it coexists with the host's routes. The caller owns the
38
+ * server's lifecycle: {@link StartedServer.close} detaches devframe's upgrade
39
+ * listener but leaves the host server running. When set, `host`/`port` are
40
+ * only used to report the resolved origin.
41
+ */
42
+ server?: Server;
43
+ /**
44
+ * When `false`, the RPC server is started without a trust handshake.
45
+ * Intended for single-user localhost tools where an auth round-trip
46
+ * would only get in the way. The Vite-flavoured auth layer in
47
+ * `@vitejs/devtools` already honors the equivalent
48
+ * `devtools.clientAuth` setting; devframe records the intent here so
49
+ * future auth plumbing can consult it without another API change.
50
+ *
51
+ * Default: `true`.
52
+ */
53
+ auth?: boolean;
54
+ /**
55
+ * Extra origins to accept on the WS upgrade beyond the loopback default
56
+ * (`localhost`/`127.0.0.1`/`::1` and any `Origin`-less request from a
57
+ * native client). Add your LAN/tunnel origin here when reaching the tool
58
+ * from another host. Pass `false` to disable origin checking entirely
59
+ * (not recommended). Default: loopback-only.
60
+ */
61
+ allowedOrigins?: readonly string[] | false;
62
+ /**
63
+ * Called once the WS server is bound so callers can mount static
64
+ * handlers whose origin depends on the resolved port, or print their
65
+ * own startup banner. Devframe does not print one itself.
66
+ */
67
+ onReady?: (info: {
68
+ origin: string;
69
+ port: number;
70
+ app: H3;
71
+ }) => void | Promise<void>;
72
+ }
73
+ interface StartedServer {
74
+ /** Listening origin, e.g. `http://localhost:9999`. */
75
+ origin: string;
76
+ port: number;
77
+ app: H3;
78
+ /** The crossws node adapter driving the RPC socket (connected peers, pub/sub). */
79
+ ws: NodeAdapter;
80
+ rpcGroup: BirpcGroup<DevframeRpcClientFunctions, DevframeRpcServerFunctions, false>;
81
+ close: () => Promise<void>;
82
+ }
83
+ /**
84
+ * Compose an h3 + WebSocket server for a devframe context. The RPC
85
+ * group is bound to `context.rpc.functions`; the WS endpoint lives on
86
+ * the same port as the HTTP server.
87
+ */
88
+ declare function startHttpAndWs(options: StartHttpAndWsOptions): Promise<StartedServer>;
89
+ //#endregion
90
+ export { StartedServer as n, startHttpAndWs as r, StartHttpAndWsOptions as t };