@camstack/addon-post-analysis 1.2.216 → 1.2.218
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/{dist-DWZqNymj.mjs → dist-BqkhuC85.mjs} +41 -4
- package/dist/{dist-B_6UZCut.js → dist-CbQHenLE.js} +41 -4
- package/dist/embedding-encoder/index.js +1 -1
- package/dist/embedding-encoder/index.mjs +1 -1
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-BmDXGilB.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-JPlVJb-8.mjs} +3 -3
- package/dist/pipeline-analytics/{hostInit-DMDZJw2T.mjs → hostInit-CqdkxAhm.mjs} +3 -3
- package/dist/pipeline-analytics/index.js +235 -29
- package/dist/pipeline-analytics/index.mjs +235 -29
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -1
|
@@ -21091,7 +21091,17 @@ var EventDensityBucketSchema = object({
|
|
|
21091
21091
|
bucketStart: number(),
|
|
21092
21092
|
motion: number().int(),
|
|
21093
21093
|
object: number().int(),
|
|
21094
|
-
audio: number().int()
|
|
21094
|
+
audio: number().int(),
|
|
21095
|
+
/**
|
|
21096
|
+
* Energy-weighted mean dBFS of the audio events in this bucket — the LEVEL,
|
|
21097
|
+
* as opposed to `audio`, which is their COUNT (D431).
|
|
21098
|
+
*
|
|
21099
|
+
* ABSENT when the bucket holds no audio event. Absence means "nothing was
|
|
21100
|
+
* measured", which is not the same claim as silence, and a lane that cannot
|
|
21101
|
+
* tell them apart paints a quiet hour at full scale — which is exactly what
|
|
21102
|
+
* the count-on-a-level-meter did.
|
|
21103
|
+
*/
|
|
21104
|
+
audioDbfs: number().optional()
|
|
21095
21105
|
});
|
|
21096
21106
|
/**
|
|
21097
21107
|
* One camera's row in a `getEventDensityBatch` answer.
|
|
@@ -31838,7 +31848,23 @@ var RecordingRangeSchema = object({
|
|
|
31838
31848
|
});
|
|
31839
31849
|
var RecordingAvailabilitySchema = object({
|
|
31840
31850
|
deviceId: number(),
|
|
31841
|
-
ranges: array(RecordingRangeSchema)
|
|
31851
|
+
ranges: array(RecordingRangeSchema),
|
|
31852
|
+
/**
|
|
31853
|
+
* Every profile this camera has footage in — not only the one `ranges`
|
|
31854
|
+
* describes (D433).
|
|
31855
|
+
*
|
|
31856
|
+
* `ranges` answers for ONE profile by design: the timeline is a single bar,
|
|
31857
|
+
* and enumerating all of them triples the directory reads for a bar that
|
|
31858
|
+
* would look identical. But the answer used to say nothing about that, so a
|
|
31859
|
+
* caller asking "what can I export?" read the single preferred profile as
|
|
31860
|
+
* the only one that exists — on this deployment every camera records `high`
|
|
31861
|
+
* AND `low`, the low tree holding 14 GB for camera 615 alone, and the export
|
|
31862
|
+
* UI could not see it.
|
|
31863
|
+
*
|
|
31864
|
+
* Cheap: it is the same shallow directory read the profile CHOICE already
|
|
31865
|
+
* makes. Ask for a specific profile's ranges with the `profile` input.
|
|
31866
|
+
*/
|
|
31867
|
+
profilesWithFootage: array(string())
|
|
31842
31868
|
});
|
|
31843
31869
|
var RecordingDaysSchema = object({
|
|
31844
31870
|
deviceId: number(),
|
|
@@ -31864,7 +31890,11 @@ var RecordingDaysSchema = object({
|
|
|
31864
31890
|
var RecordingAvailabilityForDeviceSchema = object({
|
|
31865
31891
|
deviceId: number(),
|
|
31866
31892
|
read: _enum(["read", "unreadable"]),
|
|
31867
|
-
ranges: array(RecordingRangeSchema).readonly()
|
|
31893
|
+
ranges: array(RecordingRangeSchema).readonly(),
|
|
31894
|
+
/** Same field, same meaning, as the singular answer (D433). A row that
|
|
31895
|
+
* dropped it would tell a grid caller the archive holds one profile.
|
|
31896
|
+
* Empty on an `'unreadable'` row: nobody looked. */
|
|
31897
|
+
profilesWithFootage: array(string()).readonly()
|
|
31868
31898
|
});
|
|
31869
31899
|
/**
|
|
31870
31900
|
* One camera's row in a `getDaysWithRecordingsBatch` answer. Same rule as
|
|
@@ -32154,7 +32184,14 @@ var ReadWindowBytesResultSchema = discriminatedUnion("kind", [object({
|
|
|
32154
32184
|
method(object({
|
|
32155
32185
|
deviceId: number(),
|
|
32156
32186
|
fromMs: number(),
|
|
32157
|
-
toMs: number()
|
|
32187
|
+
toMs: number(),
|
|
32188
|
+
/**
|
|
32189
|
+
* Answer for THIS profile instead of the preferred one (D433). Absent
|
|
32190
|
+
* keeps the timeline's behaviour — one bar, one profile, one set of
|
|
32191
|
+
* reads. `profilesWithFootage` on the answer says what may be asked
|
|
32192
|
+
* for.
|
|
32193
|
+
*/
|
|
32194
|
+
profile: string().optional()
|
|
32158
32195
|
}), RecordingAvailabilitySchema, {
|
|
32159
32196
|
kind: "query",
|
|
32160
32197
|
auth: "protected"
|
|
@@ -21122,7 +21122,17 @@ var EventDensityBucketSchema = object({
|
|
|
21122
21122
|
bucketStart: number(),
|
|
21123
21123
|
motion: number().int(),
|
|
21124
21124
|
object: number().int(),
|
|
21125
|
-
audio: number().int()
|
|
21125
|
+
audio: number().int(),
|
|
21126
|
+
/**
|
|
21127
|
+
* Energy-weighted mean dBFS of the audio events in this bucket — the LEVEL,
|
|
21128
|
+
* as opposed to `audio`, which is their COUNT (D431).
|
|
21129
|
+
*
|
|
21130
|
+
* ABSENT when the bucket holds no audio event. Absence means "nothing was
|
|
21131
|
+
* measured", which is not the same claim as silence, and a lane that cannot
|
|
21132
|
+
* tell them apart paints a quiet hour at full scale — which is exactly what
|
|
21133
|
+
* the count-on-a-level-meter did.
|
|
21134
|
+
*/
|
|
21135
|
+
audioDbfs: number().optional()
|
|
21126
21136
|
});
|
|
21127
21137
|
/**
|
|
21128
21138
|
* One camera's row in a `getEventDensityBatch` answer.
|
|
@@ -31869,7 +31879,23 @@ var RecordingRangeSchema = object({
|
|
|
31869
31879
|
});
|
|
31870
31880
|
var RecordingAvailabilitySchema = object({
|
|
31871
31881
|
deviceId: number(),
|
|
31872
|
-
ranges: array(RecordingRangeSchema)
|
|
31882
|
+
ranges: array(RecordingRangeSchema),
|
|
31883
|
+
/**
|
|
31884
|
+
* Every profile this camera has footage in — not only the one `ranges`
|
|
31885
|
+
* describes (D433).
|
|
31886
|
+
*
|
|
31887
|
+
* `ranges` answers for ONE profile by design: the timeline is a single bar,
|
|
31888
|
+
* and enumerating all of them triples the directory reads for a bar that
|
|
31889
|
+
* would look identical. But the answer used to say nothing about that, so a
|
|
31890
|
+
* caller asking "what can I export?" read the single preferred profile as
|
|
31891
|
+
* the only one that exists — on this deployment every camera records `high`
|
|
31892
|
+
* AND `low`, the low tree holding 14 GB for camera 615 alone, and the export
|
|
31893
|
+
* UI could not see it.
|
|
31894
|
+
*
|
|
31895
|
+
* Cheap: it is the same shallow directory read the profile CHOICE already
|
|
31896
|
+
* makes. Ask for a specific profile's ranges with the `profile` input.
|
|
31897
|
+
*/
|
|
31898
|
+
profilesWithFootage: array(string())
|
|
31873
31899
|
});
|
|
31874
31900
|
var RecordingDaysSchema = object({
|
|
31875
31901
|
deviceId: number(),
|
|
@@ -31895,7 +31921,11 @@ var RecordingDaysSchema = object({
|
|
|
31895
31921
|
var RecordingAvailabilityForDeviceSchema = object({
|
|
31896
31922
|
deviceId: number(),
|
|
31897
31923
|
read: _enum(["read", "unreadable"]),
|
|
31898
|
-
ranges: array(RecordingRangeSchema).readonly()
|
|
31924
|
+
ranges: array(RecordingRangeSchema).readonly(),
|
|
31925
|
+
/** Same field, same meaning, as the singular answer (D433). A row that
|
|
31926
|
+
* dropped it would tell a grid caller the archive holds one profile.
|
|
31927
|
+
* Empty on an `'unreadable'` row: nobody looked. */
|
|
31928
|
+
profilesWithFootage: array(string()).readonly()
|
|
31899
31929
|
});
|
|
31900
31930
|
/**
|
|
31901
31931
|
* One camera's row in a `getDaysWithRecordingsBatch` answer. Same rule as
|
|
@@ -32185,7 +32215,14 @@ var ReadWindowBytesResultSchema = discriminatedUnion("kind", [object({
|
|
|
32185
32215
|
method(object({
|
|
32186
32216
|
deviceId: number(),
|
|
32187
32217
|
fromMs: number(),
|
|
32188
|
-
toMs: number()
|
|
32218
|
+
toMs: number(),
|
|
32219
|
+
/**
|
|
32220
|
+
* Answer for THIS profile instead of the preferred one (D433). Absent
|
|
32221
|
+
* keeps the timeline's behaviour — one bar, one profile, one set of
|
|
32222
|
+
* reads. `profilesWithFootage` on the answer says what may be asked
|
|
32223
|
+
* for.
|
|
32224
|
+
*/
|
|
32225
|
+
profile: string().optional()
|
|
32189
32226
|
}), RecordingAvailabilitySchema, {
|
|
32190
32227
|
kind: "query",
|
|
32191
32228
|
auth: "protected"
|
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
5
|
+
const require_dist = require("../dist-CbQHenLE.js");
|
|
6
6
|
let node_fs = require("node:fs");
|
|
7
7
|
node_fs = require_dist.__toESM(node_fs);
|
|
8
8
|
let node_path = require("node:path");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { At as resolvePoolMemoryPolicy, B as PoolMemoryWatchdog, Ct as parseProcStatus, gt as hfModelUrl, ut as embeddingEncoderCapability, zt as BaseAddon } from "../dist-
|
|
1
|
+
import { At as resolvePoolMemoryPolicy, B as PoolMemoryWatchdog, Ct as parseProcStatus, gt as hfModelUrl, ut as embeddingEncoderCapability, zt as BaseAddon } from "../dist-BqkhuC85.mjs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
@@ -3,7 +3,7 @@ import "./dist-CYZr2fwk.mjs";
|
|
|
3
3
|
var e = {
|
|
4
4
|
"@camstack/sdk": {
|
|
5
5
|
name: "@camstack/sdk",
|
|
6
|
-
version: "1.2.
|
|
6
|
+
version: "1.2.96",
|
|
7
7
|
scope: ["default"],
|
|
8
8
|
loaded: !1,
|
|
9
9
|
from: "addon_pipeline_analytics_widgets",
|
|
@@ -18,7 +18,7 @@ var e = {
|
|
|
18
18
|
},
|
|
19
19
|
"@camstack/types": {
|
|
20
20
|
name: "@camstack/types",
|
|
21
|
-
version: "1.2.
|
|
21
|
+
version: "1.2.176",
|
|
22
22
|
scope: ["default"],
|
|
23
23
|
loaded: !1,
|
|
24
24
|
from: "addon_pipeline_analytics_widgets",
|
|
@@ -33,7 +33,7 @@ var e = {
|
|
|
33
33
|
},
|
|
34
34
|
"@camstack/ui-library": {
|
|
35
35
|
name: "@camstack/ui-library",
|
|
36
|
-
version: "1.2.
|
|
36
|
+
version: "1.2.144",
|
|
37
37
|
scope: ["default"],
|
|
38
38
|
loaded: !1,
|
|
39
39
|
from: "addon_pipeline_analytics_widgets",
|
|
@@ -36,7 +36,7 @@ async function r() {
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"@camstack/types": {
|
|
39
|
-
version: "1.2.
|
|
39
|
+
version: "1.2.176",
|
|
40
40
|
scope: "default",
|
|
41
41
|
shareConfig: {
|
|
42
42
|
singleton: !0,
|
|
@@ -45,7 +45,7 @@ async function r() {
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
"@camstack/sdk": {
|
|
48
|
-
version: "1.2.
|
|
48
|
+
version: "1.2.96",
|
|
49
49
|
scope: "default",
|
|
50
50
|
shareConfig: {
|
|
51
51
|
singleton: !0,
|
|
@@ -81,7 +81,7 @@ async function r() {
|
|
|
81
81
|
}
|
|
82
82
|
},
|
|
83
83
|
"@camstack/ui-library": {
|
|
84
|
-
version: "1.2.
|
|
84
|
+
version: "1.2.144",
|
|
85
85
|
scope: "default",
|
|
86
86
|
shareConfig: {
|
|
87
87
|
singleton: !0,
|
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
5
|
+
const require_dist = require("../dist-CbQHenLE.js");
|
|
6
6
|
let node_fs = require("node:fs");
|
|
7
7
|
node_fs = require_dist.__toESM(node_fs, 1);
|
|
8
8
|
let node_path = require("node:path");
|
|
@@ -20092,11 +20092,19 @@ var TimelapseRenderer = class {
|
|
|
20092
20092
|
async render(request) {
|
|
20093
20093
|
const { deviceId, window } = request;
|
|
20094
20094
|
const tags = { deviceId };
|
|
20095
|
-
const
|
|
20095
|
+
const first = await this.ports.getAvailability({
|
|
20096
20096
|
deviceId,
|
|
20097
20097
|
fromMs: window.startMs,
|
|
20098
20098
|
toMs: window.endMs
|
|
20099
20099
|
});
|
|
20100
|
+
const cheapest = [...first.profilesWithFootage].sort((a, b) => profileCost(a) - profileCost(b))[0];
|
|
20101
|
+
const describes = first.ranges[0]?.profile;
|
|
20102
|
+
const availability = cheapest !== void 0 && cheapest !== describes ? (await this.ports.getAvailability({
|
|
20103
|
+
deviceId,
|
|
20104
|
+
fromMs: window.startMs,
|
|
20105
|
+
toMs: window.endMs,
|
|
20106
|
+
profile: cheapest
|
|
20107
|
+
})).ranges : first.ranges;
|
|
20100
20108
|
const chosen = pickProfile(availability, window);
|
|
20101
20109
|
if (chosen === null || chosen.coverage.coveredMs === 0) {
|
|
20102
20110
|
this.ports.logger.warn("timelapse skipped: no footage covers the window", {
|
|
@@ -36091,6 +36099,17 @@ function mapObjectStateToTrackState(s) {
|
|
|
36091
36099
|
default: return "new";
|
|
36092
36100
|
}
|
|
36093
36101
|
}
|
|
36102
|
+
/**
|
|
36103
|
+
* How long an identical refusal stays collapsed. A stationary subject is
|
|
36104
|
+
* refused on every analysed frame; at ~2 fps of analysis, 60 s turns a parked
|
|
36105
|
+
* car's 120 lines into one and still reports a subject that comes back later.
|
|
36106
|
+
*/
|
|
36107
|
+
var ZONE_REFUSAL_THROTTLE_MS = 6e4;
|
|
36108
|
+
/** Box quantisation for the collapse key: a subject that moves less than this
|
|
36109
|
+
* is the same sighting for diagnostic purposes. */
|
|
36110
|
+
var ZONE_REFUSAL_BOX_CELL_PX = 64;
|
|
36111
|
+
/** Bound on the collapse map; stale keys are swept when it is exceeded. */
|
|
36112
|
+
var ZONE_REFUSAL_KEY_LIMIT = 512;
|
|
36094
36113
|
var FrameProcessor = class {
|
|
36095
36114
|
deviceId;
|
|
36096
36115
|
/** Detection source this processor handles (`pipeline` | `onboard`).
|
|
@@ -36151,7 +36170,6 @@ var FrameProcessor = class {
|
|
|
36151
36170
|
* filter). Shares the tracker's vocabulary — see {@link SpawnRefusalReason}. */
|
|
36152
36171
|
spawnRefusalObserver = null;
|
|
36153
36172
|
/** Where a collected refusal is forwarded for LOGGING, when anyone asked. */
|
|
36154
|
-
spawnRefusalForward = null;
|
|
36155
36173
|
/**
|
|
36156
36174
|
* The spawn refusals of the frame being processed.
|
|
36157
36175
|
*
|
|
@@ -36162,8 +36180,15 @@ var FrameProcessor = class {
|
|
|
36162
36180
|
* as before?" answered first. See `zone-detection-emits-without-a-track`.
|
|
36163
36181
|
*/
|
|
36164
36182
|
refusalsThisFrame = [];
|
|
36165
|
-
/** Running count of
|
|
36183
|
+
/** Running count of refusals-in-zone SEEN — never the throttled number. */
|
|
36166
36184
|
zoneRefusalEventCount = 0;
|
|
36185
|
+
zoneRefusalObserver = null;
|
|
36186
|
+
/** Track ids whose BIRTH zone history has already been folded in — the fold
|
|
36187
|
+
* happens once, on the frame a track first becomes visible. */
|
|
36188
|
+
birthZonesSeeded = /* @__PURE__ */ new Set();
|
|
36189
|
+
birthZoneTrailObserver = null;
|
|
36190
|
+
/** Last report time per collapsed key, for {@link ZONE_REFUSAL_THROTTLE_MS}. */
|
|
36191
|
+
zoneRefusalLastReport = /* @__PURE__ */ new Map();
|
|
36167
36192
|
zoneRefusalSampler = new SpawnRefusalSampler();
|
|
36168
36193
|
/** Optional stationary-object gate (parked-object suppression). Null until
|
|
36169
36194
|
* the addon wires it via {@link setStationaryGate}. */
|
|
@@ -36264,6 +36289,18 @@ var FrameProcessor = class {
|
|
|
36264
36289
|
this.tracker.setSpawnRefusalObserver(observer);
|
|
36265
36290
|
this.spawnRefusalObserver = observer;
|
|
36266
36291
|
}
|
|
36292
|
+
/**
|
|
36293
|
+
* Where a refused-inside-a-zone detection goes. Diagnostics only: this is
|
|
36294
|
+
* deliberately NOT the event stream, so nothing downstream can alert on a
|
|
36295
|
+
* box the confirmation gate threw away (D426).
|
|
36296
|
+
*/
|
|
36297
|
+
setZoneRefusalObserver(observer) {
|
|
36298
|
+
this.zoneRefusalObserver = observer;
|
|
36299
|
+
}
|
|
36300
|
+
/** Where "this track was born already carrying a zone" is reported. */
|
|
36301
|
+
setBirthZoneTrailObserver(observer) {
|
|
36302
|
+
this.birthZoneTrailObserver = observer;
|
|
36303
|
+
}
|
|
36267
36304
|
setBindObserver(observer) {
|
|
36268
36305
|
this.tracker.setBindObserver(observer);
|
|
36269
36306
|
}
|
|
@@ -36299,40 +36336,73 @@ var FrameProcessor = class {
|
|
|
36299
36336
|
* operator's own detection rules already said the box does not count on this
|
|
36300
36337
|
* camera, and re-admitting it here would overrule a decision they made.
|
|
36301
36338
|
*/
|
|
36302
|
-
|
|
36339
|
+
/**
|
|
36340
|
+
* Report every detection the tracker refused that fell INSIDE a watched
|
|
36341
|
+
* zone. Diagnostics only — see the spec
|
|
36342
|
+
* `zone-refusal-is-observed-never-notified` for the ten hours of production
|
|
36343
|
+
* that decided this: 1473 of 1511 such refusals were one stationary parcel
|
|
36344
|
+
* re-offered every frame, and both notifications this path ever produced
|
|
36345
|
+
* were phantoms the confirmation gate had correctly rejected.
|
|
36346
|
+
*
|
|
36347
|
+
* Drains {@link refusalsThisFrame}. `zone-filtered` is skipped: a box the
|
|
36348
|
+
* zone filter already removed was never in a zone.
|
|
36349
|
+
*/
|
|
36350
|
+
reportRefusedDetectionsInZones(timestamp, frameWidth, frameHeight) {
|
|
36303
36351
|
const refusals = this.refusalsThisFrame;
|
|
36304
36352
|
this.refusalsThisFrame = [];
|
|
36305
|
-
if (refusals.length === 0 || this.zones.length === 0) return
|
|
36306
|
-
const
|
|
36353
|
+
if (refusals.length === 0 || this.zones.length === 0) return;
|
|
36354
|
+
const observer = this.zoneRefusalObserver;
|
|
36355
|
+
if (observer === null) return;
|
|
36307
36356
|
for (const r of refusals) {
|
|
36308
36357
|
if (r.reason === "zone-filtered") continue;
|
|
36309
36358
|
const { memberships } = this.zoneEngine.splitDetectionZones(r.bbox, this.zones, frameWidth, frameHeight, void 0, void 0, void 0, this.zoneMembershipMinOverlap);
|
|
36310
36359
|
if (memberships.length === 0) continue;
|
|
36311
36360
|
this.zoneRefusalEventCount += 1;
|
|
36312
|
-
|
|
36313
|
-
|
|
36314
|
-
kind: "object",
|
|
36361
|
+
if (!this.shouldReportZoneRefusal(r, memberships, timestamp)) continue;
|
|
36362
|
+
observer({
|
|
36315
36363
|
deviceId: this.deviceId,
|
|
36316
36364
|
timestamp,
|
|
36317
|
-
frameId: frame.frameId,
|
|
36318
36365
|
className: r.detClass,
|
|
36319
36366
|
confidence: r.detScore,
|
|
36320
36367
|
bbox: r.bbox,
|
|
36321
|
-
|
|
36322
|
-
|
|
36323
|
-
zones: memberships.map((m) => m.zoneId),
|
|
36324
|
-
zoneOverlaps: memberships.map((m) => ({
|
|
36368
|
+
reason: r.reason,
|
|
36369
|
+
zones: memberships.map((m) => ({
|
|
36325
36370
|
zoneId: m.zoneId,
|
|
36326
36371
|
...m.zoneName === void 0 ? {} : { zoneName: m.zoneName },
|
|
36327
36372
|
overlapPct: Math.round(m.overlap * 1e3) / 10
|
|
36328
36373
|
})),
|
|
36329
|
-
|
|
36330
|
-
});
|
|
36374
|
+
seenSoFar: this.zoneRefusalEventCount
|
|
36375
|
+
});
|
|
36376
|
+
}
|
|
36377
|
+
}
|
|
36378
|
+
/**
|
|
36379
|
+
* Collapse identical refusals. A refused box is refused on EVERY analysed
|
|
36380
|
+
* frame, so a subject that does not leave — the parcel at the door, the
|
|
36381
|
+
* parked car — is an unbounded emitter. The key includes a coarse box, so a
|
|
36382
|
+
* subject that actually moves keeps reporting while a stationary one does
|
|
36383
|
+
* not.
|
|
36384
|
+
*/
|
|
36385
|
+
shouldReportZoneRefusal(r, memberships, timestamp) {
|
|
36386
|
+
const cell = (v) => Math.round(v / ZONE_REFUSAL_BOX_CELL_PX);
|
|
36387
|
+
const key = [
|
|
36388
|
+
r.detClass,
|
|
36389
|
+
r.reason,
|
|
36390
|
+
memberships.map((m) => m.zoneId).join("+"),
|
|
36391
|
+
cell(r.bbox.x),
|
|
36392
|
+
cell(r.bbox.y),
|
|
36393
|
+
cell(r.bbox.w),
|
|
36394
|
+
cell(r.bbox.h)
|
|
36395
|
+
].join("|");
|
|
36396
|
+
const last = this.zoneRefusalLastReport.get(key);
|
|
36397
|
+
if (last !== void 0 && timestamp - last < ZONE_REFUSAL_THROTTLE_MS) return false;
|
|
36398
|
+
this.zoneRefusalLastReport.set(key, timestamp);
|
|
36399
|
+
if (this.zoneRefusalLastReport.size > ZONE_REFUSAL_KEY_LIMIT) {
|
|
36400
|
+
for (const [k, t] of this.zoneRefusalLastReport) if (timestamp - t >= ZONE_REFUSAL_THROTTLE_MS) this.zoneRefusalLastReport.delete(k);
|
|
36331
36401
|
}
|
|
36332
|
-
return
|
|
36402
|
+
return true;
|
|
36333
36403
|
}
|
|
36334
|
-
/** How many zone
|
|
36335
|
-
*
|
|
36404
|
+
/** How many refusals-in-zone this processor has SEEN — the measure that says
|
|
36405
|
+
* whether this path is a trickle or a storm, before any throttling. */
|
|
36336
36406
|
zoneRefusalEventsEmitted() {
|
|
36337
36407
|
return this.zoneRefusalEventCount;
|
|
36338
36408
|
}
|
|
@@ -36566,6 +36636,32 @@ var FrameProcessor = class {
|
|
|
36566
36636
|
if (memberships.length > 0) overlapsByTrack.set(td.trackId, memberships);
|
|
36567
36637
|
if (belowBar.length > 0) rejectedByTrack.set(td.trackId, belowBar);
|
|
36568
36638
|
}
|
|
36639
|
+
for (const td of trackedDetections) {
|
|
36640
|
+
if (this.birthZonesSeeded.has(td.trackId)) continue;
|
|
36641
|
+
this.birthZonesSeeded.add(td.trackId);
|
|
36642
|
+
const trail = td.path;
|
|
36643
|
+
if (trail.length <= 1) continue;
|
|
36644
|
+
const atBirth = zonesByTrack.get(td.trackId) ?? [];
|
|
36645
|
+
const zones = new Set(atBirth);
|
|
36646
|
+
const bornWith = zones.size;
|
|
36647
|
+
for (let i = 0; i < trail.length - 1; i++) {
|
|
36648
|
+
const box = trail[i];
|
|
36649
|
+
if (box === void 0) continue;
|
|
36650
|
+
const { memberships } = this.zoneEngine.splitDetectionZones(box, this.zones, frameWidth, frameHeight, void 0, void 0, void 0, this.zoneMembershipMinOverlap);
|
|
36651
|
+
for (const m of memberships) zones.add(m.zoneId);
|
|
36652
|
+
}
|
|
36653
|
+
if (zones.size === bornWith) continue;
|
|
36654
|
+
const inherited = [...zones].filter((z) => !atBirth.includes(z));
|
|
36655
|
+
zonesByTrack.set(td.trackId, [...zones]);
|
|
36656
|
+
this.birthZoneTrailObserver?.({
|
|
36657
|
+
deviceId: this.deviceId,
|
|
36658
|
+
trackId: td.trackId,
|
|
36659
|
+
className: td.class,
|
|
36660
|
+
measurementsBeforeBirth: trail.length - 1,
|
|
36661
|
+
zonesAtBirth: [...atBirth],
|
|
36662
|
+
inheritedZones: inherited
|
|
36663
|
+
});
|
|
36664
|
+
}
|
|
36569
36665
|
this.lastZoneOverlaps = overlapsByTrack;
|
|
36570
36666
|
this.lastZoneRejections = rejectedByTrack;
|
|
36571
36667
|
for (const td of trackedDetections) this.lastDetectionByTrack.set(td.trackId, td);
|
|
@@ -36638,6 +36734,7 @@ var FrameProcessor = class {
|
|
|
36638
36734
|
for (const state of objectStates) {
|
|
36639
36735
|
if (state.state !== "leaving") continue;
|
|
36640
36736
|
this.lastZonesByTrack.delete(state.trackId);
|
|
36737
|
+
this.birthZonesSeeded.delete(state.trackId);
|
|
36641
36738
|
this.lastDetectionByTrack.delete(state.trackId);
|
|
36642
36739
|
this.reportedRefusalByTrack.delete(state.trackId);
|
|
36643
36740
|
}
|
|
@@ -36737,11 +36834,10 @@ var FrameProcessor = class {
|
|
|
36737
36834
|
frameHeight
|
|
36738
36835
|
};
|
|
36739
36836
|
};
|
|
36740
|
-
|
|
36837
|
+
this.reportRefusedDetectionsInZones(timestamp, frameWidth, frameHeight);
|
|
36741
36838
|
const withTrackId = rawEvents.filter((e) => e.detection.trackId);
|
|
36742
36839
|
const objectEvents = withTrackId.filter((e) => e.type !== "object.detected").map((e) => toObjectEvent(e));
|
|
36743
36840
|
const appearanceEvents = withTrackId.filter((e) => e.type === "object.detected").map((e) => toObjectEvent(e, "entered"));
|
|
36744
|
-
objectEvents.push(...zoneRefusalEvents);
|
|
36745
36841
|
return {
|
|
36746
36842
|
deviceId: this.deviceId,
|
|
36747
36843
|
timestamp,
|
|
@@ -39566,6 +39662,62 @@ function tieredLabelColumnData(patch) {
|
|
|
39566
39662
|
};
|
|
39567
39663
|
}
|
|
39568
39664
|
//#endregion
|
|
39665
|
+
//#region src/pipeline-analytics/store/audio-level-density.ts
|
|
39666
|
+
/**
|
|
39667
|
+
* The energy-weighted mean of a set of dBFS values, in dBFS.
|
|
39668
|
+
*
|
|
39669
|
+
* dBFS is logarithmic, so an arithmetic mean of dB numbers is the mean of
|
|
39670
|
+
* nothing physical — it under-weights the loud moment that is the reason to
|
|
39671
|
+
* look at a bar at all. Averaging POWER and converting back is what a meter is
|
|
39672
|
+
* expected to show, and it is the same arithmetic as an RMS over the interval.
|
|
39673
|
+
*
|
|
39674
|
+
* `undefined` for an empty set, and for a set with no finite sample: a bucket
|
|
39675
|
+
* nobody measured must not be reported as silence, and −∞ dBFS is not a number
|
|
39676
|
+
* a bar can paint.
|
|
39677
|
+
*/
|
|
39678
|
+
function energyWeightedMeanDbfs(samples) {
|
|
39679
|
+
let power = 0;
|
|
39680
|
+
let n = 0;
|
|
39681
|
+
for (const dbfs of samples) {
|
|
39682
|
+
if (Number.isNaN(dbfs)) continue;
|
|
39683
|
+
if (dbfs === Number.NEGATIVE_INFINITY) {
|
|
39684
|
+
n += 1;
|
|
39685
|
+
continue;
|
|
39686
|
+
}
|
|
39687
|
+
if (!Number.isFinite(dbfs)) continue;
|
|
39688
|
+
power += 10 ** (dbfs / 10);
|
|
39689
|
+
n += 1;
|
|
39690
|
+
}
|
|
39691
|
+
if (n === 0) return void 0;
|
|
39692
|
+
const mean = power / n;
|
|
39693
|
+
if (mean <= 0) return void 0;
|
|
39694
|
+
return 10 * Math.log10(mean);
|
|
39695
|
+
}
|
|
39696
|
+
/**
|
|
39697
|
+
* Energy-weighted mean dBFS per bucket index, for the buckets that HAVE audio.
|
|
39698
|
+
*
|
|
39699
|
+
* A bucket with no events is absent from the map rather than present at zero:
|
|
39700
|
+
* "nothing was heard" and "nothing was measured" are different claims, and the
|
|
39701
|
+
* lane has to be able to draw nothing.
|
|
39702
|
+
*/
|
|
39703
|
+
function audioDbfsByBucket(events, since, bucketMs) {
|
|
39704
|
+
const out = /* @__PURE__ */ new Map();
|
|
39705
|
+
if (bucketMs <= 0) return out;
|
|
39706
|
+
const byBucket = /* @__PURE__ */ new Map();
|
|
39707
|
+
for (const ev of events) {
|
|
39708
|
+
const idx = Math.floor((ev.timestamp - since) / bucketMs);
|
|
39709
|
+
if (idx < 0) continue;
|
|
39710
|
+
const bin = byBucket.get(idx);
|
|
39711
|
+
if (bin === void 0) byBucket.set(idx, [ev.dbfs]);
|
|
39712
|
+
else bin.push(ev.dbfs);
|
|
39713
|
+
}
|
|
39714
|
+
for (const [idx, samples] of byBucket) {
|
|
39715
|
+
const mean = energyWeightedMeanDbfs(samples);
|
|
39716
|
+
if (mean !== void 0) out.set(idx, mean);
|
|
39717
|
+
}
|
|
39718
|
+
return out;
|
|
39719
|
+
}
|
|
39720
|
+
//#endregion
|
|
39569
39721
|
//#region src/pipeline-analytics/store/event-store.ts
|
|
39570
39722
|
/**
|
|
39571
39723
|
* @durable class=ledger owner=pipeline-analytics
|
|
@@ -40404,10 +40556,11 @@ var EventStore = class {
|
|
|
40404
40556
|
});
|
|
40405
40557
|
return new Map(rows.map((r) => [r.bucket, r.count]));
|
|
40406
40558
|
};
|
|
40407
|
-
const [motionMap, objectMap, audioMap] = await Promise.all([
|
|
40559
|
+
const [motionMap, objectMap, audioMap, audioDbfsMap] = await Promise.all([
|
|
40408
40560
|
run(MOTION_EVENTS_COLLECTION),
|
|
40409
40561
|
run(OBJECT_EVENTS_COLLECTION),
|
|
40410
|
-
run(AUDIO_EVENTS_COLLECTION)
|
|
40562
|
+
run(AUDIO_EVENTS_COLLECTION),
|
|
40563
|
+
this.audioDbfsPerBucket(q)
|
|
40411
40564
|
]);
|
|
40412
40565
|
const buckets = Math.max(0, Math.ceil((q.until - q.since) / q.bucketMs));
|
|
40413
40566
|
const out = [];
|
|
@@ -40416,16 +40569,46 @@ var EventStore = class {
|
|
|
40416
40569
|
const o = objectMap.get(i) ?? 0;
|
|
40417
40570
|
const a = audioMap.get(i) ?? 0;
|
|
40418
40571
|
if (m + o + a === 0) continue;
|
|
40572
|
+
const dbfs = audioDbfsMap.get(i);
|
|
40419
40573
|
out.push({
|
|
40420
40574
|
bucketStart: q.since + i * q.bucketMs,
|
|
40421
40575
|
motion: m,
|
|
40422
40576
|
object: o,
|
|
40423
|
-
audio: a
|
|
40577
|
+
audio: a,
|
|
40578
|
+
...dbfs !== void 0 ? { audioDbfs: dbfs } : {}
|
|
40424
40579
|
});
|
|
40425
40580
|
}
|
|
40426
40581
|
return out;
|
|
40427
40582
|
}
|
|
40428
40583
|
/**
|
|
40584
|
+
* Energy-weighted mean dBFS per bucket index, for the buckets that have
|
|
40585
|
+
* audio. Reads the window's audio events once, `slim`, and folds them —
|
|
40586
|
+
* `histogram` can only count, and a level is not a count.
|
|
40587
|
+
*
|
|
40588
|
+
* A read that fails yields an EMPTY map, so the lane draws no level rather
|
|
40589
|
+
* than a wrong one: the density counts beside it are unaffected, and a
|
|
40590
|
+
* missing bar is the honest rendering of "not measured" (D431).
|
|
40591
|
+
*/
|
|
40592
|
+
async audioDbfsPerBucket(q) {
|
|
40593
|
+
try {
|
|
40594
|
+
return audioDbfsByBucket((await this.queryAudio({
|
|
40595
|
+
deviceId: q.deviceId,
|
|
40596
|
+
since: q.since,
|
|
40597
|
+
until: q.until,
|
|
40598
|
+
projection: "slim"
|
|
40599
|
+
})).map((e) => ({
|
|
40600
|
+
timestamp: e.timestamp,
|
|
40601
|
+
dbfs: e.dbfs
|
|
40602
|
+
})), q.since, q.bucketMs);
|
|
40603
|
+
} catch (err) {
|
|
40604
|
+
this.logger.warn("density: audio level aggregation failed — the lane will draw no level", {
|
|
40605
|
+
tags: { deviceId: q.deviceId },
|
|
40606
|
+
meta: { error: String(err) }
|
|
40607
|
+
});
|
|
40608
|
+
return /* @__PURE__ */ new Map();
|
|
40609
|
+
}
|
|
40610
|
+
}
|
|
40611
|
+
/**
|
|
40429
40612
|
* Age-evict the TRACKLESS event kinds — motion and audio.
|
|
40430
40613
|
*
|
|
40431
40614
|
* **Object events are not here, and that is the point.** An object event is a
|
|
@@ -72762,12 +72945,17 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
72762
72945
|
*/
|
|
72763
72946
|
buildTimelapsePorts(api, stores) {
|
|
72764
72947
|
return {
|
|
72765
|
-
getAvailability: async ({ deviceId, fromMs, toMs }) => {
|
|
72766
|
-
|
|
72948
|
+
getAvailability: async ({ deviceId, fromMs, toMs, profile }) => {
|
|
72949
|
+
const availability = await api.recording.getAvailability.query({
|
|
72767
72950
|
deviceId,
|
|
72768
72951
|
fromMs,
|
|
72769
|
-
toMs
|
|
72770
|
-
|
|
72952
|
+
toMs,
|
|
72953
|
+
...profile !== void 0 ? { profile } : {}
|
|
72954
|
+
});
|
|
72955
|
+
return {
|
|
72956
|
+
ranges: availability.ranges,
|
|
72957
|
+
profilesWithFootage: availability.profilesWithFootage
|
|
72958
|
+
};
|
|
72771
72959
|
},
|
|
72772
72960
|
listTracks: async ({ deviceId, since, until }) => {
|
|
72773
72961
|
return (await stores.trackStore.queryHistorical({
|
|
@@ -77554,6 +77742,24 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
77554
77742
|
}
|
|
77555
77743
|
});
|
|
77556
77744
|
});
|
|
77745
|
+
p.setBirthZoneTrailObserver((trail) => {
|
|
77746
|
+
this.ctx.logger.info("track born carrying zones it crossed before it was a track", {
|
|
77747
|
+
tags: { deviceId },
|
|
77748
|
+
meta: {
|
|
77749
|
+
source,
|
|
77750
|
+
...trail
|
|
77751
|
+
}
|
|
77752
|
+
});
|
|
77753
|
+
});
|
|
77754
|
+
p.setZoneRefusalObserver((observation) => {
|
|
77755
|
+
this.ctx.logger.info("detection refused inside a watched zone — not notified", {
|
|
77756
|
+
tags: { deviceId },
|
|
77757
|
+
meta: {
|
|
77758
|
+
source,
|
|
77759
|
+
...observation
|
|
77760
|
+
}
|
|
77761
|
+
});
|
|
77762
|
+
});
|
|
77557
77763
|
p.setSpawnBesideCoastingObserver((record) => {
|
|
77558
77764
|
this.ctx.logger.info("tracker: new id minted beside a live coasting track", {
|
|
77559
77765
|
tags: { deviceId },
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as VISIT_MERGE_GAP_MS, $t as object, A as NcRulePatchSchema, Bt as CamProfileSchema, C as NC_ALARM_SYSTEM_EVENT_KINDS, D as NC_TAXONOMY, Dt as readDeviceStateFrom, Et as plateGalleryCapability, F as NcSnoozeSchema, Ft as vectorDimFromBase64, G as SCENE_DEFAULT_UNCOVERED_POLICY, Gt as nodePin, H as RetrainStatusSchema, Ht as createEvent, I as NcSnoozeSuppressedSchema, It as videoclipsCapability, J as TIMELAPSE_DENSE_FLOOR_SEC, Jt as array, K as SCENE_DIVERGED, Kt as sleep$1, L as NcSystemEventKindSchema, Lt as zoneAnalyticsCapability, M as NcRuleTargetSchema, Mt as storageOccupancyCapability, N as NcScheduleSchema, Nt as subKindsOf, O as NcConditionDescriptorSchema, Ot as readTimelapseGeneratedAt, P as NcSnoozeInputSchema, Pt as systemEventFilterApplies, Q as TrackSourceSchema, Qt as number, R as NcTaxonomySchema, Rt as errMsg$1, S as MediaFileKindEnum, St as notificationRulesCapability, T as NC_DEFAULT_SNOOZE_MINUTES, Tt as pipelineAnalyticsCapability, Ut as hydrateSchema, V as RECORDING_EXPORT_MAX_READ_BYTES, Vt as DeviceType, W as SCENE_DEFAULT_ANCHOR_THRESHOLD, Wt as isDeviceScopedCap, X as TimelapseRulePatchSchema, Xt as discriminatedUnion, Y as TimelapseRuleInputSchema, Yt as boolean, Z as TimelapseRuleSchema, Zt as literal, _ as LabelAttributionSchema, _t as isDetectionMacroClass, a as CLUSTER_MODEL_SCOPED_STEPS, at as buildEventKindDescriptor, b as MAX_BIRTH_DECISION_RECORDS, bt as kebabToCamel, ct as defineCustomActions, dt as encodeVectorBase64, en as partialRecord, et as addonWidgetsSourceCapability, f as DeclaredDevices, ft as evaluateSensorEdge, g as FailureCounters, h as FULL_IMAGE_BBOX, ht as failureContributionCapability, i as BirthDecisionRecordSchema, in as EventCategory, it as audioModeOf, j as NcRuleSchema, jt as sceneMonitorCapability, k as NcRuleInputSchema, kt as resolveLocationMode, lt as deriveRecordingMode, m as EVENT_PAD_MS, mt as faceGalleryCapability, n as ArchivedDebugNoteSchema, nn as string, nt as assertTimelapseCadences, o as COCO_TO_MACRO, ot as cosineSimilarity$1, p as EVENT_KIND_BY_CAP, pt as evictionPolicyOfLocation, q as SceneMonitorSchema, qt as _enum, r as BaseDevice, rn as unknown, rt as audioMetricsCapability, s as DEFAULT_EVENT_COLOR, st as customAction, t as AUDIO_MACRO_LABELS, tn as record, tt as alarmPanelCapability, u as DETECTION_MACRO_CLASSES, v as MACRO_LABELS, vt as isScheduleActive, w as NC_CONDITION_CATALOG, wt as pickClusterStepModels, xt as mayWriteToLocation, y as MAX_ARCHIVED_DEBUG_NOTES, yt as isSourceCap, z as OpsLogEntrySchema, zt as BaseAddon } from "../dist-
|
|
1
|
+
import { $ as VISIT_MERGE_GAP_MS, $t as object, A as NcRulePatchSchema, Bt as CamProfileSchema, C as NC_ALARM_SYSTEM_EVENT_KINDS, D as NC_TAXONOMY, Dt as readDeviceStateFrom, Et as plateGalleryCapability, F as NcSnoozeSchema, Ft as vectorDimFromBase64, G as SCENE_DEFAULT_UNCOVERED_POLICY, Gt as nodePin, H as RetrainStatusSchema, Ht as createEvent, I as NcSnoozeSuppressedSchema, It as videoclipsCapability, J as TIMELAPSE_DENSE_FLOOR_SEC, Jt as array, K as SCENE_DIVERGED, Kt as sleep$1, L as NcSystemEventKindSchema, Lt as zoneAnalyticsCapability, M as NcRuleTargetSchema, Mt as storageOccupancyCapability, N as NcScheduleSchema, Nt as subKindsOf, O as NcConditionDescriptorSchema, Ot as readTimelapseGeneratedAt, P as NcSnoozeInputSchema, Pt as systemEventFilterApplies, Q as TrackSourceSchema, Qt as number, R as NcTaxonomySchema, Rt as errMsg$1, S as MediaFileKindEnum, St as notificationRulesCapability, T as NC_DEFAULT_SNOOZE_MINUTES, Tt as pipelineAnalyticsCapability, Ut as hydrateSchema, V as RECORDING_EXPORT_MAX_READ_BYTES, Vt as DeviceType, W as SCENE_DEFAULT_ANCHOR_THRESHOLD, Wt as isDeviceScopedCap, X as TimelapseRulePatchSchema, Xt as discriminatedUnion, Y as TimelapseRuleInputSchema, Yt as boolean, Z as TimelapseRuleSchema, Zt as literal, _ as LabelAttributionSchema, _t as isDetectionMacroClass, a as CLUSTER_MODEL_SCOPED_STEPS, at as buildEventKindDescriptor, b as MAX_BIRTH_DECISION_RECORDS, bt as kebabToCamel, ct as defineCustomActions, dt as encodeVectorBase64, en as partialRecord, et as addonWidgetsSourceCapability, f as DeclaredDevices, ft as evaluateSensorEdge, g as FailureCounters, h as FULL_IMAGE_BBOX, ht as failureContributionCapability, i as BirthDecisionRecordSchema, in as EventCategory, it as audioModeOf, j as NcRuleSchema, jt as sceneMonitorCapability, k as NcRuleInputSchema, kt as resolveLocationMode, lt as deriveRecordingMode, m as EVENT_PAD_MS, mt as faceGalleryCapability, n as ArchivedDebugNoteSchema, nn as string, nt as assertTimelapseCadences, o as COCO_TO_MACRO, ot as cosineSimilarity$1, p as EVENT_KIND_BY_CAP, pt as evictionPolicyOfLocation, q as SceneMonitorSchema, qt as _enum, r as BaseDevice, rn as unknown, rt as audioMetricsCapability, s as DEFAULT_EVENT_COLOR, st as customAction, t as AUDIO_MACRO_LABELS, tn as record, tt as alarmPanelCapability, u as DETECTION_MACRO_CLASSES, v as MACRO_LABELS, vt as isScheduleActive, w as NC_CONDITION_CATALOG, wt as pickClusterStepModels, xt as mayWriteToLocation, y as MAX_ARCHIVED_DEBUG_NOTES, yt as isSourceCap, z as OpsLogEntrySchema, zt as BaseAddon } from "../dist-BqkhuC85.mjs";
|
|
2
2
|
import { t as __exportAll } from "../embedding-encoder/index.mjs";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import { promises } from "node:fs";
|
|
@@ -20070,11 +20070,19 @@ var TimelapseRenderer = class {
|
|
|
20070
20070
|
async render(request) {
|
|
20071
20071
|
const { deviceId, window } = request;
|
|
20072
20072
|
const tags = { deviceId };
|
|
20073
|
-
const
|
|
20073
|
+
const first = await this.ports.getAvailability({
|
|
20074
20074
|
deviceId,
|
|
20075
20075
|
fromMs: window.startMs,
|
|
20076
20076
|
toMs: window.endMs
|
|
20077
20077
|
});
|
|
20078
|
+
const cheapest = [...first.profilesWithFootage].sort((a, b) => profileCost(a) - profileCost(b))[0];
|
|
20079
|
+
const describes = first.ranges[0]?.profile;
|
|
20080
|
+
const availability = cheapest !== void 0 && cheapest !== describes ? (await this.ports.getAvailability({
|
|
20081
|
+
deviceId,
|
|
20082
|
+
fromMs: window.startMs,
|
|
20083
|
+
toMs: window.endMs,
|
|
20084
|
+
profile: cheapest
|
|
20085
|
+
})).ranges : first.ranges;
|
|
20078
20086
|
const chosen = pickProfile(availability, window);
|
|
20079
20087
|
if (chosen === null || chosen.coverage.coveredMs === 0) {
|
|
20080
20088
|
this.ports.logger.warn("timelapse skipped: no footage covers the window", {
|
|
@@ -36066,6 +36074,17 @@ function mapObjectStateToTrackState(s) {
|
|
|
36066
36074
|
default: return "new";
|
|
36067
36075
|
}
|
|
36068
36076
|
}
|
|
36077
|
+
/**
|
|
36078
|
+
* How long an identical refusal stays collapsed. A stationary subject is
|
|
36079
|
+
* refused on every analysed frame; at ~2 fps of analysis, 60 s turns a parked
|
|
36080
|
+
* car's 120 lines into one and still reports a subject that comes back later.
|
|
36081
|
+
*/
|
|
36082
|
+
var ZONE_REFUSAL_THROTTLE_MS = 6e4;
|
|
36083
|
+
/** Box quantisation for the collapse key: a subject that moves less than this
|
|
36084
|
+
* is the same sighting for diagnostic purposes. */
|
|
36085
|
+
var ZONE_REFUSAL_BOX_CELL_PX = 64;
|
|
36086
|
+
/** Bound on the collapse map; stale keys are swept when it is exceeded. */
|
|
36087
|
+
var ZONE_REFUSAL_KEY_LIMIT = 512;
|
|
36069
36088
|
var FrameProcessor = class {
|
|
36070
36089
|
deviceId;
|
|
36071
36090
|
/** Detection source this processor handles (`pipeline` | `onboard`).
|
|
@@ -36126,7 +36145,6 @@ var FrameProcessor = class {
|
|
|
36126
36145
|
* filter). Shares the tracker's vocabulary — see {@link SpawnRefusalReason}. */
|
|
36127
36146
|
spawnRefusalObserver = null;
|
|
36128
36147
|
/** Where a collected refusal is forwarded for LOGGING, when anyone asked. */
|
|
36129
|
-
spawnRefusalForward = null;
|
|
36130
36148
|
/**
|
|
36131
36149
|
* The spawn refusals of the frame being processed.
|
|
36132
36150
|
*
|
|
@@ -36137,8 +36155,15 @@ var FrameProcessor = class {
|
|
|
36137
36155
|
* as before?" answered first. See `zone-detection-emits-without-a-track`.
|
|
36138
36156
|
*/
|
|
36139
36157
|
refusalsThisFrame = [];
|
|
36140
|
-
/** Running count of
|
|
36158
|
+
/** Running count of refusals-in-zone SEEN — never the throttled number. */
|
|
36141
36159
|
zoneRefusalEventCount = 0;
|
|
36160
|
+
zoneRefusalObserver = null;
|
|
36161
|
+
/** Track ids whose BIRTH zone history has already been folded in — the fold
|
|
36162
|
+
* happens once, on the frame a track first becomes visible. */
|
|
36163
|
+
birthZonesSeeded = /* @__PURE__ */ new Set();
|
|
36164
|
+
birthZoneTrailObserver = null;
|
|
36165
|
+
/** Last report time per collapsed key, for {@link ZONE_REFUSAL_THROTTLE_MS}. */
|
|
36166
|
+
zoneRefusalLastReport = /* @__PURE__ */ new Map();
|
|
36142
36167
|
zoneRefusalSampler = new SpawnRefusalSampler();
|
|
36143
36168
|
/** Optional stationary-object gate (parked-object suppression). Null until
|
|
36144
36169
|
* the addon wires it via {@link setStationaryGate}. */
|
|
@@ -36239,6 +36264,18 @@ var FrameProcessor = class {
|
|
|
36239
36264
|
this.tracker.setSpawnRefusalObserver(observer);
|
|
36240
36265
|
this.spawnRefusalObserver = observer;
|
|
36241
36266
|
}
|
|
36267
|
+
/**
|
|
36268
|
+
* Where a refused-inside-a-zone detection goes. Diagnostics only: this is
|
|
36269
|
+
* deliberately NOT the event stream, so nothing downstream can alert on a
|
|
36270
|
+
* box the confirmation gate threw away (D426).
|
|
36271
|
+
*/
|
|
36272
|
+
setZoneRefusalObserver(observer) {
|
|
36273
|
+
this.zoneRefusalObserver = observer;
|
|
36274
|
+
}
|
|
36275
|
+
/** Where "this track was born already carrying a zone" is reported. */
|
|
36276
|
+
setBirthZoneTrailObserver(observer) {
|
|
36277
|
+
this.birthZoneTrailObserver = observer;
|
|
36278
|
+
}
|
|
36242
36279
|
setBindObserver(observer) {
|
|
36243
36280
|
this.tracker.setBindObserver(observer);
|
|
36244
36281
|
}
|
|
@@ -36274,40 +36311,73 @@ var FrameProcessor = class {
|
|
|
36274
36311
|
* operator's own detection rules already said the box does not count on this
|
|
36275
36312
|
* camera, and re-admitting it here would overrule a decision they made.
|
|
36276
36313
|
*/
|
|
36277
|
-
|
|
36314
|
+
/**
|
|
36315
|
+
* Report every detection the tracker refused that fell INSIDE a watched
|
|
36316
|
+
* zone. Diagnostics only — see the spec
|
|
36317
|
+
* `zone-refusal-is-observed-never-notified` for the ten hours of production
|
|
36318
|
+
* that decided this: 1473 of 1511 such refusals were one stationary parcel
|
|
36319
|
+
* re-offered every frame, and both notifications this path ever produced
|
|
36320
|
+
* were phantoms the confirmation gate had correctly rejected.
|
|
36321
|
+
*
|
|
36322
|
+
* Drains {@link refusalsThisFrame}. `zone-filtered` is skipped: a box the
|
|
36323
|
+
* zone filter already removed was never in a zone.
|
|
36324
|
+
*/
|
|
36325
|
+
reportRefusedDetectionsInZones(timestamp, frameWidth, frameHeight) {
|
|
36278
36326
|
const refusals = this.refusalsThisFrame;
|
|
36279
36327
|
this.refusalsThisFrame = [];
|
|
36280
|
-
if (refusals.length === 0 || this.zones.length === 0) return
|
|
36281
|
-
const
|
|
36328
|
+
if (refusals.length === 0 || this.zones.length === 0) return;
|
|
36329
|
+
const observer = this.zoneRefusalObserver;
|
|
36330
|
+
if (observer === null) return;
|
|
36282
36331
|
for (const r of refusals) {
|
|
36283
36332
|
if (r.reason === "zone-filtered") continue;
|
|
36284
36333
|
const { memberships } = this.zoneEngine.splitDetectionZones(r.bbox, this.zones, frameWidth, frameHeight, void 0, void 0, void 0, this.zoneMembershipMinOverlap);
|
|
36285
36334
|
if (memberships.length === 0) continue;
|
|
36286
36335
|
this.zoneRefusalEventCount += 1;
|
|
36287
|
-
|
|
36288
|
-
|
|
36289
|
-
kind: "object",
|
|
36336
|
+
if (!this.shouldReportZoneRefusal(r, memberships, timestamp)) continue;
|
|
36337
|
+
observer({
|
|
36290
36338
|
deviceId: this.deviceId,
|
|
36291
36339
|
timestamp,
|
|
36292
|
-
frameId: frame.frameId,
|
|
36293
36340
|
className: r.detClass,
|
|
36294
36341
|
confidence: r.detScore,
|
|
36295
36342
|
bbox: r.bbox,
|
|
36296
|
-
|
|
36297
|
-
|
|
36298
|
-
zones: memberships.map((m) => m.zoneId),
|
|
36299
|
-
zoneOverlaps: memberships.map((m) => ({
|
|
36343
|
+
reason: r.reason,
|
|
36344
|
+
zones: memberships.map((m) => ({
|
|
36300
36345
|
zoneId: m.zoneId,
|
|
36301
36346
|
...m.zoneName === void 0 ? {} : { zoneName: m.zoneName },
|
|
36302
36347
|
overlapPct: Math.round(m.overlap * 1e3) / 10
|
|
36303
36348
|
})),
|
|
36304
|
-
|
|
36305
|
-
});
|
|
36349
|
+
seenSoFar: this.zoneRefusalEventCount
|
|
36350
|
+
});
|
|
36351
|
+
}
|
|
36352
|
+
}
|
|
36353
|
+
/**
|
|
36354
|
+
* Collapse identical refusals. A refused box is refused on EVERY analysed
|
|
36355
|
+
* frame, so a subject that does not leave — the parcel at the door, the
|
|
36356
|
+
* parked car — is an unbounded emitter. The key includes a coarse box, so a
|
|
36357
|
+
* subject that actually moves keeps reporting while a stationary one does
|
|
36358
|
+
* not.
|
|
36359
|
+
*/
|
|
36360
|
+
shouldReportZoneRefusal(r, memberships, timestamp) {
|
|
36361
|
+
const cell = (v) => Math.round(v / ZONE_REFUSAL_BOX_CELL_PX);
|
|
36362
|
+
const key = [
|
|
36363
|
+
r.detClass,
|
|
36364
|
+
r.reason,
|
|
36365
|
+
memberships.map((m) => m.zoneId).join("+"),
|
|
36366
|
+
cell(r.bbox.x),
|
|
36367
|
+
cell(r.bbox.y),
|
|
36368
|
+
cell(r.bbox.w),
|
|
36369
|
+
cell(r.bbox.h)
|
|
36370
|
+
].join("|");
|
|
36371
|
+
const last = this.zoneRefusalLastReport.get(key);
|
|
36372
|
+
if (last !== void 0 && timestamp - last < ZONE_REFUSAL_THROTTLE_MS) return false;
|
|
36373
|
+
this.zoneRefusalLastReport.set(key, timestamp);
|
|
36374
|
+
if (this.zoneRefusalLastReport.size > ZONE_REFUSAL_KEY_LIMIT) {
|
|
36375
|
+
for (const [k, t] of this.zoneRefusalLastReport) if (timestamp - t >= ZONE_REFUSAL_THROTTLE_MS) this.zoneRefusalLastReport.delete(k);
|
|
36306
36376
|
}
|
|
36307
|
-
return
|
|
36377
|
+
return true;
|
|
36308
36378
|
}
|
|
36309
|
-
/** How many zone
|
|
36310
|
-
*
|
|
36379
|
+
/** How many refusals-in-zone this processor has SEEN — the measure that says
|
|
36380
|
+
* whether this path is a trickle or a storm, before any throttling. */
|
|
36311
36381
|
zoneRefusalEventsEmitted() {
|
|
36312
36382
|
return this.zoneRefusalEventCount;
|
|
36313
36383
|
}
|
|
@@ -36541,6 +36611,32 @@ var FrameProcessor = class {
|
|
|
36541
36611
|
if (memberships.length > 0) overlapsByTrack.set(td.trackId, memberships);
|
|
36542
36612
|
if (belowBar.length > 0) rejectedByTrack.set(td.trackId, belowBar);
|
|
36543
36613
|
}
|
|
36614
|
+
for (const td of trackedDetections) {
|
|
36615
|
+
if (this.birthZonesSeeded.has(td.trackId)) continue;
|
|
36616
|
+
this.birthZonesSeeded.add(td.trackId);
|
|
36617
|
+
const trail = td.path;
|
|
36618
|
+
if (trail.length <= 1) continue;
|
|
36619
|
+
const atBirth = zonesByTrack.get(td.trackId) ?? [];
|
|
36620
|
+
const zones = new Set(atBirth);
|
|
36621
|
+
const bornWith = zones.size;
|
|
36622
|
+
for (let i = 0; i < trail.length - 1; i++) {
|
|
36623
|
+
const box = trail[i];
|
|
36624
|
+
if (box === void 0) continue;
|
|
36625
|
+
const { memberships } = this.zoneEngine.splitDetectionZones(box, this.zones, frameWidth, frameHeight, void 0, void 0, void 0, this.zoneMembershipMinOverlap);
|
|
36626
|
+
for (const m of memberships) zones.add(m.zoneId);
|
|
36627
|
+
}
|
|
36628
|
+
if (zones.size === bornWith) continue;
|
|
36629
|
+
const inherited = [...zones].filter((z) => !atBirth.includes(z));
|
|
36630
|
+
zonesByTrack.set(td.trackId, [...zones]);
|
|
36631
|
+
this.birthZoneTrailObserver?.({
|
|
36632
|
+
deviceId: this.deviceId,
|
|
36633
|
+
trackId: td.trackId,
|
|
36634
|
+
className: td.class,
|
|
36635
|
+
measurementsBeforeBirth: trail.length - 1,
|
|
36636
|
+
zonesAtBirth: [...atBirth],
|
|
36637
|
+
inheritedZones: inherited
|
|
36638
|
+
});
|
|
36639
|
+
}
|
|
36544
36640
|
this.lastZoneOverlaps = overlapsByTrack;
|
|
36545
36641
|
this.lastZoneRejections = rejectedByTrack;
|
|
36546
36642
|
for (const td of trackedDetections) this.lastDetectionByTrack.set(td.trackId, td);
|
|
@@ -36613,6 +36709,7 @@ var FrameProcessor = class {
|
|
|
36613
36709
|
for (const state of objectStates) {
|
|
36614
36710
|
if (state.state !== "leaving") continue;
|
|
36615
36711
|
this.lastZonesByTrack.delete(state.trackId);
|
|
36712
|
+
this.birthZonesSeeded.delete(state.trackId);
|
|
36616
36713
|
this.lastDetectionByTrack.delete(state.trackId);
|
|
36617
36714
|
this.reportedRefusalByTrack.delete(state.trackId);
|
|
36618
36715
|
}
|
|
@@ -36712,11 +36809,10 @@ var FrameProcessor = class {
|
|
|
36712
36809
|
frameHeight
|
|
36713
36810
|
};
|
|
36714
36811
|
};
|
|
36715
|
-
|
|
36812
|
+
this.reportRefusedDetectionsInZones(timestamp, frameWidth, frameHeight);
|
|
36716
36813
|
const withTrackId = rawEvents.filter((e) => e.detection.trackId);
|
|
36717
36814
|
const objectEvents = withTrackId.filter((e) => e.type !== "object.detected").map((e) => toObjectEvent(e));
|
|
36718
36815
|
const appearanceEvents = withTrackId.filter((e) => e.type === "object.detected").map((e) => toObjectEvent(e, "entered"));
|
|
36719
|
-
objectEvents.push(...zoneRefusalEvents);
|
|
36720
36816
|
return {
|
|
36721
36817
|
deviceId: this.deviceId,
|
|
36722
36818
|
timestamp,
|
|
@@ -39541,6 +39637,62 @@ function tieredLabelColumnData(patch) {
|
|
|
39541
39637
|
};
|
|
39542
39638
|
}
|
|
39543
39639
|
//#endregion
|
|
39640
|
+
//#region src/pipeline-analytics/store/audio-level-density.ts
|
|
39641
|
+
/**
|
|
39642
|
+
* The energy-weighted mean of a set of dBFS values, in dBFS.
|
|
39643
|
+
*
|
|
39644
|
+
* dBFS is logarithmic, so an arithmetic mean of dB numbers is the mean of
|
|
39645
|
+
* nothing physical — it under-weights the loud moment that is the reason to
|
|
39646
|
+
* look at a bar at all. Averaging POWER and converting back is what a meter is
|
|
39647
|
+
* expected to show, and it is the same arithmetic as an RMS over the interval.
|
|
39648
|
+
*
|
|
39649
|
+
* `undefined` for an empty set, and for a set with no finite sample: a bucket
|
|
39650
|
+
* nobody measured must not be reported as silence, and −∞ dBFS is not a number
|
|
39651
|
+
* a bar can paint.
|
|
39652
|
+
*/
|
|
39653
|
+
function energyWeightedMeanDbfs(samples) {
|
|
39654
|
+
let power = 0;
|
|
39655
|
+
let n = 0;
|
|
39656
|
+
for (const dbfs of samples) {
|
|
39657
|
+
if (Number.isNaN(dbfs)) continue;
|
|
39658
|
+
if (dbfs === Number.NEGATIVE_INFINITY) {
|
|
39659
|
+
n += 1;
|
|
39660
|
+
continue;
|
|
39661
|
+
}
|
|
39662
|
+
if (!Number.isFinite(dbfs)) continue;
|
|
39663
|
+
power += 10 ** (dbfs / 10);
|
|
39664
|
+
n += 1;
|
|
39665
|
+
}
|
|
39666
|
+
if (n === 0) return void 0;
|
|
39667
|
+
const mean = power / n;
|
|
39668
|
+
if (mean <= 0) return void 0;
|
|
39669
|
+
return 10 * Math.log10(mean);
|
|
39670
|
+
}
|
|
39671
|
+
/**
|
|
39672
|
+
* Energy-weighted mean dBFS per bucket index, for the buckets that HAVE audio.
|
|
39673
|
+
*
|
|
39674
|
+
* A bucket with no events is absent from the map rather than present at zero:
|
|
39675
|
+
* "nothing was heard" and "nothing was measured" are different claims, and the
|
|
39676
|
+
* lane has to be able to draw nothing.
|
|
39677
|
+
*/
|
|
39678
|
+
function audioDbfsByBucket(events, since, bucketMs) {
|
|
39679
|
+
const out = /* @__PURE__ */ new Map();
|
|
39680
|
+
if (bucketMs <= 0) return out;
|
|
39681
|
+
const byBucket = /* @__PURE__ */ new Map();
|
|
39682
|
+
for (const ev of events) {
|
|
39683
|
+
const idx = Math.floor((ev.timestamp - since) / bucketMs);
|
|
39684
|
+
if (idx < 0) continue;
|
|
39685
|
+
const bin = byBucket.get(idx);
|
|
39686
|
+
if (bin === void 0) byBucket.set(idx, [ev.dbfs]);
|
|
39687
|
+
else bin.push(ev.dbfs);
|
|
39688
|
+
}
|
|
39689
|
+
for (const [idx, samples] of byBucket) {
|
|
39690
|
+
const mean = energyWeightedMeanDbfs(samples);
|
|
39691
|
+
if (mean !== void 0) out.set(idx, mean);
|
|
39692
|
+
}
|
|
39693
|
+
return out;
|
|
39694
|
+
}
|
|
39695
|
+
//#endregion
|
|
39544
39696
|
//#region src/pipeline-analytics/store/event-store.ts
|
|
39545
39697
|
/**
|
|
39546
39698
|
* @durable class=ledger owner=pipeline-analytics
|
|
@@ -40379,10 +40531,11 @@ var EventStore = class {
|
|
|
40379
40531
|
});
|
|
40380
40532
|
return new Map(rows.map((r) => [r.bucket, r.count]));
|
|
40381
40533
|
};
|
|
40382
|
-
const [motionMap, objectMap, audioMap] = await Promise.all([
|
|
40534
|
+
const [motionMap, objectMap, audioMap, audioDbfsMap] = await Promise.all([
|
|
40383
40535
|
run(MOTION_EVENTS_COLLECTION),
|
|
40384
40536
|
run(OBJECT_EVENTS_COLLECTION),
|
|
40385
|
-
run(AUDIO_EVENTS_COLLECTION)
|
|
40537
|
+
run(AUDIO_EVENTS_COLLECTION),
|
|
40538
|
+
this.audioDbfsPerBucket(q)
|
|
40386
40539
|
]);
|
|
40387
40540
|
const buckets = Math.max(0, Math.ceil((q.until - q.since) / q.bucketMs));
|
|
40388
40541
|
const out = [];
|
|
@@ -40391,16 +40544,46 @@ var EventStore = class {
|
|
|
40391
40544
|
const o = objectMap.get(i) ?? 0;
|
|
40392
40545
|
const a = audioMap.get(i) ?? 0;
|
|
40393
40546
|
if (m + o + a === 0) continue;
|
|
40547
|
+
const dbfs = audioDbfsMap.get(i);
|
|
40394
40548
|
out.push({
|
|
40395
40549
|
bucketStart: q.since + i * q.bucketMs,
|
|
40396
40550
|
motion: m,
|
|
40397
40551
|
object: o,
|
|
40398
|
-
audio: a
|
|
40552
|
+
audio: a,
|
|
40553
|
+
...dbfs !== void 0 ? { audioDbfs: dbfs } : {}
|
|
40399
40554
|
});
|
|
40400
40555
|
}
|
|
40401
40556
|
return out;
|
|
40402
40557
|
}
|
|
40403
40558
|
/**
|
|
40559
|
+
* Energy-weighted mean dBFS per bucket index, for the buckets that have
|
|
40560
|
+
* audio. Reads the window's audio events once, `slim`, and folds them —
|
|
40561
|
+
* `histogram` can only count, and a level is not a count.
|
|
40562
|
+
*
|
|
40563
|
+
* A read that fails yields an EMPTY map, so the lane draws no level rather
|
|
40564
|
+
* than a wrong one: the density counts beside it are unaffected, and a
|
|
40565
|
+
* missing bar is the honest rendering of "not measured" (D431).
|
|
40566
|
+
*/
|
|
40567
|
+
async audioDbfsPerBucket(q) {
|
|
40568
|
+
try {
|
|
40569
|
+
return audioDbfsByBucket((await this.queryAudio({
|
|
40570
|
+
deviceId: q.deviceId,
|
|
40571
|
+
since: q.since,
|
|
40572
|
+
until: q.until,
|
|
40573
|
+
projection: "slim"
|
|
40574
|
+
})).map((e) => ({
|
|
40575
|
+
timestamp: e.timestamp,
|
|
40576
|
+
dbfs: e.dbfs
|
|
40577
|
+
})), q.since, q.bucketMs);
|
|
40578
|
+
} catch (err) {
|
|
40579
|
+
this.logger.warn("density: audio level aggregation failed — the lane will draw no level", {
|
|
40580
|
+
tags: { deviceId: q.deviceId },
|
|
40581
|
+
meta: { error: String(err) }
|
|
40582
|
+
});
|
|
40583
|
+
return /* @__PURE__ */ new Map();
|
|
40584
|
+
}
|
|
40585
|
+
}
|
|
40586
|
+
/**
|
|
40404
40587
|
* Age-evict the TRACKLESS event kinds — motion and audio.
|
|
40405
40588
|
*
|
|
40406
40589
|
* **Object events are not here, and that is the point.** An object event is a
|
|
@@ -72688,12 +72871,17 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
72688
72871
|
*/
|
|
72689
72872
|
buildTimelapsePorts(api, stores) {
|
|
72690
72873
|
return {
|
|
72691
|
-
getAvailability: async ({ deviceId, fromMs, toMs }) => {
|
|
72692
|
-
|
|
72874
|
+
getAvailability: async ({ deviceId, fromMs, toMs, profile }) => {
|
|
72875
|
+
const availability = await api.recording.getAvailability.query({
|
|
72693
72876
|
deviceId,
|
|
72694
72877
|
fromMs,
|
|
72695
|
-
toMs
|
|
72696
|
-
|
|
72878
|
+
toMs,
|
|
72879
|
+
...profile !== void 0 ? { profile } : {}
|
|
72880
|
+
});
|
|
72881
|
+
return {
|
|
72882
|
+
ranges: availability.ranges,
|
|
72883
|
+
profilesWithFootage: availability.profilesWithFootage
|
|
72884
|
+
};
|
|
72697
72885
|
},
|
|
72698
72886
|
listTracks: async ({ deviceId, since, until }) => {
|
|
72699
72887
|
return (await stores.trackStore.queryHistorical({
|
|
@@ -77480,6 +77668,24 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
77480
77668
|
}
|
|
77481
77669
|
});
|
|
77482
77670
|
});
|
|
77671
|
+
p.setBirthZoneTrailObserver((trail) => {
|
|
77672
|
+
this.ctx.logger.info("track born carrying zones it crossed before it was a track", {
|
|
77673
|
+
tags: { deviceId },
|
|
77674
|
+
meta: {
|
|
77675
|
+
source,
|
|
77676
|
+
...trail
|
|
77677
|
+
}
|
|
77678
|
+
});
|
|
77679
|
+
});
|
|
77680
|
+
p.setZoneRefusalObserver((observation) => {
|
|
77681
|
+
this.ctx.logger.info("detection refused inside a watched zone — not notified", {
|
|
77682
|
+
tags: { deviceId },
|
|
77683
|
+
meta: {
|
|
77684
|
+
source,
|
|
77685
|
+
...observation
|
|
77686
|
+
}
|
|
77687
|
+
});
|
|
77688
|
+
});
|
|
77483
77689
|
p.setSpawnBesideCoastingObserver((record) => {
|
|
77484
77690
|
this.ctx.logger.info("tracker: new id minted beside a live coasting track", {
|
|
77485
77691
|
tags: { deviceId },
|
|
@@ -30,7 +30,7 @@ async function d(e) {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
async function f() {
|
|
33
|
-
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-
|
|
33
|
+
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-JPlVJb-8.mjs")).catch((e) => {
|
|
34
34
|
throw l = void 0, e;
|
|
35
35
|
}), l;
|
|
36
36
|
}
|
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.218",
|
|
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",
|