@camstack/sdk 0.1.55 → 0.2.0

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