@camstack/addon-post-analysis 1.2.198 → 1.2.199
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.
|
@@ -12814,6 +12814,23 @@ function recordToRow$2(subject, data) {
|
|
|
12814
12814
|
};
|
|
12815
12815
|
}
|
|
12816
12816
|
//#endregion
|
|
12817
|
+
//#region src/notification-center/no-match-report-key.ts
|
|
12818
|
+
/**
|
|
12819
|
+
* Stand-in for "this subject named no class". A distinct token rather than an
|
|
12820
|
+
* empty string so a class-less subject occupies its own bucket instead of
|
|
12821
|
+
* sharing one with every other class-less kind by accident.
|
|
12822
|
+
*/
|
|
12823
|
+
var NO_CLASSES = "∅";
|
|
12824
|
+
/**
|
|
12825
|
+
* The bucket key. Stable across calls, insensitive to class ORDER, DUPLICATES
|
|
12826
|
+
* and CASE — a detector that re-orders its class list, repeats one, or changes
|
|
12827
|
+
* its capitalisation must not fork the bucket and double the log volume.
|
|
12828
|
+
*/
|
|
12829
|
+
function noMatchReportKey(subject) {
|
|
12830
|
+
const classes = [...new Set(subject.classNames.map((c) => c.toLowerCase()))].sort();
|
|
12831
|
+
return `${subject.deviceId}|${subject.kind}|${classes.length > 0 ? classes.join(",") : NO_CLASSES}`;
|
|
12832
|
+
}
|
|
12833
|
+
//#endregion
|
|
12817
12834
|
//#region src/notification-center/occupancy-watcher.ts
|
|
12818
12835
|
/** Sentinel key segments for the "no zone" (whole-frame) and "no class" scopes. */
|
|
12819
12836
|
var FRAME_SCOPE = "@frame";
|
|
@@ -21945,10 +21962,15 @@ var NotificationCenter = class NotificationCenter {
|
|
|
21945
21962
|
* which leaves those notifications text-only rather than broken.
|
|
21946
21963
|
*/
|
|
21947
21964
|
stills;
|
|
21948
|
-
/**
|
|
21965
|
+
/**
|
|
21966
|
+
* Rate limit for the "matched NO rule" report — see `reportNoMatch`. Keyed by
|
|
21967
|
+
* {@link noMatchReportKey} (device + subject kind + class set), NOT by device:
|
|
21968
|
+
* a person's verdict must never consume the window a cat's verdict needed on
|
|
21969
|
+
* the same camera ([D390]).
|
|
21970
|
+
*/
|
|
21949
21971
|
lastNoMatchReportAt = /* @__PURE__ */ new Map();
|
|
21950
21972
|
noMatchSuppressed = /* @__PURE__ */ new Map();
|
|
21951
|
-
/** Same rate-limit pair for the per-camera mute drop — see `reportMutedDrop`. */
|
|
21973
|
+
/** Same rate-limit pair, same key, for the per-camera mute drop — see `reportMutedDrop`. */
|
|
21952
21974
|
lastMutedReportAt = /* @__PURE__ */ new Map();
|
|
21953
21975
|
mutedSuppressed = /* @__PURE__ */ new Map();
|
|
21954
21976
|
/**
|
|
@@ -24719,14 +24741,19 @@ var NotificationCenter = class NotificationCenter {
|
|
|
24719
24741
|
* is identical: "why did this camera not notify me?".
|
|
24720
24742
|
*/
|
|
24721
24743
|
reportMutedDrop(subject, kind, now) {
|
|
24722
|
-
const
|
|
24723
|
-
|
|
24744
|
+
const bucket = noMatchReportKey({
|
|
24745
|
+
deviceId: subject.deviceId,
|
|
24746
|
+
kind,
|
|
24747
|
+
classNames: subject.classNames
|
|
24748
|
+
});
|
|
24749
|
+
const last = this.lastMutedReportAt.get(bucket) ?? 0;
|
|
24750
|
+
const suppressed = this.mutedSuppressed.get(bucket) ?? 0;
|
|
24724
24751
|
if (now - last < NO_MATCH_REPORT_INTERVAL_MS) {
|
|
24725
|
-
this.mutedSuppressed.set(
|
|
24752
|
+
this.mutedSuppressed.set(bucket, suppressed + 1);
|
|
24726
24753
|
return;
|
|
24727
24754
|
}
|
|
24728
|
-
this.lastMutedReportAt.set(
|
|
24729
|
-
this.mutedSuppressed.set(
|
|
24755
|
+
this.lastMutedReportAt.set(bucket, now);
|
|
24756
|
+
this.mutedSuppressed.set(bucket, 0);
|
|
24730
24757
|
this.logger.info("notifications muted for this camera — event dropped before rule evaluation", {
|
|
24731
24758
|
tags: { deviceId: subject.deviceId },
|
|
24732
24759
|
meta: {
|
|
@@ -24738,16 +24765,23 @@ var NotificationCenter = class NotificationCenter {
|
|
|
24738
24765
|
}
|
|
24739
24766
|
});
|
|
24740
24767
|
}
|
|
24741
|
-
/** See the call site. Bounded to one line per
|
|
24768
|
+
/** See the call site. Bounded to one line per {@link noMatchReportKey} bucket
|
|
24769
|
+
* per window — device, subject kind and class set, so one subject's verdict
|
|
24770
|
+
* cannot stand in for another's on the same camera ([D390]). */
|
|
24742
24771
|
reportNoMatch(subject, kind, rejections, now) {
|
|
24743
|
-
const
|
|
24744
|
-
|
|
24772
|
+
const bucket = noMatchReportKey({
|
|
24773
|
+
deviceId: subject.deviceId,
|
|
24774
|
+
kind,
|
|
24775
|
+
classNames: subject.classNames
|
|
24776
|
+
});
|
|
24777
|
+
const last = this.lastNoMatchReportAt.get(bucket) ?? 0;
|
|
24778
|
+
const suppressed = this.noMatchSuppressed.get(bucket) ?? 0;
|
|
24745
24779
|
if (now - last < NO_MATCH_REPORT_INTERVAL_MS) {
|
|
24746
|
-
this.noMatchSuppressed.set(
|
|
24780
|
+
this.noMatchSuppressed.set(bucket, suppressed + 1);
|
|
24747
24781
|
return;
|
|
24748
24782
|
}
|
|
24749
|
-
this.lastNoMatchReportAt.set(
|
|
24750
|
-
this.noMatchSuppressed.set(
|
|
24783
|
+
this.lastNoMatchReportAt.set(bucket, now);
|
|
24784
|
+
this.noMatchSuppressed.set(bucket, 0);
|
|
24751
24785
|
this.logger.info("event matched NO rule", {
|
|
24752
24786
|
tags: { deviceId: subject.deviceId },
|
|
24753
24787
|
meta: {
|
|
@@ -12807,6 +12807,23 @@ function recordToRow$2(subject, data) {
|
|
|
12807
12807
|
};
|
|
12808
12808
|
}
|
|
12809
12809
|
//#endregion
|
|
12810
|
+
//#region src/notification-center/no-match-report-key.ts
|
|
12811
|
+
/**
|
|
12812
|
+
* Stand-in for "this subject named no class". A distinct token rather than an
|
|
12813
|
+
* empty string so a class-less subject occupies its own bucket instead of
|
|
12814
|
+
* sharing one with every other class-less kind by accident.
|
|
12815
|
+
*/
|
|
12816
|
+
var NO_CLASSES = "∅";
|
|
12817
|
+
/**
|
|
12818
|
+
* The bucket key. Stable across calls, insensitive to class ORDER, DUPLICATES
|
|
12819
|
+
* and CASE — a detector that re-orders its class list, repeats one, or changes
|
|
12820
|
+
* its capitalisation must not fork the bucket and double the log volume.
|
|
12821
|
+
*/
|
|
12822
|
+
function noMatchReportKey(subject) {
|
|
12823
|
+
const classes = [...new Set(subject.classNames.map((c) => c.toLowerCase()))].sort();
|
|
12824
|
+
return `${subject.deviceId}|${subject.kind}|${classes.length > 0 ? classes.join(",") : NO_CLASSES}`;
|
|
12825
|
+
}
|
|
12826
|
+
//#endregion
|
|
12810
12827
|
//#region src/notification-center/occupancy-watcher.ts
|
|
12811
12828
|
/** Sentinel key segments for the "no zone" (whole-frame) and "no class" scopes. */
|
|
12812
12829
|
var FRAME_SCOPE = "@frame";
|
|
@@ -21923,10 +21940,15 @@ var NotificationCenter = class NotificationCenter {
|
|
|
21923
21940
|
* which leaves those notifications text-only rather than broken.
|
|
21924
21941
|
*/
|
|
21925
21942
|
stills;
|
|
21926
|
-
/**
|
|
21943
|
+
/**
|
|
21944
|
+
* Rate limit for the "matched NO rule" report — see `reportNoMatch`. Keyed by
|
|
21945
|
+
* {@link noMatchReportKey} (device + subject kind + class set), NOT by device:
|
|
21946
|
+
* a person's verdict must never consume the window a cat's verdict needed on
|
|
21947
|
+
* the same camera ([D390]).
|
|
21948
|
+
*/
|
|
21927
21949
|
lastNoMatchReportAt = /* @__PURE__ */ new Map();
|
|
21928
21950
|
noMatchSuppressed = /* @__PURE__ */ new Map();
|
|
21929
|
-
/** Same rate-limit pair for the per-camera mute drop — see `reportMutedDrop`. */
|
|
21951
|
+
/** Same rate-limit pair, same key, for the per-camera mute drop — see `reportMutedDrop`. */
|
|
21930
21952
|
lastMutedReportAt = /* @__PURE__ */ new Map();
|
|
21931
21953
|
mutedSuppressed = /* @__PURE__ */ new Map();
|
|
21932
21954
|
/**
|
|
@@ -24697,14 +24719,19 @@ var NotificationCenter = class NotificationCenter {
|
|
|
24697
24719
|
* is identical: "why did this camera not notify me?".
|
|
24698
24720
|
*/
|
|
24699
24721
|
reportMutedDrop(subject, kind, now) {
|
|
24700
|
-
const
|
|
24701
|
-
|
|
24722
|
+
const bucket = noMatchReportKey({
|
|
24723
|
+
deviceId: subject.deviceId,
|
|
24724
|
+
kind,
|
|
24725
|
+
classNames: subject.classNames
|
|
24726
|
+
});
|
|
24727
|
+
const last = this.lastMutedReportAt.get(bucket) ?? 0;
|
|
24728
|
+
const suppressed = this.mutedSuppressed.get(bucket) ?? 0;
|
|
24702
24729
|
if (now - last < NO_MATCH_REPORT_INTERVAL_MS) {
|
|
24703
|
-
this.mutedSuppressed.set(
|
|
24730
|
+
this.mutedSuppressed.set(bucket, suppressed + 1);
|
|
24704
24731
|
return;
|
|
24705
24732
|
}
|
|
24706
|
-
this.lastMutedReportAt.set(
|
|
24707
|
-
this.mutedSuppressed.set(
|
|
24733
|
+
this.lastMutedReportAt.set(bucket, now);
|
|
24734
|
+
this.mutedSuppressed.set(bucket, 0);
|
|
24708
24735
|
this.logger.info("notifications muted for this camera — event dropped before rule evaluation", {
|
|
24709
24736
|
tags: { deviceId: subject.deviceId },
|
|
24710
24737
|
meta: {
|
|
@@ -24716,16 +24743,23 @@ var NotificationCenter = class NotificationCenter {
|
|
|
24716
24743
|
}
|
|
24717
24744
|
});
|
|
24718
24745
|
}
|
|
24719
|
-
/** See the call site. Bounded to one line per
|
|
24746
|
+
/** See the call site. Bounded to one line per {@link noMatchReportKey} bucket
|
|
24747
|
+
* per window — device, subject kind and class set, so one subject's verdict
|
|
24748
|
+
* cannot stand in for another's on the same camera ([D390]). */
|
|
24720
24749
|
reportNoMatch(subject, kind, rejections, now) {
|
|
24721
|
-
const
|
|
24722
|
-
|
|
24750
|
+
const bucket = noMatchReportKey({
|
|
24751
|
+
deviceId: subject.deviceId,
|
|
24752
|
+
kind,
|
|
24753
|
+
classNames: subject.classNames
|
|
24754
|
+
});
|
|
24755
|
+
const last = this.lastNoMatchReportAt.get(bucket) ?? 0;
|
|
24756
|
+
const suppressed = this.noMatchSuppressed.get(bucket) ?? 0;
|
|
24723
24757
|
if (now - last < NO_MATCH_REPORT_INTERVAL_MS) {
|
|
24724
|
-
this.noMatchSuppressed.set(
|
|
24758
|
+
this.noMatchSuppressed.set(bucket, suppressed + 1);
|
|
24725
24759
|
return;
|
|
24726
24760
|
}
|
|
24727
|
-
this.lastNoMatchReportAt.set(
|
|
24728
|
-
this.noMatchSuppressed.set(
|
|
24761
|
+
this.lastNoMatchReportAt.set(bucket, now);
|
|
24762
|
+
this.noMatchSuppressed.set(bucket, 0);
|
|
24729
24763
|
this.logger.info("event matched NO rule", {
|
|
24730
24764
|
tags: { deviceId: subject.deviceId },
|
|
24731
24765
|
meta: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-post-analysis",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.199",
|
|
4
4
|
"description": "Post-Analysis bundle — enrichment, embedding-encoder, pipeline-analytics. Multi-entry npm package shipping addons that consume pipeline output.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|