devframe 0.8.2 → 0.9.0-beta.10
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/_shared-BPmX9fqK.d.mts +41 -0
- package/dist/_shared-CbZ_yhbz.mjs +68 -0
- package/dist/adapters/build.d.mts +10 -12
- package/dist/adapters/build.mjs +26 -29
- package/dist/adapters/cac.d.mts +1 -1
- package/dist/adapters/cac.mjs +156 -1
- package/dist/adapters/dev.d.mts +38 -46
- package/dist/adapters/dev.mjs +2 -1
- package/dist/adapters/embedded.d.mts +1 -1
- package/dist/adapters/initiate.d.mts +224 -0
- package/dist/adapters/initiate.mjs +2 -0
- package/dist/adapters/mcp.d.mts +24 -7
- package/dist/adapters/mcp.mjs +2 -2
- package/dist/{agent-tool-name-C3b5vEwJ.mjs → agent-tool-name-EgfoFO8C.mjs} +0 -3
- package/dist/cli/main.d.mts +0 -2
- package/dist/cli/main.mjs +4 -8
- package/dist/client/index.d.mts +71 -7
- package/dist/client/index.mjs +720 -1540
- package/dist/constants.d.mts +28 -7
- package/dist/constants.mjs +28 -7
- package/dist/{context-DoSozfvx.d.mts → context-B3YUu_95.d.mts} +19 -1
- package/dist/{host-h3-D2hYZvDK.mjs → context-CEe4PBz1.mjs} +25 -557
- package/dist/{context-CBg8iRbQ.mjs → context-xdynGSLP.mjs} +10 -1
- package/dist/dev--rAPzbvO.mjs +239 -0
- package/dist/{devframe-mQ4qZAuo.d.mts → devframe-DJMg0WLe.d.mts} +181 -134
- package/dist/{diagnostics-hwjXp_UV.mjs → diagnostics-CD8nlgll.mjs} +1 -3
- package/dist/{diagnostics-D7wM2cwM.mjs → diagnostics-DOQPnQmX.mjs} +51 -5
- package/dist/dist-eNI9qM_I.mjs +228 -0
- package/dist/host-agent-xLCSe65D.mjs +264 -0
- package/dist/host-h3-Crkzm42q.mjs +26 -0
- package/dist/{fetch-BKaD0Vb3.mjs → http-DYpK0cUi.mjs} +42 -11
- package/dist/{index-b64Uuhy2.d.mts → index-CB-fSUyn.d.mts} +2 -2
- package/dist/{index-Dbw1p5ch.d.mts → index-tdrk1bzV.d.mts} +3 -32
- package/dist/index.d.mts +12 -5
- package/dist/index.mjs +8 -1
- package/dist/{instance-registry-BnnA2Eoh.mjs → instance-registry-WvQkt42E.mjs} +9 -8
- package/dist/instance-shell-CMFyszWJ.mjs +486 -0
- package/dist/instance-shell-CljiKq1c.d.mts +255 -0
- package/dist/internal/index.d.mts +397 -0
- package/dist/internal/index.mjs +9 -0
- package/dist/node/auth.d.mts +2 -2
- package/dist/node/auth.mjs +1 -1
- package/dist/node/hub-internals.d.mts +2 -14
- package/dist/node/hub-internals.mjs +2 -2
- package/dist/node/index.d.mts +4 -268
- package/dist/node/index.mjs +3 -5
- package/dist/nostics-CzECRXpE.mjs +59 -0
- package/dist/nostics-SPGDgHEP.d.mts +33 -0
- package/dist/recipes/common-rpc-functions.d.mts +4 -4
- package/dist/recipes/interactive-auth.d.mts +3 -3
- package/dist/recipes/interactive-auth.mjs +8 -8
- package/dist/remote-assets-Bg4gCUZ_.d.mts +123 -0
- package/dist/remote-assets-nTTxY2MU.mjs +636 -0
- package/dist/rolldown-runtime-JspESFgx.mjs +35 -0
- package/dist/rpc/dump.d.mts +77 -1
- package/dist/rpc/dump.mjs +1 -1
- package/dist/rpc/index.d.mts +3 -3
- package/dist/rpc/index.mjs +4 -18
- package/dist/rpc/transports/sse-client.d.mts +2 -0
- package/dist/rpc/transports/sse-client.mjs +142 -0
- package/dist/rpc/transports/sse-server.d.mts +61 -0
- package/dist/rpc/transports/sse-server.mjs +209 -0
- package/dist/rpc/transports/ws-bun.d.mts +35 -0
- package/dist/rpc/transports/ws-bun.mjs +31 -0
- package/dist/rpc/transports/ws-client.d.mts +1 -1
- package/dist/rpc/transports/ws-client.mjs +10 -18
- package/dist/rpc/transports/ws-server.d.mts +2 -2
- package/dist/rpc/transports/ws-server.mjs +2 -235
- package/dist/rpc-core-Cc4lucI0.d.mts +48 -0
- package/dist/rpc-core-DMTe-hLo.mjs +81 -0
- package/dist/{serialization-C8Mnw9hK.mjs → serialization-BGzEwAdr.mjs} +2 -2
- package/dist/{shared-state-6kHolYZy.mjs → shared-state-fRRbRUtD.mjs} +1 -1
- package/dist/sse-client-CFBOIaUi.d.mts +37 -0
- package/dist/state-Cc1IeSmQ.mjs +1291 -0
- package/dist/{dump-CgZShDRB.mjs → static-BPZ1_OPA.mjs} +3 -266
- package/dist/storage-CYye7P7L.mjs +137 -0
- package/dist/types/index.d.mts +6 -4
- package/dist/types/index.mjs +1 -6
- package/dist/{types-CnJSgRVa.d.mts → types-Djq7CBXh.d.mts} +0 -7
- package/dist/utils/agent-tool-name.d.mts +0 -3
- package/dist/utils/agent-tool-name.mjs +0 -3
- package/dist/utils/events.d.mts +1 -1
- package/dist/utils/get-port.d.mts +17 -0
- package/dist/utils/get-port.mjs +2 -0
- package/dist/utils/launch-editor.mjs +1 -25
- package/dist/utils/nostics.d.mts +2 -0
- package/dist/utils/nostics.mjs +2 -0
- package/dist/utils/open.d.mts +2 -10
- package/dist/utils/open.mjs +56 -1
- package/dist/utils/remote-assets.d.mts +15 -0
- package/dist/utils/remote-assets.mjs +2 -0
- package/dist/utils/serve-static.d.mts +13 -5
- package/dist/utils/serve-static.mjs +119 -9
- package/dist/utils/shared-state.d.mts +1 -1
- package/dist/utils/shared-state.mjs +1 -1
- package/dist/utils/simple-schema.d.mts +101 -1
- package/dist/utils/streaming-channel.d.mts +1 -1
- package/dist/validation-CpXFB6Dz.mjs +266 -0
- package/dist/wire-codec-0K-o5MYW.mjs +56 -0
- package/dist/{ws-client-B6Tnr0ic.d.mts → ws-client-DhjNyQo3.d.mts} +7 -2
- package/dist/ws-server-BdSLrhxE.mjs +267 -0
- package/dist/{ws-server-uKQcClcJ.d.mts → ws-server-D1d3QM9f.d.mts} +90 -9
- package/package.json +13 -12
- package/skills/devframe/SKILL.md +91 -93
- package/dist/_shared-bWRzeSa0.mjs +0 -19
- package/dist/adapters/cli.d.mts +0 -11
- package/dist/adapters/cli.mjs +0 -6
- package/dist/cac-D4_9ok0z.mjs +0 -157
- package/dist/dev-Bc49u3qg.mjs +0 -439
- package/dist/diagnostics-reporter-CsIG85Q5.mjs +0 -38
- package/dist/helpers/vite.d.mts +0 -100
- package/dist/helpers/vite.mjs +0 -105
- package/dist/http-C4T68YV6.mjs +0 -38
- package/dist/index-vAX6bYuc.d.mts +0 -78
- package/dist/open-Deb5xmIT.mjs +0 -547
- package/dist/recipes/open-helpers.d.mts +0 -6
- package/dist/recipes/open-helpers.mjs +0 -6
- package/dist/server-CD8XmBXl.mjs +0 -167
- package/dist/server-UHWae43Q.d.mts +0 -149
- package/dist/simple-schema-BDzLeJDk.d.mts +0 -102
- package/dist/state-BeUHDDjk.mjs +0 -108
- package/dist/storage-CTNVbuFB.mjs +0 -1122
- package/dist/utils/promise.d.mts +0 -8
- package/dist/utils/promise.mjs +0 -15
- package/dist/utils/scope.d.mts +0 -11
- package/dist/utils/scope.mjs +0 -15
- package/skills/devframe/templates/spa-devframe.ts +0 -38
package/dist/client/index.mjs
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { t as hash } from "../hash-KtDZYXDN.mjs";
|
|
2
2
|
import { colors } from "../utils/colors.mjs";
|
|
3
3
|
import { createEventEmitter } from "../utils/events.mjs";
|
|
4
|
-
import { t as createSharedState } from "../shared-state-
|
|
4
|
+
import { t as createSharedState } from "../shared-state-fRRbRUtD.mjs";
|
|
5
5
|
import { i as structuredCloneStringify, n as structuredCloneParse, t as structuredCloneDeserialize } from "../structured-clone-CgtQ61SA.mjs";
|
|
6
6
|
import { createStreamReader, createStreamSink } from "../utils/streaming-channel.mjs";
|
|
7
|
-
import { promiseWithResolver } from "../utils/promise.mjs";
|
|
8
|
-
import { isQualifiedName, qualifyName } from "../utils/scope.mjs";
|
|
9
7
|
import { defineDiagnostics } from "nostics";
|
|
10
8
|
import { ansiFormatter } from "nostics/formatters/ansi";
|
|
11
9
|
import { withBase, withProtocol } from "ufo";
|
|
@@ -18,6 +16,13 @@ const DEVFRAME_CONNECTION_META_FILENAME = "__connection.json";
|
|
|
18
16
|
* connection from another JavaScript realm.
|
|
19
17
|
*/
|
|
20
18
|
const DEVFRAME_CONNECTION_KEY = "__DEVFRAME_CONNECTION__";
|
|
19
|
+
/**
|
|
20
|
+
* Request header carrying the SSE session id on every RPC `POST`. The
|
|
21
|
+
* server mints the id as the stream's first event (`event: session`);
|
|
22
|
+
* the client echoes it here so the POST joins that stream's birpc
|
|
23
|
+
* channel. Mirrors birpc's own SSE helpers for wire compatibility.
|
|
24
|
+
*/
|
|
25
|
+
const DEVFRAME_SSE_SESSION_HEADER = "x-birpc-session";
|
|
21
26
|
const DEVFRAME_RPC_DUMP_MANIFEST_FILENAME = "__rpc-dump/index.json";
|
|
22
27
|
/**
|
|
23
28
|
* Page-URL **fragment** parameter carrying a one-time authentication code (OTP)
|
|
@@ -43,16 +48,74 @@ const DEVFRAME_VIEWER_ORIGIN_QUERY_PARAM = "devframe_viewer_origin";
|
|
|
43
48
|
/** Token that authorizes an external viewer origin registration. */
|
|
44
49
|
const DEVFRAME_VIEWER_ORIGIN_TOKEN_QUERY_PARAM = "devframe_viewer_origin_token";
|
|
45
50
|
//#endregion
|
|
46
|
-
//#region src/
|
|
51
|
+
//#region src/rpc/cache.ts
|
|
52
|
+
/**
|
|
53
|
+
* @experimental API is expected to change.
|
|
54
|
+
*/
|
|
55
|
+
var RpcCacheManager = class {
|
|
56
|
+
cacheMap = /* @__PURE__ */ new Map();
|
|
57
|
+
options;
|
|
58
|
+
keySerializer;
|
|
59
|
+
constructor(options) {
|
|
60
|
+
this.options = options;
|
|
61
|
+
this.keySerializer = options.keySerializer || ((args) => hash(args));
|
|
62
|
+
}
|
|
63
|
+
updateOptions(options) {
|
|
64
|
+
this.options = {
|
|
65
|
+
...this.options,
|
|
66
|
+
...options
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
cached(m, a) {
|
|
70
|
+
const methodCache = this.cacheMap.get(m);
|
|
71
|
+
if (methodCache) return methodCache.get(this.keySerializer(a));
|
|
72
|
+
}
|
|
73
|
+
has(m, a) {
|
|
74
|
+
return this.cacheMap.get(m)?.has(this.keySerializer(a)) ?? false;
|
|
75
|
+
}
|
|
76
|
+
apply(req, res) {
|
|
77
|
+
const methodCache = this.cacheMap.get(req.m) || /* @__PURE__ */ new Map();
|
|
78
|
+
methodCache.set(this.keySerializer(req.a), res);
|
|
79
|
+
this.cacheMap.set(req.m, methodCache);
|
|
80
|
+
}
|
|
81
|
+
validate(m) {
|
|
82
|
+
return this.options.functions.includes(m);
|
|
83
|
+
}
|
|
84
|
+
clear(fn) {
|
|
85
|
+
if (fn) this.cacheMap.delete(fn);
|
|
86
|
+
else this.cacheMap.clear();
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
//#endregion
|
|
90
|
+
//#region src/utils/nostics.ts
|
|
47
91
|
const formatAnsi = ansiFormatter(colors);
|
|
92
|
+
/**
|
|
93
|
+
* The reporter every {@link defineDiagnostics} call below wires in ahead of
|
|
94
|
+
* any caller-supplied ones: prints the diagnostic through devframe's own
|
|
95
|
+
* ANSI colors via `console[method]` (default `'warn'`).
|
|
96
|
+
*/
|
|
48
97
|
function devframeReporter(d, { method = "warn" } = {}) {
|
|
49
98
|
console[method](formatAnsi(d));
|
|
50
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Drop-in replacement for `nostics`'s `defineDiagnostics()` with devframe's
|
|
102
|
+
* ANSI console reporter pre-wired ahead of any `reporters` passed in. Every
|
|
103
|
+
* `diagnostics.ts` in devframe core, `@devframes/hub`, `@devframes/json-render`,
|
|
104
|
+
* and the built-in plugins defines its codes through this instead of
|
|
105
|
+
* `nostics`'s own `defineDiagnostics` — the reporter registration lives
|
|
106
|
+
* here, once, so none of them need to build their own reporter (`colors`,
|
|
107
|
+
* `ansiFormatter`) or take a direct dependency on `nostics` themselves.
|
|
108
|
+
*/
|
|
109
|
+
function defineDiagnostics$1(options) {
|
|
110
|
+
return defineDiagnostics({
|
|
111
|
+
...options,
|
|
112
|
+
reporters: [devframeReporter, ...options.reporters ?? []]
|
|
113
|
+
});
|
|
114
|
+
}
|
|
51
115
|
//#endregion
|
|
52
116
|
//#region src/rpc/diagnostics.ts
|
|
53
|
-
const diagnostics = defineDiagnostics({
|
|
117
|
+
const diagnostics = defineDiagnostics$1({
|
|
54
118
|
docsBase: "https://devfra.me/errors",
|
|
55
|
-
reporters: [devframeReporter],
|
|
56
119
|
codes: {
|
|
57
120
|
DF0019: {
|
|
58
121
|
why: (p) => `RPC function "${p.name}" has \`agent\` set but \`jsonSerializable\` is not \`true\` — MCP requires JSON-serializable data.`,
|
|
@@ -87,26 +150,6 @@ const diagnostics = defineDiagnostics({
|
|
|
87
150
|
}
|
|
88
151
|
});
|
|
89
152
|
//#endregion
|
|
90
|
-
//#region src/rpc/dump/error.ts
|
|
91
|
-
/**
|
|
92
|
-
* Inverse of {@link serializeDumpError}: rebuild a thrown `Error` from
|
|
93
|
-
* the plain object stored in a dump record. Preserves `cause`, restores
|
|
94
|
-
* the original `name`, and re-attaches any custom own properties.
|
|
95
|
-
*/
|
|
96
|
-
function reviveDumpError(stored) {
|
|
97
|
-
const cause = stored.cause instanceof Error ? stored.cause : isPlainErrorShape(stored.cause) ? reviveDumpError(stored.cause) : stored.cause;
|
|
98
|
-
const error = cause !== void 0 ? new Error(stored.message, { cause }) : new Error(stored.message);
|
|
99
|
-
error.name = stored.name;
|
|
100
|
-
for (const key of Object.keys(stored)) {
|
|
101
|
-
if (key === "name" || key === "message" || key === "cause") continue;
|
|
102
|
-
error[key] = stored[key];
|
|
103
|
-
}
|
|
104
|
-
return error;
|
|
105
|
-
}
|
|
106
|
-
function isPlainErrorShape(value) {
|
|
107
|
-
return typeof value === "object" && value !== null && typeof value.message === "string" && typeof value.name === "string";
|
|
108
|
-
}
|
|
109
|
-
//#endregion
|
|
110
153
|
//#region src/rpc/validate-io.ts
|
|
111
154
|
/**
|
|
112
155
|
* Run a single [Standard Schema](https://standardschema.dev) validator,
|
|
@@ -215,45 +258,6 @@ async function getRpcHandler(definition, context) {
|
|
|
215
258
|
return validating;
|
|
216
259
|
}
|
|
217
260
|
//#endregion
|
|
218
|
-
//#region src/rpc/cache.ts
|
|
219
|
-
/**
|
|
220
|
-
* @experimental API is expected to change.
|
|
221
|
-
*/
|
|
222
|
-
var RpcCacheManager = class {
|
|
223
|
-
cacheMap = /* @__PURE__ */ new Map();
|
|
224
|
-
options;
|
|
225
|
-
keySerializer;
|
|
226
|
-
constructor(options) {
|
|
227
|
-
this.options = options;
|
|
228
|
-
this.keySerializer = options.keySerializer || ((args) => hash(args));
|
|
229
|
-
}
|
|
230
|
-
updateOptions(options) {
|
|
231
|
-
this.options = {
|
|
232
|
-
...this.options,
|
|
233
|
-
...options
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
cached(m, a) {
|
|
237
|
-
const methodCache = this.cacheMap.get(m);
|
|
238
|
-
if (methodCache) return methodCache.get(this.keySerializer(a));
|
|
239
|
-
}
|
|
240
|
-
has(m, a) {
|
|
241
|
-
return this.cacheMap.get(m)?.has(this.keySerializer(a)) ?? false;
|
|
242
|
-
}
|
|
243
|
-
apply(req, res) {
|
|
244
|
-
const methodCache = this.cacheMap.get(req.m) || /* @__PURE__ */ new Map();
|
|
245
|
-
methodCache.set(this.keySerializer(req.a), res);
|
|
246
|
-
this.cacheMap.set(req.m, methodCache);
|
|
247
|
-
}
|
|
248
|
-
validate(m) {
|
|
249
|
-
return this.options.functions.includes(m);
|
|
250
|
-
}
|
|
251
|
-
clear(fn) {
|
|
252
|
-
if (fn) this.cacheMap.delete(fn);
|
|
253
|
-
else this.cacheMap.clear();
|
|
254
|
-
}
|
|
255
|
-
};
|
|
256
|
-
//#endregion
|
|
257
261
|
//#region src/rpc/collector.ts
|
|
258
262
|
var RpcFunctionsCollectorBase = class {
|
|
259
263
|
context;
|
|
@@ -706,112 +710,590 @@ function createRpcSharedStateClientHost(rpc) {
|
|
|
706
710
|
};
|
|
707
711
|
}
|
|
708
712
|
//#endregion
|
|
709
|
-
//#region src/
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
return
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
713
|
+
//#region src/rpc/wire-codec.ts
|
|
714
|
+
const EMPTY_WIRE_DEFS = /* @__PURE__ */ new Map();
|
|
715
|
+
/**
|
|
716
|
+
* Build the per-connection wire codec every live transport (WS server, WS
|
|
717
|
+
* client, SSE server, SSE client) shares: per-method dispatch between strict
|
|
718
|
+
* JSON (methods declared `jsonSerializable: true`) and `s:`-prefixed
|
|
719
|
+
* structured-clone (everything else, including all error envelopes), with a
|
|
720
|
+
* request-id → method map so a response independently picks the same
|
|
721
|
+
* encoder as its request. One codec per connection — request-id spaces
|
|
722
|
+
* don't collide across connections.
|
|
723
|
+
*
|
|
724
|
+
* @internal
|
|
725
|
+
*/
|
|
726
|
+
function createRpcWireCodec(definitions = EMPTY_WIRE_DEFS) {
|
|
727
|
+
const pendingRequestMethods = /* @__PURE__ */ new Map();
|
|
728
|
+
return {
|
|
729
|
+
serialize: (msg) => {
|
|
730
|
+
let method;
|
|
731
|
+
if (msg.t === "q") method = msg.m;
|
|
732
|
+
else {
|
|
733
|
+
method = pendingRequestMethods.get(msg.i);
|
|
734
|
+
pendingRequestMethods.delete(msg.i);
|
|
735
|
+
}
|
|
736
|
+
if (!(msg.t === "s" && "e" in msg) && !!method && definitions.get(method)?.jsonSerializable === true) return strictJsonStringify(msg, method ?? "");
|
|
737
|
+
return `s:${structuredCloneStringify(msg)}`;
|
|
738
|
+
},
|
|
739
|
+
deserialize: (raw) => {
|
|
740
|
+
const msg = raw.startsWith("s:") ? structuredCloneParse(raw.slice(2)) : JSON.parse(raw);
|
|
741
|
+
if (msg.t === "q" && msg.i && msg.m) pendingRequestMethods.set(msg.i, msg.m);
|
|
742
|
+
return msg;
|
|
743
|
+
}
|
|
744
|
+
};
|
|
729
745
|
}
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
746
|
+
//#endregion
|
|
747
|
+
//#region src/rpc/transports/sse-client.ts
|
|
748
|
+
function NOOP$1() {}
|
|
749
|
+
/**
|
|
750
|
+
* Build a birpc `ChannelOptions` object backed by an SSE stream (server →
|
|
751
|
+
* client) and HTTP `POST` (client → server) — the WebSocket-free counterpart
|
|
752
|
+
* to `createWsRpcChannel`, wire-compatible with `attachSseRpcTransport`.
|
|
753
|
+
*
|
|
754
|
+
* The stream is consumed via `fetch` streaming (not `EventSource`), so it
|
|
755
|
+
* works in browsers and server runtimes alike. The server's first frame
|
|
756
|
+
* (`event: session`) carries the session id; every `POST` echoes it in the
|
|
757
|
+
* `x-birpc-session` header. A response to a client-initiated request comes
|
|
758
|
+
* back in the `POST`'s own body and is re-injected into the channel; a
|
|
759
|
+
* dropped stream terminates the channel — there is no reconnect, matching
|
|
760
|
+
* the WS channel's closed-is-done semantics.
|
|
761
|
+
*/
|
|
762
|
+
function createSseRpcChannel(options) {
|
|
763
|
+
const { onConnected = NOOP$1, onError = NOOP$1, onDisconnected = NOOP$1, definitions, fetch: fetchImpl = globalThis.fetch.bind(globalThis) } = options;
|
|
764
|
+
let url = options.url;
|
|
765
|
+
if (options.authToken) url = `${url}${url.includes("?") ? "&" : "?"}${DEVFRAME_AUTH_TOKEN_QUERY_PARAM}=${encodeURIComponent(options.authToken)}`;
|
|
766
|
+
const codec = createRpcWireCodec(definitions);
|
|
767
|
+
const abort = new AbortController();
|
|
768
|
+
let closed = false;
|
|
769
|
+
let onMessage;
|
|
770
|
+
let activeReader;
|
|
771
|
+
let resolveSession;
|
|
772
|
+
let rejectSession;
|
|
773
|
+
const sessionReady = new Promise((resolve, reject) => {
|
|
774
|
+
resolveSession = resolve;
|
|
775
|
+
rejectSession = reject;
|
|
776
|
+
});
|
|
777
|
+
sessionReady.catch(() => {});
|
|
778
|
+
function fail(error) {
|
|
779
|
+
if (closed) return;
|
|
780
|
+
closed = true;
|
|
781
|
+
rejectSession(error);
|
|
782
|
+
onError(error);
|
|
783
|
+
onDisconnected();
|
|
745
784
|
}
|
|
746
|
-
|
|
747
|
-
if (
|
|
748
|
-
|
|
785
|
+
function end() {
|
|
786
|
+
if (closed) return;
|
|
787
|
+
closed = true;
|
|
788
|
+
rejectSession(/* @__PURE__ */ new Error("Devframe SSE stream closed"));
|
|
789
|
+
onDisconnected();
|
|
749
790
|
}
|
|
750
|
-
|
|
751
|
-
if (
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
return await loadStatic(entry);
|
|
791
|
+
function dispatch(event, data) {
|
|
792
|
+
if (event === "session") {
|
|
793
|
+
resolveSession(data);
|
|
794
|
+
onConnected();
|
|
795
|
+
return;
|
|
756
796
|
}
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
797
|
+
onMessage?.(data);
|
|
798
|
+
}
|
|
799
|
+
/** Parse the SSE byte stream frame by frame and dispatch each event. */
|
|
800
|
+
async function consume(stream) {
|
|
801
|
+
const reader = stream.getReader();
|
|
802
|
+
activeReader = reader;
|
|
803
|
+
const decoder = new TextDecoder();
|
|
804
|
+
let buffer = "";
|
|
805
|
+
for (;;) {
|
|
806
|
+
const { done, value } = await reader.read();
|
|
807
|
+
if (done) break;
|
|
808
|
+
buffer += decoder.decode(value, { stream: true });
|
|
809
|
+
for (;;) {
|
|
810
|
+
const boundary = buffer.search(/\n\n|\r\n\r\n/);
|
|
811
|
+
if (boundary < 0) break;
|
|
812
|
+
const frame = buffer.slice(0, boundary);
|
|
813
|
+
buffer = buffer.slice(boundary + (buffer[boundary] === "\r" ? 4 : 2));
|
|
814
|
+
let event = "message";
|
|
815
|
+
const data = [];
|
|
816
|
+
for (const rawLine of frame.split(/\r?\n/)) {
|
|
817
|
+
if (rawLine.startsWith(":")) continue;
|
|
818
|
+
if (rawLine.startsWith("event:")) event = rawLine.slice(6).trimStart();
|
|
819
|
+
else if (rawLine.startsWith("data:")) data.push(rawLine.slice(5).replace(/^ /, ""));
|
|
820
|
+
}
|
|
821
|
+
if (data.length > 0) dispatch(event, data.join("\n"));
|
|
822
|
+
}
|
|
763
823
|
}
|
|
764
|
-
|
|
765
|
-
throw new Error(`[devframe-rpc] No dump match for "${functionName}" with args: ${JSON.stringify(args)}`);
|
|
824
|
+
end();
|
|
766
825
|
}
|
|
826
|
+
(async () => {
|
|
827
|
+
try {
|
|
828
|
+
const response = await fetchImpl(url, {
|
|
829
|
+
headers: { accept: "text/event-stream" },
|
|
830
|
+
signal: abort.signal
|
|
831
|
+
});
|
|
832
|
+
if (!response.ok || !response.body) throw new Error(`Devframe SSE stream request failed: ${response.status}`);
|
|
833
|
+
await consume(response.body);
|
|
834
|
+
} catch (error) {
|
|
835
|
+
if (abort.signal.aborted) {
|
|
836
|
+
end();
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
fail(error instanceof Error ? error : new Error(String(error)));
|
|
840
|
+
}
|
|
841
|
+
})();
|
|
767
842
|
return {
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
843
|
+
close: () => {
|
|
844
|
+
closed = true;
|
|
845
|
+
abort.abort();
|
|
846
|
+
activeReader?.cancel().catch(() => {});
|
|
772
847
|
},
|
|
773
|
-
|
|
848
|
+
on: (handler) => {
|
|
849
|
+
onMessage = handler;
|
|
850
|
+
},
|
|
851
|
+
post: async (data) => {
|
|
852
|
+
let sessionId;
|
|
853
|
+
try {
|
|
854
|
+
sessionId = await sessionReady;
|
|
855
|
+
} catch {
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
if (closed) {
|
|
859
|
+
onError(/* @__PURE__ */ new Error("Devframe SSE channel is closed; message dropped"));
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
try {
|
|
863
|
+
const response = await fetchImpl(url, {
|
|
864
|
+
method: "POST",
|
|
865
|
+
headers: {
|
|
866
|
+
"content-type": "text/plain; charset=utf-8",
|
|
867
|
+
[DEVFRAME_SSE_SESSION_HEADER]: sessionId
|
|
868
|
+
},
|
|
869
|
+
body: data
|
|
870
|
+
});
|
|
871
|
+
if (response.status === 200) {
|
|
872
|
+
const body = await response.text();
|
|
873
|
+
if (body) onMessage?.(body);
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
if (!response.ok) throw new Error(`Devframe SSE POST failed: ${response.status}`);
|
|
877
|
+
} catch (error) {
|
|
878
|
+
onError(error instanceof Error ? error : new Error(String(error)));
|
|
879
|
+
}
|
|
880
|
+
},
|
|
881
|
+
serialize: codec.serialize,
|
|
882
|
+
deserialize: codec.deserialize
|
|
774
883
|
};
|
|
775
884
|
}
|
|
776
885
|
//#endregion
|
|
777
|
-
//#region src/client
|
|
778
|
-
|
|
779
|
-
const
|
|
886
|
+
//#region src/rpc/client.ts
|
|
887
|
+
function createRpcClient(functions, options) {
|
|
888
|
+
const { channel, rpcOptions = {} } = options;
|
|
889
|
+
return createBirpc(functions, {
|
|
890
|
+
...channel,
|
|
891
|
+
timeout: -1,
|
|
892
|
+
...rpcOptions,
|
|
893
|
+
proxify: false
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
//#endregion
|
|
897
|
+
//#region src/utils/promise.ts
|
|
898
|
+
function promiseWithResolver() {
|
|
899
|
+
let resolve;
|
|
900
|
+
let reject;
|
|
780
901
|
return {
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
ensureTrusted: async () => true,
|
|
788
|
-
call: (...args) => staticCaller.call(args[0], args.slice(1)),
|
|
789
|
-
callEvent: (...args) => staticCaller.callEvent(args[0], args.slice(1)),
|
|
790
|
-
callOptional: (...args) => staticCaller.callOptional(args[0], args.slice(1))
|
|
902
|
+
promise: new Promise((_resolve, _reject) => {
|
|
903
|
+
resolve = _resolve;
|
|
904
|
+
reject = _reject;
|
|
905
|
+
}),
|
|
906
|
+
resolve,
|
|
907
|
+
reject
|
|
791
908
|
};
|
|
792
909
|
}
|
|
793
910
|
//#endregion
|
|
794
|
-
//#region src/client/rpc-
|
|
795
|
-
const STREAM_KEY_SEPARATOR = "";
|
|
796
|
-
function streamKey(channel, id) {
|
|
797
|
-
return `${channel}${STREAM_KEY_SEPARATOR}${id}`;
|
|
798
|
-
}
|
|
911
|
+
//#region src/client/rpc-live.ts
|
|
799
912
|
/**
|
|
800
|
-
*
|
|
801
|
-
*
|
|
802
|
-
*
|
|
913
|
+
* The transport-independent half of a live RPC client mode — connection
|
|
914
|
+
* status, pending-call guarding, and the trust handshake — shared by the
|
|
915
|
+
* WebSocket and SSE modes. Each transport supplies only its channel.
|
|
803
916
|
*/
|
|
804
|
-
function
|
|
805
|
-
const
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
917
|
+
function createLiveRpcClientMode(options) {
|
|
918
|
+
const { transport, authToken, connectionMeta, events, clientRpc, rpcOptions = {}, callTimeout = 0 } = options;
|
|
919
|
+
let isTrusted = false;
|
|
920
|
+
let status = "connecting";
|
|
921
|
+
let connectionError = null;
|
|
922
|
+
const trustedPromise = promiseWithResolver();
|
|
923
|
+
function setStatus(next, error = null) {
|
|
924
|
+
if (error) connectionError = error;
|
|
925
|
+
else if (next === "connected") connectionError = null;
|
|
926
|
+
if (next === status) return;
|
|
927
|
+
const previous = status;
|
|
928
|
+
status = next;
|
|
929
|
+
events.emit("connection:status", next, previous);
|
|
930
|
+
}
|
|
931
|
+
const pending = /* @__PURE__ */ new Set();
|
|
932
|
+
function rejectAllPending(error) {
|
|
933
|
+
for (const entry of [...pending]) entry.reject(error);
|
|
934
|
+
}
|
|
935
|
+
function terminalError() {
|
|
936
|
+
if (status === "disconnected" || status === "error") return new DevframeConnectionError("connection", "[devframe] Not connected to the devframe server", { cause: connectionError ?? void 0 });
|
|
937
|
+
if (status === "unauthorized") return new DevframeConnectionError("auth", "[devframe] Not authorized by the devframe server", { cause: connectionError ?? void 0 });
|
|
938
|
+
return null;
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* Wrap an in-flight call promise so it settles on server response, on an
|
|
942
|
+
* optional wall-clock timeout, or when the connection drops — never hangs.
|
|
943
|
+
*/
|
|
944
|
+
function guardCall(promise, method) {
|
|
945
|
+
return new Promise((resolve, reject) => {
|
|
946
|
+
let settled = false;
|
|
947
|
+
let timer;
|
|
948
|
+
const entry = { reject(error) {
|
|
949
|
+
if (settled) return;
|
|
950
|
+
finish();
|
|
951
|
+
events.emit("rpc:error", error, method);
|
|
952
|
+
reject(error);
|
|
953
|
+
} };
|
|
954
|
+
function finish() {
|
|
955
|
+
settled = true;
|
|
956
|
+
pending.delete(entry);
|
|
957
|
+
if (timer) clearTimeout(timer);
|
|
958
|
+
}
|
|
959
|
+
pending.add(entry);
|
|
960
|
+
if (callTimeout > 0) timer = setTimeout(() => {
|
|
961
|
+
entry.reject(new DevframeConnectionError("timeout", `[devframe] RPC call "${method}" timed out after ${callTimeout}ms`));
|
|
962
|
+
}, callTimeout);
|
|
963
|
+
promise.then((value) => {
|
|
964
|
+
if (settled) return;
|
|
965
|
+
finish();
|
|
966
|
+
resolve(value);
|
|
967
|
+
}, (error) => {
|
|
968
|
+
if (settled) return;
|
|
969
|
+
finish();
|
|
970
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
971
|
+
events.emit("rpc:error", err, method);
|
|
972
|
+
reject(err);
|
|
973
|
+
});
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
const definitions = /* @__PURE__ */ new Map();
|
|
977
|
+
for (const name of connectionMeta.jsonSerializableMethods ?? []) definitions.set(name, { jsonSerializable: true });
|
|
978
|
+
const channel = options.createChannel({
|
|
979
|
+
definitions,
|
|
980
|
+
onError(error) {
|
|
981
|
+
setStatus("error", error);
|
|
982
|
+
events.emit("connection:error", error);
|
|
983
|
+
rejectAllPending(new DevframeConnectionError("connection", "[devframe] Connection to the devframe server failed", { cause: error }));
|
|
984
|
+
},
|
|
985
|
+
onDisconnected() {
|
|
986
|
+
if (status !== "error") setStatus("disconnected");
|
|
987
|
+
rejectAllPending(new DevframeConnectionError("connection", "[devframe] Disconnected from the devframe server", { cause: connectionError ?? void 0 }));
|
|
988
|
+
}
|
|
989
|
+
});
|
|
990
|
+
const serverRpc = createRpcClient(clientRpc.functions, {
|
|
991
|
+
channel,
|
|
992
|
+
rpcOptions
|
|
993
|
+
});
|
|
994
|
+
clientRpc.register({
|
|
995
|
+
name: "devframe:auth:revoked",
|
|
996
|
+
type: "event",
|
|
997
|
+
handler: () => {
|
|
998
|
+
isTrusted = false;
|
|
999
|
+
const authError = new DevframeConnectionError("auth", "[devframe] The devframe server revoked this client's trust");
|
|
1000
|
+
setStatus("unauthorized", authError);
|
|
1001
|
+
events.emit("connection:error", authError);
|
|
1002
|
+
rejectAllPending(authError);
|
|
1003
|
+
events.emit("rpc:is-trusted:updated", false);
|
|
1004
|
+
}
|
|
1005
|
+
});
|
|
1006
|
+
let currentAuthToken = authToken;
|
|
1007
|
+
async function requestTrustWithToken(token) {
|
|
1008
|
+
currentAuthToken = token;
|
|
1009
|
+
const result = await serverRpc.$call("anonymous:devframe:auth", {
|
|
1010
|
+
authToken: token,
|
|
1011
|
+
ua: navigator.userAgent,
|
|
1012
|
+
origin: location.origin
|
|
1013
|
+
});
|
|
1014
|
+
isTrusted = result.isTrusted;
|
|
1015
|
+
if (isTrusted) {
|
|
1016
|
+
trustedPromise.resolve(true);
|
|
1017
|
+
setStatus("connected");
|
|
1018
|
+
} else {
|
|
1019
|
+
const authError = new DevframeConnectionError("auth", "[devframe] The devframe server refused this client's credentials");
|
|
1020
|
+
setStatus("unauthorized", authError);
|
|
1021
|
+
events.emit("connection:error", authError);
|
|
1022
|
+
}
|
|
1023
|
+
events.emit("rpc:is-trusted:updated", isTrusted);
|
|
1024
|
+
return result.isTrusted;
|
|
1025
|
+
}
|
|
1026
|
+
async function requestTrustWithCode(code) {
|
|
1027
|
+
const token = (await serverRpc.$call("anonymous:devframe:auth:exchange", {
|
|
1028
|
+
code,
|
|
1029
|
+
ua: navigator.userAgent,
|
|
1030
|
+
origin: location.origin
|
|
1031
|
+
}))?.authToken ?? null;
|
|
1032
|
+
if (token) {
|
|
1033
|
+
currentAuthToken = token;
|
|
1034
|
+
isTrusted = true;
|
|
1035
|
+
trustedPromise.resolve(true);
|
|
1036
|
+
setStatus("connected");
|
|
1037
|
+
events.emit("rpc:is-trusted:updated", true);
|
|
1038
|
+
}
|
|
1039
|
+
return token;
|
|
1040
|
+
}
|
|
1041
|
+
async function requestTrust() {
|
|
1042
|
+
if (isTrusted) return true;
|
|
1043
|
+
return requestTrustWithToken(currentAuthToken ?? "");
|
|
1044
|
+
}
|
|
1045
|
+
async function ensureTrusted(timeout = 6e4) {
|
|
1046
|
+
if (isTrusted) trustedPromise.resolve(true);
|
|
1047
|
+
if (timeout <= 0) return trustedPromise.promise;
|
|
1048
|
+
let clear = () => {};
|
|
1049
|
+
await Promise.race([trustedPromise.promise.then(clear), new Promise((resolve, reject) => {
|
|
1050
|
+
const id = setTimeout(() => {
|
|
1051
|
+
reject(/* @__PURE__ */ new Error("[devframe] Timeout waiting for rpc to be trusted"));
|
|
1052
|
+
}, timeout);
|
|
1053
|
+
clear = () => clearTimeout(id);
|
|
1054
|
+
})]);
|
|
1055
|
+
return isTrusted;
|
|
1056
|
+
}
|
|
1057
|
+
return {
|
|
1058
|
+
transport,
|
|
1059
|
+
get isTrusted() {
|
|
1060
|
+
return isTrusted;
|
|
1061
|
+
},
|
|
1062
|
+
get status() {
|
|
1063
|
+
return status;
|
|
1064
|
+
},
|
|
1065
|
+
get connectionError() {
|
|
1066
|
+
return connectionError;
|
|
1067
|
+
},
|
|
1068
|
+
requestTrust,
|
|
1069
|
+
requestTrustWithToken,
|
|
1070
|
+
requestTrustWithCode,
|
|
1071
|
+
ensureTrusted,
|
|
1072
|
+
call: (...args) => {
|
|
1073
|
+
const method = String(args[0]);
|
|
1074
|
+
const failFast = terminalError();
|
|
1075
|
+
if (failFast) {
|
|
1076
|
+
events.emit("rpc:error", failFast, method);
|
|
1077
|
+
return Promise.reject(failFast);
|
|
1078
|
+
}
|
|
1079
|
+
return guardCall(serverRpc.$call(...args), method);
|
|
1080
|
+
},
|
|
1081
|
+
callEvent: (...args) => {
|
|
1082
|
+
const failFast = terminalError();
|
|
1083
|
+
if (failFast) {
|
|
1084
|
+
events.emit("rpc:error", failFast, String(args[0]));
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
return serverRpc.$callEvent(...args);
|
|
1088
|
+
},
|
|
1089
|
+
callOptional: (...args) => {
|
|
1090
|
+
const method = String(args[0]);
|
|
1091
|
+
const failFast = terminalError();
|
|
1092
|
+
if (failFast) {
|
|
1093
|
+
events.emit("rpc:error", failFast, method);
|
|
1094
|
+
return Promise.reject(failFast);
|
|
1095
|
+
}
|
|
1096
|
+
return guardCall(serverRpc.$callOptional(...args), method);
|
|
1097
|
+
},
|
|
1098
|
+
close: () => {
|
|
1099
|
+
channel.close();
|
|
1100
|
+
}
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
//#endregion
|
|
1104
|
+
//#region src/client/rpc-sse.ts
|
|
1105
|
+
/**
|
|
1106
|
+
* Resolve a {@link ConnectionMeta.sse} descriptor into a concrete
|
|
1107
|
+
* `http(s)://` URL, with the same proxy-safe rules as `resolveWsUrl`: the
|
|
1108
|
+
* object / relative-path forms resolve against where `__connection.json`
|
|
1109
|
+
* was loaded (the client trusts its own location, never a server-baked
|
|
1110
|
+
* hostname), while an explicit `port`/`host` (or a full `http(s)://` URL
|
|
1111
|
+
* string) opts into a cross-origin endpoint.
|
|
1112
|
+
*/
|
|
1113
|
+
function resolveSseUrl(sse, metaBaseUrl, loc) {
|
|
1114
|
+
const base = (() => {
|
|
1115
|
+
try {
|
|
1116
|
+
return new URL(metaBaseUrl, loc.href);
|
|
1117
|
+
} catch {
|
|
1118
|
+
return new URL(loc.href);
|
|
1119
|
+
}
|
|
1120
|
+
})();
|
|
1121
|
+
if (sse && typeof sse === "object") {
|
|
1122
|
+
if (sse.host != null || sse.port != null) {
|
|
1123
|
+
const host = sse.host ?? `${base.hostname}:${sse.port}`;
|
|
1124
|
+
return new URL(sse.path ?? "/", `${base.protocol}//${host}`).href;
|
|
1125
|
+
}
|
|
1126
|
+
return new URL(sse.path ?? "", base).href;
|
|
1127
|
+
}
|
|
1128
|
+
const str = sse ?? "";
|
|
1129
|
+
if (/^https?:\/\//i.test(str)) return str;
|
|
1130
|
+
return new URL(str, base).href;
|
|
1131
|
+
}
|
|
1132
|
+
/**
|
|
1133
|
+
* The SSE-backed live client mode — picked by `connectDevframe` when the
|
|
1134
|
+
* server advertises SSE as its primary transport (`backend: 'sse'`) or when
|
|
1135
|
+
* the caller pins `transport: 'sse'`. Same status machine, call guarding,
|
|
1136
|
+
* and trust handshake as the WebSocket mode, over `createSseRpcChannel`.
|
|
1137
|
+
*/
|
|
1138
|
+
function createSseRpcClientMode(options) {
|
|
1139
|
+
const { authToken, connectionMeta, metaBaseUrl, events, clientRpc, rpcOptions = {}, sseOptions = {}, callTimeout = 0 } = options;
|
|
1140
|
+
const url = resolveSseUrl(connectionMeta.sse, metaBaseUrl ?? "./", location);
|
|
1141
|
+
return createLiveRpcClientMode({
|
|
1142
|
+
transport: "sse",
|
|
1143
|
+
authToken,
|
|
1144
|
+
connectionMeta,
|
|
1145
|
+
events,
|
|
1146
|
+
clientRpc,
|
|
1147
|
+
rpcOptions,
|
|
1148
|
+
callTimeout,
|
|
1149
|
+
createChannel: (handlers) => createSseRpcChannel({
|
|
1150
|
+
url,
|
|
1151
|
+
authToken,
|
|
1152
|
+
definitions: handlers.definitions,
|
|
1153
|
+
...sseOptions,
|
|
1154
|
+
onConnected() {
|
|
1155
|
+
sseOptions.onConnected?.();
|
|
1156
|
+
},
|
|
1157
|
+
onError(error) {
|
|
1158
|
+
handlers.onError(error);
|
|
1159
|
+
sseOptions.onError?.(error);
|
|
1160
|
+
},
|
|
1161
|
+
onDisconnected() {
|
|
1162
|
+
handlers.onDisconnected();
|
|
1163
|
+
sseOptions.onDisconnected?.();
|
|
1164
|
+
}
|
|
1165
|
+
})
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
//#endregion
|
|
1169
|
+
//#region src/rpc/dump/error.ts
|
|
1170
|
+
/**
|
|
1171
|
+
* Inverse of {@link serializeDumpError}: rebuild a thrown `Error` from
|
|
1172
|
+
* the plain object stored in a dump record. Preserves `cause`, restores
|
|
1173
|
+
* the original `name`, and re-attaches any custom own properties.
|
|
1174
|
+
*/
|
|
1175
|
+
function reviveDumpError(stored) {
|
|
1176
|
+
const cause = stored.cause instanceof Error ? stored.cause : isPlainErrorShape(stored.cause) ? reviveDumpError(stored.cause) : stored.cause;
|
|
1177
|
+
const error = cause !== void 0 ? new Error(stored.message, { cause }) : new Error(stored.message);
|
|
1178
|
+
error.name = stored.name;
|
|
1179
|
+
for (const key of Object.keys(stored)) {
|
|
1180
|
+
if (key === "name" || key === "message" || key === "cause") continue;
|
|
1181
|
+
error[key] = stored[key];
|
|
1182
|
+
}
|
|
1183
|
+
return error;
|
|
1184
|
+
}
|
|
1185
|
+
function isPlainErrorShape(value) {
|
|
1186
|
+
return typeof value === "object" && value !== null && typeof value.message === "string" && typeof value.name === "string";
|
|
1187
|
+
}
|
|
1188
|
+
//#endregion
|
|
1189
|
+
//#region src/client/static-rpc.ts
|
|
1190
|
+
function isStaticEntry(value) {
|
|
1191
|
+
return typeof value === "object" && value !== null && value.type === "static" && typeof value.path === "string";
|
|
1192
|
+
}
|
|
1193
|
+
function isQueryEntry(value) {
|
|
1194
|
+
return typeof value === "object" && value !== null && value.type === "query" && typeof value.records === "object" && value.records !== null;
|
|
1195
|
+
}
|
|
1196
|
+
function isRecord(value) {
|
|
1197
|
+
return typeof value === "object" && value !== null && ("output" in value || "error" in value);
|
|
1198
|
+
}
|
|
1199
|
+
function resolveRecordOutput(record) {
|
|
1200
|
+
if (record.error) throw reviveDumpError(record.error);
|
|
1201
|
+
return record.output;
|
|
1202
|
+
}
|
|
1203
|
+
function hasMeaningfulArgs(args) {
|
|
1204
|
+
return args.some((arg) => arg !== null && arg !== void 0);
|
|
1205
|
+
}
|
|
1206
|
+
function unwrapEnvelope(raw) {
|
|
1207
|
+
if (raw !== null && typeof raw === "object" && "serialization" in raw && "data" in raw) return raw.data;
|
|
1208
|
+
return raw;
|
|
1209
|
+
}
|
|
1210
|
+
function createStaticRpcCaller(manifest, fetchJson) {
|
|
1211
|
+
const staticCache = /* @__PURE__ */ new Map();
|
|
1212
|
+
const queryRecordCache = /* @__PURE__ */ new Map();
|
|
1213
|
+
function reviveIfStructuredClone(value, serialization) {
|
|
1214
|
+
if (serialization === "structured-clone" && Array.isArray(value)) return structuredCloneDeserialize(value);
|
|
1215
|
+
return value;
|
|
1216
|
+
}
|
|
1217
|
+
function decode(raw, serialization) {
|
|
1218
|
+
return reviveIfStructuredClone(unwrapEnvelope(raw), serialization);
|
|
1219
|
+
}
|
|
1220
|
+
async function loadStatic(entry) {
|
|
1221
|
+
if (!staticCache.has(entry.path)) staticCache.set(entry.path, fetchJson(entry.path).then((raw) => decode(raw, entry.serialization)));
|
|
1222
|
+
const data = await staticCache.get(entry.path);
|
|
1223
|
+
if (isRecord(data)) return resolveRecordOutput(data);
|
|
1224
|
+
return data;
|
|
1225
|
+
}
|
|
1226
|
+
async function loadQueryRecord(path, serialization) {
|
|
1227
|
+
if (!queryRecordCache.has(path)) queryRecordCache.set(path, fetchJson(path).then((raw) => decode(raw, serialization)));
|
|
1228
|
+
return await queryRecordCache.get(path);
|
|
1229
|
+
}
|
|
1230
|
+
async function call(functionName, args) {
|
|
1231
|
+
if (!(functionName in manifest)) throw new Error(`[devframe-rpc] Function "${functionName}" not found in dump store`);
|
|
1232
|
+
const entry = manifest[functionName];
|
|
1233
|
+
if (isStaticEntry(entry)) {
|
|
1234
|
+
if (hasMeaningfulArgs(args)) throw new Error(`[devframe-rpc] No dump match for "${functionName}" with args: ${JSON.stringify(args)}`);
|
|
1235
|
+
return await loadStatic(entry);
|
|
1236
|
+
}
|
|
1237
|
+
if (isQueryEntry(entry)) {
|
|
1238
|
+
const argsHash = hash(args);
|
|
1239
|
+
const recordPath = entry.records[argsHash];
|
|
1240
|
+
if (recordPath) return resolveRecordOutput(await loadQueryRecord(recordPath, entry.serialization));
|
|
1241
|
+
if (entry.fallback) return resolveRecordOutput(await loadQueryRecord(entry.fallback, entry.serialization));
|
|
1242
|
+
throw new Error(`[devframe-rpc] No dump match for "${functionName}" with args: ${JSON.stringify(args)}`);
|
|
1243
|
+
}
|
|
1244
|
+
if (!hasMeaningfulArgs(args)) return entry;
|
|
1245
|
+
throw new Error(`[devframe-rpc] No dump match for "${functionName}" with args: ${JSON.stringify(args)}`);
|
|
1246
|
+
}
|
|
1247
|
+
return {
|
|
1248
|
+
call: async (functionName, args) => await call(functionName, args),
|
|
1249
|
+
callOptional: async (functionName, args) => {
|
|
1250
|
+
if (!(functionName in manifest)) return void 0;
|
|
1251
|
+
return await call(functionName, args);
|
|
1252
|
+
},
|
|
1253
|
+
callEvent: async (_functionName, _args) => {}
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1256
|
+
//#endregion
|
|
1257
|
+
//#region src/client/rpc-static.ts
|
|
1258
|
+
async function createStaticRpcClientMode(options) {
|
|
1259
|
+
const staticCaller = createStaticRpcCaller(await options.fetchJsonFromBases(DEVFRAME_RPC_DUMP_MANIFEST_FILENAME), options.fetchJsonFromBases);
|
|
1260
|
+
return {
|
|
1261
|
+
transport: "static",
|
|
1262
|
+
isTrusted: true,
|
|
1263
|
+
status: "connected",
|
|
1264
|
+
connectionError: null,
|
|
1265
|
+
requestTrust: async () => true,
|
|
1266
|
+
requestTrustWithToken: async () => true,
|
|
1267
|
+
requestTrustWithCode: async () => null,
|
|
1268
|
+
ensureTrusted: async () => true,
|
|
1269
|
+
call: (...args) => staticCaller.call(args[0], args.slice(1)),
|
|
1270
|
+
callEvent: (...args) => staticCaller.callEvent(args[0], args.slice(1)),
|
|
1271
|
+
callOptional: (...args) => staticCaller.callOptional(args[0], args.slice(1)),
|
|
1272
|
+
close: () => {}
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1275
|
+
//#endregion
|
|
1276
|
+
//#region src/client/rpc-streaming.ts
|
|
1277
|
+
const STREAM_KEY_SEPARATOR = "";
|
|
1278
|
+
function streamKey(channel, id) {
|
|
1279
|
+
return `${channel}${STREAM_KEY_SEPARATOR}${id}`;
|
|
1280
|
+
}
|
|
1281
|
+
/**
|
|
1282
|
+
* Client-side streaming host. Mirrors `createRpcSharedStateClientHost`:
|
|
1283
|
+
* registers the two `:chunk` / `:end` event handlers once, then per-stream
|
|
1284
|
+
* state lives in a `Map<streamKey, StreamReader>`.
|
|
1285
|
+
*/
|
|
1286
|
+
function createRpcStreamingClientHost(rpc) {
|
|
1287
|
+
const readers = /* @__PURE__ */ new Map();
|
|
1288
|
+
const uploads = /* @__PURE__ */ new Map();
|
|
1289
|
+
rpc.client.register({
|
|
1290
|
+
name: "devframe:streaming:chunk",
|
|
1291
|
+
type: "event",
|
|
1292
|
+
handler(channel, id, seq, chunk) {
|
|
1293
|
+
readers.get(streamKey(channel, id))?._push(seq, chunk);
|
|
1294
|
+
}
|
|
1295
|
+
});
|
|
1296
|
+
rpc.client.register({
|
|
815
1297
|
name: "devframe:streaming:end",
|
|
816
1298
|
type: "event",
|
|
817
1299
|
handler(channel, id, error) {
|
|
@@ -881,26 +1363,15 @@ function createRpcStreamingClientHost(rpc) {
|
|
|
881
1363
|
});
|
|
882
1364
|
sink.events.on("end", (error) => {
|
|
883
1365
|
rpc.callEvent("devframe:streaming:upload-end", channel, id, error);
|
|
884
|
-
uploads.delete(key);
|
|
885
|
-
});
|
|
886
|
-
uploads.set(key, sink);
|
|
887
|
-
return sink;
|
|
888
|
-
}
|
|
889
|
-
return {
|
|
890
|
-
subscribe,
|
|
891
|
-
upload
|
|
892
|
-
};
|
|
893
|
-
}
|
|
894
|
-
//#endregion
|
|
895
|
-
//#region src/rpc/client.ts
|
|
896
|
-
function createRpcClient(functions, options) {
|
|
897
|
-
const { channel, rpcOptions = {} } = options;
|
|
898
|
-
return createBirpc(functions, {
|
|
899
|
-
...channel,
|
|
900
|
-
timeout: -1,
|
|
901
|
-
...rpcOptions,
|
|
902
|
-
proxify: false
|
|
903
|
-
});
|
|
1366
|
+
uploads.delete(key);
|
|
1367
|
+
});
|
|
1368
|
+
uploads.set(key, sink);
|
|
1369
|
+
return sink;
|
|
1370
|
+
}
|
|
1371
|
+
return {
|
|
1372
|
+
subscribe,
|
|
1373
|
+
upload
|
|
1374
|
+
};
|
|
904
1375
|
}
|
|
905
1376
|
//#endregion
|
|
906
1377
|
//#region src/rpc/transports/ws-client.ts
|
|
@@ -909,6 +1380,9 @@ const EMPTY_DEFS = /* @__PURE__ */ new Map();
|
|
|
909
1380
|
/**
|
|
910
1381
|
* Build a birpc `ChannelOptions` object backed by a browser `WebSocket`.
|
|
911
1382
|
* Pass the result straight to `createRpcClient`'s `channel` option.
|
|
1383
|
+
*
|
|
1384
|
+
* Also returns `close()`, closing the underlying socket — mirroring the server transport's
|
|
1385
|
+
* existing `WsRpcTransport.close()`. `birpc`'s own `ChannelOptions` has no teardown of its own.
|
|
912
1386
|
*/
|
|
913
1387
|
function createWsRpcChannel(options) {
|
|
914
1388
|
let url = options.url;
|
|
@@ -925,8 +1399,11 @@ function createWsRpcChannel(options) {
|
|
|
925
1399
|
ws.addEventListener("close", (e) => {
|
|
926
1400
|
onDisconnected(e);
|
|
927
1401
|
});
|
|
928
|
-
const
|
|
1402
|
+
const codec = createRpcWireCodec(definitions);
|
|
929
1403
|
return {
|
|
1404
|
+
close: () => {
|
|
1405
|
+
ws.close();
|
|
1406
|
+
},
|
|
930
1407
|
on: (handler) => {
|
|
931
1408
|
ws.addEventListener("message", (e) => {
|
|
932
1409
|
handler(e.data);
|
|
@@ -953,1183 +1430,8 @@ function createWsRpcChannel(options) {
|
|
|
953
1430
|
}
|
|
954
1431
|
onError(/* @__PURE__ */ new Error("Devframe WebSocket is not open; message dropped"));
|
|
955
1432
|
},
|
|
956
|
-
serialize:
|
|
957
|
-
|
|
958
|
-
if (msg.t === "q") method = msg.m;
|
|
959
|
-
else {
|
|
960
|
-
method = pendingRequestMethods.get(msg.i);
|
|
961
|
-
pendingRequestMethods.delete(msg.i);
|
|
962
|
-
}
|
|
963
|
-
if (!(msg.t === "s" && "e" in msg) && !!method && definitions.get(method)?.jsonSerializable === true) return strictJsonStringify(msg, method ?? "");
|
|
964
|
-
return `s:${structuredCloneStringify(msg)}`;
|
|
965
|
-
},
|
|
966
|
-
deserialize: (raw) => {
|
|
967
|
-
const msg = raw.startsWith("s:") ? structuredCloneParse(raw.slice(2)) : JSON.parse(raw);
|
|
968
|
-
if (msg.t === "q" && msg.i && msg.m) pendingRequestMethods.set(msg.i, msg.m);
|
|
969
|
-
return msg;
|
|
970
|
-
}
|
|
971
|
-
};
|
|
972
|
-
}
|
|
973
|
-
//#endregion
|
|
974
|
-
//#region ../../node_modules/.pnpm/ua-parser-modern@0.1.1/node_modules/ua-parser-modern/dist/index.mjs
|
|
975
|
-
const EMPTY = "";
|
|
976
|
-
const UNKNOWN = "?";
|
|
977
|
-
const MAJOR = "major";
|
|
978
|
-
const MODEL = "model";
|
|
979
|
-
const NAME = "name";
|
|
980
|
-
const TYPE = "type";
|
|
981
|
-
const VENDOR = "vendor";
|
|
982
|
-
const VERSION = "version";
|
|
983
|
-
const ARCHITECTURE = "architecture";
|
|
984
|
-
const CONSOLE = "console";
|
|
985
|
-
const MOBILE = "mobile";
|
|
986
|
-
const TABLET = "tablet";
|
|
987
|
-
const SMARTTV = "smarttv";
|
|
988
|
-
const WEARABLE = "wearable";
|
|
989
|
-
const EMBEDDED = "embedded";
|
|
990
|
-
const UA_MAX_LENGTH = 500;
|
|
991
|
-
const AMAZON = "Amazon";
|
|
992
|
-
const APPLE = "Apple";
|
|
993
|
-
const ASUS = "ASUS";
|
|
994
|
-
const BLACKBERRY = "BlackBerry";
|
|
995
|
-
const BROWSER_LABEL = "Browser";
|
|
996
|
-
const CHROME = "Chrome";
|
|
997
|
-
const EDGE = "Edge";
|
|
998
|
-
const FIREFOX = "Firefox";
|
|
999
|
-
const GOOGLE = "Google";
|
|
1000
|
-
const HUAWEI = "Huawei";
|
|
1001
|
-
const LG = "LG";
|
|
1002
|
-
const MICROSOFT = "Microsoft";
|
|
1003
|
-
const MOTOROLA = "Motorola";
|
|
1004
|
-
const OPERA = "Opera";
|
|
1005
|
-
const SAMSUNG = "Samsung";
|
|
1006
|
-
const SHARP = "Sharp";
|
|
1007
|
-
const SONY = "Sony";
|
|
1008
|
-
const XIAOMI = "Xiaomi";
|
|
1009
|
-
const ZEBRA = "Zebra";
|
|
1010
|
-
const FACEBOOK = "Facebook";
|
|
1011
|
-
const CHROMIUM_OS = "Chromium OS";
|
|
1012
|
-
const MAC_OS = "Mac OS";
|
|
1013
|
-
function extend(regexes, extensions = {}) {
|
|
1014
|
-
const mergedRegexes = {};
|
|
1015
|
-
for (const key of Object.keys(regexes)) {
|
|
1016
|
-
const extension = extensions[key];
|
|
1017
|
-
if (extension && extension.length % 2 === 0) mergedRegexes[key] = [...extension, ...regexes[key]];
|
|
1018
|
-
else mergedRegexes[key] = regexes[key];
|
|
1019
|
-
}
|
|
1020
|
-
return mergedRegexes;
|
|
1021
|
-
}
|
|
1022
|
-
function enumerize(arr) {
|
|
1023
|
-
const enums = {};
|
|
1024
|
-
for (let i = 0; i < arr.length; i++) enums[arr[i].toUpperCase()] = arr[i];
|
|
1025
|
-
return enums;
|
|
1026
|
-
}
|
|
1027
|
-
function lowerize(str) {
|
|
1028
|
-
return str.toLowerCase();
|
|
1029
|
-
}
|
|
1030
|
-
function has(str1, str2) {
|
|
1031
|
-
return typeof str1 === "string" ? lowerize(str2).includes(lowerize(str1)) : false;
|
|
1032
|
-
}
|
|
1033
|
-
function majorize(version) {
|
|
1034
|
-
return typeof version === "string" ? version.replace(/[^\d.]/g, EMPTY).split(".")[0] : void 0;
|
|
1035
|
-
}
|
|
1036
|
-
function trim(str, len) {
|
|
1037
|
-
const result = str.replace(/^\s+/, EMPTY);
|
|
1038
|
-
return typeof len === "undefined" ? result : result.substring(0, len);
|
|
1039
|
-
}
|
|
1040
|
-
function rgxMapper(ua, arrays, augment) {
|
|
1041
|
-
const result = augment ?? {};
|
|
1042
|
-
for (let i = 0; i < arrays.length; i += 2) {
|
|
1043
|
-
const regex = arrays[i];
|
|
1044
|
-
const props = arrays[i + 1];
|
|
1045
|
-
for (let j = 0; j < regex.length; j++) {
|
|
1046
|
-
const matcher = regex[j];
|
|
1047
|
-
if (!matcher) break;
|
|
1048
|
-
const matches = matcher.exec(ua);
|
|
1049
|
-
if (!matches) continue;
|
|
1050
|
-
for (let p = 0; p < props.length; p++) {
|
|
1051
|
-
const match = matches[p + 1];
|
|
1052
|
-
const q = props[p];
|
|
1053
|
-
if (Array.isArray(q)) {
|
|
1054
|
-
const key = q[0];
|
|
1055
|
-
const length = q.length;
|
|
1056
|
-
if (length === 2) {
|
|
1057
|
-
const value = q[1];
|
|
1058
|
-
if (typeof value === "function") result[key] = value(match, void 0, result);
|
|
1059
|
-
else result[key] = value;
|
|
1060
|
-
} else if (length === 3) {
|
|
1061
|
-
const arg1 = q[1];
|
|
1062
|
-
const arg2 = q[2];
|
|
1063
|
-
if (typeof arg1 === "function" && !("exec" in arg1) && !("test" in arg1)) result[key] = match ? arg1(match, arg2, result) : void 0;
|
|
1064
|
-
else result[key] = match ? match.replace(arg1, arg2) : void 0;
|
|
1065
|
-
} else if (length === 4) result[key] = match ? q[3](match.replace(q[1], q[2]), void 0, result) : void 0;
|
|
1066
|
-
} else result[q] = match || void 0;
|
|
1067
|
-
}
|
|
1068
|
-
return result;
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
return result;
|
|
1072
|
-
}
|
|
1073
|
-
function strMapper(str, map) {
|
|
1074
|
-
for (const i in map) {
|
|
1075
|
-
const value = map[i];
|
|
1076
|
-
if (Array.isArray(value) && value.length > 0) {
|
|
1077
|
-
for (let j = 0; j < value.length; j++) if (has(value[j], str)) return i === UNKNOWN ? void 0 : i;
|
|
1078
|
-
} else if (has(value, str)) return i === UNKNOWN ? void 0 : i;
|
|
1079
|
-
}
|
|
1080
|
-
return str;
|
|
1081
|
-
}
|
|
1082
|
-
const oldSafariMap = {
|
|
1083
|
-
"1.0": "/8",
|
|
1084
|
-
"1.2": "/1",
|
|
1085
|
-
"1.3": "/3",
|
|
1086
|
-
"2.0": "/412",
|
|
1087
|
-
"2.0.2": "/416",
|
|
1088
|
-
"2.0.3": "/417",
|
|
1089
|
-
"2.0.4": "/419",
|
|
1090
|
-
"?": "/"
|
|
1091
|
-
};
|
|
1092
|
-
const windowsVersionMap = {
|
|
1093
|
-
"ME": "4.90",
|
|
1094
|
-
"NT 3.11": "NT3.51",
|
|
1095
|
-
"NT 4.0": "NT4.0",
|
|
1096
|
-
"2000": "NT 5.0",
|
|
1097
|
-
"XP": ["NT 5.1", "NT 5.2"],
|
|
1098
|
-
"Vista": "NT 6.0",
|
|
1099
|
-
"7": "NT 6.1",
|
|
1100
|
-
"8": "NT 6.2",
|
|
1101
|
-
"8.1": "NT 6.3",
|
|
1102
|
-
"10": ["NT 6.4", "NT 10.0"],
|
|
1103
|
-
"RT": "ARM"
|
|
1104
|
-
};
|
|
1105
|
-
const regexes = {
|
|
1106
|
-
browser: [
|
|
1107
|
-
[/\b(?:crmo|crios)\/([\w.]+)/i],
|
|
1108
|
-
[VERSION, [NAME, "Chrome"]],
|
|
1109
|
-
[/edg(?:e|ios|a)?\/([\w.]+)/i],
|
|
1110
|
-
[VERSION, [NAME, "Edge"]],
|
|
1111
|
-
[
|
|
1112
|
-
/(opera mini)\/([-\w.]+)/i,
|
|
1113
|
-
/(opera [mobileta]{3,6})\b.+version\/([-\w.]+)/i,
|
|
1114
|
-
/(opera)(?:.+version\/|[/ ]+)([\w.]+)/i
|
|
1115
|
-
],
|
|
1116
|
-
[NAME, VERSION],
|
|
1117
|
-
[/opios[/ ]+([\w.]+)/i],
|
|
1118
|
-
[VERSION, [NAME, `${OPERA} Mini`]],
|
|
1119
|
-
[/\bop(?:rg)?x\/([\w.]+)/i],
|
|
1120
|
-
[VERSION, [NAME, `${OPERA} GX`]],
|
|
1121
|
-
[/\bopr\/([\w.]+)/i],
|
|
1122
|
-
[VERSION, [NAME, OPERA]],
|
|
1123
|
-
[/\bb[ai]*d(?:uhd|[ub]*[aekoprswx]{5,6})[/ ]?([\w.]+)/i],
|
|
1124
|
-
[VERSION, [NAME, "Baidu"]],
|
|
1125
|
-
[
|
|
1126
|
-
/(kindle)\/([\w.]+)/i,
|
|
1127
|
-
/(lunascape|maxthon|netfront|jasmine|blazer)[/ ]?([\w.]*)/i,
|
|
1128
|
-
/(avant|iemobile|slim)\s?(?:browser)?[/ ]?([\w.]*)/i,
|
|
1129
|
-
/(?:ms|\()(ie) ([\w.]+)/i,
|
|
1130
|
-
/(flock|rockmelt|midori|epiphany|silk|skyfire|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|qq|duckduckgo)\/([-\w.]+)/i,
|
|
1131
|
-
/(heytap|ovi)browser\/([\d.]+)/i,
|
|
1132
|
-
/(weibo)__([\d.]+)/i
|
|
1133
|
-
],
|
|
1134
|
-
[NAME, VERSION],
|
|
1135
|
-
[/\bddg\/([\w.]+)/i],
|
|
1136
|
-
[VERSION, [NAME, "DuckDuckGo"]],
|
|
1137
|
-
[/(?:\buc? ?browser|juc.+ucweb)[/ ]?([\w.]+)/i],
|
|
1138
|
-
[VERSION, [NAME, `UC${BROWSER_LABEL}`]],
|
|
1139
|
-
[
|
|
1140
|
-
/microm.+\bqbcore\/([\w.]+)/i,
|
|
1141
|
-
/\bqbcore\/([\w.]+).+microm/i,
|
|
1142
|
-
/micromessenger\/([\w.]+)/i
|
|
1143
|
-
],
|
|
1144
|
-
[VERSION, [NAME, "WeChat"]],
|
|
1145
|
-
[/konqueror\/([\w.]+)/i],
|
|
1146
|
-
[VERSION, [NAME, "Konqueror"]],
|
|
1147
|
-
[/trident.+rv[: ]([\w.]{1,9})\b.+like gecko/i],
|
|
1148
|
-
[VERSION, [NAME, "IE"]],
|
|
1149
|
-
[/ya(?:search)?browser\/([\w.]+)/i],
|
|
1150
|
-
[VERSION, [NAME, "Yandex"]],
|
|
1151
|
-
[/slbrowser\/([\w.]+)/i],
|
|
1152
|
-
[VERSION, [NAME, `Smart Lenovo ${BROWSER_LABEL}`]],
|
|
1153
|
-
[/(avast|avg)\/([\w.]+)/i],
|
|
1154
|
-
[[
|
|
1155
|
-
NAME,
|
|
1156
|
-
/(.+)/,
|
|
1157
|
-
`$1 Secure ${BROWSER_LABEL}`
|
|
1158
|
-
], VERSION],
|
|
1159
|
-
[/\bfocus\/([\w.]+)/i],
|
|
1160
|
-
[VERSION, [NAME, `${FIREFOX} Focus`]],
|
|
1161
|
-
[/\bopt\/([\w.]+)/i],
|
|
1162
|
-
[VERSION, [NAME, `${OPERA} Touch`]],
|
|
1163
|
-
[/coc_coc\w+\/([\w.]+)/i],
|
|
1164
|
-
[VERSION, [NAME, "Coc Coc"]],
|
|
1165
|
-
[/dolfin\/([\w.]+)/i],
|
|
1166
|
-
[VERSION, [NAME, "Dolphin"]],
|
|
1167
|
-
[/coast\/([\w.]+)/i],
|
|
1168
|
-
[VERSION, [NAME, `${OPERA} Coast`]],
|
|
1169
|
-
[/miuibrowser\/([\w.]+)/i],
|
|
1170
|
-
[VERSION, [NAME, `MIUI ${BROWSER_LABEL}`]],
|
|
1171
|
-
[/fxios\/([-\w.]+)/i],
|
|
1172
|
-
[VERSION, [NAME, FIREFOX]],
|
|
1173
|
-
[/\bqihu|(qi?ho{0,2}|360)browser/i],
|
|
1174
|
-
[[NAME, `360 ${BROWSER_LABEL}`]],
|
|
1175
|
-
[/(oculus|sailfish|huawei|vivo)browser\/([\w.]+)/i],
|
|
1176
|
-
[[
|
|
1177
|
-
NAME,
|
|
1178
|
-
/(.+)/,
|
|
1179
|
-
`$1 ${BROWSER_LABEL}`
|
|
1180
|
-
], VERSION],
|
|
1181
|
-
[/samsungbrowser\/([\w.]+)/i],
|
|
1182
|
-
[VERSION, [NAME, `${SAMSUNG} Internet`]],
|
|
1183
|
-
[/(comodo_dragon)\/([\w.]+)/i],
|
|
1184
|
-
[[
|
|
1185
|
-
NAME,
|
|
1186
|
-
/_/g,
|
|
1187
|
-
" "
|
|
1188
|
-
], VERSION],
|
|
1189
|
-
[/metasr[/ ]?([\d.]+)/i],
|
|
1190
|
-
[VERSION, [NAME, "Sogou Explorer"]],
|
|
1191
|
-
[/(sogou)mo\w+\/([\d.]+)/i],
|
|
1192
|
-
[[NAME, "Sogou Mobile"], VERSION],
|
|
1193
|
-
[
|
|
1194
|
-
/(electron)\/([\w.]+) safari/i,
|
|
1195
|
-
/(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w.]+))/i,
|
|
1196
|
-
/m?(qqbrowser|2345Explorer)[/ ]?([\w.]+)/i
|
|
1197
|
-
],
|
|
1198
|
-
[NAME, VERSION],
|
|
1199
|
-
[/(lbbrowser)/i, /\[(linkedin)app\]/i],
|
|
1200
|
-
[NAME],
|
|
1201
|
-
[/((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w.]+);)/i],
|
|
1202
|
-
[[NAME, FACEBOOK], VERSION],
|
|
1203
|
-
[
|
|
1204
|
-
/(Klarna)\/([\w.]+)/i,
|
|
1205
|
-
/(kakao(?:talk|story))[/ ]([\w.]+)/i,
|
|
1206
|
-
/(naver)\(.*?(\d+\.[\w.]+).*\)/i,
|
|
1207
|
-
/safari (line)\/([\w.]+)/i,
|
|
1208
|
-
/\b(line)\/([\w.]+)\/iab/i,
|
|
1209
|
-
/(alipay)client\/([\w.]+)/i,
|
|
1210
|
-
/(twitter)(?:and| f.+e\/([\w.]+))/i,
|
|
1211
|
-
/(chromium|instagram|snapchat)[/ ]([-\w.]+)/i
|
|
1212
|
-
],
|
|
1213
|
-
[NAME, VERSION],
|
|
1214
|
-
[/\bgsa\/([\w.]+) .*safari\//i],
|
|
1215
|
-
[VERSION, [NAME, "GSA"]],
|
|
1216
|
-
[/musical_ly(?:.+app_?version\/|_)([\w.]+)/i],
|
|
1217
|
-
[VERSION, [NAME, "TikTok"]],
|
|
1218
|
-
[/headlesschrome(?:\/([\w.]+)| )/i],
|
|
1219
|
-
[VERSION, [NAME, `${CHROME} Headless`]],
|
|
1220
|
-
[/ wv\).+(chrome)\/([\w.]+)/i],
|
|
1221
|
-
[[NAME, `${CHROME} WebView`], VERSION],
|
|
1222
|
-
[/droid.+ version\/([\w.]+)\b.+(?:mobile safari|safari)/i],
|
|
1223
|
-
[VERSION, [NAME, `Android ${BROWSER_LABEL}`]],
|
|
1224
|
-
[/(chrome|omniweb|arora|[tizenoka]{5} ?browser)\/v?([\w.]+)/i],
|
|
1225
|
-
[NAME, VERSION],
|
|
1226
|
-
[/version\/([\w.,]+) .*mobile\/\w+ (safari)/i],
|
|
1227
|
-
[VERSION, [NAME, "Mobile Safari"]],
|
|
1228
|
-
[/version\/([\w(.|,)]+) .*(mobile ?safari|safari)/i],
|
|
1229
|
-
[VERSION, NAME],
|
|
1230
|
-
[/webkit.+?(mobile ?safari|safari)(\/[\w.]+)/i],
|
|
1231
|
-
[NAME, [
|
|
1232
|
-
VERSION,
|
|
1233
|
-
strMapper,
|
|
1234
|
-
oldSafariMap
|
|
1235
|
-
]],
|
|
1236
|
-
[/(webkit|khtml)\/([\w.]+)/i],
|
|
1237
|
-
[NAME, VERSION],
|
|
1238
|
-
[/(navigator|netscape\d?)\/([-\w.]+)/i],
|
|
1239
|
-
[[NAME, "Netscape"], VERSION],
|
|
1240
|
-
[/mobile vr; rv:([\w.]+)\).+firefox/i],
|
|
1241
|
-
[VERSION, [NAME, `${FIREFOX} Reality`]],
|
|
1242
|
-
[
|
|
1243
|
-
/ekiohf.+(flow)\/([\w.]+)/i,
|
|
1244
|
-
/(swiftfox)/i,
|
|
1245
|
-
/(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror|klar)[/ ]?([\w.+]+)/i,
|
|
1246
|
-
/(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|palemoon|basilisk|waterfox)\/([-\w.]+)$/i,
|
|
1247
|
-
/(firefox)\/([\w.]+)/i,
|
|
1248
|
-
/(mozilla)\/([\w.]+) .+rv:.+gecko\/\d+/i,
|
|
1249
|
-
/(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|sleipnir|obigo|mosaic|(?:go|ice|up)[. ]?browser)[-/ ]?v?([\w.]+)/i,
|
|
1250
|
-
/(links) \(([\w.]+)/i,
|
|
1251
|
-
/panasonic;(viera)/i
|
|
1252
|
-
],
|
|
1253
|
-
[NAME, VERSION],
|
|
1254
|
-
[/(cobalt)\/([\w.]+)/i],
|
|
1255
|
-
[NAME, [
|
|
1256
|
-
VERSION,
|
|
1257
|
-
/master.|lts./,
|
|
1258
|
-
""
|
|
1259
|
-
]]
|
|
1260
|
-
],
|
|
1261
|
-
cpu: [
|
|
1262
|
-
[/(amd|x(?:(?:86|64)[-_])?|wow|win)64[;)]/i],
|
|
1263
|
-
[[ARCHITECTURE, "amd64"]],
|
|
1264
|
-
[/(ia32(?=;))/i],
|
|
1265
|
-
[[ARCHITECTURE, lowerize]],
|
|
1266
|
-
[/((?:i[346]|x)86)[;)]/i],
|
|
1267
|
-
[[ARCHITECTURE, "ia32"]],
|
|
1268
|
-
[/\b(aarch64|arm(v?8e?l?|_?64))\b/i],
|
|
1269
|
-
[[ARCHITECTURE, "arm64"]],
|
|
1270
|
-
[/\b(arm(?:v[67])?ht?n?[fl]p?)\b/i],
|
|
1271
|
-
[[ARCHITECTURE, "armhf"]],
|
|
1272
|
-
[/windows (ce|mobile); ppc;/i],
|
|
1273
|
-
[[ARCHITECTURE, "arm"]],
|
|
1274
|
-
[/((?:ppc|powerpc)(?:64)?)(?: mac|;|\))/i],
|
|
1275
|
-
[[
|
|
1276
|
-
ARCHITECTURE,
|
|
1277
|
-
/ower/,
|
|
1278
|
-
EMPTY,
|
|
1279
|
-
lowerize
|
|
1280
|
-
]],
|
|
1281
|
-
[/(sun4\w)[;)]/i],
|
|
1282
|
-
[[ARCHITECTURE, "sparc"]],
|
|
1283
|
-
[/(avr32|ia64(?=;)|68k(?=\))|\barm(?=v(?:[1-7]|[5-7]1)l?|;|eabi)|(?=atmel )avr|(?:irix|mips|sparc)(?:64)?\b|pa-risc)/i],
|
|
1284
|
-
[[ARCHITECTURE, lowerize]]
|
|
1285
|
-
],
|
|
1286
|
-
device: [
|
|
1287
|
-
[/\b(sch-i[89]0\d|shw-m380s|sm-[ptx]\w{2,4}|gt-[pn]\d{2,4}|sgh-t8[56]9|nexus 10)/i],
|
|
1288
|
-
[
|
|
1289
|
-
MODEL,
|
|
1290
|
-
[VENDOR, SAMSUNG],
|
|
1291
|
-
[TYPE, TABLET]
|
|
1292
|
-
],
|
|
1293
|
-
[
|
|
1294
|
-
/\b((?:s[cgp]h|gt|sm)-\w+|sc[g-]?\d+a?|galaxy nexus)/i,
|
|
1295
|
-
/samsung[- ]([-\w]+)/i,
|
|
1296
|
-
/sec-(sgh\w+)/i
|
|
1297
|
-
],
|
|
1298
|
-
[
|
|
1299
|
-
MODEL,
|
|
1300
|
-
[VENDOR, SAMSUNG],
|
|
1301
|
-
[TYPE, MOBILE]
|
|
1302
|
-
],
|
|
1303
|
-
[/(?:\/|\()(ip(?:hone|od)[\w, ]*)(?:\/|;)/i],
|
|
1304
|
-
[
|
|
1305
|
-
MODEL,
|
|
1306
|
-
[VENDOR, APPLE],
|
|
1307
|
-
[TYPE, MOBILE]
|
|
1308
|
-
],
|
|
1309
|
-
[
|
|
1310
|
-
/\((ipad);[-\w),; ]+apple/i,
|
|
1311
|
-
/applecoremedia\/[\w.]+ \((ipad)/i,
|
|
1312
|
-
/\b(ipad)\d\d?,\d\d?[;\]].+ios/i
|
|
1313
|
-
],
|
|
1314
|
-
[
|
|
1315
|
-
MODEL,
|
|
1316
|
-
[VENDOR, APPLE],
|
|
1317
|
-
[TYPE, TABLET]
|
|
1318
|
-
],
|
|
1319
|
-
[/(macintosh);/i],
|
|
1320
|
-
[MODEL, [VENDOR, APPLE]],
|
|
1321
|
-
[/\b(sh-?[altvz]?\d\d[a-ekm]?)/i],
|
|
1322
|
-
[
|
|
1323
|
-
MODEL,
|
|
1324
|
-
[VENDOR, SHARP],
|
|
1325
|
-
[TYPE, MOBILE]
|
|
1326
|
-
],
|
|
1327
|
-
[/\b((?:ag[rs][23]?|bah2?|sht?|btv)-a?[lw]\d{2})\b(?!.+d\/s)/i],
|
|
1328
|
-
[
|
|
1329
|
-
MODEL,
|
|
1330
|
-
[VENDOR, HUAWEI],
|
|
1331
|
-
[TYPE, TABLET]
|
|
1332
|
-
],
|
|
1333
|
-
[/(?:huawei|honor)([-\w ]+)[;)]/i, /\b(nexus 6p|\w{2,4}e?-[atu]?[ln][\dx][0-359c][adn]?)\b(?!.+d\/s)/i],
|
|
1334
|
-
[
|
|
1335
|
-
MODEL,
|
|
1336
|
-
[VENDOR, HUAWEI],
|
|
1337
|
-
[TYPE, MOBILE]
|
|
1338
|
-
],
|
|
1339
|
-
[
|
|
1340
|
-
/\b(poco[\w ]+|m2\d{3}j\d\d[a-z]{2})(?: bui|\))/i,
|
|
1341
|
-
/\b; (\w+) build\/hm\1/i,
|
|
1342
|
-
/\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i,
|
|
1343
|
-
/\b(redmi[\-_ ]?[\w ]+)(?: bui|\))/i,
|
|
1344
|
-
/oid[^)]+; (m?[12][0-389][01]\w{3,6}[c-y])( bui|; wv|\))/i,
|
|
1345
|
-
/\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?\d?\w?[_ ]?(?:plus|se|lite)?)(?: bui|\))/i
|
|
1346
|
-
],
|
|
1347
|
-
[
|
|
1348
|
-
[
|
|
1349
|
-
MODEL,
|
|
1350
|
-
/_/g,
|
|
1351
|
-
" "
|
|
1352
|
-
],
|
|
1353
|
-
[VENDOR, XIAOMI],
|
|
1354
|
-
[TYPE, MOBILE]
|
|
1355
|
-
],
|
|
1356
|
-
[/oid[^)]+; (2\d{4}(283|rpbf)[cgl])( bui|\))/i, /\b(mi[-_ ]?pad[\w ]+)(?: bui|\))/i],
|
|
1357
|
-
[
|
|
1358
|
-
[
|
|
1359
|
-
MODEL,
|
|
1360
|
-
/_/g,
|
|
1361
|
-
" "
|
|
1362
|
-
],
|
|
1363
|
-
[VENDOR, XIAOMI],
|
|
1364
|
-
[TYPE, TABLET]
|
|
1365
|
-
],
|
|
1366
|
-
[/; (\w+) bui.+ oppo/i, /\b(cph[12]\d{3}|p(?:af|c[al]|d\w|e[ar])[mt]\d0|x9007|a101op)\b/i],
|
|
1367
|
-
[
|
|
1368
|
-
MODEL,
|
|
1369
|
-
[VENDOR, "OPPO"],
|
|
1370
|
-
[TYPE, MOBILE]
|
|
1371
|
-
],
|
|
1372
|
-
[/\b(opd2\d{3}a?) bui/i],
|
|
1373
|
-
[
|
|
1374
|
-
MODEL,
|
|
1375
|
-
[VENDOR, "OPPO"],
|
|
1376
|
-
[TYPE, TABLET]
|
|
1377
|
-
],
|
|
1378
|
-
[/vivo (\w+)(?: bui|\))/i, /\b(v[12]\d{3}\w?[at])(?: bui|;)/i],
|
|
1379
|
-
[
|
|
1380
|
-
MODEL,
|
|
1381
|
-
[VENDOR, "Vivo"],
|
|
1382
|
-
[TYPE, MOBILE]
|
|
1383
|
-
],
|
|
1384
|
-
[/\b(rmx[1-3]\d{3})(?: bui|;|\))/i],
|
|
1385
|
-
[
|
|
1386
|
-
MODEL,
|
|
1387
|
-
[VENDOR, "Realme"],
|
|
1388
|
-
[TYPE, MOBILE]
|
|
1389
|
-
],
|
|
1390
|
-
[
|
|
1391
|
-
/\b(milestone|droid(?:[2-4x]| (?:bionic|x2|pro|razr))?:?( 4g)?)\b[\w ]+build\//i,
|
|
1392
|
-
/\bmot(?:orola)?[- ](\w*)/i,
|
|
1393
|
-
/((?:moto[\w() ]+|xt\d{3,4}|nexus 6)(?= bui|\)))/i
|
|
1394
|
-
],
|
|
1395
|
-
[
|
|
1396
|
-
MODEL,
|
|
1397
|
-
[VENDOR, MOTOROLA],
|
|
1398
|
-
[TYPE, MOBILE]
|
|
1399
|
-
],
|
|
1400
|
-
[/\b(mz60\d|xoom[2 ]{0,2}) build\//i],
|
|
1401
|
-
[
|
|
1402
|
-
MODEL,
|
|
1403
|
-
[VENDOR, MOTOROLA],
|
|
1404
|
-
[TYPE, TABLET]
|
|
1405
|
-
],
|
|
1406
|
-
[/((?=lg)?[vl]k-?\d{3}) bui| 3\.[-\w; ]{10}lg?-([06cv9]{3,4})/i],
|
|
1407
|
-
[
|
|
1408
|
-
MODEL,
|
|
1409
|
-
[VENDOR, LG],
|
|
1410
|
-
[TYPE, TABLET]
|
|
1411
|
-
],
|
|
1412
|
-
[
|
|
1413
|
-
/(lm(?:-?f100[nv]?|-[\w.]+)(?= bui|\))|nexus [45])/i,
|
|
1414
|
-
/\blg[-e;/ ]+((?!browser|netcast|android tv)\w+)/i,
|
|
1415
|
-
/\blg-?(\w+) bui/i
|
|
1416
|
-
],
|
|
1417
|
-
[
|
|
1418
|
-
MODEL,
|
|
1419
|
-
[VENDOR, LG],
|
|
1420
|
-
[TYPE, MOBILE]
|
|
1421
|
-
],
|
|
1422
|
-
[/(ideatab[-\w ]+)/i, /lenovo ?(s[56]000[-\w]+|tab[\w ]+|yt[-\w]{6}|tb[-\w]{6})/i],
|
|
1423
|
-
[
|
|
1424
|
-
MODEL,
|
|
1425
|
-
[VENDOR, "Lenovo"],
|
|
1426
|
-
[TYPE, TABLET]
|
|
1427
|
-
],
|
|
1428
|
-
[/(?:maemo|nokia).*(n900|lumia \d+)/i, /nokia[-_ ]?([-\w.]*)/i],
|
|
1429
|
-
[
|
|
1430
|
-
[
|
|
1431
|
-
MODEL,
|
|
1432
|
-
/_/g,
|
|
1433
|
-
" "
|
|
1434
|
-
],
|
|
1435
|
-
[VENDOR, "Nokia"],
|
|
1436
|
-
[TYPE, MOBILE]
|
|
1437
|
-
],
|
|
1438
|
-
[/(pixel c)\b/i],
|
|
1439
|
-
[
|
|
1440
|
-
MODEL,
|
|
1441
|
-
[VENDOR, GOOGLE],
|
|
1442
|
-
[TYPE, TABLET]
|
|
1443
|
-
],
|
|
1444
|
-
[/droid.+; (pixel[\daxl ]{0,6})(?: bui|\))/i],
|
|
1445
|
-
[
|
|
1446
|
-
MODEL,
|
|
1447
|
-
[VENDOR, GOOGLE],
|
|
1448
|
-
[TYPE, MOBILE]
|
|
1449
|
-
],
|
|
1450
|
-
[/droid.+ (a?\d[0-2]{2}so|[c-g]\d{4}|so[-gl]\w+|xq-a\w[4-7][12])(?= bui|\).+chrome\/(?![1-6]?\d\.))/i],
|
|
1451
|
-
[
|
|
1452
|
-
MODEL,
|
|
1453
|
-
[VENDOR, SONY],
|
|
1454
|
-
[TYPE, MOBILE]
|
|
1455
|
-
],
|
|
1456
|
-
[/sony tablet [ps]/i, /\b(?:sony)?sgp\w+(?: bui|\))/i],
|
|
1457
|
-
[
|
|
1458
|
-
[MODEL, "Xperia Tablet"],
|
|
1459
|
-
[VENDOR, SONY],
|
|
1460
|
-
[TYPE, TABLET]
|
|
1461
|
-
],
|
|
1462
|
-
[/ (kb2005|in20[12]5|be20[12][59])\b/i, /(?:one)?(?:plus)? (a\d0\d\d)(?: b|\))/i],
|
|
1463
|
-
[
|
|
1464
|
-
MODEL,
|
|
1465
|
-
[VENDOR, "OnePlus"],
|
|
1466
|
-
[TYPE, MOBILE]
|
|
1467
|
-
],
|
|
1468
|
-
[
|
|
1469
|
-
/(alexa)webm/i,
|
|
1470
|
-
/(kf[a-z]{2}wi|aeo[c-r]{2})( bui|\))/i,
|
|
1471
|
-
/(kf[a-z]+)( bui|\)).+silk\//i
|
|
1472
|
-
],
|
|
1473
|
-
[
|
|
1474
|
-
MODEL,
|
|
1475
|
-
[VENDOR, AMAZON],
|
|
1476
|
-
[TYPE, TABLET]
|
|
1477
|
-
],
|
|
1478
|
-
[/((?:sd|kf)[0349hijor-uw]+)( bui|\)).+silk\//i],
|
|
1479
|
-
[
|
|
1480
|
-
[
|
|
1481
|
-
MODEL,
|
|
1482
|
-
/(.+)/g,
|
|
1483
|
-
"Fire Phone $1"
|
|
1484
|
-
],
|
|
1485
|
-
[VENDOR, AMAZON],
|
|
1486
|
-
[TYPE, MOBILE]
|
|
1487
|
-
],
|
|
1488
|
-
[/(playbook);[-\w),; ]+(rim)/i],
|
|
1489
|
-
[
|
|
1490
|
-
MODEL,
|
|
1491
|
-
VENDOR,
|
|
1492
|
-
[TYPE, TABLET]
|
|
1493
|
-
],
|
|
1494
|
-
[/\b((?:bb[a-f]|st[hv])100-\d)/i, /\(bb10; (\w+)/i],
|
|
1495
|
-
[
|
|
1496
|
-
MODEL,
|
|
1497
|
-
[VENDOR, BLACKBERRY],
|
|
1498
|
-
[TYPE, MOBILE]
|
|
1499
|
-
],
|
|
1500
|
-
[/(?:\b|asus_)(transfo[prime ]{4,10} \w+|eeepc|slider \w+|nexus 7|padfone|p00[cj])/i],
|
|
1501
|
-
[
|
|
1502
|
-
MODEL,
|
|
1503
|
-
[VENDOR, ASUS],
|
|
1504
|
-
[TYPE, TABLET]
|
|
1505
|
-
],
|
|
1506
|
-
[/ (z[bes]6[027][012][km][ls]|zenfone \d\w?)\b/i],
|
|
1507
|
-
[
|
|
1508
|
-
MODEL,
|
|
1509
|
-
[VENDOR, ASUS],
|
|
1510
|
-
[TYPE, MOBILE]
|
|
1511
|
-
],
|
|
1512
|
-
[/(nexus 9)/i],
|
|
1513
|
-
[
|
|
1514
|
-
MODEL,
|
|
1515
|
-
[VENDOR, "HTC"],
|
|
1516
|
-
[TYPE, TABLET]
|
|
1517
|
-
],
|
|
1518
|
-
[
|
|
1519
|
-
/(htc)[-;_ ]{1,2}([\w ]+(?=\)| bui)|\w+)/i,
|
|
1520
|
-
/(zte)[- ]([\w ]+?)(?: bui|\/|\))/i,
|
|
1521
|
-
/(alcatel|geeksphone|nexian|panasonic(?!;|\.)|sony(?!-bra))[-_ ]?([-\w]*)/i
|
|
1522
|
-
],
|
|
1523
|
-
[
|
|
1524
|
-
VENDOR,
|
|
1525
|
-
[
|
|
1526
|
-
MODEL,
|
|
1527
|
-
/_/g,
|
|
1528
|
-
" "
|
|
1529
|
-
],
|
|
1530
|
-
[TYPE, MOBILE]
|
|
1531
|
-
],
|
|
1532
|
-
[/droid.+; ([ab][1-7]-?[0178a]\d\d?)/i],
|
|
1533
|
-
[
|
|
1534
|
-
MODEL,
|
|
1535
|
-
[VENDOR, "Acer"],
|
|
1536
|
-
[TYPE, TABLET]
|
|
1537
|
-
],
|
|
1538
|
-
[/droid.+; (m[1-5] note) bui/i, /\bmz-([-\w]{2,})/i],
|
|
1539
|
-
[
|
|
1540
|
-
MODEL,
|
|
1541
|
-
[VENDOR, "Meizu"],
|
|
1542
|
-
[TYPE, MOBILE]
|
|
1543
|
-
],
|
|
1544
|
-
[/; ((?:power )?armor[\w ]{0,8})(?: bui|\))/i],
|
|
1545
|
-
[
|
|
1546
|
-
MODEL,
|
|
1547
|
-
[VENDOR, "Ulefone"],
|
|
1548
|
-
[TYPE, MOBILE]
|
|
1549
|
-
],
|
|
1550
|
-
[
|
|
1551
|
-
/(blackberry|benq|palm(?=-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron|infinix|tecno)[-_ ]?([-\w]*)/i,
|
|
1552
|
-
/(hp) ([\w ]+\w)/i,
|
|
1553
|
-
/(asus)-?(\w+)/i,
|
|
1554
|
-
/(microsoft); (lumia[\w ]+)/i,
|
|
1555
|
-
/(lenovo)[-_ ]?([-\w]+)/i,
|
|
1556
|
-
/(jolla)/i,
|
|
1557
|
-
/(oppo) ?([\w ]+) bui/i
|
|
1558
|
-
],
|
|
1559
|
-
[
|
|
1560
|
-
VENDOR,
|
|
1561
|
-
MODEL,
|
|
1562
|
-
[TYPE, MOBILE]
|
|
1563
|
-
],
|
|
1564
|
-
[
|
|
1565
|
-
/(kobo)\s(ereader|touch)/i,
|
|
1566
|
-
/(archos) (gamepad2?)/i,
|
|
1567
|
-
/(hp).+(touchpad(?!.+tablet)|tablet)/i,
|
|
1568
|
-
/(kindle)\/([\w.]+)/i,
|
|
1569
|
-
/(nook)[\w ]+build\/(\w+)/i,
|
|
1570
|
-
/(dell) (strea[kpr\d ]*[\dko])/i,
|
|
1571
|
-
/(le[- ]+pan)[- ]+(\w{1,9}) bui/i,
|
|
1572
|
-
/(trinity)[- ]*(t\d{3}) bui/i,
|
|
1573
|
-
/(gigaset)[- ]+(q\w{1,9}) bui/i,
|
|
1574
|
-
/(vodafone) ([\w ]+)(?:\)| bui)/i
|
|
1575
|
-
],
|
|
1576
|
-
[
|
|
1577
|
-
VENDOR,
|
|
1578
|
-
MODEL,
|
|
1579
|
-
[TYPE, TABLET]
|
|
1580
|
-
],
|
|
1581
|
-
[/(surface duo)/i],
|
|
1582
|
-
[
|
|
1583
|
-
MODEL,
|
|
1584
|
-
[VENDOR, MICROSOFT],
|
|
1585
|
-
[TYPE, TABLET]
|
|
1586
|
-
],
|
|
1587
|
-
[/droid [\d.]+; (fp\du?)(?: b|\))/i],
|
|
1588
|
-
[
|
|
1589
|
-
MODEL,
|
|
1590
|
-
[VENDOR, "Fairphone"],
|
|
1591
|
-
[TYPE, MOBILE]
|
|
1592
|
-
],
|
|
1593
|
-
[/(u304aa)/i],
|
|
1594
|
-
[
|
|
1595
|
-
MODEL,
|
|
1596
|
-
[VENDOR, "AT&T"],
|
|
1597
|
-
[TYPE, MOBILE]
|
|
1598
|
-
],
|
|
1599
|
-
[/\bsie-(\w*)/i],
|
|
1600
|
-
[
|
|
1601
|
-
MODEL,
|
|
1602
|
-
[VENDOR, "Siemens"],
|
|
1603
|
-
[TYPE, MOBILE]
|
|
1604
|
-
],
|
|
1605
|
-
[/\b(rct\w+) b/i],
|
|
1606
|
-
[
|
|
1607
|
-
MODEL,
|
|
1608
|
-
[VENDOR, "RCA"],
|
|
1609
|
-
[TYPE, TABLET]
|
|
1610
|
-
],
|
|
1611
|
-
[/\b(venue[\d ]{2,7}) b/i],
|
|
1612
|
-
[
|
|
1613
|
-
MODEL,
|
|
1614
|
-
[VENDOR, "Dell"],
|
|
1615
|
-
[TYPE, TABLET]
|
|
1616
|
-
],
|
|
1617
|
-
[/\b(q(?:mv|ta)\w+) b/i],
|
|
1618
|
-
[
|
|
1619
|
-
MODEL,
|
|
1620
|
-
[VENDOR, "Verizon"],
|
|
1621
|
-
[TYPE, TABLET]
|
|
1622
|
-
],
|
|
1623
|
-
[/\b(?:barnes[& ]+noble |bn[rt])([\w+ ]*) b/i],
|
|
1624
|
-
[
|
|
1625
|
-
MODEL,
|
|
1626
|
-
[VENDOR, "Barnes & Noble"],
|
|
1627
|
-
[TYPE, TABLET]
|
|
1628
|
-
],
|
|
1629
|
-
[/\b(tm\d{3}\w+) b/i],
|
|
1630
|
-
[
|
|
1631
|
-
MODEL,
|
|
1632
|
-
[VENDOR, "NuVision"],
|
|
1633
|
-
[TYPE, TABLET]
|
|
1634
|
-
],
|
|
1635
|
-
[/\b(k88) b/i],
|
|
1636
|
-
[
|
|
1637
|
-
MODEL,
|
|
1638
|
-
[VENDOR, "ZTE"],
|
|
1639
|
-
[TYPE, TABLET]
|
|
1640
|
-
],
|
|
1641
|
-
[/\b(nx\d{3}j) b/i],
|
|
1642
|
-
[
|
|
1643
|
-
MODEL,
|
|
1644
|
-
[VENDOR, "ZTE"],
|
|
1645
|
-
[TYPE, MOBILE]
|
|
1646
|
-
],
|
|
1647
|
-
[/\b(gen\d{3}) b.+49h/i],
|
|
1648
|
-
[
|
|
1649
|
-
MODEL,
|
|
1650
|
-
[VENDOR, "Swiss"],
|
|
1651
|
-
[TYPE, MOBILE]
|
|
1652
|
-
],
|
|
1653
|
-
[/\b(zur\d{3}) b/i],
|
|
1654
|
-
[
|
|
1655
|
-
MODEL,
|
|
1656
|
-
[VENDOR, "Swiss"],
|
|
1657
|
-
[TYPE, TABLET]
|
|
1658
|
-
],
|
|
1659
|
-
[/\b((zeki)?tb.*\b) b/i],
|
|
1660
|
-
[
|
|
1661
|
-
MODEL,
|
|
1662
|
-
[VENDOR, "Zeki"],
|
|
1663
|
-
[TYPE, TABLET]
|
|
1664
|
-
],
|
|
1665
|
-
[/\b([yr]\d{2}) b/i, /\b(dragon[- ]+touch |dt)(\w{5}) b/i],
|
|
1666
|
-
[
|
|
1667
|
-
[VENDOR, "Dragon Touch"],
|
|
1668
|
-
MODEL,
|
|
1669
|
-
[TYPE, TABLET]
|
|
1670
|
-
],
|
|
1671
|
-
[/\b(ns-?\w{0,9}) b/i],
|
|
1672
|
-
[
|
|
1673
|
-
MODEL,
|
|
1674
|
-
[VENDOR, "Insignia"],
|
|
1675
|
-
[TYPE, TABLET]
|
|
1676
|
-
],
|
|
1677
|
-
[/\b((nxa|next)-?\w{0,9}) b/i],
|
|
1678
|
-
[
|
|
1679
|
-
MODEL,
|
|
1680
|
-
[VENDOR, "NextBook"],
|
|
1681
|
-
[TYPE, TABLET]
|
|
1682
|
-
],
|
|
1683
|
-
[/\b(xtreme_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i],
|
|
1684
|
-
[
|
|
1685
|
-
[VENDOR, "Voice"],
|
|
1686
|
-
MODEL,
|
|
1687
|
-
[TYPE, MOBILE]
|
|
1688
|
-
],
|
|
1689
|
-
[/\b(lvtel-)?(v1[12]) b/i],
|
|
1690
|
-
[
|
|
1691
|
-
[VENDOR, "LvTel"],
|
|
1692
|
-
MODEL,
|
|
1693
|
-
[TYPE, MOBILE]
|
|
1694
|
-
],
|
|
1695
|
-
[/\b(ph-1) /i],
|
|
1696
|
-
[
|
|
1697
|
-
MODEL,
|
|
1698
|
-
[VENDOR, "Essential"],
|
|
1699
|
-
[TYPE, MOBILE]
|
|
1700
|
-
],
|
|
1701
|
-
[/\b(v(100md|700na|7011|917g).*\b) b/i],
|
|
1702
|
-
[
|
|
1703
|
-
MODEL,
|
|
1704
|
-
[VENDOR, "Envizen"],
|
|
1705
|
-
[TYPE, TABLET]
|
|
1706
|
-
],
|
|
1707
|
-
[/\b(trio[-\w. ]+) b/i],
|
|
1708
|
-
[
|
|
1709
|
-
MODEL,
|
|
1710
|
-
[VENDOR, "MachSpeed"],
|
|
1711
|
-
[TYPE, TABLET]
|
|
1712
|
-
],
|
|
1713
|
-
[/\btu_(1491) b/i],
|
|
1714
|
-
[
|
|
1715
|
-
MODEL,
|
|
1716
|
-
[VENDOR, "Rotor"],
|
|
1717
|
-
[TYPE, TABLET]
|
|
1718
|
-
],
|
|
1719
|
-
[/(shield[\w ]+) b/i],
|
|
1720
|
-
[
|
|
1721
|
-
MODEL,
|
|
1722
|
-
[VENDOR, "Nvidia"],
|
|
1723
|
-
[TYPE, TABLET]
|
|
1724
|
-
],
|
|
1725
|
-
[/(sprint) (\w+)/i],
|
|
1726
|
-
[
|
|
1727
|
-
VENDOR,
|
|
1728
|
-
MODEL,
|
|
1729
|
-
[TYPE, MOBILE]
|
|
1730
|
-
],
|
|
1731
|
-
[/(kin\.[onetw]{3})/i],
|
|
1732
|
-
[
|
|
1733
|
-
[
|
|
1734
|
-
MODEL,
|
|
1735
|
-
/\./g,
|
|
1736
|
-
" "
|
|
1737
|
-
],
|
|
1738
|
-
[VENDOR, MICROSOFT],
|
|
1739
|
-
[TYPE, MOBILE]
|
|
1740
|
-
],
|
|
1741
|
-
[/droid.+; (cc6666?|et5[16]|mc[239][23]x?|vc8[03]x?)\)/i],
|
|
1742
|
-
[
|
|
1743
|
-
MODEL,
|
|
1744
|
-
[VENDOR, ZEBRA],
|
|
1745
|
-
[TYPE, TABLET]
|
|
1746
|
-
],
|
|
1747
|
-
[/droid.+; (ec30|ps20|tc[2-8]\d[kx])\)/i],
|
|
1748
|
-
[
|
|
1749
|
-
MODEL,
|
|
1750
|
-
[VENDOR, ZEBRA],
|
|
1751
|
-
[TYPE, MOBILE]
|
|
1752
|
-
],
|
|
1753
|
-
[/smart-tv.+(samsung)/i],
|
|
1754
|
-
[VENDOR, [TYPE, SMARTTV]],
|
|
1755
|
-
[/hbbtv.+maple;(\d+)/i],
|
|
1756
|
-
[
|
|
1757
|
-
[
|
|
1758
|
-
MODEL,
|
|
1759
|
-
/^/,
|
|
1760
|
-
"SmartTV"
|
|
1761
|
-
],
|
|
1762
|
-
[VENDOR, SAMSUNG],
|
|
1763
|
-
[TYPE, SMARTTV]
|
|
1764
|
-
],
|
|
1765
|
-
[/(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i],
|
|
1766
|
-
[[VENDOR, LG], [TYPE, SMARTTV]],
|
|
1767
|
-
[/(apple) ?tv/i],
|
|
1768
|
-
[
|
|
1769
|
-
VENDOR,
|
|
1770
|
-
[MODEL, `${APPLE} TV`],
|
|
1771
|
-
[TYPE, SMARTTV]
|
|
1772
|
-
],
|
|
1773
|
-
[/crkey/i],
|
|
1774
|
-
[
|
|
1775
|
-
[MODEL, `${CHROME}cast`],
|
|
1776
|
-
[VENDOR, GOOGLE],
|
|
1777
|
-
[TYPE, SMARTTV]
|
|
1778
|
-
],
|
|
1779
|
-
[/droid.+aft(\w+)( bui|\))/i],
|
|
1780
|
-
[
|
|
1781
|
-
MODEL,
|
|
1782
|
-
[VENDOR, AMAZON],
|
|
1783
|
-
[TYPE, SMARTTV]
|
|
1784
|
-
],
|
|
1785
|
-
[/\(dtv[);].+(aquos)/i, /(aquos-tv[\w ]+)\)/i],
|
|
1786
|
-
[
|
|
1787
|
-
MODEL,
|
|
1788
|
-
[VENDOR, SHARP],
|
|
1789
|
-
[TYPE, SMARTTV]
|
|
1790
|
-
],
|
|
1791
|
-
[/(bravia[\w ]+)( bui|\))/i],
|
|
1792
|
-
[
|
|
1793
|
-
MODEL,
|
|
1794
|
-
[VENDOR, SONY],
|
|
1795
|
-
[TYPE, SMARTTV]
|
|
1796
|
-
],
|
|
1797
|
-
[/(mitv-\w{5}) bui/i],
|
|
1798
|
-
[
|
|
1799
|
-
MODEL,
|
|
1800
|
-
[VENDOR, XIAOMI],
|
|
1801
|
-
[TYPE, SMARTTV]
|
|
1802
|
-
],
|
|
1803
|
-
[/Hbbtv.*(technisat) (.*);/i],
|
|
1804
|
-
[
|
|
1805
|
-
VENDOR,
|
|
1806
|
-
MODEL,
|
|
1807
|
-
[TYPE, SMARTTV]
|
|
1808
|
-
],
|
|
1809
|
-
[/\b(roku)[\dx]*[)/]((?:dvp-)?[\d.]*)/i, /hbbtv\/\d+\.\d+\.\d+ +\([\w+ ]*; *(\w[^;]*);([^;]*)/i],
|
|
1810
|
-
[
|
|
1811
|
-
[VENDOR, trim],
|
|
1812
|
-
[MODEL, trim],
|
|
1813
|
-
[TYPE, SMARTTV]
|
|
1814
|
-
],
|
|
1815
|
-
[/\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i],
|
|
1816
|
-
[[TYPE, SMARTTV]],
|
|
1817
|
-
[/(ouya)/i, /(nintendo) ([wids3utch]+)/i],
|
|
1818
|
-
[
|
|
1819
|
-
VENDOR,
|
|
1820
|
-
MODEL,
|
|
1821
|
-
[TYPE, CONSOLE]
|
|
1822
|
-
],
|
|
1823
|
-
[/droid.+; (shield) bui/i],
|
|
1824
|
-
[
|
|
1825
|
-
MODEL,
|
|
1826
|
-
[VENDOR, "Nvidia"],
|
|
1827
|
-
[TYPE, CONSOLE]
|
|
1828
|
-
],
|
|
1829
|
-
[/(playstation [345portablevi]+)/i],
|
|
1830
|
-
[
|
|
1831
|
-
MODEL,
|
|
1832
|
-
[VENDOR, SONY],
|
|
1833
|
-
[TYPE, CONSOLE]
|
|
1834
|
-
],
|
|
1835
|
-
[/\b(xbox(?: one)?(?!; xbox))[); ]/i],
|
|
1836
|
-
[
|
|
1837
|
-
MODEL,
|
|
1838
|
-
[VENDOR, MICROSOFT],
|
|
1839
|
-
[TYPE, CONSOLE]
|
|
1840
|
-
],
|
|
1841
|
-
[/((pebble))app/i],
|
|
1842
|
-
[
|
|
1843
|
-
VENDOR,
|
|
1844
|
-
MODEL,
|
|
1845
|
-
[TYPE, WEARABLE]
|
|
1846
|
-
],
|
|
1847
|
-
[/(watch)(?: ?os[,/]|\d,\d\/)[\d.]+/i],
|
|
1848
|
-
[
|
|
1849
|
-
MODEL,
|
|
1850
|
-
[VENDOR, APPLE],
|
|
1851
|
-
[TYPE, WEARABLE]
|
|
1852
|
-
],
|
|
1853
|
-
[/droid.+; (glass) \d/i],
|
|
1854
|
-
[
|
|
1855
|
-
MODEL,
|
|
1856
|
-
[VENDOR, GOOGLE],
|
|
1857
|
-
[TYPE, WEARABLE]
|
|
1858
|
-
],
|
|
1859
|
-
[/droid.+; (wt63?0{2,3})\)/i],
|
|
1860
|
-
[
|
|
1861
|
-
MODEL,
|
|
1862
|
-
[VENDOR, ZEBRA],
|
|
1863
|
-
[TYPE, WEARABLE]
|
|
1864
|
-
],
|
|
1865
|
-
[/(quest( \d| pro)?)/i],
|
|
1866
|
-
[
|
|
1867
|
-
MODEL,
|
|
1868
|
-
[VENDOR, FACEBOOK],
|
|
1869
|
-
[TYPE, WEARABLE]
|
|
1870
|
-
],
|
|
1871
|
-
[/(tesla)(?: qtcarbrowser|\/[-\w.]+)/i],
|
|
1872
|
-
[VENDOR, [TYPE, EMBEDDED]],
|
|
1873
|
-
[/(aeobc)\b/i],
|
|
1874
|
-
[
|
|
1875
|
-
MODEL,
|
|
1876
|
-
[VENDOR, AMAZON],
|
|
1877
|
-
[TYPE, EMBEDDED]
|
|
1878
|
-
],
|
|
1879
|
-
[/droid .+?; ([^;]+?)(?: bui|; wv\)|\) applew).+? mobile safari/i],
|
|
1880
|
-
[MODEL, [TYPE, MOBILE]],
|
|
1881
|
-
[/droid .+?; ([^;]+?)(?: bui|\) applew).+?(?! mobile) safari/i],
|
|
1882
|
-
[MODEL, [TYPE, TABLET]],
|
|
1883
|
-
[/\b((tablet|tab)[;/]|focus\/\d(?!.+mobile))/i],
|
|
1884
|
-
[[TYPE, TABLET]],
|
|
1885
|
-
[/(phone|mobile(?:[;/]| [ \w/.]*safari)|pda(?=.+windows ce))/i],
|
|
1886
|
-
[[TYPE, MOBILE]],
|
|
1887
|
-
[/(android[-\w. ]{0,9});.+buil/i],
|
|
1888
|
-
[MODEL, [VENDOR, "Generic"]]
|
|
1889
|
-
],
|
|
1890
|
-
engine: [
|
|
1891
|
-
[/windows.+ edge\/([\w.]+)/i],
|
|
1892
|
-
[VERSION, [NAME, `${EDGE}HTML`]],
|
|
1893
|
-
[/webkit\/537\.36.+chrome\/(?!27)([\w.]+)/i],
|
|
1894
|
-
[VERSION, [NAME, "Blink"]],
|
|
1895
|
-
[
|
|
1896
|
-
/(presto)\/([\w.]+)/i,
|
|
1897
|
-
/(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna)\/([\w.]+)/i,
|
|
1898
|
-
/ekioh(flow)\/([\w.]+)/i,
|
|
1899
|
-
/(khtml|tasman|links)[/ ]\(?([\w.]+)/i,
|
|
1900
|
-
/(icab)[/ ]([23]\.[\d.]+)/i,
|
|
1901
|
-
/\b(libweb)/i
|
|
1902
|
-
],
|
|
1903
|
-
[NAME, VERSION],
|
|
1904
|
-
[/rv:([\w.]{1,9})\b.+(gecko)/i],
|
|
1905
|
-
[VERSION, NAME]
|
|
1906
|
-
],
|
|
1907
|
-
os: [
|
|
1908
|
-
[/microsoft (windows) (vista|xp)/i],
|
|
1909
|
-
[NAME, VERSION],
|
|
1910
|
-
[/(windows (?:phone(?: os)?|mobile))[/ ]?([.\w ]*)/i],
|
|
1911
|
-
[NAME, [
|
|
1912
|
-
VERSION,
|
|
1913
|
-
strMapper,
|
|
1914
|
-
windowsVersionMap
|
|
1915
|
-
]],
|
|
1916
|
-
[
|
|
1917
|
-
/windows nt 6\.2; (arm)/i,
|
|
1918
|
-
/windows[/ ]?([ntce\d. ]+\w)(?!.+xbox)/i,
|
|
1919
|
-
/(?:win(?=[39n])|win 9x )([nt\d.]+)/i
|
|
1920
|
-
],
|
|
1921
|
-
[[
|
|
1922
|
-
VERSION,
|
|
1923
|
-
strMapper,
|
|
1924
|
-
windowsVersionMap
|
|
1925
|
-
], [NAME, "Windows"]],
|
|
1926
|
-
[
|
|
1927
|
-
/ip[honead]{2,4}\b(?:.*os (\w+) like mac|; opera)/i,
|
|
1928
|
-
/(?:ios;fbsv\/|iphone.+ios[/ ])([\d.]+)/i,
|
|
1929
|
-
/cfnetwork\/.+darwin/i
|
|
1930
|
-
],
|
|
1931
|
-
[[
|
|
1932
|
-
VERSION,
|
|
1933
|
-
/_/g,
|
|
1934
|
-
"."
|
|
1935
|
-
], [NAME, "iOS"]],
|
|
1936
|
-
[/(mac os x) ?([\w. ]*)/i, /(macintosh|mac_powerpc\b)(?!.+haiku)/i],
|
|
1937
|
-
[[NAME, MAC_OS], [
|
|
1938
|
-
VERSION,
|
|
1939
|
-
/_/g,
|
|
1940
|
-
"."
|
|
1941
|
-
]],
|
|
1942
|
-
[/droid ([\w.]+)\b.+(android[- ]x86|harmonyos)/i],
|
|
1943
|
-
[VERSION, NAME],
|
|
1944
|
-
[
|
|
1945
|
-
/(android|webos|qnx|bada|rim tablet os|maemo|meego|sailfish)[-/ ]?([\w.]*)/i,
|
|
1946
|
-
/(blackberry)\w*\/([\w.]*)/i,
|
|
1947
|
-
/(tizen|kaios)[/ ]([\w.]+)/i,
|
|
1948
|
-
/\((series40);/i
|
|
1949
|
-
],
|
|
1950
|
-
[NAME, VERSION],
|
|
1951
|
-
[/\(bb(10);/i],
|
|
1952
|
-
[VERSION, [NAME, BLACKBERRY]],
|
|
1953
|
-
[/(?:symbian ?os|symbos|s60(?=;)|series60)[-/ ]?([\w.]*)/i],
|
|
1954
|
-
[VERSION, [NAME, "Symbian"]],
|
|
1955
|
-
[/mozilla\/[\d.]+ \((?:mobile|tablet|tv|mobile; [\w ]+); rv:.+ gecko\/([\w.]+)/i],
|
|
1956
|
-
[VERSION, [NAME, `${FIREFOX} OS`]],
|
|
1957
|
-
[/web0s;.+rt(tv)/i, /\b(?:hp)?wos(?:browser)?\/([\w.]+)/i],
|
|
1958
|
-
[VERSION, [NAME, "webOS"]],
|
|
1959
|
-
[/watch(?: ?os[,/]|\d,\d\/)([\d.]+)/i],
|
|
1960
|
-
[VERSION, [NAME, "watchOS"]],
|
|
1961
|
-
[/crkey\/([\d.]+)/i],
|
|
1962
|
-
[VERSION, [NAME, `${CHROME}cast`]],
|
|
1963
|
-
[/(cros) \w+(?:\)| ([\w.]+)\b)/i],
|
|
1964
|
-
[[NAME, CHROMIUM_OS], VERSION],
|
|
1965
|
-
[
|
|
1966
|
-
/panasonic;(viera)/i,
|
|
1967
|
-
/(netrange)mmh/i,
|
|
1968
|
-
/(nettv)\/(\d+\.[\w.]+)/i,
|
|
1969
|
-
/(nintendo|playstation) ([wids345portablevuch]+)/i,
|
|
1970
|
-
/(xbox); +xbox ([^);]+)/i,
|
|
1971
|
-
/\b(joli|palm)\b ?(?:os)?\/?([\w.]*)/i,
|
|
1972
|
-
/(mint)[/() ]?(\w*)/i,
|
|
1973
|
-
/(mageia|vectorlinux)[; ]/i,
|
|
1974
|
-
/([kxln]?ubuntu|debian|suse|opensuse|gentoo|arch(?= linux)|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire)(?: gnu\/linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-/ ]?(?!chrom|package)([-\w.]*)/i,
|
|
1975
|
-
/(hurd|linux) ?([\w.]*)/i,
|
|
1976
|
-
/(gnu) ?([\w.]*)/i,
|
|
1977
|
-
/\b([-e-hrntopcs]{0,5}bsd|dragonfly)[/ ]?(?!amd|[ix346]{1,2}86)([\w.]*)/i,
|
|
1978
|
-
/(haiku) (\w+)/i
|
|
1979
|
-
],
|
|
1980
|
-
[NAME, VERSION],
|
|
1981
|
-
[/(sunos) ?([\w.]*)/i],
|
|
1982
|
-
[[NAME, "Solaris"], VERSION],
|
|
1983
|
-
[
|
|
1984
|
-
/((?:open)?solaris)[-/ ]?([\w.]*)/i,
|
|
1985
|
-
/(aix) ((\d)(?=[.) ])[\w.])*/i,
|
|
1986
|
-
/\b(beos|os\/2|amigaos|morphos|openvms|fuchsia|hp-ux|serenityos)/i,
|
|
1987
|
-
/(unix) ?([\w.]*)/i
|
|
1988
|
-
],
|
|
1989
|
-
[NAME, VERSION]
|
|
1990
|
-
]
|
|
1991
|
-
};
|
|
1992
|
-
function isExtensionsInput(input) {
|
|
1993
|
-
return typeof input === "object" && input !== null;
|
|
1994
|
-
}
|
|
1995
|
-
Object.freeze(enumerize([
|
|
1996
|
-
NAME,
|
|
1997
|
-
VERSION,
|
|
1998
|
-
MAJOR
|
|
1999
|
-
]));
|
|
2000
|
-
Object.freeze(enumerize([ARCHITECTURE]));
|
|
2001
|
-
Object.freeze(enumerize([
|
|
2002
|
-
MODEL,
|
|
2003
|
-
VENDOR,
|
|
2004
|
-
TYPE,
|
|
2005
|
-
CONSOLE,
|
|
2006
|
-
MOBILE,
|
|
2007
|
-
SMARTTV,
|
|
2008
|
-
TABLET,
|
|
2009
|
-
WEARABLE,
|
|
2010
|
-
EMBEDDED
|
|
2011
|
-
]));
|
|
2012
|
-
Object.freeze(enumerize([NAME, VERSION]));
|
|
2013
|
-
Object.freeze(enumerize([NAME, VERSION]));
|
|
2014
|
-
function getRuntimeNavigator() {
|
|
2015
|
-
if (typeof window === "undefined" || !window.navigator) return void 0;
|
|
2016
|
-
return window.navigator;
|
|
2017
|
-
}
|
|
2018
|
-
function normalizeUA(ua) {
|
|
2019
|
-
return ua.length > UA_MAX_LENGTH ? trim(ua, UA_MAX_LENGTH) : ua;
|
|
2020
|
-
}
|
|
2021
|
-
function createDefaultParserContext(uastring) {
|
|
2022
|
-
if (typeof uastring === "string") return {
|
|
2023
|
-
ua: normalizeUA(uastring),
|
|
2024
|
-
isSelfNavigator: false,
|
|
2025
|
-
navigator: void 0,
|
|
2026
|
-
userAgentData: void 0,
|
|
2027
|
-
regexMap: regexes
|
|
2028
|
-
};
|
|
2029
|
-
const navigator = getRuntimeNavigator();
|
|
2030
|
-
const ua = normalizeUA(navigator && navigator.userAgent ? navigator.userAgent : EMPTY);
|
|
2031
|
-
return {
|
|
2032
|
-
ua,
|
|
2033
|
-
isSelfNavigator: Boolean(navigator && navigator.userAgent === ua),
|
|
2034
|
-
navigator,
|
|
2035
|
-
userAgentData: navigator?.userAgentData,
|
|
2036
|
-
regexMap: regexes
|
|
2037
|
-
};
|
|
2038
|
-
}
|
|
2039
|
-
function createParserContext(uastringOrExtensions, extensions) {
|
|
2040
|
-
if (!extensions && !isExtensionsInput(uastringOrExtensions)) return createDefaultParserContext(uastringOrExtensions);
|
|
2041
|
-
const navigator = getRuntimeNavigator();
|
|
2042
|
-
const uastring = isExtensionsInput(uastringOrExtensions) ? void 0 : uastringOrExtensions;
|
|
2043
|
-
const resolvedExtensions = isExtensionsInput(uastringOrExtensions) ? uastringOrExtensions : extensions;
|
|
2044
|
-
const ua = normalizeUA(uastring || (navigator && navigator.userAgent ? navigator.userAgent : EMPTY));
|
|
2045
|
-
const userAgentData = navigator?.userAgentData;
|
|
2046
|
-
return {
|
|
2047
|
-
ua,
|
|
2048
|
-
isSelfNavigator: Boolean(navigator && navigator.userAgent === ua),
|
|
2049
|
-
navigator,
|
|
2050
|
-
userAgentData,
|
|
2051
|
-
regexMap: resolvedExtensions ? extend(regexes, resolvedExtensions) : regexes
|
|
2052
|
-
};
|
|
2053
|
-
}
|
|
2054
|
-
function parseBrowserFromContext(context) {
|
|
2055
|
-
const browser = {
|
|
2056
|
-
name: void 0,
|
|
2057
|
-
version: void 0,
|
|
2058
|
-
major: void 0
|
|
2059
|
-
};
|
|
2060
|
-
rgxMapper(context.ua, context.regexMap.browser, browser);
|
|
2061
|
-
browser.major = majorize(browser.version);
|
|
2062
|
-
if (context.isSelfNavigator && context.navigator?.brave && typeof context.navigator.brave.isBrave === "function") browser.name = "Brave";
|
|
2063
|
-
return browser;
|
|
2064
|
-
}
|
|
2065
|
-
function parseCPUFromContext(context) {
|
|
2066
|
-
const cpu = { architecture: void 0 };
|
|
2067
|
-
rgxMapper(context.ua, context.regexMap.cpu, cpu);
|
|
2068
|
-
return cpu;
|
|
2069
|
-
}
|
|
2070
|
-
function parseDeviceFromContext(context) {
|
|
2071
|
-
const device = {
|
|
2072
|
-
vendor: void 0,
|
|
2073
|
-
model: void 0,
|
|
2074
|
-
type: void 0
|
|
2075
|
-
};
|
|
2076
|
-
rgxMapper(context.ua, context.regexMap.device, device);
|
|
2077
|
-
if (context.isSelfNavigator && !device.type && context.userAgentData?.mobile) device.type = MOBILE;
|
|
2078
|
-
if (context.isSelfNavigator && device.model === "Macintosh" && context.navigator && typeof context.navigator.standalone !== "undefined" && context.navigator.maxTouchPoints && context.navigator.maxTouchPoints > 2) {
|
|
2079
|
-
device.model = "iPad";
|
|
2080
|
-
device.type = TABLET;
|
|
2081
|
-
}
|
|
2082
|
-
return device;
|
|
2083
|
-
}
|
|
2084
|
-
function parseEngineFromContext(context) {
|
|
2085
|
-
const engine = {
|
|
2086
|
-
name: void 0,
|
|
2087
|
-
version: void 0
|
|
2088
|
-
};
|
|
2089
|
-
rgxMapper(context.ua, context.regexMap.engine, engine);
|
|
2090
|
-
return engine;
|
|
2091
|
-
}
|
|
2092
|
-
function parseOSFromContext(context) {
|
|
2093
|
-
const os = {
|
|
2094
|
-
name: void 0,
|
|
2095
|
-
version: void 0
|
|
2096
|
-
};
|
|
2097
|
-
rgxMapper(context.ua, context.regexMap.os, os);
|
|
2098
|
-
if (context.isSelfNavigator && !os.name && context.userAgentData?.platform && context.userAgentData.platform !== "Unknown") os.name = context.userAgentData.platform.replace(/chrome os/i, CHROMIUM_OS).replace(/macos/i, MAC_OS);
|
|
2099
|
-
return os;
|
|
2100
|
-
}
|
|
2101
|
-
function parseUA(uastringOrExtensions, extensions) {
|
|
2102
|
-
let context;
|
|
2103
|
-
let browserCache;
|
|
2104
|
-
let engineCache;
|
|
2105
|
-
let osCache;
|
|
2106
|
-
let deviceCache;
|
|
2107
|
-
let cpuCache;
|
|
2108
|
-
const getContext = () => context ||= createParserContext(uastringOrExtensions, extensions);
|
|
2109
|
-
return {
|
|
2110
|
-
get ua() {
|
|
2111
|
-
return getContext().ua;
|
|
2112
|
-
},
|
|
2113
|
-
get browser() {
|
|
2114
|
-
browserCache ||= parseBrowserFromContext(getContext());
|
|
2115
|
-
return browserCache;
|
|
2116
|
-
},
|
|
2117
|
-
get engine() {
|
|
2118
|
-
engineCache ||= parseEngineFromContext(getContext());
|
|
2119
|
-
return engineCache;
|
|
2120
|
-
},
|
|
2121
|
-
get os() {
|
|
2122
|
-
osCache ||= parseOSFromContext(getContext());
|
|
2123
|
-
return osCache;
|
|
2124
|
-
},
|
|
2125
|
-
get device() {
|
|
2126
|
-
deviceCache ||= parseDeviceFromContext(getContext());
|
|
2127
|
-
return deviceCache;
|
|
2128
|
-
},
|
|
2129
|
-
get cpu() {
|
|
2130
|
-
cpuCache ||= parseCPUFromContext(getContext());
|
|
2131
|
-
return cpuCache;
|
|
2132
|
-
}
|
|
1433
|
+
serialize: codec.serialize,
|
|
1434
|
+
deserialize: codec.deserialize
|
|
2133
1435
|
};
|
|
2134
1436
|
}
|
|
2135
1437
|
//#endregion
|
|
@@ -2176,204 +1478,47 @@ function resolveWsUrl(websocket, metaBaseUrl, loc) {
|
|
|
2176
1478
|
}
|
|
2177
1479
|
function createWsRpcClientMode(options) {
|
|
2178
1480
|
const { authToken, connectionMeta, metaBaseUrl, events, clientRpc, rpcOptions = {}, wsOptions = {}, callTimeout = 0 } = options;
|
|
2179
|
-
let isTrusted = false;
|
|
2180
|
-
let status = "connecting";
|
|
2181
|
-
let connectionError = null;
|
|
2182
|
-
const trustedPromise = promiseWithResolver();
|
|
2183
1481
|
const url = resolveWsUrl(connectionMeta.websocket, metaBaseUrl ?? "./", location);
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
function rejectAllPending(error) {
|
|
2194
|
-
for (const entry of [...pending]) entry.reject(error);
|
|
2195
|
-
}
|
|
2196
|
-
function terminalError() {
|
|
2197
|
-
if (status === "disconnected" || status === "error") return new DevframeConnectionError("connection", "[devframe] Not connected to the devframe server", { cause: connectionError ?? void 0 });
|
|
2198
|
-
if (status === "unauthorized") return new DevframeConnectionError("auth", "[devframe] Not authorized by the devframe server", { cause: connectionError ?? void 0 });
|
|
2199
|
-
return null;
|
|
2200
|
-
}
|
|
2201
|
-
/**
|
|
2202
|
-
* Wrap an in-flight call promise so it settles on server response, on an
|
|
2203
|
-
* optional wall-clock timeout, or when the connection drops — never hangs.
|
|
2204
|
-
*/
|
|
2205
|
-
function guardCall(promise, method) {
|
|
2206
|
-
return new Promise((resolve, reject) => {
|
|
2207
|
-
let settled = false;
|
|
2208
|
-
let timer;
|
|
2209
|
-
const entry = { reject(error) {
|
|
2210
|
-
if (settled) return;
|
|
2211
|
-
finish();
|
|
2212
|
-
events.emit("rpc:error", error, method);
|
|
2213
|
-
reject(error);
|
|
2214
|
-
} };
|
|
2215
|
-
function finish() {
|
|
2216
|
-
settled = true;
|
|
2217
|
-
pending.delete(entry);
|
|
2218
|
-
if (timer) clearTimeout(timer);
|
|
2219
|
-
}
|
|
2220
|
-
pending.add(entry);
|
|
2221
|
-
if (callTimeout > 0) timer = setTimeout(() => {
|
|
2222
|
-
entry.reject(new DevframeConnectionError("timeout", `[devframe] RPC call "${method}" timed out after ${callTimeout}ms`));
|
|
2223
|
-
}, callTimeout);
|
|
2224
|
-
promise.then((value) => {
|
|
2225
|
-
if (settled) return;
|
|
2226
|
-
finish();
|
|
2227
|
-
resolve(value);
|
|
2228
|
-
}, (error) => {
|
|
2229
|
-
if (settled) return;
|
|
2230
|
-
finish();
|
|
2231
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
2232
|
-
events.emit("rpc:error", err, method);
|
|
2233
|
-
reject(err);
|
|
2234
|
-
});
|
|
2235
|
-
});
|
|
2236
|
-
}
|
|
2237
|
-
const definitions = /* @__PURE__ */ new Map();
|
|
2238
|
-
for (const name of connectionMeta.jsonSerializableMethods ?? []) definitions.set(name, { jsonSerializable: true });
|
|
2239
|
-
const serverRpc = createRpcClient(clientRpc.functions, {
|
|
2240
|
-
channel: createWsRpcChannel({
|
|
1482
|
+
return createLiveRpcClientMode({
|
|
1483
|
+
transport: "websocket",
|
|
1484
|
+
authToken,
|
|
1485
|
+
connectionMeta,
|
|
1486
|
+
events,
|
|
1487
|
+
clientRpc,
|
|
1488
|
+
rpcOptions,
|
|
1489
|
+
callTimeout,
|
|
1490
|
+
createChannel: (handlers) => createWsRpcChannel({
|
|
2241
1491
|
url,
|
|
2242
1492
|
authToken,
|
|
2243
|
-
definitions,
|
|
1493
|
+
definitions: handlers.definitions,
|
|
2244
1494
|
...wsOptions,
|
|
2245
1495
|
onConnected(event) {
|
|
2246
1496
|
wsOptions.onConnected?.(event);
|
|
2247
1497
|
},
|
|
2248
1498
|
onError(error) {
|
|
2249
|
-
|
|
2250
|
-
events.emit("connection:error", error);
|
|
2251
|
-
rejectAllPending(new DevframeConnectionError("connection", "[devframe] Connection to the devframe server failed", { cause: error }));
|
|
1499
|
+
handlers.onError(error);
|
|
2252
1500
|
wsOptions.onError?.(error);
|
|
2253
1501
|
},
|
|
2254
1502
|
onDisconnected(event) {
|
|
2255
|
-
|
|
2256
|
-
rejectAllPending(new DevframeConnectionError("connection", "[devframe] Disconnected from the devframe server", { cause: connectionError ?? void 0 }));
|
|
1503
|
+
handlers.onDisconnected();
|
|
2257
1504
|
wsOptions.onDisconnected?.(event);
|
|
2258
1505
|
}
|
|
2259
|
-
})
|
|
2260
|
-
rpcOptions
|
|
2261
|
-
});
|
|
2262
|
-
clientRpc.register({
|
|
2263
|
-
name: "devframe:auth:revoked",
|
|
2264
|
-
type: "event",
|
|
2265
|
-
handler: () => {
|
|
2266
|
-
isTrusted = false;
|
|
2267
|
-
const authError = new DevframeConnectionError("auth", "[devframe] The devframe server revoked this client's trust");
|
|
2268
|
-
setStatus("unauthorized", authError);
|
|
2269
|
-
events.emit("connection:error", authError);
|
|
2270
|
-
rejectAllPending(authError);
|
|
2271
|
-
events.emit("rpc:is-trusted:updated", false);
|
|
2272
|
-
}
|
|
1506
|
+
})
|
|
2273
1507
|
});
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
const result = await serverRpc.$call("anonymous:devframe:auth", {
|
|
2289
|
-
authToken: token,
|
|
2290
|
-
ua: describeUA(),
|
|
2291
|
-
origin: location.origin
|
|
2292
|
-
});
|
|
2293
|
-
isTrusted = result.isTrusted;
|
|
2294
|
-
if (isTrusted) {
|
|
2295
|
-
trustedPromise.resolve(true);
|
|
2296
|
-
setStatus("connected");
|
|
2297
|
-
} else {
|
|
2298
|
-
const authError = new DevframeConnectionError("auth", "[devframe] The devframe server refused this client's credentials");
|
|
2299
|
-
setStatus("unauthorized", authError);
|
|
2300
|
-
events.emit("connection:error", authError);
|
|
2301
|
-
}
|
|
2302
|
-
events.emit("rpc:is-trusted:updated", isTrusted);
|
|
2303
|
-
return result.isTrusted;
|
|
2304
|
-
}
|
|
2305
|
-
async function requestTrustWithCode(code) {
|
|
2306
|
-
const token = (await serverRpc.$call("anonymous:devframe:auth:exchange", {
|
|
2307
|
-
code,
|
|
2308
|
-
ua: describeUA(),
|
|
2309
|
-
origin: location.origin
|
|
2310
|
-
}))?.authToken ?? null;
|
|
2311
|
-
if (token) {
|
|
2312
|
-
currentAuthToken = token;
|
|
2313
|
-
isTrusted = true;
|
|
2314
|
-
trustedPromise.resolve(true);
|
|
2315
|
-
setStatus("connected");
|
|
2316
|
-
events.emit("rpc:is-trusted:updated", true);
|
|
2317
|
-
}
|
|
2318
|
-
return token;
|
|
2319
|
-
}
|
|
2320
|
-
async function requestTrust() {
|
|
2321
|
-
if (isTrusted) return true;
|
|
2322
|
-
return requestTrustWithToken(currentAuthToken ?? "");
|
|
2323
|
-
}
|
|
2324
|
-
async function ensureTrusted(timeout = 6e4) {
|
|
2325
|
-
if (isTrusted) trustedPromise.resolve(true);
|
|
2326
|
-
if (timeout <= 0) return trustedPromise.promise;
|
|
2327
|
-
let clear = () => {};
|
|
2328
|
-
await Promise.race([trustedPromise.promise.then(clear), new Promise((resolve, reject) => {
|
|
2329
|
-
const id = setTimeout(() => {
|
|
2330
|
-
reject(/* @__PURE__ */ new Error("[devframe] Timeout waiting for rpc to be trusted"));
|
|
2331
|
-
}, timeout);
|
|
2332
|
-
clear = () => clearTimeout(id);
|
|
2333
|
-
})]);
|
|
2334
|
-
return isTrusted;
|
|
2335
|
-
}
|
|
2336
|
-
return {
|
|
2337
|
-
get isTrusted() {
|
|
2338
|
-
return isTrusted;
|
|
2339
|
-
},
|
|
2340
|
-
get status() {
|
|
2341
|
-
return status;
|
|
2342
|
-
},
|
|
2343
|
-
get connectionError() {
|
|
2344
|
-
return connectionError;
|
|
2345
|
-
},
|
|
2346
|
-
requestTrust,
|
|
2347
|
-
requestTrustWithToken,
|
|
2348
|
-
requestTrustWithCode,
|
|
2349
|
-
ensureTrusted,
|
|
2350
|
-
call: (...args) => {
|
|
2351
|
-
const method = String(args[0]);
|
|
2352
|
-
const failFast = terminalError();
|
|
2353
|
-
if (failFast) {
|
|
2354
|
-
events.emit("rpc:error", failFast, method);
|
|
2355
|
-
return Promise.reject(failFast);
|
|
2356
|
-
}
|
|
2357
|
-
return guardCall(serverRpc.$call(...args), method);
|
|
2358
|
-
},
|
|
2359
|
-
callEvent: (...args) => {
|
|
2360
|
-
const failFast = terminalError();
|
|
2361
|
-
if (failFast) {
|
|
2362
|
-
events.emit("rpc:error", failFast, String(args[0]));
|
|
2363
|
-
return;
|
|
2364
|
-
}
|
|
2365
|
-
return serverRpc.$callEvent(...args);
|
|
2366
|
-
},
|
|
2367
|
-
callOptional: (...args) => {
|
|
2368
|
-
const method = String(args[0]);
|
|
2369
|
-
const failFast = terminalError();
|
|
2370
|
-
if (failFast) {
|
|
2371
|
-
events.emit("rpc:error", failFast, method);
|
|
2372
|
-
return Promise.reject(failFast);
|
|
2373
|
-
}
|
|
2374
|
-
return guardCall(serverRpc.$callOptional(...args), method);
|
|
2375
|
-
}
|
|
2376
|
-
};
|
|
1508
|
+
}
|
|
1509
|
+
//#endregion
|
|
1510
|
+
//#region src/utils/scope.ts
|
|
1511
|
+
/** Whether a name is already namespaced (contains a `:` separator). */
|
|
1512
|
+
function isQualifiedName(name) {
|
|
1513
|
+
return name.includes(":");
|
|
1514
|
+
}
|
|
1515
|
+
/**
|
|
1516
|
+
* Prefix a bare name with `<namespace>:`. Names that already contain a
|
|
1517
|
+
* `:` are returned unchanged, so callers can reference another scope's
|
|
1518
|
+
* ids explicitly (e.g. `ctx.rpc.call('other-plugin:fn')`).
|
|
1519
|
+
*/
|
|
1520
|
+
function qualifyName(namespace, name) {
|
|
1521
|
+
return isQualifiedName(name) ? name : `${namespace}:${name}`;
|
|
2377
1522
|
}
|
|
2378
1523
|
//#endregion
|
|
2379
1524
|
//#region src/client/settings.ts
|
|
@@ -2451,6 +1596,30 @@ function createScopedClientContext(rpc, namespace) {
|
|
|
2451
1596
|
}
|
|
2452
1597
|
//#endregion
|
|
2453
1598
|
//#region src/client/rpc.ts
|
|
1599
|
+
/**
|
|
1600
|
+
* Resolve the requested `transport` option against what the server
|
|
1601
|
+
* advertises. `'auto'` trusts the advertisement — the server's declared
|
|
1602
|
+
* primary (`backend`), preferring the WebSocket when both endpoints are
|
|
1603
|
+
* present; an explicit `'websocket'` / `'sse'` pins that transport and
|
|
1604
|
+
* throws when the server doesn't advertise it.
|
|
1605
|
+
*/
|
|
1606
|
+
function resolveClientTransport(requested, meta) {
|
|
1607
|
+
if (meta.backend === "static") return "static";
|
|
1608
|
+
const hasWebsocket = meta.websocket !== void 0;
|
|
1609
|
+
const hasSse = meta.sse !== void 0;
|
|
1610
|
+
if (requested === "websocket") {
|
|
1611
|
+
if (!hasWebsocket) throw new Error("[devframe] transport: 'websocket' was requested, but this server does not advertise a WebSocket endpoint");
|
|
1612
|
+
return "websocket";
|
|
1613
|
+
}
|
|
1614
|
+
if (requested === "sse") {
|
|
1615
|
+
if (!hasSse) throw new Error("[devframe] transport: 'sse' was requested, but this server does not advertise an SSE endpoint");
|
|
1616
|
+
return "sse";
|
|
1617
|
+
}
|
|
1618
|
+
if (meta.backend === "sse" && hasSse) return "sse";
|
|
1619
|
+
if (hasWebsocket) return "websocket";
|
|
1620
|
+
if (hasSse) return "sse";
|
|
1621
|
+
throw new Error("[devframe] This server advertises no RPC transport (backend \"none\") — nothing to connect to. Enable the WebSocket or SSE endpoint on the server, or use its static/MCP surfaces instead.");
|
|
1622
|
+
}
|
|
2454
1623
|
async function getDevframeRpcClient(options = {}) {
|
|
2455
1624
|
const { baseURL = "./", rpcOptions = {}, cacheOptions = false } = options;
|
|
2456
1625
|
const events = createEventEmitter();
|
|
@@ -2480,7 +1649,7 @@ async function getDevframeRpcClient(options = {}) {
|
|
|
2480
1649
|
}
|
|
2481
1650
|
throw new Error(`Failed to load ${path} from ${candidates.join(", ")}`, { cause: errors });
|
|
2482
1651
|
}
|
|
2483
|
-
const
|
|
1652
|
+
const liveModeOptions = {
|
|
2484
1653
|
authToken,
|
|
2485
1654
|
connectionMeta,
|
|
2486
1655
|
metaBaseUrl,
|
|
@@ -2497,7 +1666,14 @@ async function getDevframeRpcClient(options = {}) {
|
|
|
2497
1666
|
cacheManager.apply(req, res);
|
|
2498
1667
|
} else await next(req);
|
|
2499
1668
|
}
|
|
2500
|
-
}
|
|
1669
|
+
}
|
|
1670
|
+
};
|
|
1671
|
+
const transport = resolveClientTransport(options.transport ?? "auto", connectionMeta);
|
|
1672
|
+
const mode = transport === "static" ? await createStaticRpcClientMode({ fetchJsonFromBases }) : transport === "sse" ? createSseRpcClientMode({
|
|
1673
|
+
...liveModeOptions,
|
|
1674
|
+
sseOptions: options.sseOptions
|
|
1675
|
+
}) : createWsRpcClientMode({
|
|
1676
|
+
...liveModeOptions,
|
|
2501
1677
|
wsOptions: options.wsOptions
|
|
2502
1678
|
});
|
|
2503
1679
|
let authChannel;
|
|
@@ -2523,6 +1699,9 @@ async function getDevframeRpcClient(options = {}) {
|
|
|
2523
1699
|
get connectionError() {
|
|
2524
1700
|
return mode.connectionError;
|
|
2525
1701
|
},
|
|
1702
|
+
get transport() {
|
|
1703
|
+
return mode.transport ?? transport;
|
|
1704
|
+
},
|
|
2526
1705
|
get connection() {
|
|
2527
1706
|
return connection;
|
|
2528
1707
|
},
|
|
@@ -2560,7 +1739,8 @@ async function getDevframeRpcClient(options = {}) {
|
|
|
2560
1739
|
sharedState: void 0,
|
|
2561
1740
|
streaming: void 0,
|
|
2562
1741
|
cacheManager,
|
|
2563
|
-
scope: void 0
|
|
1742
|
+
scope: void 0,
|
|
1743
|
+
close: () => mode.close?.()
|
|
2564
1744
|
};
|
|
2565
1745
|
rpc.sharedState = createRpcSharedStateClientHost(rpc);
|
|
2566
1746
|
rpc.streaming = createRpcStreamingClientHost(rpc);
|
|
@@ -2614,4 +1794,4 @@ async function getDevframeRpcClient(options = {}) {
|
|
|
2614
1794
|
//#region src/client/index.ts
|
|
2615
1795
|
const connectDevframe = getDevframeRpcClient;
|
|
2616
1796
|
//#endregion
|
|
2617
|
-
export { DevframeConnectionError, authenticateWithUrlOtp, connectDevframe, consumeOtpFromUrl, createClientSettings, createRpcStreamingClientHost, createScopedClientContext, getDevframeConnection, getDevframeRpcClient, isCallableStatus, readOtpFromUrl, registerDevframeViewerOrigin, resolveWsUrl, setupDevframeConnection };
|
|
1797
|
+
export { DevframeConnectionError, authenticateWithUrlOtp, connectDevframe, consumeOtpFromUrl, createClientSettings, createRpcStreamingClientHost, createScopedClientContext, getDevframeConnection, getDevframeRpcClient, isCallableStatus, readOtpFromUrl, registerDevframeViewerOrigin, resolveClientTransport, resolveSseUrl, resolveWsUrl, setupDevframeConnection };
|