@camstack/types 1.2.266 → 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.
Files changed (57) hide show
  1. package/dist/addon.js +4 -4
  2. package/dist/addon.mjs +3 -3
  3. package/dist/capabilities/battery.cap.d.ts +17 -2
  4. package/dist/capabilities/capability-definition.d.ts +22 -0
  5. package/dist/capabilities/composer.cap.d.ts +99 -0
  6. package/dist/capabilities/consumables.cap.d.ts +79 -0
  7. package/dist/capabilities/core-blocks.cap.d.ts +1149 -91
  8. package/dist/capabilities/device-extension.cap.d.ts +12 -13
  9. package/dist/capabilities/device-state.cap.d.ts +186 -2
  10. package/dist/capabilities/index.d.ts +3 -3
  11. package/dist/capabilities/pipeline-analytics.cap.d.ts +14 -0
  12. package/dist/composition/composition-graph.d.ts +42 -9
  13. package/dist/composition/composition-items.d.ts +25 -0
  14. package/dist/composition/composition-report.d.ts +93 -2
  15. package/dist/composition/composition.d.ts +154 -3
  16. package/dist/composition/customization-name.d.ts +6 -0
  17. package/dist/composition/evaluate-field.d.ts +12 -0
  18. package/dist/composition/examples.d.ts +33 -0
  19. package/dist/composition/field-kinds.d.ts +42 -0
  20. package/dist/composition/index.d.ts +1 -0
  21. package/dist/composition/plan-result.d.ts +16 -0
  22. package/dist/composition/state-fields.d.ts +6 -0
  23. package/dist/composition/validate-composition.d.ts +44 -9
  24. package/dist/composition-CPFIlFfw.mjs +1759 -0
  25. package/dist/composition-QlOB7QTG.js +2184 -0
  26. package/dist/device/device-context.d.ts +9 -0
  27. package/dist/device/device-runtime-state.d.ts +15 -1
  28. package/dist/device/index.d.ts +1 -1
  29. package/dist/device-extension/slot-descriptor.d.ts +8 -11
  30. package/dist/device-extension/slot-provider.d.ts +0 -2
  31. package/dist/enums/event-category.d.ts +25 -0
  32. package/dist/enums.js +1 -1
  33. package/dist/enums.mjs +1 -1
  34. package/dist/err-msg-DX6i_MY4.mjs +339 -0
  35. package/dist/err-msg-Dx2Kor0g.js +368 -0
  36. package/dist/{event-category-BVDXG4tB.mjs → event-category-C5xZWqz6.mjs} +31 -1
  37. package/dist/{event-category-BVfsrBYA.js → event-category-MbnB-6AN.js} +48 -0
  38. package/dist/generated/addon-api.d.ts +42 -0
  39. package/dist/generated/device-proxy.d.ts +1 -1
  40. package/dist/generated/method-access-map.d.ts +1 -1
  41. package/dist/generated/method-device-selectors.d.ts +2 -2
  42. package/dist/generated/system-proxy.d.ts +2 -2
  43. package/dist/index.d.ts +4 -3
  44. package/dist/index.js +1548 -1660
  45. package/dist/index.mjs +1243 -1388
  46. package/dist/interfaces/kernel-abstractions.d.ts +16 -0
  47. package/dist/interfaces/status-overlay.d.ts +42 -0
  48. package/dist/node.d.ts +1 -0
  49. package/dist/node.js +29 -8
  50. package/dist/node.mjs +23 -3
  51. package/dist/{sleep-8K9gue-H.js → sleep-BSzia-xT.js} +6 -352
  52. package/dist/{sleep-DC-wdyeS.mjs → sleep-jkRQgtSc.mjs} +7 -329
  53. package/package.json +1 -1
  54. package/dist/canonical-hash-CPK2Dy60.mjs +0 -715
  55. package/dist/canonical-hash-CSE4ioRi.js +0 -792
  56. package/dist/err-msg-COpsHMw2.js +0 -18
  57. 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-8K9gue-H.js");
3
- const require_event_category = require("./event-category-BVfsrBYA.js");
4
- const require_canonical_hash = require("./canonical-hash-CSE4ioRi.js");
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:${require_canonical_hash.canonicalHash(canonicalEgressPlan(request, delivery))}`;
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: [require_sleep.DeviceType.Camera],
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: [require_sleep.DeviceType.Camera],
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: [require_sleep.DeviceType.Camera],
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
- /** How a field says "unavailable": `null` when its schema allows it, else the device goes offline (D393). */
10347
- var CompositionUnavailableModeSchema = zod.z.enum(["null", "offline"]);
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
- /** Mirrors `CoreBlockStatus`, pinned by `core-blocks-source.spec.ts` (Task 11). */
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
- /** The client-authored half of a composition block. No `code`, no placement (hub-only; Phase 7). */
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: [require_sleep.DeviceType.Camera],
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(require_sleep.DeviceType),
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: [require_sleep.DeviceType.Hub],
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(require_sleep.DeviceType),
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(require_sleep.DeviceType).nullable() })),
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(require_sleep.DeviceType) }), CreationSchemaOutputSchema),
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(require_sleep.DeviceType),
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(require_sleep.DeviceType),
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(require_sleep.DeviceType),
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: [require_sleep.DeviceType.Camera],
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(require_sleep.DeviceType)
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(require_sleep.DeviceType).optional(),
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(require_sleep.DeviceType)
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(require_sleep.DeviceType),
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(require_sleep.DeviceType),
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(require_sleep.DeviceType).nullable() }), { auth: "admin" }),
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
- /** Single cap slice. Returns null when the slice hasn't been
14235
- * written or the device isn't registered. */
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: [require_sleep.DeviceType.Camera],
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: [require_sleep.DeviceType.AlarmPanel],
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(require_sleep.DeviceType)).min(1)
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"),
@@ -22936,7 +22145,15 @@ var RebuildObjectEmbeddingsInput = zod.z.object({
22936
22145
  * force is logged at start and finish so a deliberately slow pass reads
22937
22146
  * differently from a stalled one.
22938
22147
  */
22939
- pacingMs: zod.z.number().int().nonnegative().optional()
22148
+ pacingMs: zod.z.number().int().nonnegative().optional(),
22149
+ /**
22150
+ * Re-embed ONLY the tracks whose vector is ALREADY in the pinned model's
22151
+ * space (D664) — the in-space skip inverted. It exists to redo vectors
22152
+ * written from the wrong box, which a normal pass would skip as done
22153
+ * forever. Requires `maxTracks`: a forced pass without a bound is refused
22154
+ * (`force-requires-max-tracks`).
22155
+ */
22156
+ force: zod.z.boolean().optional()
22940
22157
  });
22941
22158
  /**
22942
22159
  * Result of emptying the CLIP index.
@@ -22972,6 +22189,7 @@ var WipeObjectEmbeddingsResultSchema = zod.z.object({
22972
22189
  * - `reads-disagree` — the two reads named different choices.
22973
22190
  * - `not-a-clip-model` — the CONFIRMED id has no CLIP metadata, so there is no
22974
22191
  * index to fill and no dimension to check against (D649).
22192
+ * - `force-requires-max-tracks` — `force: true` with no `maxTracks` (D664).
22975
22193
  */
22976
22194
  var RebuildObjectEmbeddingsRefusalSchema = zod.z.enum([
22977
22195
  "stores-unavailable",
@@ -22979,7 +22197,8 @@ var RebuildObjectEmbeddingsRefusalSchema = zod.z.enum([
22979
22197
  "method-unavailable",
22980
22198
  "read-failed",
22981
22199
  "reads-disagree",
22982
- "not-a-clip-model"
22200
+ "not-a-clip-model",
22201
+ "force-requires-max-tracks"
22983
22202
  ]);
22984
22203
  /**
22985
22204
  * Acknowledgement that a rebuild STARTED.
@@ -23004,11 +22223,32 @@ var RebuildStatusSchema = zod.z.object({
23004
22223
  running: zod.z.boolean(),
23005
22224
  scanned: zod.z.number(),
23006
22225
  rebuilt: zod.z.number(),
23007
- /** Tracks whose key frame is gone — nothing to re-embed from. */
22226
+ /** Tracks whose key frame is gone — nothing to re-embed from. Pixels ONLY:
22227
+ * `missingEvent` is its own field since D664, never summed in. */
23008
22228
  missingKeyFrame: zod.z.number(),
23009
- /** Tracks with no usable detection box. */
22229
+ /** Tracks with no object event at all (D664). */
22230
+ missingEvent: zod.z.number(),
22231
+ /** Tracks with no box measured on the pixels to cut — see `boxSource.refused`. */
23010
22232
  missingBbox: zod.z.number(),
23011
22233
  /**
22234
+ * Which rule placed each judged track's box (D664): the key frame row's own
22235
+ * `subjectBox`, `track.positions` at exactly its instant (older key frames),
22236
+ * the event box (a key frame recovered from the recording, at the event's
22237
+ * instant), or `refused` by name — the reasons are in the tagged log lines.
22238
+ */
22239
+ boxSource: zod.z.object({
22240
+ subjectBox: zod.z.number(),
22241
+ positions: zod.z.number(),
22242
+ event: zod.z.number(),
22243
+ refused: zod.z.number()
22244
+ }),
22245
+ /** Tracks skipped because their vector is already in the pinned space. */
22246
+ alreadyInSpace: zod.z.number(),
22247
+ /** Whether the current / last pass was a `force` pass; `null` before any (D664). */
22248
+ force: zod.z.boolean().nullable(),
22249
+ /** In-space tracks a `force` pass selected for re-embedding. */
22250
+ forcedInSpace: zod.z.number(),
22251
+ /**
23012
22252
  * Tracks an executing node REFUSED rather than broke on — an unreadable key
23013
22253
  * frame, a step that threw. Separate from `failed` because the remedy is
23014
22254
  * different, and because a whole camera silently contributing zero vectors
@@ -23146,7 +22386,7 @@ var pipelineAnalyticsCapability = {
23146
22386
  mode: "singleton",
23147
22387
  kind: "wrapper",
23148
22388
  defaultActive: true,
23149
- deviceTypes: [require_sleep.DeviceType.Camera],
22389
+ deviceTypes: [require_err_msg.DeviceType.Camera],
23150
22390
  exposesDeviceSettings: true,
23151
22391
  methods: {
23152
22392
  getActiveTracks: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), zod.z.array(TrackSchema).readonly()),
@@ -24829,7 +24069,7 @@ var zonesCapability = {
24829
24069
  name: "zones",
24830
24070
  scope: "device",
24831
24071
  mode: "singleton",
24832
- deviceTypes: [require_sleep.DeviceType.Camera],
24072
+ deviceTypes: [require_err_msg.DeviceType.Camera],
24833
24073
  methods: {
24834
24074
  listZones: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), zod.z.array(ZoneSchema).readonly()),
24835
24075
  addZone: require_sleep.method(zod.z.object({
@@ -27811,7 +27051,7 @@ var snapshotCapability = {
27811
27051
  mode: "singleton",
27812
27052
  kind: "wrapper",
27813
27053
  defaultActive: true,
27814
- deviceTypes: [require_sleep.DeviceType.Camera],
27054
+ deviceTypes: [require_err_msg.DeviceType.Camera],
27815
27055
  exposesDeviceSettings: true,
27816
27056
  methods: {
27817
27057
  getSnapshot: require_sleep.method(zod.z.object({
@@ -28667,7 +27907,7 @@ var streamSignalsCapability = {
28667
27907
  scope: "device",
28668
27908
  deviceNative: true,
28669
27909
  mode: "singleton",
28670
- deviceTypes: Object.values(require_sleep.DeviceType),
27910
+ deviceTypes: Object.values(require_err_msg.DeviceType),
28671
27911
  runtimeState: StreamSignalsStatusSchema,
28672
27912
  /**
28673
27913
  * Runtime-state durability: **session** — mirrored in RAM, never written.
@@ -30166,7 +29406,7 @@ var videoclipsCapability = {
30166
29406
  /** A clip is a window over a camera's footage — the cap is meaningless on a
30167
29407
  * sensor, a button or an event emitter, and the `defaultActive` auto-bind
30168
29408
  * reads this to decide which devices it may claim. */
30169
- deviceTypes: [require_sleep.DeviceType.Camera],
29409
+ deviceTypes: [require_err_msg.DeviceType.Camera],
30170
29410
  /**
30171
29411
  * The Clips section of a camera's device details is FRAMEWORK-DERIVED (D14):
30172
29412
  * the aggregator turns this declaration into the `type:'widget'` section and
@@ -30760,7 +30000,7 @@ var DOORBELL_SOURCE_SLOT = {
30760
30000
  label: "Doorbell trigger devices",
30761
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.",
30762
30002
  shape: "association",
30763
- hostDeviceTypes: [require_sleep.DeviceType.Camera],
30003
+ hostDeviceTypes: [require_err_msg.DeviceType.Camera],
30764
30004
  sourceCaps: SOURCE_CAPS,
30765
30005
  cardinality: "many",
30766
30006
  value: DoorbellSourceSlotValueSchema
@@ -30772,7 +30012,7 @@ var doorbellCapability = {
30772
30012
  mode: "singleton",
30773
30013
  kind: "wrapper",
30774
30014
  defaultActive: false,
30775
- deviceTypes: [require_sleep.DeviceType.Button, require_sleep.DeviceType.Camera],
30015
+ deviceTypes: [require_err_msg.DeviceType.Button, require_err_msg.DeviceType.Camera],
30776
30016
  extensionSlots: [DOORBELL_SOURCE_SLOT],
30777
30017
  methods: {},
30778
30018
  events: {
@@ -30820,29 +30060,28 @@ var EXTENSION_SLOTS = {
30820
30060
  "doorbell-source": {
30821
30061
  descriptor: DOORBELL_SOURCE_SLOT,
30822
30062
  declaringCap: "doorbell",
30823
- hostDeviceTypes: [require_sleep.DeviceType.Camera]
30063
+ hostDeviceTypes: [require_err_msg.DeviceType.Camera]
30824
30064
  },
30825
30065
  "linked-devices": {
30826
30066
  descriptor: LINKED_DEVICES_SLOT,
30827
30067
  declaringCap: "device-manager",
30828
- hostDeviceTypes: [require_sleep.DeviceType.Camera]
30068
+ hostDeviceTypes: [require_err_msg.DeviceType.Camera]
30829
30069
  }
30830
30070
  };
30831
30071
  /** A slot's editable value, discriminated on `slot`. */
30832
30072
  var ExtensionSlotValueSchema = zod.z.discriminatedUnion("slot", [DoorbellSourceSlotValueSchema.extend({ slot: zod.z.literal("doorbell-source") }), LinkedDevicesSlotValueSchema.extend({ slot: zod.z.literal("linked-devices") })]);
30833
30073
  /** Every device type at least one slot is offered on — `device-extension`'s `deviceTypes`. */
30834
- var EXTENSION_HOST_DEVICE_TYPES = [require_sleep.DeviceType.Camera];
30074
+ var EXTENSION_HOST_DEVICE_TYPES = [require_err_msg.DeviceType.Camera];
30835
30075
  //#endregion
30836
30076
  //#region src/capabilities/device-extension.cap.ts
30837
30077
  /**
30838
30078
  * `device-extension` — ONE place to see and author what a device is extended
30839
30079
  * with by OTHER devices (D648).
30840
30080
  *
30841
- * **A contract, not a store.** Every slot (`linked-devices`,
30842
- * `doorbell-source`, later `consumables-graft`) is declared by the cap that
30843
- * owns the function (`extensionSlots` in its `*.cap.ts`, D4) and SERVED by the
30844
- * addon that already owns its store, over that store and its existing write
30845
- * 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
30846
30085
  * `scripts/check-extension-slots.ts`): D62 applied to the whole feature, and
30847
30086
  * D3 — no shadow registry.
30848
30087
  *
@@ -30863,6 +30102,11 @@ var EXTENSION_HOST_DEVICE_TYPES = [require_sleep.DeviceType.Camera];
30863
30102
  * The slot's value travels as the generated discriminated union
30864
30103
  * (`ExtensionSlotValue`, keyed on `slot`), so a value under the wrong slot is
30865
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).
30866
30110
  */
30867
30111
  /** Separator between the source and the slot id in a `slotRef`. It is the
30868
30112
  * D554 routing separator: the dispatcher matches `<source>:` prefixes. */
@@ -30885,7 +30129,7 @@ var ExtensionSourceSchema = zod.z.object({
30885
30129
  /**
30886
30130
  * How one picked source stands. `missing` — no such device (deleted, or an id
30887
30131
  * that never existed); `foreign` — a device the slot's owner cannot read from
30888
- * (slice 2's cross-owner graft source); `unavailable` — it exists but is not a
30132
+ * (a source owned by another addon); `unavailable` — it exists but is not a
30889
30133
  * valid source now (a container, which is never a pick). A source in any of
30890
30134
  * these states is SHOWN, never silently dropped or re-pointed.
30891
30135
  */
@@ -30926,10 +30170,6 @@ var ExtensionSlotStateSchema = zod.z.object({
30926
30170
  slot: ExtensionSlotIdSchema,
30927
30171
  /** Names the store written (D62), e.g. `virtual-doorbell:doorbellSources`. */
30928
30172
  authority: zod.z.string(),
30929
- /** graft: its switch. association: null — removing every source is "off". */
30930
- enabled: zod.z.boolean().nullable(),
30931
- /** graft: the cap registered on the host. association: null. */
30932
- projects: zod.z.string().nullable(),
30933
30173
  activation: ExtensionSlotActivationSchema.nullable(),
30934
30174
  value: ExtensionSlotValueSchema,
30935
30175
  sources: zod.z.array(ExtensionSlotSourceSchema),
@@ -30980,7 +30220,8 @@ var ExtensionCandidatesSchema = zod.z.object({ candidates: zod.z.array(Extension
30980
30220
  /**
30981
30221
  * What a write did. `none` — applied in place. `host` / `parent` — the owner
30982
30222
  * reloaded the host (or, for an accessory child, its parent) because the
30983
- * write changed what the host REGISTERS (a graft switched on or off, slice 2).
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`.
30984
30225
  */
30985
30226
  var ExtensionSetResultSchema = zod.z.object({
30986
30227
  applied: zod.z.literal(true),
@@ -31318,7 +30559,7 @@ var webrtcSessionCapability = {
31318
30559
  kind: "wrapper",
31319
30560
  defaultActive: true,
31320
30561
  preferredProvider: "stream-broker",
31321
- deviceTypes: [require_sleep.DeviceType.Camera],
30562
+ deviceTypes: [require_err_msg.DeviceType.Camera],
31322
30563
  methods: {
31323
30564
  /**
31324
30565
  * List available WebRTC streams for a device.
@@ -31599,18 +30840,18 @@ var accessoriesCapability = {
31599
30840
  deviceNative: true,
31600
30841
  mode: "singleton",
31601
30842
  deviceTypes: [
31602
- require_sleep.DeviceType.Camera,
31603
- require_sleep.DeviceType.Hub,
31604
- require_sleep.DeviceType.Switch,
31605
- require_sleep.DeviceType.Light,
31606
- require_sleep.DeviceType.Sensor,
31607
- require_sleep.DeviceType.Thermostat,
31608
- require_sleep.DeviceType.Cover,
31609
- require_sleep.DeviceType.Lock,
31610
- require_sleep.DeviceType.Fan,
31611
- require_sleep.DeviceType.MediaPlayer,
31612
- require_sleep.DeviceType.AlarmPanel,
31613
- require_sleep.DeviceType.Generic
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
31614
30855
  ],
31615
30856
  methods: {
31616
30857
  /**
@@ -32251,7 +31492,7 @@ var airQualitySensorCapability = {
32251
31492
  scope: "device",
32252
31493
  deviceNative: true,
32253
31494
  mode: "singleton",
32254
- deviceTypes: [require_sleep.DeviceType.Sensor],
31495
+ deviceTypes: [require_err_msg.DeviceType.Sensor],
32255
31496
  methods: {},
32256
31497
  status: {
32257
31498
  schema: AirQualitySensorStatusSchema,
@@ -32295,7 +31536,7 @@ var ambientLightSensorCapability = {
32295
31536
  scope: "device",
32296
31537
  deviceNative: true,
32297
31538
  mode: "singleton",
32298
- deviceTypes: [require_sleep.DeviceType.Sensor],
31539
+ deviceTypes: [require_err_msg.DeviceType.Sensor],
32299
31540
  methods: {},
32300
31541
  status: {
32301
31542
  schema: AmbientLightSensorStatusSchema,
@@ -32413,7 +31654,7 @@ var audioMetricsCapability = {
32413
31654
  name: "audio-metrics",
32414
31655
  scope: "device",
32415
31656
  mode: "singleton",
32416
- deviceTypes: [require_sleep.DeviceType.Camera],
31657
+ deviceTypes: [require_err_msg.DeviceType.Camera],
32417
31658
  methods: {
32418
31659
  /** Latest snapshot for this device. Null until the analytics
32419
31660
  * pipeline has processed at least one audio window. */
@@ -32481,7 +31722,7 @@ var automationControlCapability = {
32481
31722
  scope: "device",
32482
31723
  deviceNative: true,
32483
31724
  mode: "singleton",
32484
- deviceTypes: [require_sleep.DeviceType.Automation],
31725
+ deviceTypes: [require_err_msg.DeviceType.Automation],
32485
31726
  methods: {
32486
31727
  enable: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.void(), {
32487
31728
  kind: "mutation",
@@ -32607,10 +31848,12 @@ var batteryCapability = {
32607
31848
  deviceNative: true,
32608
31849
  mode: "singleton",
32609
31850
  deviceTypes: [
32610
- require_sleep.DeviceType.Camera,
32611
- require_sleep.DeviceType.Sensor,
32612
- require_sleep.DeviceType.Button,
32613
- require_sleep.DeviceType.Switch
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
32614
31857
  ],
32615
31858
  methods: {
32616
31859
  /**
@@ -32637,6 +31880,19 @@ wakeForStream: require_sleep.method(zod.z.object({
32637
31880
  awoke: zod.z.boolean(),
32638
31881
  durationMs: zod.z.number()
32639
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
+ } },
32640
31896
  events: {
32641
31897
  /**
32642
31898
  * Emitted whenever the cached status changes (firmware push OR
@@ -32652,7 +31908,7 @@ onStatusChanged: { data: zod.z.object({
32652
31908
  schema: BatteryStatusSchema,
32653
31909
  kind: "push",
32654
31910
  empty: {
32655
- percentage: 0,
31911
+ percentage: null,
32656
31912
  charging: "unknown",
32657
31913
  sleeping: false,
32658
31914
  lastUpdated: 0
@@ -32702,7 +31958,7 @@ var binaryCapability = {
32702
31958
  scope: "device",
32703
31959
  deviceNative: true,
32704
31960
  mode: "singleton",
32705
- deviceTypes: [require_sleep.DeviceType.Sensor],
31961
+ deviceTypes: [require_err_msg.DeviceType.Sensor],
32706
31962
  methods: {},
32707
31963
  status: {
32708
31964
  schema: BinaryStatusSchema,
@@ -32745,7 +32001,7 @@ var brightnessCapability = {
32745
32001
  scope: "device",
32746
32002
  deviceNative: true,
32747
32003
  mode: "singleton",
32748
- deviceTypes: [require_sleep.DeviceType.Light],
32004
+ deviceTypes: [require_err_msg.DeviceType.Light],
32749
32005
  methods: { setBrightness: require_sleep.method(zod.z.object({
32750
32006
  deviceId: zod.z.number().int().nonnegative(),
32751
32007
  percentage: zod.z.number().min(0).max(100)
@@ -32799,7 +32055,7 @@ var buttonCapability = {
32799
32055
  scope: "device",
32800
32056
  deviceNative: true,
32801
32057
  mode: "singleton",
32802
- deviceTypes: [require_sleep.DeviceType.Button],
32058
+ deviceTypes: [require_err_msg.DeviceType.Button],
32803
32059
  methods: { press: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.void(), {
32804
32060
  kind: "mutation",
32805
32061
  auth: "protected"
@@ -32876,7 +32132,7 @@ var cameraCredentialsCapability = {
32876
32132
  scope: "device",
32877
32133
  deviceNative: true,
32878
32134
  mode: "singleton",
32879
- deviceTypes: [require_sleep.DeviceType.Camera],
32135
+ deviceTypes: [require_err_msg.DeviceType.Camera],
32880
32136
  methods: {
32881
32137
  /**
32882
32138
  * Return the camera's full credential bundle for admin consumers.
@@ -33033,7 +32289,7 @@ var cameraGridLayoutCapability = {
33033
32289
  scope: "device",
33034
32290
  deviceNative: true,
33035
32291
  mode: "singleton",
33036
- deviceTypes: [require_sleep.DeviceType.Camera],
32292
+ deviceTypes: [require_err_msg.DeviceType.Camera],
33037
32293
  /**
33038
32294
  * The section is built by the ADDON and returned from
33039
32295
  * `getDeviceSettingsContribution`, not derived by the framework from a
@@ -33084,7 +32340,7 @@ var carbonMonoxideCapability = {
33084
32340
  scope: "device",
33085
32341
  deviceNative: true,
33086
32342
  mode: "singleton",
33087
- deviceTypes: [require_sleep.DeviceType.Sensor],
32343
+ deviceTypes: [require_err_msg.DeviceType.Sensor],
33088
32344
  methods: {},
33089
32345
  status: {
33090
32346
  schema: CarbonMonoxideStatusSchema,
@@ -33193,7 +32449,7 @@ var climateControlCapability = {
33193
32449
  scope: "device",
33194
32450
  deviceNative: true,
33195
32451
  mode: "singleton",
33196
- deviceTypes: [require_sleep.DeviceType.Thermostat, require_sleep.DeviceType.Climate],
32452
+ deviceTypes: [require_err_msg.DeviceType.Thermostat, require_err_msg.DeviceType.Climate],
33197
32453
  methods: {
33198
32454
  setMode: require_sleep.method(zod.z.object({
33199
32455
  deviceId: zod.z.number().int().nonnegative(),
@@ -33346,7 +32602,7 @@ var colorCapability = {
33346
32602
  scope: "device",
33347
32603
  deviceNative: true,
33348
32604
  mode: "singleton",
33349
- deviceTypes: [require_sleep.DeviceType.Light],
32605
+ deviceTypes: [require_err_msg.DeviceType.Light],
33350
32606
  methods: { setColor: require_sleep.method(zod.z.object({
33351
32607
  deviceId: zod.z.number().int().nonnegative(),
33352
32608
  color: ColorInputSchema
@@ -33506,7 +32762,7 @@ var connectivityCapability = {
33506
32762
  scope: "device",
33507
32763
  deviceNative: true,
33508
32764
  mode: "singleton",
33509
- deviceTypes: [require_sleep.DeviceType.Sensor],
32765
+ deviceTypes: [require_err_msg.DeviceType.Sensor],
33510
32766
  methods: {},
33511
32767
  status: {
33512
32768
  schema: ConnectivityStatusSchema,
@@ -33538,6 +32794,19 @@ var connectivityCapability = {
33538
32794
  * provider populates it by guessing (no HA inference). The UI renders a
33539
32795
  * "No consumables reported" placeholder when `items` is empty.
33540
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
+ });
33541
32810
  /** A single consumable item. Either a continuous `level` (remaining
33542
32811
  * life %) or a discrete `status` may be known — both may be null when a
33543
32812
  * provider only knows the item exists. `level` and `status` are not
@@ -33554,7 +32823,10 @@ var ConsumableItemSchema = zod.z.object({
33554
32823
  /** Ms epoch of the last replace, when known. */
33555
32824
  lastResetAt: zod.z.number().nullable(),
33556
32825
  /** Whether `reset()` is meaningful for this item. */
33557
- 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()
33558
32830
  });
33559
32831
  var ConsumablesStatusSchema = zod.z.object({
33560
32832
  items: zod.z.array(ConsumableItemSchema),
@@ -33565,7 +32837,7 @@ var consumablesCapability = {
33565
32837
  scope: "device",
33566
32838
  deviceNative: true,
33567
32839
  mode: "singleton",
33568
- deviceTypes: Object.values(require_sleep.DeviceType),
32840
+ deviceTypes: Object.values(require_err_msg.DeviceType),
33569
32841
  deviceConfig: { ui: {
33570
32842
  kind: "widget",
33571
32843
  widgetId: "host/consumables-panel",
@@ -33584,6 +32856,16 @@ reset: require_sleep.method(zod.z.object({
33584
32856
  kind: "mutation",
33585
32857
  auth: "admin"
33586
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
+ } },
33587
32869
  status: {
33588
32870
  schema: ConsumablesStatusSchema,
33589
32871
  kind: "push",
@@ -33648,7 +32930,7 @@ var contactCapability = {
33648
32930
  scope: "device",
33649
32931
  deviceNative: true,
33650
32932
  mode: "singleton",
33651
- deviceTypes: [require_sleep.DeviceType.Sensor],
32933
+ deviceTypes: [require_err_msg.DeviceType.Sensor],
33652
32934
  methods: {},
33653
32935
  status: {
33654
32936
  schema: ContactStatusSchema,
@@ -33779,7 +33061,7 @@ var controlCapability = {
33779
33061
  scope: "device",
33780
33062
  deviceNative: true,
33781
33063
  mode: "singleton",
33782
- deviceTypes: [require_sleep.DeviceType.Control],
33064
+ deviceTypes: [require_err_msg.DeviceType.Control],
33783
33065
  methods: { setValue: require_sleep.method(zod.z.object({
33784
33066
  deviceId: zod.z.number().int().nonnegative(),
33785
33067
  control: ControlSetValueInputSchema
@@ -33851,7 +33133,7 @@ var coverCapability = {
33851
33133
  scope: "device",
33852
33134
  deviceNative: true,
33853
33135
  mode: "singleton",
33854
- deviceTypes: [require_sleep.DeviceType.Cover],
33136
+ deviceTypes: [require_err_msg.DeviceType.Cover],
33855
33137
  methods: {
33856
33138
  open: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.void(), {
33857
33139
  kind: "mutation",
@@ -33974,7 +33256,7 @@ var dayNightCapability = {
33974
33256
  scope: "device",
33975
33257
  deviceNative: true,
33976
33258
  mode: "singleton",
33977
- deviceTypes: [require_sleep.DeviceType.Camera],
33259
+ deviceTypes: [require_err_msg.DeviceType.Camera],
33978
33260
  deviceConfig: { ui: {
33979
33261
  kind: "derived-form",
33980
33262
  builderId: "day-night",
@@ -34099,7 +33381,7 @@ var enumSensorCapability = {
34099
33381
  scope: "device",
34100
33382
  deviceNative: true,
34101
33383
  mode: "singleton",
34102
- deviceTypes: [require_sleep.DeviceType.Sensor],
33384
+ deviceTypes: [require_err_msg.DeviceType.Sensor],
34103
33385
  methods: {},
34104
33386
  status: {
34105
33387
  schema: EnumSensorStatusSchema,
@@ -34146,7 +33428,7 @@ var eventEmitterCapability = {
34146
33428
  scope: "device",
34147
33429
  deviceNative: true,
34148
33430
  mode: "singleton",
34149
- deviceTypes: [require_sleep.DeviceType.EventEmitter],
33431
+ deviceTypes: [require_err_msg.DeviceType.EventEmitter],
34150
33432
  methods: {},
34151
33433
  events: { onEvent: { data: EventFireSchema } },
34152
33434
  status: {
@@ -34614,7 +33896,7 @@ var fanControlCapability = {
34614
33896
  scope: "device",
34615
33897
  deviceNative: true,
34616
33898
  mode: "singleton",
34617
- deviceTypes: [require_sleep.DeviceType.Fan],
33899
+ deviceTypes: [require_err_msg.DeviceType.Fan],
34618
33900
  methods: {
34619
33901
  setPercentage: require_sleep.method(zod.z.object({
34620
33902
  deviceId: zod.z.number().int().nonnegative(),
@@ -34768,7 +34050,7 @@ var floodCapability = {
34768
34050
  scope: "device",
34769
34051
  deviceNative: true,
34770
34052
  mode: "singleton",
34771
- deviceTypes: [require_sleep.DeviceType.Sensor],
34053
+ deviceTypes: [require_err_msg.DeviceType.Sensor],
34772
34054
  methods: {},
34773
34055
  status: {
34774
34056
  schema: FloodStatusSchema,
@@ -34803,7 +34085,7 @@ var gasCapability = {
34803
34085
  scope: "device",
34804
34086
  deviceNative: true,
34805
34087
  mode: "singleton",
34806
- deviceTypes: [require_sleep.DeviceType.Sensor],
34088
+ deviceTypes: [require_err_msg.DeviceType.Sensor],
34807
34089
  methods: {},
34808
34090
  status: {
34809
34091
  schema: GasStatusSchema,
@@ -34865,7 +34147,7 @@ var humidifierCapability = {
34865
34147
  scope: "device",
34866
34148
  deviceNative: true,
34867
34149
  mode: "singleton",
34868
- deviceTypes: [require_sleep.DeviceType.Humidifier],
34150
+ deviceTypes: [require_err_msg.DeviceType.Humidifier],
34869
34151
  methods: {
34870
34152
  setOn: require_sleep.method(zod.z.object({
34871
34153
  deviceId: zod.z.number().int().nonnegative(),
@@ -34936,7 +34218,7 @@ var humiditySensorCapability = {
34936
34218
  scope: "device",
34937
34219
  deviceNative: true,
34938
34220
  mode: "singleton",
34939
- deviceTypes: [require_sleep.DeviceType.Sensor, require_sleep.DeviceType.Thermostat],
34221
+ deviceTypes: [require_err_msg.DeviceType.Sensor, require_err_msg.DeviceType.Thermostat],
34940
34222
  methods: {},
34941
34223
  status: {
34942
34224
  schema: HumiditySensorStatusSchema,
@@ -34982,7 +34264,7 @@ var imageCapability = {
34982
34264
  scope: "device",
34983
34265
  deviceNative: true,
34984
34266
  mode: "singleton",
34985
- deviceTypes: [require_sleep.DeviceType.Image],
34267
+ deviceTypes: [require_err_msg.DeviceType.Image],
34986
34268
  methods: {},
34987
34269
  status: {
34988
34270
  schema: ImageStatusSchema,
@@ -35130,7 +34412,7 @@ var imageSettingsCapability = {
35130
34412
  scope: "device",
35131
34413
  deviceNative: true,
35132
34414
  mode: "singleton",
35133
- deviceTypes: [require_sleep.DeviceType.Camera],
34415
+ deviceTypes: [require_err_msg.DeviceType.Camera],
35134
34416
  deviceConfig: { ui: {
35135
34417
  kind: "derived-form",
35136
34418
  builderId: "image-settings",
@@ -35407,7 +34689,7 @@ var intercomCapability = {
35407
34689
  scope: "device",
35408
34690
  deviceNative: true,
35409
34691
  mode: "singleton",
35410
- deviceTypes: [require_sleep.DeviceType.Camera],
34692
+ deviceTypes: [require_err_msg.DeviceType.Camera],
35411
34693
  /**
35412
34694
  * **Auth tier: `protected` on every method — deliberate, and load-bearing.**
35413
34695
  *
@@ -35613,7 +34895,7 @@ var lawnMowerControlCapability = {
35613
34895
  scope: "device",
35614
34896
  deviceNative: true,
35615
34897
  mode: "singleton",
35616
- deviceTypes: [require_sleep.DeviceType.LawnMower],
34898
+ deviceTypes: [require_err_msg.DeviceType.LawnMower],
35617
34899
  methods: {
35618
34900
  startMowing: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.void(), {
35619
34901
  kind: "mutation",
@@ -36043,7 +35325,7 @@ var lockControlCapability = {
36043
35325
  scope: "device",
36044
35326
  deviceNative: true,
36045
35327
  mode: "singleton",
36046
- deviceTypes: [require_sleep.DeviceType.Lock],
35328
+ deviceTypes: [require_err_msg.DeviceType.Lock],
36047
35329
  methods: {
36048
35330
  lock: require_sleep.method(zod.z.object({
36049
35331
  deviceId: zod.z.number().int().nonnegative(),
@@ -36164,7 +35446,7 @@ var mediaPlayerCapability = {
36164
35446
  scope: "device",
36165
35447
  deviceNative: true,
36166
35448
  mode: "singleton",
36167
- deviceTypes: [require_sleep.DeviceType.MediaPlayer],
35449
+ deviceTypes: [require_err_msg.DeviceType.MediaPlayer],
36168
35450
  methods: {
36169
35451
  play: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.void(), {
36170
35452
  kind: "mutation",
@@ -36608,7 +35890,7 @@ var motionCapability = {
36608
35890
  * `snapshot` and every other per-device native cap.
36609
35891
  */
36610
35892
  deviceNative: true,
36611
- deviceTypes: [require_sleep.DeviceType.Camera, require_sleep.DeviceType.Sensor],
35893
+ deviceTypes: [require_err_msg.DeviceType.Camera, require_err_msg.DeviceType.Sensor],
36612
35894
  methods: {
36613
35895
  /**
36614
35896
  * Pull the current motion state synchronously. Convenience shortcut
@@ -36701,9 +35983,9 @@ var motionTriggerCapability = {
36701
35983
  deviceNative: true,
36702
35984
  mode: "singleton",
36703
35985
  deviceTypes: [
36704
- require_sleep.DeviceType.Light,
36705
- require_sleep.DeviceType.Siren,
36706
- require_sleep.DeviceType.Switch
35986
+ require_err_msg.DeviceType.Light,
35987
+ require_err_msg.DeviceType.Siren,
35988
+ require_err_msg.DeviceType.Switch
36707
35989
  ],
36708
35990
  methods: { setMotionTrigger: require_sleep.method(zod.z.object({
36709
35991
  deviceId: zod.z.number().int().nonnegative(),
@@ -36776,7 +36058,7 @@ var motionZonesCapability = {
36776
36058
  scope: "device",
36777
36059
  deviceNative: true,
36778
36060
  mode: "singleton",
36779
- deviceTypes: [require_sleep.DeviceType.Camera],
36061
+ deviceTypes: [require_err_msg.DeviceType.Camera],
36780
36062
  deviceConfig: { ui: {
36781
36063
  kind: "widget",
36782
36064
  widgetId: "host/motion-zones-grid",
@@ -36902,7 +36184,7 @@ var nativeObjectDetectionCapability = {
36902
36184
  scope: "device",
36903
36185
  deviceNative: true,
36904
36186
  mode: "singleton",
36905
- deviceTypes: [require_sleep.DeviceType.Camera],
36187
+ deviceTypes: [require_err_msg.DeviceType.Camera],
36906
36188
  methods: {
36907
36189
  getOptions: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), NativeObjectDetectionOptionsSchema),
36908
36190
  setSettings: require_sleep.method(zod.z.object({
@@ -37271,7 +36553,7 @@ var navigationCapability = {
37271
36553
  scope: "device",
37272
36554
  deviceNative: true,
37273
36555
  mode: "singleton",
37274
- deviceTypes: [require_sleep.DeviceType.Camera],
36556
+ deviceTypes: [require_err_msg.DeviceType.Camera],
37275
36557
  deviceConfig: { ui: {
37276
36558
  kind: "widget",
37277
36559
  widgetId: "host/navigation-panel",
@@ -37441,13 +36723,13 @@ var networkLinkCapability = {
37441
36723
  deviceNative: true,
37442
36724
  mode: "singleton",
37443
36725
  deviceTypes: [
37444
- require_sleep.DeviceType.Camera,
37445
- require_sleep.DeviceType.Sensor,
37446
- require_sleep.DeviceType.Button,
37447
- require_sleep.DeviceType.Switch,
37448
- require_sleep.DeviceType.Light,
37449
- require_sleep.DeviceType.Lock,
37450
- require_sleep.DeviceType.Siren
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
37451
36733
  ],
37452
36734
  methods: {},
37453
36735
  events: {
@@ -37883,7 +37165,7 @@ var notifierCapability = {
37883
37165
  scope: "device",
37884
37166
  deviceNative: true,
37885
37167
  mode: "singleton",
37886
- deviceTypes: [require_sleep.DeviceType.Notifier],
37168
+ deviceTypes: [require_err_msg.DeviceType.Notifier],
37887
37169
  methods: {
37888
37170
  send: require_sleep.method(NotifierSendInputSchema, NotifierSendResultSchema, {
37889
37171
  kind: "mutation",
@@ -37965,7 +37247,7 @@ var numericSensorCapability = {
37965
37247
  scope: "device",
37966
37248
  deviceNative: true,
37967
37249
  mode: "singleton",
37968
- deviceTypes: [require_sleep.DeviceType.Sensor],
37250
+ deviceTypes: [require_err_msg.DeviceType.Sensor],
37969
37251
  methods: {},
37970
37252
  status: {
37971
37253
  schema: NumericSensorStatusSchema,
@@ -38034,7 +37316,7 @@ var osdCapability = {
38034
37316
  scope: "device",
38035
37317
  deviceNative: true,
38036
37318
  mode: "singleton",
38037
- deviceTypes: [require_sleep.DeviceType.Camera],
37319
+ deviceTypes: [require_err_msg.DeviceType.Camera],
38038
37320
  methods: {
38039
37321
  /**
38040
37322
  * Patch a single overlay by id. Provider handles read-modify-write
@@ -38410,7 +37692,7 @@ var petFeederCapability = {
38410
37692
  scope: "device",
38411
37693
  deviceNative: true,
38412
37694
  mode: "singleton",
38413
- deviceTypes: [require_sleep.DeviceType.PetFeeder],
37695
+ deviceTypes: [require_err_msg.DeviceType.PetFeeder],
38414
37696
  methods: {
38415
37697
  /**
38416
37698
  * Dispense food now. Single-hopper feeders take `grams`; dual-hopper
@@ -38912,7 +38194,7 @@ var powerMeterCapability = {
38912
38194
  scope: "device",
38913
38195
  deviceNative: true,
38914
38196
  mode: "singleton",
38915
- deviceTypes: [require_sleep.DeviceType.Sensor],
38197
+ deviceTypes: [require_err_msg.DeviceType.Sensor],
38916
38198
  methods: {},
38917
38199
  status: {
38918
38200
  schema: PowerMeterStatusSchema,
@@ -38975,7 +38257,7 @@ var presenceCapability = {
38975
38257
  scope: "device",
38976
38258
  deviceNative: true,
38977
38259
  mode: "singleton",
38978
- deviceTypes: [require_sleep.DeviceType.Presence],
38260
+ deviceTypes: [require_err_msg.DeviceType.Presence],
38979
38261
  methods: {},
38980
38262
  status: {
38981
38263
  schema: PresenceStatusSchema,
@@ -39029,7 +38311,7 @@ var pressureSensorCapability = {
39029
38311
  scope: "device",
39030
38312
  deviceNative: true,
39031
38313
  mode: "singleton",
39032
- deviceTypes: [require_sleep.DeviceType.Sensor],
38314
+ deviceTypes: [require_err_msg.DeviceType.Sensor],
39033
38315
  methods: {},
39034
38316
  status: {
39035
38317
  schema: PressureSensorStatusSchema,
@@ -39154,7 +38436,7 @@ var privacyMaskCapability = {
39154
38436
  scope: "device",
39155
38437
  deviceNative: true,
39156
38438
  mode: "singleton",
39157
- deviceTypes: [require_sleep.DeviceType.Camera],
38439
+ deviceTypes: [require_err_msg.DeviceType.Camera],
39158
38440
  deviceConfig: { ui: {
39159
38441
  kind: "widget",
39160
38442
  widgetId: "host/privacy-mask-grid",
@@ -39418,7 +38700,7 @@ var ptzCapability = {
39418
38700
  scope: "device",
39419
38701
  deviceNative: true,
39420
38702
  mode: "singleton",
39421
- deviceTypes: [require_sleep.DeviceType.Camera],
38703
+ deviceTypes: [require_err_msg.DeviceType.Camera],
39422
38704
  deviceConfig: { ui: {
39423
38705
  kind: "widget",
39424
38706
  widgetId: "host/ptz-panel",
@@ -39650,7 +38932,7 @@ var ptzAutotrackCapability = {
39650
38932
  */
39651
38933
  kind: "wrapper",
39652
38934
  defaultActive: false,
39653
- deviceTypes: [require_sleep.DeviceType.Camera],
38935
+ deviceTypes: [require_err_msg.DeviceType.Camera],
39654
38936
  deviceConfig: { ui: {
39655
38937
  kind: "widget",
39656
38938
  widgetId: "host/ptz-autotrack",
@@ -39741,9 +39023,9 @@ var rebootCapability = {
39741
39023
  deviceNative: true,
39742
39024
  mode: "singleton",
39743
39025
  deviceTypes: [
39744
- require_sleep.DeviceType.Camera,
39745
- require_sleep.DeviceType.Sensor,
39746
- require_sleep.DeviceType.Switch
39026
+ require_err_msg.DeviceType.Camera,
39027
+ require_err_msg.DeviceType.Sensor,
39028
+ require_err_msg.DeviceType.Switch
39747
39029
  ],
39748
39030
  methods: { reboot: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.object({ success: zod.z.literal(true) }), {
39749
39031
  kind: "mutation",
@@ -40023,7 +39305,7 @@ var recordingCapability = {
40023
39305
  /** Recorded coverage is a property of a camera — the cap is meaningless on a
40024
39306
  * sensor, a button or an event emitter, and the `defaultActive` auto-bind
40025
39307
  * reads this to decide which devices it may claim. */
40026
- deviceTypes: [require_sleep.DeviceType.Camera],
39308
+ deviceTypes: [require_err_msg.DeviceType.Camera],
40027
39309
  methods: {
40028
39310
  /**
40029
39311
  * The sources this camera has, WITH the reason any of them cannot answer.
@@ -41711,7 +40993,7 @@ var recordingOnboardCapability = {
41711
40993
  scope: "device",
41712
40994
  deviceNative: true,
41713
40995
  mode: "singleton",
41714
- deviceTypes: [require_sleep.DeviceType.Camera],
40996
+ deviceTypes: [require_err_msg.DeviceType.Camera],
41715
40997
  deviceConfig: { ui: {
41716
40998
  kind: "derived-form",
41717
40999
  builderId: "recording-onboard",
@@ -42054,7 +41336,7 @@ var recordingSignalCapability = {
42054
41336
  scope: "device",
42055
41337
  deviceNative: true,
42056
41338
  mode: "singleton",
42057
- deviceTypes: [require_sleep.DeviceType.Camera],
41339
+ deviceTypes: [require_err_msg.DeviceType.Camera],
42058
41340
  methods: {
42059
41341
  /** The current level, straight from the slice the provider keeps fresh. */
42060
41342
  getStatus: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), RecordingSignalStatusSchema) },
@@ -42373,7 +41655,7 @@ var sceneMonitorCapability = {
42373
41655
  mode: "singleton",
42374
41656
  kind: "wrapper",
42375
41657
  defaultActive: true,
42376
- deviceTypes: [require_sleep.DeviceType.Camera],
41658
+ deviceTypes: [require_err_msg.DeviceType.Camera],
42377
41659
  methods: {
42378
41660
  listScenes: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), SceneMonitorStatusSchema),
42379
41661
  /**
@@ -42626,7 +41908,7 @@ var scriptRunnerCapability = {
42626
41908
  scope: "device",
42627
41909
  deviceNative: true,
42628
41910
  mode: "singleton",
42629
- deviceTypes: [require_sleep.DeviceType.Script],
41911
+ deviceTypes: [require_err_msg.DeviceType.Script],
42630
41912
  methods: {
42631
41913
  run: require_sleep.method(zod.z.object({
42632
41914
  deviceId: zod.z.number().int().nonnegative(),
@@ -42684,7 +41966,7 @@ var smokeCapability = {
42684
41966
  scope: "device",
42685
41967
  deviceNative: true,
42686
41968
  mode: "singleton",
42687
- deviceTypes: [require_sleep.DeviceType.Sensor],
41969
+ deviceTypes: [require_err_msg.DeviceType.Sensor],
42688
41970
  methods: {},
42689
41971
  status: {
42690
41972
  schema: SmokeStatusSchema,
@@ -42783,7 +42065,7 @@ var streamCatalogCapability = {
42783
42065
  scope: "device",
42784
42066
  deviceNative: true,
42785
42067
  mode: "singleton",
42786
- deviceTypes: [require_sleep.DeviceType.Camera],
42068
+ deviceTypes: [require_err_msg.DeviceType.Camera],
42787
42069
  methods: { getCatalog: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.array(CamStreamDescriptorSchema).readonly()) },
42788
42070
  runtimeState: StreamCatalogStateSchema,
42789
42071
  /**
@@ -42901,7 +42183,7 @@ var streamParamsCapability = {
42901
42183
  scope: "device",
42902
42184
  deviceNative: true,
42903
42185
  mode: "singleton",
42904
- deviceTypes: [require_sleep.DeviceType.Camera],
42186
+ deviceTypes: [require_err_msg.DeviceType.Camera],
42905
42187
  deviceConfig: { ui: {
42906
42188
  kind: "derived-form",
42907
42189
  builderId: "stream-params",
@@ -43233,9 +42515,9 @@ var switchCapability = {
43233
42515
  deviceNative: true,
43234
42516
  mode: "singleton",
43235
42517
  deviceTypes: [
43236
- require_sleep.DeviceType.Switch,
43237
- require_sleep.DeviceType.Siren,
43238
- require_sleep.DeviceType.Light
42518
+ require_err_msg.DeviceType.Switch,
42519
+ require_err_msg.DeviceType.Siren,
42520
+ require_err_msg.DeviceType.Light
43239
42521
  ],
43240
42522
  methods: { setState: require_sleep.method(zod.z.object({
43241
42523
  deviceId: zod.z.number(),
@@ -43930,7 +43212,7 @@ var tamperCapability = {
43930
43212
  scope: "device",
43931
43213
  deviceNative: true,
43932
43214
  mode: "singleton",
43933
- deviceTypes: [require_sleep.DeviceType.Sensor],
43215
+ deviceTypes: [require_err_msg.DeviceType.Sensor],
43934
43216
  methods: {},
43935
43217
  status: {
43936
43218
  schema: TamperStatusSchema,
@@ -43987,7 +43269,7 @@ var temperatureSensorCapability = {
43987
43269
  scope: "device",
43988
43270
  deviceNative: true,
43989
43271
  mode: "singleton",
43990
- deviceTypes: [require_sleep.DeviceType.Sensor, require_sleep.DeviceType.Thermostat],
43272
+ deviceTypes: [require_err_msg.DeviceType.Sensor, require_err_msg.DeviceType.Thermostat],
43991
43273
  methods: {},
43992
43274
  status: {
43993
43275
  schema: TemperatureSensorStatusSchema,
@@ -44074,7 +43356,7 @@ var updateCapability = {
44074
43356
  scope: "device",
44075
43357
  deviceNative: true,
44076
43358
  mode: "singleton",
44077
- deviceTypes: [require_sleep.DeviceType.Update],
43359
+ deviceTypes: [require_err_msg.DeviceType.Update],
44078
43360
  methods: { installUpdate: require_sleep.method(zod.z.void(), zod.z.void(), {
44079
43361
  kind: "mutation",
44080
43362
  auth: "admin"
@@ -44432,7 +43714,7 @@ var vacuumControlCapability = {
44432
43714
  scope: "device",
44433
43715
  deviceNative: true,
44434
43716
  mode: "singleton",
44435
- deviceTypes: [require_sleep.DeviceType.Vacuum],
43717
+ deviceTypes: [require_err_msg.DeviceType.Vacuum],
44436
43718
  methods: {
44437
43719
  start: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.void(), {
44438
43720
  kind: "mutation",
@@ -44519,7 +43801,7 @@ var valveCapability = {
44519
43801
  scope: "device",
44520
43802
  deviceNative: true,
44521
43803
  mode: "singleton",
44522
- deviceTypes: [require_sleep.DeviceType.Valve],
43804
+ deviceTypes: [require_err_msg.DeviceType.Valve],
44523
43805
  methods: {
44524
43806
  open: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int().nonnegative() }), zod.z.void(), {
44525
43807
  kind: "mutation",
@@ -44574,7 +43856,7 @@ var vibrationCapability = {
44574
43856
  scope: "device",
44575
43857
  deviceNative: true,
44576
43858
  mode: "singleton",
44577
- deviceTypes: [require_sleep.DeviceType.Sensor],
43859
+ deviceTypes: [require_err_msg.DeviceType.Sensor],
44578
43860
  methods: {},
44579
43861
  status: {
44580
43862
  schema: VibrationStatusSchema,
@@ -44634,7 +43916,7 @@ var waterHeaterCapability = {
44634
43916
  scope: "device",
44635
43917
  deviceNative: true,
44636
43918
  mode: "singleton",
44637
- deviceTypes: [require_sleep.DeviceType.WaterHeater],
43919
+ deviceTypes: [require_err_msg.DeviceType.WaterHeater],
44638
43920
  methods: {
44639
43921
  setTargetTemp: require_sleep.method(zod.z.object({
44640
43922
  deviceId: zod.z.number().int().nonnegative(),
@@ -44724,7 +44006,7 @@ var weatherCapability = {
44724
44006
  scope: "device",
44725
44007
  deviceNative: true,
44726
44008
  mode: "singleton",
44727
- deviceTypes: [require_sleep.DeviceType.Weather],
44009
+ deviceTypes: [require_err_msg.DeviceType.Weather],
44728
44010
  methods: {},
44729
44011
  status: {
44730
44012
  schema: WeatherStatusSchema,
@@ -44932,7 +44214,7 @@ var zoneAnalyticsCapability = {
44932
44214
  name: "zone-analytics",
44933
44215
  scope: "device",
44934
44216
  mode: "singleton",
44935
- deviceTypes: [require_sleep.DeviceType.Camera],
44217
+ deviceTypes: [require_err_msg.DeviceType.Camera],
44936
44218
  methods: {
44937
44219
  /** Latest computed occupancy snapshot for this camera. Null when
44938
44220
  * the analytics pipeline hasn't seen a frame for this device yet
@@ -45024,7 +44306,7 @@ var zoneRulesCapability = {
45024
44306
  name: "zone-rules",
45025
44307
  scope: "device",
45026
44308
  mode: "singleton",
45027
- deviceTypes: [require_sleep.DeviceType.Camera],
44309
+ deviceTypes: [require_err_msg.DeviceType.Camera],
45028
44310
  methods: {
45029
44311
  /** Read the full rule list for a given stage (empty when no rules
45030
44312
  * are defined yet). */
@@ -45160,8 +44442,6 @@ function createDeviceExtensionProvider(options) {
45160
44442
  slotRef: extensionSlotRef(source, slot.slot),
45161
44443
  slot: slot.slot,
45162
44444
  authority: listing.authority,
45163
- enabled: listing.enabled,
45164
- projects: EXTENSION_SLOTS[slot.slot].descriptor.projects ?? null,
45165
44445
  activation: listing.activation,
45166
44446
  value: listing.value,
45167
44447
  sources: [...listing.sources],
@@ -45196,44 +44476,68 @@ function createDeviceExtensionProvider(options) {
45196
44476
  * through another composition, is refused at save by name (spec §2).
45197
44477
  *
45198
44478
  * The graph is built for ONE save: the CANDIDATE composition (the one being
45199
- * validated) plus every STORED composition. The candidate ALWAYS replaces any
45200
- * stored peer that targets the same device, regardless of where that stored
45201
- * peer sits in `stored` — whether a save introduces or removes a cycle must
45202
- * never depend on array order. Two DIFFERENT stored peers that (wrongly)
45203
- * target the same device are corrupted state, not a cycle to resolve by
45204
- * picking one silently; that is refused by name (D391).
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.
45205
44491
  *
45206
44492
  * `findCompositionCycles` reports at least one cycle per cycle-SET (the DFS
45207
- * marks a node `done` after its first expansion) — enough to refuse a save,
45208
- * not to enumerate every elementary cycle through a shared node. A dotted
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
45209
44497
  * `fieldPath` is matched only as an exact string: a write to `state.value` and
45210
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.
45211
44504
  */
45212
- /** Two stored compositions both target the same device — refused by name, never silently resolved. */
44505
+ /** Two blocks' compositions target the same device — refused by name, never silently resolved. */
45213
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
+ }
45214
44517
  function peerEdges(peer) {
45215
- return peer.composition.features.flatMap((feature) => feature.kind !== "fields" ? [] : Object.entries(feature.fields).map(([path, source]) => [compositionReadKey({
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({
45216
44520
  source: peer.target,
45217
44521
  cap: feature.cap,
45218
- fieldPath: path
45219
- }), fieldReads(source).map(compositionReadKey)]));
44522
+ fieldPath
44523
+ }), require_composition.fieldReads(source).filter((read) => readsComposedValue(peer, written, read)).map(require_composition.compositionReadKey)]));
45220
44524
  }
45221
44525
  /**
45222
- * Every peer that actually takes part in the graph: each stored peer minus
45223
- * whichever one targets the same device as the candidate (the candidate
45224
- * replaces it — this drop happens before any edge is built, so the
45225
- * candidate's old edges never enter the map to begin with), plus the
45226
- * candidate itself, exactly once. Throws {@link CompositionGraphError} if two
45227
- * DIFFERENT stored peers (neither of them the candidate's own old version)
45228
- * 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.
45229
44531
  */
45230
44532
  function resolvedPeers(candidate, stored) {
45231
- const candidateKey = compositionSourceKey(candidate.target);
44533
+ const candidateKey = require_composition.compositionSourceKey(candidate.target);
45232
44534
  const survivors = /* @__PURE__ */ new Map();
45233
44535
  for (const peer of stored) {
45234
- const key = compositionSourceKey(peer.target);
45235
- if (key === candidateKey) continue;
45236
- if (survivors.has(key)) throw new CompositionGraphError(`two stored compositions both target device \`${key}\` — refusing to build the field graph`);
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`);
45237
44541
  survivors.set(key, peer);
45238
44542
  }
45239
44543
  return [...survivors.values(), candidate];
@@ -45262,172 +44566,215 @@ function findCompositionCycles(candidate, stored) {
45262
44566
  for (const node of edges.keys()) visit(node, []);
45263
44567
  return [...found.values()];
45264
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
+ }
45265
44644
  //#endregion
45266
- //#region src/composition/evaluate-field.ts
45267
- function describeRead(read) {
45268
- return `${compositionSourceKey(read.source)} ${read.cap}.${read.fieldPath}`;
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;
45269
44652
  }
45270
- function fromReading(reading, read) {
45271
- if (reading.state === "unavailable") return {
45272
- state: "unavailable",
45273
- value: null,
45274
- reason: `${describeRead(read)}: ${reading.reason}`,
45275
- fatal: reading.fatal
45276
- };
45277
- return {
45278
- state: reading.state,
45279
- value: reading.value,
45280
- reason: reading.state === "stale" ? `${describeRead(read)}: ${reading.reason}` : null,
45281
- 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
45282
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;
45283
44663
  }
45284
- function expressionOutcome(source, read, now) {
45285
- const values = {};
45286
- const staleReasons = [];
45287
- let fatalUnavailable = null;
45288
- let nonFatalUnavailable = null;
45289
- for (const [name, binding] of Object.entries(source.bindings)) {
45290
- if (binding.kind === "literal") {
45291
- values[name] = binding.value;
45292
- continue;
45293
- }
45294
- const reading = read(binding);
45295
- if (reading.state === "unavailable") {
45296
- const outcome = {
45297
- state: "unavailable",
45298
- value: null,
45299
- reason: `binding \`${name}\` (${describeRead(binding)}): ${reading.reason}`,
45300
- fatal: reading.fatal
45301
- };
45302
- if (reading.fatal) {
45303
- fatalUnavailable = outcome;
45304
- break;
45305
- }
45306
- 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));
45307
44678
  continue;
45308
44679
  }
45309
- values[name] = reading.value;
45310
- if (reading.state === "stale") staleReasons.push(`\`${name}\`: ${reading.reason}`);
44680
+ const leaf = leafKind(field);
44681
+ if (leaf) out.push({
44682
+ path,
44683
+ kind: leaf.kind,
44684
+ ...leaf.enumValues ? { enumValues: leaf.enumValues } : {}
44685
+ });
45311
44686
  }
45312
- if (fatalUnavailable !== null) return fatalUnavailable;
45313
- if (nonFatalUnavailable !== null) return nonFatalUnavailable;
45314
- const deadlines = [];
45315
- const result = evaluateExpressionSource(source.expr, values, now, { hooks: { noteDeadline: (at) => deadlines.push(at) } });
45316
- if (!result.ok) return {
45317
- state: "unavailable",
45318
- value: null,
45319
- reason: `the expression failed: ${result.error}`,
45320
- fatal: false
45321
- };
45322
- return {
45323
- state: staleReasons.length > 0 ? "stale" : "fresh",
45324
- value: result.value,
45325
- reason: staleReasons.length > 0 ? staleReasons.join("; ") : null,
45326
- deadline: deadlines.length > 0 ? Math.min(...deadlines) : null
45327
- };
44687
+ return out;
45328
44688
  }
45329
- function evaluateCompositionField(input) {
45330
- const { source } = input;
45331
- switch (source.kind) {
45332
- case "from": return fromReading(input.read(source), source);
45333
- case "expression": return expressionOutcome(source, input.read, input.now);
45334
- case "code": return {
45335
- state: "unavailable",
45336
- value: null,
45337
- reason: "code fields are not runnable in the composer — they arrive in slice 4",
45338
- fatal: true
45339
- };
45340
- }
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
+ }));
45341
44699
  }
45342
44700
  //#endregion
45343
- //#region src/composition/examples.ts
44701
+ //#region src/composition/state-fields.ts
45344
44702
  /**
45345
- * The worked composed-device example: a ROOM. Temperature is the average of N
45346
- * sensors, and occupancy is any motion within the last five minutes. It is the
45347
- * slice-1 acceptance case and the template an author starts from (Phase 4
45348
- * §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).
45349
44706
  */
45350
- var ROOM_OCCUPANCY_WINDOW_MS = 3e5;
45351
- var ROOM_EXAMPLE_TEMPERATURE_SOURCES = [
45352
- {
45353
- addonId: "provider-homeassistant",
45354
- stableId: "sensor.living_room_temperature"
45355
- },
45356
- {
45357
- addonId: "provider-homeassistant",
45358
- stableId: "sensor.sofa_temperature"
45359
- },
45360
- {
45361
- addonId: "provider-homeassistant",
45362
- 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;
45363
44715
  }
45364
- ];
45365
- var ROOM_EXAMPLE_MOTION_SOURCES = [{
45366
- addonId: "provider-homeassistant",
45367
- stableId: "binary_sensor.living_room_motion"
45368
- }, {
45369
- addonId: "provider-homeassistant",
45370
- stableId: "binary_sensor.living_room_presence"
45371
- }];
45372
- 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;
45373
44723
  return {
45374
- kind: "from",
45375
- source,
45376
- cap,
45377
- fieldPath
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 }
45378
44729
  };
45379
44730
  }
45380
- function named(prefix, sources, cap, fieldPath) {
45381
- return Object.fromEntries(sources.map((s, i) => [`${prefix}${i + 1}`, from(s, cap, fieldPath)]));
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
+ }));
45382
44736
  }
45383
- function roomCompositionExample(input) {
45384
- const t = named("t", input.temperatureSources, "temperature-sensor", "celsius");
45385
- const f = named("f", input.temperatureSources, "temperature-sensor", "lastFetchedAt");
45386
- const d = named("d", input.motionSources, "motion", "detected");
45387
- const m = named("m", input.motionSources, "motion", "lastDetectedAt");
45388
- const windows = Object.keys(m).map((name) => `within(${name}, ${input.occupancyWindowMs})`);
45389
- return CompositionSchema.parse({
45390
- target: {
45391
- kind: "new",
45392
- type: require_sleep.DeviceType.Sensor
45393
- },
45394
- features: [{
45395
- kind: "fields",
45396
- cap: "temperature-sensor",
45397
- fields: {
45398
- celsius: {
45399
- kind: "expression",
45400
- expr: `round(avg(${Object.keys(t).join(", ")}), 1)`,
45401
- bindings: t
45402
- },
45403
- lastFetchedAt: {
45404
- kind: "expression",
45405
- expr: `max(${Object.keys(f).join(", ")})`,
45406
- bindings: f
45407
- }
45408
- }
45409
- }, {
45410
- kind: "fields",
45411
- cap: "binary",
45412
- fields: {
45413
- on: {
45414
- kind: "expression",
45415
- expr: `any(${[...Object.keys(d), ...windows].join(", ")})`,
45416
- bindings: {
45417
- ...d,
45418
- ...m
45419
- }
45420
- },
45421
- lastChangedAt: {
45422
- kind: "expression",
45423
- expr: "now",
45424
- bindings: {}
45425
- }
45426
- }
45427
- }]
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] : [];
45428
44754
  });
45429
44755
  }
45430
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
45431
44778
  //#region src/composition/expression-kinds.ts
45432
44779
  var NUMBER = {
45433
44780
  kind: "fixed",
@@ -45459,6 +44806,10 @@ var BUILTIN_RESULT_KINDS = Object.freeze({
45459
44806
  latest: {
45460
44807
  kind: "fixed",
45461
44808
  result: ["number", "null"]
44809
+ },
44810
+ number: {
44811
+ kind: "fixed",
44812
+ result: ["number", "null"]
45462
44813
  }
45463
44814
  });
45464
44815
  var KindInferenceError = class extends Error {};
@@ -45494,7 +44845,7 @@ function arityRange(builtin) {
45494
44845
  return `${builtin.minArgs}–${builtin.maxArgs}`;
45495
44846
  }
45496
44847
  function callKinds(callee, args) {
45497
- const builtin = EXPRESSION_BUILTINS[callee];
44848
+ const builtin = require_composition.EXPRESSION_BUILTINS[callee];
45498
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)}`);
45499
44850
  const rule = BUILTIN_RESULT_KINDS[callee];
45500
44851
  if (rule === void 0) throw new KindInferenceError(`no result-kind rule for builtin '${callee}'`);
@@ -45519,7 +44870,7 @@ function kindsOf(node, bindings) {
45519
44870
  }
45520
44871
  }
45521
44872
  function inferExpressionKinds(input) {
45522
- const compiled = compileExpressionSafe(input.expr);
44873
+ const compiled = require_composition.compileExpressionSafe(input.expr);
45523
44874
  if (!compiled.ok) return {
45524
44875
  ok: false,
45525
44876
  error: compiled.error
@@ -45538,129 +44889,14 @@ function inferExpressionKinds(input) {
45538
44889
  }
45539
44890
  }
45540
44891
  //#endregion
45541
- //#region src/device/schema-fields.ts
45542
- /** Unwrap ZodNullable / ZodOptional / ZodDefault wrappers to reach the inner type.
45543
- * Zod v4 exposes `.unwrap()` on all three wrapper classes. */
45544
- function unwrapSchema(schema) {
45545
- let s = schema;
45546
- while (s instanceof zod.z.ZodNullable || s instanceof zod.z.ZodOptional || s instanceof zod.z.ZodDefault) s = s.unwrap();
45547
- return s;
45548
- }
45549
- function leafKind(schema) {
45550
- const s = unwrapSchema(schema);
45551
- if (s instanceof zod.z.ZodEnum) return {
45552
- kind: "enum",
45553
- enumValues: s.options
45554
- };
45555
- if (s instanceof zod.z.ZodNumber) return { kind: "number" };
45556
- if (s instanceof zod.z.ZodString) return { kind: "string" };
45557
- if (s instanceof zod.z.ZodBoolean) return { kind: "boolean" };
45558
- return null;
45559
- }
45560
- /** Walk a cap status schema into flat wireable leaf fields (dotted paths).
45561
- * Recurses into nested ZodObject (unwrapping nullable/optional/default first),
45562
- * so fields with null live values are still offered. Arrays / unknown shapes
45563
- * are skipped — never throws. */
45564
- function enumerateSchemaFields(schema, prefix = "") {
45565
- const root = unwrapSchema(schema);
45566
- if (!(root instanceof zod.z.ZodObject)) return [];
45567
- const out = [];
45568
- const shape = root.shape;
45569
- for (const [key, field] of Object.entries(shape)) {
45570
- const path = prefix ? `${prefix}.${key}` : key;
45571
- const inner = unwrapSchema(field);
45572
- if (inner instanceof zod.z.ZodObject) {
45573
- out.push(...enumerateSchemaFields(inner, path));
45574
- continue;
45575
- }
45576
- const leaf = leafKind(field);
45577
- if (leaf) out.push({
45578
- path,
45579
- kind: leaf.kind,
45580
- ...leaf.enumValues ? { enumValues: leaf.enumValues } : {}
45581
- });
45582
- }
45583
- return out;
45584
- }
45585
- /** Enumerate the per-item wireable fields of an item-array cap (see
45586
- * `CapabilityStatusItemArray`): the item schema's leaf fields, minus the
45587
- * `keyField` (the key comes from the link's `itemKey`, never from a wired
45588
- * source), each tagged `item: true` so the authoring UI collects an
45589
- * `itemKey` alongside the field. Never throws. */
45590
- function enumerateItemArrayFields(itemArray) {
45591
- return enumerateSchemaFields(itemArray.itemSchema).filter((f) => f.path !== itemArray.keyField).map((f) => ({
45592
- ...f,
45593
- item: true
45594
- }));
45595
- }
45596
- //#endregion
45597
- //#region src/composition/state-fields.ts
44892
+ //#region src/composition/field-kinds.ts
45598
44893
  /**
45599
- * What a capability's state field IS: kind, nullability, whether required. The
45600
- * validator needs nullability, which `enumerateSchemaFields` does not report,
45601
- * and that decides a field's unavailable mode (D393).
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.
45602
44899
  */
45603
- function fieldAt(schema, path) {
45604
- let current = schema;
45605
- for (const segment of path.split(".")) {
45606
- const container = unwrapSchema(current);
45607
- if (!(container instanceof zod.z.ZodObject)) return null;
45608
- const next = container.shape[segment];
45609
- if (!(next instanceof zod.z.ZodType)) return null;
45610
- current = next;
45611
- }
45612
- return current;
45613
- }
45614
- function describeStateField(schema, path) {
45615
- const leaf = enumerateSchemaFields(schema).find((f) => f.path === path);
45616
- if (leaf === void 0) return null;
45617
- const field = fieldAt(schema, path);
45618
- if (field === null) return null;
45619
- return {
45620
- path,
45621
- kind: leaf.kind,
45622
- nullable: field.safeParse(null).success,
45623
- required: !field.safeParse(void 0).success,
45624
- ...leaf.enumValues === void 0 ? {} : { enumValues: leaf.enumValues }
45625
- };
45626
- }
45627
- function describeTopLevelStateFields(schema) {
45628
- return new Map(enumerateSchemaFields(schema).filter((f) => !f.path.includes(".")).flatMap((f) => {
45629
- const described = describeStateField(schema, f.path);
45630
- return described === null ? [] : [[f.path, described]];
45631
- }));
45632
- }
45633
- /** Top-level keys a value MUST carry: those whose schema refuses `undefined`. */
45634
- function requiredTopLevelKeys(schema) {
45635
- const root = unwrapSchema(schema);
45636
- if (!(root instanceof zod.z.ZodObject)) return [];
45637
- return Object.entries(root.shape).flatMap(([key, field]) => field instanceof zod.z.ZodType && !field.safeParse(void 0).success ? [key] : []);
45638
- }
45639
- //#endregion
45640
- //#region src/composition/validate-composition.ts
45641
- function buildCapabilityLookup(defs) {
45642
- const byName = new Map(defs.map((d) => [d.name, d]));
45643
- return (capName) => byName.get(capName) ?? null;
45644
- }
45645
- /** Caps every `BaseDevice` registers on itself; a second registration throws. */
45646
- var BASE_DEVICE_CAPS = new Set(["device-status", "feature-probe"]);
45647
- var PENDING_SELF = {
45648
- addonId: COMPOSER_ADDON_ID,
45649
- stableId: `${COMPOSED_DEVICE_STABLE_ID_PREFIX}(unsaved)`
45650
- };
45651
- function problem(code, path, message) {
45652
- return {
45653
- code,
45654
- path,
45655
- message
45656
- };
45657
- }
45658
- function refused(...problems) {
45659
- return {
45660
- problems,
45661
- fields: []
45662
- };
45663
- }
45664
44900
  function readKinds(read, lookupCap) {
45665
44901
  const def = lookupCap(read.cap);
45666
44902
  const schema = def?.runtimeState ?? def?.status?.schema;
@@ -45735,7 +44971,7 @@ function compatibility(cap, target, kinds, at) {
45735
44971
  */
45736
44972
  function literalStringValue(source) {
45737
44973
  if (source.kind !== "expression") return null;
45738
- const compiled = compileExpressionSafe(source.expr);
44974
+ const compiled = require_composition.compileExpressionSafe(source.expr);
45739
44975
  if (!compiled.ok) return null;
45740
44976
  const ast = compiled.parsed.ast;
45741
44977
  return ast.kind === "literal" && typeof ast.value === "string" ? ast.value : null;
@@ -45755,78 +44991,604 @@ function enumLiteralProblem(cap, target, source, at) {
45755
44991
  }
45756
44992
  function roleOf(source) {
45757
44993
  if (source.kind !== "expression") return "derived";
45758
- if (fieldReads(source).length > 0) return "derived";
45759
- const compiled = compileExpressionSafe(source.expr);
44994
+ if (require_composition.fieldReads(source).length > 0) return "derived";
44995
+ const compiled = require_composition.compileExpressionSafe(source.expr);
45760
44996
  return compiled.ok && compiled.parsed.identifiers.has("now") ? "clock" : "constant";
45761
44997
  }
45762
- function planFeature(feature, featureIndex, target, lookupCap) {
45763
- const at = `features[${featureIndex}]`;
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) {
45764
45417
  const def = lookupCap(feature.cap);
45765
- if (def === null) return refused(problem("unknown-capability", `${at}.cap`, `no capability is named \`${feature.cap}\``));
45766
- if (BASE_DEVICE_CAPS.has(def.name)) return refused(problem("not-composable", `${at}.cap`, `\`${def.name}\` is registered by every device itself — it cannot be composed`));
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`);
45767
45420
  const state = def.runtimeState;
45768
- if (def.scope !== "device" || state === void 0) return refused(problem("not-composable", `${at}.cap`, `\`${def.name}\` has no per-device state to compose`));
45769
- const capProblems = [];
45770
- if (def.deviceTypes !== void 0 && !def.deviceTypes.includes(target.type)) capProblems.push(problem("wrong-device-type", `${at}.cap`, `\`${def.name}\` is not offered on a \`${target.type}\` device (it allows ${def.deviceTypes.join(", ")})`));
45771
- const served = Object.entries(def.methods).filter(([, m]) => m.systemOnly !== true).map(([name]) => `\`${name}\``);
45772
- if (served.length > 0) capProblems.push(problem("has-commands", `${at}.cap`, `\`${def.name}\` has ${served.join(", ")}, which a composed device cannot serve until commands arrive (slice 3)`));
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)];
45773
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"));
45774
- const targetFields = describeTopLevelStateFields(state);
45775
- for (const key of requiredTopLevelKeys(state)) if (!targetFields.has(key)) capProblems.push(problem("not-composable", `${at}.fields`, `\`${def.name}.${key}\` is not a scalar — nested fields arrive in a later slice`));
45776
- else if (!(key in feature.fields)) capProblems.push(problem("missing-field", `${at}.fields.${key}`, `\`${def.name}.${key}\` is required and has no source`));
45777
- const perField = Object.entries(feature.fields).map(([path, source]) => {
45778
- const fieldAt = `${at}.fields.${path}`;
45779
- if (path.includes(".")) return refused(problem("nested-field", fieldAt, `\`${path}\` is nested — nested fields arrive in a later slice`));
45780
- const tf = targetFields.get(path);
45781
- if (tf === void 0) return refused(problem("unknown-field", fieldAt, `\`${def.name}\` has no field \`${path}\``));
45782
- const kinds = sourceKinds(source, lookupCap);
45783
- if (!kinds.ok) return refused(problem(kinds.code, fieldAt, kinds.message));
45784
- const plan = {
45785
- cap: def.name,
45786
- 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: [{
45787
45494
  featureIndex,
45788
- kind: tf.kind,
45789
- nullable: tf.nullable,
45790
- required: tf.required,
45791
- unavailable: tf.nullable ? "null" : "offline",
45792
- role: roleOf(source),
45793
- source,
45794
- reads: [...fieldReads(source)]
45795
- };
45796
- const enumProblem = enumLiteralProblem(def.name, tf, source, fieldAt);
45797
- return {
45798
- problems: [...compatibility(def.name, tf, kinds.kinds, fieldAt), ...enumProblem === null ? [] : [enumProblem]],
45799
- fields: [plan]
45800
- };
45801
- });
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`);
45802
45515
  return {
45803
- problems: [...capProblems, ...perField.flatMap((r) => r.problems)],
45804
- fields: perField.flatMap((r) => r.fields)
45516
+ kind: "existing",
45517
+ found: target
45805
45518
  };
45806
45519
  }
45807
- function planComposition(composition, lookupCap) {
45808
- const target = composition.target;
45809
- if (target.kind === "existing") return refused(problem("reserved-variant", "target", "an EXISTING device as the target arrives in slice 2 — this release composes new devices only"));
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);
45810
45538
  const results = composition.features.map((feature, index) => {
45811
- const at = `features[${index}]`;
45812
- if (feature.kind === "passthrough") return refused(problem("reserved-variant", at, `whole-capability passthrough of \`${feature.cap}\` arrives in slice 3`));
45813
- 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);
45814
45541
  });
45815
45542
  return {
45816
45543
  problems: results.flatMap((r) => r.problems),
45817
- fields: results.flatMap((r) => r.fields)
45544
+ fields: results.flatMap((r) => r.fields),
45545
+ features: results.flatMap((r) => r.features)
45818
45546
  };
45819
45547
  }
45820
45548
  function compositionSourceRefs(composition) {
45821
- const refs = composition.features.flatMap((f) => f.kind === "fields" ? Object.values(f.fields).flatMap(fieldReads).map((r) => r.source) : []);
45822
- 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()];
45823
45551
  }
45824
- function sourceProblems(fields, ctx) {
45825
- const selfKey = ctx.self === null ? null : compositionSourceKey(ctx.self);
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);
45826
45588
  return fields.flatMap((field) => field.reads.flatMap((read) => {
45827
- const at = `features[${field.featureIndex}].fields.${field.path}`;
45828
- const key = compositionSourceKey(read.source);
45829
- if (key === selfKey) return fields.some((f) => f.cap === read.cap && f.path === read.fieldPath) ? [] : [problem("source-field-unknown", at, `this device has no composed field \`${read.cap}.${read.fieldPath}\``)];
45589
+ const at = compositionFieldAt(field);
45590
+ const key = require_composition.compositionSourceKey(read.source);
45591
+ if (key === selfKey) return selfReadProblems(read, at, fields, self);
45830
45592
  const desc = ctx.sources.get(key);
45831
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`)];
45832
45594
  if (desc.kind === "absent") return [problem("source-missing", at, `source \`${key}\` does not exist`)];
@@ -45839,8 +45601,9 @@ function sourceProblems(fields, ctx) {
45839
45601
  * `<addonId>/<stableId>#<cap>.<fieldPath>`. When a node belongs to the
45840
45602
  * CANDIDATE itself (this composition being saved), pull its `cap`/`fieldPath`
45841
45603
  * back out so the refusal can point at that exact field, matching the
45842
- * documented `features[N].fields.<path>` convention — slice 1 has no nested
45843
- * field writes, so splitting `rest` on the FIRST `.` is exact.
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`.
45844
45607
  */
45845
45608
  function candidateFieldOfNode(nodeKey, candidateKey) {
45846
45609
  const prefix = `${candidateKey}#`;
@@ -45860,44 +45623,65 @@ function cyclePath(nodePath, candidateKey, fields) {
45860
45623
  for (const nodeKey of nodePath) {
45861
45624
  const hit = candidateFieldOfNode(nodeKey, candidateKey);
45862
45625
  if (hit === null) continue;
45863
- const field = fields.find((f) => f.cap === hit.cap && f.path === hit.fieldPath);
45864
- if (field !== void 0) return `features[${field.featureIndex}].fields.${field.path}`;
45626
+ const hitKey = `${hit.cap}.${hit.fieldPath}`;
45627
+ const field = fields.find((f) => compositionFieldKey(f) === hitKey);
45628
+ if (field !== void 0) return compositionFieldAt(field);
45865
45629
  }
45866
45630
  return "features";
45867
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
+ }
45868
45640
  /**
45869
- * Cycle check runs against the CANDIDATE (this composition, targeting `self`
45870
- * when it already exists, else a placeholder that cannot collide with a
45871
- * stored target) plus every OTHER stored composition. Two stored peers that
45872
- * (wrongly) share a target are corrupted state — `findCompositionCycles`
45873
- * throws `CompositionGraphError` rather than silently picking one, and this
45874
- * is refused BY NAME (D391) instead of escaping as an uncaught throw.
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.
45875
45650
  */
45876
- function cycleProblems(composition, ctx, fields) {
45651
+ function cycleProblems(composition, ctx, fields, self) {
45877
45652
  const candidate = {
45878
- target: ctx.self ?? PENDING_SELF,
45879
- composition
45653
+ blockId: ctx.blockId ?? "(unsaved)",
45654
+ target: self.ref,
45655
+ composition,
45656
+ replaced: self.replaced
45880
45657
  };
45881
- const candidateKey = compositionSourceKey(candidate.target);
45658
+ const candidateKey = require_composition.compositionSourceKey(candidate.target);
45659
+ const unread = ctx.unreadTargets ?? /* @__PURE__ */ new Map();
45882
45660
  try {
45883
- return findCompositionCycles(candidate, ctx.peers).map((c) => problem("cycle", cyclePath(c.path, candidateKey, fields), `a field reads itself: ${c.path.join(" → ")}`));
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
+ });
45884
45666
  } catch (err) {
45885
45667
  if (err instanceof CompositionGraphError) return [problem("duplicate-target", "features", err.message)];
45886
45668
  throw err;
45887
45669
  }
45888
45670
  }
45889
45671
  function validateComposition(composition, ctx) {
45890
- const plan = planComposition(composition, ctx.lookupCap);
45891
- const cycles = cycleProblems(composition, ctx, plan.fields);
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);
45892
45675
  const problems = [
45893
45676
  ...plan.problems,
45894
- ...sourceProblems(plan.fields, ctx),
45677
+ ...sourceProblems(plan.fields, ctx, self),
45895
45678
  ...cycles
45896
45679
  ];
45897
45680
  return {
45898
45681
  ok: problems.length === 0,
45899
45682
  problems,
45900
- fields: [...plan.fields]
45683
+ fields: [...plan.fields],
45684
+ features: [...plan.features]
45901
45685
  };
45902
45686
  }
45903
45687
  //#endregion
@@ -46062,14 +45846,14 @@ var RUNTIME_DEFAULTS = {
46062
45846
  * any role works, including non-accessory ones like Doorbell).
46063
45847
  */
46064
45848
  var AccessoryKind = {
46065
- Siren: require_sleep.DeviceRole.Siren,
46066
- Floodlight: require_sleep.DeviceRole.Floodlight,
46067
- Spotlight: require_sleep.DeviceRole.Spotlight,
46068
- PirSensor: require_sleep.DeviceRole.PirSensor,
46069
- Chime: require_sleep.DeviceRole.Chime,
46070
- Autotrack: require_sleep.DeviceRole.Autotrack,
46071
- Nightvision: require_sleep.DeviceRole.Nightvision,
46072
- PrivacyMask: require_sleep.DeviceRole.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
46073
45857
  };
46074
45858
  /**
46075
45859
  * Default English label for each accessory kind. Drivers append this
@@ -46212,38 +45996,31 @@ var DeviceConfig = class DeviceConfig {
46212
45996
  };
46213
45997
  //#endregion
46214
45998
  //#region src/device/device-runtime-state.ts
46215
- /**
46216
- * Concrete implementation. Routes every successful write through
46217
- * `writer(capName, slice)` — the kernel hooks this up to
46218
- * `device-state.setCapSlice`, the canonical cross-layer write
46219
- * entrypoint, which handles disk persistence (debounced on the hub)
46220
- * and mirror updates.
46221
- *
46222
- * Schema validation runs in-process before the writer is called —
46223
- * the round-trip should never carry an invalid slice. `flush()`
46224
- * awaits any in-flight writer promises so shutdown is lossless.
46225
- *
46226
- * `initial` is the persisted blob loaded at boot. Slices for caps
46227
- * whose schema hasn't been installed yet are kept in-memory verbatim
46228
- * and validated when the cap registers later.
46229
- */
46230
45999
  var DeviceRuntimeState = class DeviceRuntimeState {
46231
46000
  writer;
46001
+ onWriteFailed;
46232
46002
  /** In-flight writer promises tracked so `flush()` can await them. */
46233
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();
46234
46010
  /** Per-cap committed slice — after schema validation when known. */
46235
46011
  slices;
46236
46012
  /** Per-cap registered schema (set by `installCapSchema`). */
46237
46013
  schemas = /* @__PURE__ */ new Map();
46238
46014
  listeners = /* @__PURE__ */ new Set();
46239
46015
  capListeners = /* @__PURE__ */ new Map();
46240
- constructor(initial, writer) {
46016
+ constructor(initial, writer, onWriteFailed) {
46241
46017
  this.writer = writer;
46018
+ this.onWriteFailed = onWriteFailed;
46242
46019
  this.slices = /* @__PURE__ */ new Map();
46243
46020
  for (const [k, v] of Object.entries(initial)) if (v && typeof v === "object" && !Array.isArray(v)) this.slices.set(k, { ...v });
46244
46021
  }
46245
- static fromInitial(initial, writer) {
46246
- return new DeviceRuntimeState(initial, writer);
46022
+ static fromInitial(initial, writer, onWriteFailed) {
46023
+ return new DeviceRuntimeState(initial, writer, onWriteFailed);
46247
46024
  }
46248
46025
  installCapSchema(capName, schema) {
46249
46026
  const existing = this.schemas.get(capName);
@@ -46287,10 +46064,15 @@ var DeviceRuntimeState = class DeviceRuntimeState {
46287
46064
  ...value
46288
46065
  } : { ...value };
46289
46066
  const parsed = schema.parse(next);
46290
- if (shallowEqual(current, parsed)) return;
46067
+ if (shallowEqual(current, parsed) && !this.failedWrites.has(capName)) return;
46291
46068
  this.slices.set(capName, parsed);
46292
46069
  this.fireListeners([capName]);
46293
- const writePromise = this.writer(capName, { ...parsed }).catch(() => {});
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
+ });
46294
46076
  this.pendingWrites.add(writePromise);
46295
46077
  writePromise.finally(() => {
46296
46078
  this.pendingWrites.delete(writePromise);
@@ -46571,7 +46353,16 @@ var BaseDevice = class {
46571
46353
  });
46572
46354
  };
46573
46355
  const initial = ctx.initialRuntimeState ?? {};
46574
- this.runtimeState = DeviceRuntimeState.fromInitial(initial, writer);
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);
46575
46366
  ctx.bindRuntimeState?.(this.runtimeState);
46576
46367
  ctx.registerNativeCap?.(deviceStatusCapability, {});
46577
46368
  const seed = {
@@ -47166,7 +46957,7 @@ var BaseDeviceProvider = class extends require_sleep.BaseAddon {
47166
46957
  */
47167
46958
  async getManualCreationType() {
47168
46959
  if (!await this.supportsManualCreation()) return { deviceType: null };
47169
- return { deviceType: Object.values(require_sleep.DeviceType).find((t) => this.deviceClasses[t] !== void 0) ?? null };
46960
+ return { deviceType: Object.values(require_err_msg.DeviceType).find((t) => this.deviceClasses[t] !== void 0) ?? null };
47170
46961
  }
47171
46962
  async adoptDiscoveredDevice(_input) {
47172
46963
  throw new Error(`${this.providerName} provider does not support discovery-based adoption`);
@@ -47373,14 +47164,14 @@ var BaseDeviceProvider = class extends require_sleep.BaseAddon {
47373
47164
  });
47374
47165
  const meta = await this.ctx.api.deviceManager.loadMeta.query({ deviceId: id });
47375
47166
  if (meta === null) throw new Error(`${this.providerName}: no persisted meta for "${stableId}" (id ${id}) — cannot reload`);
47376
- const deviceType = Object.values(require_sleep.DeviceType).find((t) => t === meta.type);
47167
+ const deviceType = Object.values(require_err_msg.DeviceType).find((t) => t === meta.type);
47377
47168
  const Class = deviceType !== void 0 ? this.deviceClasses[deviceType] : void 0;
47378
47169
  if (!Class) throw new Error(`${this.providerName}: no device class for type "${meta.type}" — "${stableId}" is an accessory child; reload its parent instead`);
47379
47170
  await devices.create(stableId, Class, {}, meta.parentDeviceId ?? null);
47380
47171
  const rows = await this.ctx.api.deviceManager.listPersistedByAddon.query({ addonId: this.addonId });
47381
47172
  for (const row of rows) {
47382
47173
  if (row.parentDeviceId !== id) continue;
47383
- const childType = Object.values(require_sleep.DeviceType).find((t) => t === row.type);
47174
+ const childType = Object.values(require_err_msg.DeviceType).find((t) => t === row.type);
47384
47175
  const ChildClass = childType !== void 0 ? this.deviceClasses[childType] : void 0;
47385
47176
  if (!ChildClass) continue;
47386
47177
  try {
@@ -47807,7 +47598,7 @@ function expandContainer(id, byId, byParent, visited, out) {
47807
47598
  if (visited.has(id)) return;
47808
47599
  const candidate = byId.get(id);
47809
47600
  if (!candidate) return;
47810
- if (candidate.type !== require_sleep.DeviceType.Container) {
47601
+ if (candidate.type !== require_err_msg.DeviceType.Container) {
47811
47602
  out.add(id);
47812
47603
  return;
47813
47604
  }
@@ -47851,11 +47642,11 @@ function legacyAutoLinkIds(params) {
47851
47642
  const ids = /* @__PURE__ */ new Set();
47852
47643
  const expand = (id) => expandContainer(id, byId, byParent, /* @__PURE__ */ new Set(), ids);
47853
47644
  for (const c of candidates) {
47854
- if (c.type === require_sleep.DeviceType.Container) continue;
47645
+ if (c.type === require_err_msg.DeviceType.Container) continue;
47855
47646
  if (c.location !== selfLocation) continue;
47856
47647
  if (c.parentDeviceId === selfId) continue;
47857
47648
  const parent = c.parentDeviceId !== null ? byId.get(c.parentDeviceId) : void 0;
47858
- if (parent !== void 0 && parent.type === require_sleep.DeviceType.Container) {
47649
+ if (parent !== void 0 && parent.type === require_err_msg.DeviceType.Container) {
47859
47650
  expand(parent.id);
47860
47651
  continue;
47861
47652
  }
@@ -48147,39 +47938,39 @@ var DeclaredDevices = class {
48147
47938
  * placeholder HA types whose inline control renders a dash).
48148
47939
  */
48149
47940
  var DEVICE_TYPE_CONTROL_KIND = {
48150
- [require_sleep.DeviceType.Cover]: "cover",
48151
- [require_sleep.DeviceType.Valve]: "valve",
48152
- [require_sleep.DeviceType.Humidifier]: "humidifier",
48153
- [require_sleep.DeviceType.WaterHeater]: "water-heater",
48154
- [require_sleep.DeviceType.Camera]: "dummy",
48155
- [require_sleep.DeviceType.Hub]: "dummy",
48156
- [require_sleep.DeviceType.Switch]: "switch",
48157
- [require_sleep.DeviceType.Siren]: "switch",
48158
- [require_sleep.DeviceType.Light]: "light",
48159
- [require_sleep.DeviceType.Fan]: "fan",
48160
- [require_sleep.DeviceType.Sensor]: "sensor",
48161
- [require_sleep.DeviceType.Thermostat]: "thermostat",
48162
- [require_sleep.DeviceType.Climate]: "climate",
48163
- [require_sleep.DeviceType.Button]: "button",
48164
- [require_sleep.DeviceType.EventEmitter]: "dummy",
48165
- [require_sleep.DeviceType.Update]: "update",
48166
- [require_sleep.DeviceType.Generic]: "dummy",
48167
- [require_sleep.DeviceType.Notifier]: "dummy",
48168
- [require_sleep.DeviceType.Script]: "script",
48169
- [require_sleep.DeviceType.Automation]: "automation",
48170
- [require_sleep.DeviceType.Lock]: "lock",
48171
- [require_sleep.DeviceType.MediaPlayer]: "media-player",
48172
- [require_sleep.DeviceType.AlarmPanel]: "alarm",
48173
- [require_sleep.DeviceType.Control]: "control",
48174
- [require_sleep.DeviceType.Presence]: "sensor",
48175
- [require_sleep.DeviceType.Weather]: "weather",
48176
- [require_sleep.DeviceType.Vacuum]: "vacuum",
48177
- [require_sleep.DeviceType.LawnMower]: "lawn-mower",
48178
- [require_sleep.DeviceType.Container]: "dummy",
48179
- [require_sleep.DeviceType.Image]: "image",
48180
- [require_sleep.DeviceType.PetFeeder]: "pet-feeder"
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"
48181
47972
  };
48182
- var DEVICE_TYPE_VALUES = new Set(Object.values(require_sleep.DeviceType));
47973
+ var DEVICE_TYPE_VALUES = new Set(Object.values(require_err_msg.DeviceType));
48183
47974
  /**
48184
47975
  * Runtime-string-safe base resolver: a device row's `type` crosses the wire as
48185
47976
  * a plain string, so an unrecognised value resolves to `null` instead of
@@ -48223,15 +48014,15 @@ var SENSOR_FEATURES = [
48223
48014
  function pickAccessoryControl(device) {
48224
48015
  const set = new Set(device.features);
48225
48016
  switch (device.type) {
48226
- case require_sleep.DeviceType.Cover: return { kind: "cover" };
48227
- case require_sleep.DeviceType.Valve: return { kind: "valve" };
48228
- case require_sleep.DeviceType.Lock: return { kind: "lock" };
48229
- case require_sleep.DeviceType.AlarmPanel: return { kind: "alarm" };
48230
- case require_sleep.DeviceType.Light: return set.has("brightness") ? { kind: "brightness" } : { kind: "switch" };
48231
- case require_sleep.DeviceType.Switch:
48232
- case require_sleep.DeviceType.Siren: return { kind: "switch" };
48233
- case require_sleep.DeviceType.Sensor:
48234
- case require_sleep.DeviceType.Presence: {
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: {
48235
48026
  const sensor = SENSOR_FEATURES.find((f) => set.has(f));
48236
48027
  return sensor ? {
48237
48028
  kind: "sensor",
@@ -48310,7 +48101,7 @@ function deviceMatchesProfile(features, profile) {
48310
48101
  var BATTERY_DEVICE_PROFILE = {
48311
48102
  id: "battery",
48312
48103
  label: "Battery-operated camera",
48313
- when: { hasFeature: require_sleep.DeviceFeature.BatteryOperated },
48104
+ when: { hasFeature: require_err_msg.DeviceFeature.BatteryOperated },
48314
48105
  defaults: {
48315
48106
  audioMode: "disabled",
48316
48107
  detectionMode: "on-motion"
@@ -52941,6 +52732,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
52941
52732
  addonId: null,
52942
52733
  access: "view"
52943
52734
  },
52735
+ "coreBlocks.getCustomization": {
52736
+ capName: "core-blocks",
52737
+ capScope: "system",
52738
+ addonId: null,
52739
+ access: "view"
52740
+ },
52944
52741
  "coreBlocks.getTypeDefs": {
52945
52742
  capName: "core-blocks",
52946
52743
  capScope: "system",
@@ -54063,6 +53860,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
54063
53860
  addonId: null,
54064
53861
  access: "create"
54065
53862
  },
53863
+ "deviceState.claimFields": {
53864
+ capName: "device-state",
53865
+ capScope: "system",
53866
+ addonId: null,
53867
+ access: "create"
53868
+ },
54066
53869
  "deviceState.getAllSnapshots": {
54067
53870
  capName: "device-state",
54068
53871
  capScope: "system",
@@ -54075,12 +53878,36 @@ var METHOD_ACCESS_MAP = Object.freeze({
54075
53878
  addonId: null,
54076
53879
  access: "view"
54077
53880
  },
53881
+ "deviceState.getNativeCapSlice": {
53882
+ capName: "device-state",
53883
+ capScope: "system",
53884
+ addonId: null,
53885
+ access: "view"
53886
+ },
54078
53887
  "deviceState.getSnapshot": {
54079
53888
  capName: "device-state",
54080
53889
  capScope: "system",
54081
53890
  addonId: null,
54082
53891
  access: "view"
54083
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
+ },
54084
53911
  "deviceState.setCapSlice": {
54085
53912
  capName: "device-state",
54086
53913
  capScope: "system",
@@ -59931,16 +59758,36 @@ var METHOD_DEVICE_SELECTORS = Object.freeze({
59931
59758
  form: "single",
59932
59759
  optional: false
59933
59760
  }],
59761
+ "deviceState.claimFields": [{
59762
+ name: "deviceId",
59763
+ form: "single",
59764
+ optional: false
59765
+ }],
59934
59766
  "deviceState.getCapSlice": [{
59935
59767
  name: "deviceId",
59936
59768
  form: "single",
59937
59769
  optional: false
59938
59770
  }],
59771
+ "deviceState.getNativeCapSlice": [{
59772
+ name: "deviceId",
59773
+ form: "single",
59774
+ optional: false
59775
+ }],
59939
59776
  "deviceState.getSnapshot": [{
59940
59777
  name: "deviceId",
59941
59778
  form: "single",
59942
59779
  optional: false
59943
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
+ }],
59944
59791
  "deviceState.setCapSlice": [{
59945
59792
  name: "deviceId",
59946
59793
  form: "single",
@@ -61436,8 +61283,12 @@ var SYSTEM_SCOPE_DEVICE_METHODS = [
61436
61283
  "deviceManager.updateConfig",
61437
61284
  "deviceManager.updateDeviceField",
61438
61285
  "deviceManager.updateDeviceFieldsBatch",
61286
+ "deviceState.claimFields",
61439
61287
  "deviceState.getCapSlice",
61288
+ "deviceState.getNativeCapSlice",
61440
61289
  "deviceState.getSnapshot",
61290
+ "deviceState.patchOwnedFields",
61291
+ "deviceState.releaseClaim",
61441
61292
  "deviceState.setCapSlice",
61442
61293
  "faceGallery.getFaceByTrack",
61443
61294
  "faceGallery.listRecentFaces",
@@ -62117,6 +61968,7 @@ function createSystemProxy(api) {
62117
61968
  updateComposition: (input) => dispatch("coreBlocks", "updateComposition", "mutation", input),
62118
61969
  getComposition: (input) => dispatch("coreBlocks", "getComposition", "query", input),
62119
61970
  listCompositions: (input) => dispatch("coreBlocks", "listCompositions", "query", input),
61971
+ getCustomization: (input) => dispatch("coreBlocks", "getCustomization", "query", input),
62120
61972
  validateComposition: (input) => dispatch("coreBlocks", "validateComposition", "mutation", input)
62121
61973
  },
62122
61974
  decoder: {
@@ -62220,7 +62072,10 @@ function createSystemProxy(api) {
62220
62072
  createDevice: (input) => dispatch("deviceProvider", "createDevice", "mutation", input),
62221
62073
  testCreationField: (input) => dispatch("deviceProvider", "testCreationField", "mutation", input)
62222
62074
  },
62223
- deviceState: { getAllSnapshots: (input) => dispatch("deviceState", "getAllSnapshots", "query", input) },
62075
+ deviceState: {
62076
+ getAllSnapshots: (input) => dispatch("deviceState", "getAllSnapshots", "query", input),
62077
+ listClaims: (input) => dispatch("deviceState", "listClaims", "query", input)
62078
+ },
62224
62079
  faceGallery: {
62225
62080
  listIdentities: (input) => dispatch("faceGallery", "listIdentities", "query", input),
62226
62081
  createIdentity: (input) => dispatch("faceGallery", "createIdentity", "mutation", input),
@@ -68278,7 +68133,7 @@ exports.AUDIO_BACKEND_CHOICES = AUDIO_BACKEND_CHOICES;
68278
68133
  exports.AUDIO_CHUNK_FORMATS = require_sleep.AUDIO_CHUNK_FORMATS;
68279
68134
  exports.AUDIO_MACRO_LABELS = AUDIO_MACRO_LABELS;
68280
68135
  exports.AUDIO_NOT_CLASSIFIED_REASONS = AUDIO_NOT_CLASSIFIED_REASONS;
68281
- exports.AUDIO_PRESETS = require_canonical_hash.AUDIO_PRESETS;
68136
+ exports.AUDIO_PRESETS = require_composition.AUDIO_PRESETS;
68282
68137
  exports.AUDIO_SCALE_EPOCH_ISO = AUDIO_SCALE_EPOCH_ISO;
68283
68138
  exports.AUDIO_SCALE_EPOCH_MS = AUDIO_SCALE_EPOCH_MS;
68284
68139
  exports.AccessoriesStatusSchema = AccessoriesStatusSchema;
@@ -68428,13 +68283,17 @@ exports.CLUSTER_MODEL_SECTION_ID = CLUSTER_MODEL_SECTION_ID;
68428
68283
  exports.CLUSTER_STEP_SETTING_FIELDS = CLUSTER_STEP_SETTING_FIELDS;
68429
68284
  exports.COCO_80_LABELS = COCO_80_LABELS;
68430
68285
  exports.COCO_TO_MACRO = COCO_TO_MACRO;
68431
- exports.COMPOSED_DEVICE_STABLE_ID_PREFIX = COMPOSED_DEVICE_STABLE_ID_PREFIX;
68432
- exports.COMPOSER_ADDON_ID = COMPOSER_ADDON_ID;
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;
68433
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;
68434
68292
  exports.CONNECTION_TEST_TIMEOUT_MS = CONNECTION_TEST_TIMEOUT_MS;
68435
68293
  exports.CONTAINER_CHILD_PRIORITY = CONTAINER_CHILD_PRIORITY;
68436
68294
  exports.CORE_BLOCKS_ADDON_ID = CORE_BLOCKS_ADDON_ID;
68437
68295
  exports.CORE_BLOCK_ADDON_PREFIX = CORE_BLOCK_ADDON_PREFIX;
68296
+ exports.CUSTOMIZATION_BLOCK_NAME_PREFIX = require_composition.CUSTOMIZATION_BLOCK_NAME_PREFIX;
68438
68297
  exports.CamProfileSchema = require_sleep.CamProfileSchema;
68439
68298
  exports.CamStreamDescriptorSchema = CamStreamDescriptorSchema;
68440
68299
  exports.CamStreamKindSchema = require_sleep.CamStreamKindSchema;
@@ -68474,7 +68333,9 @@ exports.CandidateQueryFilterSchema = CandidateQueryFilterSchema;
68474
68333
  exports.CapScopeSchema = CapScopeSchema;
68475
68334
  exports.CapabilityBindingsSchema = CapabilityBindingsSchema;
68476
68335
  exports.CarbonMonoxideStatusSchema = CarbonMonoxideStatusSchema;
68477
- exports.ChargingStatus = require_sleep.ChargingStatus;
68336
+ exports.ChargingStatus = require_err_msg.ChargingStatus;
68337
+ exports.ClaimOutcomeSchema = ClaimOutcomeSchema;
68338
+ exports.ClaimsListingSchema = ClaimsListingSchema;
68478
68339
  exports.ClientNetworkStatsSchema = ClientNetworkStatsSchema;
68479
68340
  exports.ClimateControlStatusSchema = ClimateControlStatusSchema;
68480
68341
  exports.ClipBytesOfferSchema = ClipBytesOfferSchema;
@@ -68504,37 +68365,42 @@ exports.ComposerApplyResultSchema = ComposerApplyResultSchema;
68504
68365
  exports.ComposerBlockSchema = ComposerBlockSchema;
68505
68366
  exports.ComposerBlockVerdictSchema = ComposerBlockVerdictSchema;
68506
68367
  exports.ComposerUnreadableBlockSchema = ComposerUnreadableBlockSchema;
68507
- exports.CompositionBindingSchema = CompositionBindingSchema;
68368
+ exports.CompositionBindingSchema = require_composition.CompositionBindingSchema;
68508
68369
  exports.CompositionBlockInputSchema = CompositionBlockInputSchema;
68509
68370
  exports.CompositionBlockStateSchema = CompositionBlockStateSchema;
68510
- exports.CompositionCodeSourceSchema = CompositionCodeSourceSchema;
68511
- exports.CompositionCommandTargetSchema = CompositionCommandTargetSchema;
68512
- exports.CompositionExistingTargetSchema = CompositionExistingTargetSchema;
68513
- exports.CompositionExpressionSourceSchema = CompositionExpressionSourceSchema;
68514
- exports.CompositionFeatureSchema = CompositionFeatureSchema;
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;
68515
68380
  exports.CompositionFieldKindSchema = CompositionFieldKindSchema;
68516
68381
  exports.CompositionFieldPlanSchema = CompositionFieldPlanSchema;
68517
- exports.CompositionFieldReadSchema = CompositionFieldReadSchema;
68382
+ exports.CompositionFieldReadSchema = require_composition.CompositionFieldReadSchema;
68518
68383
  exports.CompositionFieldRoleSchema = CompositionFieldRoleSchema;
68519
- exports.CompositionFieldSourceSchema = CompositionFieldSourceSchema;
68384
+ exports.CompositionFieldSourceSchema = require_composition.CompositionFieldSourceSchema;
68520
68385
  exports.CompositionFieldStateKindSchema = CompositionFieldStateKindSchema;
68521
68386
  exports.CompositionFieldStateSchema = CompositionFieldStateSchema;
68522
- exports.CompositionFieldsFeatureSchema = CompositionFieldsFeatureSchema;
68523
- exports.CompositionForwardCommandSchema = CompositionForwardCommandSchema;
68524
- exports.CompositionFromSourceSchema = CompositionFromSourceSchema;
68387
+ exports.CompositionFieldsFeatureSchema = require_composition.CompositionFieldsFeatureSchema;
68388
+ exports.CompositionForwardCommandSchema = require_composition.CompositionForwardCommandSchema;
68389
+ exports.CompositionFromSourceSchema = require_composition.CompositionFromSourceSchema;
68525
68390
  exports.CompositionGetReadSchema = CompositionGetReadSchema;
68526
68391
  exports.CompositionGraphError = CompositionGraphError;
68392
+ exports.CompositionItemEntrySchema = require_composition.CompositionItemEntrySchema;
68527
68393
  exports.CompositionListReadSchema = CompositionListReadSchema;
68528
- exports.CompositionLiteralBindingSchema = CompositionLiteralBindingSchema;
68529
- exports.CompositionNewTargetSchema = CompositionNewTargetSchema;
68530
- exports.CompositionPassthroughFeatureSchema = CompositionPassthroughFeatureSchema;
68394
+ exports.CompositionLiteralBindingSchema = require_composition.CompositionLiteralBindingSchema;
68395
+ exports.CompositionNewTargetSchema = require_composition.CompositionNewTargetSchema;
68396
+ exports.CompositionPassthroughFeatureSchema = require_composition.CompositionPassthroughFeatureSchema;
68531
68397
  exports.CompositionProblemCodeSchema = CompositionProblemCodeSchema;
68532
68398
  exports.CompositionProblemSchema = CompositionProblemSchema;
68533
68399
  exports.CompositionRunStatusSchema = CompositionRunStatusSchema;
68534
- exports.CompositionSchema = CompositionSchema;
68535
- exports.CompositionSourceRefSchema = CompositionSourceRefSchema;
68400
+ exports.CompositionSchema = require_composition.CompositionSchema;
68401
+ exports.CompositionSourceRefSchema = require_composition.CompositionSourceRefSchema;
68536
68402
  exports.CompositionStateReadSchema = CompositionStateReadSchema;
68537
- exports.CompositionTargetSchema = CompositionTargetSchema;
68403
+ exports.CompositionTargetSchema = require_composition.CompositionTargetSchema;
68538
68404
  exports.CompositionUnavailableModeSchema = CompositionUnavailableModeSchema;
68539
68405
  exports.CompositionValidationSchema = CompositionValidationSchema;
68540
68406
  exports.CompositionViewSchema = CompositionViewSchema;
@@ -68546,6 +68412,8 @@ exports.ConnectionTestInputSchema = ConnectionTestInputSchema;
68546
68412
  exports.ConnectionTestOutcomeSchema = ConnectionTestOutcomeSchema;
68547
68413
  exports.ConnectivityStatusSchema = ConnectivityStatusSchema;
68548
68414
  exports.ConsumableItemSchema = ConsumableItemSchema;
68415
+ exports.ConsumableRemainingSchema = ConsumableRemainingSchema;
68416
+ exports.ConsumableRemainingUnitSchema = ConsumableRemainingUnitSchema;
68549
68417
  exports.ConsumablesStatusSchema = ConsumablesStatusSchema;
68550
68418
  exports.ContactStatusSchema = ContactStatusSchema;
68551
68419
  exports.ControlKindSchema = ControlKindSchema;
@@ -68639,15 +68507,15 @@ exports.DeviceDiscoveryStatusSchema = DeviceDiscoveryStatusSchema;
68639
68507
  exports.DeviceExportExposeInputSchema = ExposeInputSchema;
68640
68508
  exports.DeviceExportStatusSchema = DeviceExportStatusSchema;
68641
68509
  exports.DeviceExportUnexposeInputSchema = UnexposeInputSchema;
68642
- exports.DeviceFeature = require_sleep.DeviceFeature;
68510
+ exports.DeviceFeature = require_err_msg.DeviceFeature;
68643
68511
  exports.DeviceInfoSchema = DeviceInfoSchema;
68644
68512
  exports.DeviceNetworkStatsSchema = DeviceNetworkStatsSchema;
68645
68513
  exports.DeviceRestoreRetryScheduler = DeviceRestoreRetryScheduler;
68646
- exports.DeviceRole = require_sleep.DeviceRole;
68514
+ exports.DeviceRole = require_err_msg.DeviceRole;
68647
68515
  exports.DeviceRuntimeState = DeviceRuntimeState;
68648
68516
  exports.DeviceSelectorSchema = DeviceSelectorSchema;
68649
68517
  exports.DeviceStatusSchema = DeviceStatusSchema;
68650
- exports.DeviceType = require_sleep.DeviceType;
68518
+ exports.DeviceType = require_err_msg.DeviceType;
68651
68519
  exports.DiagnosticIdSchema = DiagnosticIdSchema;
68652
68520
  exports.DiagnosticWindowPatchSchema = DiagnosticWindowPatchSchema;
68653
68521
  exports.DiagnosticWindowSchema = DiagnosticWindowSchema;
@@ -68670,11 +68538,11 @@ exports.EVENT_OWNER_TYPES = EVENT_OWNER_TYPES;
68670
68538
  exports.EVENT_PAD_MS = EVENT_PAD_MS;
68671
68539
  exports.EVENT_TAXONOMY = EVENT_TAXONOMY;
68672
68540
  exports.EXPORT_DENSE_MAX_RANGES = EXPORT_DENSE_MAX_RANGES;
68673
- exports.EXPRESSION_BUILTINS = EXPRESSION_BUILTINS;
68674
- exports.EXPRESSION_BUILTIN_NAMES = EXPRESSION_BUILTIN_NAMES;
68675
- exports.EXPRESSION_COMPILE_CACHE_CAPACITY = EXPRESSION_COMPILE_CACHE_CAPACITY;
68676
- exports.EXPRESSION_IDENTIFIER_RE = EXPRESSION_IDENTIFIER_RE;
68677
- 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;
68678
68546
  exports.EXTENSION_HOST_DEVICE_TYPES = EXTENSION_HOST_DEVICE_TYPES;
68679
68547
  exports.EXTENSION_SLOTS = EXTENSION_SLOTS;
68680
68548
  exports.EXTENSION_SLOT_IDS = EXTENSION_SLOT_IDS;
@@ -68730,11 +68598,11 @@ exports.ExportTimelapseSchema = ExportTimelapseSchema;
68730
68598
  exports.ExposedDeviceSchema = ExposedDeviceSchema;
68731
68599
  exports.ExposureModeSchema = ExposureModeSchema;
68732
68600
  exports.ExpressionBindingSourceSchema = ExpressionBindingSourceSchema;
68733
- exports.ExpressionEvalError = ExpressionEvalError;
68601
+ exports.ExpressionEvalError = require_composition.ExpressionEvalError;
68734
68602
  exports.ExpressionFieldBindingSchema = ExpressionFieldBindingSchema;
68735
68603
  exports.ExpressionGlobalBindingSchema = ExpressionGlobalBindingSchema;
68736
68604
  exports.ExpressionLiteralBindingSchema = ExpressionLiteralBindingSchema;
68737
- exports.ExpressionParseError = ExpressionParseError;
68605
+ exports.ExpressionParseError = require_composition.ExpressionParseError;
68738
68606
  exports.ExpressionSourceSchema = ExpressionSourceSchema;
68739
68607
  exports.ExtensionCandidateSchema = ExtensionCandidateSchema;
68740
68608
  exports.ExtensionCandidatesSchema = ExtensionCandidatesSchema;
@@ -68751,6 +68619,8 @@ exports.ExtensionSlotValueSchema = ExtensionSlotValueSchema;
68751
68619
  exports.ExtensionSourceAvailabilitySchema = ExtensionSourceAvailabilitySchema;
68752
68620
  exports.ExtensionSourceHealthSchema = ExtensionSourceHealthSchema;
68753
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;
68754
68624
  exports.FIRST_LEVEL_MACRO_CLASSES = FIRST_LEVEL_MACRO_CLASSES;
68755
68625
  exports.FULL_IMAGE_BBOX = FULL_IMAGE_BBOX;
68756
68626
  exports.FailureContributionSchema = FailureContributionSchema;
@@ -68760,8 +68630,10 @@ exports.FanControlStatusSchema = FanControlStatusSchema;
68760
68630
  exports.FanDirectionSchema = FanDirectionSchema;
68761
68631
  exports.FeatureManifestSchema = FeatureManifestSchema;
68762
68632
  exports.FeatureProbeStatusSchema = FeatureProbeStatusSchema;
68633
+ exports.FieldClaimModeSchema = FieldClaimModeSchema;
68634
+ exports.FieldClaimSchema = FieldClaimSchema;
68763
68635
  exports.FloodStatusSchema = FloodStatusSchema;
68764
- exports.Fmp4BoxSplitter = require_canonical_hash.Fmp4BoxSplitter;
68636
+ exports.Fmp4BoxSplitter = require_composition.Fmp4BoxSplitter;
68765
68637
  exports.FrameHandleFormatSchema = require_sleep.FrameHandleFormatSchema;
68766
68638
  exports.FrameHandleSchema = require_sleep.FrameHandleSchema;
68767
68639
  exports.FrameInputSchema = FrameInputSchema;
@@ -68890,16 +68762,17 @@ exports.MAX_BIRTH_DECISION_RECORDS = MAX_BIRTH_DECISION_RECORDS;
68890
68762
  exports.MAX_BIRTH_LATENCY_PROXY_MS = MAX_BIRTH_LATENCY_PROXY_MS;
68891
68763
  exports.MAX_CLIP_EVENT_IDS = MAX_CLIP_EVENT_IDS;
68892
68764
  exports.MAX_CLIP_LABELS = MAX_CLIP_LABELS;
68893
- exports.MAX_COMPOSITION_CODE_LENGTH = MAX_COMPOSITION_CODE_LENGTH;
68894
- exports.MAX_COMPOSITION_FEATURES = MAX_COMPOSITION_FEATURES;
68895
- 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;
68896
68769
  exports.MAX_CONDITION_DEPTH = MAX_CONDITION_DEPTH;
68897
68770
  exports.MAX_CONDITION_LEAVES = MAX_CONDITION_LEAVES;
68898
- exports.MAX_EXPRESSION_AST_NODES = MAX_EXPRESSION_AST_NODES;
68899
- exports.MAX_EXPRESSION_BINDINGS = MAX_EXPRESSION_BINDINGS;
68900
- exports.MAX_EXPRESSION_CALL_ARGS = MAX_EXPRESSION_CALL_ARGS;
68901
- exports.MAX_EXPRESSION_EVAL_STEPS = MAX_EXPRESSION_EVAL_STEPS;
68902
- 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;
68903
68776
  exports.MAX_KEYS = MAX_KEYS;
68904
68777
  exports.MAX_REASONS_PER_KEY = MAX_REASONS_PER_KEY;
68905
68778
  exports.MAX_SENSOR_TRIGGER_DEVICES = MAX_SENSOR_TRIGGER_DEVICES;
@@ -69155,6 +69028,7 @@ exports.OPS_LOG_DEFAULT_LIMIT = OPS_LOG_DEFAULT_LIMIT;
69155
69028
  exports.OPS_LOG_RING_DEFAULT_MAX = OPS_LOG_RING_DEFAULT_MAX;
69156
69029
  exports.OVERFLOW_REASON = OVERFLOW_REASON;
69157
69030
  exports.OVERLAY_LAYERS = OVERLAY_LAYERS;
69031
+ exports.OWNER_ONLY_EVENT_CATEGORIES = require_event_category.OWNER_ONLY_EVENT_CATEGORIES;
69158
69032
  exports.OauthIntegrationDescriptorSchema = OauthIntegrationDescriptorSchema;
69159
69033
  exports.ObjectEventSchema = ObjectEventSchema;
69160
69034
  exports.OccupancyBurstRefusalSchema = OccupancyBurstRefusalSchema;
@@ -69185,6 +69059,7 @@ exports.PEER_BYTES_CHUNK_BYTES = require_sleep.PEER_BYTES_CHUNK_BYTES;
69185
69059
  exports.PEER_BYTES_MAX_OUTSTANDING = require_sleep.PEER_BYTES_MAX_OUTSTANDING;
69186
69060
  exports.PEER_BYTES_STALL_MS = require_sleep.PEER_BYTES_STALL_MS;
69187
69061
  exports.PEER_BYTES_TICKET_TTL_MS = require_sleep.PEER_BYTES_TICKET_TTL_MS;
69062
+ exports.PENDING_BLOCK_ID = PENDING_BLOCK_ID;
69188
69063
  exports.PET_FEEDER_MANUAL_FEED_MAX = PET_FEEDER_MANUAL_FEED_MAX;
69189
69064
  exports.PET_FEEDER_MANUAL_FEED_MIN = PET_FEEDER_MANUAL_FEED_MIN;
69190
69065
  exports.PIPELINE_ANALYTICS_CAP_NAME = PIPELINE_ANALYTICS_CAP_NAME;
@@ -69256,7 +69131,7 @@ exports.RECORDING_SOURCE_CAMSTACK = RECORDING_SOURCE_CAMSTACK;
69256
69131
  exports.RECORDING_SOURCE_CAMSTACK_ADDON = RECORDING_SOURCE_CAMSTACK_ADDON;
69257
69132
  exports.RECORDING_TIMELINE_BATCH_MAX = RECORDING_TIMELINE_BATCH_MAX;
69258
69133
  exports.REDACTED_SECRET = REDACTED_SECRET;
69259
- exports.RESERVED_BINDING_NAMES = RESERVED_BINDING_NAMES;
69134
+ exports.RESERVED_BINDING_NAMES = require_composition.RESERVED_BINDING_NAMES;
69260
69135
  exports.RESERVED_COLLECTION_SOURCES = RESERVED_COLLECTION_SOURCES;
69261
69136
  exports.RESTORED_CAP_NAMES = RESTORED_CAP_NAMES;
69262
69137
  exports.ROOM_EXAMPLE_MOTION_SOURCES = ROOM_EXAMPLE_MOTION_SOURCES;
@@ -69644,7 +69519,7 @@ exports.audioLabelChoices = audioLabelChoices;
69644
69519
  exports.audioMetricsCapability = audioMetricsCapability;
69645
69520
  exports.audioModeOf = audioModeOf;
69646
69521
  exports.audioOrDefaults = audioOrDefaults;
69647
- exports.audioPlanFromEncodeProfile = require_canonical_hash.audioPlanFromEncodeProfile;
69522
+ exports.audioPlanFromEncodeProfile = require_composition.audioPlanFromEncodeProfile;
69648
69523
  exports.audioScaleEraOf = audioScaleEraOf;
69649
69524
  exports.authProviderCapability = authProviderCapability;
69650
69525
  exports.autoAssignProfiles = autoAssignProfiles;
@@ -69658,17 +69533,17 @@ exports.bindAddonActions = bindAddonActions;
69658
69533
  exports.brightnessCapability = brightnessCapability;
69659
69534
  exports.brokerCapability = brokerCapability;
69660
69535
  exports.buildAddonRouteProvider = buildAddonRouteProvider;
69661
- exports.buildAudioArgs = require_canonical_hash.buildAudioArgs;
69536
+ exports.buildAudioArgs = require_composition.buildAudioArgs;
69662
69537
  exports.buildCapabilityLookup = buildCapabilityLookup;
69663
69538
  exports.buildDeviceAdminUrl = buildDeviceAdminUrl;
69664
69539
  exports.buildEventKindDescriptor = buildEventKindDescriptor;
69665
- exports.buildFfmpegArgs = require_canonical_hash.buildFfmpegArgs;
69666
- exports.buildInputArgs = require_canonical_hash.buildInputArgs;
69540
+ exports.buildFfmpegArgs = require_composition.buildFfmpegArgs;
69541
+ exports.buildInputArgs = require_composition.buildInputArgs;
69667
69542
  exports.buildModelVariantGroups = buildModelVariantGroups;
69668
69543
  exports.buildNcTaxonomy = buildNcTaxonomy;
69669
69544
  exports.buildRoleScopes = buildRoleScopes;
69670
69545
  exports.buildStreamParamsConfigSchema = buildStreamParamsConfigSchema;
69671
- exports.buildVideoArgs = require_canonical_hash.buildVideoArgs;
69546
+ exports.buildVideoArgs = require_composition.buildVideoArgs;
69672
69547
  exports.buttonCapability = buttonCapability;
69673
69548
  exports.cameraCredentialsCapability = cameraCredentialsCapability;
69674
69549
  exports.cameraGridLayoutCapability = cameraGridLayoutCapability;
@@ -69696,16 +69571,18 @@ exports.colorCapability = colorCapability;
69696
69571
  exports.colorForKind = colorForKind;
69697
69572
  exports.commitWatchdogRestart = commitWatchdogRestart;
69698
69573
  exports.compareToTarget = compareToTarget;
69699
- exports.compileExpression = compileExpression;
69700
- exports.compileExpressionSafe = compileExpressionSafe;
69574
+ exports.compileExpression = require_composition.compileExpression;
69575
+ exports.compileExpressionSafe = require_composition.compileExpressionSafe;
69701
69576
  exports.composeSwitchedOff = composeSwitchedOff;
69702
- exports.composedDeviceRef = composedDeviceRef;
69703
- exports.composedDeviceStableId = composedDeviceStableId;
69577
+ exports.composedDeviceRef = require_composition.composedDeviceRef;
69578
+ exports.composedDeviceStableId = require_composition.composedDeviceStableId;
69704
69579
  exports.composerCapability = composerCapability;
69705
69580
  exports.compositionEdges = compositionEdges;
69706
- exports.compositionReadKey = compositionReadKey;
69707
- exports.compositionSliceKey = compositionSliceKey;
69708
- exports.compositionSourceKey = compositionSourceKey;
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;
69709
69586
  exports.compositionSourceRefs = compositionSourceRefs;
69710
69587
  exports.conditionDepth = conditionDepth;
69711
69588
  exports.conditionExclusionReason = conditionExclusionReason;
@@ -69730,7 +69607,7 @@ exports.createDeviceProxy = require_sleep.createDeviceProxy;
69730
69607
  exports.createDurableState = require_sleep.createDurableState;
69731
69608
  exports.createEvent = require_sleep.createEvent;
69732
69609
  exports.createEventBusSliceSource = require_sleep.createEventBusSliceSource;
69733
- exports.createExpressionScope = createExpressionScope;
69610
+ exports.createExpressionScope = require_composition.createExpressionScope;
69734
69611
  exports.createHwAccelCache = createHwAccelCache;
69735
69612
  exports.createLazyTrpcSource = require_sleep.createLazyTrpcSource;
69736
69613
  exports.createLogChannelsProvider = createLogChannelsProvider;
@@ -69801,9 +69678,9 @@ exports.enumerateInferenceDevices = enumerateInferenceDevices;
69801
69678
  exports.enumerateItemArrayFields = enumerateItemArrayFields;
69802
69679
  exports.enumerateSchemaFields = enumerateSchemaFields;
69803
69680
  exports.errMsg = require_err_msg.errMsg;
69804
- exports.evaluateAst = evaluateAst;
69681
+ exports.evaluateAst = require_composition.evaluateAst;
69805
69682
  exports.evaluateCompositionField = evaluateCompositionField;
69806
- exports.evaluateExpressionSource = evaluateExpressionSource;
69683
+ exports.evaluateExpressionSource = require_composition.evaluateExpressionSource;
69807
69684
  exports.evaluatePoolMemory = evaluatePoolMemory;
69808
69685
  exports.evaluateSensorEdge = evaluateSensorEdge;
69809
69686
  exports.evaluateZoneRules = evaluateZoneRules;
@@ -69822,9 +69699,14 @@ exports.faceGalleryCapability = faceGalleryCapability;
69822
69699
  exports.failureContributionCapability = failureContributionCapability;
69823
69700
  exports.failureRate = failureRate;
69824
69701
  exports.fanControlCapability = fanControlCapability;
69702
+ exports.featureFieldEntries = require_composition.featureFieldEntries;
69825
69703
  exports.featureProbeCapability = featureProbeCapability;
69826
- exports.fieldReads = fieldReads;
69704
+ exports.featureSources = require_composition.featureSources;
69705
+ exports.feederCustomizationExample = feederCustomizationExample;
69706
+ exports.feederExampleSources = feederExampleSources;
69707
+ exports.fieldReads = require_composition.fieldReads;
69827
69708
  exports.filesystemBrowseCapability = filesystemBrowseCapability;
69709
+ exports.findCandidateCycles = findCandidateCycles;
69828
69710
  exports.findCompositionCycles = findCompositionCycles;
69829
69711
  exports.findTimezone = findTimezone;
69830
69712
  exports.floodCapability = floodCapability;
@@ -69856,7 +69738,7 @@ exports.integrationsCapability = integrationsCapability;
69856
69738
  exports.intercomCapability = intercomCapability;
69857
69739
  exports.interpretStoredClusterModel = interpretStoredClusterModel;
69858
69740
  exports.invalidClusterStepModelEntries = invalidClusterStepModelEntries;
69859
- exports.invocationFromEncodeProfile = require_canonical_hash.invocationFromEncodeProfile;
69741
+ exports.invocationFromEncodeProfile = require_composition.invocationFromEncodeProfile;
69860
69742
  exports.isAgentOnlyPlacement = isAgentOnlyPlacement;
69861
69743
  exports.isArrayOutputSchema = isArrayOutputSchema;
69862
69744
  exports.isAudioChunkFormat = require_sleep.isAudioChunkFormat;
@@ -69883,11 +69765,12 @@ exports.isMediaOwnerType = isMediaOwnerType;
69883
69765
  exports.isNode = isNode;
69884
69766
  exports.isObjectInput = isObjectInput;
69885
69767
  exports.isOccupancyRule = isOccupancyRule;
69768
+ exports.isOwnerOnlyEventCategory = require_event_category.isOwnerOnlyEventCategory;
69886
69769
  exports.isRestoredCap = isRestoredCap;
69887
69770
  exports.isSameAddonId = isSameAddonId;
69888
69771
  exports.isScheduleActive = isScheduleActive;
69889
69772
  exports.isSecretConfigField = isSecretConfigField;
69890
- exports.isSoftwareDecode = require_canonical_hash.isSoftwareDecode;
69773
+ exports.isSoftwareDecode = require_composition.isSoftwareDecode;
69891
69774
  exports.isSourceCap = isSourceCap;
69892
69775
  exports.isStorageLocationMode = isStorageLocationMode;
69893
69776
  exports.isSystemDelivery = isSystemDelivery;
@@ -69916,7 +69799,7 @@ exports.localNetworkCapability = localNetworkCapability;
69916
69799
  exports.locationIconKey = locationIconKey;
69917
69800
  exports.locationSimilarity = locationSimilarity;
69918
69801
  exports.lockControlCapability = lockControlCapability;
69919
- exports.logBannerArgs = require_canonical_hash.logBannerArgs;
69802
+ exports.logBannerArgs = require_composition.logBannerArgs;
69920
69803
  exports.logChannelsCapability = logChannelsCapability;
69921
69804
  exports.logDestinationCapability = logDestinationCapability;
69922
69805
  exports.logLevelAtMost = logLevelAtMost;
@@ -69969,6 +69852,7 @@ exports.oauthIntegrationCapability = oauthIntegrationCapability;
69969
69852
  exports.objectInputDeclaresAddonId = objectInputDeclaresAddonId;
69970
69853
  exports.objectInputDeclaresRequiredDeviceId = objectInputDeclaresRequiredDeviceId;
69971
69854
  exports.occupancyScope = occupancyScope;
69855
+ exports.optionalAncestors = optionalAncestors;
69972
69856
  exports.optionalMethod = require_sleep.optionalMethod;
69973
69857
  exports.osdCapability = osdCapability;
69974
69858
  exports.osdManagerCapability = osdManagerCapability;
@@ -69976,7 +69860,7 @@ exports.overlayClusterStepSettings = overlayClusterStepSettings;
69976
69860
  exports.packagePickerItems = packagePickerItems;
69977
69861
  exports.parseAdminBase = parseAdminBase;
69978
69862
  exports.parseCameraStreamConfig = parseCameraStreamConfig;
69979
- exports.parseExpression = parseExpression;
69863
+ exports.parseExpression = require_composition.parseExpression;
69980
69864
  exports.parseJsonArray = require_sleep.parseJsonArray;
69981
69865
  exports.parseJsonObject = require_sleep.parseJsonObject;
69982
69866
  exports.parseJsonUnknown = require_sleep.parseJsonUnknown;
@@ -69996,13 +69880,14 @@ exports.pickInvalidClusterStepModels = pickInvalidClusterStepModels;
69996
69880
  exports.pickNativeLeaseOverride = pickNativeLeaseOverride;
69997
69881
  exports.pickPreferredRtspEntry = pickPreferredRtspEntry;
69998
69882
  exports.pickRestartCandidate = pickRestartCandidate;
69999
- exports.pickVideoEncoder = require_canonical_hash.pickVideoEncoder;
69883
+ exports.pickVideoEncoder = require_composition.pickVideoEncoder;
70000
69884
  exports.pickerForCondition = pickerForCondition;
70001
69885
  exports.pipelineAnalyticsCapability = pipelineAnalyticsCapability;
70002
69886
  exports.pipelineExecutorCapability = pipelineExecutorCapability;
70003
69887
  exports.pipelineOrchestratorCapability = pipelineOrchestratorCapability;
70004
69888
  exports.pipelineRunnerCapability = pipelineRunnerCapability;
70005
69889
  exports.planComposition = planComposition;
69890
+ exports.planItems = planItems;
70006
69891
  exports.plateGalleryCapability = plateGalleryCapability;
70007
69892
  exports.platformProbeCapability = platformProbeCapability;
70008
69893
  exports.poolMemoryThreshold = poolMemoryThreshold;
@@ -70015,6 +69900,7 @@ exports.privacyMaskCapability = privacyMaskCapability;
70015
69900
  exports.procedureAuthKey = procedureAuthKey;
70016
69901
  exports.ptzAutotrackCapability = ptzAutotrackCapability;
70017
69902
  exports.ptzCapability = ptzCapability;
69903
+ exports.publicEventCategories = require_event_category.publicEventCategories;
70018
69904
  exports.pythonScriptForBackend = pythonScriptForBackend;
70019
69905
  exports.rankStreams = rankStreams;
70020
69906
  exports.readClusterStepModels = readClusterStepModels;
@@ -70033,6 +69919,7 @@ exports.recordingOnboardCapability = recordingOnboardCapability;
70033
69919
  exports.recordingSignalCapability = recordingSignalCapability;
70034
69920
  exports.rectsToCells = rectsToCells;
70035
69921
  exports.reducePoints = reducePoints;
69922
+ exports.replacedFieldKeys = replacedFieldKeys;
70036
69923
  exports.requiredTopLevelKeys = requiredTopLevelKeys;
70037
69924
  exports.requiresPython = requiresPython;
70038
69925
  exports.reservedSourceOwner = reservedSourceOwner;
@@ -70059,7 +69946,7 @@ exports.resolveMutate = resolveMutate;
70059
69946
  exports.resolvePoolMemoryPolicy = resolvePoolMemoryPolicy;
70060
69947
  exports.resolveRecordingProfiles = resolveRecordingProfiles;
70061
69948
  exports.resolveRunnerId = resolveRunnerId;
70062
- exports.resolveStreamMaps = require_canonical_hash.resolveStreamMaps;
69949
+ exports.resolveStreamMaps = require_composition.resolveStreamMaps;
70063
69950
  exports.resolveVariantModelId = resolveVariantModelId;
70064
69951
  exports.resolveViewableDeviceIds = resolveViewableDeviceIds;
70065
69952
  exports.roleSpec = roleSpec;
@@ -70153,24 +70040,25 @@ exports.terminalSessionCapability = terminalSessionCapability;
70153
70040
  exports.textToHtml = textToHtml;
70154
70041
  exports.timeOfDayToMinutes = timeOfDayToMinutes;
70155
70042
  exports.toDeviceSummary = toDeviceSummary;
70156
- exports.toExpressionValue = toExpressionValue;
70043
+ exports.toExpressionValue = require_composition.toExpressionValue;
70157
70044
  exports.toNodeId = require_sleep.toNodeId;
70158
70045
  exports.toStreamSourceEntry = toStreamSourceEntry;
70159
70046
  exports.toastCapability = toastCapability;
70160
70047
  exports.toggleAudioLabel = toggleAudioLabel;
70161
- exports.tokenize = tokenize;
70048
+ exports.tokenize = require_composition.tokenize;
70162
70049
  exports.transcodeBody = transcodeBody;
70163
70050
  exports.tryConvertUnit = tryConvertUnit;
70164
70051
  exports.turnProviderCapability = turnProviderCapability;
70165
70052
  exports.unitDimension = unitDimension;
70166
70053
  exports.unitsForDimension = unitsForDimension;
70167
70054
  exports.unreachableExtensionSlotRow = unreachableExtensionSlotRow;
70055
+ exports.unwrapSchema = unwrapSchema;
70168
70056
  exports.updateCapability = updateCapability;
70169
70057
  exports.userManagementCapability = userManagementCapability;
70170
70058
  exports.userPasskeysCapability = userPasskeysCapability;
70171
70059
  exports.vacuumControlCapability = vacuumControlCapability;
70172
70060
  exports.validateComposition = validateComposition;
70173
- exports.validateExpressionSource = validateExpressionSource;
70061
+ exports.validateExpressionSource = require_composition.validateExpressionSource;
70174
70062
  exports.validateRecipeBounds = validateRecipeBounds;
70175
70063
  exports.valveCapability = valveCapability;
70176
70064
  exports.vectorDimFromBase64 = vectorDimFromBase64;