devframe 0.6.0-beta.1 → 0.6.0-beta.3
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/adapters/build.d.mts +1 -1
- package/dist/adapters/build.mjs +2 -2
- package/dist/adapters/cli.d.mts +1 -1
- package/dist/adapters/cli.mjs +1 -1
- package/dist/adapters/dev.d.mts +2 -2
- package/dist/adapters/dev.mjs +1 -1
- package/dist/adapters/embedded.d.mts +1 -1
- package/dist/adapters/mcp.d.mts +1 -1
- package/dist/adapters/mcp.mjs +3 -3
- package/dist/client/index.d.mts +2 -2
- package/dist/client/index.mjs +56 -22
- package/dist/constants.d.mts +24 -1
- package/dist/constants.mjs +26 -1
- package/dist/{context-o-TwncyP.d.mts → context-B_I9fMBc.d.mts} +1 -1
- package/dist/{context-3x_bgBgZ.mjs → context-CKJheEtf.mjs} +2 -2
- package/dist/{dev-CSpLSEVh.mjs → dev-DSxKFn-v.mjs} +4 -4
- package/dist/{devframe-BwfavB78.d.mts → devframe-DgvncQy2.d.mts} +20 -4
- package/dist/{dump-DoXkj4ku.mjs → dump-D09PZb7T.mjs} +14 -4
- package/dist/{hash-DFc5WwhO.mjs → hash-PAfnyu2k.mjs} +1 -1
- package/dist/helpers/vite.d.mts +1 -1
- package/dist/helpers/vite.mjs +2 -2
- package/dist/{host-h3-C5SZlk03.mjs → host-h3-Dsf-Tgwx.mjs} +37 -11
- package/dist/index-B4lNDq5v.d.mts +107 -0
- package/dist/{index-CxaPKDXX.d.mts → index-Dy6GFDRf.d.mts} +1 -0
- package/dist/index.d.mts +3 -3
- package/dist/{launch-editor-CgXBgviI.mjs → launch-editor-DIzaS5EG.mjs} +146 -56
- package/dist/node/auth.d.mts +2 -64
- package/dist/node/auth.mjs +2 -100
- package/dist/node/hub-internals.d.mts +2 -2
- package/dist/node/hub-internals.mjs +1 -1
- package/dist/node/index.d.mts +4 -4
- package/dist/node/index.mjs +3 -3
- package/dist/recipes/interactive-auth.d.mts +53 -0
- package/dist/recipes/interactive-auth.mjs +132 -0
- package/dist/recipes/open-helpers.mjs +2 -2
- package/dist/{revoke-ENfxWkFK.mjs → revoke-BtQDKTp7.mjs} +1 -1
- package/dist/rpc/dump.mjs +1 -1
- package/dist/rpc/index.d.mts +2 -2
- package/dist/rpc/index.mjs +4 -1
- package/dist/rpc/transports/ws-client.mjs +21 -8
- package/dist/rpc/transports/ws-server.d.mts +2 -2
- package/dist/rpc/transports/ws-server.mjs +36 -7
- package/dist/{server-wyY3zh67.mjs → server-BdN41ckC.mjs} +37 -4
- package/dist/server-DXPq8q0K.d.mts +121 -0
- package/dist/{shared-state-D4PPieA0.mjs → shared-state-QtMas0NG.mjs} +34 -4
- package/dist/state-WX3HT5a3.mjs +101 -0
- package/dist/{storage-l2ezeend.mjs → storage-yrUZaiib.mjs} +55 -6
- package/dist/types/index.d.mts +3 -3
- package/dist/utils/crypto-token.mjs +1 -1
- package/dist/utils/events.d.mts +1 -1
- package/dist/utils/hash.mjs +1 -1
- package/dist/utils/launch-editor.mjs +1 -1
- package/dist/utils/open.mjs +1 -1
- package/dist/utils/serve-static.mjs +2 -1
- package/dist/utils/shared-state.d.mts +1 -1
- package/dist/utils/shared-state.mjs +1 -1
- package/dist/utils/streaming-channel.d.mts +1 -1
- package/dist/utils/when.d.mts +1 -1
- package/dist/{ws-server-BVVMDo2n.d.mts → ws-server-Bc2wBHl-.d.mts} +17 -1
- package/package.json +10 -9
- package/dist/server-oju7PTi2.d.mts +0 -82
- package/dist/{open-Dusa2Zzd.mjs → open-Deb5xmIT.mjs} +1 -1
package/dist/node/auth.mjs
CHANGED
|
@@ -1,101 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as
|
|
3
|
-
//#region src/node/auth/state.ts
|
|
4
|
-
/** Number of decimal digits in a human-typed one-time authentication code. */
|
|
5
|
-
const TEMP_AUTH_CODE_LENGTH = 6;
|
|
6
|
-
/**
|
|
7
|
-
* How long an authentication code stays valid after it is (re)generated. A
|
|
8
|
-
* 6-digit code only has ~20 bits of entropy, so a short lifetime plus the
|
|
9
|
-
* attempt cap below are what keep it brute-force resistant.
|
|
10
|
-
*/
|
|
11
|
-
const TEMP_AUTH_CODE_TTL = 5 * 6e4;
|
|
12
|
-
/** Failed attempts allowed against a single code before it is rotated. */
|
|
13
|
-
const TEMP_AUTH_MAX_ATTEMPTS = 5;
|
|
14
|
-
let tempAuthCode = generateTempCode();
|
|
15
|
-
let tempAuthCodeExpiresAt = Date.now() + TEMP_AUTH_CODE_TTL;
|
|
16
|
-
let tempAuthFailedAttempts = 0;
|
|
17
|
-
function generateTempCode() {
|
|
18
|
-
return randomDigits(TEMP_AUTH_CODE_LENGTH);
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* The current one-time authentication code. Display this to the user (e.g. in
|
|
22
|
-
* the dev-server terminal) so they can type it into the browser to authenticate.
|
|
23
|
-
*/
|
|
24
|
-
function getTempAuthCode() {
|
|
25
|
-
return tempAuthCode;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Rotate the authentication code, resetting its expiry window and failed-attempt
|
|
29
|
-
* counter. Call this when a new authentication flow begins (e.g. when an
|
|
30
|
-
* untrusted client starts authenticating) so the displayed code is freshly
|
|
31
|
-
* valid for its full TTL.
|
|
32
|
-
*/
|
|
33
|
-
function refreshTempAuthCode() {
|
|
34
|
-
tempAuthCode = generateTempCode();
|
|
35
|
-
tempAuthCodeExpiresAt = Date.now() + TEMP_AUTH_CODE_TTL;
|
|
36
|
-
tempAuthFailedAttempts = 0;
|
|
37
|
-
return tempAuthCode;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Build a "magic link" authentication URL that embeds a one-time code (OTP) as
|
|
41
|
-
* a query parameter. Opening it authenticates the client without typing — print
|
|
42
|
-
* it on startup (devframe stays headless, so the host prints its own banner).
|
|
43
|
-
* Defaults to the current code; the link is subject to the same TTL.
|
|
44
|
-
*/
|
|
45
|
-
function buildOtpAuthUrl(baseUrl, code = tempAuthCode) {
|
|
46
|
-
const url = new URL(baseUrl);
|
|
47
|
-
url.searchParams.set(DEVFRAME_OTP_URL_PARAM, code);
|
|
48
|
-
return url.href;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Re-authenticate a connection that presents a previously-issued bearer token.
|
|
52
|
-
* Returns `true` and marks the session trusted when the token is known.
|
|
53
|
-
*
|
|
54
|
-
* Used by the `devframe:anonymous:auth` handler so a client that already
|
|
55
|
-
* authenticated (token persisted in the browser) is trusted on reconnect
|
|
56
|
-
* without entering the code again.
|
|
57
|
-
*/
|
|
58
|
-
function verifyAuthToken(token, session, storage) {
|
|
59
|
-
if (!token || !storage.value().trusted[token]) return false;
|
|
60
|
-
session.meta.clientAuthToken = token;
|
|
61
|
-
session.meta.isTrusted = true;
|
|
62
|
-
return true;
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Exchange a one-time authentication code for a fresh, node-issued bearer token.
|
|
66
|
-
*
|
|
67
|
-
* On success this mints a high-entropy token, records it in the trusted store,
|
|
68
|
-
* marks the calling session trusted, rotates the code, and returns the token
|
|
69
|
-
* for the client to persist. Returns `null` on any failure.
|
|
70
|
-
*
|
|
71
|
-
* Because the code is short and human-typed, verification is hardened against
|
|
72
|
-
* brute force: it enforces a time-to-live, compares in constant time, and
|
|
73
|
-
* rotates the code after {@link TEMP_AUTH_MAX_ATTEMPTS} failed attempts so an
|
|
74
|
-
* attacker cannot keep guessing against the same code.
|
|
75
|
-
*/
|
|
76
|
-
function exchangeTempAuthCode(code, session, info, storage) {
|
|
77
|
-
if (Date.now() > tempAuthCodeExpiresAt) {
|
|
78
|
-
refreshTempAuthCode();
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
if (!timingSafeEqual(code, tempAuthCode)) {
|
|
82
|
-
tempAuthFailedAttempts += 1;
|
|
83
|
-
if (tempAuthFailedAttempts >= TEMP_AUTH_MAX_ATTEMPTS) refreshTempAuthCode();
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
const authToken = randomToken();
|
|
87
|
-
storage.mutate((state) => {
|
|
88
|
-
state.trusted[authToken] = {
|
|
89
|
-
authToken,
|
|
90
|
-
ua: info.ua,
|
|
91
|
-
origin: info.origin,
|
|
92
|
-
timestamp: Date.now()
|
|
93
|
-
};
|
|
94
|
-
});
|
|
95
|
-
session.meta.clientAuthToken = authToken;
|
|
96
|
-
session.meta.isTrusted = true;
|
|
97
|
-
refreshTempAuthCode();
|
|
98
|
-
return authToken;
|
|
99
|
-
}
|
|
100
|
-
//#endregion
|
|
1
|
+
import { n as revokeAuthToken, t as revokeActiveConnectionsForToken } from "../revoke-BtQDKTp7.mjs";
|
|
2
|
+
import { a as verifyAuthToken, i as refreshTempAuthCode, n as exchangeTempAuthCode, r as getTempAuthCode, t as buildOtpAuthUrl } from "../state-WX3HT5a3.mjs";
|
|
101
3
|
export { buildOtpAuthUrl, exchangeTempAuthCode, getTempAuthCode, refreshTempAuthCode, revokeActiveConnectionsForToken, revokeAuthToken, verifyAuthToken };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as DevframeDeploymentKind, r as DevframeDefinition } from "../devframe-
|
|
2
|
-
import { a as internalContextMap, i as getInternalContext, n as InternalAnonymousAuthStorage, r as RemoteTokenRecord, t as DevframeInternalContext } from "../context-
|
|
1
|
+
import { i as DevframeDeploymentKind, r as DevframeDefinition } from "../devframe-DgvncQy2.mjs";
|
|
2
|
+
import { a as internalContextMap, i as getInternalContext, n as InternalAnonymousAuthStorage, r as RemoteTokenRecord, t as DevframeInternalContext } from "../context-B_I9fMBc.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/adapters/_shared.d.ts
|
|
5
5
|
/**
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { n as internalContextMap, t as getInternalContext } from "../context-
|
|
1
|
+
import { n as internalContextMap, t as getInternalContext } from "../context-CKJheEtf.mjs";
|
|
2
2
|
import { n as resolveBasePath, t as normalizeBasePath } from "../_shared-bWRzeSa0.mjs";
|
|
3
3
|
export { getInternalContext, internalContextMap, normalizeBasePath, resolveBasePath };
|
package/dist/node/index.d.mts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
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-
|
|
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-DgvncQy2.mjs";
|
|
2
2
|
import { v as RpcFunctionDefinitionAny } from "../types-DZEx4ffs.mjs";
|
|
3
|
-
import { S as RpcFunctionsCollectorBase } from "../index-
|
|
4
|
-
import { t as DevframeNodeRpcSessionMeta } from "../ws-server-
|
|
5
|
-
import { n as StartedServer, r as startHttpAndWs, t as StartHttpAndWsOptions } from "../server-
|
|
3
|
+
import { S as RpcFunctionsCollectorBase } from "../index-Dy6GFDRf.mjs";
|
|
4
|
+
import { t as DevframeNodeRpcSessionMeta } from "../ws-server-Bc2wBHl-.mjs";
|
|
5
|
+
import { n as StartedServer, r as startHttpAndWs, t as StartHttpAndWsOptions } from "../server-DXPq8q0K.mjs";
|
|
6
6
|
import { BirpcGroup } from "birpc";
|
|
7
7
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
8
8
|
|
package/dist/node/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as createStorage } from "../storage-
|
|
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-
|
|
3
|
-
import { t as startHttpAndWs } from "../server-
|
|
1
|
+
import { t as createStorage } from "../storage-yrUZaiib.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-Dsf-Tgwx.mjs";
|
|
3
|
+
import { t as startHttpAndWs } from "../server-BdN41ckC.mjs";
|
|
4
4
|
import { isIP } from "node:net";
|
|
5
5
|
//#region src/node/utils.ts
|
|
6
6
|
function isObject(value) {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { m as DevframeNodeContext } from "../devframe-DgvncQy2.mjs";
|
|
2
|
+
import { c as DevframeAuthHandler } from "../index-B4lNDq5v.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/recipes/interactive-auth.d.ts
|
|
5
|
+
interface CreateInteractiveAuthOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Static, pre-shared bearer tokens that are always trusted — for CI runs
|
|
8
|
+
* or shared machines where the interactive code prompt would only get in
|
|
9
|
+
* the way. Checked in both the handshake handler and the connect-time
|
|
10
|
+
* hook, alongside tokens minted by a real code exchange.
|
|
11
|
+
*/
|
|
12
|
+
clientAuthTokens?: string[];
|
|
13
|
+
/**
|
|
14
|
+
* Print the current code + magic-link URL. Devframe stays headless, so
|
|
15
|
+
* there is no default banner printed automatically — call
|
|
16
|
+
* `auth.printBanner()` yourself once the server is listening. Override
|
|
17
|
+
* this to customize the format; defaults to a small boxed message on
|
|
18
|
+
* stdout.
|
|
19
|
+
*/
|
|
20
|
+
banner?: (info: {
|
|
21
|
+
code: string;
|
|
22
|
+
url: string;
|
|
23
|
+
}) => void;
|
|
24
|
+
/**
|
|
25
|
+
* The base URL the magic link should point at. Defaults to
|
|
26
|
+
* `context.host.resolveOrigin()`.
|
|
27
|
+
*/
|
|
28
|
+
serverUrl?: () => string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Package the interactive OTP auth protocol devframe's primitives
|
|
32
|
+
* (`exchangeTempAuthCode`, `verifyAuthToken`, `revokeAuthToken`,
|
|
33
|
+
* `getTempAuthCode`, `buildOtpAuthUrl`) implement into a ready-made
|
|
34
|
+
* {@link DevframeAuthHandler} — the handshake RPC functions, the resolver
|
|
35
|
+
* gate, the connect-time trust hook, and the startup banner.
|
|
36
|
+
*
|
|
37
|
+
* The auth storage stays internal to this handler — callers never reach into
|
|
38
|
+
* `devframe/node/hub-internals` themselves.
|
|
39
|
+
*
|
|
40
|
+
* ```ts
|
|
41
|
+
* import { createInteractiveAuth } from 'devframe/recipes/interactive-auth'
|
|
42
|
+
*
|
|
43
|
+
* const auth = createInteractiveAuth(ctx)
|
|
44
|
+
* auth.rpcFunctions.forEach(fn => ctx.rpc.register(fn))
|
|
45
|
+
* auth.printBanner()
|
|
46
|
+
*
|
|
47
|
+
* // wire `auth.authorize` / `auth.onConnect` into your transport, or pass
|
|
48
|
+
* // the whole handler to `startHttpAndWs({ auth, ... })`.
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
declare function createInteractiveAuth(context: DevframeNodeContext, options?: CreateInteractiveAuthOptions): DevframeAuthHandler;
|
|
52
|
+
//#endregion
|
|
53
|
+
export { CreateInteractiveAuthOptions, createInteractiveAuth };
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { n as colors } from "../diagnostics-reporter-CsIG85Q5.mjs";
|
|
2
|
+
import { isAnonymousRpcMethod } from "../constants.mjs";
|
|
3
|
+
import { n as defineRpcFunction } from "../define-BLWPsH6y.mjs";
|
|
4
|
+
import { t as getInternalContext } from "../context-CKJheEtf.mjs";
|
|
5
|
+
import { a as verifyAuthToken, n as exchangeTempAuthCode, r as getTempAuthCode, t as buildOtpAuthUrl } from "../state-WX3HT5a3.mjs";
|
|
6
|
+
import * as v from "valibot";
|
|
7
|
+
//#region src/recipes/interactive-auth.ts
|
|
8
|
+
function defaultBanner(info) {
|
|
9
|
+
console.log(`\n ${colors.dim("devframe auth code")} ${colors.bold(info.code)}\n ${colors.dim("or open")} ${colors.cyan(info.url)}\n`);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Package the interactive OTP auth protocol devframe's primitives
|
|
13
|
+
* (`exchangeTempAuthCode`, `verifyAuthToken`, `revokeAuthToken`,
|
|
14
|
+
* `getTempAuthCode`, `buildOtpAuthUrl`) implement into a ready-made
|
|
15
|
+
* {@link DevframeAuthHandler} — the handshake RPC functions, the resolver
|
|
16
|
+
* gate, the connect-time trust hook, and the startup banner.
|
|
17
|
+
*
|
|
18
|
+
* The auth storage stays internal to this handler — callers never reach into
|
|
19
|
+
* `devframe/node/hub-internals` themselves.
|
|
20
|
+
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* import { createInteractiveAuth } from 'devframe/recipes/interactive-auth'
|
|
23
|
+
*
|
|
24
|
+
* const auth = createInteractiveAuth(ctx)
|
|
25
|
+
* auth.rpcFunctions.forEach(fn => ctx.rpc.register(fn))
|
|
26
|
+
* auth.printBanner()
|
|
27
|
+
*
|
|
28
|
+
* // wire `auth.authorize` / `auth.onConnect` into your transport, or pass
|
|
29
|
+
* // the whole handler to `startHttpAndWs({ auth, ... })`.
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
function createInteractiveAuth(context, options = {}) {
|
|
33
|
+
const internal = getInternalContext(context);
|
|
34
|
+
const storage = internal.storage.auth;
|
|
35
|
+
const staticTokens = new Set(options.clientAuthTokens ?? []);
|
|
36
|
+
function isStaticToken(token) {
|
|
37
|
+
return !!token && staticTokens.has(token);
|
|
38
|
+
}
|
|
39
|
+
function resolveServerUrl() {
|
|
40
|
+
return options.serverUrl?.() ?? context.host.resolveOrigin();
|
|
41
|
+
}
|
|
42
|
+
let bannerPrintedForCode;
|
|
43
|
+
function printBanner() {
|
|
44
|
+
const code = getTempAuthCode();
|
|
45
|
+
if (code === bannerPrintedForCode) return;
|
|
46
|
+
bannerPrintedForCode = code;
|
|
47
|
+
const url = buildOtpAuthUrl(resolveServerUrl(), code);
|
|
48
|
+
(options.banner ?? defaultBanner)({
|
|
49
|
+
code,
|
|
50
|
+
url
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const anonymousAuth = defineRpcFunction({
|
|
54
|
+
name: "anonymous:devframe:auth",
|
|
55
|
+
type: "action",
|
|
56
|
+
jsonSerializable: true,
|
|
57
|
+
args: [v.object({
|
|
58
|
+
authToken: v.string(),
|
|
59
|
+
ua: v.string(),
|
|
60
|
+
origin: v.string()
|
|
61
|
+
})],
|
|
62
|
+
returns: v.object({ isTrusted: v.boolean() }),
|
|
63
|
+
handler(params) {
|
|
64
|
+
const session = context.rpc.getCurrentRpcSession();
|
|
65
|
+
if (!session) return { isTrusted: false };
|
|
66
|
+
if (isStaticToken(params.authToken)) {
|
|
67
|
+
session.meta.clientAuthToken = params.authToken;
|
|
68
|
+
session.meta.isTrusted = true;
|
|
69
|
+
return { isTrusted: true };
|
|
70
|
+
}
|
|
71
|
+
return { isTrusted: verifyAuthToken(params.authToken, session, storage) };
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
const anonymousAuthExchange = defineRpcFunction({
|
|
75
|
+
name: "anonymous:devframe:auth:exchange",
|
|
76
|
+
type: "action",
|
|
77
|
+
jsonSerializable: true,
|
|
78
|
+
args: [v.object({
|
|
79
|
+
code: v.string(),
|
|
80
|
+
ua: v.string(),
|
|
81
|
+
origin: v.string()
|
|
82
|
+
})],
|
|
83
|
+
returns: v.object({ authToken: v.nullable(v.string()) }),
|
|
84
|
+
handler(params) {
|
|
85
|
+
const session = context.rpc.getCurrentRpcSession();
|
|
86
|
+
if (!session) return { authToken: null };
|
|
87
|
+
const authToken = exchangeTempAuthCode(params.code, session, params, storage);
|
|
88
|
+
printBanner();
|
|
89
|
+
return { authToken };
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
const revoke = defineRpcFunction({
|
|
93
|
+
name: "devframe:auth:revoke",
|
|
94
|
+
type: "action",
|
|
95
|
+
jsonSerializable: true,
|
|
96
|
+
args: [],
|
|
97
|
+
returns: v.void(),
|
|
98
|
+
async handler() {
|
|
99
|
+
const token = context.rpc.getCurrentRpcSession()?.meta.clientAuthToken;
|
|
100
|
+
if (token) await internal.revokeAuthToken(token);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
function authorize(methodName, session) {
|
|
104
|
+
if (isAnonymousRpcMethod(methodName)) return true;
|
|
105
|
+
return !!session.meta.isTrusted;
|
|
106
|
+
}
|
|
107
|
+
function onConnect(peer, session) {
|
|
108
|
+
let token;
|
|
109
|
+
try {
|
|
110
|
+
token = new URL(peer.request?.url ?? "", "http://localhost").searchParams.get("devframe_auth_token") ?? void 0;
|
|
111
|
+
} catch {}
|
|
112
|
+
if (!token) return;
|
|
113
|
+
if (isStaticToken(token)) {
|
|
114
|
+
session.meta.clientAuthToken = token;
|
|
115
|
+
session.meta.isTrusted = true;
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
verifyAuthToken(token, session, storage);
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
rpcFunctions: [
|
|
122
|
+
anonymousAuth,
|
|
123
|
+
anonymousAuthExchange,
|
|
124
|
+
revoke
|
|
125
|
+
],
|
|
126
|
+
authorize,
|
|
127
|
+
onConnect,
|
|
128
|
+
printBanner
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
//#endregion
|
|
132
|
+
export { createInteractiveAuth };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as launchEditor } from "../launch-editor-
|
|
1
|
+
import { t as launchEditor } from "../launch-editor-DIzaS5EG.mjs";
|
|
2
2
|
import { n as defineRpcFunction } from "../define-BLWPsH6y.mjs";
|
|
3
|
-
import { t as open } from "../open-
|
|
3
|
+
import { t as open } from "../open-Deb5xmIT.mjs";
|
|
4
4
|
import * as v from "valibot";
|
|
5
5
|
//#region src/recipes/open-helpers.ts
|
|
6
6
|
/**
|
|
@@ -19,7 +19,7 @@ function randomToken(byteLength = 16) {
|
|
|
19
19
|
*/
|
|
20
20
|
function randomDigits(length) {
|
|
21
21
|
const limit = 250;
|
|
22
|
-
const buf = new Uint8Array(1);
|
|
22
|
+
const buf = /* @__PURE__ */ new Uint8Array(1);
|
|
23
23
|
let out = "";
|
|
24
24
|
while (out.length < length) {
|
|
25
25
|
globalThis.crypto.getRandomValues(buf);
|
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-
|
|
1
|
+
import { a as dumpFunctions, c as serializeDumpError, i as createClientFromDump, o as getDefinitionsWithDumps, s as reviveDumpError, t as collectStaticRpcDump } from "../dump-D09PZb7T.mjs";
|
|
2
2
|
export { collectStaticRpcDump, createClientFromDump, dumpFunctions, getDefinitionsWithDumps, reviveDumpError, serializeDumpError };
|
package/dist/rpc/index.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
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-
|
|
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 };
|
|
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-Dy6GFDRf.mjs";
|
|
3
|
+
export { type BirpcFn, type 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 };
|
package/dist/rpc/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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-
|
|
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-D09PZb7T.mjs";
|
|
2
2
|
import { t as diagnostics } from "../diagnostics-BXwBQmoN.mjs";
|
|
3
3
|
import { n as defineRpcFunction, t as createDefineWrapperWithContext } from "../define-BLWPsH6y.mjs";
|
|
4
4
|
import { n as strictJsonStringify, t as STRUCTURED_CLONE_PREFIX } from "../serialization-DpLXCy13.mjs";
|
|
@@ -24,6 +24,9 @@ var RpcCacheManager = class {
|
|
|
24
24
|
const methodCache = this.cacheMap.get(m);
|
|
25
25
|
if (methodCache) return methodCache.get(this.keySerializer(a));
|
|
26
26
|
}
|
|
27
|
+
has(m, a) {
|
|
28
|
+
return this.cacheMap.get(m)?.has(this.keySerializer(a)) ?? false;
|
|
29
|
+
}
|
|
27
30
|
apply(req, res) {
|
|
28
31
|
const methodCache = this.cacheMap.get(req.m) || /* @__PURE__ */ new Map();
|
|
29
32
|
methodCache.set(this.keySerializer(req.a), res);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DEVFRAME_AUTH_TOKEN_QUERY_PARAM } from "../../constants.mjs";
|
|
1
2
|
import { n as strictJsonStringify } from "../../serialization-DpLXCy13.mjs";
|
|
2
3
|
import { n as structuredCloneStringify, t as structuredCloneParse } from "../../structured-clone-XpHLZ8nr.mjs";
|
|
3
4
|
//#region src/rpc/transports/ws-client.ts
|
|
@@ -9,14 +10,15 @@ const EMPTY_DEFS = /* @__PURE__ */ new Map();
|
|
|
9
10
|
*/
|
|
10
11
|
function createWsRpcChannel(options) {
|
|
11
12
|
let url = options.url;
|
|
12
|
-
if (options.authToken) url = `${url}
|
|
13
|
+
if (options.authToken) url = `${url}?${DEVFRAME_AUTH_TOKEN_QUERY_PARAM}=${encodeURIComponent(options.authToken)}`;
|
|
13
14
|
const ws = new WebSocket(url);
|
|
14
15
|
const { onConnected = NOOP, onError = NOOP, onDisconnected = NOOP, definitions = EMPTY_DEFS } = options;
|
|
15
16
|
ws.addEventListener("open", (e) => {
|
|
16
17
|
onConnected(e);
|
|
17
18
|
});
|
|
18
19
|
ws.addEventListener("error", (e) => {
|
|
19
|
-
|
|
20
|
+
const _e = e instanceof Error ? e : new Error(e.type);
|
|
21
|
+
onError(_e);
|
|
20
22
|
});
|
|
21
23
|
ws.addEventListener("close", (e) => {
|
|
22
24
|
onDisconnected(e);
|
|
@@ -29,14 +31,25 @@ function createWsRpcChannel(options) {
|
|
|
29
31
|
});
|
|
30
32
|
},
|
|
31
33
|
post: (data) => {
|
|
32
|
-
if (ws.readyState === WebSocket.OPEN)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
35
|
+
ws.send(data);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (ws.readyState === WebSocket.CONNECTING) {
|
|
39
|
+
const onOpen = () => {
|
|
40
|
+
cleanup();
|
|
41
|
+
if (ws.readyState === WebSocket.OPEN) ws.send(data);
|
|
42
|
+
};
|
|
43
|
+
const onClose = () => cleanup();
|
|
44
|
+
function cleanup() {
|
|
45
|
+
ws.removeEventListener("open", onOpen);
|
|
46
|
+
ws.removeEventListener("close", onClose);
|
|
37
47
|
}
|
|
38
|
-
ws.addEventListener("open",
|
|
48
|
+
ws.addEventListener("open", onOpen);
|
|
49
|
+
ws.addEventListener("close", onClose);
|
|
50
|
+
return;
|
|
39
51
|
}
|
|
52
|
+
onError(/* @__PURE__ */ new Error("Devframe WebSocket is not open; message dropped"));
|
|
40
53
|
},
|
|
41
54
|
serialize: (msg) => {
|
|
42
55
|
let method;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as attachWsRpcTransport, n as WsRpcTransport, r as WsRpcTransportOptions, t as DevframeNodeRpcSessionMeta } from "../../ws-server-
|
|
2
|
-
export { DevframeNodeRpcSessionMeta, WsRpcTransport, 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 };
|
|
@@ -12,6 +12,26 @@ function pathMatches(a, b) {
|
|
|
12
12
|
const strip = (p) => p.length > 1 && p.endsWith("/") ? p.slice(0, -1) : p;
|
|
13
13
|
return strip(a) === strip(b);
|
|
14
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
|
+
}
|
|
19
|
+
/**
|
|
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.
|
|
25
|
+
*/
|
|
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
|
+
}
|
|
15
35
|
/**
|
|
16
36
|
* Route `upgrade` events on a server to the crossws adapter, optionally
|
|
17
37
|
* filtered to a single `path`. Non-matching requests are left untouched so
|
|
@@ -19,18 +39,27 @@ function pathMatches(a, b) {
|
|
|
19
39
|
* them, unless `destroyUnmatched` is set. Returns a detach function that
|
|
20
40
|
* removes the listener.
|
|
21
41
|
*/
|
|
22
|
-
function routeUpgrades(server, ws, path, destroyUnmatched) {
|
|
42
|
+
function routeUpgrades(server, ws, path, destroyUnmatched, allowedOrigins) {
|
|
23
43
|
const listener = (req, socket, head) => {
|
|
44
|
+
socket.on("error", () => {});
|
|
24
45
|
if (path) {
|
|
25
46
|
let pathname = req.url ?? "/";
|
|
26
47
|
try {
|
|
27
48
|
pathname = new URL(req.url ?? "/", "http://localhost").pathname;
|
|
28
49
|
} catch {}
|
|
29
50
|
if (!pathMatches(pathname, path)) {
|
|
30
|
-
if (destroyUnmatched)
|
|
51
|
+
if (destroyUnmatched) {
|
|
52
|
+
socket.write("HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n");
|
|
53
|
+
socket.destroy();
|
|
54
|
+
}
|
|
31
55
|
return;
|
|
32
56
|
}
|
|
33
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
|
+
}
|
|
34
63
|
ws.handleUpgrade(req, socket, head);
|
|
35
64
|
};
|
|
36
65
|
server.on("upgrade", listener);
|
|
@@ -47,7 +76,7 @@ function routeUpgrades(server, ws, path, destroyUnmatched) {
|
|
|
47
76
|
* teardown).
|
|
48
77
|
*/
|
|
49
78
|
function attachWsRpcTransport(rpcGroup, options = {}) {
|
|
50
|
-
const { server, port, host = "localhost", path, destroyUnmatched = false, https, onConnected = NOOP, onDisconnected = NOOP, definitions = EMPTY_DEFS, serialize: serializeOverride, deserialize: deserializeOverride } = options;
|
|
79
|
+
const { server, port, host = "localhost", path, destroyUnmatched = false, https, allowedOrigins, onConnected = NOOP, onDisconnected = NOOP, definitions = EMPTY_DEFS, serialize: serializeOverride, deserialize: deserializeOverride } = options;
|
|
51
80
|
const states = /* @__PURE__ */ new WeakMap();
|
|
52
81
|
const ws = crossws({ hooks: {
|
|
53
82
|
open: (peer) => {
|
|
@@ -108,17 +137,17 @@ function attachWsRpcTransport(rpcGroup, options = {}) {
|
|
|
108
137
|
} });
|
|
109
138
|
let detach = NOOP;
|
|
110
139
|
let ownedServer;
|
|
111
|
-
if (server) detach = routeUpgrades(server, ws, path, destroyUnmatched);
|
|
140
|
+
if (server) detach = routeUpgrades(server, ws, path, destroyUnmatched, allowedOrigins);
|
|
112
141
|
else if (https) {
|
|
113
142
|
ownedServer = createServer$1(https);
|
|
114
|
-
detach = routeUpgrades(ownedServer, ws, path, true);
|
|
143
|
+
detach = routeUpgrades(ownedServer, ws, path, true, allowedOrigins);
|
|
115
144
|
ownedServer.listen(port, host);
|
|
116
145
|
} else {
|
|
117
146
|
ownedServer = createServer((_req, res) => {
|
|
118
147
|
res.writeHead(426, { "content-type": "text/plain" });
|
|
119
148
|
res.end("Upgrade Required");
|
|
120
149
|
});
|
|
121
|
-
detach = routeUpgrades(ownedServer, ws, path, true);
|
|
150
|
+
detach = routeUpgrades(ownedServer, ws, path, true, allowedOrigins);
|
|
122
151
|
ownedServer.listen(port, host);
|
|
123
152
|
}
|
|
124
153
|
return {
|
|
@@ -135,4 +164,4 @@ function attachWsRpcTransport(rpcGroup, options = {}) {
|
|
|
135
164
|
};
|
|
136
165
|
}
|
|
137
166
|
//#endregion
|
|
138
|
-
export { attachWsRpcTransport };
|
|
167
|
+
export { attachWsRpcTransport, isAllowedOrigin, isLoopbackHostname };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createRpcServer } from "./rpc/server.mjs";
|
|
2
2
|
import { attachWsRpcTransport } from "./rpc/transports/ws-server.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { a as diagnostics } from "./storage-yrUZaiib.mjs";
|
|
4
|
+
import { t as getInternalContext } from "./context-CKJheEtf.mjs";
|
|
4
5
|
import { createServer } from "node:http";
|
|
5
6
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
7
|
import { H3, toNodeHandler } from "h3";
|
|
@@ -18,13 +19,23 @@ async function startHttpAndWs(options) {
|
|
|
18
19
|
const httpServer = options.server ?? createServer(toNodeHandler(app));
|
|
19
20
|
const rpcHost = context.rpc;
|
|
20
21
|
const asyncStorage = new AsyncLocalStorage();
|
|
22
|
+
const authHandler = typeof options.auth === "object" ? options.auth : void 0;
|
|
23
|
+
const effectiveAuthorize = options.authorize ?? authHandler?.authorize;
|
|
24
|
+
if (authHandler) {
|
|
25
|
+
for (const fn of authHandler.rpcFunctions) if (!rpcHost.definitions.has(fn.name)) rpcHost.register(fn);
|
|
26
|
+
}
|
|
21
27
|
const rpcGroup = createRpcServer(rpcHost.functions, { rpcOptions: { resolver(name, fn) {
|
|
22
28
|
const rpc = this;
|
|
23
29
|
if (!fn) return void 0;
|
|
24
30
|
return async function(...args) {
|
|
31
|
+
const meta = rpc.$meta;
|
|
32
|
+
if (effectiveAuthorize && !effectiveAuthorize(name, {
|
|
33
|
+
meta,
|
|
34
|
+
rpc
|
|
35
|
+
})) throw diagnostics.DF0036({ name });
|
|
25
36
|
return await asyncStorage.run({
|
|
26
37
|
rpc,
|
|
27
|
-
meta
|
|
38
|
+
meta
|
|
28
39
|
}, async () => {
|
|
29
40
|
return (await fn).apply(this, args);
|
|
30
41
|
});
|
|
@@ -38,6 +49,15 @@ async function startHttpAndWs(options) {
|
|
|
38
49
|
} : { server: httpServer },
|
|
39
50
|
path: options.path,
|
|
40
51
|
destroyUnmatched: ownsHttpServer,
|
|
52
|
+
allowedOrigins: options.allowedOrigins,
|
|
53
|
+
onConnected: authHandler || options.onPeerConnect ? (peer, meta) => {
|
|
54
|
+
const session = {
|
|
55
|
+
meta,
|
|
56
|
+
rpc: rpcGroup.clients.find((client) => client.$meta === meta)
|
|
57
|
+
};
|
|
58
|
+
authHandler?.onConnect(peer, session);
|
|
59
|
+
options.onPeerConnect?.(peer, session);
|
|
60
|
+
} : void 0,
|
|
41
61
|
onDisconnected: (_peer, meta) => {
|
|
42
62
|
rpcHost._emitSessionDisconnected(meta);
|
|
43
63
|
}
|
|
@@ -45,8 +65,8 @@ async function startHttpAndWs(options) {
|
|
|
45
65
|
rpcHost._rpcGroup = rpcGroup;
|
|
46
66
|
rpcHost._asyncStorage = asyncStorage;
|
|
47
67
|
rpcHost._authDisabled = options.auth === false;
|
|
48
|
-
if (options.auth === false && !rpcHost.definitions.has("devframe:
|
|
49
|
-
name: "devframe:
|
|
68
|
+
if (options.auth === false && !rpcHost.definitions.has("anonymous:devframe:auth")) rpcHost.register({
|
|
69
|
+
name: "anonymous:devframe:auth",
|
|
50
70
|
type: "action",
|
|
51
71
|
handler: () => {
|
|
52
72
|
const session = rpcHost.getCurrentRpcSession();
|
|
@@ -68,12 +88,25 @@ async function startHttpAndWs(options) {
|
|
|
68
88
|
port: resolvedPort,
|
|
69
89
|
app
|
|
70
90
|
});
|
|
91
|
+
function connectionMeta() {
|
|
92
|
+
const jsonSerializableMethods = [];
|
|
93
|
+
for (const def of rpcHost.definitions.values()) if (def.jsonSerializable === true) jsonSerializableMethods.push(def.name);
|
|
94
|
+
return {
|
|
95
|
+
backend: "websocket",
|
|
96
|
+
websocket: separateWsPort != null ? {
|
|
97
|
+
port: separateWsPort,
|
|
98
|
+
path: options.path
|
|
99
|
+
} : { path: options.path },
|
|
100
|
+
jsonSerializableMethods
|
|
101
|
+
};
|
|
102
|
+
}
|
|
71
103
|
return {
|
|
72
104
|
origin,
|
|
73
105
|
port: resolvedPort,
|
|
74
106
|
app,
|
|
75
107
|
ws,
|
|
76
108
|
rpcGroup,
|
|
109
|
+
connectionMeta,
|
|
77
110
|
async close() {
|
|
78
111
|
await closeWs();
|
|
79
112
|
if (ownsHttpServer) await new Promise((r) => httpServer.close(() => r()));
|