@camstack/addon-pipeline-orchestrator 0.1.17 → 0.1.20
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/@mf-types/compiled-types/widgets/motion-zones/MotionGridCanvas.d.ts +2 -12
- package/dist/@mf-types/compiled-types/widgets/motion-zones/MotionGridCanvas.d.ts.map +1 -1
- package/dist/@mf-types/compiled-types/widgets/motion-zones/MotionZonesTab.d.ts.map +1 -1
- package/dist/@mf-types.zip +0 -0
- package/dist/ReactKonva--rywLr1Y.mjs +7267 -0
- package/dist/_CoreInternals-B7PHssO3.mjs +3808 -0
- package/dist/__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_sdk__loadShare__.mjs-h5aXOPSA.mjs +12 -0
- package/dist/__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.mjs-UNj4rttw.mjs +20 -0
- package/dist/__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.mjs-DPoup41Y.mjs +34 -0
- package/dist/__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.mjs-DoWbefqS.mjs +104 -0
- package/dist/__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_trpc_mf_1_client__loadShare__.mjs-D4eEXltm.mjs +85 -0
- package/dist/__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_trpc_mf_1_react_mf_2_query__loadShare__.mjs-CVrnrGED.mjs +62 -0
- package/dist/__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare__konva__loadShare__.mjs-C4PYo-VP.mjs +30 -0
- package/dist/__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare__react__loadShare__.mjs-0qpbQxoV.mjs +88 -0
- package/dist/__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare__react__loadShare__.mjs_commonjs-proxy-DekuE8px.mjs +29 -0
- package/dist/__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.mjs-Cg6QsnjR.mjs +36 -0
- package/dist/__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare__react_mf_2_dom__loadShare__.mjs-Dp8hqYOB.mjs +45 -0
- package/dist/__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare__react_mf_2_dom__loadShare__.mjs_commonjs-proxy-Ba_7PYkj.mjs +6 -0
- package/dist/__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare__react_mf_2_dom_mf_1_client__loadShare__.mjs-BBmNf5hf.mjs +34 -0
- package/dist/__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare__react_mf_2_konva__loadShare__.mjs-DSZIXeAx.mjs +64 -0
- package/dist/_stub.js +1079 -21518
- package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-D4U9-5a3.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-GASHflbS.mjs} +75 -66
- package/dist/client-CzjQ3uuI.mjs +9836 -0
- package/dist/getErrorShape-BPSzUA7W-TlK8ipWe.mjs +211 -0
- package/dist/hostInit-s8NZVmrk.mjs +196 -0
- package/dist/index-BIlr4dIX.mjs +1655 -0
- package/dist/index-BP1Nti7b.mjs +13431 -0
- package/dist/index-C1DnrJuR.mjs +2603 -0
- package/dist/index-C52WDzRJ.mjs +3842 -0
- package/dist/index-CMke0KpS.mjs +20972 -0
- package/dist/index-CWkKuNLr.mjs +232 -0
- package/dist/index-Cbqs9uJn.mjs +725 -0
- package/dist/index-DOuehnyb.mjs +185 -0
- package/dist/index-DaulYonp.mjs +435 -0
- package/dist/index-xncRG7-x.mjs +2713 -0
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6 -1
- package/dist/index.mjs.map +1 -1
- package/dist/jsx-runtime-DACJhJOv.mjs +55 -0
- package/dist/remoteEntry.js +2327 -3351
- package/dist/schemas-ChN4Ih0h.mjs +3584 -0
- package/dist/virtualExposes-8FzWTdq3.mjs +42 -0
- package/package.json +1 -1
- package/dist/@mf-types/compiled-types/widgets/motion-zones/MotionZonesOverlay.d.ts +0 -14
- package/dist/@mf-types/compiled-types/widgets/motion-zones/MotionZonesOverlay.d.ts.map +0 -1
- package/dist/__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_sdk__loadShare__.mjs-Dn6pffG6.mjs +0 -14
- package/dist/__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.mjs-DrteYOPu.mjs +0 -22
- package/dist/__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.mjs-DfNJpPfu.mjs +0 -36
- package/dist/__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.mjs-CaQq3kr4.mjs +0 -112
- package/dist/__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_trpc_mf_1_client__loadShare__.mjs-DhUh9Qzt.mjs +0 -93
- package/dist/__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_trpc_mf_1_react_mf_2_query__loadShare__.mjs-DOHfklLX.mjs +0 -70
- package/dist/__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare__react__loadShare__.mjs-BX5snCRm.mjs +0 -96
- package/dist/__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare__react__loadShare__.mjs_commonjs-proxy-Bg2N8TAe.mjs +0 -34
- package/dist/__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.mjs-CO0PugXp.mjs +0 -44
- package/dist/__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare__react_mf_2_dom__loadShare__.mjs-Cqpb8kvp.mjs +0 -53
- package/dist/__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare__react_mf_2_dom__loadShare__.mjs_commonjs-proxy-DnIwYUrp.mjs +0 -6
- package/dist/__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare__react_mf_2_dom_mf_1_client__loadShare__.mjs-Iv-aGQ4k.mjs +0 -42
- package/dist/client-TtgJQfbd.mjs +0 -11569
- package/dist/getErrorShape-BPSzUA7W-D5KpHcIj.mjs +0 -244
- package/dist/hostInit-C89SX6z4.mjs +0 -198
- package/dist/index-0-mfXOlq.mjs +0 -899
- package/dist/index-BKVjF_OI.mjs +0 -14804
- package/dist/index-BN2rV_KJ.mjs +0 -463
- package/dist/index-BepH9mzJ.mjs +0 -3536
- package/dist/index-BxLvm_gu.mjs +0 -267
- package/dist/index-CVOLs7Aq.mjs +0 -24402
- package/dist/index-Cfo7sGh_.mjs +0 -2054
- package/dist/index-CrGKNQoE.mjs +0 -3260
- package/dist/index-sCAUpX2M.mjs +0 -197
- package/dist/jsx-runtime-D2w1zDMK.mjs +0 -67
- package/dist/schemas-DihhZLrP.mjs +0 -5087
- package/dist/virtualExposes-D_9EJnra.mjs +0 -59
package/dist/index-Cfo7sGh_.mjs
DELETED
|
@@ -1,2054 +0,0 @@
|
|
|
1
|
-
import { i as __mf_7, b as __mf_3, A as __mf_26, n as __mf_13 } from "./__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_trpc_mf_1_client__loadShare__.mjs-DhUh9Qzt.mjs";
|
|
2
|
-
import { a as __mf_429, b as __mf_411 } from "./__mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.mjs-DrteYOPu.mjs";
|
|
3
|
-
class DoubleIndexedKV {
|
|
4
|
-
constructor() {
|
|
5
|
-
this.keyToValue = /* @__PURE__ */ new Map();
|
|
6
|
-
this.valueToKey = /* @__PURE__ */ new Map();
|
|
7
|
-
}
|
|
8
|
-
set(key, value) {
|
|
9
|
-
this.keyToValue.set(key, value);
|
|
10
|
-
this.valueToKey.set(value, key);
|
|
11
|
-
}
|
|
12
|
-
getByKey(key) {
|
|
13
|
-
return this.keyToValue.get(key);
|
|
14
|
-
}
|
|
15
|
-
getByValue(value) {
|
|
16
|
-
return this.valueToKey.get(value);
|
|
17
|
-
}
|
|
18
|
-
clear() {
|
|
19
|
-
this.keyToValue.clear();
|
|
20
|
-
this.valueToKey.clear();
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
class Registry {
|
|
24
|
-
constructor(generateIdentifier) {
|
|
25
|
-
this.generateIdentifier = generateIdentifier;
|
|
26
|
-
this.kv = new DoubleIndexedKV();
|
|
27
|
-
}
|
|
28
|
-
register(value, identifier) {
|
|
29
|
-
if (this.kv.getByValue(value)) {
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
if (!identifier) {
|
|
33
|
-
identifier = this.generateIdentifier(value);
|
|
34
|
-
}
|
|
35
|
-
this.kv.set(identifier, value);
|
|
36
|
-
}
|
|
37
|
-
clear() {
|
|
38
|
-
this.kv.clear();
|
|
39
|
-
}
|
|
40
|
-
getIdentifier(value) {
|
|
41
|
-
return this.kv.getByValue(value);
|
|
42
|
-
}
|
|
43
|
-
getValue(identifier) {
|
|
44
|
-
return this.kv.getByKey(identifier);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
class ClassRegistry extends Registry {
|
|
48
|
-
constructor() {
|
|
49
|
-
super((c) => c.name);
|
|
50
|
-
this.classToAllowedProps = /* @__PURE__ */ new Map();
|
|
51
|
-
}
|
|
52
|
-
register(value, options) {
|
|
53
|
-
if (typeof options === "object") {
|
|
54
|
-
if (options.allowProps) {
|
|
55
|
-
this.classToAllowedProps.set(value, options.allowProps);
|
|
56
|
-
}
|
|
57
|
-
super.register(value, options.identifier);
|
|
58
|
-
} else {
|
|
59
|
-
super.register(value, options);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
getAllowedProps(value) {
|
|
63
|
-
return this.classToAllowedProps.get(value);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
function valuesOfObj(record) {
|
|
67
|
-
if ("values" in Object) {
|
|
68
|
-
return Object.values(record);
|
|
69
|
-
}
|
|
70
|
-
const values = [];
|
|
71
|
-
for (const key in record) {
|
|
72
|
-
if (record.hasOwnProperty(key)) {
|
|
73
|
-
values.push(record[key]);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return values;
|
|
77
|
-
}
|
|
78
|
-
function find(record, predicate) {
|
|
79
|
-
const values = valuesOfObj(record);
|
|
80
|
-
if ("find" in values) {
|
|
81
|
-
return values.find(predicate);
|
|
82
|
-
}
|
|
83
|
-
const valuesNotNever = values;
|
|
84
|
-
for (let i = 0; i < valuesNotNever.length; i++) {
|
|
85
|
-
const value = valuesNotNever[i];
|
|
86
|
-
if (predicate(value)) {
|
|
87
|
-
return value;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
return void 0;
|
|
91
|
-
}
|
|
92
|
-
function forEach(record, run) {
|
|
93
|
-
Object.entries(record).forEach(([key, value]) => run(value, key));
|
|
94
|
-
}
|
|
95
|
-
function includes(arr, value) {
|
|
96
|
-
return arr.indexOf(value) !== -1;
|
|
97
|
-
}
|
|
98
|
-
function findArr(record, predicate) {
|
|
99
|
-
for (let i = 0; i < record.length; i++) {
|
|
100
|
-
const value = record[i];
|
|
101
|
-
if (predicate(value)) {
|
|
102
|
-
return value;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
return void 0;
|
|
106
|
-
}
|
|
107
|
-
class CustomTransformerRegistry {
|
|
108
|
-
constructor() {
|
|
109
|
-
this.transfomers = {};
|
|
110
|
-
}
|
|
111
|
-
register(transformer) {
|
|
112
|
-
this.transfomers[transformer.name] = transformer;
|
|
113
|
-
}
|
|
114
|
-
findApplicable(v) {
|
|
115
|
-
return find(this.transfomers, (transformer) => transformer.isApplicable(v));
|
|
116
|
-
}
|
|
117
|
-
findByName(name) {
|
|
118
|
-
return this.transfomers[name];
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
const getType$1 = (payload) => Object.prototype.toString.call(payload).slice(8, -1);
|
|
122
|
-
const isUndefined = (payload) => typeof payload === "undefined";
|
|
123
|
-
const isNull = (payload) => payload === null;
|
|
124
|
-
const isPlainObject$1 = (payload) => {
|
|
125
|
-
if (typeof payload !== "object" || payload === null)
|
|
126
|
-
return false;
|
|
127
|
-
if (payload === Object.prototype)
|
|
128
|
-
return false;
|
|
129
|
-
if (Object.getPrototypeOf(payload) === null)
|
|
130
|
-
return true;
|
|
131
|
-
return Object.getPrototypeOf(payload) === Object.prototype;
|
|
132
|
-
};
|
|
133
|
-
const isEmptyObject = (payload) => isPlainObject$1(payload) && Object.keys(payload).length === 0;
|
|
134
|
-
const isArray$1 = (payload) => Array.isArray(payload);
|
|
135
|
-
const isString = (payload) => typeof payload === "string";
|
|
136
|
-
const isNumber = (payload) => typeof payload === "number" && !isNaN(payload);
|
|
137
|
-
const isBoolean = (payload) => typeof payload === "boolean";
|
|
138
|
-
const isRegExp = (payload) => payload instanceof RegExp;
|
|
139
|
-
const isMap = (payload) => payload instanceof Map;
|
|
140
|
-
const isSet = (payload) => payload instanceof Set;
|
|
141
|
-
const isSymbol = (payload) => getType$1(payload) === "Symbol";
|
|
142
|
-
const isDate = (payload) => payload instanceof Date && !isNaN(payload.valueOf());
|
|
143
|
-
const isError = (payload) => payload instanceof Error;
|
|
144
|
-
const isNaNValue = (payload) => typeof payload === "number" && isNaN(payload);
|
|
145
|
-
const isPrimitive = (payload) => isBoolean(payload) || isNull(payload) || isUndefined(payload) || isNumber(payload) || isString(payload) || isSymbol(payload);
|
|
146
|
-
const isBigint = (payload) => typeof payload === "bigint";
|
|
147
|
-
const isInfinite = (payload) => payload === Infinity || payload === -Infinity;
|
|
148
|
-
const isTypedArray = (payload) => ArrayBuffer.isView(payload) && !(payload instanceof DataView);
|
|
149
|
-
const isURL = (payload) => payload instanceof URL;
|
|
150
|
-
const escapeKey = (key) => key.replace(/\\/g, "\\\\").replace(/\./g, "\\.");
|
|
151
|
-
const stringifyPath = (path) => path.map(String).map(escapeKey).join(".");
|
|
152
|
-
const parsePath = (string, legacyPaths) => {
|
|
153
|
-
const result = [];
|
|
154
|
-
let segment = "";
|
|
155
|
-
for (let i = 0; i < string.length; i++) {
|
|
156
|
-
let char = string.charAt(i);
|
|
157
|
-
if (!legacyPaths && char === "\\") {
|
|
158
|
-
const escaped = string.charAt(i + 1);
|
|
159
|
-
if (escaped === "\\") {
|
|
160
|
-
segment += "\\";
|
|
161
|
-
i++;
|
|
162
|
-
continue;
|
|
163
|
-
} else if (escaped !== ".") {
|
|
164
|
-
throw Error("invalid path");
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
const isEscapedDot = char === "\\" && string.charAt(i + 1) === ".";
|
|
168
|
-
if (isEscapedDot) {
|
|
169
|
-
segment += ".";
|
|
170
|
-
i++;
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
const isEndOfSegment = char === ".";
|
|
174
|
-
if (isEndOfSegment) {
|
|
175
|
-
result.push(segment);
|
|
176
|
-
segment = "";
|
|
177
|
-
continue;
|
|
178
|
-
}
|
|
179
|
-
segment += char;
|
|
180
|
-
}
|
|
181
|
-
const lastSegment = segment;
|
|
182
|
-
result.push(lastSegment);
|
|
183
|
-
return result;
|
|
184
|
-
};
|
|
185
|
-
function simpleTransformation(isApplicable, annotation, transform, untransform) {
|
|
186
|
-
return {
|
|
187
|
-
isApplicable,
|
|
188
|
-
annotation,
|
|
189
|
-
transform,
|
|
190
|
-
untransform
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
const simpleRules = [
|
|
194
|
-
simpleTransformation(isUndefined, "undefined", () => null, () => void 0),
|
|
195
|
-
simpleTransformation(isBigint, "bigint", (v) => v.toString(), (v) => {
|
|
196
|
-
if (typeof BigInt !== "undefined") {
|
|
197
|
-
return BigInt(v);
|
|
198
|
-
}
|
|
199
|
-
console.error("Please add a BigInt polyfill.");
|
|
200
|
-
return v;
|
|
201
|
-
}),
|
|
202
|
-
simpleTransformation(isDate, "Date", (v) => v.toISOString(), (v) => new Date(v)),
|
|
203
|
-
simpleTransformation(isError, "Error", (v, superJson) => {
|
|
204
|
-
const baseError = {
|
|
205
|
-
name: v.name,
|
|
206
|
-
message: v.message
|
|
207
|
-
};
|
|
208
|
-
if ("cause" in v) {
|
|
209
|
-
baseError.cause = v.cause;
|
|
210
|
-
}
|
|
211
|
-
superJson.allowedErrorProps.forEach((prop) => {
|
|
212
|
-
baseError[prop] = v[prop];
|
|
213
|
-
});
|
|
214
|
-
return baseError;
|
|
215
|
-
}, (v, superJson) => {
|
|
216
|
-
const e = new Error(v.message, { cause: v.cause });
|
|
217
|
-
e.name = v.name;
|
|
218
|
-
e.stack = v.stack;
|
|
219
|
-
superJson.allowedErrorProps.forEach((prop) => {
|
|
220
|
-
e[prop] = v[prop];
|
|
221
|
-
});
|
|
222
|
-
return e;
|
|
223
|
-
}),
|
|
224
|
-
simpleTransformation(isRegExp, "regexp", (v) => "" + v, (regex) => {
|
|
225
|
-
const body = regex.slice(1, regex.lastIndexOf("/"));
|
|
226
|
-
const flags = regex.slice(regex.lastIndexOf("/") + 1);
|
|
227
|
-
return new RegExp(body, flags);
|
|
228
|
-
}),
|
|
229
|
-
simpleTransformation(
|
|
230
|
-
isSet,
|
|
231
|
-
"set",
|
|
232
|
-
// (sets only exist in es6+)
|
|
233
|
-
// eslint-disable-next-line es5/no-es6-methods
|
|
234
|
-
(v) => [...v.values()],
|
|
235
|
-
(v) => new Set(v)
|
|
236
|
-
),
|
|
237
|
-
simpleTransformation(isMap, "map", (v) => [...v.entries()], (v) => new Map(v)),
|
|
238
|
-
simpleTransformation((v) => isNaNValue(v) || isInfinite(v), "number", (v) => {
|
|
239
|
-
if (isNaNValue(v)) {
|
|
240
|
-
return "NaN";
|
|
241
|
-
}
|
|
242
|
-
if (v > 0) {
|
|
243
|
-
return "Infinity";
|
|
244
|
-
} else {
|
|
245
|
-
return "-Infinity";
|
|
246
|
-
}
|
|
247
|
-
}, Number),
|
|
248
|
-
simpleTransformation((v) => v === 0 && 1 / v === -Infinity, "number", () => {
|
|
249
|
-
return "-0";
|
|
250
|
-
}, Number),
|
|
251
|
-
simpleTransformation(isURL, "URL", (v) => v.toString(), (v) => new URL(v))
|
|
252
|
-
];
|
|
253
|
-
function compositeTransformation(isApplicable, annotation, transform, untransform) {
|
|
254
|
-
return {
|
|
255
|
-
isApplicable,
|
|
256
|
-
annotation,
|
|
257
|
-
transform,
|
|
258
|
-
untransform
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
const symbolRule = compositeTransformation((s, superJson) => {
|
|
262
|
-
if (isSymbol(s)) {
|
|
263
|
-
const isRegistered = !!superJson.symbolRegistry.getIdentifier(s);
|
|
264
|
-
return isRegistered;
|
|
265
|
-
}
|
|
266
|
-
return false;
|
|
267
|
-
}, (s, superJson) => {
|
|
268
|
-
const identifier = superJson.symbolRegistry.getIdentifier(s);
|
|
269
|
-
return ["symbol", identifier];
|
|
270
|
-
}, (v) => v.description, (_, a, superJson) => {
|
|
271
|
-
const value = superJson.symbolRegistry.getValue(a[1]);
|
|
272
|
-
if (!value) {
|
|
273
|
-
throw new Error("Trying to deserialize unknown symbol");
|
|
274
|
-
}
|
|
275
|
-
return value;
|
|
276
|
-
});
|
|
277
|
-
const constructorToName = [
|
|
278
|
-
Int8Array,
|
|
279
|
-
Uint8Array,
|
|
280
|
-
Int16Array,
|
|
281
|
-
Uint16Array,
|
|
282
|
-
Int32Array,
|
|
283
|
-
Uint32Array,
|
|
284
|
-
Float32Array,
|
|
285
|
-
Float64Array,
|
|
286
|
-
Uint8ClampedArray
|
|
287
|
-
].reduce((obj, ctor) => {
|
|
288
|
-
obj[ctor.name] = ctor;
|
|
289
|
-
return obj;
|
|
290
|
-
}, {});
|
|
291
|
-
const typedArrayRule = compositeTransformation(isTypedArray, (v) => ["typed-array", v.constructor.name], (v) => [...v], (v, a) => {
|
|
292
|
-
const ctor = constructorToName[a[1]];
|
|
293
|
-
if (!ctor) {
|
|
294
|
-
throw new Error("Trying to deserialize unknown typed array");
|
|
295
|
-
}
|
|
296
|
-
return new ctor(v);
|
|
297
|
-
});
|
|
298
|
-
function isInstanceOfRegisteredClass(potentialClass, superJson) {
|
|
299
|
-
if (potentialClass?.constructor) {
|
|
300
|
-
const isRegistered = !!superJson.classRegistry.getIdentifier(potentialClass.constructor);
|
|
301
|
-
return isRegistered;
|
|
302
|
-
}
|
|
303
|
-
return false;
|
|
304
|
-
}
|
|
305
|
-
const classRule = compositeTransformation(isInstanceOfRegisteredClass, (clazz, superJson) => {
|
|
306
|
-
const identifier = superJson.classRegistry.getIdentifier(clazz.constructor);
|
|
307
|
-
return ["class", identifier];
|
|
308
|
-
}, (clazz, superJson) => {
|
|
309
|
-
const allowedProps = superJson.classRegistry.getAllowedProps(clazz.constructor);
|
|
310
|
-
if (!allowedProps) {
|
|
311
|
-
return { ...clazz };
|
|
312
|
-
}
|
|
313
|
-
const result = {};
|
|
314
|
-
allowedProps.forEach((prop) => {
|
|
315
|
-
result[prop] = clazz[prop];
|
|
316
|
-
});
|
|
317
|
-
return result;
|
|
318
|
-
}, (v, a, superJson) => {
|
|
319
|
-
const clazz = superJson.classRegistry.getValue(a[1]);
|
|
320
|
-
if (!clazz) {
|
|
321
|
-
throw new Error(`Trying to deserialize unknown class '${a[1]}' - check https://github.com/blitz-js/superjson/issues/116#issuecomment-773996564`);
|
|
322
|
-
}
|
|
323
|
-
return Object.assign(Object.create(clazz.prototype), v);
|
|
324
|
-
});
|
|
325
|
-
const customRule = compositeTransformation((value, superJson) => {
|
|
326
|
-
return !!superJson.customTransformerRegistry.findApplicable(value);
|
|
327
|
-
}, (value, superJson) => {
|
|
328
|
-
const transformer = superJson.customTransformerRegistry.findApplicable(value);
|
|
329
|
-
return ["custom", transformer.name];
|
|
330
|
-
}, (value, superJson) => {
|
|
331
|
-
const transformer = superJson.customTransformerRegistry.findApplicable(value);
|
|
332
|
-
return transformer.serialize(value);
|
|
333
|
-
}, (v, a, superJson) => {
|
|
334
|
-
const transformer = superJson.customTransformerRegistry.findByName(a[1]);
|
|
335
|
-
if (!transformer) {
|
|
336
|
-
throw new Error("Trying to deserialize unknown custom value");
|
|
337
|
-
}
|
|
338
|
-
return transformer.deserialize(v);
|
|
339
|
-
});
|
|
340
|
-
const compositeRules = [classRule, symbolRule, customRule, typedArrayRule];
|
|
341
|
-
const transformValue = (value, superJson) => {
|
|
342
|
-
const applicableCompositeRule = findArr(compositeRules, (rule) => rule.isApplicable(value, superJson));
|
|
343
|
-
if (applicableCompositeRule) {
|
|
344
|
-
return {
|
|
345
|
-
value: applicableCompositeRule.transform(value, superJson),
|
|
346
|
-
type: applicableCompositeRule.annotation(value, superJson)
|
|
347
|
-
};
|
|
348
|
-
}
|
|
349
|
-
const applicableSimpleRule = findArr(simpleRules, (rule) => rule.isApplicable(value, superJson));
|
|
350
|
-
if (applicableSimpleRule) {
|
|
351
|
-
return {
|
|
352
|
-
value: applicableSimpleRule.transform(value, superJson),
|
|
353
|
-
type: applicableSimpleRule.annotation
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
return void 0;
|
|
357
|
-
};
|
|
358
|
-
const simpleRulesByAnnotation = {};
|
|
359
|
-
simpleRules.forEach((rule) => {
|
|
360
|
-
simpleRulesByAnnotation[rule.annotation] = rule;
|
|
361
|
-
});
|
|
362
|
-
const untransformValue = (json, type, superJson) => {
|
|
363
|
-
if (isArray$1(type)) {
|
|
364
|
-
switch (type[0]) {
|
|
365
|
-
case "symbol":
|
|
366
|
-
return symbolRule.untransform(json, type, superJson);
|
|
367
|
-
case "class":
|
|
368
|
-
return classRule.untransform(json, type, superJson);
|
|
369
|
-
case "custom":
|
|
370
|
-
return customRule.untransform(json, type, superJson);
|
|
371
|
-
case "typed-array":
|
|
372
|
-
return typedArrayRule.untransform(json, type, superJson);
|
|
373
|
-
default:
|
|
374
|
-
throw new Error("Unknown transformation: " + type);
|
|
375
|
-
}
|
|
376
|
-
} else {
|
|
377
|
-
const transformation = simpleRulesByAnnotation[type];
|
|
378
|
-
if (!transformation) {
|
|
379
|
-
throw new Error("Unknown transformation: " + type);
|
|
380
|
-
}
|
|
381
|
-
return transformation.untransform(json, superJson);
|
|
382
|
-
}
|
|
383
|
-
};
|
|
384
|
-
const getNthKey = (value, n) => {
|
|
385
|
-
if (n > value.size)
|
|
386
|
-
throw new Error("index out of bounds");
|
|
387
|
-
const keys = value.keys();
|
|
388
|
-
while (n > 0) {
|
|
389
|
-
keys.next();
|
|
390
|
-
n--;
|
|
391
|
-
}
|
|
392
|
-
return keys.next().value;
|
|
393
|
-
};
|
|
394
|
-
function validatePath(path) {
|
|
395
|
-
if (includes(path, "__proto__")) {
|
|
396
|
-
throw new Error("__proto__ is not allowed as a property");
|
|
397
|
-
}
|
|
398
|
-
if (includes(path, "prototype")) {
|
|
399
|
-
throw new Error("prototype is not allowed as a property");
|
|
400
|
-
}
|
|
401
|
-
if (includes(path, "constructor")) {
|
|
402
|
-
throw new Error("constructor is not allowed as a property");
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
const getDeep = (object, path) => {
|
|
406
|
-
validatePath(path);
|
|
407
|
-
for (let i = 0; i < path.length; i++) {
|
|
408
|
-
const key = path[i];
|
|
409
|
-
if (isSet(object)) {
|
|
410
|
-
object = getNthKey(object, +key);
|
|
411
|
-
} else if (isMap(object)) {
|
|
412
|
-
const row = +key;
|
|
413
|
-
const type = +path[++i] === 0 ? "key" : "value";
|
|
414
|
-
const keyOfRow = getNthKey(object, row);
|
|
415
|
-
switch (type) {
|
|
416
|
-
case "key":
|
|
417
|
-
object = keyOfRow;
|
|
418
|
-
break;
|
|
419
|
-
case "value":
|
|
420
|
-
object = object.get(keyOfRow);
|
|
421
|
-
break;
|
|
422
|
-
}
|
|
423
|
-
} else {
|
|
424
|
-
object = object[key];
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
return object;
|
|
428
|
-
};
|
|
429
|
-
const setDeep = (object, path, mapper) => {
|
|
430
|
-
validatePath(path);
|
|
431
|
-
if (path.length === 0) {
|
|
432
|
-
return mapper(object);
|
|
433
|
-
}
|
|
434
|
-
let parent = object;
|
|
435
|
-
for (let i = 0; i < path.length - 1; i++) {
|
|
436
|
-
const key = path[i];
|
|
437
|
-
if (isArray$1(parent)) {
|
|
438
|
-
const index = +key;
|
|
439
|
-
parent = parent[index];
|
|
440
|
-
} else if (isPlainObject$1(parent)) {
|
|
441
|
-
parent = parent[key];
|
|
442
|
-
} else if (isSet(parent)) {
|
|
443
|
-
const row = +key;
|
|
444
|
-
parent = getNthKey(parent, row);
|
|
445
|
-
} else if (isMap(parent)) {
|
|
446
|
-
const isEnd = i === path.length - 2;
|
|
447
|
-
if (isEnd) {
|
|
448
|
-
break;
|
|
449
|
-
}
|
|
450
|
-
const row = +key;
|
|
451
|
-
const type = +path[++i] === 0 ? "key" : "value";
|
|
452
|
-
const keyOfRow = getNthKey(parent, row);
|
|
453
|
-
switch (type) {
|
|
454
|
-
case "key":
|
|
455
|
-
parent = keyOfRow;
|
|
456
|
-
break;
|
|
457
|
-
case "value":
|
|
458
|
-
parent = parent.get(keyOfRow);
|
|
459
|
-
break;
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
const lastKey = path[path.length - 1];
|
|
464
|
-
if (isArray$1(parent)) {
|
|
465
|
-
parent[+lastKey] = mapper(parent[+lastKey]);
|
|
466
|
-
} else if (isPlainObject$1(parent)) {
|
|
467
|
-
parent[lastKey] = mapper(parent[lastKey]);
|
|
468
|
-
}
|
|
469
|
-
if (isSet(parent)) {
|
|
470
|
-
const oldValue = getNthKey(parent, +lastKey);
|
|
471
|
-
const newValue = mapper(oldValue);
|
|
472
|
-
if (oldValue !== newValue) {
|
|
473
|
-
parent.delete(oldValue);
|
|
474
|
-
parent.add(newValue);
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
if (isMap(parent)) {
|
|
478
|
-
const row = +path[path.length - 2];
|
|
479
|
-
const keyToRow = getNthKey(parent, row);
|
|
480
|
-
const type = +lastKey === 0 ? "key" : "value";
|
|
481
|
-
switch (type) {
|
|
482
|
-
case "key": {
|
|
483
|
-
const newKey = mapper(keyToRow);
|
|
484
|
-
parent.set(newKey, parent.get(keyToRow));
|
|
485
|
-
if (newKey !== keyToRow) {
|
|
486
|
-
parent.delete(keyToRow);
|
|
487
|
-
}
|
|
488
|
-
break;
|
|
489
|
-
}
|
|
490
|
-
case "value": {
|
|
491
|
-
parent.set(keyToRow, mapper(parent.get(keyToRow)));
|
|
492
|
-
break;
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
return object;
|
|
497
|
-
};
|
|
498
|
-
const enableLegacyPaths = (version) => version < 1;
|
|
499
|
-
function traverse(tree, walker2, version, origin = []) {
|
|
500
|
-
if (!tree) {
|
|
501
|
-
return;
|
|
502
|
-
}
|
|
503
|
-
const legacyPaths = enableLegacyPaths(version);
|
|
504
|
-
if (!isArray$1(tree)) {
|
|
505
|
-
forEach(tree, (subtree, key) => traverse(subtree, walker2, version, [
|
|
506
|
-
...origin,
|
|
507
|
-
...parsePath(key, legacyPaths)
|
|
508
|
-
]));
|
|
509
|
-
return;
|
|
510
|
-
}
|
|
511
|
-
const [nodeValue, children] = tree;
|
|
512
|
-
if (children) {
|
|
513
|
-
forEach(children, (child, key) => {
|
|
514
|
-
traverse(child, walker2, version, [
|
|
515
|
-
...origin,
|
|
516
|
-
...parsePath(key, legacyPaths)
|
|
517
|
-
]);
|
|
518
|
-
});
|
|
519
|
-
}
|
|
520
|
-
walker2(nodeValue, origin);
|
|
521
|
-
}
|
|
522
|
-
function applyValueAnnotations(plain, annotations, version, superJson) {
|
|
523
|
-
traverse(annotations, (type, path) => {
|
|
524
|
-
plain = setDeep(plain, path, (v) => untransformValue(v, type, superJson));
|
|
525
|
-
}, version);
|
|
526
|
-
return plain;
|
|
527
|
-
}
|
|
528
|
-
function applyReferentialEqualityAnnotations(plain, annotations, version) {
|
|
529
|
-
const legacyPaths = enableLegacyPaths(version);
|
|
530
|
-
function apply(identicalPaths, path) {
|
|
531
|
-
const object = getDeep(plain, parsePath(path, legacyPaths));
|
|
532
|
-
identicalPaths.map((path2) => parsePath(path2, legacyPaths)).forEach((identicalObjectPath) => {
|
|
533
|
-
plain = setDeep(plain, identicalObjectPath, () => object);
|
|
534
|
-
});
|
|
535
|
-
}
|
|
536
|
-
if (isArray$1(annotations)) {
|
|
537
|
-
const [root, other] = annotations;
|
|
538
|
-
root.forEach((identicalPath) => {
|
|
539
|
-
plain = setDeep(plain, parsePath(identicalPath, legacyPaths), () => plain);
|
|
540
|
-
});
|
|
541
|
-
if (other) {
|
|
542
|
-
forEach(other, apply);
|
|
543
|
-
}
|
|
544
|
-
} else {
|
|
545
|
-
forEach(annotations, apply);
|
|
546
|
-
}
|
|
547
|
-
return plain;
|
|
548
|
-
}
|
|
549
|
-
const isDeep = (object, superJson) => isPlainObject$1(object) || isArray$1(object) || isMap(object) || isSet(object) || isError(object) || isInstanceOfRegisteredClass(object, superJson);
|
|
550
|
-
function addIdentity(object, path, identities) {
|
|
551
|
-
const existingSet = identities.get(object);
|
|
552
|
-
if (existingSet) {
|
|
553
|
-
existingSet.push(path);
|
|
554
|
-
} else {
|
|
555
|
-
identities.set(object, [path]);
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
function generateReferentialEqualityAnnotations(identitites, dedupe) {
|
|
559
|
-
const result = {};
|
|
560
|
-
let rootEqualityPaths = void 0;
|
|
561
|
-
identitites.forEach((paths) => {
|
|
562
|
-
if (paths.length <= 1) {
|
|
563
|
-
return;
|
|
564
|
-
}
|
|
565
|
-
if (!dedupe) {
|
|
566
|
-
paths = paths.map((path) => path.map(String)).sort((a, b) => a.length - b.length);
|
|
567
|
-
}
|
|
568
|
-
const [representativePath, ...identicalPaths] = paths;
|
|
569
|
-
if (representativePath.length === 0) {
|
|
570
|
-
rootEqualityPaths = identicalPaths.map(stringifyPath);
|
|
571
|
-
} else {
|
|
572
|
-
result[stringifyPath(representativePath)] = identicalPaths.map(stringifyPath);
|
|
573
|
-
}
|
|
574
|
-
});
|
|
575
|
-
if (rootEqualityPaths) {
|
|
576
|
-
if (isEmptyObject(result)) {
|
|
577
|
-
return [rootEqualityPaths];
|
|
578
|
-
} else {
|
|
579
|
-
return [rootEqualityPaths, result];
|
|
580
|
-
}
|
|
581
|
-
} else {
|
|
582
|
-
return isEmptyObject(result) ? void 0 : result;
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
const walker = (object, identities, superJson, dedupe, path = [], objectsInThisPath = [], seenObjects = /* @__PURE__ */ new Map()) => {
|
|
586
|
-
const primitive = isPrimitive(object);
|
|
587
|
-
if (!primitive) {
|
|
588
|
-
addIdentity(object, path, identities);
|
|
589
|
-
const seen = seenObjects.get(object);
|
|
590
|
-
if (seen) {
|
|
591
|
-
return dedupe ? {
|
|
592
|
-
transformedValue: null
|
|
593
|
-
} : seen;
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
if (!isDeep(object, superJson)) {
|
|
597
|
-
const transformed2 = transformValue(object, superJson);
|
|
598
|
-
const result2 = transformed2 ? {
|
|
599
|
-
transformedValue: transformed2.value,
|
|
600
|
-
annotations: [transformed2.type]
|
|
601
|
-
} : {
|
|
602
|
-
transformedValue: object
|
|
603
|
-
};
|
|
604
|
-
if (!primitive) {
|
|
605
|
-
seenObjects.set(object, result2);
|
|
606
|
-
}
|
|
607
|
-
return result2;
|
|
608
|
-
}
|
|
609
|
-
if (includes(objectsInThisPath, object)) {
|
|
610
|
-
return {
|
|
611
|
-
transformedValue: null
|
|
612
|
-
};
|
|
613
|
-
}
|
|
614
|
-
const transformationResult = transformValue(object, superJson);
|
|
615
|
-
const transformed = transformationResult?.value ?? object;
|
|
616
|
-
const transformedValue = isArray$1(transformed) ? [] : {};
|
|
617
|
-
const innerAnnotations = {};
|
|
618
|
-
forEach(transformed, (value, index) => {
|
|
619
|
-
if (index === "__proto__" || index === "constructor" || index === "prototype") {
|
|
620
|
-
throw new Error(`Detected property ${index}. This is a prototype pollution risk, please remove it from your object.`);
|
|
621
|
-
}
|
|
622
|
-
const recursiveResult = walker(value, identities, superJson, dedupe, [...path, index], [...objectsInThisPath, object], seenObjects);
|
|
623
|
-
transformedValue[index] = recursiveResult.transformedValue;
|
|
624
|
-
if (isArray$1(recursiveResult.annotations)) {
|
|
625
|
-
innerAnnotations[escapeKey(index)] = recursiveResult.annotations;
|
|
626
|
-
} else if (isPlainObject$1(recursiveResult.annotations)) {
|
|
627
|
-
forEach(recursiveResult.annotations, (tree, key) => {
|
|
628
|
-
innerAnnotations[escapeKey(index) + "." + key] = tree;
|
|
629
|
-
});
|
|
630
|
-
}
|
|
631
|
-
});
|
|
632
|
-
const result = isEmptyObject(innerAnnotations) ? {
|
|
633
|
-
transformedValue,
|
|
634
|
-
annotations: !!transformationResult ? [transformationResult.type] : void 0
|
|
635
|
-
} : {
|
|
636
|
-
transformedValue,
|
|
637
|
-
annotations: !!transformationResult ? [transformationResult.type, innerAnnotations] : innerAnnotations
|
|
638
|
-
};
|
|
639
|
-
if (!primitive) {
|
|
640
|
-
seenObjects.set(object, result);
|
|
641
|
-
}
|
|
642
|
-
return result;
|
|
643
|
-
};
|
|
644
|
-
function getType(payload) {
|
|
645
|
-
return Object.prototype.toString.call(payload).slice(8, -1);
|
|
646
|
-
}
|
|
647
|
-
function isArray(payload) {
|
|
648
|
-
return getType(payload) === "Array";
|
|
649
|
-
}
|
|
650
|
-
function isPlainObject(payload) {
|
|
651
|
-
if (getType(payload) !== "Object")
|
|
652
|
-
return false;
|
|
653
|
-
const prototype = Object.getPrototypeOf(payload);
|
|
654
|
-
return !!prototype && prototype.constructor === Object && prototype === Object.prototype;
|
|
655
|
-
}
|
|
656
|
-
function assignProp(carry, key, newVal, originalObject, includeNonenumerable) {
|
|
657
|
-
const propType = {}.propertyIsEnumerable.call(originalObject, key) ? "enumerable" : "nonenumerable";
|
|
658
|
-
if (propType === "enumerable")
|
|
659
|
-
carry[key] = newVal;
|
|
660
|
-
if (includeNonenumerable && propType === "nonenumerable") {
|
|
661
|
-
Object.defineProperty(carry, key, {
|
|
662
|
-
value: newVal,
|
|
663
|
-
enumerable: false,
|
|
664
|
-
writable: true,
|
|
665
|
-
configurable: true
|
|
666
|
-
});
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
function copy(target, options = {}) {
|
|
670
|
-
if (isArray(target)) {
|
|
671
|
-
return target.map((item) => copy(item, options));
|
|
672
|
-
}
|
|
673
|
-
if (!isPlainObject(target)) {
|
|
674
|
-
return target;
|
|
675
|
-
}
|
|
676
|
-
const props = Object.getOwnPropertyNames(target);
|
|
677
|
-
const symbols = Object.getOwnPropertySymbols(target);
|
|
678
|
-
return [...props, ...symbols].reduce((carry, key) => {
|
|
679
|
-
if (key === "__proto__")
|
|
680
|
-
return carry;
|
|
681
|
-
if (isArray(options.props) && !options.props.includes(key)) {
|
|
682
|
-
return carry;
|
|
683
|
-
}
|
|
684
|
-
const val = target[key];
|
|
685
|
-
const newVal = copy(val, options);
|
|
686
|
-
assignProp(carry, key, newVal, target, options.nonenumerable);
|
|
687
|
-
return carry;
|
|
688
|
-
}, {});
|
|
689
|
-
}
|
|
690
|
-
class SuperJSON {
|
|
691
|
-
/**
|
|
692
|
-
* @param dedupeReferentialEqualities If true, SuperJSON will make sure only one instance of referentially equal objects are serialized and the rest are replaced with `null`.
|
|
693
|
-
*/
|
|
694
|
-
constructor({ dedupe = false } = {}) {
|
|
695
|
-
this.classRegistry = new ClassRegistry();
|
|
696
|
-
this.symbolRegistry = new Registry((s) => s.description ?? "");
|
|
697
|
-
this.customTransformerRegistry = new CustomTransformerRegistry();
|
|
698
|
-
this.allowedErrorProps = [];
|
|
699
|
-
this.dedupe = dedupe;
|
|
700
|
-
}
|
|
701
|
-
serialize(object) {
|
|
702
|
-
const identities = /* @__PURE__ */ new Map();
|
|
703
|
-
const output = walker(object, identities, this, this.dedupe);
|
|
704
|
-
const res = {
|
|
705
|
-
json: output.transformedValue
|
|
706
|
-
};
|
|
707
|
-
if (output.annotations) {
|
|
708
|
-
res.meta = {
|
|
709
|
-
...res.meta,
|
|
710
|
-
values: output.annotations
|
|
711
|
-
};
|
|
712
|
-
}
|
|
713
|
-
const equalityAnnotations = generateReferentialEqualityAnnotations(identities, this.dedupe);
|
|
714
|
-
if (equalityAnnotations) {
|
|
715
|
-
res.meta = {
|
|
716
|
-
...res.meta,
|
|
717
|
-
referentialEqualities: equalityAnnotations
|
|
718
|
-
};
|
|
719
|
-
}
|
|
720
|
-
if (res.meta)
|
|
721
|
-
res.meta.v = 1;
|
|
722
|
-
return res;
|
|
723
|
-
}
|
|
724
|
-
deserialize(payload, options) {
|
|
725
|
-
const { json, meta } = payload;
|
|
726
|
-
let result = options?.inPlace ? json : copy(json);
|
|
727
|
-
if (meta?.values) {
|
|
728
|
-
result = applyValueAnnotations(result, meta.values, meta.v ?? 0, this);
|
|
729
|
-
}
|
|
730
|
-
if (meta?.referentialEqualities) {
|
|
731
|
-
result = applyReferentialEqualityAnnotations(result, meta.referentialEqualities, meta.v ?? 0);
|
|
732
|
-
}
|
|
733
|
-
return result;
|
|
734
|
-
}
|
|
735
|
-
stringify(object) {
|
|
736
|
-
return JSON.stringify(this.serialize(object));
|
|
737
|
-
}
|
|
738
|
-
parse(string) {
|
|
739
|
-
return this.deserialize(JSON.parse(string), { inPlace: true });
|
|
740
|
-
}
|
|
741
|
-
registerClass(v, options) {
|
|
742
|
-
this.classRegistry.register(v, options);
|
|
743
|
-
}
|
|
744
|
-
registerSymbol(v, identifier) {
|
|
745
|
-
this.symbolRegistry.register(v, identifier);
|
|
746
|
-
}
|
|
747
|
-
registerCustom(transformer, name) {
|
|
748
|
-
this.customTransformerRegistry.register({
|
|
749
|
-
name,
|
|
750
|
-
...transformer
|
|
751
|
-
});
|
|
752
|
-
}
|
|
753
|
-
allowErrorProps(...props) {
|
|
754
|
-
this.allowedErrorProps.push(...props);
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
SuperJSON.defaultInstance = new SuperJSON();
|
|
758
|
-
SuperJSON.serialize = SuperJSON.defaultInstance.serialize.bind(SuperJSON.defaultInstance);
|
|
759
|
-
SuperJSON.deserialize = SuperJSON.defaultInstance.deserialize.bind(SuperJSON.defaultInstance);
|
|
760
|
-
SuperJSON.stringify = SuperJSON.defaultInstance.stringify.bind(SuperJSON.defaultInstance);
|
|
761
|
-
SuperJSON.parse = SuperJSON.defaultInstance.parse.bind(SuperJSON.defaultInstance);
|
|
762
|
-
SuperJSON.registerClass = SuperJSON.defaultInstance.registerClass.bind(SuperJSON.defaultInstance);
|
|
763
|
-
SuperJSON.registerSymbol = SuperJSON.defaultInstance.registerSymbol.bind(SuperJSON.defaultInstance);
|
|
764
|
-
SuperJSON.registerCustom = SuperJSON.defaultInstance.registerCustom.bind(SuperJSON.defaultInstance);
|
|
765
|
-
SuperJSON.allowErrorProps = SuperJSON.defaultInstance.allowErrorProps.bind(SuperJSON.defaultInstance);
|
|
766
|
-
SuperJSON.serialize;
|
|
767
|
-
SuperJSON.deserialize;
|
|
768
|
-
SuperJSON.stringify;
|
|
769
|
-
SuperJSON.parse;
|
|
770
|
-
SuperJSON.registerClass;
|
|
771
|
-
SuperJSON.registerCustom;
|
|
772
|
-
SuperJSON.registerSymbol;
|
|
773
|
-
SuperJSON.allowErrorProps;
|
|
774
|
-
const WS_KEEP_ALIVE_INTERVAL_MS = 1e4;
|
|
775
|
-
const WS_KEEP_ALIVE_PONG_TIMEOUT_MS = 3e3;
|
|
776
|
-
const WS_RECONNECT_RETRY_DELAY_MS = 2e3;
|
|
777
|
-
const WS_RECONNECT_MAX_RETRY_DELAY_MS = 3e4;
|
|
778
|
-
class System {
|
|
779
|
-
/** Active server base URL. Mutable via `switchServerUrl()` so the
|
|
780
|
-
* endpoint-race helper can pivot the transport onto a LAN candidate
|
|
781
|
-
* after the initial public-URL bootstrap. */
|
|
782
|
-
_serverUrl;
|
|
783
|
-
useWs;
|
|
784
|
-
baseRetryMs;
|
|
785
|
-
maxRetryMs;
|
|
786
|
-
onConnectionChange;
|
|
787
|
-
token;
|
|
788
|
-
_trpcClient;
|
|
789
|
-
_wsClient = null;
|
|
790
|
-
// Mirror — owns binding cache + state mirror + lifecycle listeners.
|
|
791
|
-
// Lazily initialised on `init()`. Subsequent `reconnect()` disposes
|
|
792
|
-
// the old mirror and rebuilds against the new tRPC client.
|
|
793
|
-
mirror = null;
|
|
794
|
-
mirrorInit = null;
|
|
795
|
-
// Transport-readiness probe state. `connected` flips true the first
|
|
796
|
-
// time `auth.me` succeeds against the current tRPC client; resets
|
|
797
|
-
// on every `reconnect()` / `close()` so the next consumer waits for
|
|
798
|
-
// a fresh handshake before issuing queries.
|
|
799
|
-
connected = false;
|
|
800
|
-
connectedPromise = null;
|
|
801
|
-
// System-cap namespaces — populated in the constructor from
|
|
802
|
-
// `createSystemProxy(api)`. Each namespace is a `Pick<InferProvider<typeof cap>, …>`
|
|
803
|
-
// shape generated by `scripts/generate-system-proxy.ts`.
|
|
804
|
-
_systemProxy;
|
|
805
|
-
// Connection-version + listener bookkeeping for the admin UI's
|
|
806
|
-
// reconnect watchdog.
|
|
807
|
-
_connectionVersion = 0;
|
|
808
|
-
connectionListeners = /* @__PURE__ */ new Set();
|
|
809
|
-
constructor(config) {
|
|
810
|
-
this._serverUrl = config.serverUrl.replace(/\/+$/, "");
|
|
811
|
-
this.token = config.token;
|
|
812
|
-
const isBrowser = typeof window !== "undefined";
|
|
813
|
-
this.useWs = config.useWebSocket ?? isBrowser;
|
|
814
|
-
this.onConnectionChange = config.onConnectionChange;
|
|
815
|
-
this.baseRetryMs = config.retryDelayMs ?? WS_RECONNECT_RETRY_DELAY_MS;
|
|
816
|
-
this.maxRetryMs = config.maxRetryDelayMs ?? WS_RECONNECT_MAX_RETRY_DELAY_MS;
|
|
817
|
-
this._trpcClient = this.buildTrpcClient();
|
|
818
|
-
this._systemProxy = __mf_429(this._trpcClient);
|
|
819
|
-
}
|
|
820
|
-
// ── Connection state ─────────────────────────────────────────────
|
|
821
|
-
/** Active server base URL (no trailing slash). */
|
|
822
|
-
get serverUrl() {
|
|
823
|
-
return this._serverUrl;
|
|
824
|
-
}
|
|
825
|
-
get connectionVersion() {
|
|
826
|
-
return this._connectionVersion;
|
|
827
|
-
}
|
|
828
|
-
/**
|
|
829
|
-
* Subscribe to connection-state transitions. Called with `('connected'
|
|
830
|
-
* | 'disconnected' | 'connecting', version)` whenever the SDK opens,
|
|
831
|
-
* closes, or starts a manual reconnect. Listener errors are swallowed
|
|
832
|
-
* so a misbehaving consumer cannot break the SDK's event loop.
|
|
833
|
-
*/
|
|
834
|
-
subscribeConnectionEvents(cb) {
|
|
835
|
-
this.connectionListeners.add(cb);
|
|
836
|
-
return () => {
|
|
837
|
-
this.connectionListeners.delete(cb);
|
|
838
|
-
};
|
|
839
|
-
}
|
|
840
|
-
emitConnectionEvent(state) {
|
|
841
|
-
try {
|
|
842
|
-
this.onConnectionChange?.(state);
|
|
843
|
-
} catch {
|
|
844
|
-
}
|
|
845
|
-
for (const l of this.connectionListeners) {
|
|
846
|
-
try {
|
|
847
|
-
l(state, this._connectionVersion);
|
|
848
|
-
} catch {
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
// ── Lifecycle ────────────────────────────────────────────────────
|
|
853
|
-
/**
|
|
854
|
-
* Wait until the underlying tRPC transport is connected AND the
|
|
855
|
-
* server has responded to a cheap auth round-trip (`auth.me`). This
|
|
856
|
-
* is the canonical "ready to issue queries" gate.
|
|
857
|
-
*
|
|
858
|
-
* Why a probe, not just `ws.readyState === OPEN`?
|
|
859
|
-
* The WS handshake completes asynchronously: tRPC's `wsLink`
|
|
860
|
-
* queues outgoing messages and only flushes them after `open()`
|
|
861
|
-
* resolves (post `connectionParams` send). On the server, the
|
|
862
|
-
* tRPC context is created lazily once the connectionParams
|
|
863
|
-
* message is received. A query fired between WS-open and
|
|
864
|
-
* connection-params-processed is technically queued by tRPC, but
|
|
865
|
-
* the auth context for that query is only resolved once the
|
|
866
|
-
* handshake message is decoded server-side. A probe round-trip is
|
|
867
|
-
* the safest way to confirm both sides have agreed on the auth
|
|
868
|
-
* identity before the React tree starts firing parallel queries
|
|
869
|
-
* (which can otherwise land before any addon-side service
|
|
870
|
-
* discovery has settled, returning empty results that get cached).
|
|
871
|
-
*
|
|
872
|
-
* Idempotent — concurrent callers await the same in-flight Promise.
|
|
873
|
-
* Bounded by `timeoutMs` (default 15s) — beyond which a
|
|
874
|
-
* `Error('System.awaitConnected: probe timed out after Xms')` is
|
|
875
|
-
* thrown so the host can render a clear error state instead of
|
|
876
|
-
* hanging on a bricked socket.
|
|
877
|
-
*/
|
|
878
|
-
async awaitConnected(timeoutMs) {
|
|
879
|
-
if (this.connected) return;
|
|
880
|
-
if (this.connectedPromise) return this.connectedPromise;
|
|
881
|
-
const effectiveTimeoutMs = timeoutMs ?? 15e3;
|
|
882
|
-
this.connectedPromise = (async () => {
|
|
883
|
-
const probe = this._trpcClient.auth.me.query();
|
|
884
|
-
if (!Number.isFinite(effectiveTimeoutMs)) {
|
|
885
|
-
await probe;
|
|
886
|
-
} else {
|
|
887
|
-
await new Promise((resolve, reject) => {
|
|
888
|
-
const timer = setTimeout(
|
|
889
|
-
() => reject(new Error(`System.awaitConnected: probe timed out after ${effectiveTimeoutMs}ms`)),
|
|
890
|
-
effectiveTimeoutMs
|
|
891
|
-
);
|
|
892
|
-
probe.then(
|
|
893
|
-
() => {
|
|
894
|
-
clearTimeout(timer);
|
|
895
|
-
resolve();
|
|
896
|
-
},
|
|
897
|
-
(err) => {
|
|
898
|
-
clearTimeout(timer);
|
|
899
|
-
reject(err instanceof Error ? err : new Error(String(err)));
|
|
900
|
-
}
|
|
901
|
-
);
|
|
902
|
-
});
|
|
903
|
-
}
|
|
904
|
-
this.connected = true;
|
|
905
|
-
})();
|
|
906
|
-
try {
|
|
907
|
-
await this.connectedPromise;
|
|
908
|
-
} finally {
|
|
909
|
-
this.connectedPromise = null;
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
/**
|
|
913
|
-
* Warm-boot the device mirror. Awaits the transport probe first
|
|
914
|
-
* (`awaitConnected`) so the three mirror round-trips
|
|
915
|
-
* (`getAllBindings` + `getAllSnapshots` + `listAll`) cannot race
|
|
916
|
-
* against the WS auth handshake. Subsequent `getDevice(id)` calls
|
|
917
|
-
* are sync; live `device.*` event subscriptions keep the caches
|
|
918
|
-
* fresh.
|
|
919
|
-
*
|
|
920
|
-
* Idempotent — concurrent callers await the same in-flight Promise.
|
|
921
|
-
*/
|
|
922
|
-
async init(timeoutMs) {
|
|
923
|
-
if (this.mirror?.isReady()) return;
|
|
924
|
-
if (this.mirrorInit) return this.mirrorInit;
|
|
925
|
-
const m = new __mf_411(this._trpcClient);
|
|
926
|
-
this.mirror = m;
|
|
927
|
-
this.mirrorInit = (async () => {
|
|
928
|
-
await this.awaitConnected(timeoutMs);
|
|
929
|
-
await m.init(timeoutMs);
|
|
930
|
-
})().finally(() => {
|
|
931
|
-
this.mirrorInit = null;
|
|
932
|
-
});
|
|
933
|
-
return this.mirrorInit;
|
|
934
|
-
}
|
|
935
|
-
/** Promise that resolves once `init()` has completed. */
|
|
936
|
-
async awaitReady() {
|
|
937
|
-
if (this.mirror?.isReady()) return;
|
|
938
|
-
if (this.mirrorInit) return this.mirrorInit;
|
|
939
|
-
return this.init();
|
|
940
|
-
}
|
|
941
|
-
/** True after `init()` resolves. */
|
|
942
|
-
isReady() {
|
|
943
|
-
return this.mirror?.isReady() ?? false;
|
|
944
|
-
}
|
|
945
|
-
/** True after the transport probe has succeeded at least once. */
|
|
946
|
-
isConnected() {
|
|
947
|
-
return this.connected;
|
|
948
|
-
}
|
|
949
|
-
/**
|
|
950
|
-
* Force a fresh WebSocket handshake. Tears down the wsClient + tRPC
|
|
951
|
-
* client + mirror (the mirror captures the tRPC reference at
|
|
952
|
-
* construction time and would otherwise dispatch through a closed
|
|
953
|
-
* client) and rebuilds them. No-op for HTTP transport.
|
|
954
|
-
*/
|
|
955
|
-
reconnect() {
|
|
956
|
-
if (!this.useWs) return;
|
|
957
|
-
this.emitConnectionEvent("connecting");
|
|
958
|
-
this._wsClient?.close();
|
|
959
|
-
this.disposeMirror();
|
|
960
|
-
this.connected = false;
|
|
961
|
-
this.connectedPromise = null;
|
|
962
|
-
this._trpcClient = this.buildTrpcClient();
|
|
963
|
-
this._systemProxy = __mf_429(this._trpcClient);
|
|
964
|
-
}
|
|
965
|
-
/**
|
|
966
|
-
* Pivot the underlying tRPC transport onto a different base URL.
|
|
967
|
-
* Used by the endpoint-race flow: the SDK opens against the public
|
|
968
|
-
* URL the operator provided, calls `localNetwork.getConnectionEndpoints`
|
|
969
|
-
* to discover LAN candidates, races them, and (when a faster one wins)
|
|
970
|
-
* calls `switchServerUrl(winner)` to migrate every subsequent query
|
|
971
|
-
* onto the LAN path without losing auth state.
|
|
972
|
-
*
|
|
973
|
-
* Keeps the auth token. Tears down the WS + mirror and rebuilds them
|
|
974
|
-
* against the new URL — same machinery as `reconnect()` but with a
|
|
975
|
-
* different target.
|
|
976
|
-
*/
|
|
977
|
-
switchServerUrl(nextUrl) {
|
|
978
|
-
const normalized = nextUrl.replace(/\/+$/, "");
|
|
979
|
-
if (normalized === this._serverUrl) return;
|
|
980
|
-
this._serverUrl = normalized;
|
|
981
|
-
this.reconnect();
|
|
982
|
-
}
|
|
983
|
-
/**
|
|
984
|
-
* Race the candidate base URLs reported by the hub's `local-network`
|
|
985
|
-
* cap, pick the fastest one that responds, and (if it's different
|
|
986
|
-
* from the current URL) pivot the transport onto it.
|
|
987
|
-
*
|
|
988
|
-
* Flow:
|
|
989
|
-
* 1. Query `localNetwork.getConnectionEndpoints({ port })` over the
|
|
990
|
-
* already-authenticated tRPC channel — the cap is auth-gated,
|
|
991
|
-
* so the LAN IPs never leak to anonymous callers.
|
|
992
|
-
* 2. For each candidate, fire a HEAD on `{baseUrl}/trpc/health`
|
|
993
|
-
* with a short timeout (default 1500ms). The first 2xx wins.
|
|
994
|
-
* 3. If the winner differs from `this.serverUrl`, call
|
|
995
|
-
* `switchServerUrl(winner)` and return it. Otherwise return
|
|
996
|
-
* the current URL unchanged.
|
|
997
|
-
*
|
|
998
|
-
* Bounded — if every candidate times out we keep the current URL.
|
|
999
|
-
* Idempotent — safe to call on every connect / reconnect / network
|
|
1000
|
-
* change event.
|
|
1001
|
-
*/
|
|
1002
|
-
async raceConnectionEndpoints(options) {
|
|
1003
|
-
const timeoutMs = options?.perCandidateTimeoutMs ?? 1500;
|
|
1004
|
-
const url = (() => {
|
|
1005
|
-
try {
|
|
1006
|
-
return new URL(this._serverUrl);
|
|
1007
|
-
} catch {
|
|
1008
|
-
return null;
|
|
1009
|
-
}
|
|
1010
|
-
})();
|
|
1011
|
-
const port = url?.port ? Number(url.port) : url?.protocol === "https:" ? 443 : 80;
|
|
1012
|
-
const scheme = url?.protocol === "https:" ? "https" : "http";
|
|
1013
|
-
const proxy = this._trpcClient;
|
|
1014
|
-
const cap = proxy.localNetwork?.getConnectionEndpoints;
|
|
1015
|
-
if (!cap) {
|
|
1016
|
-
return { winner: this._serverUrl, switched: false };
|
|
1017
|
-
}
|
|
1018
|
-
let endpoints;
|
|
1019
|
-
try {
|
|
1020
|
-
const res = await cap.query({ port, ipv4Only: options?.ipv4Only, scheme });
|
|
1021
|
-
endpoints = res.endpoints;
|
|
1022
|
-
} catch {
|
|
1023
|
-
return { winner: this._serverUrl, switched: false };
|
|
1024
|
-
}
|
|
1025
|
-
if (endpoints.length === 0) {
|
|
1026
|
-
return { winner: this._serverUrl, switched: false };
|
|
1027
|
-
}
|
|
1028
|
-
const winner = await raceFastestEndpoint(endpoints.map((e) => e.baseUrl), timeoutMs);
|
|
1029
|
-
if (!winner || winner === this._serverUrl) {
|
|
1030
|
-
return { winner: winner ?? this._serverUrl, switched: false };
|
|
1031
|
-
}
|
|
1032
|
-
this.switchServerUrl(winner);
|
|
1033
|
-
return { winner, switched: true };
|
|
1034
|
-
}
|
|
1035
|
-
/** Tear down WS connection + mirror. The instance is unusable afterwards. */
|
|
1036
|
-
close() {
|
|
1037
|
-
this.disposeMirror();
|
|
1038
|
-
this.connected = false;
|
|
1039
|
-
this.connectedPromise = null;
|
|
1040
|
-
this._wsClient?.close();
|
|
1041
|
-
}
|
|
1042
|
-
disposeMirror() {
|
|
1043
|
-
this.mirror?.dispose();
|
|
1044
|
-
this.mirror = null;
|
|
1045
|
-
this.mirrorInit = null;
|
|
1046
|
-
}
|
|
1047
|
-
// ── Auth ──────────────────────────────────────────────────────────
|
|
1048
|
-
async login(username, password) {
|
|
1049
|
-
const result = await this._trpcClient.auth.login.mutate({ username, password });
|
|
1050
|
-
if (typeof result === "object" && result !== null && "token" in result) {
|
|
1051
|
-
const r = result;
|
|
1052
|
-
const token = r.token;
|
|
1053
|
-
if (typeof token === "string" && r.requiresTotp !== true) this.setToken(token);
|
|
1054
|
-
}
|
|
1055
|
-
return result;
|
|
1056
|
-
}
|
|
1057
|
-
/**
|
|
1058
|
-
* Second leg of the 2FA login. The caller passes the challenge
|
|
1059
|
-
* token returned by `login` (when `requiresTotp: true`) plus the
|
|
1060
|
-
* 6-digit code from the user's authenticator app. On success the
|
|
1061
|
-
* server mints the real session JWT, which we set as the active
|
|
1062
|
-
* token on this client.
|
|
1063
|
-
*/
|
|
1064
|
-
async loginVerifyTotp(challengeToken, code) {
|
|
1065
|
-
const result = await this._trpcClient.auth.loginVerifyTotp.mutate({ challengeToken, code });
|
|
1066
|
-
if (typeof result === "object" && result !== null && "token" in result) {
|
|
1067
|
-
const token = result.token;
|
|
1068
|
-
if (typeof token === "string") this.setToken(token);
|
|
1069
|
-
}
|
|
1070
|
-
return result;
|
|
1071
|
-
}
|
|
1072
|
-
async logout() {
|
|
1073
|
-
await this._trpcClient.auth.logout.mutate();
|
|
1074
|
-
}
|
|
1075
|
-
async getMe() {
|
|
1076
|
-
const me = await this._trpcClient.auth.me.query();
|
|
1077
|
-
return me;
|
|
1078
|
-
}
|
|
1079
|
-
// ── Self-service auth (the signed-in user acting on themselves) ─────
|
|
1080
|
-
//
|
|
1081
|
-
// The hand-written `auth.*` router exposes 5 procedures that operate
|
|
1082
|
-
// on `ctx.user` (the JWT-bound identity), distinct from the admin
|
|
1083
|
-
// `user-management` cap that operates on arbitrary userIds. They live
|
|
1084
|
-
// on `System` rather than as codegen'd hooks because `auth.*` is a
|
|
1085
|
-
// legacy core router, not a `.cap.ts` — see the no-restricted-imports
|
|
1086
|
-
// rule in `eslint.config.mjs` for the canonical entry-point design.
|
|
1087
|
-
async changeOwnPassword(input) {
|
|
1088
|
-
const result = await this._trpcClient.auth.changeOwnPassword.mutate(input);
|
|
1089
|
-
return result;
|
|
1090
|
-
}
|
|
1091
|
-
async setupOwnTotp() {
|
|
1092
|
-
const result = await this._trpcClient.auth.setupOwnTotp.mutate();
|
|
1093
|
-
return result;
|
|
1094
|
-
}
|
|
1095
|
-
async confirmOwnTotp(input) {
|
|
1096
|
-
const result = await this._trpcClient.auth.confirmOwnTotp.mutate(input);
|
|
1097
|
-
return result;
|
|
1098
|
-
}
|
|
1099
|
-
async disableOwnTotp() {
|
|
1100
|
-
const result = await this._trpcClient.auth.disableOwnTotp.mutate();
|
|
1101
|
-
return result;
|
|
1102
|
-
}
|
|
1103
|
-
async getOwnTotpStatus() {
|
|
1104
|
-
const result = await this._trpcClient.auth.getOwnTotpStatus.query();
|
|
1105
|
-
return result;
|
|
1106
|
-
}
|
|
1107
|
-
/** Update the auth token (e.g. after login or token refresh). */
|
|
1108
|
-
setToken(token) {
|
|
1109
|
-
this.token = token;
|
|
1110
|
-
}
|
|
1111
|
-
// ── Devices ──────────────────────────────────────────────────────
|
|
1112
|
-
/**
|
|
1113
|
-
* Synchronous snapshot of every device matching the optional filters.
|
|
1114
|
-
* Backed by the `SystemMirror` warm-boot cache — call `init()` first
|
|
1115
|
-
* (or `awaitReady()`) before invoking. Returns an empty array if the
|
|
1116
|
-
* mirror has not yet been booted.
|
|
1117
|
-
*
|
|
1118
|
-
* Each returned proxy has `binding` populated from the mirror's
|
|
1119
|
-
* binding cache (Phase 5 dedup), so consumers no longer need to
|
|
1120
|
-
* make a separate `deviceManager.getBindings` round-trip.
|
|
1121
|
-
*/
|
|
1122
|
-
listDevices(filters) {
|
|
1123
|
-
if (!this.mirror) return [];
|
|
1124
|
-
const proxies = filters ? this.mirror.query(filters) : this.mirror.getAllDevices();
|
|
1125
|
-
return proxies.map((p) => this.attachBinding(p));
|
|
1126
|
-
}
|
|
1127
|
-
/**
|
|
1128
|
-
* Sync lookup by numeric id. `null` if the mirror has not been booted
|
|
1129
|
-
* or the device is unknown.
|
|
1130
|
-
*/
|
|
1131
|
-
getDevice(deviceId) {
|
|
1132
|
-
const proxy = this.mirror?.getDeviceById(deviceId) ?? null;
|
|
1133
|
-
return proxy ? this.attachBinding(proxy) : null;
|
|
1134
|
-
}
|
|
1135
|
-
/** Sync lookup by display name (exact match). */
|
|
1136
|
-
getDeviceByName(name) {
|
|
1137
|
-
const proxy = this.mirror?.getDeviceByName(name) ?? null;
|
|
1138
|
-
return proxy ? this.attachBinding(proxy) : null;
|
|
1139
|
-
}
|
|
1140
|
-
/** Sync lookup by stableId. */
|
|
1141
|
-
getDeviceByStableId(stableId) {
|
|
1142
|
-
const proxy = this.mirror?.getDeviceByStableId(stableId) ?? null;
|
|
1143
|
-
return proxy ? this.attachBinding(proxy) : null;
|
|
1144
|
-
}
|
|
1145
|
-
/**
|
|
1146
|
-
* Resolve when a device with `deviceId` becomes available. Resolves
|
|
1147
|
-
* immediately if already known; rejects with a timeout error
|
|
1148
|
-
* otherwise (default 30s).
|
|
1149
|
-
*/
|
|
1150
|
-
async waitForDevice(deviceId, timeoutMs) {
|
|
1151
|
-
if (!this.mirror) await this.init();
|
|
1152
|
-
const proxy = await this.mirror.waitForDevice(deviceId, timeoutMs ?? 3e4);
|
|
1153
|
-
return this.attachBinding(proxy);
|
|
1154
|
-
}
|
|
1155
|
-
/** Subscribe to `device.registered` events. */
|
|
1156
|
-
onDeviceAdded(cb) {
|
|
1157
|
-
if (!this.mirror) {
|
|
1158
|
-
throw new Error("System.onDeviceAdded: call init() before subscribing");
|
|
1159
|
-
}
|
|
1160
|
-
return this.mirror.onDeviceAdded(cb);
|
|
1161
|
-
}
|
|
1162
|
-
/** Subscribe to `device.unregistered` events. */
|
|
1163
|
-
onDeviceRemoved(cb) {
|
|
1164
|
-
if (!this.mirror) {
|
|
1165
|
-
throw new Error("System.onDeviceRemoved: call init() before subscribing");
|
|
1166
|
-
}
|
|
1167
|
-
return this.mirror.onDeviceRemoved(cb);
|
|
1168
|
-
}
|
|
1169
|
-
/**
|
|
1170
|
-
* Patch the proxy's `binding` field from the mirror's cache. The
|
|
1171
|
-
* generated `createDeviceProxy()` already sets `binding` to the
|
|
1172
|
-
* binding it was constructed with — this is a defensive overwrite
|
|
1173
|
-
* that uses the latest cached entry in case a `binding-changed`
|
|
1174
|
-
* event landed between proxy creation and access.
|
|
1175
|
-
*/
|
|
1176
|
-
attachBinding(proxy) {
|
|
1177
|
-
const binding = this.lookupBinding(proxy.deviceId);
|
|
1178
|
-
if (binding === proxy.binding) return proxy;
|
|
1179
|
-
const writable = proxy;
|
|
1180
|
-
writable.binding = binding;
|
|
1181
|
-
return proxy;
|
|
1182
|
-
}
|
|
1183
|
-
/** Fetch the latest cached binding from the mirror, or `null`. */
|
|
1184
|
-
lookupBinding(deviceId) {
|
|
1185
|
-
if (!this.mirror) return null;
|
|
1186
|
-
const proxy = this.mirror.getDeviceById(deviceId);
|
|
1187
|
-
return proxy?.binding ?? null;
|
|
1188
|
-
}
|
|
1189
|
-
// ── System caps ──────────────────────────────────────────────────
|
|
1190
|
-
//
|
|
1191
|
-
// One getter per `scope: 'system'` capability. The actual surface
|
|
1192
|
-
// comes from `createSystemProxy(api)` (codegen). Adding a new
|
|
1193
|
-
// system cap regenerates the proxy and the new namespace surfaces
|
|
1194
|
-
// automatically via `system.<newCap>.<method>(input)`.
|
|
1195
|
-
get addonPages() {
|
|
1196
|
-
return this._systemProxy.addonPages;
|
|
1197
|
-
}
|
|
1198
|
-
get addonSettings() {
|
|
1199
|
-
return this._systemProxy.addonSettings;
|
|
1200
|
-
}
|
|
1201
|
-
get alerts() {
|
|
1202
|
-
return this._systemProxy.alerts;
|
|
1203
|
-
}
|
|
1204
|
-
get audioAnalyzer() {
|
|
1205
|
-
return this._systemProxy.audioAnalyzer;
|
|
1206
|
-
}
|
|
1207
|
-
get audioCodec() {
|
|
1208
|
-
return this._systemProxy.audioCodec;
|
|
1209
|
-
}
|
|
1210
|
-
get backup() {
|
|
1211
|
-
return this._systemProxy.backup;
|
|
1212
|
-
}
|
|
1213
|
-
get decoder() {
|
|
1214
|
-
return this._systemProxy.decoder;
|
|
1215
|
-
}
|
|
1216
|
-
get deviceManager() {
|
|
1217
|
-
return this._systemProxy.deviceManager;
|
|
1218
|
-
}
|
|
1219
|
-
get deviceProvider() {
|
|
1220
|
-
return this._systemProxy.deviceProvider;
|
|
1221
|
-
}
|
|
1222
|
-
get deviceState() {
|
|
1223
|
-
return this._systemProxy.deviceState;
|
|
1224
|
-
}
|
|
1225
|
-
get metricsProvider() {
|
|
1226
|
-
return this._systemProxy.metricsProvider;
|
|
1227
|
-
}
|
|
1228
|
-
get notificationOutput() {
|
|
1229
|
-
return this._systemProxy.notificationOutput;
|
|
1230
|
-
}
|
|
1231
|
-
get pipelineExecutor() {
|
|
1232
|
-
return this._systemProxy.pipelineExecutor;
|
|
1233
|
-
}
|
|
1234
|
-
get pipelineOrchestrator() {
|
|
1235
|
-
return this._systemProxy.pipelineOrchestrator;
|
|
1236
|
-
}
|
|
1237
|
-
get pipelineRunner() {
|
|
1238
|
-
return this._systemProxy.pipelineRunner;
|
|
1239
|
-
}
|
|
1240
|
-
get platformProbe() {
|
|
1241
|
-
return this._systemProxy.platformProbe;
|
|
1242
|
-
}
|
|
1243
|
-
get recordingEngine() {
|
|
1244
|
-
return this._systemProxy.recordingEngine;
|
|
1245
|
-
}
|
|
1246
|
-
get settingsStore() {
|
|
1247
|
-
return this._systemProxy.settingsStore;
|
|
1248
|
-
}
|
|
1249
|
-
get storage() {
|
|
1250
|
-
return this._systemProxy.storage;
|
|
1251
|
-
}
|
|
1252
|
-
get streamBroker() {
|
|
1253
|
-
return this._systemProxy.streamBroker;
|
|
1254
|
-
}
|
|
1255
|
-
get turnProvider() {
|
|
1256
|
-
return this._systemProxy.turnProvider;
|
|
1257
|
-
}
|
|
1258
|
-
get userManagement() {
|
|
1259
|
-
return this._systemProxy.userManagement;
|
|
1260
|
-
}
|
|
1261
|
-
// ── Live events ──────────────────────────────────────────────────
|
|
1262
|
-
/**
|
|
1263
|
-
* Subscribe to a single event category. Returns an unsubscribe
|
|
1264
|
-
* handle. Errors thrown by the listener are swallowed so a single
|
|
1265
|
-
* misbehaving consumer cannot tear down the WS subscription.
|
|
1266
|
-
*
|
|
1267
|
-
* Categories should be values from the `EventCategory` enum
|
|
1268
|
-
* (`@camstack/types`) — passing a raw string works for forward-compat
|
|
1269
|
-
* but loses type safety. The SDK forwards the value verbatim to the
|
|
1270
|
-
* server's `live.onEvent` subscription.
|
|
1271
|
-
*/
|
|
1272
|
-
subscribeEvent(category, cb) {
|
|
1273
|
-
const sub = this._trpcClient.live.onEvent.subscribe(
|
|
1274
|
-
{ category },
|
|
1275
|
-
{
|
|
1276
|
-
onData: (event) => {
|
|
1277
|
-
try {
|
|
1278
|
-
cb(event);
|
|
1279
|
-
} catch {
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
|
-
}
|
|
1283
|
-
);
|
|
1284
|
-
return () => {
|
|
1285
|
-
try {
|
|
1286
|
-
sub.unsubscribe();
|
|
1287
|
-
} catch {
|
|
1288
|
-
}
|
|
1289
|
-
};
|
|
1290
|
-
}
|
|
1291
|
-
// ── Escape hatches ───────────────────────────────────────────────
|
|
1292
|
-
//
|
|
1293
|
-
// Kept public so the ui-library `<TrpcProvider>` can seed React
|
|
1294
|
-
// Query with the same WebSocket connection during Phase 2. Phase 4
|
|
1295
|
-
// is expected to revisit whether either field can be hidden.
|
|
1296
|
-
/** Direct tRPC client. Read once per call; rebuilt on `reconnect()`. */
|
|
1297
|
-
get trpcClient() {
|
|
1298
|
-
return this._trpcClient;
|
|
1299
|
-
}
|
|
1300
|
-
/**
|
|
1301
|
-
* Underlying WSClient (or `null` for HTTP transport). Used by
|
|
1302
|
-
* advanced consumers that need direct access to the WebSocket
|
|
1303
|
-
* (e.g. for keep-alive metrics). Rebuilt on `reconnect()`.
|
|
1304
|
-
*/
|
|
1305
|
-
get wsClient() {
|
|
1306
|
-
return this._wsClient;
|
|
1307
|
-
}
|
|
1308
|
-
// ── Internals ────────────────────────────────────────────────────
|
|
1309
|
-
buildTrpcClient() {
|
|
1310
|
-
const headers = () => {
|
|
1311
|
-
const h = {};
|
|
1312
|
-
if (this.token) h["Authorization"] = `Bearer ${this.token}`;
|
|
1313
|
-
return h;
|
|
1314
|
-
};
|
|
1315
|
-
if (this.useWs) {
|
|
1316
|
-
const wsUrl = this._serverUrl.replace(/^http/, "ws") + "/trpc";
|
|
1317
|
-
const baseRetryMs = this.baseRetryMs;
|
|
1318
|
-
const maxRetryMs = this.maxRetryMs;
|
|
1319
|
-
this._wsClient = __mf_7({
|
|
1320
|
-
url: wsUrl,
|
|
1321
|
-
connectionParams: () => ({ token: this.token }),
|
|
1322
|
-
retryDelayMs: (attemptIndex) => Math.min(baseRetryMs * Math.pow(2, attemptIndex), maxRetryMs),
|
|
1323
|
-
keepAlive: {
|
|
1324
|
-
enabled: true,
|
|
1325
|
-
intervalMs: WS_KEEP_ALIVE_INTERVAL_MS,
|
|
1326
|
-
pongTimeoutMs: WS_KEEP_ALIVE_PONG_TIMEOUT_MS
|
|
1327
|
-
},
|
|
1328
|
-
onOpen: () => {
|
|
1329
|
-
this._connectionVersion += 1;
|
|
1330
|
-
this.emitConnectionEvent("connected");
|
|
1331
|
-
},
|
|
1332
|
-
onClose: () => {
|
|
1333
|
-
this.emitConnectionEvent("disconnected");
|
|
1334
|
-
}
|
|
1335
|
-
});
|
|
1336
|
-
return __mf_3({
|
|
1337
|
-
links: [__mf_26({ client: this._wsClient, transformer: SuperJSON })]
|
|
1338
|
-
});
|
|
1339
|
-
}
|
|
1340
|
-
this._wsClient = null;
|
|
1341
|
-
return __mf_3({
|
|
1342
|
-
links: [__mf_13({ url: `${this._serverUrl}/trpc`, headers, transformer: SuperJSON })]
|
|
1343
|
-
});
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
function createSystem(config) {
|
|
1347
|
-
return new System(config);
|
|
1348
|
-
}
|
|
1349
|
-
async function raceFastestEndpoint(candidates, timeoutMs) {
|
|
1350
|
-
if (candidates.length === 0) return null;
|
|
1351
|
-
if (typeof fetch !== "function") return candidates[0] ?? null;
|
|
1352
|
-
const pageIsHttps = typeof window !== "undefined" && typeof window.location !== "undefined" && window.location.protocol === "https:";
|
|
1353
|
-
const reachable = pageIsHttps ? candidates.filter((u) => u.toLowerCase().startsWith("https://")) : candidates;
|
|
1354
|
-
if (reachable.length === 0) return null;
|
|
1355
|
-
const controllers = reachable.map(() => new AbortController());
|
|
1356
|
-
const probes = reachable.map(async (baseUrl, i) => {
|
|
1357
|
-
const ctrl = controllers[i];
|
|
1358
|
-
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
1359
|
-
try {
|
|
1360
|
-
const res = await fetch(`${baseUrl.replace(/\/+$/, "")}/trpc/health`, {
|
|
1361
|
-
method: "GET",
|
|
1362
|
-
signal: ctrl.signal,
|
|
1363
|
-
// Cross-origin probes from a browser sandbox are fine — the
|
|
1364
|
-
// /trpc/health response is open-CORS by the Fastify default.
|
|
1365
|
-
// No credentials needed (this is a transport-quality probe,
|
|
1366
|
-
// not an auth round-trip).
|
|
1367
|
-
credentials: "omit"
|
|
1368
|
-
});
|
|
1369
|
-
if (!res.ok) throw new Error(`${res.status}`);
|
|
1370
|
-
return baseUrl;
|
|
1371
|
-
} finally {
|
|
1372
|
-
clearTimeout(timer);
|
|
1373
|
-
}
|
|
1374
|
-
});
|
|
1375
|
-
try {
|
|
1376
|
-
const winner = await Promise.any(probes);
|
|
1377
|
-
for (const c of controllers) {
|
|
1378
|
-
try {
|
|
1379
|
-
c.abort();
|
|
1380
|
-
} catch {
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
return winner;
|
|
1384
|
-
} catch {
|
|
1385
|
-
return null;
|
|
1386
|
-
}
|
|
1387
|
-
}
|
|
1388
|
-
var DetectionClass = /* @__PURE__ */ ((DetectionClass2) => {
|
|
1389
|
-
DetectionClass2["Motion"] = "motion";
|
|
1390
|
-
DetectionClass2["Person"] = "person";
|
|
1391
|
-
DetectionClass2["Vehicle"] = "vehicle";
|
|
1392
|
-
DetectionClass2["Animal"] = "animal";
|
|
1393
|
-
DetectionClass2["Audio"] = "audio";
|
|
1394
|
-
DetectionClass2["Face"] = "face";
|
|
1395
|
-
DetectionClass2["Plate"] = "plate";
|
|
1396
|
-
DetectionClass2["Package"] = "package";
|
|
1397
|
-
DetectionClass2["Doorbell"] = "doorbell";
|
|
1398
|
-
DetectionClass2["Sensor"] = "sensor";
|
|
1399
|
-
return DetectionClass2;
|
|
1400
|
-
})(DetectionClass || {});
|
|
1401
|
-
const animalClasses = [
|
|
1402
|
-
"animal",
|
|
1403
|
-
"dog_cat",
|
|
1404
|
-
"dog",
|
|
1405
|
-
"cat",
|
|
1406
|
-
"horse",
|
|
1407
|
-
"sheep",
|
|
1408
|
-
"cow",
|
|
1409
|
-
"elephant",
|
|
1410
|
-
"bear",
|
|
1411
|
-
"zebra",
|
|
1412
|
-
"giraffe",
|
|
1413
|
-
"mouse",
|
|
1414
|
-
"rabbit",
|
|
1415
|
-
"deer",
|
|
1416
|
-
"lion",
|
|
1417
|
-
"tiger",
|
|
1418
|
-
"bird",
|
|
1419
|
-
"eagle",
|
|
1420
|
-
"owl",
|
|
1421
|
-
"pigeon",
|
|
1422
|
-
"fish",
|
|
1423
|
-
"whale",
|
|
1424
|
-
"dolphin",
|
|
1425
|
-
"snake",
|
|
1426
|
-
"turtle",
|
|
1427
|
-
"lizard"
|
|
1428
|
-
];
|
|
1429
|
-
const personClasses = [
|
|
1430
|
-
"person",
|
|
1431
|
-
"people",
|
|
1432
|
-
"pedestrian",
|
|
1433
|
-
"rider",
|
|
1434
|
-
"driver",
|
|
1435
|
-
"cyclist",
|
|
1436
|
-
"skier",
|
|
1437
|
-
"skateboarder",
|
|
1438
|
-
"face",
|
|
1439
|
-
"hand",
|
|
1440
|
-
"head",
|
|
1441
|
-
"body"
|
|
1442
|
-
];
|
|
1443
|
-
const vehicleClasses = [
|
|
1444
|
-
"vehicle",
|
|
1445
|
-
"car",
|
|
1446
|
-
"truck",
|
|
1447
|
-
"bus",
|
|
1448
|
-
"motorcycle",
|
|
1449
|
-
"bicycle",
|
|
1450
|
-
"van",
|
|
1451
|
-
"ambulance",
|
|
1452
|
-
"police_car",
|
|
1453
|
-
"fire_truck",
|
|
1454
|
-
"train",
|
|
1455
|
-
"subway",
|
|
1456
|
-
"tram",
|
|
1457
|
-
"airplane",
|
|
1458
|
-
"boat",
|
|
1459
|
-
"ship",
|
|
1460
|
-
"helicopter"
|
|
1461
|
-
];
|
|
1462
|
-
const faceClasses = [
|
|
1463
|
-
"face",
|
|
1464
|
-
"eyes",
|
|
1465
|
-
"nose",
|
|
1466
|
-
"mouth",
|
|
1467
|
-
"ears",
|
|
1468
|
-
"eyebrows",
|
|
1469
|
-
"left_eye",
|
|
1470
|
-
"right_eye",
|
|
1471
|
-
"pupil",
|
|
1472
|
-
"iris",
|
|
1473
|
-
"eyelid",
|
|
1474
|
-
"eye_corner",
|
|
1475
|
-
"upper_lip",
|
|
1476
|
-
"lower_lip",
|
|
1477
|
-
"teeth",
|
|
1478
|
-
"chin",
|
|
1479
|
-
"cheek",
|
|
1480
|
-
"forehead",
|
|
1481
|
-
"jaw",
|
|
1482
|
-
"glasses",
|
|
1483
|
-
"sunglasses",
|
|
1484
|
-
"facial_hair",
|
|
1485
|
-
"beard",
|
|
1486
|
-
"mustache",
|
|
1487
|
-
"facial_landmark",
|
|
1488
|
-
"facial_keypoint"
|
|
1489
|
-
];
|
|
1490
|
-
const licensePlateClasses = [
|
|
1491
|
-
"plate",
|
|
1492
|
-
"license_plate",
|
|
1493
|
-
"front_plate",
|
|
1494
|
-
"rear_plate",
|
|
1495
|
-
"motorcycle_plate",
|
|
1496
|
-
"temporary_plate",
|
|
1497
|
-
"dealer_plate",
|
|
1498
|
-
"licensePlate",
|
|
1499
|
-
"plate_number",
|
|
1500
|
-
"plate_character",
|
|
1501
|
-
"plate_digit",
|
|
1502
|
-
"plate_letter",
|
|
1503
|
-
"plate_symbol",
|
|
1504
|
-
"plate_region",
|
|
1505
|
-
"plate_country_identifier",
|
|
1506
|
-
"plate_frame",
|
|
1507
|
-
"plate_bolt",
|
|
1508
|
-
"plate_sticker",
|
|
1509
|
-
"plate_validation_tag",
|
|
1510
|
-
"damaged_plate",
|
|
1511
|
-
"obscured_plate",
|
|
1512
|
-
"dirty_plate"
|
|
1513
|
-
];
|
|
1514
|
-
const motionClasses = ["motion", "movement", "other"];
|
|
1515
|
-
const packageClasses = ["package", "packet"];
|
|
1516
|
-
const audioClasses = [
|
|
1517
|
-
"audio"
|
|
1518
|
-
/* Audio */
|
|
1519
|
-
];
|
|
1520
|
-
const audioLabelClasses = [
|
|
1521
|
-
"speech",
|
|
1522
|
-
"scream",
|
|
1523
|
-
"babbling",
|
|
1524
|
-
"yell",
|
|
1525
|
-
"bellow",
|
|
1526
|
-
"whoop",
|
|
1527
|
-
"whispering",
|
|
1528
|
-
"laughter",
|
|
1529
|
-
"snicker",
|
|
1530
|
-
"crying",
|
|
1531
|
-
"cry",
|
|
1532
|
-
"sigh",
|
|
1533
|
-
"singing",
|
|
1534
|
-
"choir",
|
|
1535
|
-
"chant",
|
|
1536
|
-
"mantra",
|
|
1537
|
-
"child_singing",
|
|
1538
|
-
"rapping",
|
|
1539
|
-
"humming",
|
|
1540
|
-
"groan",
|
|
1541
|
-
"grunt",
|
|
1542
|
-
"whistling",
|
|
1543
|
-
"breathing",
|
|
1544
|
-
"wheeze",
|
|
1545
|
-
"snoring",
|
|
1546
|
-
"gasp",
|
|
1547
|
-
"pant",
|
|
1548
|
-
"snort",
|
|
1549
|
-
"cough",
|
|
1550
|
-
"throat_clearing",
|
|
1551
|
-
"sneeze",
|
|
1552
|
-
"sniff",
|
|
1553
|
-
"cheering",
|
|
1554
|
-
"applause",
|
|
1555
|
-
"chatter",
|
|
1556
|
-
"crowd",
|
|
1557
|
-
"children_playing",
|
|
1558
|
-
"bark",
|
|
1559
|
-
"yip",
|
|
1560
|
-
"howl",
|
|
1561
|
-
"bow-wow",
|
|
1562
|
-
"growling",
|
|
1563
|
-
"whimper_dog",
|
|
1564
|
-
"purr",
|
|
1565
|
-
"meow",
|
|
1566
|
-
"hiss",
|
|
1567
|
-
"caterwaul",
|
|
1568
|
-
"pets",
|
|
1569
|
-
"livestock",
|
|
1570
|
-
"doorbell",
|
|
1571
|
-
"ding-dong",
|
|
1572
|
-
"door",
|
|
1573
|
-
"slam",
|
|
1574
|
-
"knock",
|
|
1575
|
-
"alarm",
|
|
1576
|
-
"telephone",
|
|
1577
|
-
"music",
|
|
1578
|
-
"dog",
|
|
1579
|
-
"dogs"
|
|
1580
|
-
];
|
|
1581
|
-
const doorbellClasses = ["doorbell", "ring"];
|
|
1582
|
-
const sensorLabelClasses = [
|
|
1583
|
-
"lock",
|
|
1584
|
-
"binary",
|
|
1585
|
-
"flood",
|
|
1586
|
-
"entry",
|
|
1587
|
-
"door",
|
|
1588
|
-
"leak",
|
|
1589
|
-
"door_open",
|
|
1590
|
-
"flooded",
|
|
1591
|
-
"entry_open"
|
|
1592
|
-
];
|
|
1593
|
-
const detectionClassesDefaultMap = {
|
|
1594
|
-
...animalClasses.reduce((tot, curr) => ({
|
|
1595
|
-
...tot,
|
|
1596
|
-
[curr]: "animal"
|
|
1597
|
-
/* Animal */
|
|
1598
|
-
}), {}),
|
|
1599
|
-
...personClasses.reduce((tot, curr) => ({
|
|
1600
|
-
...tot,
|
|
1601
|
-
[curr]: "person"
|
|
1602
|
-
/* Person */
|
|
1603
|
-
}), {}),
|
|
1604
|
-
...vehicleClasses.reduce((tot, curr) => ({
|
|
1605
|
-
...tot,
|
|
1606
|
-
[curr]: "vehicle"
|
|
1607
|
-
/* Vehicle */
|
|
1608
|
-
}), {}),
|
|
1609
|
-
...motionClasses.reduce((tot, curr) => ({
|
|
1610
|
-
...tot,
|
|
1611
|
-
[curr]: "motion"
|
|
1612
|
-
/* Motion */
|
|
1613
|
-
}), {}),
|
|
1614
|
-
...packageClasses.reduce((tot, curr) => ({
|
|
1615
|
-
...tot,
|
|
1616
|
-
[curr]: "package"
|
|
1617
|
-
/* Package */
|
|
1618
|
-
}), {}),
|
|
1619
|
-
...faceClasses.reduce((tot, curr) => ({
|
|
1620
|
-
...tot,
|
|
1621
|
-
[curr]: "face"
|
|
1622
|
-
/* Face */
|
|
1623
|
-
}), {}),
|
|
1624
|
-
...licensePlateClasses.reduce((tot, curr) => ({
|
|
1625
|
-
...tot,
|
|
1626
|
-
[curr]: "plate"
|
|
1627
|
-
/* Plate */
|
|
1628
|
-
}), {}),
|
|
1629
|
-
...audioClasses.reduce((tot, curr) => ({
|
|
1630
|
-
...tot,
|
|
1631
|
-
[curr]: "audio"
|
|
1632
|
-
/* Audio */
|
|
1633
|
-
}), {}),
|
|
1634
|
-
...audioLabelClasses.reduce((tot, curr) => ({
|
|
1635
|
-
...tot,
|
|
1636
|
-
[curr]: "audio"
|
|
1637
|
-
/* Audio */
|
|
1638
|
-
}), {}),
|
|
1639
|
-
...doorbellClasses.reduce((tot, curr) => ({
|
|
1640
|
-
...tot,
|
|
1641
|
-
[curr]: "doorbell"
|
|
1642
|
-
/* Doorbell */
|
|
1643
|
-
}), {}),
|
|
1644
|
-
...sensorLabelClasses.reduce((tot, curr) => ({
|
|
1645
|
-
...tot,
|
|
1646
|
-
[curr]: "sensor"
|
|
1647
|
-
/* Sensor */
|
|
1648
|
-
}), {})
|
|
1649
|
-
};
|
|
1650
|
-
const isFaceClassname = (c) => faceClasses.includes(c);
|
|
1651
|
-
const isPlateClassname = (c) => licensePlateClasses.includes(c);
|
|
1652
|
-
const isAnimalClassname = (c) => animalClasses.includes(c);
|
|
1653
|
-
const isPersonClassname = (c) => personClasses.includes(c);
|
|
1654
|
-
const isVehicleClassname = (c) => vehicleClasses.includes(c);
|
|
1655
|
-
const isMotionClassname = (c) => motionClasses.includes(c);
|
|
1656
|
-
const isDoorbellClassname = (c) => doorbellClasses.includes(c);
|
|
1657
|
-
const isPackageClassname = (c) => packageClasses.includes(c);
|
|
1658
|
-
const isAudioClassname = (c) => audioClasses.includes(c) || audioLabelClasses.includes(c);
|
|
1659
|
-
const isSensorLabelClassname = (c) => sensorLabelClasses.includes(c);
|
|
1660
|
-
const isLabelDetection = (c) => isFaceClassname(c) || isPlateClassname(c);
|
|
1661
|
-
const getParentClass = (className) => detectionClassesDefaultMap[className];
|
|
1662
|
-
const getParentDetectionClass = (det) => {
|
|
1663
|
-
const { className } = det;
|
|
1664
|
-
const baseMap = {
|
|
1665
|
-
[
|
|
1666
|
-
"face"
|
|
1667
|
-
/* Face */
|
|
1668
|
-
]: "person",
|
|
1669
|
-
[
|
|
1670
|
-
"plate"
|
|
1671
|
-
/* Plate */
|
|
1672
|
-
]: "vehicle"
|
|
1673
|
-
/* Vehicle */
|
|
1674
|
-
};
|
|
1675
|
-
const parentGroup = detectionClassesDefaultMap[className];
|
|
1676
|
-
if (parentGroup && parentGroup !== className) return parentGroup;
|
|
1677
|
-
return baseMap[className];
|
|
1678
|
-
};
|
|
1679
|
-
const defaultDetectionClasses = Object.values(DetectionClass);
|
|
1680
|
-
const DEFAULT_ENABLED_CLASSES = defaultDetectionClasses.filter(
|
|
1681
|
-
(c) => c !== "motion"
|
|
1682
|
-
/* Motion */
|
|
1683
|
-
);
|
|
1684
|
-
const TIMELINE_PRESET_CRITICAL = [
|
|
1685
|
-
"person",
|
|
1686
|
-
"doorbell",
|
|
1687
|
-
"package"
|
|
1688
|
-
/* Package */
|
|
1689
|
-
];
|
|
1690
|
-
const TIMELINE_PRESET_IMPORTANT = [
|
|
1691
|
-
...TIMELINE_PRESET_CRITICAL,
|
|
1692
|
-
"vehicle",
|
|
1693
|
-
"animal",
|
|
1694
|
-
"audio",
|
|
1695
|
-
"face",
|
|
1696
|
-
"plate"
|
|
1697
|
-
/* Plate */
|
|
1698
|
-
];
|
|
1699
|
-
const TIMELINE_PRESET_ALL = [...DEFAULT_ENABLED_CLASSES];
|
|
1700
|
-
function getClassesForTimelinePreset(preset, customClasses) {
|
|
1701
|
-
switch (preset) {
|
|
1702
|
-
case "critical":
|
|
1703
|
-
return TIMELINE_PRESET_CRITICAL;
|
|
1704
|
-
case "important":
|
|
1705
|
-
return TIMELINE_PRESET_IMPORTANT;
|
|
1706
|
-
case "all":
|
|
1707
|
-
return TIMELINE_PRESET_ALL;
|
|
1708
|
-
case "custom":
|
|
1709
|
-
return customClasses?.length ? customClasses : DEFAULT_ENABLED_CLASSES;
|
|
1710
|
-
default:
|
|
1711
|
-
return DEFAULT_ENABLED_CLASSES;
|
|
1712
|
-
}
|
|
1713
|
-
}
|
|
1714
|
-
const RAW_TO_CANONICAL = {
|
|
1715
|
-
// Scrypted PascalCase
|
|
1716
|
-
Light: "light",
|
|
1717
|
-
Switch: "switch",
|
|
1718
|
-
WindowCovering: "cover",
|
|
1719
|
-
Lock: "lock",
|
|
1720
|
-
SecuritySystem: "alarm",
|
|
1721
|
-
Buttons: "button",
|
|
1722
|
-
Select: "select",
|
|
1723
|
-
Siren: "siren",
|
|
1724
|
-
Sensor: "sensor",
|
|
1725
|
-
Entry: "entry",
|
|
1726
|
-
Program: "script",
|
|
1727
|
-
MediaPlayer: "media_player",
|
|
1728
|
-
Outlet: "switch",
|
|
1729
|
-
// Home Assistant lowercase domains
|
|
1730
|
-
light: "light",
|
|
1731
|
-
switch: "switch",
|
|
1732
|
-
input_boolean: "switch",
|
|
1733
|
-
cover: "cover",
|
|
1734
|
-
lock: "lock",
|
|
1735
|
-
alarm_control_panel: "alarm",
|
|
1736
|
-
input_button: "button",
|
|
1737
|
-
button: "button",
|
|
1738
|
-
input_select: "select",
|
|
1739
|
-
select: "select",
|
|
1740
|
-
siren: "siren",
|
|
1741
|
-
sensor: "sensor",
|
|
1742
|
-
media_player: "media_player",
|
|
1743
|
-
script: "script"
|
|
1744
|
-
};
|
|
1745
|
-
const HA_DOMAIN_TYPE_MAP = {
|
|
1746
|
-
light: "light",
|
|
1747
|
-
switch: "switch",
|
|
1748
|
-
input_boolean: "switch",
|
|
1749
|
-
cover: "cover",
|
|
1750
|
-
lock: "lock",
|
|
1751
|
-
alarm_control_panel: "alarm",
|
|
1752
|
-
input_button: "button",
|
|
1753
|
-
button: "button",
|
|
1754
|
-
input_select: "select",
|
|
1755
|
-
select: "select",
|
|
1756
|
-
siren: "siren",
|
|
1757
|
-
sensor: "sensor",
|
|
1758
|
-
binary_sensor: "sensor",
|
|
1759
|
-
media_player: "media_player",
|
|
1760
|
-
script: "script",
|
|
1761
|
-
climate: "climate",
|
|
1762
|
-
camera: "camera",
|
|
1763
|
-
fan: "fan",
|
|
1764
|
-
vacuum: "vacuum",
|
|
1765
|
-
automation: "automation",
|
|
1766
|
-
scene: "scene",
|
|
1767
|
-
input_number: "sensor",
|
|
1768
|
-
person: "person",
|
|
1769
|
-
device_tracker: "tracker",
|
|
1770
|
-
weather: "weather",
|
|
1771
|
-
water_heater: "climate"
|
|
1772
|
-
};
|
|
1773
|
-
const SCRYPTED_TYPE_TO_CANONICAL = {
|
|
1774
|
-
Light: "light",
|
|
1775
|
-
Switch: "switch",
|
|
1776
|
-
WindowCovering: "cover",
|
|
1777
|
-
Lock: "lock",
|
|
1778
|
-
SecuritySystem: "alarm",
|
|
1779
|
-
Buttons: "button",
|
|
1780
|
-
Select: "select",
|
|
1781
|
-
Siren: "siren",
|
|
1782
|
-
Sensor: "sensor",
|
|
1783
|
-
Entry: "entry",
|
|
1784
|
-
Program: "script",
|
|
1785
|
-
MediaPlayer: "media_player",
|
|
1786
|
-
Camera: "camera",
|
|
1787
|
-
Doorbell: "doorbell",
|
|
1788
|
-
Fan: "fan",
|
|
1789
|
-
Outlet: "switch"
|
|
1790
|
-
};
|
|
1791
|
-
function getCanonicalDeviceType(rawType) {
|
|
1792
|
-
const canonical = RAW_TO_CANONICAL[rawType];
|
|
1793
|
-
if (canonical) return canonical;
|
|
1794
|
-
const lower = rawType.toLowerCase();
|
|
1795
|
-
return RAW_TO_CANONICAL[lower] ?? null;
|
|
1796
|
-
}
|
|
1797
|
-
const ELIGIBLE_SCRYPTED_DEVICE_TYPES = [
|
|
1798
|
-
"Entry",
|
|
1799
|
-
"Light",
|
|
1800
|
-
"Switch",
|
|
1801
|
-
"Lock",
|
|
1802
|
-
"SecuritySystem",
|
|
1803
|
-
"Buttons",
|
|
1804
|
-
"WindowCovering",
|
|
1805
|
-
"Siren",
|
|
1806
|
-
"Sensor",
|
|
1807
|
-
"Select",
|
|
1808
|
-
"Program"
|
|
1809
|
-
];
|
|
1810
|
-
const ELIGIBLE_SCRYPTED_DEVICE_TYPES_SET = new Set(ELIGIBLE_SCRYPTED_DEVICE_TYPES);
|
|
1811
|
-
const ELIGIBLE_HA_DOMAINS = [
|
|
1812
|
-
"light",
|
|
1813
|
-
"switch",
|
|
1814
|
-
"input_boolean",
|
|
1815
|
-
"cover",
|
|
1816
|
-
"lock",
|
|
1817
|
-
"alarm_control_panel",
|
|
1818
|
-
"input_button",
|
|
1819
|
-
"button",
|
|
1820
|
-
"input_select",
|
|
1821
|
-
"select",
|
|
1822
|
-
"siren",
|
|
1823
|
-
"media_player",
|
|
1824
|
-
"script"
|
|
1825
|
-
];
|
|
1826
|
-
const ELIGIBLE_HA_DOMAINS_SET = new Set(ELIGIBLE_HA_DOMAINS);
|
|
1827
|
-
const FEATURE_MATRIX = [
|
|
1828
|
-
{
|
|
1829
|
-
id: "liveStream",
|
|
1830
|
-
label: "Live Stream",
|
|
1831
|
-
sources: { frigate: true, scrypted: true, rtsp: true },
|
|
1832
|
-
adapterMethod: "getLiveStream"
|
|
1833
|
-
},
|
|
1834
|
-
{
|
|
1835
|
-
id: "multiResolution",
|
|
1836
|
-
label: "Multi-Resolution",
|
|
1837
|
-
sources: { frigate: true, scrypted: true, rtsp: false },
|
|
1838
|
-
adapterMethod: "getResolutions"
|
|
1839
|
-
},
|
|
1840
|
-
{
|
|
1841
|
-
id: "motion",
|
|
1842
|
-
label: "Motion Detection",
|
|
1843
|
-
sources: { frigate: false, scrypted: true, rtsp: false },
|
|
1844
|
-
adapterMethod: "getMotion"
|
|
1845
|
-
},
|
|
1846
|
-
{
|
|
1847
|
-
id: "objectDetection",
|
|
1848
|
-
label: "Object Detection",
|
|
1849
|
-
sources: { frigate: false, scrypted: true, rtsp: false },
|
|
1850
|
-
adapterMethod: "getObjectDetections"
|
|
1851
|
-
},
|
|
1852
|
-
{
|
|
1853
|
-
id: "audioVolume",
|
|
1854
|
-
label: "Audio Level",
|
|
1855
|
-
sources: { frigate: false, scrypted: true, rtsp: false },
|
|
1856
|
-
adapterMethod: "getAudioVolume"
|
|
1857
|
-
},
|
|
1858
|
-
{
|
|
1859
|
-
id: "audioVolumes",
|
|
1860
|
-
label: "Audio Volumes (dBFS)",
|
|
1861
|
-
sources: { frigate: false, scrypted: true, rtsp: false },
|
|
1862
|
-
adapterMethod: "getAudioVolumes"
|
|
1863
|
-
},
|
|
1864
|
-
{
|
|
1865
|
-
id: "ptz",
|
|
1866
|
-
label: "PTZ Control",
|
|
1867
|
-
sources: { frigate: false, scrypted: true, rtsp: false },
|
|
1868
|
-
adapterMethod: "getPTZ"
|
|
1869
|
-
},
|
|
1870
|
-
{
|
|
1871
|
-
id: "intercom",
|
|
1872
|
-
label: "Intercom (Mic)",
|
|
1873
|
-
sources: { frigate: false, scrypted: true, rtsp: false },
|
|
1874
|
-
adapterMethod: "getIntercomSupport"
|
|
1875
|
-
},
|
|
1876
|
-
{
|
|
1877
|
-
id: "deviceStatus",
|
|
1878
|
-
label: "Device Status",
|
|
1879
|
-
sources: { frigate: false, scrypted: true, rtsp: false },
|
|
1880
|
-
adapterMethod: "getStatus"
|
|
1881
|
-
},
|
|
1882
|
-
{
|
|
1883
|
-
id: "timeline",
|
|
1884
|
-
label: "Detection Timeline",
|
|
1885
|
-
sources: { frigate: true, scrypted: true, rtsp: false },
|
|
1886
|
-
adapterMethod: "getCameraDayData"
|
|
1887
|
-
},
|
|
1888
|
-
{
|
|
1889
|
-
id: "clusteredTimeline",
|
|
1890
|
-
label: "Clustered Timeline",
|
|
1891
|
-
sources: { frigate: true, scrypted: true, rtsp: false },
|
|
1892
|
-
requiresBackend: true,
|
|
1893
|
-
adapterMethod: "getClusteredDayData"
|
|
1894
|
-
},
|
|
1895
|
-
{
|
|
1896
|
-
id: "detectionClasses",
|
|
1897
|
-
label: "Detection Classes",
|
|
1898
|
-
sources: { frigate: true, scrypted: true, rtsp: false },
|
|
1899
|
-
adapterMethod: "getDetectionClasses"
|
|
1900
|
-
},
|
|
1901
|
-
{
|
|
1902
|
-
id: "videoClips",
|
|
1903
|
-
label: "Video Clips",
|
|
1904
|
-
sources: { frigate: true, scrypted: true, rtsp: false },
|
|
1905
|
-
adapterMethod: "getVideoClips"
|
|
1906
|
-
},
|
|
1907
|
-
{
|
|
1908
|
-
id: "nvrPlayback",
|
|
1909
|
-
label: "NVR Playback",
|
|
1910
|
-
sources: { frigate: true, scrypted: true, rtsp: false },
|
|
1911
|
-
adapterMethod: "getNvrPlaybackSupported"
|
|
1912
|
-
},
|
|
1913
|
-
{
|
|
1914
|
-
id: "nvrScrub",
|
|
1915
|
-
label: "NVR Scrub/Seek",
|
|
1916
|
-
sources: { frigate: false, scrypted: true, rtsp: false },
|
|
1917
|
-
adapterMethod: "seekRecordingStream"
|
|
1918
|
-
},
|
|
1919
|
-
{
|
|
1920
|
-
id: "recordingThumbnail",
|
|
1921
|
-
label: "Recording Thumbnails",
|
|
1922
|
-
sources: { frigate: true, scrypted: true, rtsp: false },
|
|
1923
|
-
adapterMethod: "getRecordingStreamThumbnail"
|
|
1924
|
-
},
|
|
1925
|
-
{
|
|
1926
|
-
id: "nvrSeekToLive",
|
|
1927
|
-
label: "Seek to Live",
|
|
1928
|
-
sources: { frigate: false, scrypted: true, rtsp: false },
|
|
1929
|
-
adapterMethod: "seekNvrToLive"
|
|
1930
|
-
}
|
|
1931
|
-
];
|
|
1932
|
-
function isFeatureAvailable(featureId, source, platform) {
|
|
1933
|
-
const entry = FEATURE_MATRIX.find((f) => f.id === featureId);
|
|
1934
|
-
if (!entry) return false;
|
|
1935
|
-
if (!entry.sources[source]) return false;
|
|
1936
|
-
if (entry.platforms && entry.platforms[platform] === false) return false;
|
|
1937
|
-
return true;
|
|
1938
|
-
}
|
|
1939
|
-
function getSourceFeatures(source) {
|
|
1940
|
-
return FEATURE_MATRIX.filter((f) => f.sources[source]);
|
|
1941
|
-
}
|
|
1942
|
-
function getBackendRequiredFeatures() {
|
|
1943
|
-
return FEATURE_MATRIX.filter((f) => f.requiresBackend);
|
|
1944
|
-
}
|
|
1945
|
-
function selectOptimalStream(streams, constraints, defaultTransport) {
|
|
1946
|
-
if (streams.length === 0) return null;
|
|
1947
|
-
const viewportPixels = constraints.viewportWidth * constraints.viewportHeight * (constraints.pixelRatio ?? 1);
|
|
1948
|
-
const bw = constraints.bandwidthMbps ?? 100;
|
|
1949
|
-
const loss = constraints.packetLoss ?? 0;
|
|
1950
|
-
const rtt = constraints.rttMs ?? 50;
|
|
1951
|
-
let targetTier = "high";
|
|
1952
|
-
if (constraints.maxResolution === "low" || constraints.isCellular || bw < 1 || loss > 0.05) {
|
|
1953
|
-
targetTier = "low";
|
|
1954
|
-
} else if (constraints.maxResolution === "medium" || bw < 5 || viewportPixels < 5e5 || rtt > 200) {
|
|
1955
|
-
targetTier = "medium";
|
|
1956
|
-
} else if (constraints.maxResolution === "high" || viewportPixels > 2e6) {
|
|
1957
|
-
targetTier = "high";
|
|
1958
|
-
}
|
|
1959
|
-
const PROFILE_TIER = {
|
|
1960
|
-
main: "high",
|
|
1961
|
-
sub: "medium",
|
|
1962
|
-
ext: "low"
|
|
1963
|
-
};
|
|
1964
|
-
const preferTransport = defaultTransport ?? "native";
|
|
1965
|
-
const scored = streams.map((s) => {
|
|
1966
|
-
let score = 0;
|
|
1967
|
-
const streamTier = PROFILE_TIER[s.profile] ?? "medium";
|
|
1968
|
-
if (streamTier === targetTier) score += 100;
|
|
1969
|
-
else if (targetTier === "high" && streamTier === "medium" || targetTier === "medium" && streamTier === "high" || targetTier === "medium" && streamTier === "low" || targetTier === "low" && streamTier === "medium") score += 50;
|
|
1970
|
-
if (s.transport === preferTransport) score += 30;
|
|
1971
|
-
else if (s.transport === "native") score += 20;
|
|
1972
|
-
else if (s.transport === "rtsp") score += 15;
|
|
1973
|
-
else if (s.transport === "rtmp") score += 10;
|
|
1974
|
-
if (s.metadata?.width && s.metadata?.height) {
|
|
1975
|
-
const streamPixels = s.metadata.width * s.metadata.height;
|
|
1976
|
-
const ratio = streamPixels / Math.max(viewportPixels, 1);
|
|
1977
|
-
if (ratio >= 0.8 && ratio <= 2) score += 25;
|
|
1978
|
-
else if (ratio >= 0.5 && ratio <= 3) score += 15;
|
|
1979
|
-
if (ratio > 4 && bw < 10) score -= 20;
|
|
1980
|
-
}
|
|
1981
|
-
if (s.metadata?.codec) {
|
|
1982
|
-
if (bw < 5 && s.metadata.codec.includes("265")) score += 10;
|
|
1983
|
-
if (bw >= 10 && s.metadata.codec.includes("264")) score += 5;
|
|
1984
|
-
}
|
|
1985
|
-
return { ...s, score, targetTier };
|
|
1986
|
-
});
|
|
1987
|
-
scored.sort((a, b) => b.score - a.score);
|
|
1988
|
-
const best = scored[0];
|
|
1989
|
-
return {
|
|
1990
|
-
streamName: best.streamName,
|
|
1991
|
-
profile: best.profile,
|
|
1992
|
-
transport: best.transport,
|
|
1993
|
-
label: best.label,
|
|
1994
|
-
metadata: best.metadata,
|
|
1995
|
-
reason: `${best.targetTier} quality → ${best.profile}/${best.transport} (score: ${best.score})`
|
|
1996
|
-
};
|
|
1997
|
-
}
|
|
1998
|
-
function getNextEvalInterval(constraints, wasSwitch) {
|
|
1999
|
-
if (wasSwitch) return 10;
|
|
2000
|
-
if (constraints.isCellular) return 15;
|
|
2001
|
-
if ((constraints.packetLoss ?? 0) > 0.02) return 15;
|
|
2002
|
-
return 30;
|
|
2003
|
-
}
|
|
2004
|
-
export {
|
|
2005
|
-
DEFAULT_ENABLED_CLASSES,
|
|
2006
|
-
DetectionClass,
|
|
2007
|
-
ELIGIBLE_HA_DOMAINS,
|
|
2008
|
-
ELIGIBLE_HA_DOMAINS_SET,
|
|
2009
|
-
ELIGIBLE_SCRYPTED_DEVICE_TYPES,
|
|
2010
|
-
ELIGIBLE_SCRYPTED_DEVICE_TYPES_SET,
|
|
2011
|
-
FEATURE_MATRIX,
|
|
2012
|
-
HA_DOMAIN_TYPE_MAP,
|
|
2013
|
-
RAW_TO_CANONICAL,
|
|
2014
|
-
SCRYPTED_TYPE_TO_CANONICAL,
|
|
2015
|
-
System,
|
|
2016
|
-
TIMELINE_PRESET_ALL,
|
|
2017
|
-
TIMELINE_PRESET_CRITICAL,
|
|
2018
|
-
TIMELINE_PRESET_IMPORTANT,
|
|
2019
|
-
animalClasses,
|
|
2020
|
-
audioClasses,
|
|
2021
|
-
audioLabelClasses,
|
|
2022
|
-
createSystem,
|
|
2023
|
-
defaultDetectionClasses,
|
|
2024
|
-
detectionClassesDefaultMap,
|
|
2025
|
-
doorbellClasses,
|
|
2026
|
-
faceClasses,
|
|
2027
|
-
getBackendRequiredFeatures,
|
|
2028
|
-
getCanonicalDeviceType,
|
|
2029
|
-
getClassesForTimelinePreset,
|
|
2030
|
-
getNextEvalInterval,
|
|
2031
|
-
getParentClass,
|
|
2032
|
-
getParentDetectionClass,
|
|
2033
|
-
getSourceFeatures,
|
|
2034
|
-
isAnimalClassname,
|
|
2035
|
-
isAudioClassname,
|
|
2036
|
-
isDoorbellClassname,
|
|
2037
|
-
isFaceClassname,
|
|
2038
|
-
isFeatureAvailable,
|
|
2039
|
-
isLabelDetection,
|
|
2040
|
-
isMotionClassname,
|
|
2041
|
-
isPackageClassname,
|
|
2042
|
-
isPersonClassname,
|
|
2043
|
-
isPlateClassname,
|
|
2044
|
-
isSensorLabelClassname,
|
|
2045
|
-
isVehicleClassname,
|
|
2046
|
-
licensePlateClasses,
|
|
2047
|
-
motionClasses,
|
|
2048
|
-
packageClasses,
|
|
2049
|
-
personClasses,
|
|
2050
|
-
raceFastestEndpoint,
|
|
2051
|
-
selectOptimalStream,
|
|
2052
|
-
sensorLabelClasses,
|
|
2053
|
-
vehicleClasses
|
|
2054
|
-
};
|