@camstack/types 1.2.268 → 1.2.269
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/addon.js +1 -1
- package/dist/addon.mjs +1 -1
- package/dist/capabilities/capability-definition.d.ts +8 -1
- package/dist/capabilities/composer.cap.d.ts +6 -0
- package/dist/capabilities/core-blocks.cap.d.ts +54 -3
- package/dist/capabilities/device-manager.cap.d.ts +4 -4
- package/dist/capabilities/doorbell.cap.d.ts +4 -0
- package/dist/capabilities/media-player.cap.d.ts +4 -4
- package/dist/capabilities/recording-archive.cap.d.ts +3 -3
- package/dist/catalogs/sensor-active-state.d.ts +25 -1
- package/dist/composition/composition-events.d.ts +42 -0
- package/dist/composition/composition-report.d.ts +22 -10
- package/dist/composition/composition.d.ts +8 -0
- package/dist/composition/evaluate-field.d.ts +8 -0
- package/dist/composition/expression-kinds.d.ts +1 -0
- package/dist/composition/index.d.ts +1 -0
- package/dist/composition/plan-result.d.ts +2 -0
- package/dist/{composition-QlOB7QTG.js → composition-BAH3n-zq.js} +412 -14
- package/dist/{composition-CPFIlFfw.mjs → composition-D5BQywfe.mjs} +311 -15
- package/dist/expression/ast.d.ts +2 -0
- package/dist/expression/builtins.d.ts +9 -0
- package/dist/expression/evaluator.d.ts +8 -2
- package/dist/expression/expression-source.d.ts +26 -4
- package/dist/expression/index.d.ts +6 -6
- package/dist/index.d.ts +3 -3
- package/dist/index.js +321 -324
- package/dist/index.mjs +299 -314
- package/dist/interfaces/recording-config.d.ts +3 -3
- package/dist/node.js +1 -1
- package/dist/node.mjs +1 -1
- package/dist/{sleep-BSzia-xT.js → sleep-Bg0IB7gg.js} +4 -15
- package/dist/{sleep-jkRQgtSc.mjs → sleep-E6eD4yQS.mjs} +4 -15
- package/package.json +1 -1
|
@@ -714,6 +714,197 @@ function replaceWithSortedKeys(_key, value) {
|
|
|
714
714
|
return value;
|
|
715
715
|
}
|
|
716
716
|
//#endregion
|
|
717
|
+
//#region src/catalogs/sensor-active-state.ts
|
|
718
|
+
/**
|
|
719
|
+
* LA tabella "quale booleano di questo tipo di device conta come ALTO", e il
|
|
720
|
+
* valutatore puro del suo FRONTE.
|
|
721
|
+
*
|
|
722
|
+
* Viveva dentro il builtin virtual-doorbell
|
|
723
|
+
* (`@camstack/system` — `builtins/doorbell/trigger-engine.ts`) e i suoi
|
|
724
|
+
* predicati erano privati al modulo. Il recorder ne ha bisogno per il trigger
|
|
725
|
+
* `RecordingTriggers.sensorDeviceIds`: copiarla avrebbe creato la SECONDA
|
|
726
|
+
* tabella, che diverge alla prima cap aggiunta e il cui sintomo — "il sensore
|
|
727
|
+
* fa suonare il campanello ma non registra" — è esattamente D62. Quindi si
|
|
728
|
+
* SPOSTA qui e il doorbell la ri-esporta.
|
|
729
|
+
*
|
|
730
|
+
* ⚠ NON è `DEVICE_STATE_READERS` (`catalogs/device-state-vocabulary.ts`), e le
|
|
731
|
+
* due non vanno unificate: quella risponde a "qual è la PAROLA di stato per una
|
|
732
|
+
* regola" (e include `presence`, `cover`, `alarm-panel`), questa a "qual è il
|
|
733
|
+
* booleano il cui FRONTE conta". Vocabolari deliberatamente diversi.
|
|
734
|
+
*/
|
|
735
|
+
/**
|
|
736
|
+
* Known binary / switch source caps → the boolean slice field whose
|
|
737
|
+
* false→true rise counts as ACTIVE. Every entry is "fire on active".
|
|
738
|
+
* Sensors whose "active" reading is not a plain boolean (presence's string
|
|
739
|
+
* state, connectivity's connected flag) are deliberately excluded — a
|
|
740
|
+
* reconnect is not a doorbell press, and it is not a recording either.
|
|
741
|
+
*/
|
|
742
|
+
var SOURCE_CAP_ACTIVE_FIELD = {
|
|
743
|
+
contact: "entryOpen",
|
|
744
|
+
binary: "on",
|
|
745
|
+
switch: "on",
|
|
746
|
+
motion: "detected",
|
|
747
|
+
flood: "flooded",
|
|
748
|
+
gas: "detected",
|
|
749
|
+
smoke: "detected",
|
|
750
|
+
"carbon-monoxide": "detected",
|
|
751
|
+
vibration: "detected",
|
|
752
|
+
tamper: "tampered"
|
|
753
|
+
};
|
|
754
|
+
/**
|
|
755
|
+
* The same caps → the slice field carrying the ms-epoch timestamp of the
|
|
756
|
+
* last transition. Every source cap MUST appear here (guarded by a spec):
|
|
757
|
+
* without a transition timestamp the evaluator cannot tell a genuine rise
|
|
758
|
+
* from a boot-time hydration when the FIRST slice it ever sees is already
|
|
759
|
+
* active, and errs towards silence — swallowing the rise.
|
|
760
|
+
*
|
|
761
|
+
* These timestamps are UPSTREAM ones, not ingest ones: the Home Assistant
|
|
762
|
+
* provider derives them from `state.last_changed`, so they survive our own
|
|
763
|
+
* restarts and correctly read as "hours ago" for a state that has been
|
|
764
|
+
* active for hours. `motion` names its rise timestamp `lastDetectedAt`.
|
|
765
|
+
*/
|
|
766
|
+
var SOURCE_CAP_CHANGED_AT_FIELD = {
|
|
767
|
+
contact: "lastChangedAt",
|
|
768
|
+
binary: "lastChangedAt",
|
|
769
|
+
switch: "lastChangedAt",
|
|
770
|
+
motion: "lastDetectedAt",
|
|
771
|
+
flood: "lastChangedAt",
|
|
772
|
+
gas: "lastChangedAt",
|
|
773
|
+
smoke: "lastChangedAt",
|
|
774
|
+
"carbon-monoxide": "lastChangedAt",
|
|
775
|
+
vibration: "lastChangedAt",
|
|
776
|
+
tamper: "lastChangedAt"
|
|
777
|
+
};
|
|
778
|
+
/** Cap names whose presence in a device's bindings qualify it as a source. */
|
|
779
|
+
var SOURCE_CAPS = Object.keys(SOURCE_CAP_ACTIVE_FIELD);
|
|
780
|
+
/**
|
|
781
|
+
* Device `type` values (from `DeviceType`) that can host a binary/switch
|
|
782
|
+
* source cap. Used by the camera's `device-multiselect` picker as the
|
|
783
|
+
* CLIENT-SIDE filter, alongside `SOURCE_CAPS`.
|
|
784
|
+
*
|
|
785
|
+
* Why types and not caps alone: the shared picker filters
|
|
786
|
+
* `deviceManager.listAll` rows client-side, and those rows carry only the
|
|
787
|
+
* device's advertised `features` — NOT its registered cap list. On the live
|
|
788
|
+
* cluster binary sensors and switches advertise EMPTY features (features
|
|
789
|
+
* mirror only a handful of caps like `motion-trigger`), so a caps-only
|
|
790
|
+
* filter matched against `features` would list nothing (the very bug this
|
|
791
|
+
* replaced, which relied on the now-empty `getAllBindings`). Matching by
|
|
792
|
+
* `type` is the reliable client-side signal; the union with `SOURCE_CAPS`
|
|
793
|
+
* still captures any device that DOES advertise a source-cap feature.
|
|
794
|
+
* `sensor` covers contact/motion/flood/gas/smoke/CO/vibration/tamper,
|
|
795
|
+
* `switch` covers switches, `control` covers generic binary actuators.
|
|
796
|
+
*/
|
|
797
|
+
var SOURCE_DEVICE_TYPES = [
|
|
798
|
+
"sensor",
|
|
799
|
+
"switch",
|
|
800
|
+
"control"
|
|
801
|
+
];
|
|
802
|
+
/** True when a cap is a recognised binary/switch source. */
|
|
803
|
+
function isSourceCap(capName) {
|
|
804
|
+
return Object.prototype.hasOwnProperty.call(SOURCE_CAP_ACTIVE_FIELD, capName);
|
|
805
|
+
}
|
|
806
|
+
/** Extract the "active" boolean a source cap's slice carries, or null when
|
|
807
|
+
* the cap is unknown or the field is missing / non-boolean. */
|
|
808
|
+
function sliceActiveValue(capName, slice) {
|
|
809
|
+
const field = SOURCE_CAP_ACTIVE_FIELD[capName];
|
|
810
|
+
if (field === void 0) return null;
|
|
811
|
+
const raw = slice[field];
|
|
812
|
+
return typeof raw === "boolean" ? raw : null;
|
|
813
|
+
}
|
|
814
|
+
/** Ms-epoch transition timestamp a source cap's slice carries, or null when
|
|
815
|
+
* it is absent, non-numeric or the zero "never observed" sentinel. */
|
|
816
|
+
function sliceChangedAt(capName, slice) {
|
|
817
|
+
const field = SOURCE_CAP_CHANGED_AT_FIELD[capName];
|
|
818
|
+
if (field === void 0) return null;
|
|
819
|
+
const raw = slice[field];
|
|
820
|
+
if (typeof raw !== "number" || !Number.isFinite(raw) || raw <= 0) return null;
|
|
821
|
+
return raw;
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* How recent a source's own transition timestamp must be for a FIRST
|
|
825
|
+
* sighting that is already active to count as a genuine rise rather than a
|
|
826
|
+
* hydration of long-standing state.
|
|
827
|
+
*/
|
|
828
|
+
var DEFAULT_FIRST_SIGHTING_FRESHNESS_MS = 3e4;
|
|
829
|
+
/**
|
|
830
|
+
* The source-independent edge rule. The caller supplies an already extracted
|
|
831
|
+
* boolean level and source timestamp; this function owns first-sighting
|
|
832
|
+
* freshness and false-to-true detection.
|
|
833
|
+
*/
|
|
834
|
+
function evaluateSensorLevelEdge(input) {
|
|
835
|
+
const timestamp = input.changedAt;
|
|
836
|
+
const freshFloor = Math.max(input.startedAtMs, input.nowMs - input.firstSightingFreshnessMs);
|
|
837
|
+
const firstRise = input.priorLevel === void 0 && input.level && timestamp !== null && Number.isFinite(timestamp) && timestamp >= freshFloor;
|
|
838
|
+
const transitionRise = input.priorLevel === false && input.level && timestamp !== null && Number.isFinite(timestamp);
|
|
839
|
+
if ((firstRise || transitionRise) && timestamp !== null) return {
|
|
840
|
+
kind: "rise",
|
|
841
|
+
timestamp
|
|
842
|
+
};
|
|
843
|
+
return {
|
|
844
|
+
kind: "none",
|
|
845
|
+
nextPriorLevel: input.level
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
/**
|
|
849
|
+
* IL fronte. Puro: nessun orologio proprio, nessuna memoria — il chiamante
|
|
850
|
+
* porta `prior`, `nowMs` e il proprio `startedAtMs`.
|
|
851
|
+
*
|
|
852
|
+
* Il caso della PRIMA slice già attiva è trattato esplicitamente e vale come
|
|
853
|
+
* fronte solo se il timestamp UPSTREAM della transizione è posteriore a
|
|
854
|
+
* `startedAtMs` **e** entro `firstSightingFreshnessMs`. Entrambe le metà
|
|
855
|
+
* servono: la sola freschezza scatterebbe su un'idratazione al boot di uno
|
|
856
|
+
* stato flippato pochi secondi prima del riavvio, e il solo "dopo che abbiamo
|
|
857
|
+
* iniziato" scatterebbe, su un processo di lunga vita, per una sorgente
|
|
858
|
+
* adottata oggi il cui stato è cambiato ieri. Il caso ambiguo ERRA VERSO IL
|
|
859
|
+
* SILENZIO e lo dichiara (`baseline-seeded-stale-active`).
|
|
860
|
+
*/
|
|
861
|
+
function evaluateSensorEdge(input) {
|
|
862
|
+
const value = sliceActiveValue(input.capName, input.slice);
|
|
863
|
+
if (value === null) return {
|
|
864
|
+
edge: "none",
|
|
865
|
+
value: null,
|
|
866
|
+
reason: isSourceCap(input.capName) ? "non-boolean-value" : "unknown-cap"
|
|
867
|
+
};
|
|
868
|
+
if (evaluateSensorLevelEdge({
|
|
869
|
+
level: value,
|
|
870
|
+
changedAt: sliceChangedAt(input.capName, input.slice),
|
|
871
|
+
priorLevel: input.prior,
|
|
872
|
+
startedAtMs: input.startedAtMs,
|
|
873
|
+
nowMs: input.nowMs,
|
|
874
|
+
firstSightingFreshnessMs: input.firstSightingFreshnessMs
|
|
875
|
+
}).kind === "rise") return {
|
|
876
|
+
edge: "rising",
|
|
877
|
+
value: true
|
|
878
|
+
};
|
|
879
|
+
if (input.prior === void 0) {
|
|
880
|
+
if (!value) return {
|
|
881
|
+
edge: "none",
|
|
882
|
+
value,
|
|
883
|
+
reason: "baseline-seeded-inactive"
|
|
884
|
+
};
|
|
885
|
+
return {
|
|
886
|
+
edge: "none",
|
|
887
|
+
value,
|
|
888
|
+
reason: "baseline-seeded-stale-active"
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
if (input.prior === value) return {
|
|
892
|
+
edge: "none",
|
|
893
|
+
value,
|
|
894
|
+
reason: "no-change"
|
|
895
|
+
};
|
|
896
|
+
if (!value) return {
|
|
897
|
+
edge: "none",
|
|
898
|
+
value,
|
|
899
|
+
reason: "falling-edge"
|
|
900
|
+
};
|
|
901
|
+
return {
|
|
902
|
+
edge: "none",
|
|
903
|
+
value,
|
|
904
|
+
reason: "missing-timestamp"
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
//#endregion
|
|
717
908
|
//#region src/expression/errors.ts
|
|
718
909
|
/**
|
|
719
910
|
* Error types for the safe expression engine. Two distinct classes so callers
|
|
@@ -740,6 +931,9 @@ var ExpressionEvalError = class extends Error {
|
|
|
740
931
|
};
|
|
741
932
|
//#endregion
|
|
742
933
|
//#region src/expression/builtins.ts
|
|
934
|
+
function isStatefulBuiltin(name) {
|
|
935
|
+
return name === "rose" || name === "count";
|
|
936
|
+
}
|
|
743
937
|
function asFiniteNumber(value, name, index) {
|
|
744
938
|
if (typeof value !== "number" || !Number.isFinite(value)) throw new ExpressionEvalError(`${name}: argument ${index + 1} must be a finite number`);
|
|
745
939
|
return value;
|
|
@@ -775,6 +969,37 @@ function toNumberOrNull(value) {
|
|
|
775
969
|
const parsed = Number(trimmed);
|
|
776
970
|
return Number.isFinite(parsed) ? parsed : null;
|
|
777
971
|
}
|
|
972
|
+
var DEFAULT_ROSE_DEBOUNCE_MS = 2e3;
|
|
973
|
+
function evaluateRose(level, changedAt, debounceMs, call, field, env) {
|
|
974
|
+
const active = asBoolean(level, "rose", 0);
|
|
975
|
+
const timestamp = typeof changedAt === "number" && Number.isFinite(changedAt) ? changedAt : null;
|
|
976
|
+
const debounce = debounceMs === void 0 ? DEFAULT_ROSE_DEBOUNCE_MS : asFiniteNumber(debounceMs, "rose", 2);
|
|
977
|
+
if (debounce < 0) throw new ExpressionEvalError("rose: debounce must be a non-negative finite number");
|
|
978
|
+
const verdict = evaluateSensorLevelEdge({
|
|
979
|
+
level: active,
|
|
980
|
+
changedAt: timestamp,
|
|
981
|
+
priorLevel: call.priorLevel,
|
|
982
|
+
startedAtMs: env.startedAtMs,
|
|
983
|
+
nowMs: env.nowMs,
|
|
984
|
+
firstSightingFreshnessMs: DEFAULT_FIRST_SIGHTING_FRESHNESS_MS
|
|
985
|
+
});
|
|
986
|
+
call.priorLevel = verdict.kind === "rise" ? true : verdict.nextPriorLevel;
|
|
987
|
+
if (verdict.kind === "none") return env.currentOutput;
|
|
988
|
+
if (field.acceptedEdgeAt !== null && verdict.timestamp - field.acceptedEdgeAt < debounce) return env.currentOutput;
|
|
989
|
+
field.acceptedEdgeAt = verdict.timestamp;
|
|
990
|
+
return verdict.timestamp;
|
|
991
|
+
}
|
|
992
|
+
function evaluateCount(input, call, currentOutput) {
|
|
993
|
+
const currentCount = typeof currentOutput === "number" && Number.isFinite(currentOutput) && currentOutput >= 0 ? currentOutput : 0;
|
|
994
|
+
if (typeof input !== "number" || !Number.isFinite(input)) return currentCount;
|
|
995
|
+
if (call.highWater === null) {
|
|
996
|
+
call.highWater = input;
|
|
997
|
+
return currentCount;
|
|
998
|
+
}
|
|
999
|
+
if (input <= call.highWater) return currentCount;
|
|
1000
|
+
call.highWater = input;
|
|
1001
|
+
return currentCount + 1;
|
|
1002
|
+
}
|
|
778
1003
|
var INF = Number.POSITIVE_INFINITY;
|
|
779
1004
|
var table = {
|
|
780
1005
|
min: {
|
|
@@ -914,6 +1139,20 @@ var table = {
|
|
|
914
1139
|
const present = args.flatMap((a, i) => a === null ? [] : [asFiniteNumber(a, "latest", i)]);
|
|
915
1140
|
return present.length === 0 ? null : finiteResult(Math.max(...present), "latest");
|
|
916
1141
|
}
|
|
1142
|
+
},
|
|
1143
|
+
rose: {
|
|
1144
|
+
minArgs: 2,
|
|
1145
|
+
maxArgs: 3,
|
|
1146
|
+
apply: () => {
|
|
1147
|
+
throw new ExpressionEvalError("rose: stateful evaluation context is required");
|
|
1148
|
+
}
|
|
1149
|
+
},
|
|
1150
|
+
count: {
|
|
1151
|
+
minArgs: 1,
|
|
1152
|
+
maxArgs: 1,
|
|
1153
|
+
apply: () => {
|
|
1154
|
+
throw new ExpressionEvalError("count: stateful evaluation context is required");
|
|
1155
|
+
}
|
|
917
1156
|
}
|
|
918
1157
|
};
|
|
919
1158
|
/** Frozen, null-prototype builtin table. */
|
|
@@ -1397,22 +1636,28 @@ function step(ctx) {
|
|
|
1397
1636
|
ctx.steps += 1;
|
|
1398
1637
|
if (ctx.steps > ctx.maxSteps) throw new ExpressionEvalError("expression evaluation step budget exceeded");
|
|
1399
1638
|
}
|
|
1400
|
-
function
|
|
1639
|
+
function childPath(path, child) {
|
|
1640
|
+
return `${path}/${child}`;
|
|
1641
|
+
}
|
|
1642
|
+
function evalNode(node, ctx, path) {
|
|
1401
1643
|
step(ctx);
|
|
1402
1644
|
switch (node.kind) {
|
|
1403
1645
|
case "literal": return node.value;
|
|
1404
1646
|
case "identifier":
|
|
1405
1647
|
if (!(node.name in ctx.scope)) throw new ExpressionEvalError(`unknown identifier: ${node.name}`);
|
|
1406
1648
|
return ctx.scope[node.name];
|
|
1407
|
-
case "unary": return evalUnary(node.op, evalNode(node.operand, ctx));
|
|
1408
|
-
case "binary": return evalBinary(node.op, evalNode(node.left, ctx), evalNode(node.right, ctx));
|
|
1649
|
+
case "unary": return evalUnary(node.op, evalNode(node.operand, ctx, childPath(path, "operand")));
|
|
1650
|
+
case "binary": return evalBinary(node.op, evalNode(node.left, ctx, childPath(path, "left")), evalNode(node.right, ctx, childPath(path, "right")));
|
|
1409
1651
|
case "logical": {
|
|
1410
|
-
const left = evalNode(node.left, ctx);
|
|
1411
|
-
if (node.op === "&&") return truthy(left) ? evalNode(node.right, ctx) : left;
|
|
1412
|
-
return truthy(left) ? left : evalNode(node.right, ctx);
|
|
1652
|
+
const left = evalNode(node.left, ctx, childPath(path, "left"));
|
|
1653
|
+
if (node.op === "&&") return truthy(left) ? evalNode(node.right, ctx, childPath(path, "right")) : left;
|
|
1654
|
+
return truthy(left) ? left : evalNode(node.right, ctx, childPath(path, "right"));
|
|
1655
|
+
}
|
|
1656
|
+
case "conditional": return truthy(evalNode(node.test, ctx, childPath(path, "test"))) ? evalNode(node.consequent, ctx, childPath(path, "consequent")) : evalNode(node.alternate, ctx, childPath(path, "alternate"));
|
|
1657
|
+
case "call": {
|
|
1658
|
+
const args = node.args.map((arg, index) => evalNode(arg, ctx, childPath(path, `arg:${index}`)));
|
|
1659
|
+
return invokeCall(node.callee, args, path, ctx);
|
|
1413
1660
|
}
|
|
1414
|
-
case "conditional": return truthy(evalNode(node.test, ctx)) ? evalNode(node.consequent, ctx) : evalNode(node.alternate, ctx);
|
|
1415
|
-
case "call": return evalCall(node.callee, node.args.map((a) => evalNode(a, ctx)), ctx.hooks);
|
|
1416
1661
|
}
|
|
1417
1662
|
}
|
|
1418
1663
|
function evalUnary(op, operand) {
|
|
@@ -1443,11 +1688,19 @@ function evalBinary(op, left, right) {
|
|
|
1443
1688
|
throw new ExpressionEvalError(`"${op}" requires two numbers or two strings`);
|
|
1444
1689
|
}
|
|
1445
1690
|
}
|
|
1446
|
-
function
|
|
1691
|
+
function invokeCall(callee, args, path, ctx) {
|
|
1447
1692
|
if (!Object.prototype.hasOwnProperty.call(EXPRESSION_BUILTINS, callee)) throw new ExpressionEvalError(`unknown function: ${callee}`);
|
|
1448
1693
|
const builtin = EXPRESSION_BUILTINS[callee];
|
|
1449
1694
|
if (args.length < builtin.minArgs || args.length > builtin.maxArgs) throw new ExpressionEvalError(`${callee}: wrong number of arguments (${args.length})`);
|
|
1450
|
-
|
|
1695
|
+
if (isStatefulBuiltin(callee)) {
|
|
1696
|
+
if (ctx.invokeStateful === void 0) throw new ExpressionEvalError(`${callee}: stateful evaluation context is required`);
|
|
1697
|
+
return ctx.invokeStateful({
|
|
1698
|
+
name: callee,
|
|
1699
|
+
path,
|
|
1700
|
+
args
|
|
1701
|
+
});
|
|
1702
|
+
}
|
|
1703
|
+
return builtin.apply(args, ctx.hooks);
|
|
1451
1704
|
}
|
|
1452
1705
|
/** Evaluate an AST node against a scope. Throws `ExpressionEvalError` on any
|
|
1453
1706
|
* runtime failure (unknown identifier, type mismatch, non-finite result,
|
|
@@ -1457,11 +1710,47 @@ function evaluateAst(node, scope, opts) {
|
|
|
1457
1710
|
scope,
|
|
1458
1711
|
hooks: opts?.hooks ?? EMPTY_HOOKS,
|
|
1459
1712
|
maxSteps: opts?.maxSteps ?? 4096,
|
|
1713
|
+
...opts?.invokeStateful === void 0 ? {} : { invokeStateful: opts.invokeStateful },
|
|
1460
1714
|
steps: 0
|
|
1461
|
-
});
|
|
1715
|
+
}, "root");
|
|
1462
1716
|
}
|
|
1463
1717
|
//#endregion
|
|
1464
1718
|
//#region src/expression/expression-source.ts
|
|
1719
|
+
function createStatefulExpressionMemory() {
|
|
1720
|
+
return {
|
|
1721
|
+
calls: /* @__PURE__ */ new Map(),
|
|
1722
|
+
acceptedEdgeAt: null
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1725
|
+
function cloneStatefulExpressionMemory(memory) {
|
|
1726
|
+
return {
|
|
1727
|
+
calls: new Map([...memory.calls].map(([path, call]) => [path, { ...call }])),
|
|
1728
|
+
acceptedEdgeAt: memory.acceptedEdgeAt
|
|
1729
|
+
};
|
|
1730
|
+
}
|
|
1731
|
+
function evaluateStatefulExpressionCall(invocation, memory, env) {
|
|
1732
|
+
const existing = memory.calls.get(invocation.path);
|
|
1733
|
+
switch (invocation.name) {
|
|
1734
|
+
case "rose": {
|
|
1735
|
+
const call = existing?.kind === "rose" ? existing : { kind: "rose" };
|
|
1736
|
+
memory.calls.set(invocation.path, call);
|
|
1737
|
+
const currentOutput = env.currentOutput === null || typeof env.currentOutput === "number" && Number.isFinite(env.currentOutput) ? env.currentOutput : null;
|
|
1738
|
+
return evaluateRose(invocation.args[0], invocation.args[1], invocation.args[2], call, memory, {
|
|
1739
|
+
currentOutput,
|
|
1740
|
+
startedAtMs: env.startedAtMs,
|
|
1741
|
+
nowMs: env.nowMs
|
|
1742
|
+
});
|
|
1743
|
+
}
|
|
1744
|
+
case "count": {
|
|
1745
|
+
const call = existing?.kind === "count" ? existing : {
|
|
1746
|
+
kind: "count",
|
|
1747
|
+
highWater: null
|
|
1748
|
+
};
|
|
1749
|
+
memory.calls.set(invocation.path, call);
|
|
1750
|
+
return evaluateCount(invocation.args[0], call, env.currentOutput);
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1465
1754
|
/** The `now` epoch-ms binding is auto-injected into every evaluation and is a
|
|
1466
1755
|
* reserved binding name (authors may not rebind it). */
|
|
1467
1756
|
var EXPRESSION_INJECTED_NOW = "now";
|
|
@@ -1504,9 +1793,10 @@ function validateExpressionSource(src) {
|
|
|
1504
1793
|
/**
|
|
1505
1794
|
* Shared read-path evaluation. Builds a null-proto scope from `bindingValues`
|
|
1506
1795
|
* plus the injected `now` (supplied by the caller for determinism and
|
|
1507
|
-
* testability), compiles via the LRU, and evaluates.
|
|
1508
|
-
*
|
|
1509
|
-
*
|
|
1796
|
+
* testability), compiles via the LRU, and evaluates. Stateful calls are
|
|
1797
|
+
* delegated through `opts.invokeStateful` with their stable AST path. Any
|
|
1798
|
+
* failure (parse or eval) returns `{ ok: false }` — the caller treats that as
|
|
1799
|
+
* "skip this derivation", never as a throw that takes the pass down.
|
|
1510
1800
|
*/
|
|
1511
1801
|
function evaluateExpressionSource(expr, bindingValues, now, opts) {
|
|
1512
1802
|
const compiled = compileExpressionSafe(expr);
|
|
@@ -1650,6 +1940,7 @@ var CompositionItemEntrySchema = zod.z.object({
|
|
|
1650
1940
|
var CompositionFieldsFeatureSchema = zod.z.object({
|
|
1651
1941
|
kind: zod.z.literal("fields"),
|
|
1652
1942
|
cap: zod.z.string().min(1),
|
|
1943
|
+
enabled: zod.z.boolean().optional(),
|
|
1653
1944
|
fields: zod.z.record(zod.z.string().min(1), CompositionFieldSourceSchema).refine((fields) => Object.keys(fields).length <= 32, { message: `at most 32 fields per capability` }),
|
|
1654
1945
|
/**
|
|
1655
1946
|
* Items of the cap's item array, keyed by item key (D663). Naming `items`
|
|
@@ -1663,9 +1954,14 @@ var CompositionFieldsFeatureSchema = zod.z.object({
|
|
|
1663
1954
|
var CompositionPassthroughFeatureSchema = zod.z.object({
|
|
1664
1955
|
kind: zod.z.literal("passthrough"),
|
|
1665
1956
|
cap: zod.z.string().min(1),
|
|
1957
|
+
enabled: zod.z.boolean().optional(),
|
|
1666
1958
|
source: CompositionSourceRefSchema
|
|
1667
1959
|
});
|
|
1668
1960
|
var CompositionFeatureSchema = zod.z.discriminatedUnion("kind", [CompositionFieldsFeatureSchema, CompositionPassthroughFeatureSchema]);
|
|
1961
|
+
/** Only an explicit `false` disables a feature; omitted preserves the existing wire default. */
|
|
1962
|
+
function compositionFeatureEnabled(feature) {
|
|
1963
|
+
return feature.enabled !== false;
|
|
1964
|
+
}
|
|
1669
1965
|
/**
|
|
1670
1966
|
* A NEW device. It has no `name` of its own: the block's name IS the device's
|
|
1671
1967
|
* name (2026-08-08). A second copy here would be a second writer of one string (D62).
|
|
@@ -1900,6 +2196,12 @@ Object.defineProperty(exports, "CompositionTargetSchema", {
|
|
|
1900
2196
|
return CompositionTargetSchema;
|
|
1901
2197
|
}
|
|
1902
2198
|
});
|
|
2199
|
+
Object.defineProperty(exports, "DEFAULT_FIRST_SIGHTING_FRESHNESS_MS", {
|
|
2200
|
+
enumerable: true,
|
|
2201
|
+
get: function() {
|
|
2202
|
+
return DEFAULT_FIRST_SIGHTING_FRESHNESS_MS;
|
|
2203
|
+
}
|
|
2204
|
+
});
|
|
1903
2205
|
Object.defineProperty(exports, "EXPRESSION_BUILTINS", {
|
|
1904
2206
|
enumerable: true,
|
|
1905
2207
|
get: function() {
|
|
@@ -2008,6 +2310,30 @@ Object.defineProperty(exports, "RESERVED_BINDING_NAMES", {
|
|
|
2008
2310
|
return RESERVED_BINDING_NAMES;
|
|
2009
2311
|
}
|
|
2010
2312
|
});
|
|
2313
|
+
Object.defineProperty(exports, "SOURCE_CAPS", {
|
|
2314
|
+
enumerable: true,
|
|
2315
|
+
get: function() {
|
|
2316
|
+
return SOURCE_CAPS;
|
|
2317
|
+
}
|
|
2318
|
+
});
|
|
2319
|
+
Object.defineProperty(exports, "SOURCE_CAP_ACTIVE_FIELD", {
|
|
2320
|
+
enumerable: true,
|
|
2321
|
+
get: function() {
|
|
2322
|
+
return SOURCE_CAP_ACTIVE_FIELD;
|
|
2323
|
+
}
|
|
2324
|
+
});
|
|
2325
|
+
Object.defineProperty(exports, "SOURCE_CAP_CHANGED_AT_FIELD", {
|
|
2326
|
+
enumerable: true,
|
|
2327
|
+
get: function() {
|
|
2328
|
+
return SOURCE_CAP_CHANGED_AT_FIELD;
|
|
2329
|
+
}
|
|
2330
|
+
});
|
|
2331
|
+
Object.defineProperty(exports, "SOURCE_DEVICE_TYPES", {
|
|
2332
|
+
enumerable: true,
|
|
2333
|
+
get: function() {
|
|
2334
|
+
return SOURCE_DEVICE_TYPES;
|
|
2335
|
+
}
|
|
2336
|
+
});
|
|
2011
2337
|
Object.defineProperty(exports, "audioPlanFromEncodeProfile", {
|
|
2012
2338
|
enumerable: true,
|
|
2013
2339
|
get: function() {
|
|
@@ -2044,6 +2370,12 @@ Object.defineProperty(exports, "canonicalHash", {
|
|
|
2044
2370
|
return canonicalHash;
|
|
2045
2371
|
}
|
|
2046
2372
|
});
|
|
2373
|
+
Object.defineProperty(exports, "cloneStatefulExpressionMemory", {
|
|
2374
|
+
enumerable: true,
|
|
2375
|
+
get: function() {
|
|
2376
|
+
return cloneStatefulExpressionMemory;
|
|
2377
|
+
}
|
|
2378
|
+
});
|
|
2047
2379
|
Object.defineProperty(exports, "compileExpression", {
|
|
2048
2380
|
enumerable: true,
|
|
2049
2381
|
get: function() {
|
|
@@ -2068,6 +2400,12 @@ Object.defineProperty(exports, "composedDeviceStableId", {
|
|
|
2068
2400
|
return composedDeviceStableId;
|
|
2069
2401
|
}
|
|
2070
2402
|
});
|
|
2403
|
+
Object.defineProperty(exports, "compositionFeatureEnabled", {
|
|
2404
|
+
enumerable: true,
|
|
2405
|
+
get: function() {
|
|
2406
|
+
return compositionFeatureEnabled;
|
|
2407
|
+
}
|
|
2408
|
+
});
|
|
2071
2409
|
Object.defineProperty(exports, "compositionItemFieldPath", {
|
|
2072
2410
|
enumerable: true,
|
|
2073
2411
|
get: function() {
|
|
@@ -2098,18 +2436,54 @@ Object.defineProperty(exports, "createExpressionScope", {
|
|
|
2098
2436
|
return createExpressionScope;
|
|
2099
2437
|
}
|
|
2100
2438
|
});
|
|
2439
|
+
Object.defineProperty(exports, "createStatefulExpressionMemory", {
|
|
2440
|
+
enumerable: true,
|
|
2441
|
+
get: function() {
|
|
2442
|
+
return createStatefulExpressionMemory;
|
|
2443
|
+
}
|
|
2444
|
+
});
|
|
2101
2445
|
Object.defineProperty(exports, "evaluateAst", {
|
|
2102
2446
|
enumerable: true,
|
|
2103
2447
|
get: function() {
|
|
2104
2448
|
return evaluateAst;
|
|
2105
2449
|
}
|
|
2106
2450
|
});
|
|
2451
|
+
Object.defineProperty(exports, "evaluateCount", {
|
|
2452
|
+
enumerable: true,
|
|
2453
|
+
get: function() {
|
|
2454
|
+
return evaluateCount;
|
|
2455
|
+
}
|
|
2456
|
+
});
|
|
2107
2457
|
Object.defineProperty(exports, "evaluateExpressionSource", {
|
|
2108
2458
|
enumerable: true,
|
|
2109
2459
|
get: function() {
|
|
2110
2460
|
return evaluateExpressionSource;
|
|
2111
2461
|
}
|
|
2112
2462
|
});
|
|
2463
|
+
Object.defineProperty(exports, "evaluateRose", {
|
|
2464
|
+
enumerable: true,
|
|
2465
|
+
get: function() {
|
|
2466
|
+
return evaluateRose;
|
|
2467
|
+
}
|
|
2468
|
+
});
|
|
2469
|
+
Object.defineProperty(exports, "evaluateSensorEdge", {
|
|
2470
|
+
enumerable: true,
|
|
2471
|
+
get: function() {
|
|
2472
|
+
return evaluateSensorEdge;
|
|
2473
|
+
}
|
|
2474
|
+
});
|
|
2475
|
+
Object.defineProperty(exports, "evaluateSensorLevelEdge", {
|
|
2476
|
+
enumerable: true,
|
|
2477
|
+
get: function() {
|
|
2478
|
+
return evaluateSensorLevelEdge;
|
|
2479
|
+
}
|
|
2480
|
+
});
|
|
2481
|
+
Object.defineProperty(exports, "evaluateStatefulExpressionCall", {
|
|
2482
|
+
enumerable: true,
|
|
2483
|
+
get: function() {
|
|
2484
|
+
return evaluateStatefulExpressionCall;
|
|
2485
|
+
}
|
|
2486
|
+
});
|
|
2113
2487
|
Object.defineProperty(exports, "featureFieldEntries", {
|
|
2114
2488
|
enumerable: true,
|
|
2115
2489
|
get: function() {
|
|
@@ -2140,6 +2514,18 @@ Object.defineProperty(exports, "isSoftwareDecode", {
|
|
|
2140
2514
|
return isSoftwareDecode;
|
|
2141
2515
|
}
|
|
2142
2516
|
});
|
|
2517
|
+
Object.defineProperty(exports, "isSourceCap", {
|
|
2518
|
+
enumerable: true,
|
|
2519
|
+
get: function() {
|
|
2520
|
+
return isSourceCap;
|
|
2521
|
+
}
|
|
2522
|
+
});
|
|
2523
|
+
Object.defineProperty(exports, "isStatefulBuiltin", {
|
|
2524
|
+
enumerable: true,
|
|
2525
|
+
get: function() {
|
|
2526
|
+
return isStatefulBuiltin;
|
|
2527
|
+
}
|
|
2528
|
+
});
|
|
2143
2529
|
Object.defineProperty(exports, "logBannerArgs", {
|
|
2144
2530
|
enumerable: true,
|
|
2145
2531
|
get: function() {
|
|
@@ -2164,6 +2550,18 @@ Object.defineProperty(exports, "resolveStreamMaps", {
|
|
|
2164
2550
|
return resolveStreamMaps;
|
|
2165
2551
|
}
|
|
2166
2552
|
});
|
|
2553
|
+
Object.defineProperty(exports, "sliceActiveValue", {
|
|
2554
|
+
enumerable: true,
|
|
2555
|
+
get: function() {
|
|
2556
|
+
return sliceActiveValue;
|
|
2557
|
+
}
|
|
2558
|
+
});
|
|
2559
|
+
Object.defineProperty(exports, "sliceChangedAt", {
|
|
2560
|
+
enumerable: true,
|
|
2561
|
+
get: function() {
|
|
2562
|
+
return sliceChangedAt;
|
|
2563
|
+
}
|
|
2564
|
+
});
|
|
2167
2565
|
Object.defineProperty(exports, "toExpressionValue", {
|
|
2168
2566
|
enumerable: true,
|
|
2169
2567
|
get: function() {
|