@camstack/types 1.2.406 → 1.2.407

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 (44) hide show
  1. package/dist/capabilities/battery.cap.d.ts +46 -0
  2. package/dist/capabilities/capability-definition.d.ts +8 -0
  3. package/dist/capabilities/consumables.cap.d.ts +110 -0
  4. package/dist/capabilities/customize-catalog.d.ts +72 -0
  5. package/dist/capabilities/customize-spec.d.ts +202 -0
  6. package/dist/capabilities/doorbell.cap.d.ts +39 -0
  7. package/dist/capabilities/index.d.ts +2 -2
  8. package/dist/capabilities/motion.cap.d.ts +39 -0
  9. package/dist/capabilities/running-state.cap.d.ts +68 -4
  10. package/dist/catalogs/sensor-active-state.d.ts +1 -1
  11. package/dist/client.js +1 -1
  12. package/dist/client.mjs +1 -1
  13. package/dist/composition/customize/__fixtures__/refs.d.ts +27 -0
  14. package/dist/composition/customize/__fixtures__/stored-cases.d.ts +13 -0
  15. package/dist/composition/customize/event-compile.d.ts +20 -0
  16. package/dist/composition/customize/event-decode.d.ts +19 -0
  17. package/dist/composition/customize/event-eval.d.ts +35 -0
  18. package/dist/composition/customize/event-status.d.ts +49 -0
  19. package/dist/composition/customize/field-source.d.ts +5 -0
  20. package/dist/composition/customize/keyed-array-compile.d.ts +26 -0
  21. package/dist/composition/customize/keyed-array-decode.d.ts +20 -0
  22. package/dist/composition/customize/keyed-array-expr.d.ts +10 -0
  23. package/dist/composition/customize/keyed-array-status.d.ts +22 -0
  24. package/dist/composition/customize/normalize.d.ts +4 -0
  25. package/dist/composition/customize/override-draft.d.ts +311 -0
  26. package/dist/composition/customize/scalar-compile.d.ts +13 -0
  27. package/dist/composition/customize/scalar-decode.d.ts +17 -0
  28. package/dist/composition/customize/scalar-expr.d.ts +33 -0
  29. package/dist/composition/customize/scalar-status.d.ts +31 -0
  30. package/dist/composition/customize/stored.d.ts +15 -0
  31. package/dist/composition/index.d.ts +0 -1
  32. package/dist/{composition-xz-ud5EQ.mjs → composition-BJ8iAZb3.mjs} +1 -1
  33. package/dist/{composition-RmnnN39T.js → composition-CSLSFGTO.js} +1 -1
  34. package/dist/generated/customize-catalog.d.ts +229 -0
  35. package/dist/index.d.ts +14 -1
  36. package/dist/index.js +2245 -290
  37. package/dist/index.mjs +2223 -286
  38. package/dist/node.js +1 -1
  39. package/dist/node.mjs +1 -1
  40. package/dist/running-state/defaults.d.ts +3 -0
  41. package/dist/running-state/from-operation.d.ts +4 -4
  42. package/package.json +1 -1
  43. package/dist/composition/presets/doorbell-feature.d.ts +0 -20
  44. package/dist/running-state/assignment.d.ts +0 -34
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_sleep = require("./sleep-UP964hNP.js");
3
3
  const require_event_category = require("./event-category-DG6Vir8Z.js");
4
- const require_composition = require("./composition-RmnnN39T.js");
4
+ const require_composition = require("./composition-CSLSFGTO.js");
5
5
  const require_node_color = require("./node-color.js");
6
6
  const require_err_msg = require("./err-msg-BhVbr0NX.js");
7
7
  const require_enums = require("./enums.js");
@@ -60326,20 +60326,68 @@ var BatteryStatusSchema = zod.z.object({
60326
60326
  */
60327
60327
  binary: zod.z.boolean().optional()
60328
60328
  });
60329
+ var BATTERY_DEVICE_TYPES = [
60330
+ require_err_msg.DeviceType.Camera,
60331
+ require_err_msg.DeviceType.Sensor,
60332
+ require_err_msg.DeviceType.Button,
60333
+ require_err_msg.DeviceType.Switch,
60334
+ require_err_msg.DeviceType.PetFeeder,
60335
+ require_err_msg.DeviceType.Container,
60336
+ require_err_msg.DeviceType.Thermostat
60337
+ ];
60338
+ /** Generic Customize target for the percentage reading (D936). */
60339
+ var batteryCustomize = {
60340
+ version: 1,
60341
+ targets: [{
60342
+ id: "percentage",
60343
+ cardinality: "scalar",
60344
+ vocabulary: {
60345
+ cap: "battery",
60346
+ stateField: "percentage"
60347
+ },
60348
+ deviceTypes: BATTERY_DEVICE_TYPES,
60349
+ apply: "add-or-replace",
60350
+ path: "percentage",
60351
+ sources: [{
60352
+ kind: "field",
60353
+ valueKinds: ["number", "string"],
60354
+ transforms: [{ kind: "identity" }, {
60355
+ kind: "linear",
60356
+ defaultScale: 1,
60357
+ defaultOffset: 0,
60358
+ clampMin: 0,
60359
+ clampMax: 100
60360
+ }]
60361
+ }],
60362
+ companions: [
60363
+ {
60364
+ kind: "constant",
60365
+ path: "charging",
60366
+ value: "unknown",
60367
+ when: "add"
60368
+ },
60369
+ {
60370
+ kind: "constant",
60371
+ path: "sleeping",
60372
+ value: false,
60373
+ when: "add"
60374
+ },
60375
+ {
60376
+ kind: "clock",
60377
+ path: "lastUpdated",
60378
+ when: "add"
60379
+ }
60380
+ ],
60381
+ sourceReport: { status: "assignment-status" },
60382
+ advancedExpression: true
60383
+ }]
60384
+ };
60329
60385
  var batteryCapability = {
60330
60386
  name: "battery",
60331
60387
  scope: "device",
60332
60388
  deviceNative: true,
60333
60389
  mode: "singleton",
60334
- deviceTypes: [
60335
- require_err_msg.DeviceType.Camera,
60336
- require_err_msg.DeviceType.Sensor,
60337
- require_err_msg.DeviceType.Button,
60338
- require_err_msg.DeviceType.Switch,
60339
- require_err_msg.DeviceType.PetFeeder,
60340
- require_err_msg.DeviceType.Container,
60341
- require_err_msg.DeviceType.Thermostat
60342
- ],
60390
+ deviceTypes: BATTERY_DEVICE_TYPES,
60343
60391
  methods: {
60344
60392
  /**
60345
60393
  * Explicitly wake the camera from low-power sleep ahead of a
@@ -60423,6 +60471,7 @@ onStatusChanged: { data: zod.z.object({
60423
60471
  kind: "gauge",
60424
60472
  unit: "%"
60425
60473
  } } },
60474
+ customize: batteryCustomize,
60426
60475
  /** Clock fields: written, but excluded from the compare that decides
60427
60476
  * whether persisting is worth a SQLite commit. */
60428
60477
  volatileStateFields: ["lastUpdated"]
@@ -61621,6 +61670,106 @@ var ConsumablesStatusSchema = zod.z.object({
61621
61670
  items: zod.z.array(ConsumableItemSchema),
61622
61671
  lastChangedAt: zod.z.number()
61623
61672
  });
61673
+ /** Synthesize seed for a link-built status (no native provider).
61674
+ *
61675
+ * ⚠ NO LIVE CALLER since 2026-08-08 (device-links deleted; see
61676
+ * `CapabilityStatusSchema.empty`). The wiring guidance below was written for
61677
+ * exactly the case the operator is asking about again — a Dreame's HA
61678
+ * consumable-life sensors — so it is kept for that conversation, not because
61679
+ * anything reads it today. */
61680
+ var EMPTY_STATUS = {
61681
+ items: [],
61682
+ lastChangedAt: 0
61683
+ };
61684
+ /** Seed for a NEW link-built consumable item. `key`/`label` are overwritten
61685
+ * with the link's `itemKey` (see `status.itemArray`), so the placeholder
61686
+ * empty strings never survive into a validated item. Link-built items are
61687
+ * not resettable — there is no native provider to receive `reset()`.
61688
+ *
61689
+ * Wiring guidance (HA vacuum consumable-life sensors):
61690
+ * - a percent-remaining source (`numeric-sensor`, unit '%') → `level` via
61691
+ * `identity` (or `linear` scale 1 offset 0 clamp [0,100] to be safe);
61692
+ * - an hours-remaining source (e.g. Dreame 'Main Brush Time Left', unit 'h')
61693
+ * → `level` via `linear` with scale = 100 / max-hours for that item
61694
+ * (e.g. main brush 300 h → scale 0.3333) + clamp [0,100];
61695
+ * - a discrete source (`enum-sensor`, e.g. mop pad 'installed') → `status`
61696
+ * via `enum-map` (e.g. { installed: 'ok', missing: 'replace' }).
61697
+ * A numeric→'replace' THRESHOLD (level < 10 → 'replace') is NOT expressible
61698
+ * with the current transform kinds — leave `status` null (valid) until the
61699
+ * expression transform lands. */
61700
+ /** Generic Customize target for one consumable item (D936). Native rows stay read-only. */
61701
+ var consumablesCustomize = {
61702
+ version: 1,
61703
+ targets: [{
61704
+ id: "items",
61705
+ cardinality: "keyedArray",
61706
+ vocabulary: { cap: "consumables" },
61707
+ deviceTypes: Object.values(require_err_msg.DeviceType),
61708
+ apply: "add",
61709
+ path: "items",
61710
+ keyField: "key",
61711
+ labelField: "label",
61712
+ itemSchema: ConsumableItemSchema,
61713
+ nativeItems: "read-only",
61714
+ sourceReport: { status: "assignment-status" },
61715
+ fields: [
61716
+ {
61717
+ path: "level",
61718
+ sources: [{
61719
+ kind: "numeric",
61720
+ transforms: [{ kind: "identity" }, {
61721
+ kind: "linear",
61722
+ defaultScale: 1,
61723
+ defaultOffset: 0,
61724
+ clampMin: 0,
61725
+ clampMax: 100
61726
+ }]
61727
+ }]
61728
+ },
61729
+ {
61730
+ path: "remaining.value",
61731
+ sources: [{
61732
+ kind: "numeric",
61733
+ transforms: [{ kind: "identity" }]
61734
+ }],
61735
+ literals: [{
61736
+ path: "remaining.unit",
61737
+ values: [...ConsumableRemainingUnitSchema.options]
61738
+ }]
61739
+ },
61740
+ {
61741
+ path: "status",
61742
+ sources: [{
61743
+ kind: "enum",
61744
+ transforms: [{
61745
+ kind: "enum-map",
61746
+ outputValues: [...ConsumableStatusSchema.options],
61747
+ unmapped: null
61748
+ }]
61749
+ }]
61750
+ }
61751
+ ],
61752
+ traits: [{
61753
+ path: "kind",
61754
+ values: [...ConsumableKindSchema.options],
61755
+ defaultValue: "wear"
61756
+ }, {
61757
+ path: "form",
61758
+ values: [...ConsumableFormSchema.options],
61759
+ defaultValue: "part"
61760
+ }],
61761
+ companions: [{
61762
+ kind: "clock",
61763
+ path: "lastChangedAt",
61764
+ when: "add"
61765
+ }, {
61766
+ kind: "clock",
61767
+ path: "lastFetchedAt",
61768
+ when: "add"
61769
+ }],
61770
+ advancedExpression: true
61771
+ }]
61772
+ };
61624
61773
  var consumablesCapability = {
61625
61774
  name: "consumables",
61626
61775
  scope: "device",
@@ -61658,10 +61807,7 @@ reset: require_sleep.method(zod.z.object({
61658
61807
  status: {
61659
61808
  schema: ConsumablesStatusSchema,
61660
61809
  kind: "push",
61661
- empty: {
61662
- items: [],
61663
- lastChangedAt: 0
61664
- },
61810
+ empty: EMPTY_STATUS,
61665
61811
  itemArray: {
61666
61812
  path: "items",
61667
61813
  keyField: "key",
@@ -61697,6 +61843,7 @@ reset: require_sleep.method(zod.z.object({
61697
61843
  * brush's remaining life and a dishwasher's salt are each recorded the moment
61698
61844
  * their provider reports them; an item that appears later gets its series.
61699
61845
  */
61846
+ customize: consumablesCustomize,
61700
61847
  history: {
61701
61848
  fields: {},
61702
61849
  items: {
@@ -62499,6 +62646,38 @@ var dishwasherCapability = {
62499
62646
  };
62500
62647
  //#endregion
62501
62648
  //#region src/capabilities/doorbell.cap.ts
62649
+ /** A rise closer than this to the previous one is the same press (D671). */
62650
+ var DOORBELL_RISE_DEBOUNCE_MS = 2e3;
62651
+ /** Pulse override: a rising edge advances `lastPressedAt` and the press count. */
62652
+ var doorbellCustomize = {
62653
+ version: 1,
62654
+ targets: [{
62655
+ id: "pressed",
62656
+ cardinality: "event",
62657
+ shape: "pulse",
62658
+ vocabulary: {
62659
+ cap: "doorbell",
62660
+ eventName: "onPressed"
62661
+ },
62662
+ deviceTypes: [require_err_msg.DeviceType.Camera],
62663
+ apply: "add",
62664
+ applyAuthority: { kind: "composition-claim" },
62665
+ eventName: "onPressed",
62666
+ edges: ["rising"],
62667
+ timestampPath: "lastPressedAt",
62668
+ counterPath: "pressCountSinceStart",
62669
+ highWater: "composer-advance",
62670
+ combine: "any",
62671
+ debounce: {
62672
+ kind: "rising-edge",
62673
+ defaultDebounceMs: DOORBELL_RISE_DEBOUNCE_MS,
62674
+ changedAtRequired: true
62675
+ },
62676
+ sources: [{ kind: "binary" }, { kind: "switch" }],
62677
+ sourceReport: { status: "assignment-status" },
62678
+ advancedExpression: true
62679
+ }]
62680
+ };
62502
62681
  var DoorbellStatusSchema = zod.z.object({
62503
62682
  /** Ms epoch of the last press. null = never observed since this provider started. */
62504
62683
  lastPressedAt: zod.z.number().nullable(),
@@ -62565,7 +62744,8 @@ onPressed: {
62565
62744
  /**
62566
62745
  * History (D850): none — `pressCountSinceStart` resets with the addon, so it is not a monotonic meter; presses live in the events ledger.
62567
62746
  */
62568
- history: "none"
62747
+ history: "none",
62748
+ customize: doorbellCustomize
62569
62749
  };
62570
62750
  //#endregion
62571
62751
  //#region src/capabilities/enum-sensor.cap.ts
@@ -65312,6 +65492,44 @@ authKey: zod.z.string().optional() }), zod.z.object({
65312
65492
  //#endregion
65313
65493
  //#region src/capabilities/motion.cap.ts
65314
65494
  /**
65495
+ * Span timeout for a camera's attached sensors. This is the pipeline-runner
65496
+ * `motionCooldownMs` default: the window after which a heartbeat-only edge
65497
+ * no longer counts. The span does not declare a separate cooldown.
65498
+ */
65499
+ var MOTION_SPAN_TIMEOUT_MS = 3e4;
65500
+ /** Span override: start on a rising edge, end on a fall or this timeout. */
65501
+ var motionCustomize = {
65502
+ version: 1,
65503
+ targets: [{
65504
+ id: "detected",
65505
+ cardinality: "event",
65506
+ shape: "span",
65507
+ vocabulary: {
65508
+ cap: "motion",
65509
+ eventName: "onMotionChanged"
65510
+ },
65511
+ deviceTypes: [require_err_msg.DeviceType.Camera],
65512
+ apply: "replace",
65513
+ applyAuthority: {
65514
+ kind: "camera-sensors",
65515
+ timeoutField: "motionCooldownMs"
65516
+ },
65517
+ eventName: "onMotionChanged",
65518
+ start: "rising-edge",
65519
+ end: "falling-edge-or-timeout",
65520
+ timeoutMs: MOTION_SPAN_TIMEOUT_MS,
65521
+ combine: "any",
65522
+ sourceModes: [
65523
+ "in-addition",
65524
+ "confirm",
65525
+ "replace"
65526
+ ],
65527
+ sources: [{ kind: "binary" }, { kind: "switch" }],
65528
+ sourceReport: { status: "assignment-status" },
65529
+ advancedExpression: false
65530
+ }]
65531
+ };
65532
+ /**
65315
65533
  * Hardware / firmware motion sensor cap — binary detected state plus
65316
65534
  * a timestamp of the last observation. Distinct from
65317
65535
  * `motion-detection.cap.ts` which owns the LOCAL ML motion pipeline;
@@ -65475,7 +65693,8 @@ onMotionChanged: { data: MotionOnMotionChangedDataSchema } },
65475
65693
  history: { fields: { detected: {
65476
65694
  kind: "boolean",
65477
65695
  excludeDeviceTypes: [require_err_msg.DeviceType.Camera]
65478
- } } }
65696
+ } } },
65697
+ customize: motionCustomize
65479
65698
  };
65480
65699
  //#endregion
65481
65700
  //#region src/capabilities/motion-trigger.cap.ts
@@ -69589,6 +69808,19 @@ var ptzAutotrackCapability = {
69589
69808
  history: "none"
69590
69809
  };
69591
69810
  //#endregion
69811
+ //#region src/running-state/defaults.ts
69812
+ var RUNNING_POWER_THRESHOLD_WATTS = 5;
69813
+ var RUNNING_POWER_START_DELAY_MS = 3e4;
69814
+ var RUNNING_POWER_STOP_DELAY_MS = 18e4;
69815
+ /** Before any provider reading — all fields unknown (D933). */
69816
+ var RUNNING_STATE_COLD = {
69817
+ running: null,
69818
+ since: null,
69819
+ source: "native",
69820
+ phase: null,
69821
+ lastFetchedAt: null
69822
+ };
69823
+ //#endregion
69592
69824
  //#region src/capabilities/running-state.cap.ts
69593
69825
  var RunningStateSourceSchema = zod.z.enum(["native", "assigned"]);
69594
69826
  var RunningStateStatusSchema = zod.z.object({
@@ -69598,19 +69830,100 @@ var RunningStateStatusSchema = zod.z.object({
69598
69830
  since: zod.z.number().nullable(),
69599
69831
  source: RunningStateSourceSchema,
69600
69832
  phase: zod.z.string().nullable(),
69601
- lastFetchedAt: zod.z.number()
69833
+ /** null until a reading has been published (D393). */
69834
+ lastFetchedAt: zod.z.number().nullable()
69602
69835
  });
69603
69836
  var RunningFlipEventDataSchema = zod.z.object({
69604
69837
  deviceId: zod.z.number(),
69605
69838
  since: zod.z.number().nullable(),
69606
69839
  phase: zod.z.string().nullable()
69607
69840
  });
69841
+ var RUNNING_DEVICE_TYPES = Object.values(require_err_msg.DeviceType);
69842
+ /** Generic Customize target for the running boolean (D936). */
69843
+ var runningCustomize = {
69844
+ version: 1,
69845
+ targets: [{
69846
+ id: "running",
69847
+ cardinality: "scalar",
69848
+ vocabulary: {
69849
+ cap: "running-state",
69850
+ stateField: "running"
69851
+ },
69852
+ deviceTypes: [
69853
+ require_err_msg.DeviceType.Dishwasher,
69854
+ require_err_msg.DeviceType.Vacuum,
69855
+ require_err_msg.DeviceType.LawnMower,
69856
+ require_err_msg.DeviceType.MediaPlayer,
69857
+ require_err_msg.DeviceType.WaterHeater,
69858
+ require_err_msg.DeviceType.Humidifier,
69859
+ require_err_msg.DeviceType.Fan,
69860
+ require_err_msg.DeviceType.Climate,
69861
+ require_err_msg.DeviceType.Thermostat,
69862
+ require_err_msg.DeviceType.PetFeeder
69863
+ ],
69864
+ apply: "add-or-replace",
69865
+ path: "running",
69866
+ sources: [
69867
+ {
69868
+ kind: "switch",
69869
+ transforms: [{ kind: "identity" }]
69870
+ },
69871
+ {
69872
+ kind: "binary",
69873
+ transforms: [{ kind: "identity" }, { kind: "invert" }]
69874
+ },
69875
+ {
69876
+ kind: "numeric",
69877
+ requiredUnit: "W",
69878
+ transforms: [{
69879
+ kind: "threshold",
69880
+ comparison: "gte",
69881
+ defaultThreshold: 5,
69882
+ defaultStartDwellMs: RUNNING_POWER_START_DELAY_MS,
69883
+ defaultStopDwellMs: RUNNING_POWER_STOP_DELAY_MS,
69884
+ thresholdUnit: "W"
69885
+ }]
69886
+ }
69887
+ ],
69888
+ companions: [
69889
+ {
69890
+ kind: "constant",
69891
+ path: "source",
69892
+ value: "assigned",
69893
+ when: "always"
69894
+ },
69895
+ {
69896
+ kind: "constant",
69897
+ path: "phase",
69898
+ value: null,
69899
+ when: "always"
69900
+ },
69901
+ {
69902
+ kind: "clock",
69903
+ path: "lastFetchedAt",
69904
+ when: "always"
69905
+ },
69906
+ {
69907
+ kind: "transition-time",
69908
+ path: "since",
69909
+ sourceChangedAt: "prefer"
69910
+ }
69911
+ ],
69912
+ sourceReport: {
69913
+ status: "assignment-status",
69914
+ runtimeField: "source",
69915
+ nativeValue: "native",
69916
+ assignedValue: "assigned"
69917
+ },
69918
+ advancedExpression: true
69919
+ }]
69920
+ };
69608
69921
  var runningStateCapability = {
69609
69922
  name: "running-state",
69610
69923
  scope: "device",
69611
69924
  deviceNative: true,
69612
69925
  mode: "singleton",
69613
- deviceTypes: Object.values(require_err_msg.DeviceType),
69926
+ deviceTypes: RUNNING_DEVICE_TYPES,
69614
69927
  methods: {},
69615
69928
  events: {
69616
69929
  onStarted: { data: RunningFlipEventDataSchema },
@@ -69623,7 +69936,8 @@ var runningStateCapability = {
69623
69936
  runtimeState: RunningStateStatusSchema,
69624
69937
  durability: "restored",
69625
69938
  history: { fields: { running: { kind: "boolean" } } },
69626
- volatileStateFields: ["lastFetchedAt"]
69939
+ volatileStateFields: ["lastFetchedAt"],
69940
+ customize: runningCustomize
69627
69941
  };
69628
69942
  //#endregion
69629
69943
  //#region src/capabilities/reboot.cap.ts
@@ -75852,213 +76166,58 @@ var PIPELINE_OWNER_CAPABILITY_NAMES = [
75852
76166
  "pipeline-runner"
75853
76167
  ];
75854
76168
  //#endregion
75855
- //#region src/running-state/assignment.ts
75856
- function constant(literal) {
75857
- return {
75858
- kind: "expression",
75859
- expr: literal,
75860
- bindings: {}
75861
- };
75862
- }
75863
- function fromRead(source, cap, fieldPath) {
75864
- return {
75865
- kind: "from",
75866
- source,
75867
- cap,
75868
- fieldPath
75869
- };
75870
- }
75871
- function sortKeys(value) {
75872
- if (Array.isArray(value)) return value.map(sortKeys);
75873
- if (typeof value !== "object" || value === null) return value;
75874
- return Object.fromEntries(Object.entries(value).toSorted(([a], [b]) => a.localeCompare(b)).map(([key, inner]) => [key, sortKeys(inner)]));
75875
- }
75876
- function sameFeature(a, b) {
75877
- return JSON.stringify(sortKeys(a)) === JSON.stringify(sortKeys(b));
75878
- }
75879
- function booleanRunningField(input) {
75880
- if (input.source.kind !== "boolean") return null;
75881
- const read = fromRead(input.source.source, input.source.cap, input.source.fieldPath);
75882
- if (!input.invert) return read;
75883
- return {
75884
- kind: "expression",
75885
- expr: "!x",
75886
- bindings: { x: read }
75887
- };
75888
- }
75889
- function sinceField(input) {
75890
- if (input.source.kind === "boolean" && input.changedAtField !== null) return fromRead(input.source.source, input.source.cap, input.changedAtField);
75891
- return {
75892
- kind: "expression",
75893
- expr: "latchedAt(running)",
75894
- bindings: { running: fromRead(input.target, "running-state", "running") }
75895
- };
75896
- }
75897
- function powerRunningField(input) {
75898
- if (input.source.kind !== "number") return null;
75899
- if (!Number.isFinite(input.thresholdWatts) || !Number.isFinite(input.startDelayMs) || !Number.isFinite(input.stopDelayMs) || input.thresholdWatts < 0 || input.startDelayMs < 0 || input.stopDelayMs < 0) return null;
75900
- const read = fromRead(input.source.source, input.source.cap, input.source.fieldPath);
75901
- return {
75902
- kind: "expression",
75903
- expr: `dwell(x >= ${input.thresholdWatts}, ${input.startDelayMs}, ${input.stopDelayMs})`,
75904
- bindings: { x: read }
75905
- };
75906
- }
75907
- function runningAssignmentFeature(input) {
75908
- const running = input.source.kind === "number" ? powerRunningField(input) : booleanRunningField(input);
75909
- if (running === null) return null;
75910
- return {
75911
- kind: "fields",
75912
- cap: "running-state",
75913
- fields: {
75914
- source: constant("\"assigned\""),
75915
- phase: constant("null"),
75916
- lastFetchedAt: constant("now"),
75917
- running,
75918
- since: sinceField(input)
75919
- }
75920
- };
75921
- }
75922
- function booleanSourceFromRunningField(source) {
75923
- if (source === void 0) return null;
75924
- if (source.kind === "from") return {
75925
- source: source.source,
75926
- cap: source.cap,
75927
- fieldPath: source.fieldPath,
75928
- kind: "boolean"
75929
- };
75930
- if (source.kind !== "expression" || source.expr !== "!x") return null;
75931
- const x = source.bindings["x"];
75932
- if (x === void 0 || x.kind !== "from") return null;
75933
- return {
75934
- source: x.source,
75935
- cap: x.cap,
75936
- fieldPath: x.fieldPath,
75937
- kind: "boolean"
75938
- };
75939
- }
75940
- function recoverPowerRunning(running) {
75941
- if (running === void 0 || running.kind !== "expression") return null;
75942
- const match = /^dwell\(x >= ([\d.]+), (\d+), (\d+)\)$/.exec(running.expr);
75943
- if (match === null) return null;
75944
- const x = running.bindings["x"];
75945
- if (x === void 0 || x.kind !== "from") return null;
75946
- const thresholdWatts = Number(match[1]);
75947
- const startDelayMs = Number(match[2]);
75948
- const stopDelayMs = Number(match[3]);
75949
- if (!Number.isFinite(thresholdWatts) || !Number.isFinite(startDelayMs) || !Number.isFinite(stopDelayMs)) return null;
75950
- return {
75951
- source: {
75952
- ...x,
75953
- kind: "number"
75954
- },
75955
- invert: false,
75956
- thresholdWatts,
75957
- startDelayMs,
75958
- stopDelayMs
75959
- };
75960
- }
75961
- /** Recover what {@link runningAssignmentFeature} wrote, else null. */
75962
- function runningAssignmentRecovered(stored, target) {
75963
- if (stored.cap !== "running-state" || stored.kind !== "fields") return null;
75964
- const power = recoverPowerRunning(stored.fields["running"]);
75965
- if (power !== null) return power;
75966
- const booleanSource = booleanSourceFromRunningField(stored.fields["running"]);
75967
- if (booleanSource === null) return null;
75968
- const since = stored.fields["since"];
75969
- let changedAtField = null;
75970
- if (since?.kind === "from" && since.cap === booleanSource.cap && since.source.addonId === booleanSource.source.addonId && since.source.stableId === booleanSource.source.stableId) changedAtField = since.fieldPath;
75971
- const rebuilt = runningAssignmentFeature({
75972
- mode: "replace",
75973
- target,
75974
- source: booleanSource,
75975
- changedAtField,
75976
- invert: stored.fields["running"]?.kind === "expression" && stored.fields["running"].expr === "!x",
75977
- thresholdWatts: 0,
75978
- startDelayMs: 0,
75979
- stopDelayMs: 0
75980
- });
75981
- if (rebuilt === null) return null;
75982
- const { enabled: _e, ...rest } = stored;
75983
- if (!sameFeature(rest, rebuilt)) return null;
75984
- return {
75985
- source: booleanSource,
75986
- invert: stored.fields["running"]?.kind === "expression" && stored.fields["running"].expr === "!x",
75987
- thresholdWatts: 0,
75988
- startDelayMs: 0,
75989
- stopDelayMs: 0
75990
- };
75991
- }
75992
- function isRunningAssignmentFeature(stored, mode, target) {
75993
- const recovered = runningAssignmentRecovered(stored, target);
75994
- if (recovered === null) return false;
75995
- const rebuilt = recovered.source.kind === "number" ? runningAssignmentFeature({
75996
- mode,
75997
- target,
75998
- source: recovered.source,
75999
- changedAtField: null,
76000
- invert: false,
76001
- thresholdWatts: recovered.thresholdWatts,
76002
- startDelayMs: recovered.startDelayMs,
76003
- stopDelayMs: recovered.stopDelayMs
76004
- }) : runningAssignmentFeature({
76005
- mode,
76006
- target,
76007
- source: recovered.source,
76008
- changedAtField: stored.fields["since"]?.kind === "from" ? stored.fields["since"].fieldPath : null,
76009
- invert: recovered.invert,
76010
- thresholdWatts: 0,
76011
- startDelayMs: 0,
76012
- stopDelayMs: 0
76013
- });
76014
- if (rebuilt === null) return false;
76015
- const { enabled: _enabled, ...rest } = stored;
76016
- return sameFeature(rest, rebuilt);
76017
- }
76018
- //#endregion
76019
- //#region src/running-state/defaults.ts
76020
- var RUNNING_POWER_THRESHOLD_WATTS = 5;
76021
- var RUNNING_POWER_START_DELAY_MS = 3e4;
76022
- var RUNNING_POWER_STOP_DELAY_MS = 18e4;
76023
- //#endregion
76024
76169
  //#region src/running-state/from-operation.ts
76025
- function resolveSince(running, previous, sinceMs) {
76170
+ function programStartFromRemaining(running, remainingSeconds, fetchedAt) {
76171
+ if (!running || remainingSeconds === null || remainingSeconds === void 0) return null;
76172
+ if (remainingSeconds <= 0) return null;
76173
+ return fetchedAt - remainingSeconds * 1e3;
76174
+ }
76175
+ function resolveSince(running, previous, transitionAtMs, bootstrapSinceMs) {
76026
76176
  if (running === null) return null;
76027
- if ((previous?.running ?? null) !== running) return sinceMs;
76028
- return previous?.since ?? sinceMs;
76177
+ const prevRunning = previous?.running ?? null;
76178
+ if (prevRunning === null) return bootstrapSinceMs;
76179
+ if (prevRunning !== running) return transitionAtMs;
76180
+ return previous?.since ?? null;
76029
76181
  }
76030
- function nativeSlice(running, phase, previous, sinceMs, now) {
76182
+ function nativeSlice(running, phase, previous, transitionAtMs, fetchedAt, bootstrapSinceMs) {
76031
76183
  return {
76032
76184
  running,
76033
- since: resolveSince(running, previous, sinceMs),
76185
+ since: resolveSince(running, previous, transitionAtMs, bootstrapSinceMs),
76034
76186
  source: "native",
76035
76187
  phase: running === null ? null : phase,
76036
- lastFetchedAt: now
76188
+ lastFetchedAt: fetchedAt
76037
76189
  };
76038
76190
  }
76039
- function runningFromDishwasherOperation(operation, program, previous, now) {
76040
- if (operation === null) return nativeSlice(null, null, previous, now, now);
76041
- if (operation === "Run" || operation === "Aborting") return nativeSlice(true, program ?? operation, previous, now, now);
76042
- if (operation === "Pause") return nativeSlice(false, "Pause", previous, now, now);
76043
- return nativeSlice(false, operation, previous, now, now);
76044
- }
76045
- function runningFromVacuumState(state, previous, now) {
76046
- if (state === null) return nativeSlice(null, null, previous, now, now);
76047
- if (state === "cleaning" || state === "returning" || state === "drying") return nativeSlice(true, state, previous, now, now);
76048
- if (state === "paused") return nativeSlice(false, "paused", previous, now, now);
76049
- return nativeSlice(false, state, previous, now, now);
76050
- }
76051
- function runningFromLawnMower(activity, previous, now) {
76052
- if (activity === null) return nativeSlice(null, null, previous, now, now);
76053
- if (activity === "mowing") return nativeSlice(true, activity, previous, now, now);
76054
- if (activity === "paused") return nativeSlice(false, "paused", previous, now, now);
76055
- return nativeSlice(false, activity, previous, now, now);
76056
- }
76057
- function runningFromMediaPlayer(state, previous, now) {
76058
- if (state === null) return nativeSlice(null, null, previous, now, now);
76059
- if (state === "playing" || state === "buffering") return nativeSlice(true, state, previous, now, now);
76060
- if (state === "paused") return nativeSlice(false, "paused", previous, now, now);
76061
- return nativeSlice(false, state, previous, now, now);
76191
+ function runningFromDishwasherOperation(operation, program, previous, fetchedAt, remainingSeconds, transitionAt) {
76192
+ const transitionAtMs = transitionAt ?? fetchedAt;
76193
+ if (operation === null) return nativeSlice(null, null, previous, transitionAtMs, fetchedAt, null);
76194
+ if (operation === "Run" || operation === "Aborting") {
76195
+ const bootstrap = programStartFromRemaining(true, remainingSeconds, fetchedAt);
76196
+ return nativeSlice(true, program ?? operation, previous, transitionAtMs, fetchedAt, bootstrap);
76197
+ }
76198
+ if (operation === "Pause") return nativeSlice(false, "Pause", previous, transitionAtMs, fetchedAt, null);
76199
+ return nativeSlice(false, operation, previous, transitionAtMs, fetchedAt, null);
76200
+ }
76201
+ function runningFromVacuumState(state, previous, fetchedAt, transitionAt) {
76202
+ const transitionAtMs = transitionAt ?? fetchedAt;
76203
+ if (state === null) return nativeSlice(null, null, previous, transitionAtMs, fetchedAt, null);
76204
+ if (state === "cleaning" || state === "returning" || state === "drying") return nativeSlice(true, state, previous, transitionAtMs, fetchedAt, null);
76205
+ if (state === "paused") return nativeSlice(false, "paused", previous, transitionAtMs, fetchedAt, null);
76206
+ return nativeSlice(false, state, previous, transitionAtMs, fetchedAt, null);
76207
+ }
76208
+ function runningFromLawnMower(activity, previous, fetchedAt, transitionAt) {
76209
+ const transitionAtMs = transitionAt ?? fetchedAt;
76210
+ if (activity === null) return nativeSlice(null, null, previous, transitionAtMs, fetchedAt, null);
76211
+ if (activity === "mowing") return nativeSlice(true, activity, previous, transitionAtMs, fetchedAt, null);
76212
+ if (activity === "paused") return nativeSlice(false, "paused", previous, transitionAtMs, fetchedAt, null);
76213
+ return nativeSlice(false, activity, previous, transitionAtMs, fetchedAt, null);
76214
+ }
76215
+ function runningFromMediaPlayer(state, previous, fetchedAt, transitionAt) {
76216
+ const transitionAtMs = transitionAt ?? fetchedAt;
76217
+ if (state === null) return nativeSlice(null, null, previous, transitionAtMs, fetchedAt, null);
76218
+ if (state === "playing" || state === "buffering") return nativeSlice(true, state, previous, transitionAtMs, fetchedAt, null);
76219
+ if (state === "paused") return nativeSlice(false, "paused", previous, transitionAtMs, fetchedAt, null);
76220
+ return nativeSlice(false, state, previous, transitionAtMs, fetchedAt, null);
76062
76221
  }
76063
76222
  //#endregion
76064
76223
  //#region src/clips/clip-event-labels.ts
@@ -76950,74 +77109,6 @@ function feederCustomizationExample(sources) {
76950
77109
  });
76951
77110
  }
76952
77111
  //#endregion
76953
- //#region src/composition/presets/doorbell-feature.ts
76954
- /**
76955
- * The doorbell of a camera as a customization (D671). ONE builder: the admin
76956
- * UI's Customize → Doorbell card writes every doorbell feature through it, and
76957
- * edits one only when it is exactly what this builds (`isDoorbellPresetFeature`)
76958
- * — a composed doorbell has one canonical shape.
76959
- */
76960
- /** A rise closer than this to the previous one is the same press (D671). */
76961
- var DOORBELL_RISE_DEBOUNCE_MS = 2e3;
76962
- /** Every source cap of `source` that `SOURCE_CAP_ACTIVE_FIELD` knows AND the device has bound. */
76963
- function doorbellFeatureSources(source, boundCaps) {
76964
- return Object.keys(require_composition.SOURCE_CAP_ACTIVE_FIELD).filter((cap) => Object.hasOwn(require_composition.SOURCE_CAP_CHANGED_AT_FIELD, cap) && boundCaps.includes(cap)).toSorted().flatMap((cap) => {
76965
- const activeField = require_composition.SOURCE_CAP_ACTIVE_FIELD[cap];
76966
- const changedAtField = require_composition.SOURCE_CAP_CHANGED_AT_FIELD[cap];
76967
- return activeField === void 0 || changedAtField === void 0 ? [] : [{
76968
- source,
76969
- cap,
76970
- activeField,
76971
- changedAtField
76972
- }];
76973
- });
76974
- }
76975
- /** The canonical D671 doorbell feature. Throws on zero sources. */
76976
- function buildDoorbellCompositionFeature(input) {
76977
- const sources = [...input.sources].toSorted((a, b) => `${a.source.addonId}/${a.source.stableId}#${a.cap}`.localeCompare(`${b.source.addonId}/${b.source.stableId}#${b.cap}`));
76978
- if (sources.length === 0) throw new Error("doorbell composition needs at least one source");
76979
- const edgeBindings = {};
76980
- sources.forEach((entry, index) => {
76981
- edgeBindings[`active${index}`] = {
76982
- kind: "from",
76983
- source: entry.source,
76984
- cap: entry.cap,
76985
- fieldPath: entry.activeField
76986
- };
76987
- edgeBindings[`changed${index}`] = {
76988
- kind: "from",
76989
- source: entry.source,
76990
- cap: entry.cap,
76991
- fieldPath: entry.changedAtField
76992
- };
76993
- });
76994
- return {
76995
- kind: "fields",
76996
- cap: "doorbell",
76997
- enabled: input.enabled,
76998
- fields: {
76999
- lastPressedAt: {
77000
- kind: "expression",
77001
- expr: `latest(${sources.map((_, index) => `rose(active${index}, changed${index}, ${DOORBELL_RISE_DEBOUNCE_MS})`).join(", ")})`,
77002
- bindings: edgeBindings
77003
- },
77004
- pressCountSinceStart: {
77005
- kind: "expression",
77006
- expr: "count(lastPressedAt)",
77007
- bindings: { lastPressedAt: {
77008
- kind: "from",
77009
- source: {
77010
- addonId: input.camera.addonId,
77011
- stableId: input.camera.stableId
77012
- },
77013
- cap: "doorbell",
77014
- fieldPath: "lastPressedAt"
77015
- } }
77016
- }
77017
- }
77018
- };
77019
- }
77020
- //#endregion
77021
77112
  //#region src/composition/presets/numeric-transforms.ts
77022
77113
  /**
77023
77114
  * Self-only invert / unit-convert composition features (D663). The Numeric
@@ -100291,6 +100382,1852 @@ function historyFieldsFor(capName) {
100291
100382
  return HISTORY_CATALOG[capName] ?? {};
100292
100383
  }
100293
100384
  //#endregion
100385
+ //#region src/generated/customize-catalog.ts
100386
+ /**
100387
+ * Customize catalog, projected from every `*.cap.ts` `customize` block.
100388
+ * 5 caps. Field keys are `runtimeState` paths. No Zod.
100389
+ */
100390
+ var CAP_CUSTOMIZE_CATALOG = {
100391
+ "battery": {
100392
+ version: 1,
100393
+ targets: [{
100394
+ id: "percentage",
100395
+ cardinality: "scalar",
100396
+ deviceTypes: [
100397
+ require_err_msg.DeviceType.Camera,
100398
+ require_err_msg.DeviceType.Sensor,
100399
+ require_err_msg.DeviceType.Button,
100400
+ require_err_msg.DeviceType.Switch,
100401
+ require_err_msg.DeviceType.PetFeeder,
100402
+ require_err_msg.DeviceType.Container,
100403
+ require_err_msg.DeviceType.Thermostat
100404
+ ],
100405
+ apply: "add-or-replace",
100406
+ vocabularyCap: "battery",
100407
+ vocabularyStateField: "percentage",
100408
+ sources: [{
100409
+ kind: "field",
100410
+ valueKinds: ["number", "string"],
100411
+ transformKinds: ["identity", "linear"]
100412
+ }],
100413
+ sourceReport: { status: "assignment-status" },
100414
+ advancedExpression: true,
100415
+ path: "percentage",
100416
+ valueKind: "number",
100417
+ nullable: true,
100418
+ companions: [
100419
+ {
100420
+ kind: "constant",
100421
+ path: "charging",
100422
+ value: "unknown",
100423
+ when: "add"
100424
+ },
100425
+ {
100426
+ kind: "constant",
100427
+ path: "sleeping",
100428
+ value: false,
100429
+ when: "add"
100430
+ },
100431
+ {
100432
+ kind: "clock",
100433
+ path: "lastUpdated",
100434
+ when: "add"
100435
+ }
100436
+ ]
100437
+ }]
100438
+ },
100439
+ "consumables": {
100440
+ version: 1,
100441
+ targets: [{
100442
+ id: "items",
100443
+ cardinality: "keyedArray",
100444
+ deviceTypes: [
100445
+ require_err_msg.DeviceType.Camera,
100446
+ require_err_msg.DeviceType.Hub,
100447
+ require_err_msg.DeviceType.Light,
100448
+ require_err_msg.DeviceType.Siren,
100449
+ require_err_msg.DeviceType.Switch,
100450
+ require_err_msg.DeviceType.Sensor,
100451
+ require_err_msg.DeviceType.Thermostat,
100452
+ require_err_msg.DeviceType.Climate,
100453
+ require_err_msg.DeviceType.Button,
100454
+ require_err_msg.DeviceType.EventEmitter,
100455
+ require_err_msg.DeviceType.Update,
100456
+ require_err_msg.DeviceType.Generic,
100457
+ require_err_msg.DeviceType.Notifier,
100458
+ require_err_msg.DeviceType.Script,
100459
+ require_err_msg.DeviceType.Automation,
100460
+ require_err_msg.DeviceType.Lock,
100461
+ require_err_msg.DeviceType.Cover,
100462
+ require_err_msg.DeviceType.Valve,
100463
+ require_err_msg.DeviceType.Humidifier,
100464
+ require_err_msg.DeviceType.WaterHeater,
100465
+ require_err_msg.DeviceType.Fan,
100466
+ require_err_msg.DeviceType.MediaPlayer,
100467
+ require_err_msg.DeviceType.AlarmPanel,
100468
+ require_err_msg.DeviceType.Control,
100469
+ require_err_msg.DeviceType.Presence,
100470
+ require_err_msg.DeviceType.Weather,
100471
+ require_err_msg.DeviceType.Vacuum,
100472
+ require_err_msg.DeviceType.LawnMower,
100473
+ require_err_msg.DeviceType.Container,
100474
+ require_err_msg.DeviceType.Image,
100475
+ require_err_msg.DeviceType.PetFeeder,
100476
+ require_err_msg.DeviceType.Dishwasher
100477
+ ],
100478
+ apply: "add",
100479
+ vocabularyCap: "consumables",
100480
+ sources: [],
100481
+ sourceReport: { status: "assignment-status" },
100482
+ advancedExpression: true,
100483
+ path: "items",
100484
+ keyField: "key",
100485
+ labelField: "label",
100486
+ nativeItems: "read-only",
100487
+ fields: [
100488
+ {
100489
+ path: "level",
100490
+ valueKind: "number",
100491
+ nullable: true,
100492
+ sources: [{
100493
+ kind: "numeric",
100494
+ transformKinds: ["identity", "linear"]
100495
+ }]
100496
+ },
100497
+ {
100498
+ path: "remaining.value",
100499
+ valueKind: "number",
100500
+ nullable: true,
100501
+ sources: [{
100502
+ kind: "numeric",
100503
+ transformKinds: ["identity"]
100504
+ }],
100505
+ literals: [{
100506
+ path: "remaining.unit",
100507
+ values: [
100508
+ "days",
100509
+ "hours",
100510
+ "cycles"
100511
+ ]
100512
+ }]
100513
+ },
100514
+ {
100515
+ path: "status",
100516
+ valueKind: "enum",
100517
+ nullable: true,
100518
+ enumValues: [
100519
+ "ok",
100520
+ "low",
100521
+ "empty",
100522
+ "full",
100523
+ "replace"
100524
+ ],
100525
+ sources: [{
100526
+ kind: "enum",
100527
+ transformKinds: ["enum-map"]
100528
+ }]
100529
+ }
100530
+ ],
100531
+ traits: [{
100532
+ path: "kind",
100533
+ values: [
100534
+ "wear",
100535
+ "supply",
100536
+ "waste"
100537
+ ],
100538
+ defaultValue: "wear"
100539
+ }, {
100540
+ path: "form",
100541
+ values: [
100542
+ "part",
100543
+ "tank",
100544
+ "product",
100545
+ "bin"
100546
+ ],
100547
+ defaultValue: "part"
100548
+ }],
100549
+ companions: [{
100550
+ kind: "clock",
100551
+ path: "lastChangedAt",
100552
+ when: "add"
100553
+ }, {
100554
+ kind: "clock",
100555
+ path: "lastFetchedAt",
100556
+ when: "add"
100557
+ }]
100558
+ }]
100559
+ },
100560
+ "doorbell": {
100561
+ version: 1,
100562
+ targets: [{
100563
+ id: "pressed",
100564
+ cardinality: "event",
100565
+ deviceTypes: [require_err_msg.DeviceType.Camera],
100566
+ apply: "add",
100567
+ vocabularyCap: "doorbell",
100568
+ vocabularyEvent: "onPressed",
100569
+ sources: [{
100570
+ kind: "binary",
100571
+ transformKinds: []
100572
+ }, {
100573
+ kind: "switch",
100574
+ transformKinds: []
100575
+ }],
100576
+ sourceReport: { status: "assignment-status" },
100577
+ advancedExpression: true,
100578
+ shape: "pulse",
100579
+ applyAuthority: { kind: "composition-claim" },
100580
+ eventName: "onPressed",
100581
+ combine: "any",
100582
+ edges: ["rising"],
100583
+ timestampPath: "lastPressedAt",
100584
+ counterPath: "pressCountSinceStart",
100585
+ highWater: "composer-advance",
100586
+ debounceMs: 2e3
100587
+ }]
100588
+ },
100589
+ "motion": {
100590
+ version: 1,
100591
+ targets: [{
100592
+ id: "detected",
100593
+ cardinality: "event",
100594
+ deviceTypes: [require_err_msg.DeviceType.Camera],
100595
+ apply: "replace",
100596
+ vocabularyCap: "motion",
100597
+ vocabularyEvent: "onMotionChanged",
100598
+ sources: [{
100599
+ kind: "binary",
100600
+ transformKinds: []
100601
+ }, {
100602
+ kind: "switch",
100603
+ transformKinds: []
100604
+ }],
100605
+ sourceReport: { status: "assignment-status" },
100606
+ advancedExpression: false,
100607
+ shape: "span",
100608
+ applyAuthority: {
100609
+ kind: "camera-sensors",
100610
+ timeoutField: "motionCooldownMs"
100611
+ },
100612
+ eventName: "onMotionChanged",
100613
+ combine: "any",
100614
+ start: "rising-edge",
100615
+ end: "falling-edge-or-timeout",
100616
+ timeoutMs: 3e4,
100617
+ sourceModes: [
100618
+ "in-addition",
100619
+ "confirm",
100620
+ "replace"
100621
+ ]
100622
+ }]
100623
+ },
100624
+ "running-state": {
100625
+ version: 1,
100626
+ targets: [{
100627
+ id: "running",
100628
+ cardinality: "scalar",
100629
+ deviceTypes: [
100630
+ require_err_msg.DeviceType.Dishwasher,
100631
+ require_err_msg.DeviceType.Vacuum,
100632
+ require_err_msg.DeviceType.LawnMower,
100633
+ require_err_msg.DeviceType.MediaPlayer,
100634
+ require_err_msg.DeviceType.WaterHeater,
100635
+ require_err_msg.DeviceType.Humidifier,
100636
+ require_err_msg.DeviceType.Fan,
100637
+ require_err_msg.DeviceType.Climate,
100638
+ require_err_msg.DeviceType.Thermostat,
100639
+ require_err_msg.DeviceType.PetFeeder
100640
+ ],
100641
+ apply: "add-or-replace",
100642
+ vocabularyCap: "running-state",
100643
+ vocabularyStateField: "running",
100644
+ sources: [
100645
+ {
100646
+ kind: "switch",
100647
+ transformKinds: ["identity"]
100648
+ },
100649
+ {
100650
+ kind: "binary",
100651
+ transformKinds: ["identity", "invert"]
100652
+ },
100653
+ {
100654
+ kind: "numeric",
100655
+ requiredUnit: "W",
100656
+ transformKinds: ["threshold"]
100657
+ }
100658
+ ],
100659
+ sourceReport: {
100660
+ status: "assignment-status",
100661
+ runtimeField: "source",
100662
+ nativeValue: "native",
100663
+ assignedValue: "assigned"
100664
+ },
100665
+ advancedExpression: true,
100666
+ path: "running",
100667
+ valueKind: "boolean",
100668
+ nullable: true,
100669
+ companions: [
100670
+ {
100671
+ kind: "constant",
100672
+ path: "source",
100673
+ value: "assigned",
100674
+ when: "always"
100675
+ },
100676
+ {
100677
+ kind: "constant",
100678
+ path: "phase",
100679
+ value: null,
100680
+ when: "always"
100681
+ },
100682
+ {
100683
+ kind: "clock",
100684
+ path: "lastFetchedAt",
100685
+ when: "always"
100686
+ },
100687
+ {
100688
+ kind: "transition-time",
100689
+ path: "since",
100690
+ sourceChangedAt: "prefer"
100691
+ }
100692
+ ]
100693
+ }]
100694
+ }
100695
+ };
100696
+ //#endregion
100697
+ //#region src/composition/customize/override-draft.ts
100698
+ /**
100699
+ * A saved Customize assignment (D936). The schema is the type: callers use
100700
+ * `z.infer`, they do not restate the shape.
100701
+ */
100702
+ var OverrideSourceSchema = zod.z.object({
100703
+ addonId: zod.z.string().min(1),
100704
+ stableId: zod.z.string().min(1),
100705
+ cap: zod.z.string().min(1),
100706
+ fieldPath: zod.z.string().min(1)
100707
+ });
100708
+ var IdentityTransformDraftSchema = zod.z.object({ kind: zod.z.literal("identity") });
100709
+ var InvertTransformDraftSchema = zod.z.object({ kind: zod.z.literal("invert") });
100710
+ var ThresholdTransformDraftSchema = zod.z.object({
100711
+ kind: zod.z.literal("threshold"),
100712
+ comparison: zod.z.enum(["gte", "lte"]),
100713
+ threshold: zod.z.number(),
100714
+ startDwellMs: zod.z.number().nonnegative(),
100715
+ stopDwellMs: zod.z.number().nonnegative()
100716
+ });
100717
+ var LinearTransformDraftSchema = zod.z.object({
100718
+ kind: zod.z.literal("linear"),
100719
+ scale: zod.z.number(),
100720
+ offset: zod.z.number(),
100721
+ clampMin: zod.z.number().optional(),
100722
+ clampMax: zod.z.number().optional()
100723
+ });
100724
+ var EnumMapTransformDraftSchema = zod.z.object({
100725
+ kind: zod.z.literal("enum-map"),
100726
+ mapping: zod.z.record(zod.z.string(), zod.z.string().nullable())
100727
+ });
100728
+ var NumberBridgeTransformDraftSchema = zod.z.object({ kind: zod.z.literal("number") });
100729
+ var CustomizeTransformDraftSchema = zod.z.discriminatedUnion("kind", [
100730
+ IdentityTransformDraftSchema,
100731
+ InvertTransformDraftSchema,
100732
+ ThresholdTransformDraftSchema,
100733
+ LinearTransformDraftSchema,
100734
+ EnumMapTransformDraftSchema,
100735
+ NumberBridgeTransformDraftSchema
100736
+ ]);
100737
+ var ScalarOverrideDraftSchema = zod.z.object({
100738
+ kind: zod.z.literal("scalar"),
100739
+ cap: zod.z.string().min(1),
100740
+ targetId: zod.z.string().min(1),
100741
+ source: OverrideSourceSchema,
100742
+ transform: CustomizeTransformDraftSchema
100743
+ });
100744
+ var KeyedArrayOverrideDraftSchema = zod.z.object({
100745
+ kind: zod.z.literal("keyedArray"),
100746
+ cap: zod.z.string().min(1),
100747
+ targetId: zod.z.string().min(1),
100748
+ key: zod.z.string().min(1),
100749
+ label: zod.z.string().min(1),
100750
+ fieldPath: zod.z.string().min(1),
100751
+ source: OverrideSourceSchema,
100752
+ transform: CustomizeTransformDraftSchema
100753
+ });
100754
+ var PulseOverrideDraftSchema = zod.z.object({
100755
+ kind: zod.z.literal("pulse"),
100756
+ cap: zod.z.string().min(1),
100757
+ targetId: zod.z.string().min(1),
100758
+ enabled: zod.z.boolean(),
100759
+ debounceMs: zod.z.number().nonnegative(),
100760
+ sources: zod.z.array(OverrideSourceSchema).min(1)
100761
+ });
100762
+ var SpanSourceDraftSchema = OverrideSourceSchema.extend({ mode: zod.z.enum([
100763
+ "in-addition",
100764
+ "confirm",
100765
+ "replace"
100766
+ ]).optional() });
100767
+ var SpanOverrideDraftSchema = zod.z.object({
100768
+ kind: zod.z.literal("span"),
100769
+ cap: zod.z.string().min(1),
100770
+ targetId: zod.z.string().min(1),
100771
+ timeoutMs: zod.z.number().nonnegative(),
100772
+ cooldownMs: zod.z.number().nonnegative().optional(),
100773
+ sources: zod.z.array(SpanSourceDraftSchema)
100774
+ });
100775
+ var OverrideDraftSchema = zod.z.discriminatedUnion("kind", [
100776
+ ScalarOverrideDraftSchema,
100777
+ KeyedArrayOverrideDraftSchema,
100778
+ PulseOverrideDraftSchema,
100779
+ SpanOverrideDraftSchema
100780
+ ]);
100781
+ /** Effective authority for one declared target. Not a second stored switch. */
100782
+ var CustomizeAssignmentStatusSchema = zod.z.object({
100783
+ targetId: zod.z.string().min(1),
100784
+ configured: zod.z.boolean(),
100785
+ source: zod.z.enum(["native", "assigned"]),
100786
+ state: zod.z.enum([
100787
+ "active",
100788
+ "stale",
100789
+ "released",
100790
+ "unavailable"
100791
+ ]),
100792
+ reason: zod.z.string().nullable()
100793
+ });
100794
+ //#endregion
100795
+ //#region src/composition/customize/field-source.ts
100796
+ function fromRead(source, cap, fieldPath) {
100797
+ return {
100798
+ kind: "from",
100799
+ source,
100800
+ cap,
100801
+ fieldPath
100802
+ };
100803
+ }
100804
+ function expression(expr, bindings) {
100805
+ return {
100806
+ kind: "expression",
100807
+ expr,
100808
+ bindings
100809
+ };
100810
+ }
100811
+ function literal(expr) {
100812
+ return expression(expr, {});
100813
+ }
100814
+ function isLiteral(source, expr) {
100815
+ return source?.kind === "expression" && source.expr === expr && Object.keys(source.bindings).length === 0;
100816
+ }
100817
+ //#endregion
100818
+ //#region src/composition/customize/scalar-expr.ts
100819
+ var UNSIGNED = /^\d+(?:\.\d+)?$/;
100820
+ var SIGNED = /^-?\d+(?:\.\d+)?$/;
100821
+ var DWELL = /^dwell\(x (>=|<=) (\d+(?:\.\d+)?), (\d+(?:\.\d+)?), (\d+(?:\.\d+)?)\)$/;
100822
+ var LINEAR_CLAMP = /^clamp\((x|number\(x\)) \* (-?\d+(?:\.\d+)?) \+ (-?\d+(?:\.\d+)?), (-?\d+(?:\.\d+)?), (-?\d+(?:\.\d+)?)\)$/;
100823
+ var LINEAR_BARE = /^(x|number\(x\)) \* (-?\d+(?:\.\d+)?) \+ (-?\d+(?:\.\d+)?)$/;
100824
+ var IDENT = /^[A-Za-z_][A-Za-z0-9_]*$/;
100825
+ function unsignedToken(value) {
100826
+ return token(value, UNSIGNED);
100827
+ }
100828
+ function signedToken(value) {
100829
+ return token(value, SIGNED);
100830
+ }
100831
+ function constantExpr(value) {
100832
+ if (typeof value === "string") return JSON.stringify(value);
100833
+ if (typeof value === "number") return signedToken(value);
100834
+ if (typeof value === "boolean") return value ? "true" : "false";
100835
+ return "null";
100836
+ }
100837
+ function isIdentifier(path) {
100838
+ return IDENT.test(path);
100839
+ }
100840
+ function dwellExpr(comparison, threshold, start, stop) {
100841
+ return `dwell(x ${comparison === "gte" ? ">=" : "<="} ${threshold}, ${start}, ${stop})`;
100842
+ }
100843
+ function linearExpr(inner, scale, offset, clampMin, clampMax) {
100844
+ const arithmetic = `${inner} * ${scale} + ${offset}`;
100845
+ if (clampMin === null || clampMax === null) return arithmetic;
100846
+ return `clamp(${arithmetic}, ${clampMin}, ${clampMax})`;
100847
+ }
100848
+ function parseDwell(expr) {
100849
+ const match = DWELL.exec(expr);
100850
+ if (match === null) return null;
100851
+ const threshold = Number(match[2]);
100852
+ const startDwellMs = Number(match[3]);
100853
+ const stopDwellMs = Number(match[4]);
100854
+ if (!Number.isFinite(threshold) || !Number.isFinite(startDwellMs) || !Number.isFinite(stopDwellMs)) return null;
100855
+ return {
100856
+ comparison: match[1] === "<=" ? "lte" : "gte",
100857
+ threshold,
100858
+ startDwellMs,
100859
+ stopDwellMs
100860
+ };
100861
+ }
100862
+ function parseLinear(expr) {
100863
+ const clamped = LINEAR_CLAMP.exec(expr);
100864
+ if (clamped !== null) return linearFrom(clamped[1], clamped[2], clamped[3], clamped[4], clamped[5]);
100865
+ const bare = LINEAR_BARE.exec(expr);
100866
+ if (bare === null) return null;
100867
+ return linearFrom(bare[1], bare[2], bare[3], void 0, void 0);
100868
+ }
100869
+ function singleFrom(source) {
100870
+ if (source.kind === "from") return {
100871
+ cap: source.cap,
100872
+ fieldPath: source.fieldPath,
100873
+ addonId: source.source.addonId,
100874
+ stableId: source.source.stableId
100875
+ };
100876
+ if (source.kind !== "expression" || Object.keys(source.bindings).length !== 1) return null;
100877
+ const bound = source.bindings["x"];
100878
+ if (bound === void 0 || bound.kind !== "from") return null;
100879
+ return {
100880
+ cap: bound.cap,
100881
+ fieldPath: bound.fieldPath,
100882
+ addonId: bound.source.addonId,
100883
+ stableId: bound.source.stableId
100884
+ };
100885
+ }
100886
+ function token(value, pattern) {
100887
+ if (!Number.isFinite(value)) return null;
100888
+ const text = Object.is(value, -0) ? "0" : String(value);
100889
+ return pattern.test(text) ? text : null;
100890
+ }
100891
+ function linearFrom(inner, scale, offset, clampMin, clampMax) {
100892
+ if (inner !== "x" && inner !== "number(x)") return null;
100893
+ if (scale === void 0 || offset === void 0) return null;
100894
+ const parsedScale = Number(scale);
100895
+ const parsedOffset = Number(offset);
100896
+ if (!Number.isFinite(parsedScale) || !Number.isFinite(parsedOffset)) return null;
100897
+ if (clampMin === void 0 || clampMax === void 0) return {
100898
+ inner,
100899
+ scale: parsedScale,
100900
+ offset: parsedOffset
100901
+ };
100902
+ const parsedMin = Number(clampMin);
100903
+ const parsedMax = Number(clampMax);
100904
+ if (!Number.isFinite(parsedMin) || !Number.isFinite(parsedMax)) return null;
100905
+ return {
100906
+ inner,
100907
+ scale: parsedScale,
100908
+ offset: parsedOffset,
100909
+ clampMin: parsedMin,
100910
+ clampMax: parsedMax
100911
+ };
100912
+ }
100913
+ //#endregion
100914
+ //#region src/composition/customize/scalar-compile.ts
100915
+ var catalog$4 = CAP_CUSTOMIZE_CATALOG;
100916
+ function compileScalarOverride(input) {
100917
+ const target = catalogTarget$4(input.draft.cap, input.draft.targetId);
100918
+ if (target === null || target.cardinality !== "scalar" || target.path === void 0) return fail$2("target");
100919
+ if (input.draft.transform.kind === "enum-map") return fail$2("enum");
100920
+ const source = sourceSpec$1(target, input.sourceValueKind, input.draft.source.cap);
100921
+ if (source === null) return fail$2("source-kind");
100922
+ if (source.requiredUnit !== void 0 && input.sourceUnit !== source.requiredUnit) return fail$2("unit");
100923
+ const allowed = input.draft.transform.kind === "number" ? "identity" : input.draft.transform.kind;
100924
+ if (!source.transformKinds.includes(allowed)) return fail$2("transform");
100925
+ const value = compileValue$1(input);
100926
+ if (!value.ok) return value;
100927
+ const fields = compileCompanions$1(input, target, value.value);
100928
+ if (!fields.ok) return fields;
100929
+ return {
100930
+ ok: true,
100931
+ value: {
100932
+ kind: "fields",
100933
+ cap: input.draft.cap,
100934
+ fields: fields.value
100935
+ }
100936
+ };
100937
+ }
100938
+ function fail$2(reason) {
100939
+ return {
100940
+ ok: false,
100941
+ reason
100942
+ };
100943
+ }
100944
+ function catalogTarget$4(cap, targetId) {
100945
+ const entry = catalog$4[cap];
100946
+ if (entry === void 0) return null;
100947
+ return entry.targets.find((target) => target.id === targetId) ?? null;
100948
+ }
100949
+ function sourceSpec$1(target, valueKind, sourceCap) {
100950
+ if (valueKind === "boolean" && sourceCap === "switch") return target.sources.find((source) => source.kind === "switch") ?? null;
100951
+ if (valueKind === "boolean") return target.sources.find((source) => source.kind === "binary") ?? fieldSource(target, "boolean");
100952
+ if (valueKind === "number") return target.sources.find((source) => source.kind === "numeric") ?? fieldSource(target, "number");
100953
+ return fieldSource(target, "string");
100954
+ }
100955
+ function fieldSource(target, valueKind) {
100956
+ return target.sources.find((source) => source.kind === "field" && (source.valueKinds ?? []).includes(valueKind)) ?? null;
100957
+ }
100958
+ function compileValue$1(input) {
100959
+ const read = fromRead(refOf$1(input.draft), input.draft.source.cap, input.draft.source.fieldPath);
100960
+ const transform = input.draft.transform;
100961
+ if (transform.kind === "identity" || transform.kind === "number") return {
100962
+ ok: true,
100963
+ value: identityValue(read, input.sourceValueKind)
100964
+ };
100965
+ if (transform.kind === "invert") {
100966
+ if (input.sourceValueKind !== "boolean") return fail$2("source-kind");
100967
+ return {
100968
+ ok: true,
100969
+ value: expression("!x", { x: read })
100970
+ };
100971
+ }
100972
+ if (transform.kind === "threshold") return thresholdValue(transform, read);
100973
+ if (transform.kind === "linear") return linearValue$1(transform, read, input.sourceValueKind);
100974
+ return fail$2("transform");
100975
+ }
100976
+ function identityValue(read, valueKind) {
100977
+ if (valueKind === "string") return expression("number(x)", { x: read });
100978
+ return read;
100979
+ }
100980
+ function thresholdValue(transform, read) {
100981
+ const threshold = unsignedToken(transform.threshold);
100982
+ if (threshold === null) return fail$2("threshold");
100983
+ const start = unsignedToken(transform.startDwellMs);
100984
+ const stop = unsignedToken(transform.stopDwellMs);
100985
+ if (start === null || stop === null) return fail$2("dwell");
100986
+ return {
100987
+ ok: true,
100988
+ value: expression(dwellExpr(transform.comparison, threshold, start, stop), { x: read })
100989
+ };
100990
+ }
100991
+ function linearValue$1(transform, read, valueKind) {
100992
+ const scale = signedToken(transform.scale);
100993
+ const offset = signedToken(transform.offset);
100994
+ if (scale === null || offset === null) return fail$2("scale");
100995
+ const clamps = clampTokens$1(transform.clampMin, transform.clampMax);
100996
+ if (!clamps.ok) return clamps;
100997
+ return {
100998
+ ok: true,
100999
+ value: expression(linearExpr(valueKind === "string" ? "number(x)" : "x", scale, offset, clamps.value.min, clamps.value.max), { x: read })
101000
+ };
101001
+ }
101002
+ function clampTokens$1(clampMin, clampMax) {
101003
+ if (clampMin === void 0 && clampMax === void 0) return {
101004
+ ok: true,
101005
+ value: {
101006
+ min: null,
101007
+ max: null
101008
+ }
101009
+ };
101010
+ if (clampMin === void 0 || clampMax === void 0) return fail$2("clamp");
101011
+ const min = signedToken(clampMin);
101012
+ const max = signedToken(clampMax);
101013
+ if (min === null || max === null || clampMin > clampMax) return fail$2("clamp");
101014
+ return {
101015
+ ok: true,
101016
+ value: {
101017
+ min,
101018
+ max
101019
+ }
101020
+ };
101021
+ }
101022
+ function compileCompanions$1(input, target, value) {
101023
+ if (target.path === void 0) return fail$2("target");
101024
+ const path = target.path;
101025
+ return (target.companions ?? []).filter((companion) => companionIncluded$1(companion, input.apply)).reduce((acc, companion) => {
101026
+ if (!acc.ok) return acc;
101027
+ const built = companionField(input, path, companion);
101028
+ if (!built.ok) return {
101029
+ ok: false,
101030
+ reason: built.reason
101031
+ };
101032
+ return {
101033
+ ok: true,
101034
+ value: {
101035
+ ...acc.value,
101036
+ [companion.path]: built.value
101037
+ }
101038
+ };
101039
+ }, {
101040
+ ok: true,
101041
+ value: { [path]: value }
101042
+ });
101043
+ }
101044
+ function companionIncluded$1(companion, apply) {
101045
+ if (companion.kind === "transition-time") return true;
101046
+ return companion.when === "always" || apply === "add";
101047
+ }
101048
+ function companionField(input, path, companion) {
101049
+ if (companion.kind === "constant") {
101050
+ const expr = constantExpr(companion.value);
101051
+ if (expr === null) return fail$2("scale");
101052
+ return {
101053
+ ok: true,
101054
+ value: literal(expr)
101055
+ };
101056
+ }
101057
+ if (companion.kind === "clock") return {
101058
+ ok: true,
101059
+ value: literal("now")
101060
+ };
101061
+ return transitionField(input, path, companion.sourceChangedAt);
101062
+ }
101063
+ function transitionField(input, path, sourceChangedAt) {
101064
+ if (sourceChangedAt === "prefer" && input.sourceValueKind === "boolean" && input.changedAtField !== null && isIdentifier(input.changedAtField)) return {
101065
+ ok: true,
101066
+ value: fromRead(refOf$1(input.draft), input.draft.source.cap, input.changedAtField)
101067
+ };
101068
+ if (!isIdentifier(path)) return fail$2("transform");
101069
+ const bindings = { [path]: fromRead(input.device, input.draft.cap, path) };
101070
+ return {
101071
+ ok: true,
101072
+ value: expression(`latchedAt(${path})`, bindings)
101073
+ };
101074
+ }
101075
+ function refOf$1(draft) {
101076
+ return {
101077
+ addonId: draft.source.addonId,
101078
+ stableId: draft.source.stableId
101079
+ };
101080
+ }
101081
+ //#endregion
101082
+ //#region src/composition/customize/normalize.ts
101083
+ function sortKeys(value) {
101084
+ if (Array.isArray(value)) return value.map(sortKeys);
101085
+ if (typeof value !== "object" || value === null) return value;
101086
+ return Object.fromEntries(Object.entries(value).toSorted(([a], [b]) => a.localeCompare(b)).map(([key, inner]) => [key, sortKeys(inner)]));
101087
+ }
101088
+ /** Key order is not part of the stored contract. Array order is. */
101089
+ function normalizeStored(stored) {
101090
+ return sortKeys(stored);
101091
+ }
101092
+ function sameStored(a, b) {
101093
+ return JSON.stringify(normalizeStored(a)) === JSON.stringify(normalizeStored(b));
101094
+ }
101095
+ //#endregion
101096
+ //#region src/composition/customize/scalar-decode.ts
101097
+ var catalog$3 = CAP_CUSTOMIZE_CATALOG;
101098
+ function decodeScalarOverride(input) {
101099
+ const target = catalogTarget$3(input.cap, input.targetId);
101100
+ if (target === null || target.cardinality !== "scalar" || target.path === void 0) return null;
101101
+ if (input.feature.cap !== input.cap || input.feature.kind !== "fields") return null;
101102
+ const outputKind = scalarOutputKind(target.valueKind);
101103
+ if (outputKind === null) return null;
101104
+ const value = input.feature.fields[target.path];
101105
+ const read = readValue(value, outputKind, input.cap, input.targetId);
101106
+ if (read === null) return null;
101107
+ const apply = inferApply$1(target, input.feature.fields);
101108
+ if (apply === null) return null;
101109
+ const changedAtField = inferChangedAt(target, input.feature.fields, read);
101110
+ const sourceUnit = unitFor(target, read.sourceValueKind, read.draft.source.cap);
101111
+ const decoded = {
101112
+ ...read,
101113
+ apply,
101114
+ changedAtField,
101115
+ sourceUnit
101116
+ };
101117
+ return reproduces$1(decoded, input.device, input.feature) ? decoded : null;
101118
+ }
101119
+ function catalogTarget$3(cap, targetId) {
101120
+ const entry = catalog$3[cap];
101121
+ if (entry === void 0) return null;
101122
+ return entry.targets.find((target) => target.id === targetId) ?? null;
101123
+ }
101124
+ function scalarOutputKind(valueKind) {
101125
+ if (valueKind === "boolean" || valueKind === "number" || valueKind === "string") return valueKind;
101126
+ return null;
101127
+ }
101128
+ function readValue(field, outputKind, cap, targetId) {
101129
+ if (field === void 0) return null;
101130
+ if (field.kind === "from") return drafted(field, { kind: "identity" }, outputKind, cap, targetId);
101131
+ if (field.kind !== "expression") return null;
101132
+ if (field.expr === "number(x)" && outputKind === "number") return drafted(field, { kind: "identity" }, "string", cap, targetId);
101133
+ if (field.expr === "!x" && outputKind === "boolean") return drafted(field, { kind: "invert" }, "boolean", cap, targetId);
101134
+ const dwell = parseDwell(field.expr);
101135
+ if (dwell !== null) return drafted(field, {
101136
+ kind: "threshold",
101137
+ comparison: dwell.comparison,
101138
+ threshold: dwell.threshold,
101139
+ startDwellMs: dwell.startDwellMs,
101140
+ stopDwellMs: dwell.stopDwellMs
101141
+ }, "number", cap, targetId);
101142
+ const linear = parseLinear(field.expr);
101143
+ if (linear === null || outputKind !== "number") return null;
101144
+ return drafted(field, {
101145
+ kind: "linear",
101146
+ scale: linear.scale,
101147
+ offset: linear.offset,
101148
+ ...linear.clampMin === void 0 || linear.clampMax === void 0 ? {} : {
101149
+ clampMin: linear.clampMin,
101150
+ clampMax: linear.clampMax
101151
+ }
101152
+ }, linear.inner === "number(x)" ? "string" : "number", cap, targetId);
101153
+ }
101154
+ function drafted(field, transform, sourceValueKind, cap, targetId) {
101155
+ const bound = singleFrom(field);
101156
+ if (bound === null) return null;
101157
+ return {
101158
+ sourceValueKind,
101159
+ draft: {
101160
+ kind: "scalar",
101161
+ cap,
101162
+ targetId,
101163
+ source: {
101164
+ addonId: bound.addonId,
101165
+ stableId: bound.stableId,
101166
+ cap: bound.cap,
101167
+ fieldPath: bound.fieldPath
101168
+ },
101169
+ transform
101170
+ }
101171
+ };
101172
+ }
101173
+ function inferApply$1(target, fields) {
101174
+ const addPaths = (target.companions ?? []).flatMap((companion) => companion.kind !== "transition-time" && companion.when === "add" ? [companion.path] : []);
101175
+ if (addPaths.length === 0) return "replace";
101176
+ const present = addPaths.filter((path) => fields[path] !== void 0);
101177
+ if (present.length === addPaths.length) return "add";
101178
+ if (present.length === 0) return "replace";
101179
+ return null;
101180
+ }
101181
+ function inferChangedAt(target, fields, read) {
101182
+ if (read.sourceValueKind !== "boolean") return null;
101183
+ const transition = (target.companions ?? []).find((companion) => companion.kind === "transition-time");
101184
+ if (transition === void 0 || transition.sourceChangedAt !== "prefer") return null;
101185
+ const since = fields[transition.path];
101186
+ if (since?.kind !== "from") return null;
101187
+ if (since.cap !== read.draft.source.cap) return null;
101188
+ if (since.source.addonId !== read.draft.source.addonId || since.source.stableId !== read.draft.source.stableId) return null;
101189
+ return since.fieldPath;
101190
+ }
101191
+ function unitFor(target, valueKind, sourceCap) {
101192
+ if (valueKind === "boolean" && sourceCap === "switch") return null;
101193
+ if (valueKind === "number") return target.sources.find((source) => source.kind === "numeric")?.requiredUnit ?? null;
101194
+ return null;
101195
+ }
101196
+ function reproduces$1(decoded, device, stored) {
101197
+ const compiled = compileScalarOverride({
101198
+ draft: decoded.draft,
101199
+ device,
101200
+ apply: decoded.apply,
101201
+ sourceValueKind: decoded.sourceValueKind,
101202
+ changedAtField: decoded.changedAtField,
101203
+ sourceUnit: decoded.sourceUnit
101204
+ });
101205
+ if (!compiled.ok) return false;
101206
+ return sameFeature$2(compiled.value, stored);
101207
+ }
101208
+ function sameFeature$2(compiled, stored) {
101209
+ return sameStored(wrap$1(withoutEnabled(compiled)), wrap$1(withoutEnabled(stored)));
101210
+ }
101211
+ function withoutEnabled(feature) {
101212
+ const { enabled: _enabled, ...rest } = feature;
101213
+ return rest;
101214
+ }
101215
+ function wrap$1(feature) {
101216
+ return {
101217
+ family: "composition",
101218
+ target: {
101219
+ addonId: "t",
101220
+ stableId: "t"
101221
+ },
101222
+ feature
101223
+ };
101224
+ }
101225
+ //#endregion
101226
+ //#region src/composition/customize/scalar-status.ts
101227
+ function projectScalarAssignment(input) {
101228
+ if (input.stored.kind === "pending") return unanswered$1(input.targetId, "pending", null);
101229
+ if (input.stored.kind === "unknown") return unanswered$1(input.targetId, "unknown", input.stored.reason ?? "unknown");
101230
+ if (input.stored.value === null) return native$2(input.targetId);
101231
+ const decoded = decodeScalarOverride({
101232
+ feature: input.stored.value,
101233
+ device: input.device,
101234
+ cap: input.cap,
101235
+ targetId: input.targetId
101236
+ });
101237
+ if (decoded === null) return {
101238
+ text: "unknown",
101239
+ status: status$1(input.targetId, true, "assigned", "unavailable", "not-canonical")
101240
+ };
101241
+ return projectDecoded$1(input, decoded.apply, sourceLabel(decoded.draft.source.cap, decoded.draft.source.fieldPath));
101242
+ }
101243
+ function projectDecoded$1(input, apply, label) {
101244
+ if (input.sourcePresence === "pending") return {
101245
+ text: "pending",
101246
+ status: status$1(input.targetId, true, "assigned", "stale", null)
101247
+ };
101248
+ if (input.sourcePresence === "unknown") return {
101249
+ text: "unknown",
101250
+ status: status$1(input.targetId, true, "assigned", "stale", input.sourceUnknownReason ?? "unknown")
101251
+ };
101252
+ if (input.sourcePresence === "absent" && apply === "replace") return {
101253
+ text: "Native · source missing",
101254
+ status: status$1(input.targetId, true, "native", "released", "source-missing")
101255
+ };
101256
+ if (input.sourcePresence === "absent") return {
101257
+ text: `Assigned · ${label}`,
101258
+ status: status$1(input.targetId, true, "assigned", "unavailable", "source-missing")
101259
+ };
101260
+ return {
101261
+ text: `Assigned · ${label}`,
101262
+ status: status$1(input.targetId, true, "assigned", "active", null)
101263
+ };
101264
+ }
101265
+ function native$2(targetId) {
101266
+ return {
101267
+ text: "Native",
101268
+ status: status$1(targetId, false, "native", "active", null)
101269
+ };
101270
+ }
101271
+ function unanswered$1(targetId, text, reason) {
101272
+ return {
101273
+ text,
101274
+ status: status$1(targetId, false, "native", text === "pending" ? "unavailable" : "stale", reason)
101275
+ };
101276
+ }
101277
+ function sourceLabel(cap, fieldPath) {
101278
+ return `${cap}.${fieldPath}`;
101279
+ }
101280
+ function status$1(targetId, configured, source, state, reason) {
101281
+ return {
101282
+ targetId,
101283
+ configured,
101284
+ source,
101285
+ state,
101286
+ reason
101287
+ };
101288
+ }
101289
+ //#endregion
101290
+ //#region src/composition/customize/event-compile.ts
101291
+ var catalog$2 = CAP_CUSTOMIZE_CATALOG;
101292
+ function compilePulseOverride(input) {
101293
+ const target = catalogTarget$2(input.draft.cap, input.draft.targetId);
101294
+ if (target === null || target.shape !== "pulse") return fail$1("target");
101295
+ const timestampPath = target.timestampPath;
101296
+ const counterPath = target.counterPath;
101297
+ if (timestampPath === void 0 || counterPath === void 0 || !isIdentifier(timestampPath) || !isIdentifier(counterPath)) return fail$1("path");
101298
+ if (!(target.edges ?? []).includes("rising")) return fail$1("edge");
101299
+ const pairs = pairSources(input);
101300
+ if (!pairs.ok) return pairs;
101301
+ if (pairs.value.length === 0) return fail$1("sources");
101302
+ const debounce = unsignedToken(input.draft.debounceMs);
101303
+ if (debounce === null) return fail$1("debounce");
101304
+ const ordered = pairs.value.toSorted((a, b) => pairKey(a).localeCompare(pairKey(b)));
101305
+ return {
101306
+ ok: true,
101307
+ value: {
101308
+ kind: "fields",
101309
+ cap: input.draft.cap,
101310
+ enabled: input.draft.enabled,
101311
+ fields: {
101312
+ [timestampPath]: expression(pulseExpr(ordered.length, debounce), pulseBindings(ordered)),
101313
+ [counterPath]: expression(`count(${timestampPath})`, { [timestampPath]: fromRead(input.device, input.draft.cap, timestampPath) })
101314
+ }
101315
+ }
101316
+ };
101317
+ }
101318
+ function compileSpanOverride(input) {
101319
+ const target = catalogTarget$2(input.draft.cap, input.draft.targetId);
101320
+ if (target === null || target.shape !== "span") return fail$1("target");
101321
+ if (target.applyAuthority?.kind !== "camera-sensors") return fail$1("authority");
101322
+ if (input.draft.timeoutMs < 0 || !Number.isFinite(input.draft.timeoutMs)) return fail$1("timeout");
101323
+ if (target.cooldownMs === void 0 && input.draft.cooldownMs !== void 0) return fail$1("cooldown");
101324
+ if (input.draft.cooldownMs !== void 0 && input.draft.cooldownMs < 0) return fail$1("cooldown");
101325
+ const sensors = sensorEntries(input.draft.sources, input.device, target.sourceModes);
101326
+ if (!sensors.ok) return sensors;
101327
+ if (sensors.value.length === 0) return {
101328
+ ok: true,
101329
+ value: {
101330
+ config: null,
101331
+ timeoutMs: input.draft.timeoutMs,
101332
+ cooldownMs: input.draft.cooldownMs
101333
+ }
101334
+ };
101335
+ const parsed = CameraSensorsConfigSchema.safeParse({ sensors: sensors.value });
101336
+ if (!parsed.success) {
101337
+ const issue = parsed.error.issues[0];
101338
+ return fail$1(issue === void 0 ? "cameraSensors" : issue.message);
101339
+ }
101340
+ return {
101341
+ ok: true,
101342
+ value: {
101343
+ config: parsed.data,
101344
+ timeoutMs: input.draft.timeoutMs,
101345
+ cooldownMs: input.draft.cooldownMs
101346
+ }
101347
+ };
101348
+ }
101349
+ function pairSources(input) {
101350
+ if (input.changedAtFields.length !== input.draft.sources.length) return fail$1("changed-at");
101351
+ const pairs = input.draft.sources.map((source, index) => {
101352
+ return {
101353
+ source,
101354
+ changedAtField: input.changedAtFields[index] ?? ""
101355
+ };
101356
+ });
101357
+ if (pairs.some((pair) => pair.changedAtField.length === 0 || pair.source.fieldPath.length === 0)) return fail$1("changed-at");
101358
+ return {
101359
+ ok: true,
101360
+ value: pairs
101361
+ };
101362
+ }
101363
+ function pairKey(pair) {
101364
+ return `${pair.source.addonId}/${pair.source.stableId}#${pair.source.cap}#${pair.source.fieldPath}`;
101365
+ }
101366
+ function pulseExpr(count, debounce) {
101367
+ const roses = [];
101368
+ for (let index = 0; index < count; index += 1) roses.push(`rose(active${index}, changed${index}, ${debounce})`);
101369
+ return `latest(${roses.join(", ")})`;
101370
+ }
101371
+ function pulseBindings(pairs) {
101372
+ const bindings = {};
101373
+ pairs.forEach((pair, index) => {
101374
+ const ref = {
101375
+ addonId: pair.source.addonId,
101376
+ stableId: pair.source.stableId
101377
+ };
101378
+ bindings[`active${index}`] = fromRead(ref, pair.source.cap, pair.source.fieldPath);
101379
+ bindings[`changed${index}`] = fromRead(ref, pair.source.cap, pair.changedAtField);
101380
+ });
101381
+ return bindings;
101382
+ }
101383
+ function sensorEntries(sources, device, modes) {
101384
+ const entries = [];
101385
+ for (const source of sources) {
101386
+ if (source.addonId === device.addonId && source.stableId === device.stableId) return fail$1("self");
101387
+ const mode = source.mode ?? "in-addition";
101388
+ if (modes !== void 0 && !modes.includes(mode)) return fail$1("mode");
101389
+ if (modes === void 0 && source.mode !== void 0 && source.mode !== "in-addition") return fail$1("mode");
101390
+ entries.push({
101391
+ addonId: source.addonId,
101392
+ stableId: source.stableId,
101393
+ mode
101394
+ });
101395
+ }
101396
+ return {
101397
+ ok: true,
101398
+ value: entries
101399
+ };
101400
+ }
101401
+ function catalogTarget$2(cap, targetId) {
101402
+ const entry = catalog$2[cap];
101403
+ if (entry === void 0) return null;
101404
+ return entry.targets.find((target) => target.id === targetId) ?? null;
101405
+ }
101406
+ function fail$1(reason) {
101407
+ return {
101408
+ ok: false,
101409
+ reason
101410
+ };
101411
+ }
101412
+ //#endregion
101413
+ //#region src/composition/customize/event-decode.ts
101414
+ var ROSE_SOURCE = String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
101415
+ function decodePulseOverride(input) {
101416
+ if (input.feature.cap !== input.cap || input.feature.kind !== "fields") return null;
101417
+ if (input.feature.enabled === void 0) return null;
101418
+ const timestamp = input.feature.fields[input.timestampPath];
101419
+ const counter = input.feature.fields[input.counterPath];
101420
+ if (timestamp === void 0 || counter === void 0) return null;
101421
+ if (Object.keys(input.feature.fields).length !== 2) return null;
101422
+ const edges = readEdges(timestamp);
101423
+ if (edges === null) return null;
101424
+ if (readCount(counter, input) === null) return null;
101425
+ const draft = {
101426
+ kind: "pulse",
101427
+ cap: input.cap,
101428
+ targetId: input.targetId,
101429
+ enabled: input.feature.enabled,
101430
+ debounceMs: edges.debounceMs,
101431
+ sources: edges.sources
101432
+ };
101433
+ const decoded = {
101434
+ draft,
101435
+ changedAtFields: edges.changedAtFields
101436
+ };
101437
+ const compiled = compilePulseOverride({
101438
+ draft,
101439
+ device: input.device,
101440
+ changedAtFields: edges.changedAtFields
101441
+ });
101442
+ if (!compiled.ok) return null;
101443
+ return sameFeature$1(compiled.value, input.feature) ? decoded : null;
101444
+ }
101445
+ function readEdges(field) {
101446
+ if (field.kind !== "expression" || !field.expr.startsWith("latest(") || !field.expr.endsWith(")")) return null;
101447
+ const inner = field.expr.slice(7, -1);
101448
+ const found = [...inner.matchAll(new RegExp(ROSE_SOURCE, "g"))];
101449
+ if (found.length === 0 || found.map((match) => match[0]).join(", ") !== inner) return null;
101450
+ if (Object.keys(field.bindings).length !== found.length * 2) return null;
101451
+ const rows = found.map((match, index) => readRose(match, index, field.bindings));
101452
+ if (rows.some((row) => row === null)) return null;
101453
+ const edges = rows.filter((row) => row !== null);
101454
+ const debounceMs = edges[0]?.debounceMs;
101455
+ if (debounceMs === void 0 || edges.some((row) => row.debounceMs !== debounceMs)) return null;
101456
+ return {
101457
+ debounceMs,
101458
+ sources: edges.map((row) => row.source),
101459
+ changedAtFields: edges.map((row) => row.changedAtField)
101460
+ };
101461
+ }
101462
+ function readRose(match, index, bindings) {
101463
+ const activeIndex = Number(match[1]);
101464
+ const changedIndex = Number(match[2]);
101465
+ const debounceMs = Number(match[3]);
101466
+ if (activeIndex !== index || changedIndex !== index || !Number.isFinite(debounceMs)) return null;
101467
+ const active = fromBinding(bindings[`active${index}`]);
101468
+ const changed = fromBinding(bindings[`changed${index}`]);
101469
+ if (active === null || changed === null) return null;
101470
+ if (active.cap !== changed.cap) return null;
101471
+ if (active.source.addonId !== changed.source.addonId) return null;
101472
+ if (active.source.stableId !== changed.source.stableId) return null;
101473
+ return {
101474
+ debounceMs,
101475
+ changedAtField: changed.fieldPath,
101476
+ source: {
101477
+ addonId: active.source.addonId,
101478
+ stableId: active.source.stableId,
101479
+ cap: active.cap,
101480
+ fieldPath: active.fieldPath
101481
+ }
101482
+ };
101483
+ }
101484
+ function fromBinding(value) {
101485
+ if (value === void 0 || value.kind !== "from") return null;
101486
+ return value;
101487
+ }
101488
+ function readCount(field, input) {
101489
+ if (field.kind !== "expression" || field.expr !== `count(${input.timestampPath})`) return null;
101490
+ const binding = fromBinding(field.bindings[input.timestampPath]);
101491
+ if (binding === null) return null;
101492
+ if (Object.keys(field.bindings).length !== 1) return null;
101493
+ if (binding.cap !== input.cap || binding.fieldPath !== input.timestampPath) return null;
101494
+ if (binding.source.addonId !== input.device.addonId) return null;
101495
+ if (binding.source.stableId !== input.device.stableId) return null;
101496
+ return true;
101497
+ }
101498
+ function sameFeature$1(left, right) {
101499
+ return JSON.stringify(left) === JSON.stringify(right);
101500
+ }
101501
+ //#endregion
101502
+ //#region src/composition/customize/event-eval.ts
101503
+ var INITIAL_PULSE = {
101504
+ priorActive: void 0,
101505
+ lastPressedAt: null,
101506
+ count: 0,
101507
+ acceptedAt: null
101508
+ };
101509
+ var INITIAL_SPAN = {
101510
+ open: false,
101511
+ startedAt: null,
101512
+ endedAt: null,
101513
+ quietUntil: null,
101514
+ priorActive: void 0
101515
+ };
101516
+ function stepPulse(state, sample, debounceMs) {
101517
+ if (sample.kind === "unread" || debounceMs < 0) return state;
101518
+ if (state.priorActive === void 0) return {
101519
+ ...state,
101520
+ priorActive: sample.active
101521
+ };
101522
+ if (!sample.active) return {
101523
+ ...state,
101524
+ priorActive: false
101525
+ };
101526
+ if (state.priorActive) return state;
101527
+ const at = finite(sample.changedAt);
101528
+ if (at === null) return {
101529
+ ...state,
101530
+ priorActive: true
101531
+ };
101532
+ if (state.acceptedAt !== null && at - state.acceptedAt < debounceMs) return {
101533
+ ...state,
101534
+ priorActive: true
101535
+ };
101536
+ return {
101537
+ priorActive: true,
101538
+ lastPressedAt: at,
101539
+ count: state.count + 1,
101540
+ acceptedAt: at
101541
+ };
101542
+ }
101543
+ function stepSpan(state, sample, timing) {
101544
+ const timed = expire(state, timing);
101545
+ if (sample.kind === "unread" || timing.timeoutMs < 0 || timing.cooldownMs < 0) return timed;
101546
+ if (timed.priorActive === void 0) return {
101547
+ ...timed,
101548
+ priorActive: sample.active
101549
+ };
101550
+ if (timed.open && timed.priorActive && !sample.active) return finish(timed, finite(sample.changedAt) ?? timing.nowMs, timing.cooldownMs, false);
101551
+ if (!sample.active) return {
101552
+ ...timed,
101553
+ priorActive: false
101554
+ };
101555
+ if (timed.priorActive || timed.open) return {
101556
+ ...timed,
101557
+ priorActive: true
101558
+ };
101559
+ const at = finite(sample.changedAt) ?? timing.nowMs;
101560
+ if (timed.quietUntil !== null && at < timed.quietUntil) return {
101561
+ ...timed,
101562
+ priorActive: true
101563
+ };
101564
+ return {
101565
+ open: true,
101566
+ startedAt: at,
101567
+ endedAt: null,
101568
+ quietUntil: null,
101569
+ priorActive: true
101570
+ };
101571
+ }
101572
+ function expire(state, timing) {
101573
+ if (!state.open || state.startedAt === null) return state;
101574
+ const deadline = state.startedAt + timing.timeoutMs;
101575
+ if (timing.nowMs < deadline) return state;
101576
+ return finish(state, deadline, timing.cooldownMs, state.priorActive === true);
101577
+ }
101578
+ function finish(state, endedAt, cooldownMs, priorActive) {
101579
+ return {
101580
+ open: false,
101581
+ startedAt: state.startedAt,
101582
+ endedAt,
101583
+ quietUntil: cooldownMs > 0 ? endedAt + cooldownMs : null,
101584
+ priorActive
101585
+ };
101586
+ }
101587
+ function finite(value) {
101588
+ return value !== null && Number.isFinite(value) ? value : null;
101589
+ }
101590
+ //#endregion
101591
+ //#region src/composition/customize/event-status.ts
101592
+ function projectPulseAssignment(input) {
101593
+ if (input.stored.kind === "pending") return held(input.targetId, "pending", null);
101594
+ if (input.stored.kind === "unknown") return held(input.targetId, "unknown", input.stored.reason ?? "unknown");
101595
+ if (input.stored.value === null) return native$1(input.targetId);
101596
+ const decoded = decodePulseOverride({
101597
+ feature: input.stored.value,
101598
+ device: input.device,
101599
+ cap: input.cap,
101600
+ targetId: input.targetId,
101601
+ timestampPath: input.timestampPath,
101602
+ counterPath: input.counterPath
101603
+ });
101604
+ if (decoded === null) return {
101605
+ text: "unknown",
101606
+ status: row(input.targetId, true, "assigned", "unavailable", "not-canonical")
101607
+ };
101608
+ if (input.sourcePresence === "pending") return {
101609
+ text: "pending",
101610
+ status: row(input.targetId, true, "assigned", "stale", null)
101611
+ };
101612
+ if (input.sourcePresence === "unknown") return {
101613
+ text: "unknown",
101614
+ status: row(input.targetId, true, "assigned", "stale", input.sourceUnknownReason ?? "unknown")
101615
+ };
101616
+ if (input.sourcePresence === "absent" && input.apply === "replace") return {
101617
+ text: "Native · source missing",
101618
+ status: row(input.targetId, true, "native", "released", "source-missing")
101619
+ };
101620
+ if (input.sourcePresence === "absent") return {
101621
+ text: "Assigned · source missing",
101622
+ status: row(input.targetId, true, "assigned", "unavailable", "source-missing")
101623
+ };
101624
+ const source = decoded.draft.sources[0];
101625
+ return {
101626
+ text: `Assigned · ${source === void 0 ? "assigned" : `${source.cap}.${source.fieldPath}`}`,
101627
+ status: row(input.targetId, true, "assigned", "active", null)
101628
+ };
101629
+ }
101630
+ function projectSpanAssignment(targetId, stored) {
101631
+ if (stored.kind === "pending") return {
101632
+ text: "pending",
101633
+ writable: false,
101634
+ rows: [],
101635
+ status: row(targetId, false, "native", "unavailable", null)
101636
+ };
101637
+ if (stored.kind === "unknown") return {
101638
+ text: "unknown",
101639
+ writable: false,
101640
+ rows: [],
101641
+ status: row(targetId, false, "native", "stale", stored.reason ?? "unknown")
101642
+ };
101643
+ if (stored.value.state === "unreadable") return {
101644
+ text: "unreadable",
101645
+ writable: false,
101646
+ rows: [],
101647
+ status: row(targetId, false, "assigned", "unavailable", stored.value.reason)
101648
+ };
101649
+ if (stored.value.state === "not-configured") return {
101650
+ text: "Native",
101651
+ writable: true,
101652
+ rows: [],
101653
+ status: row(targetId, false, "native", "active", null)
101654
+ };
101655
+ const rows = stored.value.sensors.map(sensorRow);
101656
+ const reason = spanReason(stored.value);
101657
+ return {
101658
+ text: rows.length === 0 ? "Native" : "Assigned",
101659
+ writable: true,
101660
+ rows,
101661
+ status: row(targetId, rows.length > 0, "assigned", "active", reason)
101662
+ };
101663
+ }
101664
+ function spanReason(status) {
101665
+ if (status.notForwarded !== void 0) return status.notForwarded;
101666
+ if (status.notEnforced !== void 0) return status.notEnforced;
101667
+ return status.sensors.map(attachmentReason).find((reason) => reason !== null) ?? null;
101668
+ }
101669
+ function sensorRow(sensor) {
101670
+ return {
101671
+ key: `${sensor.addonId}/${sensor.stableId}`,
101672
+ addonId: sensor.addonId,
101673
+ stableId: sensor.stableId,
101674
+ name: sensor.state === "missing" || sensor.state === "unreadable" ? sensor.stableId : sensor.name,
101675
+ mode: sensor.mode,
101676
+ state: sensor.state,
101677
+ reason: attachmentReason(sensor)
101678
+ };
101679
+ }
101680
+ function attachmentReason(sensor) {
101681
+ switch (sensor.state) {
101682
+ case "ready": return null;
101683
+ case "offline":
101684
+ case "missing":
101685
+ case "unsupported":
101686
+ case "unreadable": return sensor.reason;
101687
+ }
101688
+ }
101689
+ function native$1(targetId) {
101690
+ return {
101691
+ text: "Native",
101692
+ status: row(targetId, false, "native", "active", null)
101693
+ };
101694
+ }
101695
+ function held(targetId, text, reason) {
101696
+ return {
101697
+ text,
101698
+ status: row(targetId, false, "native", text === "pending" ? "unavailable" : "stale", reason)
101699
+ };
101700
+ }
101701
+ function row(targetId, configured, source, state, reason) {
101702
+ return {
101703
+ targetId,
101704
+ configured,
101705
+ source,
101706
+ state,
101707
+ reason
101708
+ };
101709
+ }
101710
+ //#endregion
101711
+ //#region src/composition/customize/keyed-array-expr.ts
101712
+ var STEP = /^x == ("(?:\\.|[^"\\])*") \? ("(?:\\.|[^"\\])*"|null) : ([\s\S]+)$/;
101713
+ function enumMapExpr(mapping) {
101714
+ const keys = Object.keys(mapping).toSorted();
101715
+ if (keys.length === 0) return null;
101716
+ return keys.reduceRight((rest, key) => {
101717
+ if (rest === null) return null;
101718
+ const value = mapping[key];
101719
+ if (value === void 0) return null;
101720
+ const rhs = value === null ? "null" : JSON.stringify(value);
101721
+ return `x == ${JSON.stringify(key)} ? ${rhs} : ${rest}`;
101722
+ }, "null");
101723
+ }
101724
+ function parseEnumMap(expr) {
101725
+ const mapping = {};
101726
+ let rest = expr;
101727
+ while (rest !== "null") {
101728
+ const match = STEP.exec(rest);
101729
+ if (match === null || match[1] === void 0 || match[2] === void 0 || match[3] === void 0) return null;
101730
+ const key = jsonString(match[1]);
101731
+ const value = match[2] === "null" ? null : jsonString(match[2]);
101732
+ if (key === null || match[2] !== "null" && value === null) return null;
101733
+ if (Object.prototype.hasOwnProperty.call(mapping, key)) return null;
101734
+ mapping[key] = value;
101735
+ rest = match[3];
101736
+ }
101737
+ return Object.keys(mapping).length === 0 ? null : mapping;
101738
+ }
101739
+ function enumMapDraft(mapping) {
101740
+ return {
101741
+ kind: "enum-map",
101742
+ mapping: { ...mapping }
101743
+ };
101744
+ }
101745
+ function jsonString(token) {
101746
+ try {
101747
+ const parsed = JSON.parse(token);
101748
+ return typeof parsed === "string" ? parsed : null;
101749
+ } catch {
101750
+ return null;
101751
+ }
101752
+ }
101753
+ //#endregion
101754
+ //#region src/composition/customize/keyed-array-compile.ts
101755
+ var catalog$1 = CAP_CUSTOMIZE_CATALOG;
101756
+ function compileKeyedArrayOverride(input) {
101757
+ const target = catalogTarget$1(input.cap, input.targetId);
101758
+ if (target === null || target.cardinality !== "keyedArray") return fail("target");
101759
+ if (input.items.length === 0) return fail("items");
101760
+ if (input.items.some((item) => item.draft.cap !== input.cap || item.draft.targetId !== input.targetId)) return fail("target");
101761
+ const keys = input.items.map((item) => item.draft.key);
101762
+ if (new Set(keys).size !== keys.length) return fail("key");
101763
+ const built = input.items.reduce((acc, item) => {
101764
+ if (!acc.ok) return acc;
101765
+ const entry = compileItem(target, item);
101766
+ if (!entry.ok) return entry;
101767
+ return {
101768
+ ok: true,
101769
+ value: {
101770
+ ...acc.value,
101771
+ [item.draft.key]: entry.value
101772
+ }
101773
+ };
101774
+ }, {
101775
+ ok: true,
101776
+ value: {}
101777
+ });
101778
+ if (!built.ok) return built;
101779
+ const fields = compileCompanions(target, input.apply);
101780
+ if (!fields.ok) return fields;
101781
+ return {
101782
+ ok: true,
101783
+ value: {
101784
+ kind: "fields",
101785
+ cap: input.cap,
101786
+ fields: fields.value,
101787
+ items: { ...built.value }
101788
+ }
101789
+ };
101790
+ }
101791
+ /** Put one compiled item into an existing feature. Other items stay as stored. */
101792
+ function mergeKeyedArrayItem(current, compiled, key) {
101793
+ const item = compiled.items?.[key];
101794
+ if (item === void 0) return null;
101795
+ return {
101796
+ kind: "fields",
101797
+ cap: compiled.cap,
101798
+ ...current?.enabled === void 0 ? {} : { enabled: current.enabled },
101799
+ fields: current === null ? compiled.fields : current.fields,
101800
+ items: {
101801
+ ...current?.items,
101802
+ [key]: item
101803
+ }
101804
+ };
101805
+ }
101806
+ /** The feature without item `key`. Null when that item was the last one. */
101807
+ function withoutKeyedArrayItem(feature, key) {
101808
+ const rest = Object.fromEntries(Object.entries(feature.items ?? {}).filter(([name]) => name !== key));
101809
+ if (Object.keys(rest).length === 0) return null;
101810
+ return {
101811
+ ...feature,
101812
+ items: rest
101813
+ };
101814
+ }
101815
+ function fail(reason) {
101816
+ return {
101817
+ ok: false,
101818
+ reason
101819
+ };
101820
+ }
101821
+ function catalogTarget$1(cap, targetId) {
101822
+ const entry = catalog$1[cap];
101823
+ if (entry === void 0) return null;
101824
+ return entry.targets.find((target) => target.id === targetId) ?? null;
101825
+ }
101826
+ function compileItem(target, item) {
101827
+ if (!require_composition.COMPOSITION_ITEM_KEY_RE.test(item.draft.key) || item.draft.label.trim() === "") return fail("key");
101828
+ const field = (target.fields ?? []).find((entry) => entry.path === item.draft.fieldPath);
101829
+ if (field === void 0) return fail("field");
101830
+ const source = sourceSpec(field, item);
101831
+ if (source === null) return fail("source-kind");
101832
+ if (source.requiredUnit !== void 0 && item.sourceUnit !== source.requiredUnit) return fail("unit");
101833
+ const allowed = item.draft.transform.kind;
101834
+ if (!source.transformKinds.includes(allowed)) return fail("transform");
101835
+ const value = compileValue(item, field);
101836
+ if (!value.ok) return value;
101837
+ const fields = compileLiterals(target, field, item, value.value);
101838
+ if (!fields.ok) return fields;
101839
+ return {
101840
+ ok: true,
101841
+ value: {
101842
+ label: item.draft.label.trim(),
101843
+ fields: fields.value
101844
+ }
101845
+ };
101846
+ }
101847
+ function sourceSpec(field, item) {
101848
+ if (item.sourceValueKind === "number") return field.sources.find((source) => source.kind === "numeric") ?? null;
101849
+ if (item.sourceValueKind === "enum" || item.sourceValueKind === "string") return field.sources.find((source) => source.kind === "enum") ?? null;
101850
+ return null;
101851
+ }
101852
+ function compileValue(item, field) {
101853
+ const read = fromRead(refOf(item.draft), item.draft.source.cap, item.draft.source.fieldPath);
101854
+ const transform = item.draft.transform;
101855
+ if (transform.kind === "identity") return {
101856
+ ok: true,
101857
+ value: read
101858
+ };
101859
+ if (transform.kind === "linear") return linearValue(transform, read);
101860
+ if (transform.kind === "enum-map") return enumValue(transform, read, field);
101861
+ return fail("transform");
101862
+ }
101863
+ function linearValue(transform, read) {
101864
+ const scale = signedToken(transform.scale);
101865
+ const offset = signedToken(transform.offset);
101866
+ if (scale === null || offset === null) return fail("scale");
101867
+ const clamps = clampTokens(transform.clampMin, transform.clampMax);
101868
+ if (!clamps.ok) return clamps;
101869
+ return {
101870
+ ok: true,
101871
+ value: expression(linearExpr("x", scale, offset, clamps.value.min, clamps.value.max), { x: read })
101872
+ };
101873
+ }
101874
+ function clampTokens(clampMin, clampMax) {
101875
+ if (clampMin === void 0 && clampMax === void 0) return {
101876
+ ok: true,
101877
+ value: {
101878
+ min: null,
101879
+ max: null
101880
+ }
101881
+ };
101882
+ if (clampMin === void 0 || clampMax === void 0) return fail("clamp");
101883
+ const min = signedToken(clampMin);
101884
+ const max = signedToken(clampMax);
101885
+ if (min === null || max === null || clampMin > clampMax) return fail("clamp");
101886
+ return {
101887
+ ok: true,
101888
+ value: {
101889
+ min,
101890
+ max
101891
+ }
101892
+ };
101893
+ }
101894
+ function enumValue(transform, read, field) {
101895
+ const allowed = new Set(field.enumValues ?? []);
101896
+ const entries = Object.entries(transform.mapping);
101897
+ if (entries.length === 0) return fail("enum");
101898
+ if (entries.some(([key, value]) => key.length === 0 || value !== null && !allowed.has(value))) return fail("enum");
101899
+ const expr = enumMapExpr(transform.mapping);
101900
+ if (expr === null) return fail("enum");
101901
+ return {
101902
+ ok: true,
101903
+ value: expression(expr, { x: read })
101904
+ };
101905
+ }
101906
+ function compileLiterals(target, field, item, value) {
101907
+ const required = new Set((field.literals ?? []).map((literalSpec) => literalSpec.path));
101908
+ const traits = new Map((target.traits ?? []).map((trait) => [trait.path, new Set(trait.values)]));
101909
+ if ([...required].filter((path) => !item.literals.some((literalSpec) => literalSpec.path === path)).length > 0) return fail("literal");
101910
+ return item.literals.reduce((acc, entry) => {
101911
+ if (!acc.ok) return acc;
101912
+ if (entry.path === field.path || acc.value[entry.path] !== void 0) return fail("literal");
101913
+ const allowed = allowedLiteral(field, traits, entry.path);
101914
+ if (allowed === null || !allowed.has(entry.value)) return fail("literal");
101915
+ const expr = constantExpr(entry.value);
101916
+ if (expr === null) return fail("literal");
101917
+ return {
101918
+ ok: true,
101919
+ value: {
101920
+ ...acc.value,
101921
+ [entry.path]: literal(expr)
101922
+ }
101923
+ };
101924
+ }, {
101925
+ ok: true,
101926
+ value: { [field.path]: value }
101927
+ });
101928
+ }
101929
+ function allowedLiteral(field, traits, path) {
101930
+ const trait = traits.get(path);
101931
+ if (trait !== void 0) return trait;
101932
+ const required = (field.literals ?? []).find((entry) => entry.path === path);
101933
+ return required === void 0 ? null : new Set(required.values);
101934
+ }
101935
+ function compileCompanions(target, apply) {
101936
+ return (target.companions ?? []).filter((companion) => companionIncluded(companion, apply)).reduce((acc, companion) => {
101937
+ if (!acc.ok) return acc;
101938
+ if (companion.kind !== "clock") return fail("companion");
101939
+ return {
101940
+ ok: true,
101941
+ value: {
101942
+ ...acc.value,
101943
+ [companion.path]: literal("now")
101944
+ }
101945
+ };
101946
+ }, {
101947
+ ok: true,
101948
+ value: {}
101949
+ });
101950
+ }
101951
+ function companionIncluded(companion, apply) {
101952
+ if (companion.kind === "transition-time") return false;
101953
+ return companion.when === "always" || apply === "add";
101954
+ }
101955
+ function refOf(draft) {
101956
+ return {
101957
+ addonId: draft.source.addonId,
101958
+ stableId: draft.source.stableId
101959
+ };
101960
+ }
101961
+ //#endregion
101962
+ //#region src/composition/customize/keyed-array-decode.ts
101963
+ var catalog = CAP_CUSTOMIZE_CATALOG;
101964
+ function readKeyedArrayItems(feature, cap, targetId) {
101965
+ const target = catalogTarget(cap, targetId);
101966
+ if (target === null || target.cardinality !== "keyedArray" || feature.cap !== cap) return null;
101967
+ const apply = inferApply(target, feature.fields);
101968
+ const canonical = apply !== null && companionsMatch(target, feature.fields, apply);
101969
+ const items = Object.entries(feature.items ?? {}).map(([key, item]) => viewOf(target, cap, targetId, key, item, canonical));
101970
+ return {
101971
+ canonical,
101972
+ apply: apply ?? "add",
101973
+ items
101974
+ };
101975
+ }
101976
+ function decodeKeyedArrayOverride(input) {
101977
+ const read = readKeyedArrayItems(input.feature, input.cap, input.targetId);
101978
+ if (read === null || !read.canonical) return null;
101979
+ if (read.items.some((item) => item.compile === null)) return null;
101980
+ const compiles = read.items.flatMap((item) => item.compile === null ? [] : [item.compile]);
101981
+ const compiled = compileKeyedArrayOverride({
101982
+ cap: input.cap,
101983
+ targetId: input.targetId,
101984
+ device: input.device,
101985
+ apply: read.apply,
101986
+ items: compiles
101987
+ });
101988
+ if (!compiled.ok) return null;
101989
+ return sameFeature(compiled.value, input.feature) ? read : null;
101990
+ }
101991
+ function viewOf(target, cap, targetId, key, item, canonical) {
101992
+ const blank = {
101993
+ key,
101994
+ label: item.label,
101995
+ recognized: false,
101996
+ compile: null
101997
+ };
101998
+ if (!canonical) return blank;
101999
+ const compile = compileOf(target, cap, targetId, key, item);
102000
+ if (compile === null) return blank;
102001
+ return {
102002
+ key,
102003
+ label: item.label,
102004
+ recognized: true,
102005
+ compile
102006
+ };
102007
+ }
102008
+ function compileOf(target, cap, targetId, key, item) {
102009
+ const assigned = assignedField(target, item);
102010
+ if (assigned === null) return null;
102011
+ const literals = literalFields(target, assigned.field, item, assigned.path);
102012
+ if (literals === null) return null;
102013
+ const draft = draftOf(cap, targetId, key, item.label, assigned);
102014
+ if (draft === null) return null;
102015
+ const compile = {
102016
+ draft,
102017
+ sourceValueKind: assigned.sourceValueKind,
102018
+ sourceUnit: null,
102019
+ literals
102020
+ };
102021
+ return reproduces(cap, targetId, key, compile, item) ? compile : null;
102022
+ }
102023
+ function assignedField(target, item) {
102024
+ const paths = Object.keys(item.fields).filter((path) => !isConstant(item.fields[path]));
102025
+ if (paths.length !== 1) return null;
102026
+ const path = paths[0];
102027
+ if (path === void 0) return null;
102028
+ const field = (target.fields ?? []).find((entry) => entry.path === path);
102029
+ const source = item.fields[path];
102030
+ if (field === void 0 || source === void 0) return null;
102031
+ const read = readTransform(source, field);
102032
+ if (read === null) return null;
102033
+ return {
102034
+ path,
102035
+ field,
102036
+ source,
102037
+ ...read
102038
+ };
102039
+ }
102040
+ function readTransform(source, field) {
102041
+ if (source.kind === "from") return field.valueKind === "enum" ? {
102042
+ sourceValueKind: "enum",
102043
+ transform: { kind: "identity" }
102044
+ } : {
102045
+ sourceValueKind: "number",
102046
+ transform: { kind: "identity" }
102047
+ };
102048
+ if (source.kind !== "expression") return null;
102049
+ const mapped = parseEnumMap(source.expr);
102050
+ if (mapped !== null && field.valueKind === "enum") return {
102051
+ sourceValueKind: "enum",
102052
+ transform: enumMapDraft(mapped)
102053
+ };
102054
+ const linear = parseLinear(source.expr);
102055
+ if (linear === null || linear.inner !== "x" || field.valueKind !== "number") return null;
102056
+ return {
102057
+ sourceValueKind: "number",
102058
+ transform: {
102059
+ kind: "linear",
102060
+ scale: linear.scale,
102061
+ offset: linear.offset,
102062
+ ...linear.clampMin === void 0 || linear.clampMax === void 0 ? {} : {
102063
+ clampMin: linear.clampMin,
102064
+ clampMax: linear.clampMax
102065
+ }
102066
+ }
102067
+ };
102068
+ }
102069
+ function literalFields(target, field, item, assignedPath) {
102070
+ const required = new Set((field.literals ?? []).map((entry) => entry.path));
102071
+ const traits = new Set((target.traits ?? []).map((trait) => trait.path));
102072
+ const found = Object.keys(item.fields).filter((path) => path !== assignedPath);
102073
+ if (found.some((path) => !required.has(path) && !traits.has(path))) return null;
102074
+ if ([...required].some((path) => !found.includes(path))) return null;
102075
+ const literals = found.map((path) => literalValue(item.fields[path], path));
102076
+ if (literals.some((entry) => entry === null)) return null;
102077
+ return literals.flatMap((entry) => entry === null ? [] : [entry]);
102078
+ }
102079
+ function literalValue(source, path) {
102080
+ if (source?.kind !== "expression" || Object.keys(source.bindings).length !== 0) return null;
102081
+ if (!(source.expr.startsWith("\"") && source.expr.endsWith("\""))) return null;
102082
+ try {
102083
+ const parsed = JSON.parse(source.expr);
102084
+ return typeof parsed === "string" ? {
102085
+ path,
102086
+ value: parsed
102087
+ } : null;
102088
+ } catch {
102089
+ return null;
102090
+ }
102091
+ }
102092
+ function draftOf(cap, targetId, key, label, assigned) {
102093
+ const bound = singleFrom(assigned.source);
102094
+ if (bound === null) return null;
102095
+ return {
102096
+ kind: "keyedArray",
102097
+ cap,
102098
+ targetId,
102099
+ key,
102100
+ label,
102101
+ fieldPath: assigned.path,
102102
+ source: {
102103
+ addonId: bound.addonId,
102104
+ stableId: bound.stableId,
102105
+ cap: bound.cap,
102106
+ fieldPath: bound.fieldPath
102107
+ },
102108
+ transform: assigned.transform
102109
+ };
102110
+ }
102111
+ function reproduces(cap, targetId, key, compile, item) {
102112
+ const compiled = compileKeyedArrayOverride({
102113
+ cap,
102114
+ targetId,
102115
+ device: {
102116
+ addonId: "t",
102117
+ stableId: "t"
102118
+ },
102119
+ apply: "add",
102120
+ items: [compile]
102121
+ });
102122
+ if (!compiled.ok) return false;
102123
+ const written = compiled.value.items?.[key];
102124
+ if (written === void 0) return false;
102125
+ return JSON.stringify(written) === JSON.stringify(item);
102126
+ }
102127
+ function isConstant(source) {
102128
+ return source?.kind === "expression" && Object.keys(source.bindings).length === 0;
102129
+ }
102130
+ function inferApply(target, fields) {
102131
+ const addPaths = (target.companions ?? []).flatMap((companion) => companion.kind !== "transition-time" && companion.when === "add" ? [companion.path] : []);
102132
+ if (addPaths.length === 0) return "replace";
102133
+ const present = addPaths.filter((path) => fields[path] !== void 0);
102134
+ if (present.length === addPaths.length) return "add";
102135
+ if (present.length === 0) return "replace";
102136
+ return null;
102137
+ }
102138
+ function companionsMatch(target, fields, apply) {
102139
+ const expected = (target.companions ?? []).flatMap((companion) => {
102140
+ if (companion.kind === "transition-time") return [];
102141
+ if (companion.when === "always" || apply === "add") return [companion.path];
102142
+ return [];
102143
+ });
102144
+ if (Object.keys(fields).toSorted().join(",") !== [...expected].toSorted().join(",")) return false;
102145
+ return expected.every((path) => isLiteral(fields[path], "now"));
102146
+ }
102147
+ function catalogTarget(cap, targetId) {
102148
+ const entry = catalog[cap];
102149
+ if (entry === void 0) return null;
102150
+ return entry.targets.find((target) => target.id === targetId) ?? null;
102151
+ }
102152
+ function sameFeature(compiled, stored) {
102153
+ return sameStored(wrap(stripEnabled(compiled)), wrap(stripEnabled(stored)));
102154
+ }
102155
+ function stripEnabled(feature) {
102156
+ const { enabled: _enabled, ...rest } = feature;
102157
+ return rest;
102158
+ }
102159
+ function wrap(feature) {
102160
+ return {
102161
+ family: "composition",
102162
+ target: {
102163
+ addonId: "t",
102164
+ stableId: "t"
102165
+ },
102166
+ feature
102167
+ };
102168
+ }
102169
+ //#endregion
102170
+ //#region src/composition/customize/keyed-array-status.ts
102171
+ function projectKeyedArrayAssignment(input) {
102172
+ if (input.stored.kind === "pending") return unanswered(input.targetId, "pending", null);
102173
+ if (input.stored.kind === "unknown") return unanswered(input.targetId, "unknown", input.stored.reason ?? "unknown");
102174
+ if (input.stored.value === null) return native(input.targetId);
102175
+ const read = readKeyedArrayItems(input.stored.value, input.cap, input.targetId);
102176
+ const count = read?.items.filter((item) => item.recognized).length ?? 0;
102177
+ if (read === null || !read.canonical || count === 0) return {
102178
+ text: "unknown",
102179
+ assignedCount: 0,
102180
+ status: status(input.targetId, true, "assigned", "unavailable", "not-canonical")
102181
+ };
102182
+ return projectDecoded(input, count);
102183
+ }
102184
+ function projectDecoded(input, count) {
102185
+ const text = `${count} assigned`;
102186
+ if (input.sourcePresence === "pending") return {
102187
+ text: "pending",
102188
+ assignedCount: count,
102189
+ status: status(input.targetId, true, "assigned", "stale", null)
102190
+ };
102191
+ if (input.sourcePresence === "unknown") return {
102192
+ text: "unknown",
102193
+ assignedCount: count,
102194
+ status: status(input.targetId, true, "assigned", "stale", input.sourceUnknownReason ?? "unknown")
102195
+ };
102196
+ if (input.sourcePresence === "absent") return {
102197
+ text,
102198
+ assignedCount: count,
102199
+ status: status(input.targetId, true, "assigned", "unavailable", "source-missing")
102200
+ };
102201
+ return {
102202
+ text,
102203
+ assignedCount: count,
102204
+ status: status(input.targetId, true, "assigned", "active", null)
102205
+ };
102206
+ }
102207
+ function native(targetId) {
102208
+ return {
102209
+ text: "Native",
102210
+ assignedCount: 0,
102211
+ status: status(targetId, false, "native", "active", null)
102212
+ };
102213
+ }
102214
+ function unanswered(targetId, text, reason) {
102215
+ return {
102216
+ text,
102217
+ assignedCount: 0,
102218
+ status: status(targetId, false, "native", text === "pending" ? "unavailable" : "stale", reason)
102219
+ };
102220
+ }
102221
+ function status(targetId, configured, source, state, reason) {
102222
+ return {
102223
+ targetId,
102224
+ configured,
102225
+ source,
102226
+ state,
102227
+ reason
102228
+ };
102229
+ }
102230
+ //#endregion
100294
102231
  //#region src/generated/scope-presets.ts
100295
102232
  var SCOPE_PRESETS = [
100296
102233
  {
@@ -111026,6 +112963,7 @@ exports.CAMSTACK_EVENT_PREFIX = CAMSTACK_EVENT_PREFIX;
111026
112963
  exports.CAM_PROFILE_ORDER = require_sleep.CAM_PROFILE_ORDER;
111027
112964
  exports.CAPABILITY_NAMES = CAPABILITY_NAMES;
111028
112965
  exports.CAPABILITY_ROUTER_KEYS = CAPABILITY_ROUTER_KEYS;
112966
+ exports.CAP_CUSTOMIZE_CATALOG = CAP_CUSTOMIZE_CATALOG;
111029
112967
  exports.CAP_NAMES_WITH_STATUS = CAP_NAMES_WITH_STATUS;
111030
112968
  exports.CAP_NODE_PIN_CONTEXT_KEY = require_sleep.CAP_NODE_PIN_CONTEXT_KEY;
111031
112969
  exports.CAP_PROVIDER_KIND_MAP = CAP_PROVIDER_KIND_MAP;
@@ -111254,6 +113192,7 @@ exports.CredentialScopeSchema = CredentialScopeSchema;
111254
113192
  exports.CredentialTokenClaimsSchema = CredentialTokenClaimsSchema;
111255
113193
  exports.CustomActionInputSchema = CustomActionInputSchema;
111256
113194
  exports.CustomModelDescriptorSchema = CustomModelDescriptorSchema;
113195
+ exports.CustomizeAssignmentStatusSchema = CustomizeAssignmentStatusSchema;
111257
113196
  exports.DATABASE_SNAPSHOT_PARTIAL_SUFFIX = DATABASE_SNAPSHOT_PARTIAL_SUFFIX;
111258
113197
  exports.DATABASE_SNAPSHOT_SUFFIX = DATABASE_SNAPSHOT_SUFFIX;
111259
113198
  exports.DATABASE_SNAPSHOT_TIMEOUT_MS = DATABASE_SNAPSHOT_TIMEOUT_MS;
@@ -111595,6 +113534,8 @@ exports.ICON_ENTRIES = ICON_ENTRIES;
111595
113534
  exports.ICON_KEYS = ICON_KEYS;
111596
113535
  exports.ICON_VIEWER_MODULES = ICON_VIEWER_MODULES;
111597
113536
  exports.INFERENCE_DEVICE_EXCLUSION_REASONS = INFERENCE_DEVICE_EXCLUSION_REASONS;
113537
+ exports.INITIAL_PULSE = INITIAL_PULSE;
113538
+ exports.INITIAL_SPAN = INITIAL_SPAN;
111598
113539
  exports.INVERTIBLE_CAPS = INVERTIBLE_CAPS;
111599
113540
  exports.IconKeySchema = IconKeySchema;
111600
113541
  exports.ImageContractSchema = ImageContractSchema;
@@ -111776,6 +113717,7 @@ exports.MOTION_CROP_MAX_REGIONS = MOTION_CROP_MAX_REGIONS;
111776
113717
  exports.MOTION_CROP_MOTION_PAD = MOTION_CROP_MOTION_PAD;
111777
113718
  exports.MOTION_CROP_TRACKED_PAD = MOTION_CROP_TRACKED_PAD;
111778
113719
  exports.MOTION_SOURCE_LABELS = MOTION_SOURCE_LABELS;
113720
+ exports.MOTION_SPAN_TIMEOUT_MS = MOTION_SPAN_TIMEOUT_MS;
111779
113721
  exports.MOTION_TRIGGER_FEATURE = MOTION_TRIGGER_FEATURE;
111780
113722
  exports.ManagedModelCatalogEntrySchema = ManagedModelCatalogEntrySchema;
111781
113723
  exports.ManagedModelExtraFileSchema = ManagedModelExtraFileSchema;
@@ -112151,6 +114093,7 @@ exports.OsdTemplateBindingSchema = OsdTemplateBindingSchema;
112151
114093
  exports.OsdTemplateFormatSchema = OsdTemplateFormatSchema;
112152
114094
  exports.OsdTemplatePickSchema = OsdTemplatePickSchema;
112153
114095
  exports.OsdTemplateTokenKindSchema = OsdTemplateTokenKindSchema;
114096
+ exports.OverrideDraftSchema = OverrideDraftSchema;
112154
114097
  exports.PEER_BYTES_CHUNK_BYTES = require_sleep.PEER_BYTES_CHUNK_BYTES;
112155
114098
  exports.PEER_BYTES_MAX_OUTSTANDING = require_sleep.PEER_BYTES_MAX_OUTSTANDING;
112156
114099
  exports.PEER_BYTES_STALL_MS = require_sleep.PEER_BYTES_STALL_MS;
@@ -112314,6 +114257,7 @@ exports.ROOT_SUPPLIABLE_STEP_IDS = ROOT_SUPPLIABLE_STEP_IDS;
112314
114257
  exports.RUNNING_POWER_START_DELAY_MS = RUNNING_POWER_START_DELAY_MS;
112315
114258
  exports.RUNNING_POWER_STOP_DELAY_MS = RUNNING_POWER_STOP_DELAY_MS;
112316
114259
  exports.RUNNING_POWER_THRESHOLD_WATTS = RUNNING_POWER_THRESHOLD_WATTS;
114260
+ exports.RUNNING_STATE_COLD = RUNNING_STATE_COLD;
112317
114261
  exports.RUNTIME_DEFAULTS = require_sleep.RUNTIME_DEFAULTS;
112318
114262
  exports.RUNTIME_STATE_POLICY = RUNTIME_STATE_POLICY;
112319
114263
  exports.RUNTIME_STATE_REFRESH_MISS_COOLDOWN_MS = RUNTIME_STATE_REFRESH_MISS_COOLDOWN_MS;
@@ -112906,7 +114850,6 @@ exports.buildCapabilityLookup = buildCapabilityLookup;
112906
114850
  exports.buildDeviceAdminUrl = buildDeviceAdminUrl;
112907
114851
  exports.buildDeviceEnrollmentLink = buildDeviceEnrollmentLink;
112908
114852
  exports.buildDeviceSignatureMessage = buildDeviceSignatureMessage;
112909
- exports.buildDoorbellCompositionFeature = buildDoorbellCompositionFeature;
112910
114853
  exports.buildEventKindDescriptor = buildEventKindDescriptor;
112911
114854
  exports.buildFfmpegArgs = require_composition.buildFfmpegArgs;
112912
114855
  exports.buildFrigateClassMap = buildFrigateClassMap;
@@ -112980,6 +114923,10 @@ exports.commitWatchdogRestart = commitWatchdogRestart;
112980
114923
  exports.compareToTarget = compareToTarget;
112981
114924
  exports.compileExpression = require_composition.compileExpression;
112982
114925
  exports.compileExpressionSafe = require_composition.compileExpressionSafe;
114926
+ exports.compileKeyedArrayOverride = compileKeyedArrayOverride;
114927
+ exports.compilePulseOverride = compilePulseOverride;
114928
+ exports.compileScalarOverride = compileScalarOverride;
114929
+ exports.compileSpanOverride = compileSpanOverride;
112983
114930
  exports.composeSwitchedOff = composeSwitchedOff;
112984
114931
  exports.composedDeviceRef = require_composition.composedDeviceRef;
112985
114932
  exports.composedDeviceStableId = require_composition.composedDeviceStableId;
@@ -113044,6 +114991,9 @@ exports.dayOfWeekToIndex = dayOfWeekToIndex;
113044
114991
  exports.declarationOwnerNodeId = declarationOwnerNodeId;
113045
114992
  exports.declarationsOf = require_sleep.declarationsOf;
113046
114993
  exports.declareLogChannel = declareLogChannel;
114994
+ exports.decodeKeyedArrayOverride = decodeKeyedArrayOverride;
114995
+ exports.decodePulseOverride = decodePulseOverride;
114996
+ exports.decodeScalarOverride = decodeScalarOverride;
113047
114997
  exports.decodeVectorBase64 = decodeVectorBase64;
113048
114998
  exports.decoderCapability = decoderCapability;
113049
114999
  exports.defaultDeliveryForSection = defaultDeliveryForSection;
@@ -113102,7 +115052,6 @@ exports.dishwasherCapability = dishwasherCapability;
113102
115052
  exports.doActionSelectValue = doActionSelectValue;
113103
115053
  exports.dominantClipLabels = dominantClipLabels;
113104
115054
  exports.doorbellCapability = doorbellCapability;
113105
- exports.doorbellFeatureSources = doorbellFeatureSources;
113106
115055
  exports.droppedConditionsForKind = droppedConditionsForKind;
113107
115056
  exports.edgetpuDelegateDevice = edgetpuDelegateDevice;
113108
115057
  exports.effectiveStepSettings = effectiveStepSettings;
@@ -113280,7 +115229,6 @@ exports.isRemovedModelId = isRemovedModelId;
113280
115229
  exports.isRestoredCap = isRestoredCap;
113281
115230
  exports.isRetiredClearKey = isRetiredClearKey;
113282
115231
  exports.isRootSuppliableStepId = isRootSuppliableStepId;
113283
- exports.isRunningAssignmentFeature = isRunningAssignmentFeature;
113284
115232
  exports.isSameAddonId = isSameAddonId;
113285
115233
  exports.isScheduleActive = isScheduleActive;
113286
115234
  exports.isSecretClear = require_sleep.isSecretClear;
@@ -113355,6 +115303,7 @@ exports.measuredDetectorCells = measuredDetectorCells;
113355
115303
  exports.mediaPlayerCapability = mediaPlayerCapability;
113356
115304
  exports.memoizePinnedFfmpeg = memoizePinnedFfmpeg;
113357
115305
  exports.mergeClipLabels = mergeClipLabels;
115306
+ exports.mergeKeyedArrayItem = mergeKeyedArrayItem;
113358
115307
  exports.mergeSecretStatePaths = require_sleep.mergeSecretStatePaths;
113359
115308
  exports.mergeSettingsBags = mergeSettingsBags;
113360
115309
  exports.mergeSourceInfo = mergeSourceInfo;
@@ -113481,6 +115430,10 @@ exports.pressureSensorCapability = pressureSensorCapability;
113481
115430
  exports.principalMayReachAddon = principalMayReachAddon;
113482
115431
  exports.privacyMaskCapability = privacyMaskCapability;
113483
115432
  exports.procedureAuthKey = procedureAuthKey;
115433
+ exports.projectKeyedArrayAssignment = projectKeyedArrayAssignment;
115434
+ exports.projectPulseAssignment = projectPulseAssignment;
115435
+ exports.projectScalarAssignment = projectScalarAssignment;
115436
+ exports.projectSpanAssignment = projectSpanAssignment;
113484
115437
  exports.projectSubjectPath = projectSubjectPath;
113485
115438
  exports.providerMotionEvent = providerMotionEvent;
113486
115439
  exports.ptzAutotrackCapability = ptzAutotrackCapability;
@@ -113496,6 +115449,7 @@ exports.readErrorI18n = require_sleep.readErrorI18n;
113496
115449
  exports.readFaceRescoreIntervalMinutes = readFaceRescoreIntervalMinutes;
113497
115450
  exports.readHubMotionDetected = readHubMotionDetected;
113498
115451
  exports.readHubMotionStatus = readHubMotionStatus;
115452
+ exports.readKeyedArrayItems = readKeyedArrayItems;
113499
115453
  exports.readMotionTriggeredBy = readMotionTriggeredBy;
113500
115454
  exports.readNativeLeaseOverride = readNativeLeaseOverride;
113501
115455
  exports.readNodePin = require_sleep.readNodePin;
@@ -113598,8 +115552,6 @@ exports.ruleSectionOf = ruleSectionOf;
113598
115552
  exports.ruleSeedForSection = ruleSeedForSection;
113599
115553
  exports.ruleTriggerZoneIds = ruleTriggerZoneIds;
113600
115554
  exports.runInferenceStep = runInferenceStep;
113601
- exports.runningAssignmentFeature = runningAssignmentFeature;
113602
- exports.runningAssignmentRecovered = runningAssignmentRecovered;
113603
115555
  exports.runningFromDishwasherOperation = runningFromDishwasherOperation;
113604
115556
  exports.runningFromLawnMower = runningFromLawnMower;
113605
115557
  exports.runningFromMediaPlayer = runningFromMediaPlayer;
@@ -113662,7 +115614,9 @@ exports.startReachabilityPoll = startReachabilityPoll;
113662
115614
  exports.stateLabel = stateLabel;
113663
115615
  exports.stateVocabularyFor = stateVocabularyFor;
113664
115616
  exports.stepModelRunsOnFormat = stepModelRunsOnFormat;
115617
+ exports.stepPulse = stepPulse;
113665
115618
  exports.stepSettingSource = stepSettingSource;
115619
+ exports.stepSpan = stepSpan;
113666
115620
  exports.storageAdminCapability = storageAdminCapability;
113667
115621
  exports.storageCapability = storageCapability;
113668
115622
  exports.storageEvictableCapability = storageEvictableCapability;
@@ -113780,6 +115734,7 @@ exports.withLocationMode = withLocationMode;
113780
115734
  exports.withSystemEventClasses = withSystemEventClasses;
113781
115735
  exports.withoutCallerStamp = require_sleep.withoutCallerStamp;
113782
115736
  exports.withoutField = withoutField;
115737
+ exports.withoutKeyedArrayItem = withoutKeyedArrayItem;
113783
115738
  exports.writeModelBoundSetting = writeModelBoundSetting;
113784
115739
  exports.zodEntriesToConfigUI = zodEntriesToConfigUI;
113785
115740
  exports.zoneAnalyticsCapability = zoneAnalyticsCapability;