@camstack/addon-post-analysis 1.2.362 → 1.2.364

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.
@@ -64878,6 +64878,22 @@ var TrackResidentState = class {
64878
64878
  size() {
64879
64879
  return this.residents.size;
64880
64880
  }
64881
+ /** Residents whose last frame stamp is older than `olderThanMs` (a resident
64882
+ * never stamped is not listed: it carries no clock to judge). The host
64883
+ * subtracts the ids a live tracker still carries — what remains is a record
64884
+ * nothing will ever close. */
64885
+ idleResidents(olderThanMs) {
64886
+ const idle = [];
64887
+ for (const [trackId, resident] of this.residents) {
64888
+ const at = resident.lastFrameAt;
64889
+ if (at !== void 0 && at < olderThanMs) idle.push({
64890
+ trackId,
64891
+ deviceId: resident.deviceId,
64892
+ lastFrameAt: at
64893
+ });
64894
+ }
64895
+ return idle;
64896
+ }
64881
64897
  trackIdsForDevice(deviceId) {
64882
64898
  const ids = [];
64883
64899
  for (const [trackId, resident] of this.residents) if (resident.deviceId === deviceId) ids.push(trackId);
@@ -64888,7 +64904,7 @@ var TrackResidentState = class {
64888
64904
  observeBestFrame(deviceId, trackId, confidence, timestamp, edgeClear, centerScore, containment, sizeScore, squareFraction, semantic, canvasFill) {
64889
64905
  const id = this.liveId(deviceId, trackId);
64890
64906
  if (id === void 0) return false;
64891
- this.open(deviceId, id);
64907
+ if (this.ensure(deviceId, id, "bestShot", "observeBestFrame") === void 0) return false;
64892
64908
  const isNewBest = this.bestFrameTracker.observe(id, confidence, timestamp, edgeClear, centerScore, containment, sizeScore, squareFraction, semantic, canvasFill);
64893
64909
  if (isNewBest) this.bestShotHolder.delete(id);
64894
64910
  return isNewBest;
@@ -93487,6 +93503,8 @@ function planeRunsStationary(source) {
93487
93503
  */
93488
93504
  var SuppressedBirthRegistry = class {
93489
93505
  byDevice = /* @__PURE__ */ new Map();
93506
+ /** Rejected ids whose per-frame skip has already been reported once. */
93507
+ skipReported = /* @__PURE__ */ new Map();
93490
93508
  /** Record that this birth was rejected; later frames must not re-upsert it. */
93491
93509
  reject(deviceKey, trackId) {
93492
93510
  let ids = this.byDevice.get(deviceKey);
@@ -93500,12 +93518,31 @@ var SuppressedBirthRegistry = class {
93500
93518
  return this.byDevice.get(deviceKey)?.has(trackId) === true;
93501
93519
  }
93502
93520
  /**
93521
+ * True the FIRST time a rejected id's frame work is skipped (so the host logs
93522
+ * one line per id, not one per frame); false afterwards.
93523
+ */
93524
+ firstSkip(deviceKey, trackId) {
93525
+ let ids = this.skipReported.get(deviceKey);
93526
+ if (!ids) {
93527
+ ids = /* @__PURE__ */ new Set();
93528
+ this.skipReported.set(deviceKey, ids);
93529
+ }
93530
+ if (ids.has(trackId)) return false;
93531
+ ids.add(trackId);
93532
+ return true;
93533
+ }
93534
+ /**
93503
93535
  * Forget every rejected id the tracker is no longer carrying.
93504
93536
  *
93505
93537
  * Called once per frame with the ids present THIS frame. An id absent from
93506
93538
  * that set can never be upserted again, so holding it serves nothing.
93507
93539
  */
93508
93540
  retain(deviceKey, currentTrackIds) {
93541
+ const reported = this.skipReported.get(deviceKey);
93542
+ if (reported) {
93543
+ for (const id of reported) if (!currentTrackIds.has(id)) reported.delete(id);
93544
+ if (reported.size === 0) this.skipReported.delete(deviceKey);
93545
+ }
93509
93546
  const ids = this.byDevice.get(deviceKey);
93510
93547
  if (!ids) return;
93511
93548
  for (const id of ids) if (!currentTrackIds.has(id)) ids.delete(id);
@@ -93518,6 +93555,7 @@ var SuppressedBirthRegistry = class {
93518
93555
  /** Drop a device's memory wholesale (device removed / pipeline reset). */
93519
93556
  clearDevice(deviceKey) {
93520
93557
  this.byDevice.delete(deviceKey);
93558
+ this.skipReported.delete(deviceKey);
93521
93559
  }
93522
93560
  };
93523
93561
  //#endregion
@@ -110800,6 +110838,8 @@ var NO_PUBLICATION = {
110800
110838
  };
110801
110839
  /** How often the resident-vs-live-track heartbeat line is written. */
110802
110840
  var RESIDENT_HEARTBEAT_INTERVAL_MS = 5 * 6e4;
110841
+ /** A resident idle this long that nothing carries is an orphan. */
110842
+ var RESIDENT_ORPHAN_IDLE_MS = 2 * 6e4;
110803
110843
  /**
110804
110844
  * A tRPC-shaped router or procedure reached by DYNAMIC name off `ctx.api`.
110805
110845
  *
@@ -115362,13 +115402,29 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
115362
115402
  this.residentHeartbeatAt = now;
115363
115403
  let liveTracks = 0;
115364
115404
  for (const ids of this.lastActiveTrackIds.values()) liveTracks += ids.size;
115405
+ const orphans = this.orphanResidents(now);
115365
115406
  const mem = process.memoryUsage();
115366
115407
  this.ctx.logger.info("analytics resident heartbeat", { meta: {
115367
115408
  residents: this.residents.size(),
115368
115409
  liveTracks,
115410
+ orphans: orphans.length,
115411
+ orphanSamples: orphans.slice(0, 3).map((o) => ({
115412
+ trackId: o.trackId,
115413
+ deviceId: o.deviceId
115414
+ })),
115369
115415
  externalNonBufferMb: Math.max(0, Math.round((mem.external - mem.arrayBuffers) / 1048576))
115370
115416
  } });
115371
115417
  }
115418
+ /** Residents neither active in the TrackStore nor carried by the tracker
115419
+ * (`lastActiveTrackIds`) whose last frame is older than the orphan window:
115420
+ * a record no sweep can ever close. Healthy value: 0. */
115421
+ orphanResidents(now) {
115422
+ return this.residents.idleResidents(now - RESIDENT_ORPHAN_IDLE_MS).filter((r) => {
115423
+ if (this.trackStore?.peekActive(r.trackId) !== null) return false;
115424
+ for (const ids of this.lastActiveTrackIds.values()) if (ids.has(r.trackId)) return false;
115425
+ return true;
115426
+ });
115427
+ }
115372
115428
  /** Periodic sweeps — TTL promotion + retention/stationary aging. */
115373
115429
  startSweepTimers() {
115374
115430
  this.ttlSweepTimer = setInterval(() => {
@@ -116489,7 +116545,10 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
116489
116545
  }
116490
116546
  if (result.rawTrackedDetections.length > 0) {
116491
116547
  const { minHits } = await this.resolveDeviceDetectionSensitivitySettings(deviceId);
116492
- for (const rt of result.rawTrackedDetections) if (rt.trackAge >= minHits) this.residents.markConfirmed(deviceId, rt.trackId);
116548
+ for (const rt of result.rawTrackedDetections) {
116549
+ if (this.suppressedBirths.isRejected(key, rt.trackId)) continue;
116550
+ if (rt.trackAge >= minHits) this.residents.markConfirmed(deviceId, rt.trackId);
116551
+ }
116493
116552
  }
116494
116553
  const log = this.ctx.logger.withTags({ deviceId });
116495
116554
  const prevIds = this.lastActiveTrackIds.get(key) ?? /* @__PURE__ */ new Set();
@@ -116505,6 +116564,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
116505
116564
  positionsCount: positionsCountById.get(id) ?? 1
116506
116565
  });
116507
116566
  if (this.startedBirths.hasStarted(key, id)) continue;
116567
+ if (this.suppressedBirths.isRejected(key, id)) continue;
116508
116568
  if (appearance === "resurrection") {
116509
116569
  log.info("track resumed", {
116510
116570
  tags: { deviceId },
@@ -116528,6 +116588,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
116528
116588
  }
116529
116589
  for (const id of this.deferredBirths.deferredIds(key)) {
116530
116590
  if (bornCandidates.some((c) => c.id === id)) continue;
116591
+ if (this.suppressedBirths.isRejected(key, id)) continue;
116531
116592
  const t = result.tracked.find((x) => x.trackId === id);
116532
116593
  if (t) bornCandidates.push({
116533
116594
  id,
@@ -116589,6 +116650,19 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
116589
116650
  } });
116590
116651
  continue;
116591
116652
  }
116653
+ if (this.suppressedBirths.isRejected(key, id)) {
116654
+ this.deferredBirths.resolve(key, id);
116655
+ this.birthEvidence.resolve(key, id);
116656
+ log.info("late birth verdict ignored — the birth was already suppressed", {
116657
+ tags: { deviceId },
116658
+ meta: {
116659
+ trackId: id,
116660
+ className: t.className,
116661
+ verdict: outcome.confirmed.has(id) ? "confirmed" : "suppressed"
116662
+ }
116663
+ });
116664
+ continue;
116665
+ }
116592
116666
  this.recordBirthDecision({
116593
116667
  deviceId,
116594
116668
  trackId: id,
@@ -116775,6 +116849,25 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
116775
116849
  this.deferredBirths.retain(key, currentTrackIds);
116776
116850
  this.birthEvidence.retain(key, currentTrackIds);
116777
116851
  this.lastActiveTrackIds.set(key, currentTrackIds);
116852
+ const rejectedThisFrame = /* @__PURE__ */ new Set();
116853
+ for (const t of result.tracked) {
116854
+ if (!this.suppressedBirths.isRejected(key, t.trackId)) continue;
116855
+ rejectedThisFrame.add(t.trackId);
116856
+ if (this.suppressedBirths.firstSkip(key, t.trackId)) log.info("frame work skipped — the track birth was suppressed", {
116857
+ tags: { deviceId },
116858
+ meta: {
116859
+ trackId: t.trackId,
116860
+ className: t.className
116861
+ }
116862
+ });
116863
+ }
116864
+ if (rejectedThisFrame.size > 0) result = {
116865
+ ...result,
116866
+ tracked: result.tracked.filter((t) => !rejectedThisFrame.has(t.trackId)),
116867
+ rawTrackedDetections: result.rawTrackedDetections.filter((rt) => !rejectedThisFrame.has(rt.trackId)),
116868
+ objectEvents: result.objectEvents.filter((e) => e.trackId === void 0 || !rejectedThisFrame.has(e.trackId)),
116869
+ appearanceEvents: result.appearanceEvents.filter((e) => e.trackId === void 0 || !rejectedThisFrame.has(e.trackId))
116870
+ };
116778
116871
  if (currentTrackIds.size > 0) this.lastTrackActivityMs.set(deviceId, Date.now());
116779
116872
  const stationarySettings = this.stationarySettingsFromCache(deviceId);
116780
116873
  const packageSettings = this.packageDropSettingsFromCache(deviceId);
@@ -64834,6 +64834,22 @@ var TrackResidentState = class {
64834
64834
  size() {
64835
64835
  return this.residents.size;
64836
64836
  }
64837
+ /** Residents whose last frame stamp is older than `olderThanMs` (a resident
64838
+ * never stamped is not listed: it carries no clock to judge). The host
64839
+ * subtracts the ids a live tracker still carries — what remains is a record
64840
+ * nothing will ever close. */
64841
+ idleResidents(olderThanMs) {
64842
+ const idle = [];
64843
+ for (const [trackId, resident] of this.residents) {
64844
+ const at = resident.lastFrameAt;
64845
+ if (at !== void 0 && at < olderThanMs) idle.push({
64846
+ trackId,
64847
+ deviceId: resident.deviceId,
64848
+ lastFrameAt: at
64849
+ });
64850
+ }
64851
+ return idle;
64852
+ }
64837
64853
  trackIdsForDevice(deviceId) {
64838
64854
  const ids = [];
64839
64855
  for (const [trackId, resident] of this.residents) if (resident.deviceId === deviceId) ids.push(trackId);
@@ -64844,7 +64860,7 @@ var TrackResidentState = class {
64844
64860
  observeBestFrame(deviceId, trackId, confidence, timestamp, edgeClear, centerScore, containment, sizeScore, squareFraction, semantic, canvasFill) {
64845
64861
  const id = this.liveId(deviceId, trackId);
64846
64862
  if (id === void 0) return false;
64847
- this.open(deviceId, id);
64863
+ if (this.ensure(deviceId, id, "bestShot", "observeBestFrame") === void 0) return false;
64848
64864
  const isNewBest = this.bestFrameTracker.observe(id, confidence, timestamp, edgeClear, centerScore, containment, sizeScore, squareFraction, semantic, canvasFill);
64849
64865
  if (isNewBest) this.bestShotHolder.delete(id);
64850
64866
  return isNewBest;
@@ -93392,6 +93408,8 @@ function planeRunsStationary(source) {
93392
93408
  */
93393
93409
  var SuppressedBirthRegistry = class {
93394
93410
  byDevice = /* @__PURE__ */ new Map();
93411
+ /** Rejected ids whose per-frame skip has already been reported once. */
93412
+ skipReported = /* @__PURE__ */ new Map();
93395
93413
  /** Record that this birth was rejected; later frames must not re-upsert it. */
93396
93414
  reject(deviceKey, trackId) {
93397
93415
  let ids = this.byDevice.get(deviceKey);
@@ -93405,12 +93423,31 @@ var SuppressedBirthRegistry = class {
93405
93423
  return this.byDevice.get(deviceKey)?.has(trackId) === true;
93406
93424
  }
93407
93425
  /**
93426
+ * True the FIRST time a rejected id's frame work is skipped (so the host logs
93427
+ * one line per id, not one per frame); false afterwards.
93428
+ */
93429
+ firstSkip(deviceKey, trackId) {
93430
+ let ids = this.skipReported.get(deviceKey);
93431
+ if (!ids) {
93432
+ ids = /* @__PURE__ */ new Set();
93433
+ this.skipReported.set(deviceKey, ids);
93434
+ }
93435
+ if (ids.has(trackId)) return false;
93436
+ ids.add(trackId);
93437
+ return true;
93438
+ }
93439
+ /**
93408
93440
  * Forget every rejected id the tracker is no longer carrying.
93409
93441
  *
93410
93442
  * Called once per frame with the ids present THIS frame. An id absent from
93411
93443
  * that set can never be upserted again, so holding it serves nothing.
93412
93444
  */
93413
93445
  retain(deviceKey, currentTrackIds) {
93446
+ const reported = this.skipReported.get(deviceKey);
93447
+ if (reported) {
93448
+ for (const id of reported) if (!currentTrackIds.has(id)) reported.delete(id);
93449
+ if (reported.size === 0) this.skipReported.delete(deviceKey);
93450
+ }
93414
93451
  const ids = this.byDevice.get(deviceKey);
93415
93452
  if (!ids) return;
93416
93453
  for (const id of ids) if (!currentTrackIds.has(id)) ids.delete(id);
@@ -93423,6 +93460,7 @@ var SuppressedBirthRegistry = class {
93423
93460
  /** Drop a device's memory wholesale (device removed / pipeline reset). */
93424
93461
  clearDevice(deviceKey) {
93425
93462
  this.byDevice.delete(deviceKey);
93463
+ this.skipReported.delete(deviceKey);
93426
93464
  }
93427
93465
  };
93428
93466
  //#endregion
@@ -110705,6 +110743,8 @@ var NO_PUBLICATION = {
110705
110743
  };
110706
110744
  /** How often the resident-vs-live-track heartbeat line is written. */
110707
110745
  var RESIDENT_HEARTBEAT_INTERVAL_MS = 5 * 6e4;
110746
+ /** A resident idle this long that nothing carries is an orphan. */
110747
+ var RESIDENT_ORPHAN_IDLE_MS = 2 * 6e4;
110708
110748
  /**
110709
110749
  * A tRPC-shaped router or procedure reached by DYNAMIC name off `ctx.api`.
110710
110750
  *
@@ -115267,13 +115307,29 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
115267
115307
  this.residentHeartbeatAt = now;
115268
115308
  let liveTracks = 0;
115269
115309
  for (const ids of this.lastActiveTrackIds.values()) liveTracks += ids.size;
115310
+ const orphans = this.orphanResidents(now);
115270
115311
  const mem = process.memoryUsage();
115271
115312
  this.ctx.logger.info("analytics resident heartbeat", { meta: {
115272
115313
  residents: this.residents.size(),
115273
115314
  liveTracks,
115315
+ orphans: orphans.length,
115316
+ orphanSamples: orphans.slice(0, 3).map((o) => ({
115317
+ trackId: o.trackId,
115318
+ deviceId: o.deviceId
115319
+ })),
115274
115320
  externalNonBufferMb: Math.max(0, Math.round((mem.external - mem.arrayBuffers) / 1048576))
115275
115321
  } });
115276
115322
  }
115323
+ /** Residents neither active in the TrackStore nor carried by the tracker
115324
+ * (`lastActiveTrackIds`) whose last frame is older than the orphan window:
115325
+ * a record no sweep can ever close. Healthy value: 0. */
115326
+ orphanResidents(now) {
115327
+ return this.residents.idleResidents(now - RESIDENT_ORPHAN_IDLE_MS).filter((r) => {
115328
+ if (this.trackStore?.peekActive(r.trackId) !== null) return false;
115329
+ for (const ids of this.lastActiveTrackIds.values()) if (ids.has(r.trackId)) return false;
115330
+ return true;
115331
+ });
115332
+ }
115277
115333
  /** Periodic sweeps — TTL promotion + retention/stationary aging. */
115278
115334
  startSweepTimers() {
115279
115335
  this.ttlSweepTimer = setInterval(() => {
@@ -116394,7 +116450,10 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
116394
116450
  }
116395
116451
  if (result.rawTrackedDetections.length > 0) {
116396
116452
  const { minHits } = await this.resolveDeviceDetectionSensitivitySettings(deviceId);
116397
- for (const rt of result.rawTrackedDetections) if (rt.trackAge >= minHits) this.residents.markConfirmed(deviceId, rt.trackId);
116453
+ for (const rt of result.rawTrackedDetections) {
116454
+ if (this.suppressedBirths.isRejected(key, rt.trackId)) continue;
116455
+ if (rt.trackAge >= minHits) this.residents.markConfirmed(deviceId, rt.trackId);
116456
+ }
116398
116457
  }
116399
116458
  const log = this.ctx.logger.withTags({ deviceId });
116400
116459
  const prevIds = this.lastActiveTrackIds.get(key) ?? /* @__PURE__ */ new Set();
@@ -116410,6 +116469,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
116410
116469
  positionsCount: positionsCountById.get(id) ?? 1
116411
116470
  });
116412
116471
  if (this.startedBirths.hasStarted(key, id)) continue;
116472
+ if (this.suppressedBirths.isRejected(key, id)) continue;
116413
116473
  if (appearance === "resurrection") {
116414
116474
  log.info("track resumed", {
116415
116475
  tags: { deviceId },
@@ -116433,6 +116493,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
116433
116493
  }
116434
116494
  for (const id of this.deferredBirths.deferredIds(key)) {
116435
116495
  if (bornCandidates.some((c) => c.id === id)) continue;
116496
+ if (this.suppressedBirths.isRejected(key, id)) continue;
116436
116497
  const t = result.tracked.find((x) => x.trackId === id);
116437
116498
  if (t) bornCandidates.push({
116438
116499
  id,
@@ -116494,6 +116555,19 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
116494
116555
  } });
116495
116556
  continue;
116496
116557
  }
116558
+ if (this.suppressedBirths.isRejected(key, id)) {
116559
+ this.deferredBirths.resolve(key, id);
116560
+ this.birthEvidence.resolve(key, id);
116561
+ log.info("late birth verdict ignored — the birth was already suppressed", {
116562
+ tags: { deviceId },
116563
+ meta: {
116564
+ trackId: id,
116565
+ className: t.className,
116566
+ verdict: outcome.confirmed.has(id) ? "confirmed" : "suppressed"
116567
+ }
116568
+ });
116569
+ continue;
116570
+ }
116497
116571
  this.recordBirthDecision({
116498
116572
  deviceId,
116499
116573
  trackId: id,
@@ -116680,6 +116754,25 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
116680
116754
  this.deferredBirths.retain(key, currentTrackIds);
116681
116755
  this.birthEvidence.retain(key, currentTrackIds);
116682
116756
  this.lastActiveTrackIds.set(key, currentTrackIds);
116757
+ const rejectedThisFrame = /* @__PURE__ */ new Set();
116758
+ for (const t of result.tracked) {
116759
+ if (!this.suppressedBirths.isRejected(key, t.trackId)) continue;
116760
+ rejectedThisFrame.add(t.trackId);
116761
+ if (this.suppressedBirths.firstSkip(key, t.trackId)) log.info("frame work skipped — the track birth was suppressed", {
116762
+ tags: { deviceId },
116763
+ meta: {
116764
+ trackId: t.trackId,
116765
+ className: t.className
116766
+ }
116767
+ });
116768
+ }
116769
+ if (rejectedThisFrame.size > 0) result = {
116770
+ ...result,
116771
+ tracked: result.tracked.filter((t) => !rejectedThisFrame.has(t.trackId)),
116772
+ rawTrackedDetections: result.rawTrackedDetections.filter((rt) => !rejectedThisFrame.has(rt.trackId)),
116773
+ objectEvents: result.objectEvents.filter((e) => e.trackId === void 0 || !rejectedThisFrame.has(e.trackId)),
116774
+ appearanceEvents: result.appearanceEvents.filter((e) => e.trackId === void 0 || !rejectedThisFrame.has(e.trackId))
116775
+ };
116683
116776
  if (currentTrackIds.size > 0) this.lastTrackActivityMs.set(deviceId, Date.now());
116684
116777
  const stationarySettings = this.stationarySettingsFromCache(deviceId);
116685
116778
  const packageSettings = this.packageDropSettingsFromCache(deviceId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-post-analysis",
3
- "version": "1.2.362",
3
+ "version": "1.2.364",
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",