@camstack/addon-pipeline 1.1.25 → 1.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/audio-analyzer/index.js +1 -1
  2. package/dist/audio-analyzer/index.mjs +1 -1
  3. package/dist/audio-codec-ffmpeg/index.js +1 -1
  4. package/dist/audio-codec-ffmpeg/index.mjs +1 -1
  5. package/dist/decoder-ffmpeg/index.js +1 -1
  6. package/dist/decoder-ffmpeg/index.mjs +1 -1
  7. package/dist/detection-pipeline/index.js +148 -26
  8. package/dist/detection-pipeline/index.mjs +148 -26
  9. package/dist/{dist-BecXbIzJ.mjs → dist-CgEP_0OL.mjs} +730 -14
  10. package/dist/{dist-wzWBZ26C.js → dist-DAIlCdAx.js} +730 -14
  11. package/dist/{frame-handle-plane-DGg0Aevs.mjs → frame-handle-plane-Dq20KtKL.mjs} +1 -1
  12. package/dist/{frame-handle-plane-BAKiW6t4.js → frame-handle-plane-DtTRX_0n.js} +1 -1
  13. package/dist/motion-wasm/index.js +1 -1
  14. package/dist/motion-wasm/index.mjs +1 -1
  15. package/dist/pipeline-runner/index.js +38 -9
  16. package/dist/pipeline-runner/index.mjs +38 -9
  17. package/dist/recorder/index.js +1 -1
  18. package/dist/recorder/index.mjs +1 -1
  19. package/dist/stream-broker/_stub.js +2 -2
  20. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CWPU9tbs.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-5tQlh9h4.mjs} +2 -2
  21. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CkOPfV8r.mjs +26 -0
  22. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BJK0-svt.mjs +26 -0
  23. package/dist/stream-broker/{hostInit-B7Fx3R_6.mjs → hostInit-DyLqyJaS.mjs} +2 -2
  24. package/dist/stream-broker/index.js +2 -2
  25. package/dist/stream-broker/index.mjs +2 -2
  26. package/dist/stream-broker/remoteEntry.js +1 -1
  27. package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-CPkQfKnu.js → MaskShapeCanvas-DI4BY7W2-BDLNwJ_F.js} +1 -1
  28. package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-DlLMQBag.js → MotionZonesSettings-NcxxQN8r-CoLjNiUN.js} +1 -1
  29. package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-kNcauIAN.js → PrivacyMaskSettings-APgPLF7p-DJE3OU-q.js} +1 -1
  30. package/embed-dist/assets/index-C-pL8ETk.js +81 -0
  31. package/embed-dist/index.html +1 -1
  32. package/package.json +1 -1
  33. package/python/inference_pool.py +522 -27
  34. package/python/test_inference_pool_backpressure.py +121 -0
  35. package/python/test_inference_pool_coreml_cache.py +416 -0
  36. package/python/test_inference_pool_device_selection.py +256 -0
  37. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-wfWYFiTT.mjs +0 -26
  38. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-lpFwIH9e.mjs +0 -26
  39. package/embed-dist/assets/index-xpPLFfsT.js +0 -80
@@ -4627,7 +4627,7 @@ function _instanceof(cls, params = {}) {
4627
4627
  return inst;
4628
4628
  }
4629
4629
  //#endregion
4630
- //#region ../types/dist/sleep-MHm--th-.mjs
4630
+ //#region ../types/dist/sleep-BO1nweKv.mjs
4631
4631
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4632
4632
  EventCategory["SystemBoot"] = "system.boot";
4633
4633
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -8132,6 +8132,601 @@ function mapAudioLabelToMacro(label) {
8132
8132
  return _macroLookup.get(label.toLowerCase()) ?? null;
8133
8133
  }
8134
8134
  /**
8135
+ * Error types for the safe expression engine. Two distinct classes so callers
8136
+ * can tell a compile-time (grammar) failure from a runtime (evaluation)
8137
+ * failure — both are non-fatal to the host: read paths degrade to "skip link".
8138
+ */
8139
+ /** Thrown by the tokenizer / parser. Carries a 0-based source `position` when
8140
+ * the failure is anchored to a character (author-facing inline feedback). */
8141
+ var ExpressionParseError = class extends Error {
8142
+ position;
8143
+ constructor(message, position) {
8144
+ super(message);
8145
+ this.name = "ExpressionParseError";
8146
+ this.position = position;
8147
+ }
8148
+ };
8149
+ /** Thrown by the evaluator (unknown identifier, type mismatch, non-finite
8150
+ * result, unknown builtin, step-budget exceeded). */
8151
+ var ExpressionEvalError = class extends Error {
8152
+ constructor(message) {
8153
+ super(message);
8154
+ this.name = "ExpressionEvalError";
8155
+ }
8156
+ };
8157
+ /**
8158
+ * Resource-bound constants for the safe expression engine.
8159
+ *
8160
+ * Every bound is defense-in-depth: the grammar is non-Turing-complete (no
8161
+ * loops, recursion, lambdas or member access — see `ast.ts`), so evaluation is
8162
+ * O(nodeCount) by construction. These caps merely put a hard ceiling on the
8163
+ * work a single author-supplied expression can request, so a hostile or
8164
+ * accidental pathological string can never spend unbounded CPU/memory.
8165
+ */
8166
+ /** Max source length (chars) — checked BEFORE tokenizing so a huge string is
8167
+ * rejected without allocation. */
8168
+ var MAX_EXPRESSION_SOURCE_LENGTH = 2048;
8169
+ /** A legal binding / identifier name. */
8170
+ var EXPRESSION_IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
8171
+ /** Binding names an author may NOT use: `now` is auto-injected; the literal
8172
+ * keywords lex as values, not identifiers, so binding to them is meaningless. */
8173
+ var RESERVED_BINDING_NAMES = new Set([
8174
+ "now",
8175
+ "true",
8176
+ "false",
8177
+ "null"
8178
+ ]);
8179
+ /**
8180
+ * Tokenizer for the safe expression mini-language. Hand-rolled, single-pass,
8181
+ * zero-dependency. The grammar is deliberately boring: decimal numbers,
8182
+ * single/double-quoted strings with a tiny escape set, identifiers, the three
8183
+ * value keywords (`true`/`false`/`null`) and a fixed punctuator set. Anything
8184
+ * outside that — a bare `.`, `=`, `[`, `]`, `{`, `}`, `;`, backtick, `&`, `|` —
8185
+ * is a parse error with a source position, so member access / assignment /
8186
+ * template literals are lexically impossible.
8187
+ */
8188
+ var KEYWORDS = new Set([
8189
+ "true",
8190
+ "false",
8191
+ "null"
8192
+ ]);
8193
+ function isDigit(ch) {
8194
+ return ch >= "0" && ch <= "9";
8195
+ }
8196
+ function isIdentStart(ch) {
8197
+ return ch >= "A" && ch <= "Z" || ch >= "a" && ch <= "z" || ch === "_";
8198
+ }
8199
+ function isIdentPart(ch) {
8200
+ return isIdentStart(ch) || isDigit(ch);
8201
+ }
8202
+ function isWhitespace(ch) {
8203
+ return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f" || ch === "\v";
8204
+ }
8205
+ /** Tokenize `source` into a flat token list ending with a single `eof` token.
8206
+ * Throws `ExpressionParseError` on any illegal character or unterminated
8207
+ * string. */
8208
+ function tokenize(source) {
8209
+ if (source.length > 2048) throw new ExpressionParseError(`expression too long (${source.length} > ${MAX_EXPRESSION_SOURCE_LENGTH} chars)`, 0);
8210
+ const tokens = [];
8211
+ let i = 0;
8212
+ const n = source.length;
8213
+ while (i < n) {
8214
+ const ch = source[i];
8215
+ if (isWhitespace(ch)) {
8216
+ i += 1;
8217
+ continue;
8218
+ }
8219
+ if (isDigit(ch)) {
8220
+ const start = i;
8221
+ while (i < n && isDigit(source[i])) i += 1;
8222
+ if (i < n && source[i] === ".") {
8223
+ if (i + 1 >= n || !isDigit(source[i + 1])) throw new ExpressionParseError("malformed number: decimal point needs a digit", i);
8224
+ i += 1;
8225
+ while (i < n && isDigit(source[i])) i += 1;
8226
+ }
8227
+ const text = source.slice(start, i);
8228
+ const value = Number(text);
8229
+ if (!Number.isFinite(value)) throw new ExpressionParseError(`malformed number: '${text}'`, start);
8230
+ tokens.push({
8231
+ type: "number",
8232
+ value,
8233
+ pos: start
8234
+ });
8235
+ continue;
8236
+ }
8237
+ if (ch === "'" || ch === "\"") {
8238
+ const quote = ch;
8239
+ const start = i;
8240
+ i += 1;
8241
+ let out = "";
8242
+ let closed = false;
8243
+ while (i < n) {
8244
+ const c = source[i];
8245
+ if (c === "\\") {
8246
+ const next = i + 1 < n ? source[i + 1] : "";
8247
+ if (next === "\\" || next === "'" || next === "\"") {
8248
+ out += next;
8249
+ i += 2;
8250
+ continue;
8251
+ }
8252
+ throw new ExpressionParseError(`invalid string escape: '\\${next}'`, i);
8253
+ }
8254
+ if (c === quote) {
8255
+ closed = true;
8256
+ i += 1;
8257
+ break;
8258
+ }
8259
+ out += c;
8260
+ i += 1;
8261
+ }
8262
+ if (!closed) throw new ExpressionParseError("unterminated string literal", start);
8263
+ tokens.push({
8264
+ type: "string",
8265
+ value: out,
8266
+ pos: start
8267
+ });
8268
+ continue;
8269
+ }
8270
+ if (isIdentStart(ch)) {
8271
+ const start = i;
8272
+ while (i < n && isIdentPart(source[i])) i += 1;
8273
+ const text = source.slice(start, i);
8274
+ if (KEYWORDS.has(text)) tokens.push({
8275
+ type: "keyword",
8276
+ keyword: keywordOf(text),
8277
+ pos: start
8278
+ });
8279
+ else tokens.push({
8280
+ type: "identifier",
8281
+ name: text,
8282
+ pos: start
8283
+ });
8284
+ continue;
8285
+ }
8286
+ const two = i + 1 < n ? source.slice(i, i + 2) : "";
8287
+ if (two === "<=" || two === ">=" || two === "==" || two === "!=" || two === "&&" || two === "||") {
8288
+ tokens.push({
8289
+ type: "punct",
8290
+ punct: two,
8291
+ pos: i
8292
+ });
8293
+ i += 2;
8294
+ continue;
8295
+ }
8296
+ if (isSinglePunct(ch)) {
8297
+ tokens.push({
8298
+ type: "punct",
8299
+ punct: ch,
8300
+ pos: i
8301
+ });
8302
+ i += 1;
8303
+ continue;
8304
+ }
8305
+ throw new ExpressionParseError(`unexpected character '${ch}'`, i);
8306
+ }
8307
+ tokens.push({
8308
+ type: "eof",
8309
+ pos: n
8310
+ });
8311
+ return tokens;
8312
+ }
8313
+ function keywordOf(text) {
8314
+ if (text === "true") return "true";
8315
+ if (text === "false") return "false";
8316
+ return "null";
8317
+ }
8318
+ function isSinglePunct(ch) {
8319
+ return ch === "(" || ch === ")" || ch === "," || ch === "?" || ch === ":" || ch === "+" || ch === "-" || ch === "*" || ch === "/" || ch === "%" || ch === "!" || ch === "<" || ch === ">";
8320
+ }
8321
+ /**
8322
+ * Frozen, null-prototype builtin function table for the expression engine
8323
+ * (spec §4 rule 4). The table is the SOLE surface of callable functions: the
8324
+ * parser rejects any callee not in it, and the evaluator gates each call on an
8325
+ * own-property check against it.
8326
+ *
8327
+ * Because the object has a NULL prototype AND is `Object.freeze`d:
8328
+ * - it cannot be polluted (no `__proto__` / `constructor` write reaches it);
8329
+ * - a lookup for `toString` / `hasOwnProperty` / `constructor` finds NOTHING
8330
+ * (there is no `Object.prototype` in the chain), so those names are not
8331
+ * callable — they are simply "unknown function" at parse time.
8332
+ *
8333
+ * Every numeric argument is validated as a finite number and every numeric
8334
+ * RESULT is re-checked finite, so `/0`, `sqrt(-1)` (→ NaN) and overflow
8335
+ * (`pow(10,400)` → Infinity) all raise `ExpressionEvalError` and fail the link
8336
+ * closed rather than emitting a garbage value.
8337
+ */
8338
+ function asFiniteNumber(value, name, index) {
8339
+ if (typeof value !== "number" || !Number.isFinite(value)) throw new ExpressionEvalError(`${name}: argument ${index + 1} must be a finite number`);
8340
+ return value;
8341
+ }
8342
+ function asString$1(value, name, index) {
8343
+ if (typeof value !== "string") throw new ExpressionEvalError(`${name}: argument ${index + 1} must be a string`);
8344
+ return value;
8345
+ }
8346
+ function finiteResult(value, name) {
8347
+ if (!Number.isFinite(value)) throw new ExpressionEvalError(`${name}: produced a non-finite result`);
8348
+ return value;
8349
+ }
8350
+ function allFiniteNumbers(args, name) {
8351
+ return args.map((a, idx) => asFiniteNumber(a, name, idx));
8352
+ }
8353
+ var INF = Number.POSITIVE_INFINITY;
8354
+ var table = {
8355
+ min: {
8356
+ minArgs: 1,
8357
+ maxArgs: INF,
8358
+ apply: (args) => finiteResult(Math.min(...allFiniteNumbers(args, "min")), "min")
8359
+ },
8360
+ max: {
8361
+ minArgs: 1,
8362
+ maxArgs: INF,
8363
+ apply: (args) => finiteResult(Math.max(...allFiniteNumbers(args, "max")), "max")
8364
+ },
8365
+ abs: {
8366
+ minArgs: 1,
8367
+ maxArgs: 1,
8368
+ apply: (args) => finiteResult(Math.abs(asFiniteNumber(args[0], "abs", 0)), "abs")
8369
+ },
8370
+ floor: {
8371
+ minArgs: 1,
8372
+ maxArgs: 1,
8373
+ apply: (args) => finiteResult(Math.floor(asFiniteNumber(args[0], "floor", 0)), "floor")
8374
+ },
8375
+ ceil: {
8376
+ minArgs: 1,
8377
+ maxArgs: 1,
8378
+ apply: (args) => finiteResult(Math.ceil(asFiniteNumber(args[0], "ceil", 0)), "ceil")
8379
+ },
8380
+ sqrt: {
8381
+ minArgs: 1,
8382
+ maxArgs: 1,
8383
+ apply: (args) => finiteResult(Math.sqrt(asFiniteNumber(args[0], "sqrt", 0)), "sqrt")
8384
+ },
8385
+ round: {
8386
+ minArgs: 1,
8387
+ maxArgs: 2,
8388
+ apply: (args) => {
8389
+ const x = asFiniteNumber(args[0], "round", 0);
8390
+ const digits = args.length > 1 ? Math.trunc(asFiniteNumber(args[1], "round", 1)) : 0;
8391
+ if (digits < 0 || digits > 100) throw new ExpressionEvalError("round: digits must be between 0 and 100");
8392
+ const factor = 10 ** digits;
8393
+ return finiteResult(Math.round(x * factor) / factor, "round");
8394
+ }
8395
+ },
8396
+ pow: {
8397
+ minArgs: 2,
8398
+ maxArgs: 2,
8399
+ apply: (args) => finiteResult(asFiniteNumber(args[0], "pow", 0) ** asFiniteNumber(args[1], "pow", 1), "pow")
8400
+ },
8401
+ clamp: {
8402
+ minArgs: 3,
8403
+ maxArgs: 3,
8404
+ apply: (args) => {
8405
+ const x = asFiniteNumber(args[0], "clamp", 0);
8406
+ const lo = asFiniteNumber(args[1], "clamp", 1);
8407
+ const hi = asFiniteNumber(args[2], "clamp", 2);
8408
+ if (lo > hi) throw new ExpressionEvalError("clamp: lower bound is greater than upper bound");
8409
+ return finiteResult(Math.min(hi, Math.max(lo, x)), "clamp");
8410
+ }
8411
+ },
8412
+ avg: {
8413
+ minArgs: 1,
8414
+ maxArgs: INF,
8415
+ apply: (args) => {
8416
+ const nums = allFiniteNumbers(args, "avg");
8417
+ return finiteResult(nums.reduce((acc, v) => acc + v, 0) / nums.length, "avg");
8418
+ }
8419
+ },
8420
+ sum: {
8421
+ minArgs: 1,
8422
+ maxArgs: INF,
8423
+ apply: (args) => finiteResult(allFiniteNumbers(args, "sum").reduce((acc, v) => acc + v, 0), "sum")
8424
+ },
8425
+ coalesce: {
8426
+ minArgs: 1,
8427
+ maxArgs: INF,
8428
+ apply: (args) => {
8429
+ for (const a of args) if (a !== null) return a;
8430
+ return null;
8431
+ }
8432
+ },
8433
+ age: {
8434
+ minArgs: 2,
8435
+ maxArgs: 2,
8436
+ apply: (args) => finiteResult(asFiniteNumber(args[0], "age", 0) - asFiniteNumber(args[1], "age", 1), "age")
8437
+ },
8438
+ convert: {
8439
+ minArgs: 3,
8440
+ maxArgs: 3,
8441
+ apply: (args, hooks) => {
8442
+ const x = asFiniteNumber(args[0], "convert", 0);
8443
+ const from = asString$1(args[1], "convert", 1).trim();
8444
+ const to = asString$1(args[2], "convert", 2).trim();
8445
+ if (hooks.convert) {
8446
+ const out = hooks.convert(x, from, to);
8447
+ if (out === null) throw new ExpressionEvalError(`convert: cannot convert '${from}' to '${to}'`);
8448
+ return finiteResult(out, "convert");
8449
+ }
8450
+ if (from === to) return x;
8451
+ throw new ExpressionEvalError("convert: unit conversion table not installed");
8452
+ }
8453
+ }
8454
+ };
8455
+ Object.freeze(Object.assign(Object.create(null), table));
8456
+ /** The set of valid builtin names — used by the parser to reject unknown
8457
+ * callees at parse time (immediate author feedback). */
8458
+ var EXPRESSION_BUILTIN_NAMES = new Set(Object.keys(table));
8459
+ /**
8460
+ * Pratt (precedence-climbing) parser for the safe expression mini-language.
8461
+ *
8462
+ * Precedence (low → high): ternary `?:` (right-assoc) → `||` → `&&` → equality
8463
+ * → relational → additive → multiplicative → unary `! -` → call / primary.
8464
+ * Calls are ONLY `IDENT '(' args? ')'` at primary position — the callee is a
8465
+ * string validated against the builtin table at parse time, so an unknown
8466
+ * function is rejected immediately (author feedback) and a persisted expression
8467
+ * that references a since-removed builtin degrades at read.
8468
+ *
8469
+ * A node counter caps total AST size (`MAX_EXPRESSION_AST_NODES`) and call
8470
+ * arity is capped (`MAX_EXPRESSION_CALL_ARGS`) — both raise `ExpressionParseError`.
8471
+ */
8472
+ /** Binary/logical operator precedence (higher binds tighter). */
8473
+ var BINARY_PRECEDENCE = {
8474
+ "||": 1,
8475
+ "&&": 2,
8476
+ "==": 3,
8477
+ "!=": 3,
8478
+ "<": 4,
8479
+ "<=": 4,
8480
+ ">": 4,
8481
+ ">=": 4,
8482
+ "+": 5,
8483
+ "-": 5,
8484
+ "*": 6,
8485
+ "/": 6,
8486
+ "%": 6
8487
+ };
8488
+ function isLogicalOp(op) {
8489
+ return op === "&&" || op === "||";
8490
+ }
8491
+ function isBinaryOp(op) {
8492
+ return op === "+" || op === "-" || op === "*" || op === "/" || op === "%" || op === "==" || op === "!=" || op === "<" || op === "<=" || op === ">" || op === ">=";
8493
+ }
8494
+ var Parser = class {
8495
+ tokens;
8496
+ pos = 0;
8497
+ nodeCount = 0;
8498
+ identifiers = /* @__PURE__ */ new Set();
8499
+ callees = /* @__PURE__ */ new Set();
8500
+ constructor(tokens) {
8501
+ this.tokens = tokens;
8502
+ }
8503
+ parse() {
8504
+ const ast = this.parseTernary();
8505
+ const tok = this.peek();
8506
+ if (tok.type !== "eof") throw new ExpressionParseError("unexpected trailing input", tok.pos);
8507
+ return {
8508
+ ast,
8509
+ identifiers: this.identifiers,
8510
+ callees: this.callees,
8511
+ nodeCount: this.nodeCount
8512
+ };
8513
+ }
8514
+ peek() {
8515
+ return this.tokens[this.pos];
8516
+ }
8517
+ next() {
8518
+ return this.tokens[this.pos++];
8519
+ }
8520
+ /** Consume a punctuator token, erroring if the next token isn't it. */
8521
+ expectPunct(punct) {
8522
+ const tok = this.peek();
8523
+ if (tok.type !== "punct" || tok.punct !== punct) throw new ExpressionParseError(`expected '${punct}'`, tok.pos);
8524
+ this.pos += 1;
8525
+ }
8526
+ matchPunct(punct) {
8527
+ const tok = this.peek();
8528
+ if (tok.type === "punct" && tok.punct === punct) {
8529
+ this.pos += 1;
8530
+ return true;
8531
+ }
8532
+ return false;
8533
+ }
8534
+ countNode() {
8535
+ this.nodeCount += 1;
8536
+ if (this.nodeCount > 256) throw new ExpressionParseError("expression too complex", this.peek().pos);
8537
+ }
8538
+ parseTernary() {
8539
+ const test = this.parseBinary(1);
8540
+ if (this.matchPunct("?")) {
8541
+ const consequent = this.parseTernary();
8542
+ this.expectPunct(":");
8543
+ const alternate = this.parseTernary();
8544
+ this.countNode();
8545
+ return {
8546
+ kind: "conditional",
8547
+ test,
8548
+ consequent,
8549
+ alternate
8550
+ };
8551
+ }
8552
+ return test;
8553
+ }
8554
+ parseBinary(minPrec) {
8555
+ let left = this.parseUnary();
8556
+ for (;;) {
8557
+ const tok = this.peek();
8558
+ if (tok.type !== "punct") break;
8559
+ const prec = BINARY_PRECEDENCE[tok.punct];
8560
+ if (prec === void 0 || prec < minPrec) break;
8561
+ const op = tok.punct;
8562
+ this.pos += 1;
8563
+ const right = this.parseBinary(prec + 1);
8564
+ this.countNode();
8565
+ if (isLogicalOp(op)) left = {
8566
+ kind: "logical",
8567
+ op,
8568
+ left,
8569
+ right
8570
+ };
8571
+ else if (isBinaryOp(op)) left = {
8572
+ kind: "binary",
8573
+ op,
8574
+ left,
8575
+ right
8576
+ };
8577
+ else throw new ExpressionParseError(`unexpected operator '${op}'`, tok.pos);
8578
+ }
8579
+ return left;
8580
+ }
8581
+ parseUnary() {
8582
+ const tok = this.peek();
8583
+ if (tok.type === "punct" && (tok.punct === "!" || tok.punct === "-")) {
8584
+ const op = tok.punct;
8585
+ this.pos += 1;
8586
+ const operand = this.parseUnary();
8587
+ this.countNode();
8588
+ return {
8589
+ kind: "unary",
8590
+ op,
8591
+ operand
8592
+ };
8593
+ }
8594
+ return this.parsePrimary();
8595
+ }
8596
+ parsePrimary() {
8597
+ const tok = this.next();
8598
+ switch (tok.type) {
8599
+ case "number":
8600
+ this.countNode();
8601
+ return {
8602
+ kind: "literal",
8603
+ value: tok.value
8604
+ };
8605
+ case "string":
8606
+ this.countNode();
8607
+ return {
8608
+ kind: "literal",
8609
+ value: tok.value
8610
+ };
8611
+ case "keyword":
8612
+ this.countNode();
8613
+ return {
8614
+ kind: "literal",
8615
+ value: tok.keyword === "null" ? null : tok.keyword === "true"
8616
+ };
8617
+ case "identifier": {
8618
+ const nextTok = this.peek();
8619
+ if (nextTok.type === "punct" && nextTok.punct === "(") return this.parseCall(tok.name, tok.pos);
8620
+ this.identifiers.add(tok.name);
8621
+ this.countNode();
8622
+ return {
8623
+ kind: "identifier",
8624
+ name: tok.name
8625
+ };
8626
+ }
8627
+ case "punct":
8628
+ if (tok.punct === "(") {
8629
+ const inner = this.parseTernary();
8630
+ this.expectPunct(")");
8631
+ return inner;
8632
+ }
8633
+ throw new ExpressionParseError(`unexpected token '${tok.punct}'`, tok.pos);
8634
+ case "eof": throw new ExpressionParseError("unexpected end of expression", tok.pos);
8635
+ }
8636
+ }
8637
+ parseCall(callee, pos) {
8638
+ if (!EXPRESSION_BUILTIN_NAMES.has(callee)) throw new ExpressionParseError(`unknown function '${callee}'`, pos);
8639
+ this.expectPunct("(");
8640
+ const args = [];
8641
+ if (!this.matchPunct(")")) for (;;) {
8642
+ args.push(this.parseTernary());
8643
+ if (args.length > 16) throw new ExpressionParseError(`too many arguments to '${callee}'`, pos);
8644
+ if (this.matchPunct(",")) continue;
8645
+ this.expectPunct(")");
8646
+ break;
8647
+ }
8648
+ this.callees.add(callee);
8649
+ this.countNode();
8650
+ return {
8651
+ kind: "call",
8652
+ callee,
8653
+ args
8654
+ };
8655
+ }
8656
+ };
8657
+ /** Tokenize + parse `source` into a validated `ParsedExpression`. Throws
8658
+ * `ExpressionParseError` on any lexical or grammatical failure. */
8659
+ function parseExpression(source) {
8660
+ return new Parser(tokenize(source)).parse();
8661
+ }
8662
+ Object.freeze({});
8663
+ /**
8664
+ * LRU compile cache for parsed expressions (spec §2.4 "parse once … LRU keyed
8665
+ * by expr"). The cache stores BOTH successes and failures (negative caching),
8666
+ * so a corrupt persisted string costs exactly one tokenize+parse total — not
8667
+ * one per read on a hot resolve path.
8668
+ *
8669
+ * The cache is a module-level singleton: entries are pure, content-addressed
8670
+ * ASTs keyed by the raw source string, so sharing one instance across all
8671
+ * callers is safe and maximises hit rate.
8672
+ */
8673
+ var cache = /* @__PURE__ */ new Map();
8674
+ function getCached(source) {
8675
+ const hit = cache.get(source);
8676
+ if (hit !== void 0) {
8677
+ cache.delete(source);
8678
+ cache.set(source, hit);
8679
+ return hit;
8680
+ }
8681
+ let result;
8682
+ try {
8683
+ result = {
8684
+ ok: true,
8685
+ parsed: parseExpression(source)
8686
+ };
8687
+ } catch (err) {
8688
+ result = {
8689
+ ok: false,
8690
+ error: err instanceof ExpressionParseError ? err.message : String(err)
8691
+ };
8692
+ }
8693
+ cache.set(source, result);
8694
+ if (cache.size > 256) {
8695
+ const oldest = cache.keys().next().value;
8696
+ if (oldest !== void 0) cache.delete(oldest);
8697
+ }
8698
+ return result;
8699
+ }
8700
+ /** Compile `source`, returning a discriminated result instead of throwing.
8701
+ * Used by read paths that must degrade rather than raise. LRU/negative-cached. */
8702
+ function compileExpressionSafe(source) {
8703
+ return getCached(source);
8704
+ }
8705
+ /**
8706
+ * Author-time validation. Returns `null` when the source is valid, else a
8707
+ * human-readable error message. Checks: the expression compiles; binding count
8708
+ * is within `MAX_EXPRESSION_BINDINGS`; every binding name is a legal identifier,
8709
+ * is not reserved (`now`/keywords) and does not shadow a builtin; and every
8710
+ * FREE identifier of the AST is covered by a binding or the injected `now`.
8711
+ */
8712
+ function validateExpressionSource(src) {
8713
+ const names = Object.keys(src.bindings);
8714
+ if (names.length > 32) return `too many bindings (${names.length} > 32)`;
8715
+ for (const name of names) {
8716
+ if (!EXPRESSION_IDENTIFIER_RE.test(name)) return `invalid binding name '${name}'`;
8717
+ if (RESERVED_BINDING_NAMES.has(name)) return `binding name '${name}' is reserved`;
8718
+ if (EXPRESSION_BUILTIN_NAMES.has(name)) return `binding name '${name}' shadows a builtin function`;
8719
+ }
8720
+ const compiled = compileExpressionSafe(src.expr);
8721
+ if (!compiled.ok) return compiled.error;
8722
+ const bound = new Set(names);
8723
+ for (const id of compiled.parsed.identifiers) {
8724
+ if (id === "now") continue;
8725
+ if (!bound.has(id)) return `expression references unbound identifier '${id}'`;
8726
+ }
8727
+ return null;
8728
+ }
8729
+ /**
8135
8730
  * Accessory device helpers — shared across drivers.
8136
8731
  *
8137
8732
  * Many vendor-specific drivers register accessory child devices on
@@ -13834,14 +14429,63 @@ var ChildLayoutEntrySchema = object({
13834
14429
  collapsed: boolean().optional()
13835
14430
  });
13836
14431
  /** Cap-wire shape of a DeviceLink — structurally mirrors `DeviceLink` in
13837
- * `device-management.ts`. */
14432
+ * `device-management.ts`. Source is a union: a FIELD source copies a sibling
14433
+ * accessory's status field (`kind` optional/absent for wire compat); a
14434
+ * LITERAL source carries a per-device constant (no sibling is read); a
14435
+ * GLOBAL source (P2e) copies ANY device's status field, addressed by the
14436
+ * source device's full re-sync-stable `stableId`. */
14437
+ var DeviceLinkFieldSourceSchema = object({
14438
+ kind: literal("field").optional(),
14439
+ sourceKey: string(),
14440
+ cap: string(),
14441
+ fieldPath: string()
14442
+ });
14443
+ var DeviceLinkLiteralSourceSchema = object({
14444
+ kind: literal("literal"),
14445
+ value: union([
14446
+ string(),
14447
+ number(),
14448
+ boolean(),
14449
+ _null()
14450
+ ])
14451
+ });
14452
+ var DeviceLinkGlobalSourceSchema = object({
14453
+ kind: literal("global"),
14454
+ sourceStableId: string(),
14455
+ cap: string(),
14456
+ fieldPath: string()
14457
+ });
14458
+ /** Expression source (Stage X): compute the target field from N named bindings
14459
+ * via the safe expression engine. Bindings are field | literal | global — never
14460
+ * another expression (no nesting). The `superRefine` runs the SAME author-time
14461
+ * validation as `validateExpressionSource` (compiles the expr, checks binding
14462
+ * names + identifier coverage) so every wire boundary that parses a DeviceLink
14463
+ * (tRPC mount, kernel create pre-seed, projection output) validates-at-write.
14464
+ * Compiles are LRU-cached, so repeated validation of the same expr is a hit. */
14465
+ var DeviceLinkExpressionSourceSchema = object({
14466
+ kind: literal("expression"),
14467
+ expr: string().min(1).max(MAX_EXPRESSION_SOURCE_LENGTH),
14468
+ bindings: record(string().regex(EXPRESSION_IDENTIFIER_RE), union([
14469
+ DeviceLinkFieldSourceSchema,
14470
+ DeviceLinkLiteralSourceSchema,
14471
+ DeviceLinkGlobalSourceSchema
14472
+ ]))
14473
+ }).superRefine((src, ctx) => {
14474
+ const err = validateExpressionSource(src);
14475
+ if (err !== null) ctx.addIssue({
14476
+ code: "custom",
14477
+ message: err,
14478
+ path: ["expr"]
14479
+ });
14480
+ });
13838
14481
  var DeviceLinkSchema = object({
13839
14482
  id: string(),
13840
- source: object({
13841
- sourceKey: string(),
13842
- cap: string(),
13843
- fieldPath: string()
13844
- }),
14483
+ source: union([
14484
+ DeviceLinkFieldSourceSchema,
14485
+ DeviceLinkLiteralSourceSchema,
14486
+ DeviceLinkGlobalSourceSchema,
14487
+ DeviceLinkExpressionSourceSchema
14488
+ ]),
13845
14489
  target: object({
13846
14490
  cap: string(),
13847
14491
  fieldPath: string(),
@@ -13870,6 +14514,31 @@ var DeviceLinkSchema = object({
13870
14514
  })
13871
14515
  ]).optional()
13872
14516
  });
14517
+ /** Cap-wire shape of a per-cap display refinement — mirrors
14518
+ * `DeviceCapDisplayOverride` in `device-management.ts`. */
14519
+ var DeviceCapDisplayOverrideSchema = object({
14520
+ unit: string().min(1).optional(),
14521
+ precision: number().int().min(0).max(10).optional()
14522
+ });
14523
+ /** Cap-wire shape of an operator-authored per-device display override —
14524
+ * mirrors `DeviceDisplayOverride` in `device-management.ts`. `precision`
14525
+ * bounds mirror `numeric-sensor.cap.ts` (`int 0-10`). */
14526
+ var DeviceDisplayOverrideSchema = object({
14527
+ icon: string().min(1).optional(),
14528
+ label: string().min(1).optional(),
14529
+ unit: string().min(1).optional(),
14530
+ precision: number().int().min(0).max(10).optional(),
14531
+ hidden: boolean().optional(),
14532
+ perCap: record(string(), DeviceCapDisplayOverrideSchema).optional()
14533
+ });
14534
+ /** Cap-wire shape of a per-role display default — mirrors `RoleDisplayDefault`
14535
+ * in `device-management.ts`. Keyed by `DeviceRole` string (role strings cross
14536
+ * the wire as plain strings everywhere else — cf. `DeviceInfoSchema.role`). */
14537
+ var RoleDisplayDefaultSchema = object({
14538
+ unit: string().min(1).optional(),
14539
+ precision: number().int().min(0).max(10).optional(),
14540
+ icon: string().min(1).optional()
14541
+ });
13873
14542
  /**
13874
14543
  * Serializable projection of a live IDevice.
13875
14544
  * Returned by listAll, getDevice, getChildren.
@@ -13925,7 +14594,9 @@ var DeviceInfoSchema = object({
13925
14594
  * `DeviceMeta.childLayout`. Absent ⇒ no layout declared. */
13926
14595
  childLayout: array(ChildLayoutEntrySchema).readonly().optional(),
13927
14596
  /** Operator-authored cross-device field wirings. See `DeviceMeta.deviceLinks`. */
13928
- deviceLinks: array(DeviceLinkSchema).readonly().optional()
14597
+ deviceLinks: array(DeviceLinkSchema).readonly().optional(),
14598
+ /** Operator-authored per-device display override. See `DeviceMeta.display`. */
14599
+ display: DeviceDisplayOverrideSchema.optional()
13929
14600
  });
13930
14601
  var ConfigEntrySchema = object({
13931
14602
  key: string(),
@@ -13990,7 +14661,9 @@ var DeviceMetaSchema = object({
13990
14661
  deviceLinks: array(DeviceLinkSchema).readonly().optional(),
13991
14662
  /** Semantic role string (`DeviceRole`) — propagated from the spawn pre-seed.
13992
14663
  * Optional: only present for accessory children that carry a known role. */
13993
- role: string().nullable().optional()
14664
+ role: string().nullable().optional(),
14665
+ /** Operator-authored per-device display override. See `DeviceMeta.display`. */
14666
+ display: DeviceDisplayOverrideSchema.optional()
13994
14667
  });
13995
14668
  /** ConfigUISchema passed through as unknown — mirrors device-provider's CreationSchemaOutputSchema */
13996
14669
  var ConfigUISchemaOutput = unknown().nullable();
@@ -14084,7 +14757,19 @@ method(object({
14084
14757
  }), _void(), {
14085
14758
  kind: "mutation",
14086
14759
  auth: "admin"
14087
- }), method(object({ deviceId: number() }), object({ caps: array(object({
14760
+ }), method(object({
14761
+ deviceId: number(),
14762
+ display: DeviceDisplayOverrideSchema.nullable()
14763
+ }), _void(), {
14764
+ kind: "mutation",
14765
+ auth: "admin"
14766
+ }), method(object({}), object({ defaults: record(string(), RoleDisplayDefaultSchema) }), { kind: "query" }), method(object({ defaults: record(string(), RoleDisplayDefaultSchema) }), _void(), {
14767
+ kind: "mutation",
14768
+ auth: "admin"
14769
+ }), method(object({
14770
+ deviceId: number(),
14771
+ includeSynthesizable: boolean().optional()
14772
+ }), object({ caps: array(object({
14088
14773
  cap: string(),
14089
14774
  fields: array(object({
14090
14775
  path: string(),
@@ -14094,8 +14779,13 @@ method(object({
14094
14779
  "boolean",
14095
14780
  "enum"
14096
14781
  ]),
14097
- enumValues: array(string()).optional()
14098
- })).readonly()
14782
+ enumValues: array(string()).optional(),
14783
+ item: boolean().optional()
14784
+ })).readonly(),
14785
+ itemArray: object({
14786
+ path: string(),
14787
+ keyField: string()
14788
+ }).optional()
14099
14789
  })).readonly() }), { kind: "query" }), method(object({
14100
14790
  deviceId: number(),
14101
14791
  role: string().nullable()
@@ -14165,7 +14855,11 @@ method(object({
14165
14855
  deviceId: number(),
14166
14856
  entries: array(object({
14167
14857
  capName: string(),
14168
- kind: _enum(["native", "wrapped"]),
14858
+ kind: _enum([
14859
+ "native",
14860
+ "wrapped",
14861
+ "linked"
14862
+ ]),
14169
14863
  providerAddonId: string(),
14170
14864
  providerNodeId: string(),
14171
14865
  nativeAddonId: string()
@@ -14174,7 +14868,11 @@ method(object({
14174
14868
  deviceId: number(),
14175
14869
  entries: array(object({
14176
14870
  capName: string(),
14177
- kind: _enum(["native", "wrapped"]),
14871
+ kind: _enum([
14872
+ "native",
14873
+ "wrapped",
14874
+ "linked"
14875
+ ]),
14178
14876
  providerAddonId: string(),
14179
14877
  providerNodeId: string(),
14180
14878
  nativeAddonId: string()
@@ -19875,6 +20573,12 @@ Object.freeze({
19875
20573
  addonId: null,
19876
20574
  access: "view"
19877
20575
  },
20576
+ "deviceManager.getRoleDisplayDefaults": {
20577
+ capName: "device-manager",
20578
+ capScope: "system",
20579
+ addonId: null,
20580
+ access: "view"
20581
+ },
19878
20582
  "deviceManager.getSettingsSchema": {
19879
20583
  capName: "device-manager",
19880
20584
  capScope: "system",
@@ -20025,6 +20729,12 @@ Object.freeze({
20025
20729
  addonId: null,
20026
20730
  access: "create"
20027
20731
  },
20732
+ "deviceManager.setDisplay": {
20733
+ capName: "device-manager",
20734
+ capScope: "system",
20735
+ addonId: null,
20736
+ access: "create"
20737
+ },
20028
20738
  "deviceManager.setIntegrationId": {
20029
20739
  capName: "device-manager",
20030
20740
  capScope: "system",
@@ -20067,6 +20777,12 @@ Object.freeze({
20067
20777
  addonId: null,
20068
20778
  access: "create"
20069
20779
  },
20780
+ "deviceManager.setRoleDisplayDefaults": {
20781
+ capName: "device-manager",
20782
+ capScope: "system",
20783
+ addonId: null,
20784
+ access: "create"
20785
+ },
20070
20786
  "deviceManager.setStreamProfileMap": {
20071
20787
  capName: "device-manager",
20072
20788
  capScope: "system",