@camstack/types 1.2.267 → 1.2.268
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/addon.js +4 -4
- package/dist/addon.mjs +3 -3
- package/dist/capabilities/battery.cap.d.ts +17 -2
- package/dist/capabilities/capability-definition.d.ts +22 -0
- package/dist/capabilities/composer.cap.d.ts +99 -0
- package/dist/capabilities/consumables.cap.d.ts +79 -0
- package/dist/capabilities/core-blocks.cap.d.ts +1149 -91
- package/dist/capabilities/device-extension.cap.d.ts +12 -13
- package/dist/capabilities/device-state.cap.d.ts +186 -2
- package/dist/capabilities/index.d.ts +3 -3
- package/dist/composition/composition-graph.d.ts +42 -9
- package/dist/composition/composition-items.d.ts +25 -0
- package/dist/composition/composition-report.d.ts +93 -2
- package/dist/composition/composition.d.ts +154 -3
- package/dist/composition/customization-name.d.ts +6 -0
- package/dist/composition/evaluate-field.d.ts +12 -0
- package/dist/composition/examples.d.ts +33 -0
- package/dist/composition/field-kinds.d.ts +42 -0
- package/dist/composition/index.d.ts +1 -0
- package/dist/composition/plan-result.d.ts +16 -0
- package/dist/composition/state-fields.d.ts +6 -0
- package/dist/composition/validate-composition.d.ts +44 -9
- package/dist/composition-CPFIlFfw.mjs +1759 -0
- package/dist/composition-QlOB7QTG.js +2184 -0
- package/dist/device/device-context.d.ts +9 -0
- package/dist/device/device-runtime-state.d.ts +15 -1
- package/dist/device/index.d.ts +1 -1
- package/dist/device-extension/slot-descriptor.d.ts +8 -11
- package/dist/device-extension/slot-provider.d.ts +0 -2
- package/dist/enums/event-category.d.ts +25 -0
- package/dist/enums.js +1 -1
- package/dist/enums.mjs +1 -1
- package/dist/err-msg-DX6i_MY4.mjs +339 -0
- package/dist/err-msg-Dx2Kor0g.js +368 -0
- package/dist/{event-category-BVDXG4tB.mjs → event-category-C5xZWqz6.mjs} +31 -1
- package/dist/{event-category-BVfsrBYA.js → event-category-MbnB-6AN.js} +48 -0
- package/dist/generated/addon-api.d.ts +42 -0
- package/dist/generated/device-proxy.d.ts +1 -1
- package/dist/generated/method-access-map.d.ts +1 -1
- package/dist/generated/method-device-selectors.d.ts +2 -2
- package/dist/generated/system-proxy.d.ts +2 -2
- package/dist/index.d.ts +4 -3
- package/dist/index.js +1513 -1656
- package/dist/index.mjs +1208 -1384
- package/dist/interfaces/kernel-abstractions.d.ts +16 -0
- package/dist/interfaces/status-overlay.d.ts +42 -0
- package/dist/node.d.ts +1 -0
- package/dist/node.js +29 -8
- package/dist/node.mjs +23 -3
- package/dist/{sleep-8K9gue-H.js → sleep-BSzia-xT.js} +6 -352
- package/dist/{sleep-DC-wdyeS.mjs → sleep-jkRQgtSc.mjs} +7 -329
- package/package.json +1 -1
- package/dist/canonical-hash-CPK2Dy60.mjs +0 -715
- package/dist/canonical-hash-CSE4ioRi.js +0 -792
- package/dist/err-msg-COpsHMw2.js +0 -18
- package/dist/err-msg-IQTHeDzc.mjs +0 -13
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_sleep = require("./sleep-
|
|
3
|
-
const require_event_category = require("./event-category-
|
|
4
|
-
const
|
|
2
|
+
const require_sleep = require("./sleep-BSzia-xT.js");
|
|
3
|
+
const require_event_category = require("./event-category-MbnB-6AN.js");
|
|
4
|
+
const require_composition = require("./composition-QlOB7QTG.js");
|
|
5
|
+
const require_err_msg = require("./err-msg-Dx2Kor0g.js");
|
|
5
6
|
const require_enums = require("./enums.js");
|
|
6
|
-
const require_err_msg = require("./err-msg-COpsHMw2.js");
|
|
7
7
|
let zod = require("zod");
|
|
8
8
|
//#region src/media/media-owner-key.ts
|
|
9
9
|
/**
|
|
@@ -741,7 +741,7 @@ function canonicalEgressPlan(request, delivery = egressTransportFromRequest(requ
|
|
|
741
741
|
* every other consumer to 360p.
|
|
742
742
|
*/
|
|
743
743
|
function egressTranscodeSharingKey(request, delivery = egressTransportFromRequest(request)) {
|
|
744
|
-
return `egress:${
|
|
744
|
+
return `egress:${require_composition.canonicalHash(canonicalEgressPlan(request, delivery))}`;
|
|
745
745
|
}
|
|
746
746
|
//#endregion
|
|
747
747
|
//#region src/health/wiring-health.ts
|
|
@@ -5531,809 +5531,6 @@ function principalMayReachAddon(input) {
|
|
|
5531
5531
|
};
|
|
5532
5532
|
}
|
|
5533
5533
|
//#endregion
|
|
5534
|
-
//#region src/expression/errors.ts
|
|
5535
|
-
/**
|
|
5536
|
-
* Error types for the safe expression engine. Two distinct classes so callers
|
|
5537
|
-
* can tell a compile-time (grammar) failure from a runtime (evaluation)
|
|
5538
|
-
* failure — both are non-fatal to the host: read paths degrade to "skip link".
|
|
5539
|
-
*/
|
|
5540
|
-
/** Thrown by the tokenizer / parser. Carries a 0-based source `position` when
|
|
5541
|
-
* the failure is anchored to a character (author-facing inline feedback). */
|
|
5542
|
-
var ExpressionParseError = class extends Error {
|
|
5543
|
-
position;
|
|
5544
|
-
constructor(message, position) {
|
|
5545
|
-
super(message);
|
|
5546
|
-
this.name = "ExpressionParseError";
|
|
5547
|
-
this.position = position;
|
|
5548
|
-
}
|
|
5549
|
-
};
|
|
5550
|
-
/** Thrown by the evaluator (unknown identifier, type mismatch, non-finite
|
|
5551
|
-
* result, unknown builtin, step-budget exceeded). */
|
|
5552
|
-
var ExpressionEvalError = class extends Error {
|
|
5553
|
-
constructor(message) {
|
|
5554
|
-
super(message);
|
|
5555
|
-
this.name = "ExpressionEvalError";
|
|
5556
|
-
}
|
|
5557
|
-
};
|
|
5558
|
-
//#endregion
|
|
5559
|
-
//#region src/expression/builtins.ts
|
|
5560
|
-
function asFiniteNumber(value, name, index) {
|
|
5561
|
-
if (typeof value !== "number" || !Number.isFinite(value)) throw new ExpressionEvalError(`${name}: argument ${index + 1} must be a finite number`);
|
|
5562
|
-
return value;
|
|
5563
|
-
}
|
|
5564
|
-
function asString$1(value, name, index) {
|
|
5565
|
-
if (typeof value !== "string") throw new ExpressionEvalError(`${name}: argument ${index + 1} must be a string`);
|
|
5566
|
-
return value;
|
|
5567
|
-
}
|
|
5568
|
-
function finiteResult(value, name) {
|
|
5569
|
-
if (!Number.isFinite(value)) throw new ExpressionEvalError(`${name}: produced a non-finite result`);
|
|
5570
|
-
return value;
|
|
5571
|
-
}
|
|
5572
|
-
function allFiniteNumbers(args, name) {
|
|
5573
|
-
return args.map((a, idx) => asFiniteNumber(a, name, idx));
|
|
5574
|
-
}
|
|
5575
|
-
function asBoolean$1(value, name, index) {
|
|
5576
|
-
if (typeof value !== "boolean") throw new ExpressionEvalError(`${name}: argument ${index + 1} must be a boolean`);
|
|
5577
|
-
return value;
|
|
5578
|
-
}
|
|
5579
|
-
var INF = Number.POSITIVE_INFINITY;
|
|
5580
|
-
var table = {
|
|
5581
|
-
min: {
|
|
5582
|
-
minArgs: 1,
|
|
5583
|
-
maxArgs: INF,
|
|
5584
|
-
apply: (args) => finiteResult(Math.min(...allFiniteNumbers(args, "min")), "min")
|
|
5585
|
-
},
|
|
5586
|
-
max: {
|
|
5587
|
-
minArgs: 1,
|
|
5588
|
-
maxArgs: INF,
|
|
5589
|
-
apply: (args) => finiteResult(Math.max(...allFiniteNumbers(args, "max")), "max")
|
|
5590
|
-
},
|
|
5591
|
-
abs: {
|
|
5592
|
-
minArgs: 1,
|
|
5593
|
-
maxArgs: 1,
|
|
5594
|
-
apply: (args) => finiteResult(Math.abs(asFiniteNumber(args[0], "abs", 0)), "abs")
|
|
5595
|
-
},
|
|
5596
|
-
floor: {
|
|
5597
|
-
minArgs: 1,
|
|
5598
|
-
maxArgs: 1,
|
|
5599
|
-
apply: (args) => finiteResult(Math.floor(asFiniteNumber(args[0], "floor", 0)), "floor")
|
|
5600
|
-
},
|
|
5601
|
-
ceil: {
|
|
5602
|
-
minArgs: 1,
|
|
5603
|
-
maxArgs: 1,
|
|
5604
|
-
apply: (args) => finiteResult(Math.ceil(asFiniteNumber(args[0], "ceil", 0)), "ceil")
|
|
5605
|
-
},
|
|
5606
|
-
sqrt: {
|
|
5607
|
-
minArgs: 1,
|
|
5608
|
-
maxArgs: 1,
|
|
5609
|
-
apply: (args) => finiteResult(Math.sqrt(asFiniteNumber(args[0], "sqrt", 0)), "sqrt")
|
|
5610
|
-
},
|
|
5611
|
-
round: {
|
|
5612
|
-
minArgs: 1,
|
|
5613
|
-
maxArgs: 2,
|
|
5614
|
-
apply: (args) => {
|
|
5615
|
-
const x = asFiniteNumber(args[0], "round", 0);
|
|
5616
|
-
const digits = args.length > 1 ? Math.trunc(asFiniteNumber(args[1], "round", 1)) : 0;
|
|
5617
|
-
if (digits < 0 || digits > 100) throw new ExpressionEvalError("round: digits must be between 0 and 100");
|
|
5618
|
-
const factor = 10 ** digits;
|
|
5619
|
-
return finiteResult(Math.round(x * factor) / factor, "round");
|
|
5620
|
-
}
|
|
5621
|
-
},
|
|
5622
|
-
pow: {
|
|
5623
|
-
minArgs: 2,
|
|
5624
|
-
maxArgs: 2,
|
|
5625
|
-
apply: (args) => finiteResult(asFiniteNumber(args[0], "pow", 0) ** asFiniteNumber(args[1], "pow", 1), "pow")
|
|
5626
|
-
},
|
|
5627
|
-
clamp: {
|
|
5628
|
-
minArgs: 3,
|
|
5629
|
-
maxArgs: 3,
|
|
5630
|
-
apply: (args) => {
|
|
5631
|
-
const x = asFiniteNumber(args[0], "clamp", 0);
|
|
5632
|
-
const lo = asFiniteNumber(args[1], "clamp", 1);
|
|
5633
|
-
const hi = asFiniteNumber(args[2], "clamp", 2);
|
|
5634
|
-
if (lo > hi) throw new ExpressionEvalError("clamp: lower bound is greater than upper bound");
|
|
5635
|
-
return finiteResult(Math.min(hi, Math.max(lo, x)), "clamp");
|
|
5636
|
-
}
|
|
5637
|
-
},
|
|
5638
|
-
avg: {
|
|
5639
|
-
minArgs: 1,
|
|
5640
|
-
maxArgs: INF,
|
|
5641
|
-
apply: (args) => {
|
|
5642
|
-
const nums = allFiniteNumbers(args, "avg");
|
|
5643
|
-
return finiteResult(nums.reduce((acc, v) => acc + v, 0) / nums.length, "avg");
|
|
5644
|
-
}
|
|
5645
|
-
},
|
|
5646
|
-
sum: {
|
|
5647
|
-
minArgs: 1,
|
|
5648
|
-
maxArgs: INF,
|
|
5649
|
-
apply: (args) => finiteResult(allFiniteNumbers(args, "sum").reduce((acc, v) => acc + v, 0), "sum")
|
|
5650
|
-
},
|
|
5651
|
-
coalesce: {
|
|
5652
|
-
minArgs: 1,
|
|
5653
|
-
maxArgs: INF,
|
|
5654
|
-
apply: (args) => {
|
|
5655
|
-
for (const a of args) if (a !== null) return a;
|
|
5656
|
-
return null;
|
|
5657
|
-
}
|
|
5658
|
-
},
|
|
5659
|
-
age: {
|
|
5660
|
-
minArgs: 2,
|
|
5661
|
-
maxArgs: 2,
|
|
5662
|
-
apply: (args) => finiteResult(asFiniteNumber(args[0], "age", 0) - asFiniteNumber(args[1], "age", 1), "age")
|
|
5663
|
-
},
|
|
5664
|
-
convert: {
|
|
5665
|
-
minArgs: 3,
|
|
5666
|
-
maxArgs: 3,
|
|
5667
|
-
apply: (args, hooks) => {
|
|
5668
|
-
const x = asFiniteNumber(args[0], "convert", 0);
|
|
5669
|
-
const from = asString$1(args[1], "convert", 1).trim();
|
|
5670
|
-
const to = asString$1(args[2], "convert", 2).trim();
|
|
5671
|
-
if (hooks.convert) {
|
|
5672
|
-
const out = hooks.convert(x, from, to);
|
|
5673
|
-
if (out === null) throw new ExpressionEvalError(`convert: cannot convert '${from}' to '${to}'`);
|
|
5674
|
-
return finiteResult(out, "convert");
|
|
5675
|
-
}
|
|
5676
|
-
if (from === to) return x;
|
|
5677
|
-
throw new ExpressionEvalError("convert: unit conversion table not installed");
|
|
5678
|
-
}
|
|
5679
|
-
},
|
|
5680
|
-
any: {
|
|
5681
|
-
minArgs: 1,
|
|
5682
|
-
maxArgs: INF,
|
|
5683
|
-
apply: (args) => args.map((a, i) => asBoolean$1(a, "any", i)).some((b) => b)
|
|
5684
|
-
},
|
|
5685
|
-
all: {
|
|
5686
|
-
minArgs: 1,
|
|
5687
|
-
maxArgs: INF,
|
|
5688
|
-
apply: (args) => args.map((a, i) => asBoolean$1(a, "all", i)).every((b) => b)
|
|
5689
|
-
},
|
|
5690
|
-
within: {
|
|
5691
|
-
minArgs: 2,
|
|
5692
|
-
maxArgs: 2,
|
|
5693
|
-
apply: (args, hooks) => {
|
|
5694
|
-
const windowMs = asFiniteNumber(args[1], "within", 1);
|
|
5695
|
-
if (windowMs < 0) throw new ExpressionEvalError("within: the window must not be negative");
|
|
5696
|
-
const at = args[0];
|
|
5697
|
-
if (at === null) return false;
|
|
5698
|
-
const ts = asFiniteNumber(at, "within", 0);
|
|
5699
|
-
const now = hooks.now;
|
|
5700
|
-
if (now === void 0 || !Number.isFinite(now)) throw new ExpressionEvalError("within: no clock was supplied to this evaluation");
|
|
5701
|
-
const inside = now - ts <= windowMs;
|
|
5702
|
-
if (inside) hooks.noteDeadline?.(ts + windowMs + 1);
|
|
5703
|
-
return inside;
|
|
5704
|
-
}
|
|
5705
|
-
},
|
|
5706
|
-
latest: {
|
|
5707
|
-
minArgs: 1,
|
|
5708
|
-
maxArgs: INF,
|
|
5709
|
-
apply: (args) => {
|
|
5710
|
-
const present = args.flatMap((a, i) => a === null ? [] : [asFiniteNumber(a, "latest", i)]);
|
|
5711
|
-
return present.length === 0 ? null : finiteResult(Math.max(...present), "latest");
|
|
5712
|
-
}
|
|
5713
|
-
}
|
|
5714
|
-
};
|
|
5715
|
-
/** Frozen, null-prototype builtin table. */
|
|
5716
|
-
var EXPRESSION_BUILTINS = Object.freeze(Object.assign(Object.create(null), table));
|
|
5717
|
-
/** The set of valid builtin names — used by the parser to reject unknown
|
|
5718
|
-
* callees at parse time (immediate author feedback). */
|
|
5719
|
-
var EXPRESSION_BUILTIN_NAMES = new Set(Object.keys(table));
|
|
5720
|
-
//#endregion
|
|
5721
|
-
//#region src/expression/limits.ts
|
|
5722
|
-
/**
|
|
5723
|
-
* Resource-bound constants for the safe expression engine.
|
|
5724
|
-
*
|
|
5725
|
-
* Every bound is defense-in-depth: the grammar is non-Turing-complete (no
|
|
5726
|
-
* loops, recursion, lambdas or member access — see `ast.ts`), so evaluation is
|
|
5727
|
-
* O(nodeCount) by construction. These caps merely put a hard ceiling on the
|
|
5728
|
-
* work a single author-supplied expression can request, so a hostile or
|
|
5729
|
-
* accidental pathological string can never spend unbounded CPU/memory.
|
|
5730
|
-
*/
|
|
5731
|
-
/** Max source length (chars) — checked BEFORE tokenizing so a huge string is
|
|
5732
|
-
* rejected without allocation. */
|
|
5733
|
-
var MAX_EXPRESSION_SOURCE_LENGTH = 2048;
|
|
5734
|
-
/** Max AST nodes — checked during parse; a deeply nested grouping that exceeds
|
|
5735
|
-
* this is rejected as "expression too complex". */
|
|
5736
|
-
var MAX_EXPRESSION_AST_NODES = 256;
|
|
5737
|
-
/** Defense-in-depth walker step budget — one increment per node visit during
|
|
5738
|
-
* evaluation. The grammar guarantees O(nodeCount) walks, so this can only trip
|
|
5739
|
-
* on a crafted maximum-size AST. */
|
|
5740
|
-
var MAX_EXPRESSION_EVAL_STEPS = 4096;
|
|
5741
|
-
/** Max named bindings on one {@link ExpressionSource}. */
|
|
5742
|
-
var MAX_EXPRESSION_BINDINGS = 32;
|
|
5743
|
-
/** Max positional arguments to any builtin call. */
|
|
5744
|
-
var MAX_EXPRESSION_CALL_ARGS = 16;
|
|
5745
|
-
/** LRU compile-cache capacity (parsed ASTs keyed by raw source string). */
|
|
5746
|
-
var EXPRESSION_COMPILE_CACHE_CAPACITY = 256;
|
|
5747
|
-
/** A legal binding / identifier name. */
|
|
5748
|
-
var EXPRESSION_IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
5749
|
-
/** Binding names an author may NOT use: `now` is auto-injected; the literal
|
|
5750
|
-
* keywords lex as values, not identifiers, so binding to them is meaningless. */
|
|
5751
|
-
var RESERVED_BINDING_NAMES = new Set([
|
|
5752
|
-
"now",
|
|
5753
|
-
"true",
|
|
5754
|
-
"false",
|
|
5755
|
-
"null"
|
|
5756
|
-
]);
|
|
5757
|
-
//#endregion
|
|
5758
|
-
//#region src/expression/tokenizer.ts
|
|
5759
|
-
/**
|
|
5760
|
-
* Tokenizer for the safe expression mini-language. Hand-rolled, single-pass,
|
|
5761
|
-
* zero-dependency. The grammar is deliberately boring: decimal numbers,
|
|
5762
|
-
* single/double-quoted strings with a tiny escape set, identifiers, the three
|
|
5763
|
-
* value keywords (`true`/`false`/`null`) and a fixed punctuator set. Anything
|
|
5764
|
-
* outside that — a bare `.`, `=`, `[`, `]`, `{`, `}`, `;`, backtick, `&`, `|` —
|
|
5765
|
-
* is a parse error with a source position, so member access / assignment /
|
|
5766
|
-
* template literals are lexically impossible.
|
|
5767
|
-
*/
|
|
5768
|
-
var KEYWORDS = new Set([
|
|
5769
|
-
"true",
|
|
5770
|
-
"false",
|
|
5771
|
-
"null"
|
|
5772
|
-
]);
|
|
5773
|
-
function isDigit(ch) {
|
|
5774
|
-
return ch >= "0" && ch <= "9";
|
|
5775
|
-
}
|
|
5776
|
-
function isIdentStart(ch) {
|
|
5777
|
-
return ch >= "A" && ch <= "Z" || ch >= "a" && ch <= "z" || ch === "_";
|
|
5778
|
-
}
|
|
5779
|
-
function isIdentPart(ch) {
|
|
5780
|
-
return isIdentStart(ch) || isDigit(ch);
|
|
5781
|
-
}
|
|
5782
|
-
function isWhitespace(ch) {
|
|
5783
|
-
return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f" || ch === "\v";
|
|
5784
|
-
}
|
|
5785
|
-
/** Tokenize `source` into a flat token list ending with a single `eof` token.
|
|
5786
|
-
* Throws `ExpressionParseError` on any illegal character or unterminated
|
|
5787
|
-
* string. */
|
|
5788
|
-
function tokenize(source) {
|
|
5789
|
-
if (source.length > 2048) throw new ExpressionParseError(`expression too long (${source.length} > ${MAX_EXPRESSION_SOURCE_LENGTH} chars)`, 0);
|
|
5790
|
-
const tokens = [];
|
|
5791
|
-
let i = 0;
|
|
5792
|
-
const n = source.length;
|
|
5793
|
-
while (i < n) {
|
|
5794
|
-
const ch = source[i];
|
|
5795
|
-
if (isWhitespace(ch)) {
|
|
5796
|
-
i += 1;
|
|
5797
|
-
continue;
|
|
5798
|
-
}
|
|
5799
|
-
if (isDigit(ch)) {
|
|
5800
|
-
const start = i;
|
|
5801
|
-
while (i < n && isDigit(source[i])) i += 1;
|
|
5802
|
-
if (i < n && source[i] === ".") {
|
|
5803
|
-
if (i + 1 >= n || !isDigit(source[i + 1])) throw new ExpressionParseError("malformed number: decimal point needs a digit", i);
|
|
5804
|
-
i += 1;
|
|
5805
|
-
while (i < n && isDigit(source[i])) i += 1;
|
|
5806
|
-
}
|
|
5807
|
-
const text = source.slice(start, i);
|
|
5808
|
-
const value = Number(text);
|
|
5809
|
-
if (!Number.isFinite(value)) throw new ExpressionParseError(`malformed number: '${text}'`, start);
|
|
5810
|
-
tokens.push({
|
|
5811
|
-
type: "number",
|
|
5812
|
-
value,
|
|
5813
|
-
pos: start
|
|
5814
|
-
});
|
|
5815
|
-
continue;
|
|
5816
|
-
}
|
|
5817
|
-
if (ch === "'" || ch === "\"") {
|
|
5818
|
-
const quote = ch;
|
|
5819
|
-
const start = i;
|
|
5820
|
-
i += 1;
|
|
5821
|
-
let out = "";
|
|
5822
|
-
let closed = false;
|
|
5823
|
-
while (i < n) {
|
|
5824
|
-
const c = source[i];
|
|
5825
|
-
if (c === "\\") {
|
|
5826
|
-
const next = i + 1 < n ? source[i + 1] : "";
|
|
5827
|
-
if (next === "\\" || next === "'" || next === "\"") {
|
|
5828
|
-
out += next;
|
|
5829
|
-
i += 2;
|
|
5830
|
-
continue;
|
|
5831
|
-
}
|
|
5832
|
-
throw new ExpressionParseError(`invalid string escape: '\\${next}'`, i);
|
|
5833
|
-
}
|
|
5834
|
-
if (c === quote) {
|
|
5835
|
-
closed = true;
|
|
5836
|
-
i += 1;
|
|
5837
|
-
break;
|
|
5838
|
-
}
|
|
5839
|
-
out += c;
|
|
5840
|
-
i += 1;
|
|
5841
|
-
}
|
|
5842
|
-
if (!closed) throw new ExpressionParseError("unterminated string literal", start);
|
|
5843
|
-
tokens.push({
|
|
5844
|
-
type: "string",
|
|
5845
|
-
value: out,
|
|
5846
|
-
pos: start
|
|
5847
|
-
});
|
|
5848
|
-
continue;
|
|
5849
|
-
}
|
|
5850
|
-
if (isIdentStart(ch)) {
|
|
5851
|
-
const start = i;
|
|
5852
|
-
while (i < n && isIdentPart(source[i])) i += 1;
|
|
5853
|
-
const text = source.slice(start, i);
|
|
5854
|
-
if (KEYWORDS.has(text)) tokens.push({
|
|
5855
|
-
type: "keyword",
|
|
5856
|
-
keyword: keywordOf(text),
|
|
5857
|
-
pos: start
|
|
5858
|
-
});
|
|
5859
|
-
else tokens.push({
|
|
5860
|
-
type: "identifier",
|
|
5861
|
-
name: text,
|
|
5862
|
-
pos: start
|
|
5863
|
-
});
|
|
5864
|
-
continue;
|
|
5865
|
-
}
|
|
5866
|
-
const two = i + 1 < n ? source.slice(i, i + 2) : "";
|
|
5867
|
-
if (two === "<=" || two === ">=" || two === "==" || two === "!=" || two === "&&" || two === "||") {
|
|
5868
|
-
tokens.push({
|
|
5869
|
-
type: "punct",
|
|
5870
|
-
punct: two,
|
|
5871
|
-
pos: i
|
|
5872
|
-
});
|
|
5873
|
-
i += 2;
|
|
5874
|
-
continue;
|
|
5875
|
-
}
|
|
5876
|
-
if (isSinglePunct(ch)) {
|
|
5877
|
-
tokens.push({
|
|
5878
|
-
type: "punct",
|
|
5879
|
-
punct: ch,
|
|
5880
|
-
pos: i
|
|
5881
|
-
});
|
|
5882
|
-
i += 1;
|
|
5883
|
-
continue;
|
|
5884
|
-
}
|
|
5885
|
-
throw new ExpressionParseError(`unexpected character '${ch}'`, i);
|
|
5886
|
-
}
|
|
5887
|
-
tokens.push({
|
|
5888
|
-
type: "eof",
|
|
5889
|
-
pos: n
|
|
5890
|
-
});
|
|
5891
|
-
return tokens;
|
|
5892
|
-
}
|
|
5893
|
-
function keywordOf(text) {
|
|
5894
|
-
if (text === "true") return "true";
|
|
5895
|
-
if (text === "false") return "false";
|
|
5896
|
-
return "null";
|
|
5897
|
-
}
|
|
5898
|
-
function isSinglePunct(ch) {
|
|
5899
|
-
return ch === "(" || ch === ")" || ch === "," || ch === "?" || ch === ":" || ch === "+" || ch === "-" || ch === "*" || ch === "/" || ch === "%" || ch === "!" || ch === "<" || ch === ">";
|
|
5900
|
-
}
|
|
5901
|
-
//#endregion
|
|
5902
|
-
//#region src/expression/parser.ts
|
|
5903
|
-
/**
|
|
5904
|
-
* Pratt (precedence-climbing) parser for the safe expression mini-language.
|
|
5905
|
-
*
|
|
5906
|
-
* Precedence (low → high): ternary `?:` (right-assoc) → `||` → `&&` → equality
|
|
5907
|
-
* → relational → additive → multiplicative → unary `! -` → call / primary.
|
|
5908
|
-
* Calls are ONLY `IDENT '(' args? ')'` at primary position — the callee is a
|
|
5909
|
-
* string validated against the builtin table at parse time, so an unknown
|
|
5910
|
-
* function is rejected immediately (author feedback) and a persisted expression
|
|
5911
|
-
* that references a since-removed builtin degrades at read.
|
|
5912
|
-
*
|
|
5913
|
-
* A node counter caps total AST size (`MAX_EXPRESSION_AST_NODES`) and call
|
|
5914
|
-
* arity is capped (`MAX_EXPRESSION_CALL_ARGS`) — both raise `ExpressionParseError`.
|
|
5915
|
-
*/
|
|
5916
|
-
/** Binary/logical operator precedence (higher binds tighter). */
|
|
5917
|
-
var BINARY_PRECEDENCE = {
|
|
5918
|
-
"||": 1,
|
|
5919
|
-
"&&": 2,
|
|
5920
|
-
"==": 3,
|
|
5921
|
-
"!=": 3,
|
|
5922
|
-
"<": 4,
|
|
5923
|
-
"<=": 4,
|
|
5924
|
-
">": 4,
|
|
5925
|
-
">=": 4,
|
|
5926
|
-
"+": 5,
|
|
5927
|
-
"-": 5,
|
|
5928
|
-
"*": 6,
|
|
5929
|
-
"/": 6,
|
|
5930
|
-
"%": 6
|
|
5931
|
-
};
|
|
5932
|
-
function isLogicalOp(op) {
|
|
5933
|
-
return op === "&&" || op === "||";
|
|
5934
|
-
}
|
|
5935
|
-
function isBinaryOp(op) {
|
|
5936
|
-
return op === "+" || op === "-" || op === "*" || op === "/" || op === "%" || op === "==" || op === "!=" || op === "<" || op === "<=" || op === ">" || op === ">=";
|
|
5937
|
-
}
|
|
5938
|
-
var Parser = class {
|
|
5939
|
-
tokens;
|
|
5940
|
-
pos = 0;
|
|
5941
|
-
nodeCount = 0;
|
|
5942
|
-
identifiers = /* @__PURE__ */ new Set();
|
|
5943
|
-
callees = /* @__PURE__ */ new Set();
|
|
5944
|
-
constructor(tokens) {
|
|
5945
|
-
this.tokens = tokens;
|
|
5946
|
-
}
|
|
5947
|
-
parse() {
|
|
5948
|
-
const ast = this.parseTernary();
|
|
5949
|
-
const tok = this.peek();
|
|
5950
|
-
if (tok.type !== "eof") throw new ExpressionParseError("unexpected trailing input", tok.pos);
|
|
5951
|
-
return {
|
|
5952
|
-
ast,
|
|
5953
|
-
identifiers: this.identifiers,
|
|
5954
|
-
callees: this.callees,
|
|
5955
|
-
nodeCount: this.nodeCount
|
|
5956
|
-
};
|
|
5957
|
-
}
|
|
5958
|
-
peek() {
|
|
5959
|
-
return this.tokens[this.pos];
|
|
5960
|
-
}
|
|
5961
|
-
next() {
|
|
5962
|
-
return this.tokens[this.pos++];
|
|
5963
|
-
}
|
|
5964
|
-
/** Consume a punctuator token, erroring if the next token isn't it. */
|
|
5965
|
-
expectPunct(punct) {
|
|
5966
|
-
const tok = this.peek();
|
|
5967
|
-
if (tok.type !== "punct" || tok.punct !== punct) throw new ExpressionParseError(`expected '${punct}'`, tok.pos);
|
|
5968
|
-
this.pos += 1;
|
|
5969
|
-
}
|
|
5970
|
-
matchPunct(punct) {
|
|
5971
|
-
const tok = this.peek();
|
|
5972
|
-
if (tok.type === "punct" && tok.punct === punct) {
|
|
5973
|
-
this.pos += 1;
|
|
5974
|
-
return true;
|
|
5975
|
-
}
|
|
5976
|
-
return false;
|
|
5977
|
-
}
|
|
5978
|
-
countNode() {
|
|
5979
|
-
this.nodeCount += 1;
|
|
5980
|
-
if (this.nodeCount > 256) throw new ExpressionParseError("expression too complex", this.peek().pos);
|
|
5981
|
-
}
|
|
5982
|
-
parseTernary() {
|
|
5983
|
-
const test = this.parseBinary(1);
|
|
5984
|
-
if (this.matchPunct("?")) {
|
|
5985
|
-
const consequent = this.parseTernary();
|
|
5986
|
-
this.expectPunct(":");
|
|
5987
|
-
const alternate = this.parseTernary();
|
|
5988
|
-
this.countNode();
|
|
5989
|
-
return {
|
|
5990
|
-
kind: "conditional",
|
|
5991
|
-
test,
|
|
5992
|
-
consequent,
|
|
5993
|
-
alternate
|
|
5994
|
-
};
|
|
5995
|
-
}
|
|
5996
|
-
return test;
|
|
5997
|
-
}
|
|
5998
|
-
parseBinary(minPrec) {
|
|
5999
|
-
let left = this.parseUnary();
|
|
6000
|
-
for (;;) {
|
|
6001
|
-
const tok = this.peek();
|
|
6002
|
-
if (tok.type !== "punct") break;
|
|
6003
|
-
const prec = BINARY_PRECEDENCE[tok.punct];
|
|
6004
|
-
if (prec === void 0 || prec < minPrec) break;
|
|
6005
|
-
const op = tok.punct;
|
|
6006
|
-
this.pos += 1;
|
|
6007
|
-
const right = this.parseBinary(prec + 1);
|
|
6008
|
-
this.countNode();
|
|
6009
|
-
if (isLogicalOp(op)) left = {
|
|
6010
|
-
kind: "logical",
|
|
6011
|
-
op,
|
|
6012
|
-
left,
|
|
6013
|
-
right
|
|
6014
|
-
};
|
|
6015
|
-
else if (isBinaryOp(op)) left = {
|
|
6016
|
-
kind: "binary",
|
|
6017
|
-
op,
|
|
6018
|
-
left,
|
|
6019
|
-
right
|
|
6020
|
-
};
|
|
6021
|
-
else throw new ExpressionParseError(`unexpected operator '${op}'`, tok.pos);
|
|
6022
|
-
}
|
|
6023
|
-
return left;
|
|
6024
|
-
}
|
|
6025
|
-
parseUnary() {
|
|
6026
|
-
const tok = this.peek();
|
|
6027
|
-
if (tok.type === "punct" && (tok.punct === "!" || tok.punct === "-")) {
|
|
6028
|
-
const op = tok.punct;
|
|
6029
|
-
this.pos += 1;
|
|
6030
|
-
const operand = this.parseUnary();
|
|
6031
|
-
this.countNode();
|
|
6032
|
-
return {
|
|
6033
|
-
kind: "unary",
|
|
6034
|
-
op,
|
|
6035
|
-
operand
|
|
6036
|
-
};
|
|
6037
|
-
}
|
|
6038
|
-
return this.parsePrimary();
|
|
6039
|
-
}
|
|
6040
|
-
parsePrimary() {
|
|
6041
|
-
const tok = this.next();
|
|
6042
|
-
switch (tok.type) {
|
|
6043
|
-
case "number":
|
|
6044
|
-
this.countNode();
|
|
6045
|
-
return {
|
|
6046
|
-
kind: "literal",
|
|
6047
|
-
value: tok.value
|
|
6048
|
-
};
|
|
6049
|
-
case "string":
|
|
6050
|
-
this.countNode();
|
|
6051
|
-
return {
|
|
6052
|
-
kind: "literal",
|
|
6053
|
-
value: tok.value
|
|
6054
|
-
};
|
|
6055
|
-
case "keyword":
|
|
6056
|
-
this.countNode();
|
|
6057
|
-
return {
|
|
6058
|
-
kind: "literal",
|
|
6059
|
-
value: tok.keyword === "null" ? null : tok.keyword === "true"
|
|
6060
|
-
};
|
|
6061
|
-
case "identifier": {
|
|
6062
|
-
const nextTok = this.peek();
|
|
6063
|
-
if (nextTok.type === "punct" && nextTok.punct === "(") return this.parseCall(tok.name, tok.pos);
|
|
6064
|
-
this.identifiers.add(tok.name);
|
|
6065
|
-
this.countNode();
|
|
6066
|
-
return {
|
|
6067
|
-
kind: "identifier",
|
|
6068
|
-
name: tok.name
|
|
6069
|
-
};
|
|
6070
|
-
}
|
|
6071
|
-
case "punct":
|
|
6072
|
-
if (tok.punct === "(") {
|
|
6073
|
-
const inner = this.parseTernary();
|
|
6074
|
-
this.expectPunct(")");
|
|
6075
|
-
return inner;
|
|
6076
|
-
}
|
|
6077
|
-
throw new ExpressionParseError(`unexpected token '${tok.punct}'`, tok.pos);
|
|
6078
|
-
case "eof": throw new ExpressionParseError("unexpected end of expression", tok.pos);
|
|
6079
|
-
}
|
|
6080
|
-
}
|
|
6081
|
-
parseCall(callee, pos) {
|
|
6082
|
-
if (!EXPRESSION_BUILTIN_NAMES.has(callee)) throw new ExpressionParseError(`unknown function '${callee}'`, pos);
|
|
6083
|
-
this.expectPunct("(");
|
|
6084
|
-
const args = [];
|
|
6085
|
-
if (!this.matchPunct(")")) for (;;) {
|
|
6086
|
-
args.push(this.parseTernary());
|
|
6087
|
-
if (args.length > 16) throw new ExpressionParseError(`too many arguments to '${callee}'`, pos);
|
|
6088
|
-
if (this.matchPunct(",")) continue;
|
|
6089
|
-
this.expectPunct(")");
|
|
6090
|
-
break;
|
|
6091
|
-
}
|
|
6092
|
-
this.callees.add(callee);
|
|
6093
|
-
this.countNode();
|
|
6094
|
-
return {
|
|
6095
|
-
kind: "call",
|
|
6096
|
-
callee,
|
|
6097
|
-
args
|
|
6098
|
-
};
|
|
6099
|
-
}
|
|
6100
|
-
};
|
|
6101
|
-
/** Tokenize + parse `source` into a validated `ParsedExpression`. Throws
|
|
6102
|
-
* `ExpressionParseError` on any lexical or grammatical failure. */
|
|
6103
|
-
function parseExpression(source) {
|
|
6104
|
-
return new Parser(tokenize(source)).parse();
|
|
6105
|
-
}
|
|
6106
|
-
//#endregion
|
|
6107
|
-
//#region src/expression/compile.ts
|
|
6108
|
-
/**
|
|
6109
|
-
* LRU compile cache for parsed expressions (spec §2.4 "parse once … LRU keyed
|
|
6110
|
-
* by expr"). The cache stores BOTH successes and failures (negative caching),
|
|
6111
|
-
* so a corrupt persisted string costs exactly one tokenize+parse total — not
|
|
6112
|
-
* one per read on a hot resolve path.
|
|
6113
|
-
*
|
|
6114
|
-
* The cache is a module-level singleton: entries are pure, content-addressed
|
|
6115
|
-
* ASTs keyed by the raw source string, so sharing one instance across all
|
|
6116
|
-
* callers is safe and maximises hit rate.
|
|
6117
|
-
*/
|
|
6118
|
-
var cache = /* @__PURE__ */ new Map();
|
|
6119
|
-
function getCached(source) {
|
|
6120
|
-
const hit = cache.get(source);
|
|
6121
|
-
if (hit !== void 0) {
|
|
6122
|
-
cache.delete(source);
|
|
6123
|
-
cache.set(source, hit);
|
|
6124
|
-
return hit;
|
|
6125
|
-
}
|
|
6126
|
-
let result;
|
|
6127
|
-
try {
|
|
6128
|
-
result = {
|
|
6129
|
-
ok: true,
|
|
6130
|
-
parsed: parseExpression(source)
|
|
6131
|
-
};
|
|
6132
|
-
} catch (err) {
|
|
6133
|
-
result = {
|
|
6134
|
-
ok: false,
|
|
6135
|
-
error: err instanceof ExpressionParseError ? err.message : String(err)
|
|
6136
|
-
};
|
|
6137
|
-
}
|
|
6138
|
-
cache.set(source, result);
|
|
6139
|
-
if (cache.size > 256) {
|
|
6140
|
-
const oldest = cache.keys().next().value;
|
|
6141
|
-
if (oldest !== void 0) cache.delete(oldest);
|
|
6142
|
-
}
|
|
6143
|
-
return result;
|
|
6144
|
-
}
|
|
6145
|
-
/** Compile `source` to a `ParsedExpression`, throwing `ExpressionParseError`
|
|
6146
|
-
* on failure. LRU/negative-cached. */
|
|
6147
|
-
function compileExpression(source) {
|
|
6148
|
-
const result = getCached(source);
|
|
6149
|
-
if (result.ok) return result.parsed;
|
|
6150
|
-
throw new ExpressionParseError(result.error);
|
|
6151
|
-
}
|
|
6152
|
-
/** Compile `source`, returning a discriminated result instead of throwing.
|
|
6153
|
-
* Used by read paths that must degrade rather than raise. LRU/negative-cached. */
|
|
6154
|
-
function compileExpressionSafe(source) {
|
|
6155
|
-
return getCached(source);
|
|
6156
|
-
}
|
|
6157
|
-
//#endregion
|
|
6158
|
-
//#region src/expression/evaluator.ts
|
|
6159
|
-
/**
|
|
6160
|
-
* Tree-walking evaluator for the safe expression mini-language.
|
|
6161
|
-
*
|
|
6162
|
-
* SECURITY (spec §4 rule 2/5):
|
|
6163
|
-
* - The scope is an `Object.create(null)` copy of ONLY the caller's own
|
|
6164
|
-
* enumerable binding entries, so `name in scope` is a pure own-key check and
|
|
6165
|
-
* `constructor` / `__proto__` / `toString` are plain unknown identifiers.
|
|
6166
|
-
* - Performs ZERO I/O and never touches `globalThis` / `Date` / `Math`
|
|
6167
|
-
* directly — the only external calls are into the frozen builtin table.
|
|
6168
|
-
* - The grammar has no loops/recursion/lambdas, so a walk is O(nodeCount) by
|
|
6169
|
-
* construction; the step counter is defense-in-depth for a crafted max-size
|
|
6170
|
-
* AST. Nothing blocks: there are no timers, awaits or unbounded loops.
|
|
6171
|
-
*/
|
|
6172
|
-
var EMPTY_HOOKS = Object.freeze({});
|
|
6173
|
-
/** Build a null-prototype scope from own-enumerable binding entries. Inherited
|
|
6174
|
-
* keys of the input (e.g. from a `{__proto__: {...}}` payload) are NOT copied,
|
|
6175
|
-
* so nothing smuggles in via the prototype chain. */
|
|
6176
|
-
function createExpressionScope(bindings) {
|
|
6177
|
-
const scope = Object.create(null);
|
|
6178
|
-
for (const key of Object.keys(bindings)) if (Object.prototype.hasOwnProperty.call(bindings, key)) scope[key] = bindings[key];
|
|
6179
|
-
return scope;
|
|
6180
|
-
}
|
|
6181
|
-
function isFiniteNumber(value) {
|
|
6182
|
-
return typeof value === "number" && Number.isFinite(value);
|
|
6183
|
-
}
|
|
6184
|
-
/** JS truthiness of a primitive value. */
|
|
6185
|
-
function truthy(value) {
|
|
6186
|
-
return Boolean(value);
|
|
6187
|
-
}
|
|
6188
|
-
function requireFinite(value, context) {
|
|
6189
|
-
if (!Number.isFinite(value)) throw new ExpressionEvalError(`${context} produced a non-finite result`);
|
|
6190
|
-
return value;
|
|
6191
|
-
}
|
|
6192
|
-
function step(ctx) {
|
|
6193
|
-
ctx.steps += 1;
|
|
6194
|
-
if (ctx.steps > ctx.maxSteps) throw new ExpressionEvalError("expression evaluation step budget exceeded");
|
|
6195
|
-
}
|
|
6196
|
-
function evalNode(node, ctx) {
|
|
6197
|
-
step(ctx);
|
|
6198
|
-
switch (node.kind) {
|
|
6199
|
-
case "literal": return node.value;
|
|
6200
|
-
case "identifier":
|
|
6201
|
-
if (!(node.name in ctx.scope)) throw new ExpressionEvalError(`unknown identifier: ${node.name}`);
|
|
6202
|
-
return ctx.scope[node.name];
|
|
6203
|
-
case "unary": return evalUnary(node.op, evalNode(node.operand, ctx));
|
|
6204
|
-
case "binary": return evalBinary(node.op, evalNode(node.left, ctx), evalNode(node.right, ctx));
|
|
6205
|
-
case "logical": {
|
|
6206
|
-
const left = evalNode(node.left, ctx);
|
|
6207
|
-
if (node.op === "&&") return truthy(left) ? evalNode(node.right, ctx) : left;
|
|
6208
|
-
return truthy(left) ? left : evalNode(node.right, ctx);
|
|
6209
|
-
}
|
|
6210
|
-
case "conditional": return truthy(evalNode(node.test, ctx)) ? evalNode(node.consequent, ctx) : evalNode(node.alternate, ctx);
|
|
6211
|
-
case "call": return evalCall(node.callee, node.args.map((a) => evalNode(a, ctx)), ctx.hooks);
|
|
6212
|
-
}
|
|
6213
|
-
}
|
|
6214
|
-
function evalUnary(op, operand) {
|
|
6215
|
-
if (op === "!") return !truthy(operand);
|
|
6216
|
-
if (!isFiniteNumber(operand)) throw new ExpressionEvalError("unary \"-\" requires a finite number");
|
|
6217
|
-
return requireFinite(-operand, "unary \"-\"");
|
|
6218
|
-
}
|
|
6219
|
-
function evalBinary(op, left, right) {
|
|
6220
|
-
switch (op) {
|
|
6221
|
-
case "==": return left === right;
|
|
6222
|
-
case "!=": return left !== right;
|
|
6223
|
-
case "+":
|
|
6224
|
-
if (typeof left === "string" && typeof right === "string") return left + right;
|
|
6225
|
-
if (isFiniteNumber(left) && isFiniteNumber(right)) return requireFinite(left + right, "\"+\"");
|
|
6226
|
-
throw new ExpressionEvalError("\"+\" requires two numbers or two strings");
|
|
6227
|
-
case "-":
|
|
6228
|
-
case "*":
|
|
6229
|
-
case "/":
|
|
6230
|
-
case "%":
|
|
6231
|
-
if (!isFiniteNumber(left) || !isFiniteNumber(right)) throw new ExpressionEvalError(`"${op}" requires two finite numbers`);
|
|
6232
|
-
return requireFinite(op === "-" ? left - right : op === "*" ? left * right : op === "/" ? left / right : left % right, `"${op}"`);
|
|
6233
|
-
case "<":
|
|
6234
|
-
case "<=":
|
|
6235
|
-
case ">":
|
|
6236
|
-
case ">=":
|
|
6237
|
-
if (isFiniteNumber(left) && isFiniteNumber(right)) return op === "<" ? left < right : op === "<=" ? left <= right : op === ">" ? left > right : left >= right;
|
|
6238
|
-
if (typeof left === "string" && typeof right === "string") return op === "<" ? left < right : op === "<=" ? left <= right : op === ">" ? left > right : left >= right;
|
|
6239
|
-
throw new ExpressionEvalError(`"${op}" requires two numbers or two strings`);
|
|
6240
|
-
}
|
|
6241
|
-
}
|
|
6242
|
-
function evalCall(callee, args, hooks) {
|
|
6243
|
-
if (!Object.prototype.hasOwnProperty.call(EXPRESSION_BUILTINS, callee)) throw new ExpressionEvalError(`unknown function: ${callee}`);
|
|
6244
|
-
const builtin = EXPRESSION_BUILTINS[callee];
|
|
6245
|
-
if (args.length < builtin.minArgs || args.length > builtin.maxArgs) throw new ExpressionEvalError(`${callee}: wrong number of arguments (${args.length})`);
|
|
6246
|
-
return builtin.apply(args, hooks);
|
|
6247
|
-
}
|
|
6248
|
-
/** Evaluate an AST node against a scope. Throws `ExpressionEvalError` on any
|
|
6249
|
-
* runtime failure (unknown identifier, type mismatch, non-finite result,
|
|
6250
|
-
* step-budget exhaustion). */
|
|
6251
|
-
function evaluateAst(node, scope, opts) {
|
|
6252
|
-
return evalNode(node, {
|
|
6253
|
-
scope,
|
|
6254
|
-
hooks: opts?.hooks ?? EMPTY_HOOKS,
|
|
6255
|
-
maxSteps: opts?.maxSteps ?? 4096,
|
|
6256
|
-
steps: 0
|
|
6257
|
-
});
|
|
6258
|
-
}
|
|
6259
|
-
//#endregion
|
|
6260
|
-
//#region src/expression/expression-source.ts
|
|
6261
|
-
/** The `now` epoch-ms binding is auto-injected into every evaluation and is a
|
|
6262
|
-
* reserved binding name (authors may not rebind it). */
|
|
6263
|
-
var EXPRESSION_INJECTED_NOW = "now";
|
|
6264
|
-
/**
|
|
6265
|
-
* Coerce an untrusted `getByPath` / mirror read to an `ExpressionValue`.
|
|
6266
|
-
* Non-primitive values (objects, arrays, `undefined`, functions, bigint,
|
|
6267
|
-
* symbol) and non-finite numbers become `undefined` so the caller can apply
|
|
6268
|
-
* its binding-miss policy (→ `null`). `null` itself is a valid value.
|
|
6269
|
-
*/
|
|
6270
|
-
function toExpressionValue(raw) {
|
|
6271
|
-
if (raw === null) return null;
|
|
6272
|
-
if (typeof raw === "string") return raw;
|
|
6273
|
-
if (typeof raw === "boolean") return raw;
|
|
6274
|
-
if (typeof raw === "number") return Number.isFinite(raw) ? raw : void 0;
|
|
6275
|
-
}
|
|
6276
|
-
/**
|
|
6277
|
-
* Author-time validation. Returns `null` when the source is valid, else a
|
|
6278
|
-
* human-readable error message. Checks: the expression compiles; binding count
|
|
6279
|
-
* is within `MAX_EXPRESSION_BINDINGS`; every binding name is a legal identifier,
|
|
6280
|
-
* is not reserved (`now`/keywords) and does not shadow a builtin; and every
|
|
6281
|
-
* FREE identifier of the AST is covered by a binding or the injected `now`.
|
|
6282
|
-
*/
|
|
6283
|
-
function validateExpressionSource(src) {
|
|
6284
|
-
const names = Object.keys(src.bindings);
|
|
6285
|
-
if (names.length > 32) return `too many bindings (${names.length} > 32)`;
|
|
6286
|
-
for (const name of names) {
|
|
6287
|
-
if (!EXPRESSION_IDENTIFIER_RE.test(name)) return `invalid binding name '${name}'`;
|
|
6288
|
-
if (RESERVED_BINDING_NAMES.has(name)) return `binding name '${name}' is reserved`;
|
|
6289
|
-
if (EXPRESSION_BUILTIN_NAMES.has(name)) return `binding name '${name}' shadows a builtin function`;
|
|
6290
|
-
}
|
|
6291
|
-
const compiled = compileExpressionSafe(src.expr);
|
|
6292
|
-
if (!compiled.ok) return compiled.error;
|
|
6293
|
-
const bound = new Set(names);
|
|
6294
|
-
for (const id of compiled.parsed.identifiers) {
|
|
6295
|
-
if (id === "now") continue;
|
|
6296
|
-
if (!bound.has(id)) return `expression references unbound identifier '${id}'`;
|
|
6297
|
-
}
|
|
6298
|
-
return null;
|
|
6299
|
-
}
|
|
6300
|
-
/**
|
|
6301
|
-
* Shared read-path evaluation. Builds a null-proto scope from `bindingValues`
|
|
6302
|
-
* plus the injected `now` (supplied by the caller for determinism and
|
|
6303
|
-
* testability), compiles via the LRU, and evaluates. Any failure (parse or
|
|
6304
|
-
* eval) returns `{ ok: false }` — the caller treats that as "skip this
|
|
6305
|
-
* derivation", never as a throw that takes the pass down.
|
|
6306
|
-
*/
|
|
6307
|
-
function evaluateExpressionSource(expr, bindingValues, now, opts) {
|
|
6308
|
-
const compiled = compileExpressionSafe(expr);
|
|
6309
|
-
if (!compiled.ok) return {
|
|
6310
|
-
ok: false,
|
|
6311
|
-
error: compiled.error
|
|
6312
|
-
};
|
|
6313
|
-
const scope = createExpressionScope({
|
|
6314
|
-
...bindingValues,
|
|
6315
|
-
["now"]: now
|
|
6316
|
-
});
|
|
6317
|
-
const hooks = {
|
|
6318
|
-
...opts?.hooks,
|
|
6319
|
-
now
|
|
6320
|
-
};
|
|
6321
|
-
try {
|
|
6322
|
-
return {
|
|
6323
|
-
ok: true,
|
|
6324
|
-
value: evaluateAst(compiled.parsed.ast, scope, {
|
|
6325
|
-
...opts,
|
|
6326
|
-
hooks
|
|
6327
|
-
})
|
|
6328
|
-
};
|
|
6329
|
-
} catch (err) {
|
|
6330
|
-
return {
|
|
6331
|
-
ok: false,
|
|
6332
|
-
error: err instanceof ExpressionEvalError ? err.message : String(err)
|
|
6333
|
-
};
|
|
6334
|
-
}
|
|
6335
|
-
}
|
|
6336
|
-
//#endregion
|
|
6337
5534
|
//#region src/expression/binding-source.ts
|
|
6338
5535
|
/**
|
|
6339
5536
|
* What an expression's named bindings READ from.
|
|
@@ -6394,10 +5591,10 @@ var ExpressionBindingSourceSchema = zod.z.union([
|
|
|
6394
5591
|
* Compiles are LRU-cached, so repeated validation of the same string is a hit.
|
|
6395
5592
|
*/
|
|
6396
5593
|
var ExpressionSourceSchema = zod.z.object({
|
|
6397
|
-
expr: zod.z.string().min(1).max(MAX_EXPRESSION_SOURCE_LENGTH),
|
|
6398
|
-
bindings: zod.z.record(zod.z.string().regex(EXPRESSION_IDENTIFIER_RE), ExpressionBindingSourceSchema)
|
|
5594
|
+
expr: zod.z.string().min(1).max(require_composition.MAX_EXPRESSION_SOURCE_LENGTH),
|
|
5595
|
+
bindings: zod.z.record(zod.z.string().regex(require_composition.EXPRESSION_IDENTIFIER_RE), ExpressionBindingSourceSchema)
|
|
6399
5596
|
}).superRefine((src, ctx) => {
|
|
6400
|
-
const err = validateExpressionSource(src);
|
|
5597
|
+
const err = require_composition.validateExpressionSource(src);
|
|
6401
5598
|
if (err !== null) ctx.addIssue({
|
|
6402
5599
|
code: "custom",
|
|
6403
5600
|
message: err,
|
|
@@ -6478,10 +5675,10 @@ var AutomationConditionLeafSchema = zod.z.object({
|
|
|
6478
5675
|
*/
|
|
6479
5676
|
var AutomationConditionExpressionSchema = zod.z.object({
|
|
6480
5677
|
kind: zod.z.literal("expression"),
|
|
6481
|
-
expr: zod.z.string().min(1).max(MAX_EXPRESSION_SOURCE_LENGTH),
|
|
6482
|
-
bindings: zod.z.record(zod.z.string().regex(EXPRESSION_IDENTIFIER_RE), ExpressionBindingSourceSchema)
|
|
5678
|
+
expr: zod.z.string().min(1).max(require_composition.MAX_EXPRESSION_SOURCE_LENGTH),
|
|
5679
|
+
bindings: zod.z.record(zod.z.string().regex(require_composition.EXPRESSION_IDENTIFIER_RE), ExpressionBindingSourceSchema)
|
|
6483
5680
|
}).superRefine((src, ctx) => {
|
|
6484
|
-
const err = validateExpressionSource(src);
|
|
5681
|
+
const err = require_composition.validateExpressionSource(src);
|
|
6485
5682
|
if (err !== null) ctx.addIssue({
|
|
6486
5683
|
code: "custom",
|
|
6487
5684
|
message: err,
|
|
@@ -7555,7 +6752,7 @@ var audioAnalysisCapability = {
|
|
|
7555
6752
|
mode: "singleton",
|
|
7556
6753
|
kind: "wrapper",
|
|
7557
6754
|
defaultActive: true,
|
|
7558
|
-
deviceTypes: [
|
|
6755
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
7559
6756
|
exposesDeviceSettings: true,
|
|
7560
6757
|
methods: {
|
|
7561
6758
|
/**
|
|
@@ -8702,7 +7899,7 @@ var cameraPipelineConfigCapability = {
|
|
|
8702
7899
|
mode: "singleton",
|
|
8703
7900
|
kind: "wrapper",
|
|
8704
7901
|
defaultActive: true,
|
|
8705
|
-
deviceTypes: [
|
|
7902
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
8706
7903
|
exposesDeviceSettings: true,
|
|
8707
7904
|
methods: {}
|
|
8708
7905
|
};
|
|
@@ -9812,7 +9009,7 @@ var cameraStreamsCapability = {
|
|
|
9812
9009
|
mode: "singleton",
|
|
9813
9010
|
kind: "wrapper",
|
|
9814
9011
|
defaultActive: true,
|
|
9815
|
-
deviceTypes: [
|
|
9012
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
9816
9013
|
methods: {
|
|
9817
9014
|
getCameraStreams: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.array(require_sleep.CameraStreamSchema).readonly()),
|
|
9818
9015
|
getBrokerStreams: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.array(require_sleep.ProfileSlotSchema).readonly()),
|
|
@@ -10136,177 +9333,6 @@ function kebabToCamel(s) {
|
|
|
10136
9333
|
return s.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
10137
9334
|
}
|
|
10138
9335
|
//#endregion
|
|
10139
|
-
//#region src/composition/composition.ts
|
|
10140
|
-
/**
|
|
10141
|
-
* A COMPOSITION: a device assembled from other devices' state (D659).
|
|
10142
|
-
*
|
|
10143
|
-
* The data half of the composed-devices spec §1. A composition is stored inside
|
|
10144
|
-
* a core block (`CoreBlock.source.kind === 'composition'`) and run by the shared
|
|
10145
|
-
* `composer` runner. It names a TARGET and, for each capability of the target, a
|
|
10146
|
-
* FIELD MAP: every state field has exactly one source.
|
|
10147
|
-
*
|
|
10148
|
-
* Sources are keyed by `(addonId, stableId)`, NEVER by numeric device id. A
|
|
10149
|
-
* resync reissues numeric ids, and a composition that stored them would be
|
|
10150
|
-
* orphaned by the first re-adoption (the linked-devices lesson). A `stableId` is
|
|
10151
|
-
* unique only within its owning addon, so the owner travels with it.
|
|
10152
|
-
*
|
|
10153
|
-
* Later slices EXTEND these unions and do not reshape them. The variants a later
|
|
10154
|
-
* slice implements are already in the wire shape, and `validateComposition`
|
|
10155
|
-
* refuses them BY NAME until then:
|
|
10156
|
-
* target `existing` → slice 2 · feature `passthrough` → slice 3 ·
|
|
10157
|
-
* `commands` (`forward`) → slice 3 · source `code` → slice 4.
|
|
10158
|
-
*/
|
|
10159
|
-
/** The owner of every composed device: the `composer` builtin. */
|
|
10160
|
-
var COMPOSER_ADDON_ID = "composer";
|
|
10161
|
-
/** A composed device's stableId is this prefix plus the block id. */
|
|
10162
|
-
var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
|
|
10163
|
-
/** A device with more capabilities than this is two devices. */
|
|
10164
|
-
var MAX_COMPOSITION_FEATURES = 16;
|
|
10165
|
-
var MAX_COMPOSITION_FIELDS_PER_FEATURE = 32;
|
|
10166
|
-
/** Longest snippet a `code` source may carry (slice 4). */
|
|
10167
|
-
var MAX_COMPOSITION_CODE_LENGTH = 2e4;
|
|
10168
|
-
var CompositionSourceRefSchema = zod.z.object({
|
|
10169
|
-
/** The owning addon; `stableId` is unique only within it. */
|
|
10170
|
-
addonId: zod.z.string().min(1),
|
|
10171
|
-
stableId: zod.z.string().min(1)
|
|
10172
|
-
});
|
|
10173
|
-
/** One field of one capability of one source device. */
|
|
10174
|
-
var CompositionFieldReadSchema = zod.z.object({
|
|
10175
|
-
source: CompositionSourceRefSchema,
|
|
10176
|
-
cap: zod.z.string().min(1),
|
|
10177
|
-
/** Dotted path into the source cap's runtime-state slice. */
|
|
10178
|
-
fieldPath: zod.z.string().min(1)
|
|
10179
|
-
});
|
|
10180
|
-
/** `from`: copy one source field verbatim. It is also an expression's `from` binding. */
|
|
10181
|
-
var CompositionFromSourceSchema = CompositionFieldReadSchema.extend({ kind: zod.z.literal("from") });
|
|
10182
|
-
/** A constant binding. No device is read. */
|
|
10183
|
-
var CompositionLiteralBindingSchema = zod.z.object({
|
|
10184
|
-
kind: zod.z.literal("literal"),
|
|
10185
|
-
value: zod.z.union([
|
|
10186
|
-
zod.z.string(),
|
|
10187
|
-
zod.z.number(),
|
|
10188
|
-
zod.z.boolean(),
|
|
10189
|
-
zod.z.null()
|
|
10190
|
-
])
|
|
10191
|
-
});
|
|
10192
|
-
var CompositionBindingSchema = zod.z.discriminatedUnion("kind", [CompositionFromSourceSchema, CompositionLiteralBindingSchema]);
|
|
10193
|
-
/**
|
|
10194
|
-
* `expression`: a formula over named bindings, in the salvaged expression
|
|
10195
|
-
* engine. Validated at parse by the SAME function every other consumer runs,
|
|
10196
|
-
* so the editor and the store cannot disagree.
|
|
10197
|
-
*/
|
|
10198
|
-
var CompositionExpressionSourceSchema = zod.z.object({
|
|
10199
|
-
kind: zod.z.literal("expression"),
|
|
10200
|
-
expr: zod.z.string().min(1).max(MAX_EXPRESSION_SOURCE_LENGTH),
|
|
10201
|
-
bindings: zod.z.record(zod.z.string().regex(EXPRESSION_IDENTIFIER_RE), CompositionBindingSchema)
|
|
10202
|
-
}).superRefine((src, ctx) => {
|
|
10203
|
-
const err = validateExpressionSource(src);
|
|
10204
|
-
if (err !== null) ctx.addIssue({
|
|
10205
|
-
code: "custom",
|
|
10206
|
-
message: err,
|
|
10207
|
-
path: ["expr"]
|
|
10208
|
-
});
|
|
10209
|
-
});
|
|
10210
|
-
/** `code`: RESERVED for slice 4 (own runner, one-way eject). */
|
|
10211
|
-
var CompositionCodeSourceSchema = zod.z.object({
|
|
10212
|
-
kind: zod.z.literal("code"),
|
|
10213
|
-
code: zod.z.string().min(1).max(MAX_COMPOSITION_CODE_LENGTH)
|
|
10214
|
-
});
|
|
10215
|
-
var CompositionFieldSourceSchema = zod.z.discriminatedUnion("kind", [
|
|
10216
|
-
CompositionFromSourceSchema,
|
|
10217
|
-
CompositionExpressionSourceSchema,
|
|
10218
|
-
CompositionCodeSourceSchema
|
|
10219
|
-
]);
|
|
10220
|
-
/** `forward`: RESERVED for slice 3. Argument mapping arrives there as an additive optional `args`. */
|
|
10221
|
-
var CompositionForwardCommandSchema = zod.z.object({
|
|
10222
|
-
kind: zod.z.literal("forward"),
|
|
10223
|
-
source: CompositionSourceRefSchema,
|
|
10224
|
-
cap: zod.z.string().min(1),
|
|
10225
|
-
method: zod.z.string().min(1)
|
|
10226
|
-
});
|
|
10227
|
-
var CompositionCommandTargetSchema = zod.z.discriminatedUnion("kind", [CompositionForwardCommandSchema, CompositionCodeSourceSchema]);
|
|
10228
|
-
var CompositionFieldsFeatureSchema = zod.z.object({
|
|
10229
|
-
kind: zod.z.literal("fields"),
|
|
10230
|
-
cap: zod.z.string().min(1),
|
|
10231
|
-
fields: zod.z.record(zod.z.string().min(1), CompositionFieldSourceSchema).refine((fields) => Object.keys(fields).length <= 32, { message: `at most 32 fields per capability` }),
|
|
10232
|
-
/** RESERVED for slice 3; refused by the validator when non-empty. */
|
|
10233
|
-
commands: zod.z.record(zod.z.string().min(1), CompositionCommandTargetSchema).optional()
|
|
10234
|
-
});
|
|
10235
|
-
/** RESERVED for slice 3: a media / data-plane cap, whole, from one source. */
|
|
10236
|
-
var CompositionPassthroughFeatureSchema = zod.z.object({
|
|
10237
|
-
kind: zod.z.literal("passthrough"),
|
|
10238
|
-
cap: zod.z.string().min(1),
|
|
10239
|
-
source: CompositionSourceRefSchema
|
|
10240
|
-
});
|
|
10241
|
-
var CompositionFeatureSchema = zod.z.discriminatedUnion("kind", [CompositionFieldsFeatureSchema, CompositionPassthroughFeatureSchema]);
|
|
10242
|
-
/**
|
|
10243
|
-
* A NEW device. It has no `name` of its own: the block's name IS the device's
|
|
10244
|
-
* name (2026-08-08). A second copy here would be a second writer of one string (D62).
|
|
10245
|
-
*/
|
|
10246
|
-
var CompositionNewTargetSchema = zod.z.object({
|
|
10247
|
-
kind: zod.z.literal("new"),
|
|
10248
|
-
type: zod.z.enum(require_sleep.DeviceType),
|
|
10249
|
-
role: zod.z.enum(require_sleep.DeviceRole).optional()
|
|
10250
|
-
});
|
|
10251
|
-
/** RESERVED for slice 2: customize an existing device. */
|
|
10252
|
-
var CompositionExistingTargetSchema = zod.z.object({
|
|
10253
|
-
kind: zod.z.literal("existing"),
|
|
10254
|
-
device: CompositionSourceRefSchema
|
|
10255
|
-
});
|
|
10256
|
-
var CompositionTargetSchema = zod.z.discriminatedUnion("kind", [CompositionNewTargetSchema, CompositionExistingTargetSchema]);
|
|
10257
|
-
var CompositionSchema = zod.z.object({
|
|
10258
|
-
target: CompositionTargetSchema,
|
|
10259
|
-
features: zod.z.array(CompositionFeatureSchema).min(1).max(16)
|
|
10260
|
-
}).superRefine((composition, ctx) => {
|
|
10261
|
-
const seen = /* @__PURE__ */ new Set();
|
|
10262
|
-
composition.features.forEach((feature, index) => {
|
|
10263
|
-
if (seen.has(feature.cap)) ctx.addIssue({
|
|
10264
|
-
code: "custom",
|
|
10265
|
-
message: `capability \`${feature.cap}\` is composed twice — one feature per capability`,
|
|
10266
|
-
path: [
|
|
10267
|
-
"features",
|
|
10268
|
-
index,
|
|
10269
|
-
"cap"
|
|
10270
|
-
]
|
|
10271
|
-
});
|
|
10272
|
-
seen.add(feature.cap);
|
|
10273
|
-
});
|
|
10274
|
-
});
|
|
10275
|
-
function compositionSourceKey(ref) {
|
|
10276
|
-
return `${ref.addonId}/${ref.stableId}`;
|
|
10277
|
-
}
|
|
10278
|
-
function compositionSliceKey(ref, cap) {
|
|
10279
|
-
return `${compositionSourceKey(ref)}#${cap}`;
|
|
10280
|
-
}
|
|
10281
|
-
function compositionReadKey(read) {
|
|
10282
|
-
return `${compositionSliceKey(read.source, read.cap)}.${read.fieldPath}`;
|
|
10283
|
-
}
|
|
10284
|
-
function composedDeviceStableId(blockId) {
|
|
10285
|
-
return `${COMPOSED_DEVICE_STABLE_ID_PREFIX}${blockId}`;
|
|
10286
|
-
}
|
|
10287
|
-
function composedDeviceRef(blockId) {
|
|
10288
|
-
return {
|
|
10289
|
-
addonId: COMPOSER_ADDON_ID,
|
|
10290
|
-
stableId: composedDeviceStableId(blockId)
|
|
10291
|
-
};
|
|
10292
|
-
}
|
|
10293
|
-
/** Every device field a source reads: `from` is one read, an expression is its `from` bindings. */
|
|
10294
|
-
function fieldReads(source) {
|
|
10295
|
-
switch (source.kind) {
|
|
10296
|
-
case "from": return [{
|
|
10297
|
-
source: source.source,
|
|
10298
|
-
cap: source.cap,
|
|
10299
|
-
fieldPath: source.fieldPath
|
|
10300
|
-
}];
|
|
10301
|
-
case "expression": return Object.values(source.bindings).flatMap((binding) => binding.kind === "from" ? [{
|
|
10302
|
-
source: binding.source,
|
|
10303
|
-
cap: binding.cap,
|
|
10304
|
-
fieldPath: binding.fieldPath
|
|
10305
|
-
}] : []);
|
|
10306
|
-
case "code": return [];
|
|
10307
|
-
}
|
|
10308
|
-
}
|
|
10309
|
-
//#endregion
|
|
10310
9336
|
//#region src/composition/composition-report.ts
|
|
10311
9337
|
/**
|
|
10312
9338
|
* What the validator says about a composition and what the composer says about
|
|
@@ -10329,7 +9355,12 @@ var CompositionProblemCodeSchema = zod.z.enum([
|
|
|
10329
9355
|
"source-field-unknown",
|
|
10330
9356
|
"expression-invalid",
|
|
10331
9357
|
"cycle",
|
|
10332
|
-
"duplicate-target"
|
|
9358
|
+
"duplicate-target",
|
|
9359
|
+
"not-item-array",
|
|
9360
|
+
"item-field-reserved",
|
|
9361
|
+
"target-missing",
|
|
9362
|
+
"target-unreadable",
|
|
9363
|
+
"not-replaceable"
|
|
10333
9364
|
]);
|
|
10334
9365
|
/** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.celsius`. */
|
|
10335
9366
|
var CompositionProblemSchema = zod.z.object({
|
|
@@ -10343,8 +9374,18 @@ var CompositionFieldKindSchema = zod.z.enum([
|
|
|
10343
9374
|
"boolean",
|
|
10344
9375
|
"enum"
|
|
10345
9376
|
]);
|
|
10346
|
-
/**
|
|
10347
|
-
|
|
9377
|
+
/**
|
|
9378
|
+
* How a field says "unavailable" (D393, D663): `null` when its schema allows it;
|
|
9379
|
+
* `unknown` — the enum's own word — for an ADDED field that has one; `release`
|
|
9380
|
+
* for a REPLACED field of an existing device, whose claim is released so the
|
|
9381
|
+
* native provider's reading shows again; else the (composed) device goes offline.
|
|
9382
|
+
*/
|
|
9383
|
+
var CompositionUnavailableModeSchema = zod.z.enum([
|
|
9384
|
+
"null",
|
|
9385
|
+
"offline",
|
|
9386
|
+
"unknown",
|
|
9387
|
+
"release"
|
|
9388
|
+
]);
|
|
10348
9389
|
/**
|
|
10349
9390
|
* `derived` reads a source · `clock` reads only `now`, recomputed when a sibling
|
|
10350
9391
|
* field of its cap changes value · `constant` reads nothing, computed once.
|
|
@@ -10354,9 +9395,17 @@ var CompositionFieldRoleSchema = zod.z.enum([
|
|
|
10354
9395
|
"clock",
|
|
10355
9396
|
"constant"
|
|
10356
9397
|
]);
|
|
9398
|
+
/** The item a planned field belongs to; its `path` is then the dotted path INSIDE the item. */
|
|
9399
|
+
var CompositionFieldItemSchema = zod.z.object({
|
|
9400
|
+
arrayPath: zod.z.string(),
|
|
9401
|
+
key: zod.z.string(),
|
|
9402
|
+
label: zod.z.string()
|
|
9403
|
+
});
|
|
10357
9404
|
var CompositionFieldPlanSchema = zod.z.object({
|
|
10358
9405
|
cap: zod.z.string(),
|
|
9406
|
+
/** Top-level field, or — when `item` is set — the dotted path inside that item. */
|
|
10359
9407
|
path: zod.z.string(),
|
|
9408
|
+
item: CompositionFieldItemSchema.optional(),
|
|
10360
9409
|
/** Index into `composition.features` this field's capability came from —
|
|
10361
9410
|
* lets a source/cycle problem name its path as `features[N].fields.<path>`
|
|
10362
9411
|
* (the same convention `planComposition`'s own problems already use). */
|
|
@@ -10366,19 +9415,45 @@ var CompositionFieldPlanSchema = zod.z.object({
|
|
|
10366
9415
|
required: zod.z.boolean(),
|
|
10367
9416
|
unavailable: CompositionUnavailableModeSchema,
|
|
10368
9417
|
role: CompositionFieldRoleSchema,
|
|
10369
|
-
source: CompositionFieldSourceSchema,
|
|
10370
|
-
reads: zod.z.array(CompositionFieldReadSchema)
|
|
9418
|
+
source: require_composition.CompositionFieldSourceSchema,
|
|
9419
|
+
reads: zod.z.array(require_composition.CompositionFieldReadSchema)
|
|
9420
|
+
});
|
|
9421
|
+
/** The key every record map, log line and graph node uses for a planned field. Two items' `status` are two keys. */
|
|
9422
|
+
function compositionFieldKey(plan) {
|
|
9423
|
+
const path = plan.item === void 0 ? plan.path : require_composition.compositionItemFieldPath(plan.item.arrayPath, plan.item.key, plan.path);
|
|
9424
|
+
return `${plan.cap}.${path}`;
|
|
9425
|
+
}
|
|
9426
|
+
/**
|
|
9427
|
+
* What a feature does to its target (D663): `new` builds a capability on a new
|
|
9428
|
+
* device; on an existing device it `add`s a capability the device lacks, or
|
|
9429
|
+
* `replace`s named fields of one it has natively.
|
|
9430
|
+
*/
|
|
9431
|
+
var CompositionFeatureModeSchema = zod.z.enum([
|
|
9432
|
+
"new",
|
|
9433
|
+
"add",
|
|
9434
|
+
"replace"
|
|
9435
|
+
]);
|
|
9436
|
+
var CompositionFeaturePlanSchema = zod.z.object({
|
|
9437
|
+
featureIndex: zod.z.number().int().nonnegative(),
|
|
9438
|
+
cap: zod.z.string(),
|
|
9439
|
+
mode: CompositionFeatureModeSchema
|
|
10371
9440
|
});
|
|
10372
9441
|
var CompositionValidationSchema = zod.z.object({
|
|
10373
9442
|
ok: zod.z.boolean(),
|
|
10374
9443
|
problems: zod.z.array(CompositionProblemSchema),
|
|
10375
|
-
fields: zod.z.array(CompositionFieldPlanSchema)
|
|
9444
|
+
fields: zod.z.array(CompositionFieldPlanSchema),
|
|
9445
|
+
features: zod.z.array(CompositionFeaturePlanSchema)
|
|
10376
9446
|
});
|
|
10377
|
-
/**
|
|
9447
|
+
/**
|
|
9448
|
+
* Mirrors `CoreBlockStatus`, pinned by `core-blocks-source.spec.ts`. `degraded`:
|
|
9449
|
+
* running, but a replaced field is released to the native provider while its
|
|
9450
|
+
* source is unavailable (ruling S1b, D663) — the reason names each such field.
|
|
9451
|
+
*/
|
|
10378
9452
|
var CompositionRunStatusSchema = zod.z.enum([
|
|
10379
9453
|
"stopped",
|
|
10380
9454
|
"starting",
|
|
10381
9455
|
"running",
|
|
9456
|
+
"degraded",
|
|
10382
9457
|
"failed"
|
|
10383
9458
|
]);
|
|
10384
9459
|
var CompositionFieldStateKindSchema = zod.z.enum([
|
|
@@ -10389,6 +9464,8 @@ var CompositionFieldStateKindSchema = zod.z.enum([
|
|
|
10389
9464
|
var CompositionFieldStateSchema = zod.z.object({
|
|
10390
9465
|
cap: zod.z.string(),
|
|
10391
9466
|
path: zod.z.string(),
|
|
9467
|
+
/** Same as the plan's: two items' `status` are told apart by it. */
|
|
9468
|
+
item: CompositionFieldItemSchema.optional(),
|
|
10392
9469
|
state: CompositionFieldStateKindSchema,
|
|
10393
9470
|
/** `null` whenever `state === 'unavailable'`; never a plausible stand-in (D393). */
|
|
10394
9471
|
value: zod.z.union([
|
|
@@ -10470,11 +9547,14 @@ function coreBlockIdFromAddonId(addonId) {
|
|
|
10470
9547
|
* agent is the reason placement is not fixed to the hub. */
|
|
10471
9548
|
var CoreBlockPlacementSchema = zod.z.union([zod.z.literal("hub"), zod.z.string().min(1)]);
|
|
10472
9549
|
/** What a block's process is doing. Mirrors the addon runner's own lifecycle so
|
|
10473
|
-
* a failing block reads the same way a failing addon does.
|
|
9550
|
+
* a failing block reads the same way a failing addon does. `degraded` is a
|
|
9551
|
+
* composition on an existing device with a field handed back to the native
|
|
9552
|
+
* provider while its source is unavailable (S1b, D663); the error names it. */
|
|
10474
9553
|
var CoreBlockStatusSchema = zod.z.enum([
|
|
10475
9554
|
"stopped",
|
|
10476
9555
|
"starting",
|
|
10477
9556
|
"running",
|
|
9557
|
+
"degraded",
|
|
10478
9558
|
"failed"
|
|
10479
9559
|
]);
|
|
10480
9560
|
/** Client-authored fields. */
|
|
@@ -10507,14 +9587,21 @@ var CoreBlockSourceSchema = zod.z.discriminatedUnion("kind", [
|
|
|
10507
9587
|
}),
|
|
10508
9588
|
zod.z.object({
|
|
10509
9589
|
kind: zod.z.literal("composition"),
|
|
10510
|
-
composition: CompositionSchema
|
|
9590
|
+
composition: require_composition.CompositionSchema
|
|
10511
9591
|
})
|
|
10512
9592
|
]);
|
|
10513
|
-
/**
|
|
9593
|
+
/**
|
|
9594
|
+
* The client-authored half of a composition block. No `code`, no placement (hub-only; Phase 7).
|
|
9595
|
+
*
|
|
9596
|
+
* `name`: REQUIRED for a `new` target — the block's name IS the device's name
|
|
9597
|
+
* (D659). ABSENT for an `existing` target — a customization has no name of its
|
|
9598
|
+
* own; the server derives one (`customizationBlockName`, D663) and refuses a
|
|
9599
|
+
* client-sent name by name.
|
|
9600
|
+
*/
|
|
10514
9601
|
var CompositionBlockInputSchema = zod.z.object({
|
|
10515
|
-
name: zod.z.string().min(1).max(120),
|
|
9602
|
+
name: zod.z.string().min(1).max(120).optional(),
|
|
10516
9603
|
enabled: zod.z.boolean(),
|
|
10517
|
-
composition: CompositionSchema
|
|
9604
|
+
composition: require_composition.CompositionSchema
|
|
10518
9605
|
});
|
|
10519
9606
|
/** A stored block. */
|
|
10520
9607
|
var CoreBlockSchema = CoreBlockInputSchema.extend({
|
|
@@ -10571,6 +9658,11 @@ var CompositionGetReadSchema = zod.z.discriminatedUnion("state", [zod.z.object({
|
|
|
10571
9658
|
state: zod.z.literal("loaded"),
|
|
10572
9659
|
view: CompositionViewSchema.nullable()
|
|
10573
9660
|
}), CompositionsNotLoadedSchema]);
|
|
9661
|
+
/** The customization of one existing device, found by its TARGET, or `not-loaded` (D315). */
|
|
9662
|
+
var CompositionCustomizationReadSchema = zod.z.discriminatedUnion("state", [zod.z.object({
|
|
9663
|
+
state: zod.z.literal("loaded"),
|
|
9664
|
+
view: CompositionViewSchema.nullable()
|
|
9665
|
+
}), CompositionsNotLoadedSchema]);
|
|
10574
9666
|
/** What a compile attempt produced. */
|
|
10575
9667
|
var CoreBlockCompileResultSchema = zod.z.object({
|
|
10576
9668
|
ok: zod.z.boolean(),
|
|
@@ -10681,11 +9773,19 @@ var coreBlocksCapability = {
|
|
|
10681
9773
|
getComposition: require_sleep.method(zod.z.object({ blockId: zod.z.string() }), CompositionGetReadSchema, { auth: "admin" }),
|
|
10682
9774
|
listCompositions: require_sleep.method(zod.z.object({}), CompositionListReadSchema, { auth: "admin" }),
|
|
10683
9775
|
/**
|
|
9776
|
+
* The composition that customizes an existing device (D663), found by its
|
|
9777
|
+
* TARGET — never by the block's derived name. One per device at most.
|
|
9778
|
+
*/
|
|
9779
|
+
getCustomization: require_sleep.method(zod.z.object({
|
|
9780
|
+
addonId: zod.z.string().min(1),
|
|
9781
|
+
stableId: zod.z.string().min(1)
|
|
9782
|
+
}), CompositionCustomizationReadSchema, { auth: "admin" }),
|
|
9783
|
+
/**
|
|
10684
9784
|
* Validate without saving — the same verdict the save reaches. A mutation
|
|
10685
9785
|
* for its body size; it writes nothing, so its access is `view`.
|
|
10686
9786
|
*/
|
|
10687
9787
|
validateComposition: require_sleep.method(zod.z.object({
|
|
10688
|
-
composition: CompositionSchema,
|
|
9788
|
+
composition: require_composition.CompositionSchema,
|
|
10689
9789
|
blockId: zod.z.string().optional()
|
|
10690
9790
|
}), CompositionValidationSchema, {
|
|
10691
9791
|
kind: "mutation",
|
|
@@ -10704,13 +9804,13 @@ var coreBlocksCapability = {
|
|
|
10704
9804
|
* and reads their state for the API. Nothing else talks to it.
|
|
10705
9805
|
*/
|
|
10706
9806
|
/** The composer's runner id, `resolveRunnerId(decl, 'composer')`; pinned by `composer-manifest.spec.ts`. */
|
|
10707
|
-
var COMPOSER_RUNNER_ID = COMPOSER_ADDON_ID;
|
|
9807
|
+
var COMPOSER_RUNNER_ID = require_composition.COMPOSER_ADDON_ID;
|
|
10708
9808
|
var ComposerBlockSchema = zod.z.object({
|
|
10709
9809
|
id: zod.z.string().min(1),
|
|
10710
9810
|
name: zod.z.string().min(1).max(120),
|
|
10711
9811
|
enabled: zod.z.boolean(),
|
|
10712
9812
|
updatedAt: zod.z.number(),
|
|
10713
|
-
composition: CompositionSchema
|
|
9813
|
+
composition: require_composition.CompositionSchema
|
|
10714
9814
|
});
|
|
10715
9815
|
/**
|
|
10716
9816
|
* A stored composition row that no longer PARSES — a schema a release
|
|
@@ -11660,7 +10760,7 @@ var detectionPipelineCapability = {
|
|
|
11660
10760
|
mode: "singleton",
|
|
11661
10761
|
kind: "wrapper",
|
|
11662
10762
|
defaultActive: true,
|
|
11663
|
-
deviceTypes: [
|
|
10763
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
11664
10764
|
exposesDeviceSettings: true,
|
|
11665
10765
|
methods: {}
|
|
11666
10766
|
};
|
|
@@ -12093,7 +11193,7 @@ var DiscoveredChildDeviceSchema = zod.z.lazy(() => zod.z.object({
|
|
|
12093
11193
|
/** Friendly name as reported by the source (Reolink camera name, ONVIF profile, ...). */
|
|
12094
11194
|
name: zod.z.string(),
|
|
12095
11195
|
/** DeviceType the child should be created with on adopt. */
|
|
12096
|
-
type: zod.z.enum(
|
|
11196
|
+
type: zod.z.enum(require_err_msg.DeviceType),
|
|
12097
11197
|
status: DiscoveredChildStatusSchema,
|
|
12098
11198
|
/** Free-form integration-specific metadata surfaced in the panel. */
|
|
12099
11199
|
metadata: zod.z.object({
|
|
@@ -12149,7 +11249,7 @@ var deviceDiscoveryCapability = {
|
|
|
12149
11249
|
scope: "device",
|
|
12150
11250
|
deviceNative: true,
|
|
12151
11251
|
mode: "singleton",
|
|
12152
|
-
deviceTypes: [
|
|
11252
|
+
deviceTypes: [require_err_msg.DeviceType.Hub],
|
|
12153
11253
|
status: {
|
|
12154
11254
|
schema: DeviceDiscoveryStatusSchema,
|
|
12155
11255
|
kind: "poll"
|
|
@@ -12751,7 +11851,7 @@ var DiscoveredDeviceSchema = zod.z.object({
|
|
|
12751
11851
|
*/
|
|
12752
11852
|
var DiscoveryCandidateSchema = zod.z.object({
|
|
12753
11853
|
stableId: zod.z.string(),
|
|
12754
|
-
type: zod.z.enum(
|
|
11854
|
+
type: zod.z.enum(require_err_msg.DeviceType),
|
|
12755
11855
|
suggestedName: zod.z.string(),
|
|
12756
11856
|
prefilledConfig: zod.z.record(zod.z.string(), zod.z.unknown()),
|
|
12757
11857
|
/**
|
|
@@ -12888,7 +11988,7 @@ var deviceProviderCapability = {
|
|
|
12888
11988
|
* provider does not support manual creation. Lets the Add-Device dialog
|
|
12889
11989
|
* pick the right type instead of assuming Camera.
|
|
12890
11990
|
*/
|
|
12891
|
-
getManualCreationType: require_sleep.method(zod.z.object({}), zod.z.object({ deviceType: zod.z.enum(
|
|
11991
|
+
getManualCreationType: require_sleep.method(zod.z.object({}), zod.z.object({ deviceType: zod.z.enum(require_err_msg.DeviceType).nullable() })),
|
|
12892
11992
|
adoptDiscoveredDevice: require_sleep.method(zod.z.object({ candidate: DiscoveryCandidateSchema }), DeviceSummarySchema, {
|
|
12893
11993
|
kind: "mutation",
|
|
12894
11994
|
auth: "admin"
|
|
@@ -12900,9 +12000,9 @@ var deviceProviderCapability = {
|
|
|
12900
12000
|
* devices of that type. The output is a `ConfigUISchema` — the
|
|
12901
12001
|
* router type-asserts it at the boundary.
|
|
12902
12002
|
*/
|
|
12903
|
-
getChildCreationSchema: require_sleep.method(zod.z.object({ type: zod.z.enum(
|
|
12003
|
+
getChildCreationSchema: require_sleep.method(zod.z.object({ type: zod.z.enum(require_err_msg.DeviceType) }), CreationSchemaOutputSchema),
|
|
12904
12004
|
createDevice: require_sleep.method(zod.z.object({
|
|
12905
|
-
type: zod.z.enum(
|
|
12005
|
+
type: zod.z.enum(require_err_msg.DeviceType),
|
|
12906
12006
|
config: zod.z.record(zod.z.string(), zod.z.unknown())
|
|
12907
12007
|
}), DeviceSummarySchema, {
|
|
12908
12008
|
kind: "mutation",
|
|
@@ -12921,7 +12021,7 @@ var deviceProviderCapability = {
|
|
|
12921
12021
|
* for backwards compatibility; providers free to ignore it.
|
|
12922
12022
|
*/
|
|
12923
12023
|
testCreationField: require_sleep.method(zod.z.object({
|
|
12924
|
-
type: zod.z.enum(
|
|
12024
|
+
type: zod.z.enum(require_err_msg.DeviceType),
|
|
12925
12025
|
key: zod.z.string(),
|
|
12926
12026
|
value: zod.z.unknown(),
|
|
12927
12027
|
formValues: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
|
|
@@ -13001,7 +12101,7 @@ var DeviceInfoSchema = zod.z.object({
|
|
|
13001
12101
|
id: zod.z.number(),
|
|
13002
12102
|
stableId: zod.z.string(),
|
|
13003
12103
|
addonId: zod.z.string(),
|
|
13004
|
-
type: zod.z.enum(
|
|
12104
|
+
type: zod.z.enum(require_err_msg.DeviceType),
|
|
13005
12105
|
name: zod.z.string(),
|
|
13006
12106
|
/** Operator-organisational location label. `null` when unset. */
|
|
13007
12107
|
location: zod.z.string().nullable(),
|
|
@@ -13094,7 +12194,7 @@ var LINKED_DEVICES_SLOT = {
|
|
|
13094
12194
|
label: "Linked devices",
|
|
13095
12195
|
description: "Companion devices of this camera: their controls appear with it, and the ones you opt in produce tracked events on it.",
|
|
13096
12196
|
shape: "association",
|
|
13097
|
-
hostDeviceTypes: [
|
|
12197
|
+
hostDeviceTypes: [require_err_msg.DeviceType.Camera],
|
|
13098
12198
|
sourceCaps: "any",
|
|
13099
12199
|
cardinality: "many",
|
|
13100
12200
|
value: LinkedDevicesSlotValueSchema
|
|
@@ -13336,7 +12436,7 @@ var deviceManagerCapability = {
|
|
|
13336
12436
|
* placeholder `'generic'`. */
|
|
13337
12437
|
setType: require_sleep.method(zod.z.object({
|
|
13338
12438
|
deviceId: zod.z.number(),
|
|
13339
|
-
type: zod.z.enum(
|
|
12439
|
+
type: zod.z.enum(require_err_msg.DeviceType)
|
|
13340
12440
|
}), zod.z.void(), {
|
|
13341
12441
|
kind: "mutation",
|
|
13342
12442
|
auth: "admin"
|
|
@@ -13464,7 +12564,7 @@ var deviceManagerCapability = {
|
|
|
13464
12564
|
deviceId: zod.z.number(),
|
|
13465
12565
|
name: zod.z.string().optional(),
|
|
13466
12566
|
location: zod.z.string().nullable().optional(),
|
|
13467
|
-
type: zod.z.enum(
|
|
12567
|
+
type: zod.z.enum(require_err_msg.DeviceType).optional(),
|
|
13468
12568
|
integrationId: zod.z.string().optional(),
|
|
13469
12569
|
linkDeviceId: zod.z.number().nullable().optional(),
|
|
13470
12570
|
role: zod.z.string().nullable().optional()
|
|
@@ -14015,12 +13115,12 @@ var deviceManagerCapability = {
|
|
|
14015
13115
|
*/
|
|
14016
13116
|
getCreationSchema: require_sleep.method(zod.z.object({
|
|
14017
13117
|
addonId: zod.z.string(),
|
|
14018
|
-
type: zod.z.enum(
|
|
13118
|
+
type: zod.z.enum(require_err_msg.DeviceType)
|
|
14019
13119
|
}), zod.z.unknown().nullable()),
|
|
14020
13120
|
/** Create a new device via the device-provider capability. */
|
|
14021
13121
|
createDevice: require_sleep.method(zod.z.object({
|
|
14022
13122
|
addonId: zod.z.string(),
|
|
14023
|
-
type: zod.z.enum(
|
|
13123
|
+
type: zod.z.enum(require_err_msg.DeviceType),
|
|
14024
13124
|
config: zod.z.record(zod.z.string(), zod.z.unknown()),
|
|
14025
13125
|
/** Owning integration id, stamped onto the new device's meta by the
|
|
14026
13126
|
* device-manager forwarder so `removeByIntegration` can cascade it.
|
|
@@ -14040,7 +13140,7 @@ var deviceManagerCapability = {
|
|
|
14040
13140
|
*/
|
|
14041
13141
|
testCreationField: require_sleep.method(zod.z.object({
|
|
14042
13142
|
addonId: zod.z.string(),
|
|
14043
|
-
type: zod.z.enum(
|
|
13143
|
+
type: zod.z.enum(require_err_msg.DeviceType),
|
|
14044
13144
|
key: zod.z.string(),
|
|
14045
13145
|
value: zod.z.unknown(),
|
|
14046
13146
|
formValues: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
|
|
@@ -14135,7 +13235,7 @@ var deviceManagerCapability = {
|
|
|
14135
13235
|
}),
|
|
14136
13236
|
/** The device type a provider creates via manual add (Camera/Container/Hub),
|
|
14137
13237
|
* so the Add-Device dialog uses the right type instead of assuming Camera. */
|
|
14138
|
-
providerCreationType: require_sleep.method(zod.z.object({ addonId: zod.z.string() }), zod.z.object({ deviceType: zod.z.enum(
|
|
13238
|
+
providerCreationType: require_sleep.method(zod.z.object({ addonId: zod.z.string() }), zod.z.object({ deviceType: zod.z.enum(require_err_msg.DeviceType).nullable() }), { auth: "admin" }),
|
|
14139
13239
|
/** A provider's extra per-scan param form (ConfigUISchema) for the
|
|
14140
13240
|
* per-integration scan, or null when it takes none. */
|
|
14141
13241
|
providerDiscoveryParamsSchema: require_sleep.method(zod.z.object({ addonId: zod.z.string() }), zod.z.unknown(), { auth: "admin" }),
|
|
@@ -14201,6 +13301,52 @@ var deviceManagerCapability = {
|
|
|
14201
13301
|
//#endregion
|
|
14202
13302
|
//#region src/capabilities/device-state.cap.ts
|
|
14203
13303
|
/**
|
|
13304
|
+
* Per-field ownership (D663). A composition may CLAIM fields of a native
|
|
13305
|
+
* cap's runtime-state slice on an existing device: `replace` overlays the
|
|
13306
|
+
* claimed fields on the native slice, `add` builds a slice the device does
|
|
13307
|
+
* not natively have. Declared here — once — so the hub mirror (Task 5) and
|
|
13308
|
+
* the claim methods (Task 6) share one union.
|
|
13309
|
+
*/
|
|
13310
|
+
var FieldClaimModeSchema = zod.z.enum(["replace", "add"]);
|
|
13311
|
+
/** What a claim, patch or release answers. A refusal always names its reason. */
|
|
13312
|
+
var ClaimOutcomeSchema = zod.z.discriminatedUnion("ok", [zod.z.object({
|
|
13313
|
+
ok: zod.z.literal(true),
|
|
13314
|
+
changed: zod.z.boolean()
|
|
13315
|
+
}), zod.z.object({
|
|
13316
|
+
ok: zod.z.literal(false),
|
|
13317
|
+
code: zod.z.enum([
|
|
13318
|
+
"owned-by-other",
|
|
13319
|
+
"not-claimed",
|
|
13320
|
+
"field-not-claimed",
|
|
13321
|
+
"invalid-slice",
|
|
13322
|
+
"unknown-device",
|
|
13323
|
+
"row-unreadable",
|
|
13324
|
+
"migration-in-flight"
|
|
13325
|
+
]),
|
|
13326
|
+
message: zod.z.string()
|
|
13327
|
+
})]);
|
|
13328
|
+
/** One claim as `listClaims` reports it. `fields` is the PLANNED set, explicit (ruling S5). */
|
|
13329
|
+
var FieldClaimSchema = zod.z.object({
|
|
13330
|
+
deviceId: zod.z.number(),
|
|
13331
|
+
capName: zod.z.string(),
|
|
13332
|
+
owner: zod.z.string().min(1),
|
|
13333
|
+
mode: FieldClaimModeSchema,
|
|
13334
|
+
fields: zod.z.array(zod.z.string().min(1)).min(1)
|
|
13335
|
+
});
|
|
13336
|
+
/**
|
|
13337
|
+
* What `listClaims` answers. The claims INDEX is a listing projection loaded
|
|
13338
|
+
* from the settings store at boot; until a load has succeeded it is
|
|
13339
|
+
* `not-loaded`, never `[]` — an unanswered question must not look like an
|
|
13340
|
+
* empty answer (D315).
|
|
13341
|
+
*/
|
|
13342
|
+
var ClaimsListingSchema = zod.z.discriminatedUnion("state", [zod.z.object({
|
|
13343
|
+
state: zod.z.literal("loaded"),
|
|
13344
|
+
claims: zod.z.array(FieldClaimSchema)
|
|
13345
|
+
}), zod.z.object({
|
|
13346
|
+
state: zod.z.literal("not-loaded"),
|
|
13347
|
+
reason: zod.z.string()
|
|
13348
|
+
})]);
|
|
13349
|
+
/**
|
|
14204
13350
|
* `deviceState` — generic, cap-agnostic surface for cross-process
|
|
14205
13351
|
* access to per-device runtime state. Read pendant for every cap's
|
|
14206
13352
|
* runtime-state slice + the canonical write entrypoint. Mirrors the
|
|
@@ -14231,8 +13377,12 @@ var deviceStateCapability = {
|
|
|
14231
13377
|
/** Full state snapshot for one device. Empty object if no
|
|
14232
13378
|
* cap-keyed slice has been written yet. */
|
|
14233
13379
|
getSnapshot: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), zod.z.record(zod.z.string(), zod.z.record(zod.z.string(), zod.z.unknown()))),
|
|
14234
|
-
/**
|
|
14235
|
-
*
|
|
13380
|
+
/**
|
|
13381
|
+
* Single cap slice — the EFFECTIVE one: the merge every consumer sees, null
|
|
13382
|
+
* when the slice hasn't been written, the device isn't registered, or a
|
|
13383
|
+
* claim HOLDS the cap (D663). Never the native shadow — that is
|
|
13384
|
+
* `getNativeCapSlice`, admin-only.
|
|
13385
|
+
*/
|
|
14236
13386
|
getCapSlice: require_sleep.method(zod.z.object({
|
|
14237
13387
|
deviceId: zod.z.number(),
|
|
14238
13388
|
capName: zod.z.string()
|
|
@@ -14277,7 +13427,66 @@ var deviceStateCapability = {
|
|
|
14277
13427
|
deviceId: zod.z.number(),
|
|
14278
13428
|
capName: zod.z.string(),
|
|
14279
13429
|
slice: zod.z.record(zod.z.string(), zod.z.unknown())
|
|
14280
|
-
}), zod.z.void(), { kind: "mutation" })
|
|
13430
|
+
}), zod.z.void(), { kind: "mutation" }),
|
|
13431
|
+
/**
|
|
13432
|
+
* Claim `fields` of `capName` on a device for `owner`. The PLANNED field set
|
|
13433
|
+
* is explicit (ruling S5): `values` may be partial and MUST be a subset of
|
|
13434
|
+
* `fields` (else `field-not-claimed`); a claimed field with no value is HELD,
|
|
13435
|
+
* never answered from the native. A re-claim by the same owner REPLACES
|
|
13436
|
+
* `fields` and MERGES `values` — a field dropped from `fields` loses its
|
|
13437
|
+
* value. Another owner's claim → `owned-by-other`.
|
|
13438
|
+
*/
|
|
13439
|
+
claimFields: require_sleep.method(zod.z.object({
|
|
13440
|
+
deviceId: zod.z.number(),
|
|
13441
|
+
capName: zod.z.string(),
|
|
13442
|
+
owner: zod.z.string().min(1),
|
|
13443
|
+
mode: FieldClaimModeSchema,
|
|
13444
|
+
fields: zod.z.array(zod.z.string().min(1)).min(1),
|
|
13445
|
+
values: zod.z.record(zod.z.string(), zod.z.unknown())
|
|
13446
|
+
}), ClaimOutcomeSchema, {
|
|
13447
|
+
kind: "mutation",
|
|
13448
|
+
auth: "admin"
|
|
13449
|
+
}),
|
|
13450
|
+
/**
|
|
13451
|
+
* Write owned values. Every key of `values` must be in the claim's `fields`
|
|
13452
|
+
* (`field-not-claimed`); no claim → `not-claimed`; another owner's →
|
|
13453
|
+
* `owned-by-other`. Partial patches merge (S5); debounced like a native slice.
|
|
13454
|
+
*/
|
|
13455
|
+
patchOwnedFields: require_sleep.method(zod.z.object({
|
|
13456
|
+
deviceId: zod.z.number(),
|
|
13457
|
+
capName: zod.z.string(),
|
|
13458
|
+
owner: zod.z.string().min(1),
|
|
13459
|
+
values: zod.z.record(zod.z.string(), zod.z.unknown())
|
|
13460
|
+
}), ClaimOutcomeSchema, {
|
|
13461
|
+
kind: "mutation",
|
|
13462
|
+
auth: "admin"
|
|
13463
|
+
}),
|
|
13464
|
+
/**
|
|
13465
|
+
* Hand the cap back. A `replace` re-emits the native slice exactly as last
|
|
13466
|
+
* written; an `add` leaves the mirror. `not-claimed` also removes a stale
|
|
13467
|
+
* index entry — the index self-heals through this method.
|
|
13468
|
+
*/
|
|
13469
|
+
releaseClaim: require_sleep.method(zod.z.object({
|
|
13470
|
+
deviceId: zod.z.number(),
|
|
13471
|
+
capName: zod.z.string(),
|
|
13472
|
+
owner: zod.z.string().min(1)
|
|
13473
|
+
}), ClaimOutcomeSchema, {
|
|
13474
|
+
kind: "mutation",
|
|
13475
|
+
auth: "admin"
|
|
13476
|
+
}),
|
|
13477
|
+
/** Every claim the index lists, optionally those whose owner starts with `ownerPrefix`. */
|
|
13478
|
+
listClaims: require_sleep.method(zod.z.object({ ownerPrefix: zod.z.string().optional() }), ClaimsListingSchema, { auth: "admin" }),
|
|
13479
|
+
/**
|
|
13480
|
+
* The NATIVE slice — what the provider wrote, the shadow under a `replace`
|
|
13481
|
+
* claim — for the owner's derived reads. Admin-only (D224, ruled in the
|
|
13482
|
+
* Task 6 review): the shadow is a second truth beside the merge, and the
|
|
13483
|
+
* event channel already refuses it to everyone but the composer; a
|
|
13484
|
+
* protected read of it would hand out what the events withhold.
|
|
13485
|
+
*/
|
|
13486
|
+
getNativeCapSlice: require_sleep.method(zod.z.object({
|
|
13487
|
+
deviceId: zod.z.number(),
|
|
13488
|
+
capName: zod.z.string()
|
|
13489
|
+
}), zod.z.record(zod.z.string(), zod.z.unknown()).nullable(), { auth: "admin" })
|
|
14281
13490
|
},
|
|
14282
13491
|
events: {
|
|
14283
13492
|
/**
|
|
@@ -16269,7 +15478,7 @@ var motionDetectionCapability = {
|
|
|
16269
15478
|
/** Frame-differencing motion analysis over a video stream — only a camera
|
|
16270
15479
|
* produces the frames. Read by the `defaultActive` auto-bind in
|
|
16271
15480
|
* `device-manager.getBindings` to decide which devices it may claim. */
|
|
16272
|
-
deviceTypes: [
|
|
15481
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
16273
15482
|
exposesDeviceSettings: true,
|
|
16274
15483
|
methods: {
|
|
16275
15484
|
analyze: require_sleep.method(zod.z.object({
|
|
@@ -17854,7 +17063,7 @@ var alarmPanelCapability = {
|
|
|
17854
17063
|
scope: "device",
|
|
17855
17064
|
deviceNative: true,
|
|
17856
17065
|
mode: "singleton",
|
|
17857
|
-
deviceTypes: [
|
|
17066
|
+
deviceTypes: [require_err_msg.DeviceType.AlarmPanel],
|
|
17858
17067
|
methods: {
|
|
17859
17068
|
arm: require_sleep.method(zod.z.object({
|
|
17860
17069
|
deviceId: zod.z.number().int().nonnegative(),
|
|
@@ -20814,7 +20023,7 @@ var DeviceSelectorSchema = zod.z.discriminatedUnion("kind", [
|
|
|
20814
20023
|
}),
|
|
20815
20024
|
zod.z.object({
|
|
20816
20025
|
kind: zod.z.literal("types"),
|
|
20817
|
-
types: zod.z.array(zod.z.enum(
|
|
20026
|
+
types: zod.z.array(zod.z.enum(require_err_msg.DeviceType)).min(1)
|
|
20818
20027
|
}),
|
|
20819
20028
|
zod.z.object({
|
|
20820
20029
|
kind: zod.z.literal("locations"),
|
|
@@ -23177,7 +22386,7 @@ var pipelineAnalyticsCapability = {
|
|
|
23177
22386
|
mode: "singleton",
|
|
23178
22387
|
kind: "wrapper",
|
|
23179
22388
|
defaultActive: true,
|
|
23180
|
-
deviceTypes: [
|
|
22389
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
23181
22390
|
exposesDeviceSettings: true,
|
|
23182
22391
|
methods: {
|
|
23183
22392
|
getActiveTracks: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), zod.z.array(TrackSchema).readonly()),
|
|
@@ -24860,7 +24069,7 @@ var zonesCapability = {
|
|
|
24860
24069
|
name: "zones",
|
|
24861
24070
|
scope: "device",
|
|
24862
24071
|
mode: "singleton",
|
|
24863
|
-
deviceTypes: [
|
|
24072
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
24864
24073
|
methods: {
|
|
24865
24074
|
listZones: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), zod.z.array(ZoneSchema).readonly()),
|
|
24866
24075
|
addZone: require_sleep.method(zod.z.object({
|
|
@@ -27842,7 +27051,7 @@ var snapshotCapability = {
|
|
|
27842
27051
|
mode: "singleton",
|
|
27843
27052
|
kind: "wrapper",
|
|
27844
27053
|
defaultActive: true,
|
|
27845
|
-
deviceTypes: [
|
|
27054
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
27846
27055
|
exposesDeviceSettings: true,
|
|
27847
27056
|
methods: {
|
|
27848
27057
|
getSnapshot: require_sleep.method(zod.z.object({
|
|
@@ -28698,7 +27907,7 @@ var streamSignalsCapability = {
|
|
|
28698
27907
|
scope: "device",
|
|
28699
27908
|
deviceNative: true,
|
|
28700
27909
|
mode: "singleton",
|
|
28701
|
-
deviceTypes: Object.values(
|
|
27910
|
+
deviceTypes: Object.values(require_err_msg.DeviceType),
|
|
28702
27911
|
runtimeState: StreamSignalsStatusSchema,
|
|
28703
27912
|
/**
|
|
28704
27913
|
* Runtime-state durability: **session** — mirrored in RAM, never written.
|
|
@@ -30197,7 +29406,7 @@ var videoclipsCapability = {
|
|
|
30197
29406
|
/** A clip is a window over a camera's footage — the cap is meaningless on a
|
|
30198
29407
|
* sensor, a button or an event emitter, and the `defaultActive` auto-bind
|
|
30199
29408
|
* reads this to decide which devices it may claim. */
|
|
30200
|
-
deviceTypes: [
|
|
29409
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
30201
29410
|
/**
|
|
30202
29411
|
* The Clips section of a camera's device details is FRAMEWORK-DERIVED (D14):
|
|
30203
29412
|
* the aggregator turns this declaration into the `type:'widget'` section and
|
|
@@ -30791,7 +30000,7 @@ var DOORBELL_SOURCE_SLOT = {
|
|
|
30791
30000
|
label: "Doorbell trigger devices",
|
|
30792
30001
|
description: "Any of these becoming active rings this camera's virtual doorbell: a binary sensor when it goes active, a switch when it turns on.",
|
|
30793
30002
|
shape: "association",
|
|
30794
|
-
hostDeviceTypes: [
|
|
30003
|
+
hostDeviceTypes: [require_err_msg.DeviceType.Camera],
|
|
30795
30004
|
sourceCaps: SOURCE_CAPS,
|
|
30796
30005
|
cardinality: "many",
|
|
30797
30006
|
value: DoorbellSourceSlotValueSchema
|
|
@@ -30803,7 +30012,7 @@ var doorbellCapability = {
|
|
|
30803
30012
|
mode: "singleton",
|
|
30804
30013
|
kind: "wrapper",
|
|
30805
30014
|
defaultActive: false,
|
|
30806
|
-
deviceTypes: [
|
|
30015
|
+
deviceTypes: [require_err_msg.DeviceType.Button, require_err_msg.DeviceType.Camera],
|
|
30807
30016
|
extensionSlots: [DOORBELL_SOURCE_SLOT],
|
|
30808
30017
|
methods: {},
|
|
30809
30018
|
events: {
|
|
@@ -30851,29 +30060,28 @@ var EXTENSION_SLOTS = {
|
|
|
30851
30060
|
"doorbell-source": {
|
|
30852
30061
|
descriptor: DOORBELL_SOURCE_SLOT,
|
|
30853
30062
|
declaringCap: "doorbell",
|
|
30854
|
-
hostDeviceTypes: [
|
|
30063
|
+
hostDeviceTypes: [require_err_msg.DeviceType.Camera]
|
|
30855
30064
|
},
|
|
30856
30065
|
"linked-devices": {
|
|
30857
30066
|
descriptor: LINKED_DEVICES_SLOT,
|
|
30858
30067
|
declaringCap: "device-manager",
|
|
30859
|
-
hostDeviceTypes: [
|
|
30068
|
+
hostDeviceTypes: [require_err_msg.DeviceType.Camera]
|
|
30860
30069
|
}
|
|
30861
30070
|
};
|
|
30862
30071
|
/** A slot's editable value, discriminated on `slot`. */
|
|
30863
30072
|
var ExtensionSlotValueSchema = zod.z.discriminatedUnion("slot", [DoorbellSourceSlotValueSchema.extend({ slot: zod.z.literal("doorbell-source") }), LinkedDevicesSlotValueSchema.extend({ slot: zod.z.literal("linked-devices") })]);
|
|
30864
30073
|
/** Every device type at least one slot is offered on — `device-extension`'s `deviceTypes`. */
|
|
30865
|
-
var EXTENSION_HOST_DEVICE_TYPES = [
|
|
30074
|
+
var EXTENSION_HOST_DEVICE_TYPES = [require_err_msg.DeviceType.Camera];
|
|
30866
30075
|
//#endregion
|
|
30867
30076
|
//#region src/capabilities/device-extension.cap.ts
|
|
30868
30077
|
/**
|
|
30869
30078
|
* `device-extension` — ONE place to see and author what a device is extended
|
|
30870
30079
|
* with by OTHER devices (D648).
|
|
30871
30080
|
*
|
|
30872
|
-
* **A contract, not a store.** Every slot (`linked-devices`,
|
|
30873
|
-
*
|
|
30874
|
-
*
|
|
30875
|
-
*
|
|
30876
|
-
* path. This cap persists nothing and has no config method (guarded by
|
|
30081
|
+
* **A contract, not a store.** Every slot (`linked-devices`, `doorbell-source`)
|
|
30082
|
+
* is declared by the cap that owns the function (`extensionSlots` in its
|
|
30083
|
+
* `*.cap.ts`, D4) and SERVED by the addon that already owns its store, over
|
|
30084
|
+
* that store and its existing write path. This cap persists nothing and has no config method (guarded by
|
|
30877
30085
|
* `scripts/check-extension-slots.ts`): D62 applied to the whole feature, and
|
|
30878
30086
|
* D3 — no shadow registry.
|
|
30879
30087
|
*
|
|
@@ -30894,6 +30102,11 @@ var EXTENSION_HOST_DEVICE_TYPES = [require_sleep.DeviceType.Camera];
|
|
|
30894
30102
|
* The slot's value travels as the generated discriminated union
|
|
30895
30103
|
* (`ExtensionSlotValue`, keyed on `slot`), so a value under the wrong slot is
|
|
30896
30104
|
* a schema error before it reaches any owner.
|
|
30105
|
+
*
|
|
30106
|
+
* **Associations only.** A slot LISTS other devices on its host. A host that
|
|
30107
|
+
* GAINS a capability from other devices is a composition (D663, amends D648:
|
|
30108
|
+
* the reserved `graft` shape, its `projects` and its `enabled` switch were
|
|
30109
|
+
* retired unimplemented).
|
|
30897
30110
|
*/
|
|
30898
30111
|
/** Separator between the source and the slot id in a `slotRef`. It is the
|
|
30899
30112
|
* D554 routing separator: the dispatcher matches `<source>:` prefixes. */
|
|
@@ -30916,7 +30129,7 @@ var ExtensionSourceSchema = zod.z.object({
|
|
|
30916
30129
|
/**
|
|
30917
30130
|
* How one picked source stands. `missing` — no such device (deleted, or an id
|
|
30918
30131
|
* that never existed); `foreign` — a device the slot's owner cannot read from
|
|
30919
|
-
* (
|
|
30132
|
+
* (a source owned by another addon); `unavailable` — it exists but is not a
|
|
30920
30133
|
* valid source now (a container, which is never a pick). A source in any of
|
|
30921
30134
|
* these states is SHOWN, never silently dropped or re-pointed.
|
|
30922
30135
|
*/
|
|
@@ -30957,10 +30170,6 @@ var ExtensionSlotStateSchema = zod.z.object({
|
|
|
30957
30170
|
slot: ExtensionSlotIdSchema,
|
|
30958
30171
|
/** Names the store written (D62), e.g. `virtual-doorbell:doorbellSources`. */
|
|
30959
30172
|
authority: zod.z.string(),
|
|
30960
|
-
/** graft: its switch. association: null — removing every source is "off". */
|
|
30961
|
-
enabled: zod.z.boolean().nullable(),
|
|
30962
|
-
/** graft: the cap registered on the host. association: null. */
|
|
30963
|
-
projects: zod.z.string().nullable(),
|
|
30964
30173
|
activation: ExtensionSlotActivationSchema.nullable(),
|
|
30965
30174
|
value: ExtensionSlotValueSchema,
|
|
30966
30175
|
sources: zod.z.array(ExtensionSlotSourceSchema),
|
|
@@ -31011,7 +30220,8 @@ var ExtensionCandidatesSchema = zod.z.object({ candidates: zod.z.array(Extension
|
|
|
31011
30220
|
/**
|
|
31012
30221
|
* What a write did. `none` — applied in place. `host` / `parent` — the owner
|
|
31013
30222
|
* reloaded the host (or, for an accessory child, its parent) because the
|
|
31014
|
-
* write changed what the host REGISTERS
|
|
30223
|
+
* write changed what the host REGISTERS. A slot never adds a capability to its
|
|
30224
|
+
* host — that is a composition (D663) — so today's owners answer `none`.
|
|
31015
30225
|
*/
|
|
31016
30226
|
var ExtensionSetResultSchema = zod.z.object({
|
|
31017
30227
|
applied: zod.z.literal(true),
|
|
@@ -31349,7 +30559,7 @@ var webrtcSessionCapability = {
|
|
|
31349
30559
|
kind: "wrapper",
|
|
31350
30560
|
defaultActive: true,
|
|
31351
30561
|
preferredProvider: "stream-broker",
|
|
31352
|
-
deviceTypes: [
|
|
30562
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
31353
30563
|
methods: {
|
|
31354
30564
|
/**
|
|
31355
30565
|
* List available WebRTC streams for a device.
|
|
@@ -31630,18 +30840,18 @@ var accessoriesCapability = {
|
|
|
31630
30840
|
deviceNative: true,
|
|
31631
30841
|
mode: "singleton",
|
|
31632
30842
|
deviceTypes: [
|
|
31633
|
-
|
|
31634
|
-
|
|
31635
|
-
|
|
31636
|
-
|
|
31637
|
-
|
|
31638
|
-
|
|
31639
|
-
|
|
31640
|
-
|
|
31641
|
-
|
|
31642
|
-
|
|
31643
|
-
|
|
31644
|
-
|
|
30843
|
+
require_err_msg.DeviceType.Camera,
|
|
30844
|
+
require_err_msg.DeviceType.Hub,
|
|
30845
|
+
require_err_msg.DeviceType.Switch,
|
|
30846
|
+
require_err_msg.DeviceType.Light,
|
|
30847
|
+
require_err_msg.DeviceType.Sensor,
|
|
30848
|
+
require_err_msg.DeviceType.Thermostat,
|
|
30849
|
+
require_err_msg.DeviceType.Cover,
|
|
30850
|
+
require_err_msg.DeviceType.Lock,
|
|
30851
|
+
require_err_msg.DeviceType.Fan,
|
|
30852
|
+
require_err_msg.DeviceType.MediaPlayer,
|
|
30853
|
+
require_err_msg.DeviceType.AlarmPanel,
|
|
30854
|
+
require_err_msg.DeviceType.Generic
|
|
31645
30855
|
],
|
|
31646
30856
|
methods: {
|
|
31647
30857
|
/**
|
|
@@ -32282,7 +31492,7 @@ var airQualitySensorCapability = {
|
|
|
32282
31492
|
scope: "device",
|
|
32283
31493
|
deviceNative: true,
|
|
32284
31494
|
mode: "singleton",
|
|
32285
|
-
deviceTypes: [
|
|
31495
|
+
deviceTypes: [require_err_msg.DeviceType.Sensor],
|
|
32286
31496
|
methods: {},
|
|
32287
31497
|
status: {
|
|
32288
31498
|
schema: AirQualitySensorStatusSchema,
|
|
@@ -32326,7 +31536,7 @@ var ambientLightSensorCapability = {
|
|
|
32326
31536
|
scope: "device",
|
|
32327
31537
|
deviceNative: true,
|
|
32328
31538
|
mode: "singleton",
|
|
32329
|
-
deviceTypes: [
|
|
31539
|
+
deviceTypes: [require_err_msg.DeviceType.Sensor],
|
|
32330
31540
|
methods: {},
|
|
32331
31541
|
status: {
|
|
32332
31542
|
schema: AmbientLightSensorStatusSchema,
|
|
@@ -32444,7 +31654,7 @@ var audioMetricsCapability = {
|
|
|
32444
31654
|
name: "audio-metrics",
|
|
32445
31655
|
scope: "device",
|
|
32446
31656
|
mode: "singleton",
|
|
32447
|
-
deviceTypes: [
|
|
31657
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
32448
31658
|
methods: {
|
|
32449
31659
|
/** Latest snapshot for this device. Null until the analytics
|
|
32450
31660
|
* pipeline has processed at least one audio window. */
|
|
@@ -32512,7 +31722,7 @@ var automationControlCapability = {
|
|
|
32512
31722
|
scope: "device",
|
|
32513
31723
|
deviceNative: true,
|
|
32514
31724
|
mode: "singleton",
|
|
32515
|
-
deviceTypes: [
|
|
31725
|
+
deviceTypes: [require_err_msg.DeviceType.Automation],
|
|
32516
31726
|
methods: {
|
|
32517
31727
|
enable: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.void(), {
|
|
32518
31728
|
kind: "mutation",
|
|
@@ -32638,10 +31848,12 @@ var batteryCapability = {
|
|
|
32638
31848
|
deviceNative: true,
|
|
32639
31849
|
mode: "singleton",
|
|
32640
31850
|
deviceTypes: [
|
|
32641
|
-
|
|
32642
|
-
|
|
32643
|
-
|
|
32644
|
-
|
|
31851
|
+
require_err_msg.DeviceType.Camera,
|
|
31852
|
+
require_err_msg.DeviceType.Sensor,
|
|
31853
|
+
require_err_msg.DeviceType.Button,
|
|
31854
|
+
require_err_msg.DeviceType.Switch,
|
|
31855
|
+
require_err_msg.DeviceType.PetFeeder,
|
|
31856
|
+
require_err_msg.DeviceType.Container
|
|
32645
31857
|
],
|
|
32646
31858
|
methods: {
|
|
32647
31859
|
/**
|
|
@@ -32668,6 +31880,19 @@ wakeForStream: require_sleep.method(zod.z.object({
|
|
|
32668
31880
|
awoke: zod.z.boolean(),
|
|
32669
31881
|
durationMs: zod.z.number()
|
|
32670
31882
|
}), { kind: "mutation" }) },
|
|
31883
|
+
/**
|
|
31884
|
+
* A composed provider (battery ADDED to a pet feeder or container that has
|
|
31885
|
+
* no wake surface) answers `wakeForStream` with the same "unavailable"
|
|
31886
|
+
* result the contract already defines above: "Returns `awoke: false` when
|
|
31887
|
+
* … the cap surface is unavailable." (D663).
|
|
31888
|
+
*/
|
|
31889
|
+
composedMethods: { wakeForStream: {
|
|
31890
|
+
kind: "answer",
|
|
31891
|
+
value: {
|
|
31892
|
+
awoke: false,
|
|
31893
|
+
durationMs: 0
|
|
31894
|
+
}
|
|
31895
|
+
} },
|
|
32671
31896
|
events: {
|
|
32672
31897
|
/**
|
|
32673
31898
|
* Emitted whenever the cached status changes (firmware push OR
|
|
@@ -32683,7 +31908,7 @@ onStatusChanged: { data: zod.z.object({
|
|
|
32683
31908
|
schema: BatteryStatusSchema,
|
|
32684
31909
|
kind: "push",
|
|
32685
31910
|
empty: {
|
|
32686
|
-
percentage:
|
|
31911
|
+
percentage: null,
|
|
32687
31912
|
charging: "unknown",
|
|
32688
31913
|
sleeping: false,
|
|
32689
31914
|
lastUpdated: 0
|
|
@@ -32733,7 +31958,7 @@ var binaryCapability = {
|
|
|
32733
31958
|
scope: "device",
|
|
32734
31959
|
deviceNative: true,
|
|
32735
31960
|
mode: "singleton",
|
|
32736
|
-
deviceTypes: [
|
|
31961
|
+
deviceTypes: [require_err_msg.DeviceType.Sensor],
|
|
32737
31962
|
methods: {},
|
|
32738
31963
|
status: {
|
|
32739
31964
|
schema: BinaryStatusSchema,
|
|
@@ -32776,7 +32001,7 @@ var brightnessCapability = {
|
|
|
32776
32001
|
scope: "device",
|
|
32777
32002
|
deviceNative: true,
|
|
32778
32003
|
mode: "singleton",
|
|
32779
|
-
deviceTypes: [
|
|
32004
|
+
deviceTypes: [require_err_msg.DeviceType.Light],
|
|
32780
32005
|
methods: { setBrightness: require_sleep.method(zod.z.object({
|
|
32781
32006
|
deviceId: zod.z.number().int().nonnegative(),
|
|
32782
32007
|
percentage: zod.z.number().min(0).max(100)
|
|
@@ -32830,7 +32055,7 @@ var buttonCapability = {
|
|
|
32830
32055
|
scope: "device",
|
|
32831
32056
|
deviceNative: true,
|
|
32832
32057
|
mode: "singleton",
|
|
32833
|
-
deviceTypes: [
|
|
32058
|
+
deviceTypes: [require_err_msg.DeviceType.Button],
|
|
32834
32059
|
methods: { press: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.void(), {
|
|
32835
32060
|
kind: "mutation",
|
|
32836
32061
|
auth: "protected"
|
|
@@ -32907,7 +32132,7 @@ var cameraCredentialsCapability = {
|
|
|
32907
32132
|
scope: "device",
|
|
32908
32133
|
deviceNative: true,
|
|
32909
32134
|
mode: "singleton",
|
|
32910
|
-
deviceTypes: [
|
|
32135
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
32911
32136
|
methods: {
|
|
32912
32137
|
/**
|
|
32913
32138
|
* Return the camera's full credential bundle for admin consumers.
|
|
@@ -33064,7 +32289,7 @@ var cameraGridLayoutCapability = {
|
|
|
33064
32289
|
scope: "device",
|
|
33065
32290
|
deviceNative: true,
|
|
33066
32291
|
mode: "singleton",
|
|
33067
|
-
deviceTypes: [
|
|
32292
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
33068
32293
|
/**
|
|
33069
32294
|
* The section is built by the ADDON and returned from
|
|
33070
32295
|
* `getDeviceSettingsContribution`, not derived by the framework from a
|
|
@@ -33115,7 +32340,7 @@ var carbonMonoxideCapability = {
|
|
|
33115
32340
|
scope: "device",
|
|
33116
32341
|
deviceNative: true,
|
|
33117
32342
|
mode: "singleton",
|
|
33118
|
-
deviceTypes: [
|
|
32343
|
+
deviceTypes: [require_err_msg.DeviceType.Sensor],
|
|
33119
32344
|
methods: {},
|
|
33120
32345
|
status: {
|
|
33121
32346
|
schema: CarbonMonoxideStatusSchema,
|
|
@@ -33224,7 +32449,7 @@ var climateControlCapability = {
|
|
|
33224
32449
|
scope: "device",
|
|
33225
32450
|
deviceNative: true,
|
|
33226
32451
|
mode: "singleton",
|
|
33227
|
-
deviceTypes: [
|
|
32452
|
+
deviceTypes: [require_err_msg.DeviceType.Thermostat, require_err_msg.DeviceType.Climate],
|
|
33228
32453
|
methods: {
|
|
33229
32454
|
setMode: require_sleep.method(zod.z.object({
|
|
33230
32455
|
deviceId: zod.z.number().int().nonnegative(),
|
|
@@ -33377,7 +32602,7 @@ var colorCapability = {
|
|
|
33377
32602
|
scope: "device",
|
|
33378
32603
|
deviceNative: true,
|
|
33379
32604
|
mode: "singleton",
|
|
33380
|
-
deviceTypes: [
|
|
32605
|
+
deviceTypes: [require_err_msg.DeviceType.Light],
|
|
33381
32606
|
methods: { setColor: require_sleep.method(zod.z.object({
|
|
33382
32607
|
deviceId: zod.z.number().int().nonnegative(),
|
|
33383
32608
|
color: ColorInputSchema
|
|
@@ -33537,7 +32762,7 @@ var connectivityCapability = {
|
|
|
33537
32762
|
scope: "device",
|
|
33538
32763
|
deviceNative: true,
|
|
33539
32764
|
mode: "singleton",
|
|
33540
|
-
deviceTypes: [
|
|
32765
|
+
deviceTypes: [require_err_msg.DeviceType.Sensor],
|
|
33541
32766
|
methods: {},
|
|
33542
32767
|
status: {
|
|
33543
32768
|
schema: ConnectivityStatusSchema,
|
|
@@ -33569,6 +32794,19 @@ var connectivityCapability = {
|
|
|
33569
32794
|
* provider populates it by guessing (no HA inference). The UI renders a
|
|
33570
32795
|
* "No consumables reported" placeholder when `items` is empty.
|
|
33571
32796
|
*/
|
|
32797
|
+
/** Units a remaining-life counter is reported in. Closed: the panel renders
|
|
32798
|
+
* each. */
|
|
32799
|
+
var ConsumableRemainingUnitSchema = zod.z.enum([
|
|
32800
|
+
"days",
|
|
32801
|
+
"hours",
|
|
32802
|
+
"cycles"
|
|
32803
|
+
]);
|
|
32804
|
+
/** Remaining life as a COUNT, not a percentage (the PetKit desiccant reports
|
|
32805
|
+
* days). `value` null = unknown (D393). */
|
|
32806
|
+
var ConsumableRemainingSchema = zod.z.object({
|
|
32807
|
+
value: zod.z.number().min(0).nullable(),
|
|
32808
|
+
unit: ConsumableRemainingUnitSchema
|
|
32809
|
+
});
|
|
33572
32810
|
/** A single consumable item. Either a continuous `level` (remaining
|
|
33573
32811
|
* life %) or a discrete `status` may be known — both may be null when a
|
|
33574
32812
|
* provider only knows the item exists. `level` and `status` are not
|
|
@@ -33585,7 +32823,10 @@ var ConsumableItemSchema = zod.z.object({
|
|
|
33585
32823
|
/** Ms epoch of the last replace, when known. */
|
|
33586
32824
|
lastResetAt: zod.z.number().nullable(),
|
|
33587
32825
|
/** Whether `reset()` is meaningful for this item. */
|
|
33588
|
-
resettable: zod.z.boolean()
|
|
32826
|
+
resettable: zod.z.boolean(),
|
|
32827
|
+
/** Remaining life as a count (days/hours/cycles), when a provider reports
|
|
32828
|
+
* it that way instead of — or alongside — `level`. Absent = not reported. */
|
|
32829
|
+
remaining: ConsumableRemainingSchema.optional()
|
|
33589
32830
|
});
|
|
33590
32831
|
var ConsumablesStatusSchema = zod.z.object({
|
|
33591
32832
|
items: zod.z.array(ConsumableItemSchema),
|
|
@@ -33596,7 +32837,7 @@ var consumablesCapability = {
|
|
|
33596
32837
|
scope: "device",
|
|
33597
32838
|
deviceNative: true,
|
|
33598
32839
|
mode: "singleton",
|
|
33599
|
-
deviceTypes: Object.values(
|
|
32840
|
+
deviceTypes: Object.values(require_err_msg.DeviceType),
|
|
33600
32841
|
deviceConfig: { ui: {
|
|
33601
32842
|
kind: "widget",
|
|
33602
32843
|
widgetId: "host/consumables-panel",
|
|
@@ -33615,6 +32856,16 @@ reset: require_sleep.method(zod.z.object({
|
|
|
33615
32856
|
kind: "mutation",
|
|
33616
32857
|
auth: "admin"
|
|
33617
32858
|
}) },
|
|
32859
|
+
/**
|
|
32860
|
+
* A composed consumable (item added to a device with no native consumables
|
|
32861
|
+
* provider) refuses `reset` by name: "Only meaningful when the item's
|
|
32862
|
+
* `resettable` is true" — a composed item is never resettable until
|
|
32863
|
+
* commands (`forward`) arrive in slice 3 (D663).
|
|
32864
|
+
*/
|
|
32865
|
+
composedMethods: { reset: {
|
|
32866
|
+
kind: "refuse",
|
|
32867
|
+
reason: "a composed consumable is not resettable until commands (forward) arrive — slice 3"
|
|
32868
|
+
} },
|
|
33618
32869
|
status: {
|
|
33619
32870
|
schema: ConsumablesStatusSchema,
|
|
33620
32871
|
kind: "push",
|
|
@@ -33679,7 +32930,7 @@ var contactCapability = {
|
|
|
33679
32930
|
scope: "device",
|
|
33680
32931
|
deviceNative: true,
|
|
33681
32932
|
mode: "singleton",
|
|
33682
|
-
deviceTypes: [
|
|
32933
|
+
deviceTypes: [require_err_msg.DeviceType.Sensor],
|
|
33683
32934
|
methods: {},
|
|
33684
32935
|
status: {
|
|
33685
32936
|
schema: ContactStatusSchema,
|
|
@@ -33810,7 +33061,7 @@ var controlCapability = {
|
|
|
33810
33061
|
scope: "device",
|
|
33811
33062
|
deviceNative: true,
|
|
33812
33063
|
mode: "singleton",
|
|
33813
|
-
deviceTypes: [
|
|
33064
|
+
deviceTypes: [require_err_msg.DeviceType.Control],
|
|
33814
33065
|
methods: { setValue: require_sleep.method(zod.z.object({
|
|
33815
33066
|
deviceId: zod.z.number().int().nonnegative(),
|
|
33816
33067
|
control: ControlSetValueInputSchema
|
|
@@ -33882,7 +33133,7 @@ var coverCapability = {
|
|
|
33882
33133
|
scope: "device",
|
|
33883
33134
|
deviceNative: true,
|
|
33884
33135
|
mode: "singleton",
|
|
33885
|
-
deviceTypes: [
|
|
33136
|
+
deviceTypes: [require_err_msg.DeviceType.Cover],
|
|
33886
33137
|
methods: {
|
|
33887
33138
|
open: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.void(), {
|
|
33888
33139
|
kind: "mutation",
|
|
@@ -34005,7 +33256,7 @@ var dayNightCapability = {
|
|
|
34005
33256
|
scope: "device",
|
|
34006
33257
|
deviceNative: true,
|
|
34007
33258
|
mode: "singleton",
|
|
34008
|
-
deviceTypes: [
|
|
33259
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
34009
33260
|
deviceConfig: { ui: {
|
|
34010
33261
|
kind: "derived-form",
|
|
34011
33262
|
builderId: "day-night",
|
|
@@ -34130,7 +33381,7 @@ var enumSensorCapability = {
|
|
|
34130
33381
|
scope: "device",
|
|
34131
33382
|
deviceNative: true,
|
|
34132
33383
|
mode: "singleton",
|
|
34133
|
-
deviceTypes: [
|
|
33384
|
+
deviceTypes: [require_err_msg.DeviceType.Sensor],
|
|
34134
33385
|
methods: {},
|
|
34135
33386
|
status: {
|
|
34136
33387
|
schema: EnumSensorStatusSchema,
|
|
@@ -34177,7 +33428,7 @@ var eventEmitterCapability = {
|
|
|
34177
33428
|
scope: "device",
|
|
34178
33429
|
deviceNative: true,
|
|
34179
33430
|
mode: "singleton",
|
|
34180
|
-
deviceTypes: [
|
|
33431
|
+
deviceTypes: [require_err_msg.DeviceType.EventEmitter],
|
|
34181
33432
|
methods: {},
|
|
34182
33433
|
events: { onEvent: { data: EventFireSchema } },
|
|
34183
33434
|
status: {
|
|
@@ -34645,7 +33896,7 @@ var fanControlCapability = {
|
|
|
34645
33896
|
scope: "device",
|
|
34646
33897
|
deviceNative: true,
|
|
34647
33898
|
mode: "singleton",
|
|
34648
|
-
deviceTypes: [
|
|
33899
|
+
deviceTypes: [require_err_msg.DeviceType.Fan],
|
|
34649
33900
|
methods: {
|
|
34650
33901
|
setPercentage: require_sleep.method(zod.z.object({
|
|
34651
33902
|
deviceId: zod.z.number().int().nonnegative(),
|
|
@@ -34799,7 +34050,7 @@ var floodCapability = {
|
|
|
34799
34050
|
scope: "device",
|
|
34800
34051
|
deviceNative: true,
|
|
34801
34052
|
mode: "singleton",
|
|
34802
|
-
deviceTypes: [
|
|
34053
|
+
deviceTypes: [require_err_msg.DeviceType.Sensor],
|
|
34803
34054
|
methods: {},
|
|
34804
34055
|
status: {
|
|
34805
34056
|
schema: FloodStatusSchema,
|
|
@@ -34834,7 +34085,7 @@ var gasCapability = {
|
|
|
34834
34085
|
scope: "device",
|
|
34835
34086
|
deviceNative: true,
|
|
34836
34087
|
mode: "singleton",
|
|
34837
|
-
deviceTypes: [
|
|
34088
|
+
deviceTypes: [require_err_msg.DeviceType.Sensor],
|
|
34838
34089
|
methods: {},
|
|
34839
34090
|
status: {
|
|
34840
34091
|
schema: GasStatusSchema,
|
|
@@ -34896,7 +34147,7 @@ var humidifierCapability = {
|
|
|
34896
34147
|
scope: "device",
|
|
34897
34148
|
deviceNative: true,
|
|
34898
34149
|
mode: "singleton",
|
|
34899
|
-
deviceTypes: [
|
|
34150
|
+
deviceTypes: [require_err_msg.DeviceType.Humidifier],
|
|
34900
34151
|
methods: {
|
|
34901
34152
|
setOn: require_sleep.method(zod.z.object({
|
|
34902
34153
|
deviceId: zod.z.number().int().nonnegative(),
|
|
@@ -34967,7 +34218,7 @@ var humiditySensorCapability = {
|
|
|
34967
34218
|
scope: "device",
|
|
34968
34219
|
deviceNative: true,
|
|
34969
34220
|
mode: "singleton",
|
|
34970
|
-
deviceTypes: [
|
|
34221
|
+
deviceTypes: [require_err_msg.DeviceType.Sensor, require_err_msg.DeviceType.Thermostat],
|
|
34971
34222
|
methods: {},
|
|
34972
34223
|
status: {
|
|
34973
34224
|
schema: HumiditySensorStatusSchema,
|
|
@@ -35013,7 +34264,7 @@ var imageCapability = {
|
|
|
35013
34264
|
scope: "device",
|
|
35014
34265
|
deviceNative: true,
|
|
35015
34266
|
mode: "singleton",
|
|
35016
|
-
deviceTypes: [
|
|
34267
|
+
deviceTypes: [require_err_msg.DeviceType.Image],
|
|
35017
34268
|
methods: {},
|
|
35018
34269
|
status: {
|
|
35019
34270
|
schema: ImageStatusSchema,
|
|
@@ -35161,7 +34412,7 @@ var imageSettingsCapability = {
|
|
|
35161
34412
|
scope: "device",
|
|
35162
34413
|
deviceNative: true,
|
|
35163
34414
|
mode: "singleton",
|
|
35164
|
-
deviceTypes: [
|
|
34415
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
35165
34416
|
deviceConfig: { ui: {
|
|
35166
34417
|
kind: "derived-form",
|
|
35167
34418
|
builderId: "image-settings",
|
|
@@ -35438,7 +34689,7 @@ var intercomCapability = {
|
|
|
35438
34689
|
scope: "device",
|
|
35439
34690
|
deviceNative: true,
|
|
35440
34691
|
mode: "singleton",
|
|
35441
|
-
deviceTypes: [
|
|
34692
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
35442
34693
|
/**
|
|
35443
34694
|
* **Auth tier: `protected` on every method — deliberate, and load-bearing.**
|
|
35444
34695
|
*
|
|
@@ -35644,7 +34895,7 @@ var lawnMowerControlCapability = {
|
|
|
35644
34895
|
scope: "device",
|
|
35645
34896
|
deviceNative: true,
|
|
35646
34897
|
mode: "singleton",
|
|
35647
|
-
deviceTypes: [
|
|
34898
|
+
deviceTypes: [require_err_msg.DeviceType.LawnMower],
|
|
35648
34899
|
methods: {
|
|
35649
34900
|
startMowing: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.void(), {
|
|
35650
34901
|
kind: "mutation",
|
|
@@ -36074,7 +35325,7 @@ var lockControlCapability = {
|
|
|
36074
35325
|
scope: "device",
|
|
36075
35326
|
deviceNative: true,
|
|
36076
35327
|
mode: "singleton",
|
|
36077
|
-
deviceTypes: [
|
|
35328
|
+
deviceTypes: [require_err_msg.DeviceType.Lock],
|
|
36078
35329
|
methods: {
|
|
36079
35330
|
lock: require_sleep.method(zod.z.object({
|
|
36080
35331
|
deviceId: zod.z.number().int().nonnegative(),
|
|
@@ -36195,7 +35446,7 @@ var mediaPlayerCapability = {
|
|
|
36195
35446
|
scope: "device",
|
|
36196
35447
|
deviceNative: true,
|
|
36197
35448
|
mode: "singleton",
|
|
36198
|
-
deviceTypes: [
|
|
35449
|
+
deviceTypes: [require_err_msg.DeviceType.MediaPlayer],
|
|
36199
35450
|
methods: {
|
|
36200
35451
|
play: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.void(), {
|
|
36201
35452
|
kind: "mutation",
|
|
@@ -36639,7 +35890,7 @@ var motionCapability = {
|
|
|
36639
35890
|
* `snapshot` and every other per-device native cap.
|
|
36640
35891
|
*/
|
|
36641
35892
|
deviceNative: true,
|
|
36642
|
-
deviceTypes: [
|
|
35893
|
+
deviceTypes: [require_err_msg.DeviceType.Camera, require_err_msg.DeviceType.Sensor],
|
|
36643
35894
|
methods: {
|
|
36644
35895
|
/**
|
|
36645
35896
|
* Pull the current motion state synchronously. Convenience shortcut
|
|
@@ -36732,9 +35983,9 @@ var motionTriggerCapability = {
|
|
|
36732
35983
|
deviceNative: true,
|
|
36733
35984
|
mode: "singleton",
|
|
36734
35985
|
deviceTypes: [
|
|
36735
|
-
|
|
36736
|
-
|
|
36737
|
-
|
|
35986
|
+
require_err_msg.DeviceType.Light,
|
|
35987
|
+
require_err_msg.DeviceType.Siren,
|
|
35988
|
+
require_err_msg.DeviceType.Switch
|
|
36738
35989
|
],
|
|
36739
35990
|
methods: { setMotionTrigger: require_sleep.method(zod.z.object({
|
|
36740
35991
|
deviceId: zod.z.number().int().nonnegative(),
|
|
@@ -36807,7 +36058,7 @@ var motionZonesCapability = {
|
|
|
36807
36058
|
scope: "device",
|
|
36808
36059
|
deviceNative: true,
|
|
36809
36060
|
mode: "singleton",
|
|
36810
|
-
deviceTypes: [
|
|
36061
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
36811
36062
|
deviceConfig: { ui: {
|
|
36812
36063
|
kind: "widget",
|
|
36813
36064
|
widgetId: "host/motion-zones-grid",
|
|
@@ -36933,7 +36184,7 @@ var nativeObjectDetectionCapability = {
|
|
|
36933
36184
|
scope: "device",
|
|
36934
36185
|
deviceNative: true,
|
|
36935
36186
|
mode: "singleton",
|
|
36936
|
-
deviceTypes: [
|
|
36187
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
36937
36188
|
methods: {
|
|
36938
36189
|
getOptions: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), NativeObjectDetectionOptionsSchema),
|
|
36939
36190
|
setSettings: require_sleep.method(zod.z.object({
|
|
@@ -37302,7 +36553,7 @@ var navigationCapability = {
|
|
|
37302
36553
|
scope: "device",
|
|
37303
36554
|
deviceNative: true,
|
|
37304
36555
|
mode: "singleton",
|
|
37305
|
-
deviceTypes: [
|
|
36556
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
37306
36557
|
deviceConfig: { ui: {
|
|
37307
36558
|
kind: "widget",
|
|
37308
36559
|
widgetId: "host/navigation-panel",
|
|
@@ -37472,13 +36723,13 @@ var networkLinkCapability = {
|
|
|
37472
36723
|
deviceNative: true,
|
|
37473
36724
|
mode: "singleton",
|
|
37474
36725
|
deviceTypes: [
|
|
37475
|
-
|
|
37476
|
-
|
|
37477
|
-
|
|
37478
|
-
|
|
37479
|
-
|
|
37480
|
-
|
|
37481
|
-
|
|
36726
|
+
require_err_msg.DeviceType.Camera,
|
|
36727
|
+
require_err_msg.DeviceType.Sensor,
|
|
36728
|
+
require_err_msg.DeviceType.Button,
|
|
36729
|
+
require_err_msg.DeviceType.Switch,
|
|
36730
|
+
require_err_msg.DeviceType.Light,
|
|
36731
|
+
require_err_msg.DeviceType.Lock,
|
|
36732
|
+
require_err_msg.DeviceType.Siren
|
|
37482
36733
|
],
|
|
37483
36734
|
methods: {},
|
|
37484
36735
|
events: {
|
|
@@ -37914,7 +37165,7 @@ var notifierCapability = {
|
|
|
37914
37165
|
scope: "device",
|
|
37915
37166
|
deviceNative: true,
|
|
37916
37167
|
mode: "singleton",
|
|
37917
|
-
deviceTypes: [
|
|
37168
|
+
deviceTypes: [require_err_msg.DeviceType.Notifier],
|
|
37918
37169
|
methods: {
|
|
37919
37170
|
send: require_sleep.method(NotifierSendInputSchema, NotifierSendResultSchema, {
|
|
37920
37171
|
kind: "mutation",
|
|
@@ -37996,7 +37247,7 @@ var numericSensorCapability = {
|
|
|
37996
37247
|
scope: "device",
|
|
37997
37248
|
deviceNative: true,
|
|
37998
37249
|
mode: "singleton",
|
|
37999
|
-
deviceTypes: [
|
|
37250
|
+
deviceTypes: [require_err_msg.DeviceType.Sensor],
|
|
38000
37251
|
methods: {},
|
|
38001
37252
|
status: {
|
|
38002
37253
|
schema: NumericSensorStatusSchema,
|
|
@@ -38065,7 +37316,7 @@ var osdCapability = {
|
|
|
38065
37316
|
scope: "device",
|
|
38066
37317
|
deviceNative: true,
|
|
38067
37318
|
mode: "singleton",
|
|
38068
|
-
deviceTypes: [
|
|
37319
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
38069
37320
|
methods: {
|
|
38070
37321
|
/**
|
|
38071
37322
|
* Patch a single overlay by id. Provider handles read-modify-write
|
|
@@ -38441,7 +37692,7 @@ var petFeederCapability = {
|
|
|
38441
37692
|
scope: "device",
|
|
38442
37693
|
deviceNative: true,
|
|
38443
37694
|
mode: "singleton",
|
|
38444
|
-
deviceTypes: [
|
|
37695
|
+
deviceTypes: [require_err_msg.DeviceType.PetFeeder],
|
|
38445
37696
|
methods: {
|
|
38446
37697
|
/**
|
|
38447
37698
|
* Dispense food now. Single-hopper feeders take `grams`; dual-hopper
|
|
@@ -38943,7 +38194,7 @@ var powerMeterCapability = {
|
|
|
38943
38194
|
scope: "device",
|
|
38944
38195
|
deviceNative: true,
|
|
38945
38196
|
mode: "singleton",
|
|
38946
|
-
deviceTypes: [
|
|
38197
|
+
deviceTypes: [require_err_msg.DeviceType.Sensor],
|
|
38947
38198
|
methods: {},
|
|
38948
38199
|
status: {
|
|
38949
38200
|
schema: PowerMeterStatusSchema,
|
|
@@ -39006,7 +38257,7 @@ var presenceCapability = {
|
|
|
39006
38257
|
scope: "device",
|
|
39007
38258
|
deviceNative: true,
|
|
39008
38259
|
mode: "singleton",
|
|
39009
|
-
deviceTypes: [
|
|
38260
|
+
deviceTypes: [require_err_msg.DeviceType.Presence],
|
|
39010
38261
|
methods: {},
|
|
39011
38262
|
status: {
|
|
39012
38263
|
schema: PresenceStatusSchema,
|
|
@@ -39060,7 +38311,7 @@ var pressureSensorCapability = {
|
|
|
39060
38311
|
scope: "device",
|
|
39061
38312
|
deviceNative: true,
|
|
39062
38313
|
mode: "singleton",
|
|
39063
|
-
deviceTypes: [
|
|
38314
|
+
deviceTypes: [require_err_msg.DeviceType.Sensor],
|
|
39064
38315
|
methods: {},
|
|
39065
38316
|
status: {
|
|
39066
38317
|
schema: PressureSensorStatusSchema,
|
|
@@ -39185,7 +38436,7 @@ var privacyMaskCapability = {
|
|
|
39185
38436
|
scope: "device",
|
|
39186
38437
|
deviceNative: true,
|
|
39187
38438
|
mode: "singleton",
|
|
39188
|
-
deviceTypes: [
|
|
38439
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
39189
38440
|
deviceConfig: { ui: {
|
|
39190
38441
|
kind: "widget",
|
|
39191
38442
|
widgetId: "host/privacy-mask-grid",
|
|
@@ -39449,7 +38700,7 @@ var ptzCapability = {
|
|
|
39449
38700
|
scope: "device",
|
|
39450
38701
|
deviceNative: true,
|
|
39451
38702
|
mode: "singleton",
|
|
39452
|
-
deviceTypes: [
|
|
38703
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
39453
38704
|
deviceConfig: { ui: {
|
|
39454
38705
|
kind: "widget",
|
|
39455
38706
|
widgetId: "host/ptz-panel",
|
|
@@ -39681,7 +38932,7 @@ var ptzAutotrackCapability = {
|
|
|
39681
38932
|
*/
|
|
39682
38933
|
kind: "wrapper",
|
|
39683
38934
|
defaultActive: false,
|
|
39684
|
-
deviceTypes: [
|
|
38935
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
39685
38936
|
deviceConfig: { ui: {
|
|
39686
38937
|
kind: "widget",
|
|
39687
38938
|
widgetId: "host/ptz-autotrack",
|
|
@@ -39772,9 +39023,9 @@ var rebootCapability = {
|
|
|
39772
39023
|
deviceNative: true,
|
|
39773
39024
|
mode: "singleton",
|
|
39774
39025
|
deviceTypes: [
|
|
39775
|
-
|
|
39776
|
-
|
|
39777
|
-
|
|
39026
|
+
require_err_msg.DeviceType.Camera,
|
|
39027
|
+
require_err_msg.DeviceType.Sensor,
|
|
39028
|
+
require_err_msg.DeviceType.Switch
|
|
39778
39029
|
],
|
|
39779
39030
|
methods: { reboot: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.object({ success: zod.z.literal(true) }), {
|
|
39780
39031
|
kind: "mutation",
|
|
@@ -40054,7 +39305,7 @@ var recordingCapability = {
|
|
|
40054
39305
|
/** Recorded coverage is a property of a camera — the cap is meaningless on a
|
|
40055
39306
|
* sensor, a button or an event emitter, and the `defaultActive` auto-bind
|
|
40056
39307
|
* reads this to decide which devices it may claim. */
|
|
40057
|
-
deviceTypes: [
|
|
39308
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
40058
39309
|
methods: {
|
|
40059
39310
|
/**
|
|
40060
39311
|
* The sources this camera has, WITH the reason any of them cannot answer.
|
|
@@ -41742,7 +40993,7 @@ var recordingOnboardCapability = {
|
|
|
41742
40993
|
scope: "device",
|
|
41743
40994
|
deviceNative: true,
|
|
41744
40995
|
mode: "singleton",
|
|
41745
|
-
deviceTypes: [
|
|
40996
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
41746
40997
|
deviceConfig: { ui: {
|
|
41747
40998
|
kind: "derived-form",
|
|
41748
40999
|
builderId: "recording-onboard",
|
|
@@ -42085,7 +41336,7 @@ var recordingSignalCapability = {
|
|
|
42085
41336
|
scope: "device",
|
|
42086
41337
|
deviceNative: true,
|
|
42087
41338
|
mode: "singleton",
|
|
42088
|
-
deviceTypes: [
|
|
41339
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
42089
41340
|
methods: {
|
|
42090
41341
|
/** The current level, straight from the slice the provider keeps fresh. */
|
|
42091
41342
|
getStatus: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), RecordingSignalStatusSchema) },
|
|
@@ -42404,7 +41655,7 @@ var sceneMonitorCapability = {
|
|
|
42404
41655
|
mode: "singleton",
|
|
42405
41656
|
kind: "wrapper",
|
|
42406
41657
|
defaultActive: true,
|
|
42407
|
-
deviceTypes: [
|
|
41658
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
42408
41659
|
methods: {
|
|
42409
41660
|
listScenes: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), SceneMonitorStatusSchema),
|
|
42410
41661
|
/**
|
|
@@ -42657,7 +41908,7 @@ var scriptRunnerCapability = {
|
|
|
42657
41908
|
scope: "device",
|
|
42658
41909
|
deviceNative: true,
|
|
42659
41910
|
mode: "singleton",
|
|
42660
|
-
deviceTypes: [
|
|
41911
|
+
deviceTypes: [require_err_msg.DeviceType.Script],
|
|
42661
41912
|
methods: {
|
|
42662
41913
|
run: require_sleep.method(zod.z.object({
|
|
42663
41914
|
deviceId: zod.z.number().int().nonnegative(),
|
|
@@ -42715,7 +41966,7 @@ var smokeCapability = {
|
|
|
42715
41966
|
scope: "device",
|
|
42716
41967
|
deviceNative: true,
|
|
42717
41968
|
mode: "singleton",
|
|
42718
|
-
deviceTypes: [
|
|
41969
|
+
deviceTypes: [require_err_msg.DeviceType.Sensor],
|
|
42719
41970
|
methods: {},
|
|
42720
41971
|
status: {
|
|
42721
41972
|
schema: SmokeStatusSchema,
|
|
@@ -42814,7 +42065,7 @@ var streamCatalogCapability = {
|
|
|
42814
42065
|
scope: "device",
|
|
42815
42066
|
deviceNative: true,
|
|
42816
42067
|
mode: "singleton",
|
|
42817
|
-
deviceTypes: [
|
|
42068
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
42818
42069
|
methods: { getCatalog: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.array(CamStreamDescriptorSchema).readonly()) },
|
|
42819
42070
|
runtimeState: StreamCatalogStateSchema,
|
|
42820
42071
|
/**
|
|
@@ -42932,7 +42183,7 @@ var streamParamsCapability = {
|
|
|
42932
42183
|
scope: "device",
|
|
42933
42184
|
deviceNative: true,
|
|
42934
42185
|
mode: "singleton",
|
|
42935
|
-
deviceTypes: [
|
|
42186
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
42936
42187
|
deviceConfig: { ui: {
|
|
42937
42188
|
kind: "derived-form",
|
|
42938
42189
|
builderId: "stream-params",
|
|
@@ -43264,9 +42515,9 @@ var switchCapability = {
|
|
|
43264
42515
|
deviceNative: true,
|
|
43265
42516
|
mode: "singleton",
|
|
43266
42517
|
deviceTypes: [
|
|
43267
|
-
|
|
43268
|
-
|
|
43269
|
-
|
|
42518
|
+
require_err_msg.DeviceType.Switch,
|
|
42519
|
+
require_err_msg.DeviceType.Siren,
|
|
42520
|
+
require_err_msg.DeviceType.Light
|
|
43270
42521
|
],
|
|
43271
42522
|
methods: { setState: require_sleep.method(zod.z.object({
|
|
43272
42523
|
deviceId: zod.z.number(),
|
|
@@ -43961,7 +43212,7 @@ var tamperCapability = {
|
|
|
43961
43212
|
scope: "device",
|
|
43962
43213
|
deviceNative: true,
|
|
43963
43214
|
mode: "singleton",
|
|
43964
|
-
deviceTypes: [
|
|
43215
|
+
deviceTypes: [require_err_msg.DeviceType.Sensor],
|
|
43965
43216
|
methods: {},
|
|
43966
43217
|
status: {
|
|
43967
43218
|
schema: TamperStatusSchema,
|
|
@@ -44018,7 +43269,7 @@ var temperatureSensorCapability = {
|
|
|
44018
43269
|
scope: "device",
|
|
44019
43270
|
deviceNative: true,
|
|
44020
43271
|
mode: "singleton",
|
|
44021
|
-
deviceTypes: [
|
|
43272
|
+
deviceTypes: [require_err_msg.DeviceType.Sensor, require_err_msg.DeviceType.Thermostat],
|
|
44022
43273
|
methods: {},
|
|
44023
43274
|
status: {
|
|
44024
43275
|
schema: TemperatureSensorStatusSchema,
|
|
@@ -44105,7 +43356,7 @@ var updateCapability = {
|
|
|
44105
43356
|
scope: "device",
|
|
44106
43357
|
deviceNative: true,
|
|
44107
43358
|
mode: "singleton",
|
|
44108
|
-
deviceTypes: [
|
|
43359
|
+
deviceTypes: [require_err_msg.DeviceType.Update],
|
|
44109
43360
|
methods: { installUpdate: require_sleep.method(zod.z.void(), zod.z.void(), {
|
|
44110
43361
|
kind: "mutation",
|
|
44111
43362
|
auth: "admin"
|
|
@@ -44463,7 +43714,7 @@ var vacuumControlCapability = {
|
|
|
44463
43714
|
scope: "device",
|
|
44464
43715
|
deviceNative: true,
|
|
44465
43716
|
mode: "singleton",
|
|
44466
|
-
deviceTypes: [
|
|
43717
|
+
deviceTypes: [require_err_msg.DeviceType.Vacuum],
|
|
44467
43718
|
methods: {
|
|
44468
43719
|
start: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.void(), {
|
|
44469
43720
|
kind: "mutation",
|
|
@@ -44550,7 +43801,7 @@ var valveCapability = {
|
|
|
44550
43801
|
scope: "device",
|
|
44551
43802
|
deviceNative: true,
|
|
44552
43803
|
mode: "singleton",
|
|
44553
|
-
deviceTypes: [
|
|
43804
|
+
deviceTypes: [require_err_msg.DeviceType.Valve],
|
|
44554
43805
|
methods: {
|
|
44555
43806
|
open: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.void(), {
|
|
44556
43807
|
kind: "mutation",
|
|
@@ -44605,7 +43856,7 @@ var vibrationCapability = {
|
|
|
44605
43856
|
scope: "device",
|
|
44606
43857
|
deviceNative: true,
|
|
44607
43858
|
mode: "singleton",
|
|
44608
|
-
deviceTypes: [
|
|
43859
|
+
deviceTypes: [require_err_msg.DeviceType.Sensor],
|
|
44609
43860
|
methods: {},
|
|
44610
43861
|
status: {
|
|
44611
43862
|
schema: VibrationStatusSchema,
|
|
@@ -44665,7 +43916,7 @@ var waterHeaterCapability = {
|
|
|
44665
43916
|
scope: "device",
|
|
44666
43917
|
deviceNative: true,
|
|
44667
43918
|
mode: "singleton",
|
|
44668
|
-
deviceTypes: [
|
|
43919
|
+
deviceTypes: [require_err_msg.DeviceType.WaterHeater],
|
|
44669
43920
|
methods: {
|
|
44670
43921
|
setTargetTemp: require_sleep.method(zod.z.object({
|
|
44671
43922
|
deviceId: zod.z.number().int().nonnegative(),
|
|
@@ -44755,7 +44006,7 @@ var weatherCapability = {
|
|
|
44755
44006
|
scope: "device",
|
|
44756
44007
|
deviceNative: true,
|
|
44757
44008
|
mode: "singleton",
|
|
44758
|
-
deviceTypes: [
|
|
44009
|
+
deviceTypes: [require_err_msg.DeviceType.Weather],
|
|
44759
44010
|
methods: {},
|
|
44760
44011
|
status: {
|
|
44761
44012
|
schema: WeatherStatusSchema,
|
|
@@ -44963,7 +44214,7 @@ var zoneAnalyticsCapability = {
|
|
|
44963
44214
|
name: "zone-analytics",
|
|
44964
44215
|
scope: "device",
|
|
44965
44216
|
mode: "singleton",
|
|
44966
|
-
deviceTypes: [
|
|
44217
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
44967
44218
|
methods: {
|
|
44968
44219
|
/** Latest computed occupancy snapshot for this camera. Null when
|
|
44969
44220
|
* the analytics pipeline hasn't seen a frame for this device yet
|
|
@@ -45055,7 +44306,7 @@ var zoneRulesCapability = {
|
|
|
45055
44306
|
name: "zone-rules",
|
|
45056
44307
|
scope: "device",
|
|
45057
44308
|
mode: "singleton",
|
|
45058
|
-
deviceTypes: [
|
|
44309
|
+
deviceTypes: [require_err_msg.DeviceType.Camera],
|
|
45059
44310
|
methods: {
|
|
45060
44311
|
/** Read the full rule list for a given stage (empty when no rules
|
|
45061
44312
|
* are defined yet). */
|
|
@@ -45191,8 +44442,6 @@ function createDeviceExtensionProvider(options) {
|
|
|
45191
44442
|
slotRef: extensionSlotRef(source, slot.slot),
|
|
45192
44443
|
slot: slot.slot,
|
|
45193
44444
|
authority: listing.authority,
|
|
45194
|
-
enabled: listing.enabled,
|
|
45195
|
-
projects: EXTENSION_SLOTS[slot.slot].descriptor.projects ?? null,
|
|
45196
44445
|
activation: listing.activation,
|
|
45197
44446
|
value: listing.value,
|
|
45198
44447
|
sources: [...listing.sources],
|
|
@@ -45227,44 +44476,68 @@ function createDeviceExtensionProvider(options) {
|
|
|
45227
44476
|
* through another composition, is refused at save by name (spec §2).
|
|
45228
44477
|
*
|
|
45229
44478
|
* The graph is built for ONE save: the CANDIDATE composition (the one being
|
|
45230
|
-
* validated) plus every STORED composition. The candidate
|
|
45231
|
-
*
|
|
45232
|
-
*
|
|
45233
|
-
*
|
|
45234
|
-
*
|
|
45235
|
-
*
|
|
44479
|
+
* validated) plus every STORED composition. The candidate replaces its own old
|
|
44480
|
+
* version, identified BY BLOCK ID wherever it sits in `stored` — whether a save
|
|
44481
|
+
* introduces or removes a cycle must never depend on array order. A DIFFERENT
|
|
44482
|
+
* block targeting the candidate's device is refused by name: one composition
|
|
44483
|
+
* per device (D663). Two different stored peers that (wrongly) target the same
|
|
44484
|
+
* device are corrupted state, not a cycle to resolve by picking one silently;
|
|
44485
|
+
* that is refused by name too (D391).
|
|
44486
|
+
*
|
|
44487
|
+
* A customization of an EXISTING device reads that device's NATIVE value for a
|
|
44488
|
+
* field it replaces or does not write ("Invert state" reads the `on` it
|
|
44489
|
+
* replaces): such a self-read is no edge, so it is never a cycle (D663).
|
|
44490
|
+
* Another composition reading that field reads the customizer's output.
|
|
45236
44491
|
*
|
|
45237
44492
|
* `findCompositionCycles` reports at least one cycle per cycle-SET (the DFS
|
|
45238
|
-
* marks a node `done` after its first expansion) —
|
|
45239
|
-
*
|
|
44493
|
+
* marks a node `done` after its first expansion) — not every elementary cycle
|
|
44494
|
+
* through a shared node, so the path it names may AVOID the candidate while
|
|
44495
|
+
* one through it goes unreported. What a save refuses is therefore decided per
|
|
44496
|
+
* strongly connected component (`findCandidateCycles`, C-1). A dotted
|
|
45240
44497
|
* `fieldPath` is matched only as an exact string: a write to `state.value` and
|
|
45241
44498
|
* a read of `state` would not connect (no nested field writes exist yet).
|
|
44499
|
+
*
|
|
44500
|
+
* An item leaf is its own node, `…#consumables.items[desiccant].status`
|
|
44501
|
+
* (`featureFieldEntries`), so two items sharing an inner path never merge. No
|
|
44502
|
+
* READ can name an array element (`describeStateField` never describes one),
|
|
44503
|
+
* so an item node is write-only: its edges to sources are what the check needs.
|
|
45242
44504
|
*/
|
|
45243
|
-
/** Two
|
|
44505
|
+
/** Two blocks' compositions target the same device — refused by name, never silently resolved. */
|
|
45244
44506
|
var CompositionGraphError = class extends Error {};
|
|
44507
|
+
/** `"cap.field"` of every field a peer writes — the same spelling as `compositionFieldKey`. */
|
|
44508
|
+
function writtenFields(peer) {
|
|
44509
|
+
return new Set(peer.composition.features.flatMap((feature) => feature.kind !== "fields" ? [] : require_composition.featureFieldEntries(feature).map(({ fieldPath }) => `${feature.cap}.${fieldPath}`)));
|
|
44510
|
+
}
|
|
44511
|
+
/** A read of the peer's OWN device is an edge only when it reads the peer's own output (rule 5, D663). */
|
|
44512
|
+
function readsComposedValue(peer, written, read) {
|
|
44513
|
+
if (require_composition.compositionSourceKey(read.source) !== require_composition.compositionSourceKey(peer.target)) return true;
|
|
44514
|
+
const field = `${read.cap}.${read.fieldPath}`;
|
|
44515
|
+
return written.has(field) && peer.replaced?.has(field) !== true;
|
|
44516
|
+
}
|
|
45245
44517
|
function peerEdges(peer) {
|
|
45246
|
-
|
|
44518
|
+
const written = writtenFields(peer);
|
|
44519
|
+
return peer.composition.features.flatMap((feature) => feature.kind !== "fields" ? [] : require_composition.featureFieldEntries(feature).map(({ fieldPath, source }) => [require_composition.compositionReadKey({
|
|
45247
44520
|
source: peer.target,
|
|
45248
44521
|
cap: feature.cap,
|
|
45249
|
-
fieldPath
|
|
45250
|
-
}), fieldReads(source).map(compositionReadKey)]));
|
|
44522
|
+
fieldPath
|
|
44523
|
+
}), require_composition.fieldReads(source).filter((read) => readsComposedValue(peer, written, read)).map(require_composition.compositionReadKey)]));
|
|
45251
44524
|
}
|
|
45252
44525
|
/**
|
|
45253
|
-
* Every peer that actually takes part in the graph: each stored peer minus
|
|
45254
|
-
*
|
|
45255
|
-
*
|
|
45256
|
-
*
|
|
45257
|
-
* candidate
|
|
45258
|
-
* DIFFERENT stored peers (neither of them the candidate's own old version)
|
|
45259
|
-
* target the same device.
|
|
44526
|
+
* Every peer that actually takes part in the graph: each stored peer minus the
|
|
44527
|
+
* candidate's own old version (same block id — the drop happens before any
|
|
44528
|
+
* edge is built, so its old edges never enter the map), plus the candidate
|
|
44529
|
+
* itself, exactly once. Throws {@link CompositionGraphError} when a DIFFERENT
|
|
44530
|
+
* block targets the candidate's device, or two stored peers share a device.
|
|
45260
44531
|
*/
|
|
45261
44532
|
function resolvedPeers(candidate, stored) {
|
|
45262
|
-
const candidateKey = compositionSourceKey(candidate.target);
|
|
44533
|
+
const candidateKey = require_composition.compositionSourceKey(candidate.target);
|
|
45263
44534
|
const survivors = /* @__PURE__ */ new Map();
|
|
45264
44535
|
for (const peer of stored) {
|
|
45265
|
-
|
|
45266
|
-
|
|
45267
|
-
if (
|
|
44536
|
+
if (peer.blockId === candidate.blockId) continue;
|
|
44537
|
+
const key = require_composition.compositionSourceKey(peer.target);
|
|
44538
|
+
if (key === candidateKey) throw new CompositionGraphError(`block \`${peer.blockId}\` and block \`${candidate.blockId}\` both target device \`${key}\` — one composition per device; delete or edit the existing one`);
|
|
44539
|
+
const clash = survivors.get(key);
|
|
44540
|
+
if (clash !== void 0) throw new CompositionGraphError(`two stored compositions (\`${clash.blockId}\`, \`${peer.blockId}\`) both target device \`${key}\` — refusing to build the field graph`);
|
|
45268
44541
|
survivors.set(key, peer);
|
|
45269
44542
|
}
|
|
45270
44543
|
return [...survivors.values(), candidate];
|
|
@@ -45293,172 +44566,215 @@ function findCompositionCycles(candidate, stored) {
|
|
|
45293
44566
|
for (const node of edges.keys()) visit(node, []);
|
|
45294
44567
|
return [...found.values()];
|
|
45295
44568
|
}
|
|
44569
|
+
/** Tarjan's strongly connected components over every node of the graph (sources included). */
|
|
44570
|
+
function stronglyConnected(edges) {
|
|
44571
|
+
const index = /* @__PURE__ */ new Map();
|
|
44572
|
+
const low = /* @__PURE__ */ new Map();
|
|
44573
|
+
const onStack = /* @__PURE__ */ new Set();
|
|
44574
|
+
const stack = [];
|
|
44575
|
+
const components = [];
|
|
44576
|
+
const connect = (node) => {
|
|
44577
|
+
index.set(node, index.size);
|
|
44578
|
+
low.set(node, index.get(node) ?? 0);
|
|
44579
|
+
stack.push(node);
|
|
44580
|
+
onStack.add(node);
|
|
44581
|
+
for (const next of edges.get(node) ?? []) if (!index.has(next)) {
|
|
44582
|
+
connect(next);
|
|
44583
|
+
low.set(node, Math.min(low.get(node) ?? 0, low.get(next) ?? 0));
|
|
44584
|
+
} else if (onStack.has(next)) low.set(node, Math.min(low.get(node) ?? 0, index.get(next) ?? 0));
|
|
44585
|
+
if (low.get(node) !== index.get(node)) return;
|
|
44586
|
+
const component = [];
|
|
44587
|
+
for (let member = stack.pop(); member !== void 0; member = stack.pop()) {
|
|
44588
|
+
onStack.delete(member);
|
|
44589
|
+
component.push(member);
|
|
44590
|
+
if (member === node) break;
|
|
44591
|
+
}
|
|
44592
|
+
components.push(component);
|
|
44593
|
+
};
|
|
44594
|
+
for (const node of edges.keys()) if (!index.has(node)) connect(node);
|
|
44595
|
+
return components;
|
|
44596
|
+
}
|
|
44597
|
+
/** The shortest walk from `start` back to itself inside `component` (one exists: the component has a cycle). */
|
|
44598
|
+
function cycleThrough(start, component, edges) {
|
|
44599
|
+
const parent = /* @__PURE__ */ new Map();
|
|
44600
|
+
const queue = [start];
|
|
44601
|
+
for (let head = 0; head < queue.length; head += 1) {
|
|
44602
|
+
const node = queue[head];
|
|
44603
|
+
if (node === void 0) break;
|
|
44604
|
+
for (const next of edges.get(node) ?? []) {
|
|
44605
|
+
if (next === start) {
|
|
44606
|
+
const chain = [];
|
|
44607
|
+
for (let at = node; at !== void 0 && at !== start; at = parent.get(at)) chain.push(at);
|
|
44608
|
+
return [
|
|
44609
|
+
start,
|
|
44610
|
+
...chain.reverse(),
|
|
44611
|
+
start
|
|
44612
|
+
];
|
|
44613
|
+
}
|
|
44614
|
+
if (!component.has(next) || parent.has(next)) continue;
|
|
44615
|
+
parent.set(next, node);
|
|
44616
|
+
queue.push(next);
|
|
44617
|
+
}
|
|
44618
|
+
}
|
|
44619
|
+
return [start, start];
|
|
44620
|
+
}
|
|
44621
|
+
/**
|
|
44622
|
+
* The cycles THIS save closes, decided per strongly connected component — never
|
|
44623
|
+
* per reported DFS path, which names one back-edge per revisit and can report a
|
|
44624
|
+
* component's cycle by a path that avoids the candidate while the one through it
|
|
44625
|
+
* goes unreported. A component refuses when it holds a candidate node and a
|
|
44626
|
+
* cycle (more than one node, or a self-loop). Components without a candidate
|
|
44627
|
+
* node are not this save's cycles.
|
|
44628
|
+
*/
|
|
44629
|
+
function findCandidateCycles(candidate, stored) {
|
|
44630
|
+
const edges = compositionEdges(candidate, stored);
|
|
44631
|
+
const prefix = `${require_composition.compositionSourceKey(candidate.target)}#`;
|
|
44632
|
+
return stronglyConnected(edges).flatMap((members) => {
|
|
44633
|
+
const start = members.find((n) => n.startsWith(prefix));
|
|
44634
|
+
if (start === void 0) return [];
|
|
44635
|
+
const component = new Set(members);
|
|
44636
|
+
const selfLoop = members.length === 1 && (edges.get(start) ?? []).includes(start);
|
|
44637
|
+
if (members.length === 1 && !selfLoop) return [];
|
|
44638
|
+
return [{
|
|
44639
|
+
path: cycleThrough(start, component, edges),
|
|
44640
|
+
component
|
|
44641
|
+
}];
|
|
44642
|
+
});
|
|
44643
|
+
}
|
|
45296
44644
|
//#endregion
|
|
45297
|
-
//#region src/
|
|
45298
|
-
|
|
45299
|
-
|
|
44645
|
+
//#region src/device/schema-fields.ts
|
|
44646
|
+
/** Unwrap ZodNullable / ZodOptional / ZodDefault wrappers to reach the inner type.
|
|
44647
|
+
* Zod v4 exposes `.unwrap()` on all three wrapper classes. */
|
|
44648
|
+
function unwrapSchema(schema) {
|
|
44649
|
+
let s = schema;
|
|
44650
|
+
while (s instanceof zod.z.ZodNullable || s instanceof zod.z.ZodOptional || s instanceof zod.z.ZodDefault) s = s.unwrap();
|
|
44651
|
+
return s;
|
|
45300
44652
|
}
|
|
45301
|
-
function
|
|
45302
|
-
|
|
45303
|
-
|
|
45304
|
-
|
|
45305
|
-
|
|
45306
|
-
fatal: reading.fatal
|
|
45307
|
-
};
|
|
45308
|
-
return {
|
|
45309
|
-
state: reading.state,
|
|
45310
|
-
value: reading.value,
|
|
45311
|
-
reason: reading.state === "stale" ? `${describeRead(read)}: ${reading.reason}` : null,
|
|
45312
|
-
deadline: null
|
|
44653
|
+
function leafKind(schema) {
|
|
44654
|
+
const s = unwrapSchema(schema);
|
|
44655
|
+
if (s instanceof zod.z.ZodEnum) return {
|
|
44656
|
+
kind: "enum",
|
|
44657
|
+
enumValues: s.options
|
|
45313
44658
|
};
|
|
44659
|
+
if (s instanceof zod.z.ZodNumber) return { kind: "number" };
|
|
44660
|
+
if (s instanceof zod.z.ZodString) return { kind: "string" };
|
|
44661
|
+
if (s instanceof zod.z.ZodBoolean) return { kind: "boolean" };
|
|
44662
|
+
return null;
|
|
45314
44663
|
}
|
|
45315
|
-
|
|
45316
|
-
|
|
45317
|
-
|
|
45318
|
-
|
|
45319
|
-
|
|
45320
|
-
|
|
45321
|
-
|
|
45322
|
-
|
|
45323
|
-
|
|
45324
|
-
|
|
45325
|
-
const
|
|
45326
|
-
|
|
45327
|
-
|
|
45328
|
-
|
|
45329
|
-
value: null,
|
|
45330
|
-
reason: `binding \`${name}\` (${describeRead(binding)}): ${reading.reason}`,
|
|
45331
|
-
fatal: reading.fatal
|
|
45332
|
-
};
|
|
45333
|
-
if (reading.fatal) {
|
|
45334
|
-
fatalUnavailable = outcome;
|
|
45335
|
-
break;
|
|
45336
|
-
}
|
|
45337
|
-
if (nonFatalUnavailable === null) nonFatalUnavailable = outcome;
|
|
44664
|
+
/** Walk a cap status schema into flat wireable leaf fields (dotted paths).
|
|
44665
|
+
* Recurses into nested ZodObject (unwrapping nullable/optional/default first),
|
|
44666
|
+
* so fields with null live values are still offered. Arrays / unknown shapes
|
|
44667
|
+
* are skipped — never throws. */
|
|
44668
|
+
function enumerateSchemaFields(schema, prefix = "") {
|
|
44669
|
+
const root = unwrapSchema(schema);
|
|
44670
|
+
if (!(root instanceof zod.z.ZodObject)) return [];
|
|
44671
|
+
const out = [];
|
|
44672
|
+
const shape = root.shape;
|
|
44673
|
+
for (const [key, field] of Object.entries(shape)) {
|
|
44674
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
44675
|
+
const inner = unwrapSchema(field);
|
|
44676
|
+
if (inner instanceof zod.z.ZodObject) {
|
|
44677
|
+
out.push(...enumerateSchemaFields(inner, path));
|
|
45338
44678
|
continue;
|
|
45339
44679
|
}
|
|
45340
|
-
|
|
45341
|
-
if (
|
|
44680
|
+
const leaf = leafKind(field);
|
|
44681
|
+
if (leaf) out.push({
|
|
44682
|
+
path,
|
|
44683
|
+
kind: leaf.kind,
|
|
44684
|
+
...leaf.enumValues ? { enumValues: leaf.enumValues } : {}
|
|
44685
|
+
});
|
|
45342
44686
|
}
|
|
45343
|
-
|
|
45344
|
-
if (nonFatalUnavailable !== null) return nonFatalUnavailable;
|
|
45345
|
-
const deadlines = [];
|
|
45346
|
-
const result = evaluateExpressionSource(source.expr, values, now, { hooks: { noteDeadline: (at) => deadlines.push(at) } });
|
|
45347
|
-
if (!result.ok) return {
|
|
45348
|
-
state: "unavailable",
|
|
45349
|
-
value: null,
|
|
45350
|
-
reason: `the expression failed: ${result.error}`,
|
|
45351
|
-
fatal: false
|
|
45352
|
-
};
|
|
45353
|
-
return {
|
|
45354
|
-
state: staleReasons.length > 0 ? "stale" : "fresh",
|
|
45355
|
-
value: result.value,
|
|
45356
|
-
reason: staleReasons.length > 0 ? staleReasons.join("; ") : null,
|
|
45357
|
-
deadline: deadlines.length > 0 ? Math.min(...deadlines) : null
|
|
45358
|
-
};
|
|
44687
|
+
return out;
|
|
45359
44688
|
}
|
|
45360
|
-
|
|
45361
|
-
|
|
45362
|
-
|
|
45363
|
-
|
|
45364
|
-
|
|
45365
|
-
|
|
45366
|
-
|
|
45367
|
-
|
|
45368
|
-
|
|
45369
|
-
|
|
45370
|
-
};
|
|
45371
|
-
}
|
|
44689
|
+
/** Enumerate the per-item wireable fields of an item-array cap (see
|
|
44690
|
+
* `CapabilityStatusItemArray`): the item schema's leaf fields, minus the
|
|
44691
|
+
* `keyField` (the key comes from the link's `itemKey`, never from a wired
|
|
44692
|
+
* source), each tagged `item: true` so the authoring UI collects an
|
|
44693
|
+
* `itemKey` alongside the field. Never throws. */
|
|
44694
|
+
function enumerateItemArrayFields(itemArray) {
|
|
44695
|
+
return enumerateSchemaFields(itemArray.itemSchema).filter((f) => f.path !== itemArray.keyField).map((f) => ({
|
|
44696
|
+
...f,
|
|
44697
|
+
item: true
|
|
44698
|
+
}));
|
|
45372
44699
|
}
|
|
45373
44700
|
//#endregion
|
|
45374
|
-
//#region src/composition/
|
|
44701
|
+
//#region src/composition/state-fields.ts
|
|
45375
44702
|
/**
|
|
45376
|
-
*
|
|
45377
|
-
*
|
|
45378
|
-
*
|
|
45379
|
-
* §4 item 4 asked for one).
|
|
44703
|
+
* What a capability's state field IS: kind, nullability, whether required. The
|
|
44704
|
+
* validator needs nullability, which `enumerateSchemaFields` does not report,
|
|
44705
|
+
* and that decides a field's unavailable mode (D393).
|
|
45380
44706
|
*/
|
|
45381
|
-
|
|
45382
|
-
|
|
45383
|
-
{
|
|
45384
|
-
|
|
45385
|
-
|
|
45386
|
-
|
|
45387
|
-
|
|
45388
|
-
|
|
45389
|
-
stableId: "sensor.sofa_temperature"
|
|
45390
|
-
},
|
|
45391
|
-
{
|
|
45392
|
-
addonId: "provider-homeassistant",
|
|
45393
|
-
stableId: "sensor.window_temperature"
|
|
44707
|
+
function fieldAt(schema, path) {
|
|
44708
|
+
let current = schema;
|
|
44709
|
+
for (const segment of path.split(".")) {
|
|
44710
|
+
const container = unwrapSchema(current);
|
|
44711
|
+
if (!(container instanceof zod.z.ZodObject)) return null;
|
|
44712
|
+
const next = container.shape[segment];
|
|
44713
|
+
if (!(next instanceof zod.z.ZodType)) return null;
|
|
44714
|
+
current = next;
|
|
45394
44715
|
}
|
|
45395
|
-
|
|
45396
|
-
|
|
45397
|
-
|
|
45398
|
-
|
|
45399
|
-
|
|
45400
|
-
|
|
45401
|
-
|
|
45402
|
-
}];
|
|
45403
|
-
function from(source, cap, fieldPath) {
|
|
44716
|
+
return current;
|
|
44717
|
+
}
|
|
44718
|
+
function describeStateField(schema, path) {
|
|
44719
|
+
const leaf = enumerateSchemaFields(schema).find((f) => f.path === path);
|
|
44720
|
+
if (leaf === void 0) return null;
|
|
44721
|
+
const field = fieldAt(schema, path);
|
|
44722
|
+
if (field === null) return null;
|
|
45404
44723
|
return {
|
|
45405
|
-
|
|
45406
|
-
|
|
45407
|
-
|
|
45408
|
-
|
|
44724
|
+
path,
|
|
44725
|
+
kind: leaf.kind,
|
|
44726
|
+
nullable: field.safeParse(null).success,
|
|
44727
|
+
required: !field.safeParse(void 0).success,
|
|
44728
|
+
...leaf.enumValues === void 0 ? {} : { enumValues: leaf.enumValues }
|
|
45409
44729
|
};
|
|
45410
44730
|
}
|
|
45411
|
-
function
|
|
45412
|
-
return
|
|
44731
|
+
function describeTopLevelStateFields(schema) {
|
|
44732
|
+
return new Map(enumerateSchemaFields(schema).filter((f) => !f.path.includes(".")).flatMap((f) => {
|
|
44733
|
+
const described = describeStateField(schema, f.path);
|
|
44734
|
+
return described === null ? [] : [[f.path, described]];
|
|
44735
|
+
}));
|
|
45413
44736
|
}
|
|
45414
|
-
|
|
45415
|
-
|
|
45416
|
-
const
|
|
45417
|
-
|
|
45418
|
-
|
|
45419
|
-
|
|
45420
|
-
|
|
45421
|
-
|
|
45422
|
-
|
|
45423
|
-
|
|
45424
|
-
|
|
45425
|
-
|
|
45426
|
-
|
|
45427
|
-
|
|
45428
|
-
|
|
45429
|
-
|
|
45430
|
-
|
|
45431
|
-
expr: `round(avg(${Object.keys(t).join(", ")}), 1)`,
|
|
45432
|
-
bindings: t
|
|
45433
|
-
},
|
|
45434
|
-
lastFetchedAt: {
|
|
45435
|
-
kind: "expression",
|
|
45436
|
-
expr: `max(${Object.keys(f).join(", ")})`,
|
|
45437
|
-
bindings: f
|
|
45438
|
-
}
|
|
45439
|
-
}
|
|
45440
|
-
}, {
|
|
45441
|
-
kind: "fields",
|
|
45442
|
-
cap: "binary",
|
|
45443
|
-
fields: {
|
|
45444
|
-
on: {
|
|
45445
|
-
kind: "expression",
|
|
45446
|
-
expr: `any(${[...Object.keys(d), ...windows].join(", ")})`,
|
|
45447
|
-
bindings: {
|
|
45448
|
-
...d,
|
|
45449
|
-
...m
|
|
45450
|
-
}
|
|
45451
|
-
},
|
|
45452
|
-
lastChangedAt: {
|
|
45453
|
-
kind: "expression",
|
|
45454
|
-
expr: "now",
|
|
45455
|
-
bindings: {}
|
|
45456
|
-
}
|
|
45457
|
-
}
|
|
45458
|
-
}]
|
|
44737
|
+
/** Top-level keys a value MUST carry: those whose schema refuses `undefined`. */
|
|
44738
|
+
function requiredTopLevelKeys(schema) {
|
|
44739
|
+
const root = unwrapSchema(schema);
|
|
44740
|
+
if (!(root instanceof zod.z.ZodObject)) return [];
|
|
44741
|
+
return Object.entries(root.shape).flatMap(([key, field]) => field instanceof zod.z.ZodType && !field.safeParse(void 0).success ? [key] : []);
|
|
44742
|
+
}
|
|
44743
|
+
/**
|
|
44744
|
+
* The proper prefixes of `path` whose schema accepts `undefined`: a required
|
|
44745
|
+
* leaf under one of them is required only once something writes that parent
|
|
44746
|
+
* (`remaining.unit` is required only when `remaining` is present).
|
|
44747
|
+
*/
|
|
44748
|
+
function optionalAncestors(schema, path) {
|
|
44749
|
+
const segments = path.split(".");
|
|
44750
|
+
return segments.slice(0, -1).flatMap((_, i) => {
|
|
44751
|
+
const prefix = segments.slice(0, i + 1).join(".");
|
|
44752
|
+
const field = fieldAt(schema, prefix);
|
|
44753
|
+
return field !== null && field.safeParse(void 0).success ? [prefix] : [];
|
|
45459
44754
|
});
|
|
45460
44755
|
}
|
|
45461
44756
|
//#endregion
|
|
44757
|
+
//#region src/composition/plan-result.ts
|
|
44758
|
+
function problem(code, path, message) {
|
|
44759
|
+
return {
|
|
44760
|
+
code,
|
|
44761
|
+
path,
|
|
44762
|
+
message
|
|
44763
|
+
};
|
|
44764
|
+
}
|
|
44765
|
+
function refused(...problems) {
|
|
44766
|
+
return {
|
|
44767
|
+
problems,
|
|
44768
|
+
fields: [],
|
|
44769
|
+
features: []
|
|
44770
|
+
};
|
|
44771
|
+
}
|
|
44772
|
+
/** Where a planned field sits in the composition: `features[N].fields.<path>` or `features[N].items.<key>.fields.<path>`. */
|
|
44773
|
+
function compositionFieldAt(field) {
|
|
44774
|
+
const inner = field.item === void 0 ? "fields" : `items.${field.item.key}.fields`;
|
|
44775
|
+
return `features[${field.featureIndex}].${inner}.${field.path}`;
|
|
44776
|
+
}
|
|
44777
|
+
//#endregion
|
|
45462
44778
|
//#region src/composition/expression-kinds.ts
|
|
45463
44779
|
var NUMBER = {
|
|
45464
44780
|
kind: "fixed",
|
|
@@ -45490,6 +44806,10 @@ var BUILTIN_RESULT_KINDS = Object.freeze({
|
|
|
45490
44806
|
latest: {
|
|
45491
44807
|
kind: "fixed",
|
|
45492
44808
|
result: ["number", "null"]
|
|
44809
|
+
},
|
|
44810
|
+
number: {
|
|
44811
|
+
kind: "fixed",
|
|
44812
|
+
result: ["number", "null"]
|
|
45493
44813
|
}
|
|
45494
44814
|
});
|
|
45495
44815
|
var KindInferenceError = class extends Error {};
|
|
@@ -45525,7 +44845,7 @@ function arityRange(builtin) {
|
|
|
45525
44845
|
return `${builtin.minArgs}–${builtin.maxArgs}`;
|
|
45526
44846
|
}
|
|
45527
44847
|
function callKinds(callee, args) {
|
|
45528
|
-
const builtin = EXPRESSION_BUILTINS[callee];
|
|
44848
|
+
const builtin = require_composition.EXPRESSION_BUILTINS[callee];
|
|
45529
44849
|
if (builtin !== void 0 && (args.length < builtin.minArgs || args.length > builtin.maxArgs)) throw new KindInferenceError(`${callee}: wrong number of arguments (${args.length}); expected ${arityRange(builtin)}`);
|
|
45530
44850
|
const rule = BUILTIN_RESULT_KINDS[callee];
|
|
45531
44851
|
if (rule === void 0) throw new KindInferenceError(`no result-kind rule for builtin '${callee}'`);
|
|
@@ -45550,7 +44870,7 @@ function kindsOf(node, bindings) {
|
|
|
45550
44870
|
}
|
|
45551
44871
|
}
|
|
45552
44872
|
function inferExpressionKinds(input) {
|
|
45553
|
-
const compiled = compileExpressionSafe(input.expr);
|
|
44873
|
+
const compiled = require_composition.compileExpressionSafe(input.expr);
|
|
45554
44874
|
if (!compiled.ok) return {
|
|
45555
44875
|
ok: false,
|
|
45556
44876
|
error: compiled.error
|
|
@@ -45569,129 +44889,14 @@ function inferExpressionKinds(input) {
|
|
|
45569
44889
|
}
|
|
45570
44890
|
}
|
|
45571
44891
|
//#endregion
|
|
45572
|
-
//#region src/
|
|
45573
|
-
/** Unwrap ZodNullable / ZodOptional / ZodDefault wrappers to reach the inner type.
|
|
45574
|
-
* Zod v4 exposes `.unwrap()` on all three wrapper classes. */
|
|
45575
|
-
function unwrapSchema(schema) {
|
|
45576
|
-
let s = schema;
|
|
45577
|
-
while (s instanceof zod.z.ZodNullable || s instanceof zod.z.ZodOptional || s instanceof zod.z.ZodDefault) s = s.unwrap();
|
|
45578
|
-
return s;
|
|
45579
|
-
}
|
|
45580
|
-
function leafKind(schema) {
|
|
45581
|
-
const s = unwrapSchema(schema);
|
|
45582
|
-
if (s instanceof zod.z.ZodEnum) return {
|
|
45583
|
-
kind: "enum",
|
|
45584
|
-
enumValues: s.options
|
|
45585
|
-
};
|
|
45586
|
-
if (s instanceof zod.z.ZodNumber) return { kind: "number" };
|
|
45587
|
-
if (s instanceof zod.z.ZodString) return { kind: "string" };
|
|
45588
|
-
if (s instanceof zod.z.ZodBoolean) return { kind: "boolean" };
|
|
45589
|
-
return null;
|
|
45590
|
-
}
|
|
45591
|
-
/** Walk a cap status schema into flat wireable leaf fields (dotted paths).
|
|
45592
|
-
* Recurses into nested ZodObject (unwrapping nullable/optional/default first),
|
|
45593
|
-
* so fields with null live values are still offered. Arrays / unknown shapes
|
|
45594
|
-
* are skipped — never throws. */
|
|
45595
|
-
function enumerateSchemaFields(schema, prefix = "") {
|
|
45596
|
-
const root = unwrapSchema(schema);
|
|
45597
|
-
if (!(root instanceof zod.z.ZodObject)) return [];
|
|
45598
|
-
const out = [];
|
|
45599
|
-
const shape = root.shape;
|
|
45600
|
-
for (const [key, field] of Object.entries(shape)) {
|
|
45601
|
-
const path = prefix ? `${prefix}.${key}` : key;
|
|
45602
|
-
const inner = unwrapSchema(field);
|
|
45603
|
-
if (inner instanceof zod.z.ZodObject) {
|
|
45604
|
-
out.push(...enumerateSchemaFields(inner, path));
|
|
45605
|
-
continue;
|
|
45606
|
-
}
|
|
45607
|
-
const leaf = leafKind(field);
|
|
45608
|
-
if (leaf) out.push({
|
|
45609
|
-
path,
|
|
45610
|
-
kind: leaf.kind,
|
|
45611
|
-
...leaf.enumValues ? { enumValues: leaf.enumValues } : {}
|
|
45612
|
-
});
|
|
45613
|
-
}
|
|
45614
|
-
return out;
|
|
45615
|
-
}
|
|
45616
|
-
/** Enumerate the per-item wireable fields of an item-array cap (see
|
|
45617
|
-
* `CapabilityStatusItemArray`): the item schema's leaf fields, minus the
|
|
45618
|
-
* `keyField` (the key comes from the link's `itemKey`, never from a wired
|
|
45619
|
-
* source), each tagged `item: true` so the authoring UI collects an
|
|
45620
|
-
* `itemKey` alongside the field. Never throws. */
|
|
45621
|
-
function enumerateItemArrayFields(itemArray) {
|
|
45622
|
-
return enumerateSchemaFields(itemArray.itemSchema).filter((f) => f.path !== itemArray.keyField).map((f) => ({
|
|
45623
|
-
...f,
|
|
45624
|
-
item: true
|
|
45625
|
-
}));
|
|
45626
|
-
}
|
|
45627
|
-
//#endregion
|
|
45628
|
-
//#region src/composition/state-fields.ts
|
|
44892
|
+
//#region src/composition/field-kinds.ts
|
|
45629
44893
|
/**
|
|
45630
|
-
*
|
|
45631
|
-
*
|
|
45632
|
-
*
|
|
44894
|
+
* The kind half of planning a composition field (D659): which kinds a source
|
|
44895
|
+
* can produce, whether they fit a target field, and the field's role.
|
|
44896
|
+
* Shared by the top-level planner (`validate-composition.ts`) and the item
|
|
44897
|
+
* planner (`composition-items.ts`). Internal to the composition module — NOT
|
|
44898
|
+
* in the barrel: these are planner internals, not a published contract.
|
|
45633
44899
|
*/
|
|
45634
|
-
function fieldAt(schema, path) {
|
|
45635
|
-
let current = schema;
|
|
45636
|
-
for (const segment of path.split(".")) {
|
|
45637
|
-
const container = unwrapSchema(current);
|
|
45638
|
-
if (!(container instanceof zod.z.ZodObject)) return null;
|
|
45639
|
-
const next = container.shape[segment];
|
|
45640
|
-
if (!(next instanceof zod.z.ZodType)) return null;
|
|
45641
|
-
current = next;
|
|
45642
|
-
}
|
|
45643
|
-
return current;
|
|
45644
|
-
}
|
|
45645
|
-
function describeStateField(schema, path) {
|
|
45646
|
-
const leaf = enumerateSchemaFields(schema).find((f) => f.path === path);
|
|
45647
|
-
if (leaf === void 0) return null;
|
|
45648
|
-
const field = fieldAt(schema, path);
|
|
45649
|
-
if (field === null) return null;
|
|
45650
|
-
return {
|
|
45651
|
-
path,
|
|
45652
|
-
kind: leaf.kind,
|
|
45653
|
-
nullable: field.safeParse(null).success,
|
|
45654
|
-
required: !field.safeParse(void 0).success,
|
|
45655
|
-
...leaf.enumValues === void 0 ? {} : { enumValues: leaf.enumValues }
|
|
45656
|
-
};
|
|
45657
|
-
}
|
|
45658
|
-
function describeTopLevelStateFields(schema) {
|
|
45659
|
-
return new Map(enumerateSchemaFields(schema).filter((f) => !f.path.includes(".")).flatMap((f) => {
|
|
45660
|
-
const described = describeStateField(schema, f.path);
|
|
45661
|
-
return described === null ? [] : [[f.path, described]];
|
|
45662
|
-
}));
|
|
45663
|
-
}
|
|
45664
|
-
/** Top-level keys a value MUST carry: those whose schema refuses `undefined`. */
|
|
45665
|
-
function requiredTopLevelKeys(schema) {
|
|
45666
|
-
const root = unwrapSchema(schema);
|
|
45667
|
-
if (!(root instanceof zod.z.ZodObject)) return [];
|
|
45668
|
-
return Object.entries(root.shape).flatMap(([key, field]) => field instanceof zod.z.ZodType && !field.safeParse(void 0).success ? [key] : []);
|
|
45669
|
-
}
|
|
45670
|
-
//#endregion
|
|
45671
|
-
//#region src/composition/validate-composition.ts
|
|
45672
|
-
function buildCapabilityLookup(defs) {
|
|
45673
|
-
const byName = new Map(defs.map((d) => [d.name, d]));
|
|
45674
|
-
return (capName) => byName.get(capName) ?? null;
|
|
45675
|
-
}
|
|
45676
|
-
/** Caps every `BaseDevice` registers on itself; a second registration throws. */
|
|
45677
|
-
var BASE_DEVICE_CAPS = new Set(["device-status", "feature-probe"]);
|
|
45678
|
-
var PENDING_SELF = {
|
|
45679
|
-
addonId: COMPOSER_ADDON_ID,
|
|
45680
|
-
stableId: `${COMPOSED_DEVICE_STABLE_ID_PREFIX}(unsaved)`
|
|
45681
|
-
};
|
|
45682
|
-
function problem(code, path, message) {
|
|
45683
|
-
return {
|
|
45684
|
-
code,
|
|
45685
|
-
path,
|
|
45686
|
-
message
|
|
45687
|
-
};
|
|
45688
|
-
}
|
|
45689
|
-
function refused(...problems) {
|
|
45690
|
-
return {
|
|
45691
|
-
problems,
|
|
45692
|
-
fields: []
|
|
45693
|
-
};
|
|
45694
|
-
}
|
|
45695
44900
|
function readKinds(read, lookupCap) {
|
|
45696
44901
|
const def = lookupCap(read.cap);
|
|
45697
44902
|
const schema = def?.runtimeState ?? def?.status?.schema;
|
|
@@ -45766,7 +44971,7 @@ function compatibility(cap, target, kinds, at) {
|
|
|
45766
44971
|
*/
|
|
45767
44972
|
function literalStringValue(source) {
|
|
45768
44973
|
if (source.kind !== "expression") return null;
|
|
45769
|
-
const compiled = compileExpressionSafe(source.expr);
|
|
44974
|
+
const compiled = require_composition.compileExpressionSafe(source.expr);
|
|
45770
44975
|
if (!compiled.ok) return null;
|
|
45771
44976
|
const ast = compiled.parsed.ast;
|
|
45772
44977
|
return ast.kind === "literal" && typeof ast.value === "string" ? ast.value : null;
|
|
@@ -45786,78 +44991,604 @@ function enumLiteralProblem(cap, target, source, at) {
|
|
|
45786
44991
|
}
|
|
45787
44992
|
function roleOf(source) {
|
|
45788
44993
|
if (source.kind !== "expression") return "derived";
|
|
45789
|
-
if (fieldReads(source).length > 0) return "derived";
|
|
45790
|
-
const compiled = compileExpressionSafe(source.expr);
|
|
44994
|
+
if (require_composition.fieldReads(source).length > 0) return "derived";
|
|
44995
|
+
const compiled = require_composition.compileExpressionSafe(source.expr);
|
|
45791
44996
|
return compiled.ok && compiled.parsed.identifiers.has("now") ? "clock" : "constant";
|
|
45792
44997
|
}
|
|
45793
|
-
|
|
45794
|
-
|
|
44998
|
+
/**
|
|
44999
|
+
* How a planned field says "unavailable", per feature mode and AFTER `roleOf`
|
|
45000
|
+
* (D393, D663 rulings S1 + S1b). The composer cannot take a device it does not
|
|
45001
|
+
* own offline, and D393 forbids a stale number, so on an existing target a
|
|
45002
|
+
* field that READS a source never gets `offline`.
|
|
45003
|
+
*/
|
|
45004
|
+
function unavailableFor(tf, role, mode, cap, at) {
|
|
45005
|
+
if (mode === "new" || role !== "derived") return {
|
|
45006
|
+
ok: true,
|
|
45007
|
+
unavailable: tf.nullable ? "null" : "offline"
|
|
45008
|
+
};
|
|
45009
|
+
if (mode === "replace") return {
|
|
45010
|
+
ok: true,
|
|
45011
|
+
unavailable: "release"
|
|
45012
|
+
};
|
|
45013
|
+
if (tf.nullable) return {
|
|
45014
|
+
ok: true,
|
|
45015
|
+
unavailable: "null"
|
|
45016
|
+
};
|
|
45017
|
+
if (tf.enumValues?.includes("unknown") === true) return {
|
|
45018
|
+
ok: true,
|
|
45019
|
+
unavailable: "unknown"
|
|
45020
|
+
};
|
|
45021
|
+
return {
|
|
45022
|
+
ok: false,
|
|
45023
|
+
problem: problem("not-replaceable", at, `\`${cap}.${tf.path}\` reads a source but can say neither null nor 'unknown', and an ADDED capability has no native reading to fall back to (D393, D663)`)
|
|
45024
|
+
};
|
|
45025
|
+
}
|
|
45026
|
+
//#endregion
|
|
45027
|
+
//#region src/composition/composition-items.ts
|
|
45028
|
+
/** Item fields the composer writes itself, with the value it writes. */
|
|
45029
|
+
var COMPOSED_ITEM_FIXED_FIELDS = { resettable: false };
|
|
45030
|
+
function reservedItemFields(itemArray) {
|
|
45031
|
+
return new Set([
|
|
45032
|
+
itemArray.keyField,
|
|
45033
|
+
...itemArray.labelField === void 0 ? [] : [itemArray.labelField],
|
|
45034
|
+
...Object.keys(COMPOSED_ITEM_FIXED_FIELDS)
|
|
45035
|
+
]);
|
|
45036
|
+
}
|
|
45037
|
+
/** A required leaf under an optional parent is required only once something writes that parent. */
|
|
45038
|
+
function leafRequired(itemArray, path, written) {
|
|
45039
|
+
const described = describeStateField(itemArray.itemSchema, path);
|
|
45040
|
+
if (described === null || !described.required) return false;
|
|
45041
|
+
return optionalAncestors(itemArray.itemSchema, path).every((parent) => written.some((w) => w.startsWith(`${parent}.`)));
|
|
45042
|
+
}
|
|
45043
|
+
function missingLeaves({ capName, itemArray, reserved }, item, at) {
|
|
45044
|
+
const written = Object.keys(item.fields);
|
|
45045
|
+
return enumerateSchemaFields(itemArray.itemSchema).flatMap((leaf) => {
|
|
45046
|
+
if (reserved.has(leaf.path) || leaf.path in item.fields) return [];
|
|
45047
|
+
if (!leafRequired(itemArray, leaf.path, written)) return [];
|
|
45048
|
+
const described = describeStateField(itemArray.itemSchema, leaf.path);
|
|
45049
|
+
if (described === null || described.nullable) return [];
|
|
45050
|
+
return [problem("missing-field", `${at}.fields.${leaf.path}`, `\`${capName}.${itemArray.path}[].${leaf.path}\` is required and has no source`)];
|
|
45051
|
+
});
|
|
45052
|
+
}
|
|
45053
|
+
function planItemField(ctx, leaf) {
|
|
45054
|
+
const { capName, itemArray, featureIndex, lookupCap } = ctx;
|
|
45055
|
+
const { key, label, path, source } = leaf;
|
|
45056
|
+
const at = `features[${featureIndex}].items.${key}.fields.${path}`;
|
|
45057
|
+
if (ctx.reserved.has(path)) return refused(problem("item-field-reserved", at, `\`${capName}.${itemArray.path}[].${path}\` is set by the composer itself — it takes no source`));
|
|
45058
|
+
const tf = describeStateField(itemArray.itemSchema, path);
|
|
45059
|
+
if (tf === null) return refused(problem("unknown-field", at, `a \`${capName}\` item has no field \`${path}\``));
|
|
45060
|
+
const kinds = sourceKinds(source, lookupCap);
|
|
45061
|
+
if (!kinds.ok) return refused(problem(kinds.code, at, kinds.message));
|
|
45062
|
+
const role = roleOf(source);
|
|
45063
|
+
const unavailable = unavailableFor(tf, role, ctx.mode, capName, at);
|
|
45064
|
+
if (!unavailable.ok) return refused(unavailable.problem);
|
|
45065
|
+
const plan = {
|
|
45066
|
+
cap: capName,
|
|
45067
|
+
path,
|
|
45068
|
+
item: {
|
|
45069
|
+
arrayPath: itemArray.path,
|
|
45070
|
+
key,
|
|
45071
|
+
label
|
|
45072
|
+
},
|
|
45073
|
+
featureIndex,
|
|
45074
|
+
kind: tf.kind,
|
|
45075
|
+
nullable: tf.nullable,
|
|
45076
|
+
required: tf.required,
|
|
45077
|
+
unavailable: unavailable.unavailable,
|
|
45078
|
+
role,
|
|
45079
|
+
source,
|
|
45080
|
+
reads: [...require_composition.fieldReads(source)]
|
|
45081
|
+
};
|
|
45082
|
+
const enumProblem = enumLiteralProblem(capName, tf, source, at);
|
|
45083
|
+
return {
|
|
45084
|
+
problems: [...compatibility(capName, tf, kinds.kinds, at), ...enumProblem === null ? [] : [enumProblem]],
|
|
45085
|
+
fields: [plan],
|
|
45086
|
+
features: []
|
|
45087
|
+
};
|
|
45088
|
+
}
|
|
45089
|
+
function planItems(def, feature, featureIndex, lookupCap, mode = "new") {
|
|
45090
|
+
const at = `features[${featureIndex}].items`;
|
|
45091
|
+
const itemArray = def.status?.itemArray;
|
|
45092
|
+
if (itemArray === void 0) return refused(problem("not-item-array", at, `\`${def.name}\` has no item array — it takes no \`items\``));
|
|
45093
|
+
if (itemArray.path !== "items") return refused(problem("not-item-array", at, `\`${def.name}\`'s item array is at \`${itemArray.path}\`, not \`${require_composition.COMPOSITION_ITEM_ARRAY_PATH}\` — not composable yet`));
|
|
45094
|
+
const ctx = {
|
|
45095
|
+
capName: def.name,
|
|
45096
|
+
itemArray,
|
|
45097
|
+
reserved: reservedItemFields(itemArray),
|
|
45098
|
+
featureIndex,
|
|
45099
|
+
lookupCap,
|
|
45100
|
+
mode
|
|
45101
|
+
};
|
|
45102
|
+
const results = Object.entries(feature.items ?? {}).flatMap(([key, item]) => [{
|
|
45103
|
+
problems: missingLeaves(ctx, item, `${at}.${key}`),
|
|
45104
|
+
fields: [],
|
|
45105
|
+
features: []
|
|
45106
|
+
}, ...Object.entries(item.fields).map(([path, source]) => planItemField(ctx, {
|
|
45107
|
+
key,
|
|
45108
|
+
label: item.label,
|
|
45109
|
+
path,
|
|
45110
|
+
source
|
|
45111
|
+
}))]);
|
|
45112
|
+
return {
|
|
45113
|
+
problems: results.flatMap((r) => r.problems),
|
|
45114
|
+
fields: results.flatMap((r) => r.fields),
|
|
45115
|
+
features: []
|
|
45116
|
+
};
|
|
45117
|
+
}
|
|
45118
|
+
//#endregion
|
|
45119
|
+
//#region src/composition/evaluate-field.ts
|
|
45120
|
+
function describeRead(read) {
|
|
45121
|
+
return `${require_composition.compositionSourceKey(read.source)} ${read.cap}.${read.fieldPath}`;
|
|
45122
|
+
}
|
|
45123
|
+
function isConfirmed(reading) {
|
|
45124
|
+
return reading.fatal || reading.confirmed === true;
|
|
45125
|
+
}
|
|
45126
|
+
function fromReading(reading, read) {
|
|
45127
|
+
if (reading.state === "unavailable") return {
|
|
45128
|
+
state: "unavailable",
|
|
45129
|
+
value: null,
|
|
45130
|
+
reason: `${describeRead(read)}: ${reading.reason}`,
|
|
45131
|
+
fatal: reading.fatal,
|
|
45132
|
+
confirmed: isConfirmed(reading)
|
|
45133
|
+
};
|
|
45134
|
+
return {
|
|
45135
|
+
state: reading.state,
|
|
45136
|
+
value: reading.value,
|
|
45137
|
+
reason: reading.state === "stale" ? `${describeRead(read)}: ${reading.reason}` : null,
|
|
45138
|
+
deadline: null
|
|
45139
|
+
};
|
|
45140
|
+
}
|
|
45141
|
+
function expressionOutcome(source, read, now) {
|
|
45142
|
+
const values = {};
|
|
45143
|
+
const staleReasons = [];
|
|
45144
|
+
let fatalUnavailable = null;
|
|
45145
|
+
let nonFatalUnavailable = null;
|
|
45146
|
+
for (const [name, binding] of Object.entries(source.bindings)) {
|
|
45147
|
+
if (binding.kind === "literal") {
|
|
45148
|
+
values[name] = binding.value;
|
|
45149
|
+
continue;
|
|
45150
|
+
}
|
|
45151
|
+
const reading = read(binding);
|
|
45152
|
+
if (reading.state === "unavailable") {
|
|
45153
|
+
const outcome = {
|
|
45154
|
+
state: "unavailable",
|
|
45155
|
+
value: null,
|
|
45156
|
+
reason: `binding \`${name}\` (${describeRead(binding)}): ${reading.reason}`,
|
|
45157
|
+
fatal: reading.fatal,
|
|
45158
|
+
confirmed: isConfirmed(reading)
|
|
45159
|
+
};
|
|
45160
|
+
if (reading.fatal) {
|
|
45161
|
+
fatalUnavailable = outcome;
|
|
45162
|
+
break;
|
|
45163
|
+
}
|
|
45164
|
+
if (nonFatalUnavailable === null || outcome.confirmed && !nonFatalUnavailable.confirmed) nonFatalUnavailable = outcome;
|
|
45165
|
+
continue;
|
|
45166
|
+
}
|
|
45167
|
+
values[name] = reading.value;
|
|
45168
|
+
if (reading.state === "stale") staleReasons.push(`\`${name}\`: ${reading.reason}`);
|
|
45169
|
+
}
|
|
45170
|
+
if (fatalUnavailable !== null) return fatalUnavailable;
|
|
45171
|
+
if (nonFatalUnavailable !== null) return nonFatalUnavailable;
|
|
45172
|
+
const deadlines = [];
|
|
45173
|
+
const result = require_composition.evaluateExpressionSource(source.expr, values, now, { hooks: { noteDeadline: (at) => deadlines.push(at) } });
|
|
45174
|
+
if (!result.ok) return {
|
|
45175
|
+
state: "unavailable",
|
|
45176
|
+
value: null,
|
|
45177
|
+
reason: `the expression failed: ${result.error}`,
|
|
45178
|
+
fatal: false,
|
|
45179
|
+
confirmed: true
|
|
45180
|
+
};
|
|
45181
|
+
return {
|
|
45182
|
+
state: staleReasons.length > 0 ? "stale" : "fresh",
|
|
45183
|
+
value: result.value,
|
|
45184
|
+
reason: staleReasons.length > 0 ? staleReasons.join("; ") : null,
|
|
45185
|
+
deadline: deadlines.length > 0 ? Math.min(...deadlines) : null
|
|
45186
|
+
};
|
|
45187
|
+
}
|
|
45188
|
+
function evaluateCompositionField(input) {
|
|
45189
|
+
const { source } = input;
|
|
45190
|
+
switch (source.kind) {
|
|
45191
|
+
case "from": return fromReading(input.read(source), source);
|
|
45192
|
+
case "expression": return expressionOutcome(source, input.read, input.now);
|
|
45193
|
+
case "code": return {
|
|
45194
|
+
state: "unavailable",
|
|
45195
|
+
value: null,
|
|
45196
|
+
reason: "code fields are not runnable in the composer — they arrive in slice 4",
|
|
45197
|
+
fatal: true,
|
|
45198
|
+
confirmed: true
|
|
45199
|
+
};
|
|
45200
|
+
}
|
|
45201
|
+
}
|
|
45202
|
+
//#endregion
|
|
45203
|
+
//#region src/composition/examples.ts
|
|
45204
|
+
/**
|
|
45205
|
+
* The worked composed-device example: a ROOM. Temperature is the average of N
|
|
45206
|
+
* sensors, and occupancy is any motion within the last five minutes. It is the
|
|
45207
|
+
* slice-1 acceptance case and the template an author starts from (Phase 4
|
|
45208
|
+
* §4 item 4 asked for one).
|
|
45209
|
+
*/
|
|
45210
|
+
var ROOM_OCCUPANCY_WINDOW_MS = 3e5;
|
|
45211
|
+
var ROOM_EXAMPLE_TEMPERATURE_SOURCES = [
|
|
45212
|
+
{
|
|
45213
|
+
addonId: "provider-homeassistant",
|
|
45214
|
+
stableId: "sensor.living_room_temperature"
|
|
45215
|
+
},
|
|
45216
|
+
{
|
|
45217
|
+
addonId: "provider-homeassistant",
|
|
45218
|
+
stableId: "sensor.sofa_temperature"
|
|
45219
|
+
},
|
|
45220
|
+
{
|
|
45221
|
+
addonId: "provider-homeassistant",
|
|
45222
|
+
stableId: "sensor.window_temperature"
|
|
45223
|
+
}
|
|
45224
|
+
];
|
|
45225
|
+
var ROOM_EXAMPLE_MOTION_SOURCES = [{
|
|
45226
|
+
addonId: "provider-homeassistant",
|
|
45227
|
+
stableId: "binary_sensor.living_room_motion"
|
|
45228
|
+
}, {
|
|
45229
|
+
addonId: "provider-homeassistant",
|
|
45230
|
+
stableId: "binary_sensor.living_room_presence"
|
|
45231
|
+
}];
|
|
45232
|
+
function from(source, cap, fieldPath) {
|
|
45233
|
+
return {
|
|
45234
|
+
kind: "from",
|
|
45235
|
+
source,
|
|
45236
|
+
cap,
|
|
45237
|
+
fieldPath
|
|
45238
|
+
};
|
|
45239
|
+
}
|
|
45240
|
+
function named(prefix, sources, cap, fieldPath) {
|
|
45241
|
+
return Object.fromEntries(sources.map((s, i) => [`${prefix}${i + 1}`, from(s, cap, fieldPath)]));
|
|
45242
|
+
}
|
|
45243
|
+
function roomCompositionExample(input) {
|
|
45244
|
+
const t = named("t", input.temperatureSources, "temperature-sensor", "celsius");
|
|
45245
|
+
const f = named("f", input.temperatureSources, "temperature-sensor", "lastFetchedAt");
|
|
45246
|
+
const d = named("d", input.motionSources, "motion", "detected");
|
|
45247
|
+
const m = named("m", input.motionSources, "motion", "lastDetectedAt");
|
|
45248
|
+
const windows = Object.keys(m).map((name) => `within(${name}, ${input.occupancyWindowMs})`);
|
|
45249
|
+
return require_composition.CompositionSchema.parse({
|
|
45250
|
+
target: {
|
|
45251
|
+
kind: "new",
|
|
45252
|
+
type: require_err_msg.DeviceType.Sensor
|
|
45253
|
+
},
|
|
45254
|
+
features: [{
|
|
45255
|
+
kind: "fields",
|
|
45256
|
+
cap: "temperature-sensor",
|
|
45257
|
+
fields: {
|
|
45258
|
+
celsius: {
|
|
45259
|
+
kind: "expression",
|
|
45260
|
+
expr: `round(avg(${Object.keys(t).join(", ")}), 1)`,
|
|
45261
|
+
bindings: t
|
|
45262
|
+
},
|
|
45263
|
+
lastFetchedAt: {
|
|
45264
|
+
kind: "expression",
|
|
45265
|
+
expr: `max(${Object.keys(f).join(", ")})`,
|
|
45266
|
+
bindings: f
|
|
45267
|
+
}
|
|
45268
|
+
}
|
|
45269
|
+
}, {
|
|
45270
|
+
kind: "fields",
|
|
45271
|
+
cap: "binary",
|
|
45272
|
+
fields: {
|
|
45273
|
+
on: {
|
|
45274
|
+
kind: "expression",
|
|
45275
|
+
expr: `any(${[...Object.keys(d), ...windows].join(", ")})`,
|
|
45276
|
+
bindings: {
|
|
45277
|
+
...d,
|
|
45278
|
+
...m
|
|
45279
|
+
}
|
|
45280
|
+
},
|
|
45281
|
+
lastChangedAt: {
|
|
45282
|
+
kind: "expression",
|
|
45283
|
+
expr: "now",
|
|
45284
|
+
bindings: {}
|
|
45285
|
+
}
|
|
45286
|
+
}
|
|
45287
|
+
}]
|
|
45288
|
+
});
|
|
45289
|
+
}
|
|
45290
|
+
/**
|
|
45291
|
+
* The slice-2 worked customization (D663): the PetKit "Fresh Element Infinity"
|
|
45292
|
+
* feeder as Home Assistant imports it — ONE `Container` with a child per entity
|
|
45293
|
+
* (`<container stableId>-<entityId>`), and NO native battery (its
|
|
45294
|
+
* `binary_sensor.*_battery` is `battery_charging`, which `isBatteryEntity` does
|
|
45295
|
+
* not match). The customization ADDS `consumables` (the desiccant) and
|
|
45296
|
+
* `battery`. It is the Task 14 acceptance case and the template slice 5's
|
|
45297
|
+
* presets start from.
|
|
45298
|
+
*
|
|
45299
|
+
* Every read names what the HA provider REALLY registers (read 2026-09-28):
|
|
45300
|
+
* the desiccant sensor has a unit (`d`) but no device_class, so HA hosts it as
|
|
45301
|
+
* an `enum-sensor` whose `value` is the raw STRING state — `number(d)` reads it,
|
|
45302
|
+
* and anything that is not a number is `null`, never 0 (D393). The battery
|
|
45303
|
+
* status sensor is an `enum-sensor` too (`unavailable` keeps its cold `''`, so
|
|
45304
|
+
* `percentage` is `null`). `charging` maps `battery_charging` off to
|
|
45305
|
+
* `'unknown'`, never `'none'`: off does not prove "on battery alone" (the
|
|
45306
|
+
* operator confirms the real semantics live).
|
|
45307
|
+
*/
|
|
45308
|
+
var FEEDER_EXAMPLE_HA_DEVICE_ID = "8dec3b512fda40cbb8cd121c7a99f890";
|
|
45309
|
+
var FEEDER_EXAMPLE_ADDON_ID = "provider-homeassistant";
|
|
45310
|
+
/** The HA container's stableId (`ha-discovery.ts`) and a child's (`<container>-<entityId>`). */
|
|
45311
|
+
function feederExampleSources(brokerId) {
|
|
45312
|
+
const feeder = {
|
|
45313
|
+
addonId: FEEDER_EXAMPLE_ADDON_ID,
|
|
45314
|
+
stableId: `ha:${brokerId}:dev:${FEEDER_EXAMPLE_HA_DEVICE_ID}`
|
|
45315
|
+
};
|
|
45316
|
+
const child = (entityId) => ({
|
|
45317
|
+
addonId: FEEDER_EXAMPLE_ADDON_ID,
|
|
45318
|
+
stableId: `${feeder.stableId}-${entityId}`
|
|
45319
|
+
});
|
|
45320
|
+
return {
|
|
45321
|
+
feeder,
|
|
45322
|
+
desiccantDays: child("sensor.freshelement_3_desiccant_days_remaining"),
|
|
45323
|
+
batteryStatus: child("sensor.freshelement_3_battery_status"),
|
|
45324
|
+
batteryCharging: child("binary_sensor.freshelement_3_battery")
|
|
45325
|
+
};
|
|
45326
|
+
}
|
|
45327
|
+
function feederCustomizationExample(sources) {
|
|
45328
|
+
const days = { d: from(sources.desiccantDays, "enum-sensor", "value") };
|
|
45329
|
+
return require_composition.CompositionSchema.parse({
|
|
45330
|
+
target: {
|
|
45331
|
+
kind: "existing",
|
|
45332
|
+
device: sources.feeder
|
|
45333
|
+
},
|
|
45334
|
+
features: [{
|
|
45335
|
+
kind: "fields",
|
|
45336
|
+
cap: "consumables",
|
|
45337
|
+
fields: {
|
|
45338
|
+
lastChangedAt: {
|
|
45339
|
+
kind: "expression",
|
|
45340
|
+
expr: "now",
|
|
45341
|
+
bindings: {}
|
|
45342
|
+
},
|
|
45343
|
+
lastFetchedAt: {
|
|
45344
|
+
kind: "expression",
|
|
45345
|
+
expr: "now",
|
|
45346
|
+
bindings: {}
|
|
45347
|
+
}
|
|
45348
|
+
},
|
|
45349
|
+
items: { desiccant: {
|
|
45350
|
+
label: "Desiccant",
|
|
45351
|
+
fields: {
|
|
45352
|
+
"remaining.value": {
|
|
45353
|
+
kind: "expression",
|
|
45354
|
+
expr: "number(d)",
|
|
45355
|
+
bindings: days
|
|
45356
|
+
},
|
|
45357
|
+
"remaining.unit": {
|
|
45358
|
+
kind: "expression",
|
|
45359
|
+
expr: "\"days\"",
|
|
45360
|
+
bindings: {}
|
|
45361
|
+
},
|
|
45362
|
+
status: {
|
|
45363
|
+
kind: "expression",
|
|
45364
|
+
expr: "number(d) == null ? null : (number(d) <= 0 ? \"replace\" : \"ok\")",
|
|
45365
|
+
bindings: days
|
|
45366
|
+
}
|
|
45367
|
+
}
|
|
45368
|
+
} }
|
|
45369
|
+
}, {
|
|
45370
|
+
kind: "fields",
|
|
45371
|
+
cap: "battery",
|
|
45372
|
+
fields: {
|
|
45373
|
+
percentage: {
|
|
45374
|
+
kind: "expression",
|
|
45375
|
+
expr: "s == \"low\" ? 15 : (s == \"normal\" ? 100 : null)",
|
|
45376
|
+
bindings: { s: from(sources.batteryStatus, "enum-sensor", "value") }
|
|
45377
|
+
},
|
|
45378
|
+
binary: {
|
|
45379
|
+
kind: "expression",
|
|
45380
|
+
expr: "true",
|
|
45381
|
+
bindings: {}
|
|
45382
|
+
},
|
|
45383
|
+
charging: {
|
|
45384
|
+
kind: "expression",
|
|
45385
|
+
expr: "c ? \"dc\" : \"unknown\"",
|
|
45386
|
+
bindings: { c: from(sources.batteryCharging, "binary", "on") }
|
|
45387
|
+
},
|
|
45388
|
+
sleeping: {
|
|
45389
|
+
kind: "expression",
|
|
45390
|
+
expr: "false",
|
|
45391
|
+
bindings: {}
|
|
45392
|
+
},
|
|
45393
|
+
lastUpdated: {
|
|
45394
|
+
kind: "expression",
|
|
45395
|
+
expr: "now",
|
|
45396
|
+
bindings: {}
|
|
45397
|
+
}
|
|
45398
|
+
}
|
|
45399
|
+
}]
|
|
45400
|
+
});
|
|
45401
|
+
}
|
|
45402
|
+
//#endregion
|
|
45403
|
+
//#region src/composition/validate-composition.ts
|
|
45404
|
+
function buildCapabilityLookup(defs) {
|
|
45405
|
+
const byName = new Map(defs.map((d) => [d.name, d]));
|
|
45406
|
+
return (capName) => byName.get(capName) ?? null;
|
|
45407
|
+
}
|
|
45408
|
+
/** Caps every `BaseDevice` registers on itself; a second registration throws. */
|
|
45409
|
+
var BASE_DEVICE_CAPS = new Set(["device-status", "feature-probe"]);
|
|
45410
|
+
var PENDING_SELF = {
|
|
45411
|
+
addonId: require_composition.COMPOSER_ADDON_ID,
|
|
45412
|
+
stableId: `${require_composition.COMPOSED_DEVICE_STABLE_ID_PREFIX}(unsaved)`
|
|
45413
|
+
};
|
|
45414
|
+
/** The candidate's block id before its first save. Stored block ids are `randomUUID()`, so none can carry it. */
|
|
45415
|
+
var PENDING_BLOCK_ID = "(unsaved)";
|
|
45416
|
+
function composableCap(feature, at, lookupCap) {
|
|
45795
45417
|
const def = lookupCap(feature.cap);
|
|
45796
|
-
if (def === null) return
|
|
45797
|
-
if (BASE_DEVICE_CAPS.has(def.name)) return
|
|
45418
|
+
if (def === null) return problem("unknown-capability", `${at}.cap`, `no capability is named \`${feature.cap}\``);
|
|
45419
|
+
if (BASE_DEVICE_CAPS.has(def.name)) return problem("not-composable", `${at}.cap`, `\`${def.name}\` is registered by every device itself — it cannot be composed`);
|
|
45798
45420
|
const state = def.runtimeState;
|
|
45799
|
-
if (def.scope !== "device" || state === void 0) return
|
|
45800
|
-
|
|
45801
|
-
|
|
45802
|
-
|
|
45803
|
-
|
|
45421
|
+
if (def.scope !== "device" || state === void 0) return problem("not-composable", `${at}.cap`, `\`${def.name}\` has no per-device state to compose`);
|
|
45422
|
+
return {
|
|
45423
|
+
def,
|
|
45424
|
+
state,
|
|
45425
|
+
fields: describeTopLevelStateFields(state)
|
|
45426
|
+
};
|
|
45427
|
+
}
|
|
45428
|
+
/**
|
|
45429
|
+
* A capability BUILT by the composition (a new device, or one ADDED to an
|
|
45430
|
+
* existing device) must fit the device type, serve no command, and have every
|
|
45431
|
+
* required field. A REPLACE needs none of it: the cap is bound (a fact), the
|
|
45432
|
+
* native keeps serving its methods, and only the named fields are owned (D663).
|
|
45433
|
+
*/
|
|
45434
|
+
function builtCapProblems({ def, state, fields: targetFields }, feature, at, deviceType) {
|
|
45435
|
+
const problems = [];
|
|
45436
|
+
if (def.deviceTypes !== void 0 && !def.deviceTypes.includes(deviceType)) problems.push(problem("wrong-device-type", `${at}.cap`, `\`${def.name}\` is not offered on a \`${deviceType}\` device (it allows ${def.deviceTypes.join(", ")})`));
|
|
45437
|
+
const served = Object.entries(def.methods).filter(([name, m]) => m.systemOnly !== true && def.composedMethods?.[name] === void 0).map(([name]) => `\`${name}\``);
|
|
45438
|
+
if (served.length > 0) problems.push(problem("has-commands", `${at}.cap`, `\`${def.name}\` has ${served.join(", ")}, which a composed device cannot serve until commands arrive (slice 3)`));
|
|
45439
|
+
const itemArrayPath = feature.items === void 0 ? null : def.status?.itemArray?.path ?? null;
|
|
45440
|
+
for (const key of requiredTopLevelKeys(state)) {
|
|
45441
|
+
if (key === itemArrayPath) continue;
|
|
45442
|
+
if (!targetFields.has(key)) problems.push(problem("not-composable", `${at}.fields`, `\`${def.name}.${key}\` is not a scalar — nested fields arrive in a later slice`));
|
|
45443
|
+
else if (!(key in feature.fields)) problems.push(problem("missing-field", `${at}.fields.${key}`, `\`${def.name}.${key}\` is required and has no source`));
|
|
45444
|
+
}
|
|
45445
|
+
return problems;
|
|
45446
|
+
}
|
|
45447
|
+
function planTopLevelField({ def, fields }, field, mode, lookupCap) {
|
|
45448
|
+
const { featureIndex, path, source } = field;
|
|
45449
|
+
const fieldAt = `features[${featureIndex}].fields.${path}`;
|
|
45450
|
+
if (path.includes(".")) return refused(problem("nested-field", fieldAt, `\`${path}\` is nested — nested fields arrive in a later slice`));
|
|
45451
|
+
const tf = fields.get(path);
|
|
45452
|
+
if (tf === void 0) return refused(problem("unknown-field", fieldAt, `\`${def.name}\` has no field \`${path}\``));
|
|
45453
|
+
const kinds = sourceKinds(source, lookupCap);
|
|
45454
|
+
if (!kinds.ok) return refused(problem(kinds.code, fieldAt, kinds.message));
|
|
45455
|
+
const role = roleOf(source);
|
|
45456
|
+
const unavailable = unavailableFor(tf, role, mode, def.name, fieldAt);
|
|
45457
|
+
if (!unavailable.ok) return refused(unavailable.problem);
|
|
45458
|
+
const plan = {
|
|
45459
|
+
cap: def.name,
|
|
45460
|
+
path,
|
|
45461
|
+
featureIndex,
|
|
45462
|
+
kind: tf.kind,
|
|
45463
|
+
nullable: tf.nullable,
|
|
45464
|
+
required: tf.required,
|
|
45465
|
+
unavailable: unavailable.unavailable,
|
|
45466
|
+
role,
|
|
45467
|
+
source,
|
|
45468
|
+
reads: [...require_composition.fieldReads(source)]
|
|
45469
|
+
};
|
|
45470
|
+
const enumProblem = enumLiteralProblem(def.name, tf, source, fieldAt);
|
|
45471
|
+
return {
|
|
45472
|
+
problems: [...compatibility(def.name, tf, kinds.kinds, fieldAt), ...enumProblem === null ? [] : [enumProblem]],
|
|
45473
|
+
fields: [plan],
|
|
45474
|
+
features: []
|
|
45475
|
+
};
|
|
45476
|
+
}
|
|
45477
|
+
function planFeature(feature, featureIndex, target, lookupCap) {
|
|
45478
|
+
const at = `features[${featureIndex}]`;
|
|
45479
|
+
const cap = composableCap(feature, at, lookupCap);
|
|
45480
|
+
if ("code" in cap) return refused(cap);
|
|
45481
|
+
const capProblems = target.mode === "replace" ? [] : [...builtCapProblems(cap, feature, at, target.deviceType)];
|
|
45804
45482
|
if (feature.commands !== void 0 && Object.keys(feature.commands).length > 0) capProblems.push(problem("reserved-variant", `${at}.commands`, "commands (`forward`) arrive in slice 3"));
|
|
45805
|
-
const
|
|
45806
|
-
|
|
45807
|
-
|
|
45808
|
-
|
|
45809
|
-
|
|
45810
|
-
|
|
45811
|
-
|
|
45812
|
-
|
|
45813
|
-
|
|
45814
|
-
|
|
45815
|
-
|
|
45816
|
-
cap: def.name,
|
|
45817
|
-
path,
|
|
45483
|
+
const perField = Object.entries(feature.fields).map(([path, source]) => planTopLevelField(cap, {
|
|
45484
|
+
featureIndex,
|
|
45485
|
+
path,
|
|
45486
|
+
source
|
|
45487
|
+
}, target.mode, lookupCap));
|
|
45488
|
+
const items = feature.items === void 0 ? [] : [planItems(cap.def, feature, featureIndex, lookupCap, target.mode)];
|
|
45489
|
+
const all = [...perField, ...items];
|
|
45490
|
+
return {
|
|
45491
|
+
problems: [...capProblems, ...all.flatMap((r) => r.problems)],
|
|
45492
|
+
fields: all.flatMap((r) => r.fields),
|
|
45493
|
+
features: [{
|
|
45818
45494
|
featureIndex,
|
|
45819
|
-
|
|
45820
|
-
|
|
45821
|
-
|
|
45822
|
-
|
|
45823
|
-
|
|
45824
|
-
|
|
45825
|
-
|
|
45826
|
-
|
|
45827
|
-
|
|
45828
|
-
|
|
45829
|
-
|
|
45830
|
-
|
|
45831
|
-
|
|
45832
|
-
|
|
45495
|
+
cap: cap.def.name,
|
|
45496
|
+
mode: target.mode
|
|
45497
|
+
}]
|
|
45498
|
+
};
|
|
45499
|
+
}
|
|
45500
|
+
/** A cap the device serves itself; one the composer grafted (an earlier ADD) is not native. */
|
|
45501
|
+
function isNativeCap(target, cap) {
|
|
45502
|
+
const owner = target.nativeCaps.get(cap);
|
|
45503
|
+
return owner !== void 0 && owner !== "composer";
|
|
45504
|
+
}
|
|
45505
|
+
/** The target read, or the refusal that replaces the whole plan when it could not be (D391). */
|
|
45506
|
+
function planTargetOf(composition, target) {
|
|
45507
|
+
if (composition.target.kind === "new") return {
|
|
45508
|
+
kind: "new",
|
|
45509
|
+
type: composition.target.type
|
|
45510
|
+
};
|
|
45511
|
+
const key = require_composition.compositionSourceKey(composition.target.device);
|
|
45512
|
+
if (target === void 0) return problem("target-unreadable", "target", `target device \`${key}\` was not read — the save is refused rather than stored unchecked`);
|
|
45513
|
+
if (target.kind === "absent") return problem("target-missing", "target", `target device \`${key}\` does not exist`);
|
|
45514
|
+
if (target.kind === "unreadable") return problem("target-unreadable", "target", `could not read target device \`${key}\` (${target.error}) — the save is refused rather than stored unchecked`);
|
|
45833
45515
|
return {
|
|
45834
|
-
|
|
45835
|
-
|
|
45516
|
+
kind: "existing",
|
|
45517
|
+
found: target
|
|
45836
45518
|
};
|
|
45837
45519
|
}
|
|
45838
|
-
function
|
|
45839
|
-
|
|
45840
|
-
|
|
45520
|
+
function featureTargetOf(target, cap) {
|
|
45521
|
+
if (target.kind === "new") return {
|
|
45522
|
+
mode: "new",
|
|
45523
|
+
deviceType: target.type
|
|
45524
|
+
};
|
|
45525
|
+
return {
|
|
45526
|
+
mode: isNativeCap(target.found, cap) ? "replace" : "add",
|
|
45527
|
+
deviceType: target.found.type
|
|
45528
|
+
};
|
|
45529
|
+
}
|
|
45530
|
+
/**
|
|
45531
|
+
* The static plan. `target` describes an EXISTING target device (one read);
|
|
45532
|
+
* it is ignored for a new one. On an existing target each feature REPLACES a
|
|
45533
|
+
* cap the device has natively, else ADDS it (D663).
|
|
45534
|
+
*/
|
|
45535
|
+
function planComposition(composition, lookupCap, target) {
|
|
45536
|
+
const planTarget = planTargetOf(composition, target);
|
|
45537
|
+
if ("code" in planTarget) return refused(planTarget);
|
|
45841
45538
|
const results = composition.features.map((feature, index) => {
|
|
45842
|
-
|
|
45843
|
-
|
|
45844
|
-
return planFeature(feature, index, target, lookupCap);
|
|
45539
|
+
if (feature.kind === "passthrough") return refused(problem("reserved-variant", `features[${index}]`, `whole-capability passthrough of \`${feature.cap}\` arrives in slice 3`));
|
|
45540
|
+
return planFeature(feature, index, featureTargetOf(planTarget, feature.cap), lookupCap);
|
|
45845
45541
|
});
|
|
45846
45542
|
return {
|
|
45847
45543
|
problems: results.flatMap((r) => r.problems),
|
|
45848
|
-
fields: results.flatMap((r) => r.fields)
|
|
45544
|
+
fields: results.flatMap((r) => r.fields),
|
|
45545
|
+
features: results.flatMap((r) => r.features)
|
|
45849
45546
|
};
|
|
45850
45547
|
}
|
|
45851
45548
|
function compositionSourceRefs(composition) {
|
|
45852
|
-
const refs = composition.features.flatMap((f) => f.kind === "fields" ?
|
|
45853
|
-
return [...new Map(refs.map((r) => [compositionSourceKey(r), r])).values()];
|
|
45549
|
+
const refs = composition.features.flatMap((f) => f.kind === "fields" ? require_composition.featureSources(f).flatMap(require_composition.fieldReads).map((r) => r.source) : []);
|
|
45550
|
+
return [...new Map(refs.map((r) => [require_composition.compositionSourceKey(r), r])).values()];
|
|
45854
45551
|
}
|
|
45855
|
-
|
|
45856
|
-
|
|
45552
|
+
/**
|
|
45553
|
+
* The candidate's device: the existing target itself, else `self`, else a
|
|
45554
|
+
* placeholder that cannot collide with a stored target. An existing target is
|
|
45555
|
+
* known even on create, so a second customization of it is refused on create too.
|
|
45556
|
+
*/
|
|
45557
|
+
function candidateSelf(composition, ctx, plan) {
|
|
45558
|
+
return {
|
|
45559
|
+
ref: composition.target.kind === "existing" ? composition.target.device : ctx.self ?? PENDING_SELF,
|
|
45560
|
+
native: composition.target.kind === "existing" && ctx.target?.kind === "found" ? ctx.target : null,
|
|
45561
|
+
replaced: replacedFieldKeys(plan)
|
|
45562
|
+
};
|
|
45563
|
+
}
|
|
45564
|
+
/**
|
|
45565
|
+
* `compositionFieldKey` of every field a REPLACE feature of `plan` owns — the
|
|
45566
|
+
* candidate's own, and a stored peer's `CompositionPeer.replaced` (D663): one
|
|
45567
|
+
* derivation, so the graph reads both the same way.
|
|
45568
|
+
*/
|
|
45569
|
+
function replacedFieldKeys(plan) {
|
|
45570
|
+
const replacing = new Set(plan.features.filter((f) => f.mode === "replace").map((f) => f.featureIndex));
|
|
45571
|
+
return new Set(plan.fields.filter((f) => replacing.has(f.featureIndex)).map(compositionFieldKey));
|
|
45572
|
+
}
|
|
45573
|
+
/**
|
|
45574
|
+
* A read of the candidate's own device. Its own composed output (a new device's
|
|
45575
|
+
* field, an ADDED field) is the slice-1 own-field rule; on an existing target
|
|
45576
|
+
* every other field — replaced, or not composed at all — is the NATIVE value,
|
|
45577
|
+
* valid when the device serves that cap natively (D663, rule 5).
|
|
45578
|
+
*/
|
|
45579
|
+
function selfReadProblems(read, at, fields, self) {
|
|
45580
|
+
const readKey = `${read.cap}.${read.fieldPath}`;
|
|
45581
|
+
if (fields.some((f) => compositionFieldKey(f) === readKey && !self.replaced.has(readKey))) return [];
|
|
45582
|
+
if (self.native === null) return [problem("source-field-unknown", at, `this device has no composed field \`${read.cap}.${read.fieldPath}\``)];
|
|
45583
|
+
if (isNativeCap(self.native, read.cap)) return [];
|
|
45584
|
+
return [problem("source-capability-missing", at, `the target device (device ${self.native.deviceId}) has no native \`${read.cap}\``)];
|
|
45585
|
+
}
|
|
45586
|
+
function sourceProblems(fields, ctx, self) {
|
|
45587
|
+
const selfKey = require_composition.compositionSourceKey(self.ref);
|
|
45857
45588
|
return fields.flatMap((field) => field.reads.flatMap((read) => {
|
|
45858
|
-
const at =
|
|
45859
|
-
const key = compositionSourceKey(read.source);
|
|
45860
|
-
if (key === selfKey) return
|
|
45589
|
+
const at = compositionFieldAt(field);
|
|
45590
|
+
const key = require_composition.compositionSourceKey(read.source);
|
|
45591
|
+
if (key === selfKey) return selfReadProblems(read, at, fields, self);
|
|
45861
45592
|
const desc = ctx.sources.get(key);
|
|
45862
45593
|
if (desc === void 0 || desc.kind === "unreadable") return [problem("source-unreadable", at, `could not read source \`${key}\` (${desc === void 0 ? "it was not read" : desc.error}) — the save is refused rather than stored unchecked`)];
|
|
45863
45594
|
if (desc.kind === "absent") return [problem("source-missing", at, `source \`${key}\` does not exist`)];
|
|
@@ -45870,8 +45601,9 @@ function sourceProblems(fields, ctx) {
|
|
|
45870
45601
|
* `<addonId>/<stableId>#<cap>.<fieldPath>`. When a node belongs to the
|
|
45871
45602
|
* CANDIDATE itself (this composition being saved), pull its `cap`/`fieldPath`
|
|
45872
45603
|
* back out so the refusal can point at that exact field, matching the
|
|
45873
|
-
* documented `features[N].fields.<path>` convention
|
|
45874
|
-
*
|
|
45604
|
+
* documented `features[N].fields.<path>` convention. A cap name has no `.`,
|
|
45605
|
+
* so splitting `rest` on the FIRST `.` is exact; an item leaf's remainder
|
|
45606
|
+
* (`items[desiccant].status`) is matched through `compositionFieldKey`.
|
|
45875
45607
|
*/
|
|
45876
45608
|
function candidateFieldOfNode(nodeKey, candidateKey) {
|
|
45877
45609
|
const prefix = `${candidateKey}#`;
|
|
@@ -45891,44 +45623,65 @@ function cyclePath(nodePath, candidateKey, fields) {
|
|
|
45891
45623
|
for (const nodeKey of nodePath) {
|
|
45892
45624
|
const hit = candidateFieldOfNode(nodeKey, candidateKey);
|
|
45893
45625
|
if (hit === null) continue;
|
|
45894
|
-
const
|
|
45895
|
-
|
|
45626
|
+
const hitKey = `${hit.cap}.${hit.fieldPath}`;
|
|
45627
|
+
const field = fields.find((f) => compositionFieldKey(f) === hitKey);
|
|
45628
|
+
if (field !== void 0) return compositionFieldAt(field);
|
|
45896
45629
|
}
|
|
45897
45630
|
return "features";
|
|
45898
45631
|
}
|
|
45632
|
+
/** The first unread device any of `nodes` belongs to; null when none does. */
|
|
45633
|
+
function unreadDeviceOf(nodes, unread) {
|
|
45634
|
+
for (const [key, error] of unread) if (nodes.some((node) => node.startsWith(`${key}#`))) return {
|
|
45635
|
+
key,
|
|
45636
|
+
error
|
|
45637
|
+
};
|
|
45638
|
+
return null;
|
|
45639
|
+
}
|
|
45899
45640
|
/**
|
|
45900
|
-
* Cycle check runs against the CANDIDATE (this composition,
|
|
45901
|
-
*
|
|
45902
|
-
*
|
|
45903
|
-
*
|
|
45904
|
-
*
|
|
45905
|
-
*
|
|
45641
|
+
* Cycle check runs against the CANDIDATE (this composition, as block
|
|
45642
|
+
* `ctx.blockId`, writing `self.ref`) plus every OTHER stored composition,
|
|
45643
|
+
* decided per strongly connected component (`findCandidateCycles`): a cyclic
|
|
45644
|
+
* component holding a candidate node refuses, with one representative cycle
|
|
45645
|
+
* through it; one without is not this save's cycle. A
|
|
45646
|
+
* stored composition of the same block is its old version; another block on
|
|
45647
|
+
* the same device, or two stored peers sharing a device, make
|
|
45648
|
+
* `compositionEdges` throw `CompositionGraphError` — refused BY NAME as
|
|
45649
|
+
* `duplicate-target` (D391, D663) instead of escaping as an uncaught throw.
|
|
45906
45650
|
*/
|
|
45907
|
-
function cycleProblems(composition, ctx, fields) {
|
|
45651
|
+
function cycleProblems(composition, ctx, fields, self) {
|
|
45908
45652
|
const candidate = {
|
|
45909
|
-
|
|
45910
|
-
|
|
45653
|
+
blockId: ctx.blockId ?? "(unsaved)",
|
|
45654
|
+
target: self.ref,
|
|
45655
|
+
composition,
|
|
45656
|
+
replaced: self.replaced
|
|
45911
45657
|
};
|
|
45912
|
-
const candidateKey = compositionSourceKey(candidate.target);
|
|
45658
|
+
const candidateKey = require_composition.compositionSourceKey(candidate.target);
|
|
45659
|
+
const unread = ctx.unreadTargets ?? /* @__PURE__ */ new Map();
|
|
45913
45660
|
try {
|
|
45914
|
-
return
|
|
45661
|
+
return findCandidateCycles(candidate, ctx.peers).map((c) => {
|
|
45662
|
+
const at = cyclePath(c.path, candidateKey, fields);
|
|
45663
|
+
const through = unreadDeviceOf([...c.component], unread);
|
|
45664
|
+
return through === null ? problem("cycle", at, `a field reads itself: ${c.path.join(" → ")}`) : problem("cycle", at, `possible cycle through \`${through.key}\` — its state could not be read (${through.error}): ${c.path.join(" → ")}`);
|
|
45665
|
+
});
|
|
45915
45666
|
} catch (err) {
|
|
45916
45667
|
if (err instanceof CompositionGraphError) return [problem("duplicate-target", "features", err.message)];
|
|
45917
45668
|
throw err;
|
|
45918
45669
|
}
|
|
45919
45670
|
}
|
|
45920
45671
|
function validateComposition(composition, ctx) {
|
|
45921
|
-
const plan = planComposition(composition, ctx.lookupCap);
|
|
45922
|
-
const
|
|
45672
|
+
const plan = planComposition(composition, ctx.lookupCap, ctx.target ?? void 0);
|
|
45673
|
+
const self = candidateSelf(composition, ctx, plan);
|
|
45674
|
+
const cycles = cycleProblems(composition, ctx, plan.fields, self);
|
|
45923
45675
|
const problems = [
|
|
45924
45676
|
...plan.problems,
|
|
45925
|
-
...sourceProblems(plan.fields, ctx),
|
|
45677
|
+
...sourceProblems(plan.fields, ctx, self),
|
|
45926
45678
|
...cycles
|
|
45927
45679
|
];
|
|
45928
45680
|
return {
|
|
45929
45681
|
ok: problems.length === 0,
|
|
45930
45682
|
problems,
|
|
45931
|
-
fields: [...plan.fields]
|
|
45683
|
+
fields: [...plan.fields],
|
|
45684
|
+
features: [...plan.features]
|
|
45932
45685
|
};
|
|
45933
45686
|
}
|
|
45934
45687
|
//#endregion
|
|
@@ -46093,14 +45846,14 @@ var RUNTIME_DEFAULTS = {
|
|
|
46093
45846
|
* any role works, including non-accessory ones like Doorbell).
|
|
46094
45847
|
*/
|
|
46095
45848
|
var AccessoryKind = {
|
|
46096
|
-
Siren:
|
|
46097
|
-
Floodlight:
|
|
46098
|
-
Spotlight:
|
|
46099
|
-
PirSensor:
|
|
46100
|
-
Chime:
|
|
46101
|
-
Autotrack:
|
|
46102
|
-
Nightvision:
|
|
46103
|
-
PrivacyMask:
|
|
45849
|
+
Siren: require_err_msg.DeviceRole.Siren,
|
|
45850
|
+
Floodlight: require_err_msg.DeviceRole.Floodlight,
|
|
45851
|
+
Spotlight: require_err_msg.DeviceRole.Spotlight,
|
|
45852
|
+
PirSensor: require_err_msg.DeviceRole.PirSensor,
|
|
45853
|
+
Chime: require_err_msg.DeviceRole.Chime,
|
|
45854
|
+
Autotrack: require_err_msg.DeviceRole.Autotrack,
|
|
45855
|
+
Nightvision: require_err_msg.DeviceRole.Nightvision,
|
|
45856
|
+
PrivacyMask: require_err_msg.DeviceRole.PrivacyMask
|
|
46104
45857
|
};
|
|
46105
45858
|
/**
|
|
46106
45859
|
* Default English label for each accessory kind. Drivers append this
|
|
@@ -46243,38 +45996,31 @@ var DeviceConfig = class DeviceConfig {
|
|
|
46243
45996
|
};
|
|
46244
45997
|
//#endregion
|
|
46245
45998
|
//#region src/device/device-runtime-state.ts
|
|
46246
|
-
/**
|
|
46247
|
-
* Concrete implementation. Routes every successful write through
|
|
46248
|
-
* `writer(capName, slice)` — the kernel hooks this up to
|
|
46249
|
-
* `device-state.setCapSlice`, the canonical cross-layer write
|
|
46250
|
-
* entrypoint, which handles disk persistence (debounced on the hub)
|
|
46251
|
-
* and mirror updates.
|
|
46252
|
-
*
|
|
46253
|
-
* Schema validation runs in-process before the writer is called —
|
|
46254
|
-
* the round-trip should never carry an invalid slice. `flush()`
|
|
46255
|
-
* awaits any in-flight writer promises so shutdown is lossless.
|
|
46256
|
-
*
|
|
46257
|
-
* `initial` is the persisted blob loaded at boot. Slices for caps
|
|
46258
|
-
* whose schema hasn't been installed yet are kept in-memory verbatim
|
|
46259
|
-
* and validated when the cap registers later.
|
|
46260
|
-
*/
|
|
46261
45999
|
var DeviceRuntimeState = class DeviceRuntimeState {
|
|
46262
46000
|
writer;
|
|
46001
|
+
onWriteFailed;
|
|
46263
46002
|
/** In-flight writer promises tracked so `flush()` can await them. */
|
|
46264
46003
|
pendingWrites = /* @__PURE__ */ new Set();
|
|
46004
|
+
/**
|
|
46005
|
+
* Caps whose last write the hub refused. The equality gate in
|
|
46006
|
+
* `applyCapWrite` is bypassed for these, so the SAME value is re-sent on the
|
|
46007
|
+
* next write instead of being swallowed forever; a landing write clears it.
|
|
46008
|
+
*/
|
|
46009
|
+
failedWrites = /* @__PURE__ */ new Set();
|
|
46265
46010
|
/** Per-cap committed slice — after schema validation when known. */
|
|
46266
46011
|
slices;
|
|
46267
46012
|
/** Per-cap registered schema (set by `installCapSchema`). */
|
|
46268
46013
|
schemas = /* @__PURE__ */ new Map();
|
|
46269
46014
|
listeners = /* @__PURE__ */ new Set();
|
|
46270
46015
|
capListeners = /* @__PURE__ */ new Map();
|
|
46271
|
-
constructor(initial, writer) {
|
|
46016
|
+
constructor(initial, writer, onWriteFailed) {
|
|
46272
46017
|
this.writer = writer;
|
|
46018
|
+
this.onWriteFailed = onWriteFailed;
|
|
46273
46019
|
this.slices = /* @__PURE__ */ new Map();
|
|
46274
46020
|
for (const [k, v] of Object.entries(initial)) if (v && typeof v === "object" && !Array.isArray(v)) this.slices.set(k, { ...v });
|
|
46275
46021
|
}
|
|
46276
|
-
static fromInitial(initial, writer) {
|
|
46277
|
-
return new DeviceRuntimeState(initial, writer);
|
|
46022
|
+
static fromInitial(initial, writer, onWriteFailed) {
|
|
46023
|
+
return new DeviceRuntimeState(initial, writer, onWriteFailed);
|
|
46278
46024
|
}
|
|
46279
46025
|
installCapSchema(capName, schema) {
|
|
46280
46026
|
const existing = this.schemas.get(capName);
|
|
@@ -46318,10 +46064,15 @@ var DeviceRuntimeState = class DeviceRuntimeState {
|
|
|
46318
46064
|
...value
|
|
46319
46065
|
} : { ...value };
|
|
46320
46066
|
const parsed = schema.parse(next);
|
|
46321
|
-
if (shallowEqual(current, parsed)) return;
|
|
46067
|
+
if (shallowEqual(current, parsed) && !this.failedWrites.has(capName)) return;
|
|
46322
46068
|
this.slices.set(capName, parsed);
|
|
46323
46069
|
this.fireListeners([capName]);
|
|
46324
|
-
const writePromise = this.writer(capName, { ...parsed }).
|
|
46070
|
+
const writePromise = this.writer(capName, { ...parsed }).then(() => {
|
|
46071
|
+
this.failedWrites.delete(capName);
|
|
46072
|
+
}).catch((err) => {
|
|
46073
|
+
this.failedWrites.add(capName);
|
|
46074
|
+
this.onWriteFailed?.(capName, err instanceof Error ? err.message : String(err));
|
|
46075
|
+
});
|
|
46325
46076
|
this.pendingWrites.add(writePromise);
|
|
46326
46077
|
writePromise.finally(() => {
|
|
46327
46078
|
this.pendingWrites.delete(writePromise);
|
|
@@ -46602,7 +46353,16 @@ var BaseDevice = class {
|
|
|
46602
46353
|
});
|
|
46603
46354
|
};
|
|
46604
46355
|
const initial = ctx.initialRuntimeState ?? {};
|
|
46605
|
-
|
|
46356
|
+
const onWriteFailed = (cap, error) => {
|
|
46357
|
+
ctx.logger.warn("runtime-state write refused by the hub — the slice is not mirrored; it is re-sent on the next write", {
|
|
46358
|
+
tags: { deviceId: ctx.id },
|
|
46359
|
+
meta: {
|
|
46360
|
+
cap,
|
|
46361
|
+
error
|
|
46362
|
+
}
|
|
46363
|
+
});
|
|
46364
|
+
};
|
|
46365
|
+
this.runtimeState = DeviceRuntimeState.fromInitial(initial, writer, onWriteFailed);
|
|
46606
46366
|
ctx.bindRuntimeState?.(this.runtimeState);
|
|
46607
46367
|
ctx.registerNativeCap?.(deviceStatusCapability, {});
|
|
46608
46368
|
const seed = {
|
|
@@ -47197,7 +46957,7 @@ var BaseDeviceProvider = class extends require_sleep.BaseAddon {
|
|
|
47197
46957
|
*/
|
|
47198
46958
|
async getManualCreationType() {
|
|
47199
46959
|
if (!await this.supportsManualCreation()) return { deviceType: null };
|
|
47200
|
-
return { deviceType: Object.values(
|
|
46960
|
+
return { deviceType: Object.values(require_err_msg.DeviceType).find((t) => this.deviceClasses[t] !== void 0) ?? null };
|
|
47201
46961
|
}
|
|
47202
46962
|
async adoptDiscoveredDevice(_input) {
|
|
47203
46963
|
throw new Error(`${this.providerName} provider does not support discovery-based adoption`);
|
|
@@ -47404,14 +47164,14 @@ var BaseDeviceProvider = class extends require_sleep.BaseAddon {
|
|
|
47404
47164
|
});
|
|
47405
47165
|
const meta = await this.ctx.api.deviceManager.loadMeta.query({ deviceId: id });
|
|
47406
47166
|
if (meta === null) throw new Error(`${this.providerName}: no persisted meta for "${stableId}" (id ${id}) — cannot reload`);
|
|
47407
|
-
const deviceType = Object.values(
|
|
47167
|
+
const deviceType = Object.values(require_err_msg.DeviceType).find((t) => t === meta.type);
|
|
47408
47168
|
const Class = deviceType !== void 0 ? this.deviceClasses[deviceType] : void 0;
|
|
47409
47169
|
if (!Class) throw new Error(`${this.providerName}: no device class for type "${meta.type}" — "${stableId}" is an accessory child; reload its parent instead`);
|
|
47410
47170
|
await devices.create(stableId, Class, {}, meta.parentDeviceId ?? null);
|
|
47411
47171
|
const rows = await this.ctx.api.deviceManager.listPersistedByAddon.query({ addonId: this.addonId });
|
|
47412
47172
|
for (const row of rows) {
|
|
47413
47173
|
if (row.parentDeviceId !== id) continue;
|
|
47414
|
-
const childType = Object.values(
|
|
47174
|
+
const childType = Object.values(require_err_msg.DeviceType).find((t) => t === row.type);
|
|
47415
47175
|
const ChildClass = childType !== void 0 ? this.deviceClasses[childType] : void 0;
|
|
47416
47176
|
if (!ChildClass) continue;
|
|
47417
47177
|
try {
|
|
@@ -47838,7 +47598,7 @@ function expandContainer(id, byId, byParent, visited, out) {
|
|
|
47838
47598
|
if (visited.has(id)) return;
|
|
47839
47599
|
const candidate = byId.get(id);
|
|
47840
47600
|
if (!candidate) return;
|
|
47841
|
-
if (candidate.type !==
|
|
47601
|
+
if (candidate.type !== require_err_msg.DeviceType.Container) {
|
|
47842
47602
|
out.add(id);
|
|
47843
47603
|
return;
|
|
47844
47604
|
}
|
|
@@ -47882,11 +47642,11 @@ function legacyAutoLinkIds(params) {
|
|
|
47882
47642
|
const ids = /* @__PURE__ */ new Set();
|
|
47883
47643
|
const expand = (id) => expandContainer(id, byId, byParent, /* @__PURE__ */ new Set(), ids);
|
|
47884
47644
|
for (const c of candidates) {
|
|
47885
|
-
if (c.type ===
|
|
47645
|
+
if (c.type === require_err_msg.DeviceType.Container) continue;
|
|
47886
47646
|
if (c.location !== selfLocation) continue;
|
|
47887
47647
|
if (c.parentDeviceId === selfId) continue;
|
|
47888
47648
|
const parent = c.parentDeviceId !== null ? byId.get(c.parentDeviceId) : void 0;
|
|
47889
|
-
if (parent !== void 0 && parent.type ===
|
|
47649
|
+
if (parent !== void 0 && parent.type === require_err_msg.DeviceType.Container) {
|
|
47890
47650
|
expand(parent.id);
|
|
47891
47651
|
continue;
|
|
47892
47652
|
}
|
|
@@ -48178,39 +47938,39 @@ var DeclaredDevices = class {
|
|
|
48178
47938
|
* placeholder HA types whose inline control renders a dash).
|
|
48179
47939
|
*/
|
|
48180
47940
|
var DEVICE_TYPE_CONTROL_KIND = {
|
|
48181
|
-
[
|
|
48182
|
-
[
|
|
48183
|
-
[
|
|
48184
|
-
[
|
|
48185
|
-
[
|
|
48186
|
-
[
|
|
48187
|
-
[
|
|
48188
|
-
[
|
|
48189
|
-
[
|
|
48190
|
-
[
|
|
48191
|
-
[
|
|
48192
|
-
[
|
|
48193
|
-
[
|
|
48194
|
-
[
|
|
48195
|
-
[
|
|
48196
|
-
[
|
|
48197
|
-
[
|
|
48198
|
-
[
|
|
48199
|
-
[
|
|
48200
|
-
[
|
|
48201
|
-
[
|
|
48202
|
-
[
|
|
48203
|
-
[
|
|
48204
|
-
[
|
|
48205
|
-
[
|
|
48206
|
-
[
|
|
48207
|
-
[
|
|
48208
|
-
[
|
|
48209
|
-
[
|
|
48210
|
-
[
|
|
48211
|
-
[
|
|
47941
|
+
[require_err_msg.DeviceType.Cover]: "cover",
|
|
47942
|
+
[require_err_msg.DeviceType.Valve]: "valve",
|
|
47943
|
+
[require_err_msg.DeviceType.Humidifier]: "humidifier",
|
|
47944
|
+
[require_err_msg.DeviceType.WaterHeater]: "water-heater",
|
|
47945
|
+
[require_err_msg.DeviceType.Camera]: "dummy",
|
|
47946
|
+
[require_err_msg.DeviceType.Hub]: "dummy",
|
|
47947
|
+
[require_err_msg.DeviceType.Switch]: "switch",
|
|
47948
|
+
[require_err_msg.DeviceType.Siren]: "switch",
|
|
47949
|
+
[require_err_msg.DeviceType.Light]: "light",
|
|
47950
|
+
[require_err_msg.DeviceType.Fan]: "fan",
|
|
47951
|
+
[require_err_msg.DeviceType.Sensor]: "sensor",
|
|
47952
|
+
[require_err_msg.DeviceType.Thermostat]: "thermostat",
|
|
47953
|
+
[require_err_msg.DeviceType.Climate]: "climate",
|
|
47954
|
+
[require_err_msg.DeviceType.Button]: "button",
|
|
47955
|
+
[require_err_msg.DeviceType.EventEmitter]: "dummy",
|
|
47956
|
+
[require_err_msg.DeviceType.Update]: "update",
|
|
47957
|
+
[require_err_msg.DeviceType.Generic]: "dummy",
|
|
47958
|
+
[require_err_msg.DeviceType.Notifier]: "dummy",
|
|
47959
|
+
[require_err_msg.DeviceType.Script]: "script",
|
|
47960
|
+
[require_err_msg.DeviceType.Automation]: "automation",
|
|
47961
|
+
[require_err_msg.DeviceType.Lock]: "lock",
|
|
47962
|
+
[require_err_msg.DeviceType.MediaPlayer]: "media-player",
|
|
47963
|
+
[require_err_msg.DeviceType.AlarmPanel]: "alarm",
|
|
47964
|
+
[require_err_msg.DeviceType.Control]: "control",
|
|
47965
|
+
[require_err_msg.DeviceType.Presence]: "sensor",
|
|
47966
|
+
[require_err_msg.DeviceType.Weather]: "weather",
|
|
47967
|
+
[require_err_msg.DeviceType.Vacuum]: "vacuum",
|
|
47968
|
+
[require_err_msg.DeviceType.LawnMower]: "lawn-mower",
|
|
47969
|
+
[require_err_msg.DeviceType.Container]: "dummy",
|
|
47970
|
+
[require_err_msg.DeviceType.Image]: "image",
|
|
47971
|
+
[require_err_msg.DeviceType.PetFeeder]: "pet-feeder"
|
|
48212
47972
|
};
|
|
48213
|
-
var DEVICE_TYPE_VALUES = new Set(Object.values(
|
|
47973
|
+
var DEVICE_TYPE_VALUES = new Set(Object.values(require_err_msg.DeviceType));
|
|
48214
47974
|
/**
|
|
48215
47975
|
* Runtime-string-safe base resolver: a device row's `type` crosses the wire as
|
|
48216
47976
|
* a plain string, so an unrecognised value resolves to `null` instead of
|
|
@@ -48254,15 +48014,15 @@ var SENSOR_FEATURES = [
|
|
|
48254
48014
|
function pickAccessoryControl(device) {
|
|
48255
48015
|
const set = new Set(device.features);
|
|
48256
48016
|
switch (device.type) {
|
|
48257
|
-
case
|
|
48258
|
-
case
|
|
48259
|
-
case
|
|
48260
|
-
case
|
|
48261
|
-
case
|
|
48262
|
-
case
|
|
48263
|
-
case
|
|
48264
|
-
case
|
|
48265
|
-
case
|
|
48017
|
+
case require_err_msg.DeviceType.Cover: return { kind: "cover" };
|
|
48018
|
+
case require_err_msg.DeviceType.Valve: return { kind: "valve" };
|
|
48019
|
+
case require_err_msg.DeviceType.Lock: return { kind: "lock" };
|
|
48020
|
+
case require_err_msg.DeviceType.AlarmPanel: return { kind: "alarm" };
|
|
48021
|
+
case require_err_msg.DeviceType.Light: return set.has("brightness") ? { kind: "brightness" } : { kind: "switch" };
|
|
48022
|
+
case require_err_msg.DeviceType.Switch:
|
|
48023
|
+
case require_err_msg.DeviceType.Siren: return { kind: "switch" };
|
|
48024
|
+
case require_err_msg.DeviceType.Sensor:
|
|
48025
|
+
case require_err_msg.DeviceType.Presence: {
|
|
48266
48026
|
const sensor = SENSOR_FEATURES.find((f) => set.has(f));
|
|
48267
48027
|
return sensor ? {
|
|
48268
48028
|
kind: "sensor",
|
|
@@ -48341,7 +48101,7 @@ function deviceMatchesProfile(features, profile) {
|
|
|
48341
48101
|
var BATTERY_DEVICE_PROFILE = {
|
|
48342
48102
|
id: "battery",
|
|
48343
48103
|
label: "Battery-operated camera",
|
|
48344
|
-
when: { hasFeature:
|
|
48104
|
+
when: { hasFeature: require_err_msg.DeviceFeature.BatteryOperated },
|
|
48345
48105
|
defaults: {
|
|
48346
48106
|
audioMode: "disabled",
|
|
48347
48107
|
detectionMode: "on-motion"
|
|
@@ -52972,6 +52732,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
52972
52732
|
addonId: null,
|
|
52973
52733
|
access: "view"
|
|
52974
52734
|
},
|
|
52735
|
+
"coreBlocks.getCustomization": {
|
|
52736
|
+
capName: "core-blocks",
|
|
52737
|
+
capScope: "system",
|
|
52738
|
+
addonId: null,
|
|
52739
|
+
access: "view"
|
|
52740
|
+
},
|
|
52975
52741
|
"coreBlocks.getTypeDefs": {
|
|
52976
52742
|
capName: "core-blocks",
|
|
52977
52743
|
capScope: "system",
|
|
@@ -54094,6 +53860,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
54094
53860
|
addonId: null,
|
|
54095
53861
|
access: "create"
|
|
54096
53862
|
},
|
|
53863
|
+
"deviceState.claimFields": {
|
|
53864
|
+
capName: "device-state",
|
|
53865
|
+
capScope: "system",
|
|
53866
|
+
addonId: null,
|
|
53867
|
+
access: "create"
|
|
53868
|
+
},
|
|
54097
53869
|
"deviceState.getAllSnapshots": {
|
|
54098
53870
|
capName: "device-state",
|
|
54099
53871
|
capScope: "system",
|
|
@@ -54106,12 +53878,36 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
54106
53878
|
addonId: null,
|
|
54107
53879
|
access: "view"
|
|
54108
53880
|
},
|
|
53881
|
+
"deviceState.getNativeCapSlice": {
|
|
53882
|
+
capName: "device-state",
|
|
53883
|
+
capScope: "system",
|
|
53884
|
+
addonId: null,
|
|
53885
|
+
access: "view"
|
|
53886
|
+
},
|
|
54109
53887
|
"deviceState.getSnapshot": {
|
|
54110
53888
|
capName: "device-state",
|
|
54111
53889
|
capScope: "system",
|
|
54112
53890
|
addonId: null,
|
|
54113
53891
|
access: "view"
|
|
54114
53892
|
},
|
|
53893
|
+
"deviceState.listClaims": {
|
|
53894
|
+
capName: "device-state",
|
|
53895
|
+
capScope: "system",
|
|
53896
|
+
addonId: null,
|
|
53897
|
+
access: "view"
|
|
53898
|
+
},
|
|
53899
|
+
"deviceState.patchOwnedFields": {
|
|
53900
|
+
capName: "device-state",
|
|
53901
|
+
capScope: "system",
|
|
53902
|
+
addonId: null,
|
|
53903
|
+
access: "create"
|
|
53904
|
+
},
|
|
53905
|
+
"deviceState.releaseClaim": {
|
|
53906
|
+
capName: "device-state",
|
|
53907
|
+
capScope: "system",
|
|
53908
|
+
addonId: null,
|
|
53909
|
+
access: "create"
|
|
53910
|
+
},
|
|
54115
53911
|
"deviceState.setCapSlice": {
|
|
54116
53912
|
capName: "device-state",
|
|
54117
53913
|
capScope: "system",
|
|
@@ -59962,16 +59758,36 @@ var METHOD_DEVICE_SELECTORS = Object.freeze({
|
|
|
59962
59758
|
form: "single",
|
|
59963
59759
|
optional: false
|
|
59964
59760
|
}],
|
|
59761
|
+
"deviceState.claimFields": [{
|
|
59762
|
+
name: "deviceId",
|
|
59763
|
+
form: "single",
|
|
59764
|
+
optional: false
|
|
59765
|
+
}],
|
|
59965
59766
|
"deviceState.getCapSlice": [{
|
|
59966
59767
|
name: "deviceId",
|
|
59967
59768
|
form: "single",
|
|
59968
59769
|
optional: false
|
|
59969
59770
|
}],
|
|
59771
|
+
"deviceState.getNativeCapSlice": [{
|
|
59772
|
+
name: "deviceId",
|
|
59773
|
+
form: "single",
|
|
59774
|
+
optional: false
|
|
59775
|
+
}],
|
|
59970
59776
|
"deviceState.getSnapshot": [{
|
|
59971
59777
|
name: "deviceId",
|
|
59972
59778
|
form: "single",
|
|
59973
59779
|
optional: false
|
|
59974
59780
|
}],
|
|
59781
|
+
"deviceState.patchOwnedFields": [{
|
|
59782
|
+
name: "deviceId",
|
|
59783
|
+
form: "single",
|
|
59784
|
+
optional: false
|
|
59785
|
+
}],
|
|
59786
|
+
"deviceState.releaseClaim": [{
|
|
59787
|
+
name: "deviceId",
|
|
59788
|
+
form: "single",
|
|
59789
|
+
optional: false
|
|
59790
|
+
}],
|
|
59975
59791
|
"deviceState.setCapSlice": [{
|
|
59976
59792
|
name: "deviceId",
|
|
59977
59793
|
form: "single",
|
|
@@ -61467,8 +61283,12 @@ var SYSTEM_SCOPE_DEVICE_METHODS = [
|
|
|
61467
61283
|
"deviceManager.updateConfig",
|
|
61468
61284
|
"deviceManager.updateDeviceField",
|
|
61469
61285
|
"deviceManager.updateDeviceFieldsBatch",
|
|
61286
|
+
"deviceState.claimFields",
|
|
61470
61287
|
"deviceState.getCapSlice",
|
|
61288
|
+
"deviceState.getNativeCapSlice",
|
|
61471
61289
|
"deviceState.getSnapshot",
|
|
61290
|
+
"deviceState.patchOwnedFields",
|
|
61291
|
+
"deviceState.releaseClaim",
|
|
61472
61292
|
"deviceState.setCapSlice",
|
|
61473
61293
|
"faceGallery.getFaceByTrack",
|
|
61474
61294
|
"faceGallery.listRecentFaces",
|
|
@@ -62148,6 +61968,7 @@ function createSystemProxy(api) {
|
|
|
62148
61968
|
updateComposition: (input) => dispatch("coreBlocks", "updateComposition", "mutation", input),
|
|
62149
61969
|
getComposition: (input) => dispatch("coreBlocks", "getComposition", "query", input),
|
|
62150
61970
|
listCompositions: (input) => dispatch("coreBlocks", "listCompositions", "query", input),
|
|
61971
|
+
getCustomization: (input) => dispatch("coreBlocks", "getCustomization", "query", input),
|
|
62151
61972
|
validateComposition: (input) => dispatch("coreBlocks", "validateComposition", "mutation", input)
|
|
62152
61973
|
},
|
|
62153
61974
|
decoder: {
|
|
@@ -62251,7 +62072,10 @@ function createSystemProxy(api) {
|
|
|
62251
62072
|
createDevice: (input) => dispatch("deviceProvider", "createDevice", "mutation", input),
|
|
62252
62073
|
testCreationField: (input) => dispatch("deviceProvider", "testCreationField", "mutation", input)
|
|
62253
62074
|
},
|
|
62254
|
-
deviceState: {
|
|
62075
|
+
deviceState: {
|
|
62076
|
+
getAllSnapshots: (input) => dispatch("deviceState", "getAllSnapshots", "query", input),
|
|
62077
|
+
listClaims: (input) => dispatch("deviceState", "listClaims", "query", input)
|
|
62078
|
+
},
|
|
62255
62079
|
faceGallery: {
|
|
62256
62080
|
listIdentities: (input) => dispatch("faceGallery", "listIdentities", "query", input),
|
|
62257
62081
|
createIdentity: (input) => dispatch("faceGallery", "createIdentity", "mutation", input),
|
|
@@ -68309,7 +68133,7 @@ exports.AUDIO_BACKEND_CHOICES = AUDIO_BACKEND_CHOICES;
|
|
|
68309
68133
|
exports.AUDIO_CHUNK_FORMATS = require_sleep.AUDIO_CHUNK_FORMATS;
|
|
68310
68134
|
exports.AUDIO_MACRO_LABELS = AUDIO_MACRO_LABELS;
|
|
68311
68135
|
exports.AUDIO_NOT_CLASSIFIED_REASONS = AUDIO_NOT_CLASSIFIED_REASONS;
|
|
68312
|
-
exports.AUDIO_PRESETS =
|
|
68136
|
+
exports.AUDIO_PRESETS = require_composition.AUDIO_PRESETS;
|
|
68313
68137
|
exports.AUDIO_SCALE_EPOCH_ISO = AUDIO_SCALE_EPOCH_ISO;
|
|
68314
68138
|
exports.AUDIO_SCALE_EPOCH_MS = AUDIO_SCALE_EPOCH_MS;
|
|
68315
68139
|
exports.AccessoriesStatusSchema = AccessoriesStatusSchema;
|
|
@@ -68459,13 +68283,17 @@ exports.CLUSTER_MODEL_SECTION_ID = CLUSTER_MODEL_SECTION_ID;
|
|
|
68459
68283
|
exports.CLUSTER_STEP_SETTING_FIELDS = CLUSTER_STEP_SETTING_FIELDS;
|
|
68460
68284
|
exports.COCO_80_LABELS = COCO_80_LABELS;
|
|
68461
68285
|
exports.COCO_TO_MACRO = COCO_TO_MACRO;
|
|
68462
|
-
exports.COMPOSED_DEVICE_STABLE_ID_PREFIX = COMPOSED_DEVICE_STABLE_ID_PREFIX;
|
|
68463
|
-
exports.
|
|
68286
|
+
exports.COMPOSED_DEVICE_STABLE_ID_PREFIX = require_composition.COMPOSED_DEVICE_STABLE_ID_PREFIX;
|
|
68287
|
+
exports.COMPOSED_ITEM_FIXED_FIELDS = COMPOSED_ITEM_FIXED_FIELDS;
|
|
68288
|
+
exports.COMPOSER_ADDON_ID = require_composition.COMPOSER_ADDON_ID;
|
|
68464
68289
|
exports.COMPOSER_RUNNER_ID = COMPOSER_RUNNER_ID;
|
|
68290
|
+
exports.COMPOSITION_ITEM_ARRAY_PATH = require_composition.COMPOSITION_ITEM_ARRAY_PATH;
|
|
68291
|
+
exports.COMPOSITION_ITEM_KEY_RE = require_composition.COMPOSITION_ITEM_KEY_RE;
|
|
68465
68292
|
exports.CONNECTION_TEST_TIMEOUT_MS = CONNECTION_TEST_TIMEOUT_MS;
|
|
68466
68293
|
exports.CONTAINER_CHILD_PRIORITY = CONTAINER_CHILD_PRIORITY;
|
|
68467
68294
|
exports.CORE_BLOCKS_ADDON_ID = CORE_BLOCKS_ADDON_ID;
|
|
68468
68295
|
exports.CORE_BLOCK_ADDON_PREFIX = CORE_BLOCK_ADDON_PREFIX;
|
|
68296
|
+
exports.CUSTOMIZATION_BLOCK_NAME_PREFIX = require_composition.CUSTOMIZATION_BLOCK_NAME_PREFIX;
|
|
68469
68297
|
exports.CamProfileSchema = require_sleep.CamProfileSchema;
|
|
68470
68298
|
exports.CamStreamDescriptorSchema = CamStreamDescriptorSchema;
|
|
68471
68299
|
exports.CamStreamKindSchema = require_sleep.CamStreamKindSchema;
|
|
@@ -68505,7 +68333,9 @@ exports.CandidateQueryFilterSchema = CandidateQueryFilterSchema;
|
|
|
68505
68333
|
exports.CapScopeSchema = CapScopeSchema;
|
|
68506
68334
|
exports.CapabilityBindingsSchema = CapabilityBindingsSchema;
|
|
68507
68335
|
exports.CarbonMonoxideStatusSchema = CarbonMonoxideStatusSchema;
|
|
68508
|
-
exports.ChargingStatus =
|
|
68336
|
+
exports.ChargingStatus = require_err_msg.ChargingStatus;
|
|
68337
|
+
exports.ClaimOutcomeSchema = ClaimOutcomeSchema;
|
|
68338
|
+
exports.ClaimsListingSchema = ClaimsListingSchema;
|
|
68509
68339
|
exports.ClientNetworkStatsSchema = ClientNetworkStatsSchema;
|
|
68510
68340
|
exports.ClimateControlStatusSchema = ClimateControlStatusSchema;
|
|
68511
68341
|
exports.ClipBytesOfferSchema = ClipBytesOfferSchema;
|
|
@@ -68535,37 +68365,42 @@ exports.ComposerApplyResultSchema = ComposerApplyResultSchema;
|
|
|
68535
68365
|
exports.ComposerBlockSchema = ComposerBlockSchema;
|
|
68536
68366
|
exports.ComposerBlockVerdictSchema = ComposerBlockVerdictSchema;
|
|
68537
68367
|
exports.ComposerUnreadableBlockSchema = ComposerUnreadableBlockSchema;
|
|
68538
|
-
exports.CompositionBindingSchema = CompositionBindingSchema;
|
|
68368
|
+
exports.CompositionBindingSchema = require_composition.CompositionBindingSchema;
|
|
68539
68369
|
exports.CompositionBlockInputSchema = CompositionBlockInputSchema;
|
|
68540
68370
|
exports.CompositionBlockStateSchema = CompositionBlockStateSchema;
|
|
68541
|
-
exports.CompositionCodeSourceSchema = CompositionCodeSourceSchema;
|
|
68542
|
-
exports.CompositionCommandTargetSchema = CompositionCommandTargetSchema;
|
|
68543
|
-
exports.
|
|
68544
|
-
exports.
|
|
68545
|
-
exports.
|
|
68371
|
+
exports.CompositionCodeSourceSchema = require_composition.CompositionCodeSourceSchema;
|
|
68372
|
+
exports.CompositionCommandTargetSchema = require_composition.CompositionCommandTargetSchema;
|
|
68373
|
+
exports.CompositionCustomizationReadSchema = CompositionCustomizationReadSchema;
|
|
68374
|
+
exports.CompositionExistingTargetSchema = require_composition.CompositionExistingTargetSchema;
|
|
68375
|
+
exports.CompositionExpressionSourceSchema = require_composition.CompositionExpressionSourceSchema;
|
|
68376
|
+
exports.CompositionFeatureModeSchema = CompositionFeatureModeSchema;
|
|
68377
|
+
exports.CompositionFeaturePlanSchema = CompositionFeaturePlanSchema;
|
|
68378
|
+
exports.CompositionFeatureSchema = require_composition.CompositionFeatureSchema;
|
|
68379
|
+
exports.CompositionFieldItemSchema = CompositionFieldItemSchema;
|
|
68546
68380
|
exports.CompositionFieldKindSchema = CompositionFieldKindSchema;
|
|
68547
68381
|
exports.CompositionFieldPlanSchema = CompositionFieldPlanSchema;
|
|
68548
|
-
exports.CompositionFieldReadSchema = CompositionFieldReadSchema;
|
|
68382
|
+
exports.CompositionFieldReadSchema = require_composition.CompositionFieldReadSchema;
|
|
68549
68383
|
exports.CompositionFieldRoleSchema = CompositionFieldRoleSchema;
|
|
68550
|
-
exports.CompositionFieldSourceSchema = CompositionFieldSourceSchema;
|
|
68384
|
+
exports.CompositionFieldSourceSchema = require_composition.CompositionFieldSourceSchema;
|
|
68551
68385
|
exports.CompositionFieldStateKindSchema = CompositionFieldStateKindSchema;
|
|
68552
68386
|
exports.CompositionFieldStateSchema = CompositionFieldStateSchema;
|
|
68553
|
-
exports.CompositionFieldsFeatureSchema = CompositionFieldsFeatureSchema;
|
|
68554
|
-
exports.CompositionForwardCommandSchema = CompositionForwardCommandSchema;
|
|
68555
|
-
exports.CompositionFromSourceSchema = CompositionFromSourceSchema;
|
|
68387
|
+
exports.CompositionFieldsFeatureSchema = require_composition.CompositionFieldsFeatureSchema;
|
|
68388
|
+
exports.CompositionForwardCommandSchema = require_composition.CompositionForwardCommandSchema;
|
|
68389
|
+
exports.CompositionFromSourceSchema = require_composition.CompositionFromSourceSchema;
|
|
68556
68390
|
exports.CompositionGetReadSchema = CompositionGetReadSchema;
|
|
68557
68391
|
exports.CompositionGraphError = CompositionGraphError;
|
|
68392
|
+
exports.CompositionItemEntrySchema = require_composition.CompositionItemEntrySchema;
|
|
68558
68393
|
exports.CompositionListReadSchema = CompositionListReadSchema;
|
|
68559
|
-
exports.CompositionLiteralBindingSchema = CompositionLiteralBindingSchema;
|
|
68560
|
-
exports.CompositionNewTargetSchema = CompositionNewTargetSchema;
|
|
68561
|
-
exports.CompositionPassthroughFeatureSchema = CompositionPassthroughFeatureSchema;
|
|
68394
|
+
exports.CompositionLiteralBindingSchema = require_composition.CompositionLiteralBindingSchema;
|
|
68395
|
+
exports.CompositionNewTargetSchema = require_composition.CompositionNewTargetSchema;
|
|
68396
|
+
exports.CompositionPassthroughFeatureSchema = require_composition.CompositionPassthroughFeatureSchema;
|
|
68562
68397
|
exports.CompositionProblemCodeSchema = CompositionProblemCodeSchema;
|
|
68563
68398
|
exports.CompositionProblemSchema = CompositionProblemSchema;
|
|
68564
68399
|
exports.CompositionRunStatusSchema = CompositionRunStatusSchema;
|
|
68565
|
-
exports.CompositionSchema = CompositionSchema;
|
|
68566
|
-
exports.CompositionSourceRefSchema = CompositionSourceRefSchema;
|
|
68400
|
+
exports.CompositionSchema = require_composition.CompositionSchema;
|
|
68401
|
+
exports.CompositionSourceRefSchema = require_composition.CompositionSourceRefSchema;
|
|
68567
68402
|
exports.CompositionStateReadSchema = CompositionStateReadSchema;
|
|
68568
|
-
exports.CompositionTargetSchema = CompositionTargetSchema;
|
|
68403
|
+
exports.CompositionTargetSchema = require_composition.CompositionTargetSchema;
|
|
68569
68404
|
exports.CompositionUnavailableModeSchema = CompositionUnavailableModeSchema;
|
|
68570
68405
|
exports.CompositionValidationSchema = CompositionValidationSchema;
|
|
68571
68406
|
exports.CompositionViewSchema = CompositionViewSchema;
|
|
@@ -68577,6 +68412,8 @@ exports.ConnectionTestInputSchema = ConnectionTestInputSchema;
|
|
|
68577
68412
|
exports.ConnectionTestOutcomeSchema = ConnectionTestOutcomeSchema;
|
|
68578
68413
|
exports.ConnectivityStatusSchema = ConnectivityStatusSchema;
|
|
68579
68414
|
exports.ConsumableItemSchema = ConsumableItemSchema;
|
|
68415
|
+
exports.ConsumableRemainingSchema = ConsumableRemainingSchema;
|
|
68416
|
+
exports.ConsumableRemainingUnitSchema = ConsumableRemainingUnitSchema;
|
|
68580
68417
|
exports.ConsumablesStatusSchema = ConsumablesStatusSchema;
|
|
68581
68418
|
exports.ContactStatusSchema = ContactStatusSchema;
|
|
68582
68419
|
exports.ControlKindSchema = ControlKindSchema;
|
|
@@ -68670,15 +68507,15 @@ exports.DeviceDiscoveryStatusSchema = DeviceDiscoveryStatusSchema;
|
|
|
68670
68507
|
exports.DeviceExportExposeInputSchema = ExposeInputSchema;
|
|
68671
68508
|
exports.DeviceExportStatusSchema = DeviceExportStatusSchema;
|
|
68672
68509
|
exports.DeviceExportUnexposeInputSchema = UnexposeInputSchema;
|
|
68673
|
-
exports.DeviceFeature =
|
|
68510
|
+
exports.DeviceFeature = require_err_msg.DeviceFeature;
|
|
68674
68511
|
exports.DeviceInfoSchema = DeviceInfoSchema;
|
|
68675
68512
|
exports.DeviceNetworkStatsSchema = DeviceNetworkStatsSchema;
|
|
68676
68513
|
exports.DeviceRestoreRetryScheduler = DeviceRestoreRetryScheduler;
|
|
68677
|
-
exports.DeviceRole =
|
|
68514
|
+
exports.DeviceRole = require_err_msg.DeviceRole;
|
|
68678
68515
|
exports.DeviceRuntimeState = DeviceRuntimeState;
|
|
68679
68516
|
exports.DeviceSelectorSchema = DeviceSelectorSchema;
|
|
68680
68517
|
exports.DeviceStatusSchema = DeviceStatusSchema;
|
|
68681
|
-
exports.DeviceType =
|
|
68518
|
+
exports.DeviceType = require_err_msg.DeviceType;
|
|
68682
68519
|
exports.DiagnosticIdSchema = DiagnosticIdSchema;
|
|
68683
68520
|
exports.DiagnosticWindowPatchSchema = DiagnosticWindowPatchSchema;
|
|
68684
68521
|
exports.DiagnosticWindowSchema = DiagnosticWindowSchema;
|
|
@@ -68701,11 +68538,11 @@ exports.EVENT_OWNER_TYPES = EVENT_OWNER_TYPES;
|
|
|
68701
68538
|
exports.EVENT_PAD_MS = EVENT_PAD_MS;
|
|
68702
68539
|
exports.EVENT_TAXONOMY = EVENT_TAXONOMY;
|
|
68703
68540
|
exports.EXPORT_DENSE_MAX_RANGES = EXPORT_DENSE_MAX_RANGES;
|
|
68704
|
-
exports.EXPRESSION_BUILTINS = EXPRESSION_BUILTINS;
|
|
68705
|
-
exports.EXPRESSION_BUILTIN_NAMES = EXPRESSION_BUILTIN_NAMES;
|
|
68706
|
-
exports.EXPRESSION_COMPILE_CACHE_CAPACITY = EXPRESSION_COMPILE_CACHE_CAPACITY;
|
|
68707
|
-
exports.EXPRESSION_IDENTIFIER_RE = EXPRESSION_IDENTIFIER_RE;
|
|
68708
|
-
exports.EXPRESSION_INJECTED_NOW = EXPRESSION_INJECTED_NOW;
|
|
68541
|
+
exports.EXPRESSION_BUILTINS = require_composition.EXPRESSION_BUILTINS;
|
|
68542
|
+
exports.EXPRESSION_BUILTIN_NAMES = require_composition.EXPRESSION_BUILTIN_NAMES;
|
|
68543
|
+
exports.EXPRESSION_COMPILE_CACHE_CAPACITY = require_composition.EXPRESSION_COMPILE_CACHE_CAPACITY;
|
|
68544
|
+
exports.EXPRESSION_IDENTIFIER_RE = require_composition.EXPRESSION_IDENTIFIER_RE;
|
|
68545
|
+
exports.EXPRESSION_INJECTED_NOW = require_composition.EXPRESSION_INJECTED_NOW;
|
|
68709
68546
|
exports.EXTENSION_HOST_DEVICE_TYPES = EXTENSION_HOST_DEVICE_TYPES;
|
|
68710
68547
|
exports.EXTENSION_SLOTS = EXTENSION_SLOTS;
|
|
68711
68548
|
exports.EXTENSION_SLOT_IDS = EXTENSION_SLOT_IDS;
|
|
@@ -68761,11 +68598,11 @@ exports.ExportTimelapseSchema = ExportTimelapseSchema;
|
|
|
68761
68598
|
exports.ExposedDeviceSchema = ExposedDeviceSchema;
|
|
68762
68599
|
exports.ExposureModeSchema = ExposureModeSchema;
|
|
68763
68600
|
exports.ExpressionBindingSourceSchema = ExpressionBindingSourceSchema;
|
|
68764
|
-
exports.ExpressionEvalError = ExpressionEvalError;
|
|
68601
|
+
exports.ExpressionEvalError = require_composition.ExpressionEvalError;
|
|
68765
68602
|
exports.ExpressionFieldBindingSchema = ExpressionFieldBindingSchema;
|
|
68766
68603
|
exports.ExpressionGlobalBindingSchema = ExpressionGlobalBindingSchema;
|
|
68767
68604
|
exports.ExpressionLiteralBindingSchema = ExpressionLiteralBindingSchema;
|
|
68768
|
-
exports.ExpressionParseError = ExpressionParseError;
|
|
68605
|
+
exports.ExpressionParseError = require_composition.ExpressionParseError;
|
|
68769
68606
|
exports.ExpressionSourceSchema = ExpressionSourceSchema;
|
|
68770
68607
|
exports.ExtensionCandidateSchema = ExtensionCandidateSchema;
|
|
68771
68608
|
exports.ExtensionCandidatesSchema = ExtensionCandidatesSchema;
|
|
@@ -68782,6 +68619,8 @@ exports.ExtensionSlotValueSchema = ExtensionSlotValueSchema;
|
|
|
68782
68619
|
exports.ExtensionSourceAvailabilitySchema = ExtensionSourceAvailabilitySchema;
|
|
68783
68620
|
exports.ExtensionSourceHealthSchema = ExtensionSourceHealthSchema;
|
|
68784
68621
|
exports.ExtensionSourceSchema = ExtensionSourceSchema;
|
|
68622
|
+
exports.FEEDER_EXAMPLE_ADDON_ID = FEEDER_EXAMPLE_ADDON_ID;
|
|
68623
|
+
exports.FEEDER_EXAMPLE_HA_DEVICE_ID = FEEDER_EXAMPLE_HA_DEVICE_ID;
|
|
68785
68624
|
exports.FIRST_LEVEL_MACRO_CLASSES = FIRST_LEVEL_MACRO_CLASSES;
|
|
68786
68625
|
exports.FULL_IMAGE_BBOX = FULL_IMAGE_BBOX;
|
|
68787
68626
|
exports.FailureContributionSchema = FailureContributionSchema;
|
|
@@ -68791,8 +68630,10 @@ exports.FanControlStatusSchema = FanControlStatusSchema;
|
|
|
68791
68630
|
exports.FanDirectionSchema = FanDirectionSchema;
|
|
68792
68631
|
exports.FeatureManifestSchema = FeatureManifestSchema;
|
|
68793
68632
|
exports.FeatureProbeStatusSchema = FeatureProbeStatusSchema;
|
|
68633
|
+
exports.FieldClaimModeSchema = FieldClaimModeSchema;
|
|
68634
|
+
exports.FieldClaimSchema = FieldClaimSchema;
|
|
68794
68635
|
exports.FloodStatusSchema = FloodStatusSchema;
|
|
68795
|
-
exports.Fmp4BoxSplitter =
|
|
68636
|
+
exports.Fmp4BoxSplitter = require_composition.Fmp4BoxSplitter;
|
|
68796
68637
|
exports.FrameHandleFormatSchema = require_sleep.FrameHandleFormatSchema;
|
|
68797
68638
|
exports.FrameHandleSchema = require_sleep.FrameHandleSchema;
|
|
68798
68639
|
exports.FrameInputSchema = FrameInputSchema;
|
|
@@ -68921,16 +68762,17 @@ exports.MAX_BIRTH_DECISION_RECORDS = MAX_BIRTH_DECISION_RECORDS;
|
|
|
68921
68762
|
exports.MAX_BIRTH_LATENCY_PROXY_MS = MAX_BIRTH_LATENCY_PROXY_MS;
|
|
68922
68763
|
exports.MAX_CLIP_EVENT_IDS = MAX_CLIP_EVENT_IDS;
|
|
68923
68764
|
exports.MAX_CLIP_LABELS = MAX_CLIP_LABELS;
|
|
68924
|
-
exports.MAX_COMPOSITION_CODE_LENGTH = MAX_COMPOSITION_CODE_LENGTH;
|
|
68925
|
-
exports.MAX_COMPOSITION_FEATURES = MAX_COMPOSITION_FEATURES;
|
|
68926
|
-
exports.MAX_COMPOSITION_FIELDS_PER_FEATURE = MAX_COMPOSITION_FIELDS_PER_FEATURE;
|
|
68765
|
+
exports.MAX_COMPOSITION_CODE_LENGTH = require_composition.MAX_COMPOSITION_CODE_LENGTH;
|
|
68766
|
+
exports.MAX_COMPOSITION_FEATURES = require_composition.MAX_COMPOSITION_FEATURES;
|
|
68767
|
+
exports.MAX_COMPOSITION_FIELDS_PER_FEATURE = require_composition.MAX_COMPOSITION_FIELDS_PER_FEATURE;
|
|
68768
|
+
exports.MAX_COMPOSITION_ITEMS = require_composition.MAX_COMPOSITION_ITEMS;
|
|
68927
68769
|
exports.MAX_CONDITION_DEPTH = MAX_CONDITION_DEPTH;
|
|
68928
68770
|
exports.MAX_CONDITION_LEAVES = MAX_CONDITION_LEAVES;
|
|
68929
|
-
exports.MAX_EXPRESSION_AST_NODES = MAX_EXPRESSION_AST_NODES;
|
|
68930
|
-
exports.MAX_EXPRESSION_BINDINGS = MAX_EXPRESSION_BINDINGS;
|
|
68931
|
-
exports.MAX_EXPRESSION_CALL_ARGS = MAX_EXPRESSION_CALL_ARGS;
|
|
68932
|
-
exports.MAX_EXPRESSION_EVAL_STEPS = MAX_EXPRESSION_EVAL_STEPS;
|
|
68933
|
-
exports.MAX_EXPRESSION_SOURCE_LENGTH = MAX_EXPRESSION_SOURCE_LENGTH;
|
|
68771
|
+
exports.MAX_EXPRESSION_AST_NODES = require_composition.MAX_EXPRESSION_AST_NODES;
|
|
68772
|
+
exports.MAX_EXPRESSION_BINDINGS = require_composition.MAX_EXPRESSION_BINDINGS;
|
|
68773
|
+
exports.MAX_EXPRESSION_CALL_ARGS = require_composition.MAX_EXPRESSION_CALL_ARGS;
|
|
68774
|
+
exports.MAX_EXPRESSION_EVAL_STEPS = require_composition.MAX_EXPRESSION_EVAL_STEPS;
|
|
68775
|
+
exports.MAX_EXPRESSION_SOURCE_LENGTH = require_composition.MAX_EXPRESSION_SOURCE_LENGTH;
|
|
68934
68776
|
exports.MAX_KEYS = MAX_KEYS;
|
|
68935
68777
|
exports.MAX_REASONS_PER_KEY = MAX_REASONS_PER_KEY;
|
|
68936
68778
|
exports.MAX_SENSOR_TRIGGER_DEVICES = MAX_SENSOR_TRIGGER_DEVICES;
|
|
@@ -69186,6 +69028,7 @@ exports.OPS_LOG_DEFAULT_LIMIT = OPS_LOG_DEFAULT_LIMIT;
|
|
|
69186
69028
|
exports.OPS_LOG_RING_DEFAULT_MAX = OPS_LOG_RING_DEFAULT_MAX;
|
|
69187
69029
|
exports.OVERFLOW_REASON = OVERFLOW_REASON;
|
|
69188
69030
|
exports.OVERLAY_LAYERS = OVERLAY_LAYERS;
|
|
69031
|
+
exports.OWNER_ONLY_EVENT_CATEGORIES = require_event_category.OWNER_ONLY_EVENT_CATEGORIES;
|
|
69189
69032
|
exports.OauthIntegrationDescriptorSchema = OauthIntegrationDescriptorSchema;
|
|
69190
69033
|
exports.ObjectEventSchema = ObjectEventSchema;
|
|
69191
69034
|
exports.OccupancyBurstRefusalSchema = OccupancyBurstRefusalSchema;
|
|
@@ -69216,6 +69059,7 @@ exports.PEER_BYTES_CHUNK_BYTES = require_sleep.PEER_BYTES_CHUNK_BYTES;
|
|
|
69216
69059
|
exports.PEER_BYTES_MAX_OUTSTANDING = require_sleep.PEER_BYTES_MAX_OUTSTANDING;
|
|
69217
69060
|
exports.PEER_BYTES_STALL_MS = require_sleep.PEER_BYTES_STALL_MS;
|
|
69218
69061
|
exports.PEER_BYTES_TICKET_TTL_MS = require_sleep.PEER_BYTES_TICKET_TTL_MS;
|
|
69062
|
+
exports.PENDING_BLOCK_ID = PENDING_BLOCK_ID;
|
|
69219
69063
|
exports.PET_FEEDER_MANUAL_FEED_MAX = PET_FEEDER_MANUAL_FEED_MAX;
|
|
69220
69064
|
exports.PET_FEEDER_MANUAL_FEED_MIN = PET_FEEDER_MANUAL_FEED_MIN;
|
|
69221
69065
|
exports.PIPELINE_ANALYTICS_CAP_NAME = PIPELINE_ANALYTICS_CAP_NAME;
|
|
@@ -69287,7 +69131,7 @@ exports.RECORDING_SOURCE_CAMSTACK = RECORDING_SOURCE_CAMSTACK;
|
|
|
69287
69131
|
exports.RECORDING_SOURCE_CAMSTACK_ADDON = RECORDING_SOURCE_CAMSTACK_ADDON;
|
|
69288
69132
|
exports.RECORDING_TIMELINE_BATCH_MAX = RECORDING_TIMELINE_BATCH_MAX;
|
|
69289
69133
|
exports.REDACTED_SECRET = REDACTED_SECRET;
|
|
69290
|
-
exports.RESERVED_BINDING_NAMES = RESERVED_BINDING_NAMES;
|
|
69134
|
+
exports.RESERVED_BINDING_NAMES = require_composition.RESERVED_BINDING_NAMES;
|
|
69291
69135
|
exports.RESERVED_COLLECTION_SOURCES = RESERVED_COLLECTION_SOURCES;
|
|
69292
69136
|
exports.RESTORED_CAP_NAMES = RESTORED_CAP_NAMES;
|
|
69293
69137
|
exports.ROOM_EXAMPLE_MOTION_SOURCES = ROOM_EXAMPLE_MOTION_SOURCES;
|
|
@@ -69675,7 +69519,7 @@ exports.audioLabelChoices = audioLabelChoices;
|
|
|
69675
69519
|
exports.audioMetricsCapability = audioMetricsCapability;
|
|
69676
69520
|
exports.audioModeOf = audioModeOf;
|
|
69677
69521
|
exports.audioOrDefaults = audioOrDefaults;
|
|
69678
|
-
exports.audioPlanFromEncodeProfile =
|
|
69522
|
+
exports.audioPlanFromEncodeProfile = require_composition.audioPlanFromEncodeProfile;
|
|
69679
69523
|
exports.audioScaleEraOf = audioScaleEraOf;
|
|
69680
69524
|
exports.authProviderCapability = authProviderCapability;
|
|
69681
69525
|
exports.autoAssignProfiles = autoAssignProfiles;
|
|
@@ -69689,17 +69533,17 @@ exports.bindAddonActions = bindAddonActions;
|
|
|
69689
69533
|
exports.brightnessCapability = brightnessCapability;
|
|
69690
69534
|
exports.brokerCapability = brokerCapability;
|
|
69691
69535
|
exports.buildAddonRouteProvider = buildAddonRouteProvider;
|
|
69692
|
-
exports.buildAudioArgs =
|
|
69536
|
+
exports.buildAudioArgs = require_composition.buildAudioArgs;
|
|
69693
69537
|
exports.buildCapabilityLookup = buildCapabilityLookup;
|
|
69694
69538
|
exports.buildDeviceAdminUrl = buildDeviceAdminUrl;
|
|
69695
69539
|
exports.buildEventKindDescriptor = buildEventKindDescriptor;
|
|
69696
|
-
exports.buildFfmpegArgs =
|
|
69697
|
-
exports.buildInputArgs =
|
|
69540
|
+
exports.buildFfmpegArgs = require_composition.buildFfmpegArgs;
|
|
69541
|
+
exports.buildInputArgs = require_composition.buildInputArgs;
|
|
69698
69542
|
exports.buildModelVariantGroups = buildModelVariantGroups;
|
|
69699
69543
|
exports.buildNcTaxonomy = buildNcTaxonomy;
|
|
69700
69544
|
exports.buildRoleScopes = buildRoleScopes;
|
|
69701
69545
|
exports.buildStreamParamsConfigSchema = buildStreamParamsConfigSchema;
|
|
69702
|
-
exports.buildVideoArgs =
|
|
69546
|
+
exports.buildVideoArgs = require_composition.buildVideoArgs;
|
|
69703
69547
|
exports.buttonCapability = buttonCapability;
|
|
69704
69548
|
exports.cameraCredentialsCapability = cameraCredentialsCapability;
|
|
69705
69549
|
exports.cameraGridLayoutCapability = cameraGridLayoutCapability;
|
|
@@ -69727,16 +69571,18 @@ exports.colorCapability = colorCapability;
|
|
|
69727
69571
|
exports.colorForKind = colorForKind;
|
|
69728
69572
|
exports.commitWatchdogRestart = commitWatchdogRestart;
|
|
69729
69573
|
exports.compareToTarget = compareToTarget;
|
|
69730
|
-
exports.compileExpression = compileExpression;
|
|
69731
|
-
exports.compileExpressionSafe = compileExpressionSafe;
|
|
69574
|
+
exports.compileExpression = require_composition.compileExpression;
|
|
69575
|
+
exports.compileExpressionSafe = require_composition.compileExpressionSafe;
|
|
69732
69576
|
exports.composeSwitchedOff = composeSwitchedOff;
|
|
69733
|
-
exports.composedDeviceRef = composedDeviceRef;
|
|
69734
|
-
exports.composedDeviceStableId = composedDeviceStableId;
|
|
69577
|
+
exports.composedDeviceRef = require_composition.composedDeviceRef;
|
|
69578
|
+
exports.composedDeviceStableId = require_composition.composedDeviceStableId;
|
|
69735
69579
|
exports.composerCapability = composerCapability;
|
|
69736
69580
|
exports.compositionEdges = compositionEdges;
|
|
69737
|
-
exports.
|
|
69738
|
-
exports.
|
|
69739
|
-
exports.
|
|
69581
|
+
exports.compositionFieldKey = compositionFieldKey;
|
|
69582
|
+
exports.compositionItemFieldPath = require_composition.compositionItemFieldPath;
|
|
69583
|
+
exports.compositionReadKey = require_composition.compositionReadKey;
|
|
69584
|
+
exports.compositionSliceKey = require_composition.compositionSliceKey;
|
|
69585
|
+
exports.compositionSourceKey = require_composition.compositionSourceKey;
|
|
69740
69586
|
exports.compositionSourceRefs = compositionSourceRefs;
|
|
69741
69587
|
exports.conditionDepth = conditionDepth;
|
|
69742
69588
|
exports.conditionExclusionReason = conditionExclusionReason;
|
|
@@ -69761,7 +69607,7 @@ exports.createDeviceProxy = require_sleep.createDeviceProxy;
|
|
|
69761
69607
|
exports.createDurableState = require_sleep.createDurableState;
|
|
69762
69608
|
exports.createEvent = require_sleep.createEvent;
|
|
69763
69609
|
exports.createEventBusSliceSource = require_sleep.createEventBusSliceSource;
|
|
69764
|
-
exports.createExpressionScope = createExpressionScope;
|
|
69610
|
+
exports.createExpressionScope = require_composition.createExpressionScope;
|
|
69765
69611
|
exports.createHwAccelCache = createHwAccelCache;
|
|
69766
69612
|
exports.createLazyTrpcSource = require_sleep.createLazyTrpcSource;
|
|
69767
69613
|
exports.createLogChannelsProvider = createLogChannelsProvider;
|
|
@@ -69832,9 +69678,9 @@ exports.enumerateInferenceDevices = enumerateInferenceDevices;
|
|
|
69832
69678
|
exports.enumerateItemArrayFields = enumerateItemArrayFields;
|
|
69833
69679
|
exports.enumerateSchemaFields = enumerateSchemaFields;
|
|
69834
69680
|
exports.errMsg = require_err_msg.errMsg;
|
|
69835
|
-
exports.evaluateAst = evaluateAst;
|
|
69681
|
+
exports.evaluateAst = require_composition.evaluateAst;
|
|
69836
69682
|
exports.evaluateCompositionField = evaluateCompositionField;
|
|
69837
|
-
exports.evaluateExpressionSource = evaluateExpressionSource;
|
|
69683
|
+
exports.evaluateExpressionSource = require_composition.evaluateExpressionSource;
|
|
69838
69684
|
exports.evaluatePoolMemory = evaluatePoolMemory;
|
|
69839
69685
|
exports.evaluateSensorEdge = evaluateSensorEdge;
|
|
69840
69686
|
exports.evaluateZoneRules = evaluateZoneRules;
|
|
@@ -69853,9 +69699,14 @@ exports.faceGalleryCapability = faceGalleryCapability;
|
|
|
69853
69699
|
exports.failureContributionCapability = failureContributionCapability;
|
|
69854
69700
|
exports.failureRate = failureRate;
|
|
69855
69701
|
exports.fanControlCapability = fanControlCapability;
|
|
69702
|
+
exports.featureFieldEntries = require_composition.featureFieldEntries;
|
|
69856
69703
|
exports.featureProbeCapability = featureProbeCapability;
|
|
69857
|
-
exports.
|
|
69704
|
+
exports.featureSources = require_composition.featureSources;
|
|
69705
|
+
exports.feederCustomizationExample = feederCustomizationExample;
|
|
69706
|
+
exports.feederExampleSources = feederExampleSources;
|
|
69707
|
+
exports.fieldReads = require_composition.fieldReads;
|
|
69858
69708
|
exports.filesystemBrowseCapability = filesystemBrowseCapability;
|
|
69709
|
+
exports.findCandidateCycles = findCandidateCycles;
|
|
69859
69710
|
exports.findCompositionCycles = findCompositionCycles;
|
|
69860
69711
|
exports.findTimezone = findTimezone;
|
|
69861
69712
|
exports.floodCapability = floodCapability;
|
|
@@ -69887,7 +69738,7 @@ exports.integrationsCapability = integrationsCapability;
|
|
|
69887
69738
|
exports.intercomCapability = intercomCapability;
|
|
69888
69739
|
exports.interpretStoredClusterModel = interpretStoredClusterModel;
|
|
69889
69740
|
exports.invalidClusterStepModelEntries = invalidClusterStepModelEntries;
|
|
69890
|
-
exports.invocationFromEncodeProfile =
|
|
69741
|
+
exports.invocationFromEncodeProfile = require_composition.invocationFromEncodeProfile;
|
|
69891
69742
|
exports.isAgentOnlyPlacement = isAgentOnlyPlacement;
|
|
69892
69743
|
exports.isArrayOutputSchema = isArrayOutputSchema;
|
|
69893
69744
|
exports.isAudioChunkFormat = require_sleep.isAudioChunkFormat;
|
|
@@ -69914,11 +69765,12 @@ exports.isMediaOwnerType = isMediaOwnerType;
|
|
|
69914
69765
|
exports.isNode = isNode;
|
|
69915
69766
|
exports.isObjectInput = isObjectInput;
|
|
69916
69767
|
exports.isOccupancyRule = isOccupancyRule;
|
|
69768
|
+
exports.isOwnerOnlyEventCategory = require_event_category.isOwnerOnlyEventCategory;
|
|
69917
69769
|
exports.isRestoredCap = isRestoredCap;
|
|
69918
69770
|
exports.isSameAddonId = isSameAddonId;
|
|
69919
69771
|
exports.isScheduleActive = isScheduleActive;
|
|
69920
69772
|
exports.isSecretConfigField = isSecretConfigField;
|
|
69921
|
-
exports.isSoftwareDecode =
|
|
69773
|
+
exports.isSoftwareDecode = require_composition.isSoftwareDecode;
|
|
69922
69774
|
exports.isSourceCap = isSourceCap;
|
|
69923
69775
|
exports.isStorageLocationMode = isStorageLocationMode;
|
|
69924
69776
|
exports.isSystemDelivery = isSystemDelivery;
|
|
@@ -69947,7 +69799,7 @@ exports.localNetworkCapability = localNetworkCapability;
|
|
|
69947
69799
|
exports.locationIconKey = locationIconKey;
|
|
69948
69800
|
exports.locationSimilarity = locationSimilarity;
|
|
69949
69801
|
exports.lockControlCapability = lockControlCapability;
|
|
69950
|
-
exports.logBannerArgs =
|
|
69802
|
+
exports.logBannerArgs = require_composition.logBannerArgs;
|
|
69951
69803
|
exports.logChannelsCapability = logChannelsCapability;
|
|
69952
69804
|
exports.logDestinationCapability = logDestinationCapability;
|
|
69953
69805
|
exports.logLevelAtMost = logLevelAtMost;
|
|
@@ -70000,6 +69852,7 @@ exports.oauthIntegrationCapability = oauthIntegrationCapability;
|
|
|
70000
69852
|
exports.objectInputDeclaresAddonId = objectInputDeclaresAddonId;
|
|
70001
69853
|
exports.objectInputDeclaresRequiredDeviceId = objectInputDeclaresRequiredDeviceId;
|
|
70002
69854
|
exports.occupancyScope = occupancyScope;
|
|
69855
|
+
exports.optionalAncestors = optionalAncestors;
|
|
70003
69856
|
exports.optionalMethod = require_sleep.optionalMethod;
|
|
70004
69857
|
exports.osdCapability = osdCapability;
|
|
70005
69858
|
exports.osdManagerCapability = osdManagerCapability;
|
|
@@ -70007,7 +69860,7 @@ exports.overlayClusterStepSettings = overlayClusterStepSettings;
|
|
|
70007
69860
|
exports.packagePickerItems = packagePickerItems;
|
|
70008
69861
|
exports.parseAdminBase = parseAdminBase;
|
|
70009
69862
|
exports.parseCameraStreamConfig = parseCameraStreamConfig;
|
|
70010
|
-
exports.parseExpression = parseExpression;
|
|
69863
|
+
exports.parseExpression = require_composition.parseExpression;
|
|
70011
69864
|
exports.parseJsonArray = require_sleep.parseJsonArray;
|
|
70012
69865
|
exports.parseJsonObject = require_sleep.parseJsonObject;
|
|
70013
69866
|
exports.parseJsonUnknown = require_sleep.parseJsonUnknown;
|
|
@@ -70027,13 +69880,14 @@ exports.pickInvalidClusterStepModels = pickInvalidClusterStepModels;
|
|
|
70027
69880
|
exports.pickNativeLeaseOverride = pickNativeLeaseOverride;
|
|
70028
69881
|
exports.pickPreferredRtspEntry = pickPreferredRtspEntry;
|
|
70029
69882
|
exports.pickRestartCandidate = pickRestartCandidate;
|
|
70030
|
-
exports.pickVideoEncoder =
|
|
69883
|
+
exports.pickVideoEncoder = require_composition.pickVideoEncoder;
|
|
70031
69884
|
exports.pickerForCondition = pickerForCondition;
|
|
70032
69885
|
exports.pipelineAnalyticsCapability = pipelineAnalyticsCapability;
|
|
70033
69886
|
exports.pipelineExecutorCapability = pipelineExecutorCapability;
|
|
70034
69887
|
exports.pipelineOrchestratorCapability = pipelineOrchestratorCapability;
|
|
70035
69888
|
exports.pipelineRunnerCapability = pipelineRunnerCapability;
|
|
70036
69889
|
exports.planComposition = planComposition;
|
|
69890
|
+
exports.planItems = planItems;
|
|
70037
69891
|
exports.plateGalleryCapability = plateGalleryCapability;
|
|
70038
69892
|
exports.platformProbeCapability = platformProbeCapability;
|
|
70039
69893
|
exports.poolMemoryThreshold = poolMemoryThreshold;
|
|
@@ -70046,6 +69900,7 @@ exports.privacyMaskCapability = privacyMaskCapability;
|
|
|
70046
69900
|
exports.procedureAuthKey = procedureAuthKey;
|
|
70047
69901
|
exports.ptzAutotrackCapability = ptzAutotrackCapability;
|
|
70048
69902
|
exports.ptzCapability = ptzCapability;
|
|
69903
|
+
exports.publicEventCategories = require_event_category.publicEventCategories;
|
|
70049
69904
|
exports.pythonScriptForBackend = pythonScriptForBackend;
|
|
70050
69905
|
exports.rankStreams = rankStreams;
|
|
70051
69906
|
exports.readClusterStepModels = readClusterStepModels;
|
|
@@ -70064,6 +69919,7 @@ exports.recordingOnboardCapability = recordingOnboardCapability;
|
|
|
70064
69919
|
exports.recordingSignalCapability = recordingSignalCapability;
|
|
70065
69920
|
exports.rectsToCells = rectsToCells;
|
|
70066
69921
|
exports.reducePoints = reducePoints;
|
|
69922
|
+
exports.replacedFieldKeys = replacedFieldKeys;
|
|
70067
69923
|
exports.requiredTopLevelKeys = requiredTopLevelKeys;
|
|
70068
69924
|
exports.requiresPython = requiresPython;
|
|
70069
69925
|
exports.reservedSourceOwner = reservedSourceOwner;
|
|
@@ -70090,7 +69946,7 @@ exports.resolveMutate = resolveMutate;
|
|
|
70090
69946
|
exports.resolvePoolMemoryPolicy = resolvePoolMemoryPolicy;
|
|
70091
69947
|
exports.resolveRecordingProfiles = resolveRecordingProfiles;
|
|
70092
69948
|
exports.resolveRunnerId = resolveRunnerId;
|
|
70093
|
-
exports.resolveStreamMaps =
|
|
69949
|
+
exports.resolveStreamMaps = require_composition.resolveStreamMaps;
|
|
70094
69950
|
exports.resolveVariantModelId = resolveVariantModelId;
|
|
70095
69951
|
exports.resolveViewableDeviceIds = resolveViewableDeviceIds;
|
|
70096
69952
|
exports.roleSpec = roleSpec;
|
|
@@ -70184,24 +70040,25 @@ exports.terminalSessionCapability = terminalSessionCapability;
|
|
|
70184
70040
|
exports.textToHtml = textToHtml;
|
|
70185
70041
|
exports.timeOfDayToMinutes = timeOfDayToMinutes;
|
|
70186
70042
|
exports.toDeviceSummary = toDeviceSummary;
|
|
70187
|
-
exports.toExpressionValue = toExpressionValue;
|
|
70043
|
+
exports.toExpressionValue = require_composition.toExpressionValue;
|
|
70188
70044
|
exports.toNodeId = require_sleep.toNodeId;
|
|
70189
70045
|
exports.toStreamSourceEntry = toStreamSourceEntry;
|
|
70190
70046
|
exports.toastCapability = toastCapability;
|
|
70191
70047
|
exports.toggleAudioLabel = toggleAudioLabel;
|
|
70192
|
-
exports.tokenize = tokenize;
|
|
70048
|
+
exports.tokenize = require_composition.tokenize;
|
|
70193
70049
|
exports.transcodeBody = transcodeBody;
|
|
70194
70050
|
exports.tryConvertUnit = tryConvertUnit;
|
|
70195
70051
|
exports.turnProviderCapability = turnProviderCapability;
|
|
70196
70052
|
exports.unitDimension = unitDimension;
|
|
70197
70053
|
exports.unitsForDimension = unitsForDimension;
|
|
70198
70054
|
exports.unreachableExtensionSlotRow = unreachableExtensionSlotRow;
|
|
70055
|
+
exports.unwrapSchema = unwrapSchema;
|
|
70199
70056
|
exports.updateCapability = updateCapability;
|
|
70200
70057
|
exports.userManagementCapability = userManagementCapability;
|
|
70201
70058
|
exports.userPasskeysCapability = userPasskeysCapability;
|
|
70202
70059
|
exports.vacuumControlCapability = vacuumControlCapability;
|
|
70203
70060
|
exports.validateComposition = validateComposition;
|
|
70204
|
-
exports.validateExpressionSource = validateExpressionSource;
|
|
70061
|
+
exports.validateExpressionSource = require_composition.validateExpressionSource;
|
|
70205
70062
|
exports.validateRecipeBounds = validateRecipeBounds;
|
|
70206
70063
|
exports.valveCapability = valveCapability;
|
|
70207
70064
|
exports.vectorDimFromBase64 = vectorDimFromBase64;
|