@camstack/addon-post-analysis 1.2.80 → 1.2.81

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.
@@ -11176,6 +11176,8 @@ var QueryFilterSchema = object({
11176
11176
  where: record(string(), unknown()).optional(),
11177
11177
  whereIn: record(string(), array(unknown())).optional(),
11178
11178
  whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
11179
+ /** NULL-safe exclusion: matches rows whose field is NULL OR != the value. */
11180
+ whereNot: record(string(), unknown()).optional(),
11179
11181
  orderBy: object({
11180
11182
  field: string(),
11181
11183
  direction: _enum(["asc", "desc"])
@@ -11195,7 +11197,8 @@ var QueryFilterSchema = object({
11195
11197
  var MutationFilterSchema = object({
11196
11198
  where: record(string(), unknown()).optional(),
11197
11199
  whereIn: record(string(), array(unknown())).optional(),
11198
- whereBetween: record(string(), tuple([unknown(), unknown()])).optional()
11200
+ whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
11201
+ whereNot: record(string(), unknown()).optional()
11199
11202
  });
11200
11203
  /** A single stored record: `{ id, data }`. */
11201
11204
  var SettingsRecordSchema = object({
@@ -17824,7 +17827,10 @@ var RecentTracksQueryInput = object({
17824
17827
  * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
17825
17828
  cursor: string().optional(),
17826
17829
  /** See {@link TrackProjectionSchema}. Default `full`. */
17827
- projection: TrackProjectionSchema.optional()
17830
+ projection: TrackProjectionSchema.optional(),
17831
+ /** Include stationary-promoted rows (parked objects). Default false: the
17832
+ * feed lists passages; parking records live on the stationary registry. */
17833
+ includeStationary: boolean().optional()
17828
17834
  });
17829
17835
  var RecentTracksPageSchema = object({
17830
17836
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -18056,7 +18062,11 @@ var pipelineAnalyticsCapability = {
18056
18062
  zone: TrackZoneFilterSchema.optional(),
18057
18063
  /** See {@link TrackProjectionSchema}. Default `full` (backward
18058
18064
  * compatible — omitting the field keeps today's exact behaviour). */
18059
- projection: TrackProjectionSchema.optional()
18065
+ projection: TrackProjectionSchema.optional(),
18066
+ /** Include stationary-promoted rows (parked objects handed to the
18067
+ * stationary registry). Default false: the timeline lists passages,
18068
+ * not parking records (operator decision, 2026-08-15). */
18069
+ includeStationary: boolean().optional()
18060
18070
  }), array(TrackSchema).readonly()),
18061
18071
  /**
18062
18072
  * Batched cluster-wide track listing — ONE call for the events page /
@@ -18547,7 +18557,8 @@ var pipelineAnalyticsCapability = {
18547
18557
  }),
18548
18558
  getEventMedia: method(object({
18549
18559
  eventId: string(),
18550
- kind: MediaFileKindEnum.optional()
18560
+ kind: MediaFileKindEnum.optional(),
18561
+ deviceId: number()
18551
18562
  }), array(MediaFileSchema).readonly()),
18552
18563
  /** All media rows owned by a track. `kinds` narrows to a kind subset so a
18553
18564
  * client can fetch the SMALL display variants on open and pull the
@@ -18555,7 +18566,8 @@ var pipelineAnalyticsCapability = {
18555
18566
  * Absent ⇒ every kind (back-compat). */
18556
18567
  getTrackMedia: method(object({
18557
18568
  trackId: string(),
18558
- kinds: array(MediaFileKindEnum).optional()
18569
+ kinds: array(MediaFileKindEnum).optional(),
18570
+ deviceId: number()
18559
18571
  }), array(MediaFileSchema).readonly()),
18560
18572
  /**
18561
18573
  * What media a track HAS, without any of it.
@@ -18572,7 +18584,10 @@ var pipelineAnalyticsCapability = {
18572
18584
  * tell a full-resolution variant existed — and the affordance that opens it
18573
18585
  * would silently disappear.
18574
18586
  */
18575
- listTrackMedia: method(object({ trackId: string() }), array(MediaFileInfoSchema).readonly()),
18587
+ listTrackMedia: method(object({
18588
+ trackId: string(),
18589
+ deviceId: number()
18590
+ }), array(MediaFileInfoSchema).readonly()),
18576
18591
  /**
18577
18592
  * Search object events by text query using CLIP cosine similarity.
18578
18593
  * Encodes `text` via the `embedding-encoder` cap, queries the
@@ -37427,6 +37442,11 @@ Object.freeze({
37427
37442
  form: "single",
37428
37443
  optional: false
37429
37444
  }],
37445
+ "pipelineAnalytics.getEventMedia": [{
37446
+ name: "deviceId",
37447
+ form: "single",
37448
+ optional: false
37449
+ }],
37430
37450
  "pipelineAnalytics.getKeyEvents": [{
37431
37451
  name: "deviceId",
37432
37452
  form: "single",
@@ -37457,6 +37477,11 @@ Object.freeze({
37457
37477
  form: "single",
37458
37478
  optional: false
37459
37479
  }],
37480
+ "pipelineAnalytics.getTrackMedia": [{
37481
+ name: "deviceId",
37482
+ form: "single",
37483
+ optional: false
37484
+ }],
37460
37485
  "pipelineAnalytics.getTrainingExportSummary": [{
37461
37486
  name: "deviceIds",
37462
37487
  form: "array",
@@ -37492,6 +37517,11 @@ Object.freeze({
37492
37517
  form: "array",
37493
37518
  optional: true
37494
37519
  }],
37520
+ "pipelineAnalytics.listTrackMedia": [{
37521
+ name: "deviceId",
37522
+ form: "single",
37523
+ optional: false
37524
+ }],
37495
37525
  "pipelineAnalytics.listTracks": [{
37496
37526
  name: "deviceId",
37497
37527
  form: "single",
@@ -37932,6 +37962,12 @@ Object.freeze({
37932
37962
  form: "single",
37933
37963
  optional: false
37934
37964
  }],
37965
+ "snapshot.getSnapshotLinks": [{
37966
+ name: "targets",
37967
+ form: "object-array",
37968
+ optional: false,
37969
+ itemField: "deviceId"
37970
+ }],
37935
37971
  "snapshot.getSnapshotOverview": [{
37936
37972
  name: "deviceIds",
37937
37973
  form: "array",
@@ -11207,6 +11207,8 @@ var QueryFilterSchema = object({
11207
11207
  where: record(string(), unknown()).optional(),
11208
11208
  whereIn: record(string(), array(unknown())).optional(),
11209
11209
  whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
11210
+ /** NULL-safe exclusion: matches rows whose field is NULL OR != the value. */
11211
+ whereNot: record(string(), unknown()).optional(),
11210
11212
  orderBy: object({
11211
11213
  field: string(),
11212
11214
  direction: _enum(["asc", "desc"])
@@ -11226,7 +11228,8 @@ var QueryFilterSchema = object({
11226
11228
  var MutationFilterSchema = object({
11227
11229
  where: record(string(), unknown()).optional(),
11228
11230
  whereIn: record(string(), array(unknown())).optional(),
11229
- whereBetween: record(string(), tuple([unknown(), unknown()])).optional()
11231
+ whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
11232
+ whereNot: record(string(), unknown()).optional()
11230
11233
  });
11231
11234
  /** A single stored record: `{ id, data }`. */
11232
11235
  var SettingsRecordSchema = object({
@@ -17855,7 +17858,10 @@ var RecentTracksQueryInput = object({
17855
17858
  * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
17856
17859
  cursor: string().optional(),
17857
17860
  /** See {@link TrackProjectionSchema}. Default `full`. */
17858
- projection: TrackProjectionSchema.optional()
17861
+ projection: TrackProjectionSchema.optional(),
17862
+ /** Include stationary-promoted rows (parked objects). Default false: the
17863
+ * feed lists passages; parking records live on the stationary registry. */
17864
+ includeStationary: boolean().optional()
17859
17865
  });
17860
17866
  var RecentTracksPageSchema = object({
17861
17867
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -18087,7 +18093,11 @@ var pipelineAnalyticsCapability = {
18087
18093
  zone: TrackZoneFilterSchema.optional(),
18088
18094
  /** See {@link TrackProjectionSchema}. Default `full` (backward
18089
18095
  * compatible — omitting the field keeps today's exact behaviour). */
18090
- projection: TrackProjectionSchema.optional()
18096
+ projection: TrackProjectionSchema.optional(),
18097
+ /** Include stationary-promoted rows (parked objects handed to the
18098
+ * stationary registry). Default false: the timeline lists passages,
18099
+ * not parking records (operator decision, 2026-08-15). */
18100
+ includeStationary: boolean().optional()
18091
18101
  }), array(TrackSchema).readonly()),
18092
18102
  /**
18093
18103
  * Batched cluster-wide track listing — ONE call for the events page /
@@ -18578,7 +18588,8 @@ var pipelineAnalyticsCapability = {
18578
18588
  }),
18579
18589
  getEventMedia: method(object({
18580
18590
  eventId: string(),
18581
- kind: MediaFileKindEnum.optional()
18591
+ kind: MediaFileKindEnum.optional(),
18592
+ deviceId: number()
18582
18593
  }), array(MediaFileSchema).readonly()),
18583
18594
  /** All media rows owned by a track. `kinds` narrows to a kind subset so a
18584
18595
  * client can fetch the SMALL display variants on open and pull the
@@ -18586,7 +18597,8 @@ var pipelineAnalyticsCapability = {
18586
18597
  * Absent ⇒ every kind (back-compat). */
18587
18598
  getTrackMedia: method(object({
18588
18599
  trackId: string(),
18589
- kinds: array(MediaFileKindEnum).optional()
18600
+ kinds: array(MediaFileKindEnum).optional(),
18601
+ deviceId: number()
18590
18602
  }), array(MediaFileSchema).readonly()),
18591
18603
  /**
18592
18604
  * What media a track HAS, without any of it.
@@ -18603,7 +18615,10 @@ var pipelineAnalyticsCapability = {
18603
18615
  * tell a full-resolution variant existed — and the affordance that opens it
18604
18616
  * would silently disappear.
18605
18617
  */
18606
- listTrackMedia: method(object({ trackId: string() }), array(MediaFileInfoSchema).readonly()),
18618
+ listTrackMedia: method(object({
18619
+ trackId: string(),
18620
+ deviceId: number()
18621
+ }), array(MediaFileInfoSchema).readonly()),
18607
18622
  /**
18608
18623
  * Search object events by text query using CLIP cosine similarity.
18609
18624
  * Encodes `text` via the `embedding-encoder` cap, queries the
@@ -37458,6 +37473,11 @@ Object.freeze({
37458
37473
  form: "single",
37459
37474
  optional: false
37460
37475
  }],
37476
+ "pipelineAnalytics.getEventMedia": [{
37477
+ name: "deviceId",
37478
+ form: "single",
37479
+ optional: false
37480
+ }],
37461
37481
  "pipelineAnalytics.getKeyEvents": [{
37462
37482
  name: "deviceId",
37463
37483
  form: "single",
@@ -37488,6 +37508,11 @@ Object.freeze({
37488
37508
  form: "single",
37489
37509
  optional: false
37490
37510
  }],
37511
+ "pipelineAnalytics.getTrackMedia": [{
37512
+ name: "deviceId",
37513
+ form: "single",
37514
+ optional: false
37515
+ }],
37491
37516
  "pipelineAnalytics.getTrainingExportSummary": [{
37492
37517
  name: "deviceIds",
37493
37518
  form: "array",
@@ -37523,6 +37548,11 @@ Object.freeze({
37523
37548
  form: "array",
37524
37549
  optional: true
37525
37550
  }],
37551
+ "pipelineAnalytics.listTrackMedia": [{
37552
+ name: "deviceId",
37553
+ form: "single",
37554
+ optional: false
37555
+ }],
37526
37556
  "pipelineAnalytics.listTracks": [{
37527
37557
  name: "deviceId",
37528
37558
  form: "single",
@@ -37963,6 +37993,12 @@ Object.freeze({
37963
37993
  form: "single",
37964
37994
  optional: false
37965
37995
  }],
37996
+ "snapshot.getSnapshotLinks": [{
37997
+ name: "targets",
37998
+ form: "object-array",
37999
+ optional: false,
38000
+ itemField: "deviceId"
38001
+ }],
37966
38002
  "snapshot.getSnapshotOverview": [{
37967
38003
  name: "deviceIds",
37968
38004
  form: "array",
@@ -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-BTbbBBpS.js");
5
+ const require_dist = require("../dist-XdSS4Y-z.js");
6
6
  let node_fs = require("node:fs");
7
7
  let node_fs$1 = require_dist.__toESM(node_fs, 1);
8
8
  node_fs = require_dist.__toESM(node_fs);
@@ -1,4 +1,4 @@
1
- import { $ as hfModelUrl, X as embeddingEncoderCapability, mt as BaseAddon } from "../dist-D9Z7czrz.mjs";
1
+ import { $ as hfModelUrl, X as embeddingEncoderCapability, mt as BaseAddon } from "../dist-CeJ9gG8F.mjs";
2
2
  import { createRequire } from "node:module";
3
3
  import * as fs from "node:fs";
4
4
  import "node:fs";
@@ -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.19",
6
+ version: "1.2.20",
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.78",
21
+ version: "1.2.80",
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.54",
36
+ version: "1.2.55",
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.78",
39
+ version: "1.2.80",
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.19",
48
+ version: "1.2.20",
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.54",
84
+ version: "1.2.55",
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-BTbbBBpS.js");
5
+ const require_dist = require("../dist-XdSS4Y-z.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");
@@ -29050,7 +29050,12 @@ var AnalyticsQueryFacade = class {
29050
29050
  return named ?? track;
29051
29051
  }
29052
29052
  async listTracks(input) {
29053
- return this.withZoneNames(await this.deps.trackStore()?.queryHistorical(input) ?? []);
29053
+ const store = this.deps.trackStore();
29054
+ if (!store) return [];
29055
+ return this.withZoneNames(await store.queryHistorical({
29056
+ ...input,
29057
+ excludeStationaryPromotions: input.includeStationary !== true
29058
+ }));
29054
29059
  }
29055
29060
  /**
29056
29061
  * Batched cluster-wide track listing — one merged (`lastSeen` DESC,
@@ -29060,7 +29065,10 @@ var AnalyticsQueryFacade = class {
29060
29065
  * indexed page + k-way merge and the cursor encoding.
29061
29066
  */
29062
29067
  async listRecentTracks(input) {
29063
- const page = await this.deps.trackStore()?.queryRecent(input) ?? {
29068
+ const page = await this.deps.trackStore()?.queryRecent({
29069
+ ...input,
29070
+ excludeStationaryPromotions: input.includeStationary !== true
29071
+ }) ?? {
29064
29072
  tracks: [],
29065
29073
  nextCursor: null
29066
29074
  };
@@ -29092,11 +29100,11 @@ var AnalyticsQueryFacade = class {
29092
29100
  return this.withMediaUrl(events);
29093
29101
  }
29094
29102
  async getEventMedia(input) {
29095
- const all = await (this.deps.mediaStore()?.listByOwner("event", input.eventId) ?? Promise.resolve([]));
29103
+ const all = await (this.deps.mediaStore()?.listByOwner("event", input.eventId, input.deviceId) ?? Promise.resolve([]));
29096
29104
  return input.kind ? all.filter((m) => m.kind === input.kind) : all;
29097
29105
  }
29098
29106
  async getTrackMedia(input) {
29099
- const all = await (this.deps.mediaStore()?.listByOwner("track", input.trackId) ?? Promise.resolve([]));
29107
+ const all = await (this.deps.mediaStore()?.listByOwner("track", input.trackId, input.deviceId) ?? Promise.resolve([]));
29100
29108
  const kinds = input.kinds;
29101
29109
  return kinds && kinds.length > 0 ? all.filter((m) => kinds.includes(m.kind)) : all;
29102
29110
  }
@@ -29110,7 +29118,7 @@ var AnalyticsQueryFacade = class {
29110
29118
  * ~10 MB of string allocated, to answer a question the index already holds.
29111
29119
  */
29112
29120
  async listTrackMedia(input) {
29113
- return this.deps.mediaStore()?.listInfoByOwner("track", input.trackId) ?? Promise.resolve([]);
29121
+ return this.deps.mediaStore()?.listInfoByOwner("track", input.trackId, input.deviceId) ?? Promise.resolve([]);
29114
29122
  }
29115
29123
  async pruneEventsBefore(input) {
29116
29124
  const eventStore = this.deps.eventStore();
@@ -34551,13 +34559,23 @@ var MediaStore = class {
34551
34559
  } });
34552
34560
  }
34553
34561
  }
34554
- async listByOwner(ownerKind, ownerId) {
34562
+ /**
34563
+ * `deviceId` is the AUTHORIZATION filter: every media row is stamped with
34564
+ * its device at write time, so scoping the query by it makes a media read
34565
+ * for a device the caller cannot name return nothing — the DB itself
34566
+ * enforces the boundary, no post-hoc ownership check to forget.
34567
+ */
34568
+ async listByOwner(ownerKind, ownerId, deviceId) {
34555
34569
  const rows = await this.store.query.query({
34556
34570
  collection: MEDIA_COLLECTION,
34557
34571
  filter: {
34558
- where: {
34572
+ where: deviceId === void 0 ? {
34559
34573
  ownerKind,
34560
34574
  ownerId
34575
+ } : {
34576
+ ownerKind,
34577
+ ownerId,
34578
+ deviceId
34561
34579
  },
34562
34580
  orderBy: {
34563
34581
  field: "timestamp",
@@ -34608,13 +34626,17 @@ var MediaStore = class {
34608
34626
  * crops), and retention deletes it later — a manifest frozen at close would
34609
34627
  * advertise tiles that 404 and hide ones that exist.
34610
34628
  */
34611
- async listInfoByOwner(ownerKind, ownerId) {
34629
+ async listInfoByOwner(ownerKind, ownerId, deviceId) {
34612
34630
  return (await this.store.query.query({
34613
34631
  collection: MEDIA_COLLECTION,
34614
34632
  filter: {
34615
- where: {
34633
+ where: deviceId === void 0 ? {
34616
34634
  ownerKind,
34617
34635
  ownerId
34636
+ } : {
34637
+ ownerKind,
34638
+ ownerId,
34639
+ deviceId
34618
34640
  },
34619
34641
  orderBy: {
34620
34642
  field: "timestamp",
@@ -41082,11 +41104,12 @@ function isAfterCursor(row, cursor) {
41082
41104
  * `'stationary-promotion'` is the only member and means: the tracker decided
41083
41105
  * this subject had PARKED, handed it to the stationary registry, and the row
41084
41106
  * was written so its media, events and vector keep an owner (D161). It is not a
41085
- * quality signal and it never hides the track — a parked car is still a track,
41086
- * still on the events page, still swept by the same retention. It exists so a
41087
- * consumer that wants PASSAGES can say so exactly, instead of re-deriving
41088
- * "did it move" from a trajectory downstream and getting it wrong on the
41089
- * subject that stood still for one honest reason.
41107
+ * quality signal — and since 2026-08-15 it is NOT on the default read path:
41108
+ * the parked row exists to own media and serve the stationary registry, while
41109
+ * the events feed and the per-camera timeline exclude it by default
41110
+ * (`excludeStationaryPromotions` — operator decision: a parked object updates
41111
+ * the stationary record, it is not a passage). Retention and forensics read
41112
+ * the store WITHOUT the exclusion, so the row keeps its D161 cascade.
41090
41113
  *
41091
41114
  * An ABSENT value means "expired normally, or written before this marker
41092
41115
  * existed". Never read an absence as a positive — same rule as `hasFace`.
@@ -42268,11 +42291,13 @@ var TrackStore = class {
42268
42291
  async queryHistorical(params) {
42269
42292
  const limit = params.limit ?? 50;
42270
42293
  const timeBetween = params.since !== void 0 || params.until !== void 0 ? { firstSeen: [params.since ?? 0, params.until ?? Date.now()] } : {};
42294
+ const stationaryExclusion = params.excludeStationaryPromotions === true ? { whereNot: { closedReason: "stationary-promotion" } } : {};
42271
42295
  if (params.zone === void 0) return (await this.store.query.query({
42272
42296
  collection: TRACKS_COLLECTION,
42273
42297
  filter: {
42274
42298
  where: { deviceId: params.deviceId },
42275
42299
  ...Object.keys(timeBetween).length > 0 ? { whereBetween: timeBetween } : {},
42300
+ ...stationaryExclusion,
42276
42301
  orderBy: {
42277
42302
  field: "firstSeen",
42278
42303
  direction: "desc"
@@ -42286,6 +42311,7 @@ var TrackStore = class {
42286
42311
  collection: TRACKS_COLLECTION,
42287
42312
  filter: {
42288
42313
  where: { deviceId: params.deviceId },
42314
+ ...stationaryExclusion,
42289
42315
  whereBetween: {
42290
42316
  ...timeBetween,
42291
42317
  envMinX: [-1e6, bounds.maxX],
@@ -42304,6 +42330,7 @@ var TrackStore = class {
42304
42330
  collection: TRACKS_COLLECTION,
42305
42331
  filter: {
42306
42332
  where: { deviceId: params.deviceId },
42333
+ ...stationaryExclusion,
42307
42334
  ...Object.keys(timeBetween).length > 0 ? { whereBetween: timeBetween } : {},
42308
42335
  orderBy: {
42309
42336
  field: "firstSeen",
@@ -42386,10 +42413,12 @@ var TrackStore = class {
42386
42413
  tracks: [],
42387
42414
  nextCursor: null
42388
42415
  };
42416
+ const stationaryExclusion = params.excludeStationaryPromotions === true ? { whereNot: { closedReason: "stationary-promotion" } } : {};
42389
42417
  const fetchRange = async (deviceId, range, pageLimit) => this.store.query.query({
42390
42418
  collection: TRACKS_COLLECTION,
42391
42419
  filter: {
42392
42420
  where: { deviceId },
42421
+ ...stationaryExclusion,
42393
42422
  whereBetween: { lastSeen: range },
42394
42423
  orderBy: {
42395
42424
  field: "lastSeen",
@@ -1,4 +1,4 @@
1
- import { At as string, B as addonWidgetsSourceCapability, C as NcSnoozeSchema, Ct as boolean, D as OpsLogEntrySchema, Dt as object, E as NcTaxonomySchema, Et as number, F as TIMELAPSE_DENSE_FLOOR_SEC, G as buildEventKindDescriptor, H as assertTimelapseCadences, I as TimelapseRuleInputSchema, J as defineCustomActions, K as cosineSimilarity$1, L as TimelapseRulePatchSchema, M as SCENE_DEFAULT_UNCOVERED_POLICY, Mt as EventCategory, N as SCENE_DIVERGED, O as RECORDING_EXPORT_MAX_READ_BYTES, Ot as partialRecord, P as SceneMonitorSchema, Q as faceGalleryCapability, R as TimelapseRuleSchema, S as NcSnoozeInputSchema, St as array, T as NcSystemEventKindSchema, Tt as literal, U as audioMetricsCapability, V as alarmPanelCapability, W as audioModeOf, Y as deriveRecordingMode, Z as encodeVectorBase64, _ as NcRuleInputSchema, _t as hydrateSchema, at as plateGalleryCapability, b as NcRuleTargetSchema, bt as sleep, c as EVENT_PAD_MS, ct as sceneMonitorCapability, d as NC_ALARM_SYSTEM_EVENT_KINDS, dt as videoclipsCapability, et as isDetectionMacroClass, f as NC_CONDITION_CATALOG, ft as zoneAnalyticsCapability, g as NcConditionDescriptorSchema, gt as createEvent, h as NC_TAXONOMY, ht as DeviceType, i as DETECTION_MACRO_CLASSES, it as pipelineAnalyticsCapability, j as SCENE_DEFAULT_ANCHOR_THRESHOLD, jt as unknown, k as RetrainStatusSchema, kt as record, l as LabelAttributionSchema, lt as subKindsOf, mt as BaseAddon, n as DEFAULT_EVENT_COLOR, nt as kebabToCamel, o as DeclaredDevices, ot as readDeviceStateFrom, p as NC_DEFAULT_SNOOZE_MINUTES, pt as errMsg, q as customAction, rt as notificationRulesCapability, s as EVENT_KIND_BY_CAP, st as readTimelapseGeneratedAt, t as BaseDevice, tt as isScheduleActive, u as MACRO_LABELS, ut as vectorDimFromBase64, v as NcRulePatchSchema, vt as isDeviceScopedCap, w as NcSnoozeSuppressedSchema, wt as discriminatedUnion, x as NcScheduleSchema, xt as _enum, y as NcRuleSchema, yt as nodePin, z as TrackSourceSchema } from "../dist-D9Z7czrz.mjs";
1
+ import { At as string, B as addonWidgetsSourceCapability, C as NcSnoozeSchema, Ct as boolean, D as OpsLogEntrySchema, Dt as object, E as NcTaxonomySchema, Et as number, F as TIMELAPSE_DENSE_FLOOR_SEC, G as buildEventKindDescriptor, H as assertTimelapseCadences, I as TimelapseRuleInputSchema, J as defineCustomActions, K as cosineSimilarity$1, L as TimelapseRulePatchSchema, M as SCENE_DEFAULT_UNCOVERED_POLICY, Mt as EventCategory, N as SCENE_DIVERGED, O as RECORDING_EXPORT_MAX_READ_BYTES, Ot as partialRecord, P as SceneMonitorSchema, Q as faceGalleryCapability, R as TimelapseRuleSchema, S as NcSnoozeInputSchema, St as array, T as NcSystemEventKindSchema, Tt as literal, U as audioMetricsCapability, V as alarmPanelCapability, W as audioModeOf, Y as deriveRecordingMode, Z as encodeVectorBase64, _ as NcRuleInputSchema, _t as hydrateSchema, at as plateGalleryCapability, b as NcRuleTargetSchema, bt as sleep, c as EVENT_PAD_MS, ct as sceneMonitorCapability, d as NC_ALARM_SYSTEM_EVENT_KINDS, dt as videoclipsCapability, et as isDetectionMacroClass, f as NC_CONDITION_CATALOG, ft as zoneAnalyticsCapability, g as NcConditionDescriptorSchema, gt as createEvent, h as NC_TAXONOMY, ht as DeviceType, i as DETECTION_MACRO_CLASSES, it as pipelineAnalyticsCapability, j as SCENE_DEFAULT_ANCHOR_THRESHOLD, jt as unknown, k as RetrainStatusSchema, kt as record, l as LabelAttributionSchema, lt as subKindsOf, mt as BaseAddon, n as DEFAULT_EVENT_COLOR, nt as kebabToCamel, o as DeclaredDevices, ot as readDeviceStateFrom, p as NC_DEFAULT_SNOOZE_MINUTES, pt as errMsg, q as customAction, rt as notificationRulesCapability, s as EVENT_KIND_BY_CAP, st as readTimelapseGeneratedAt, t as BaseDevice, tt as isScheduleActive, u as MACRO_LABELS, ut as vectorDimFromBase64, v as NcRulePatchSchema, vt as isDeviceScopedCap, w as NcSnoozeSuppressedSchema, wt as discriminatedUnion, x as NcScheduleSchema, xt as _enum, y as NcRuleSchema, yt as nodePin, z as TrackSourceSchema } from "../dist-CeJ9gG8F.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";
@@ -28992,7 +28992,12 @@ var AnalyticsQueryFacade = class {
28992
28992
  return named ?? track;
28993
28993
  }
28994
28994
  async listTracks(input) {
28995
- return this.withZoneNames(await this.deps.trackStore()?.queryHistorical(input) ?? []);
28995
+ const store = this.deps.trackStore();
28996
+ if (!store) return [];
28997
+ return this.withZoneNames(await store.queryHistorical({
28998
+ ...input,
28999
+ excludeStationaryPromotions: input.includeStationary !== true
29000
+ }));
28996
29001
  }
28997
29002
  /**
28998
29003
  * Batched cluster-wide track listing — one merged (`lastSeen` DESC,
@@ -29002,7 +29007,10 @@ var AnalyticsQueryFacade = class {
29002
29007
  * indexed page + k-way merge and the cursor encoding.
29003
29008
  */
29004
29009
  async listRecentTracks(input) {
29005
- const page = await this.deps.trackStore()?.queryRecent(input) ?? {
29010
+ const page = await this.deps.trackStore()?.queryRecent({
29011
+ ...input,
29012
+ excludeStationaryPromotions: input.includeStationary !== true
29013
+ }) ?? {
29006
29014
  tracks: [],
29007
29015
  nextCursor: null
29008
29016
  };
@@ -29034,11 +29042,11 @@ var AnalyticsQueryFacade = class {
29034
29042
  return this.withMediaUrl(events);
29035
29043
  }
29036
29044
  async getEventMedia(input) {
29037
- const all = await (this.deps.mediaStore()?.listByOwner("event", input.eventId) ?? Promise.resolve([]));
29045
+ const all = await (this.deps.mediaStore()?.listByOwner("event", input.eventId, input.deviceId) ?? Promise.resolve([]));
29038
29046
  return input.kind ? all.filter((m) => m.kind === input.kind) : all;
29039
29047
  }
29040
29048
  async getTrackMedia(input) {
29041
- const all = await (this.deps.mediaStore()?.listByOwner("track", input.trackId) ?? Promise.resolve([]));
29049
+ const all = await (this.deps.mediaStore()?.listByOwner("track", input.trackId, input.deviceId) ?? Promise.resolve([]));
29042
29050
  const kinds = input.kinds;
29043
29051
  return kinds && kinds.length > 0 ? all.filter((m) => kinds.includes(m.kind)) : all;
29044
29052
  }
@@ -29052,7 +29060,7 @@ var AnalyticsQueryFacade = class {
29052
29060
  * ~10 MB of string allocated, to answer a question the index already holds.
29053
29061
  */
29054
29062
  async listTrackMedia(input) {
29055
- return this.deps.mediaStore()?.listInfoByOwner("track", input.trackId) ?? Promise.resolve([]);
29063
+ return this.deps.mediaStore()?.listInfoByOwner("track", input.trackId, input.deviceId) ?? Promise.resolve([]);
29056
29064
  }
29057
29065
  async pruneEventsBefore(input) {
29058
29066
  const eventStore = this.deps.eventStore();
@@ -34493,13 +34501,23 @@ var MediaStore = class {
34493
34501
  } });
34494
34502
  }
34495
34503
  }
34496
- async listByOwner(ownerKind, ownerId) {
34504
+ /**
34505
+ * `deviceId` is the AUTHORIZATION filter: every media row is stamped with
34506
+ * its device at write time, so scoping the query by it makes a media read
34507
+ * for a device the caller cannot name return nothing — the DB itself
34508
+ * enforces the boundary, no post-hoc ownership check to forget.
34509
+ */
34510
+ async listByOwner(ownerKind, ownerId, deviceId) {
34497
34511
  const rows = await this.store.query.query({
34498
34512
  collection: MEDIA_COLLECTION,
34499
34513
  filter: {
34500
- where: {
34514
+ where: deviceId === void 0 ? {
34501
34515
  ownerKind,
34502
34516
  ownerId
34517
+ } : {
34518
+ ownerKind,
34519
+ ownerId,
34520
+ deviceId
34503
34521
  },
34504
34522
  orderBy: {
34505
34523
  field: "timestamp",
@@ -34550,13 +34568,17 @@ var MediaStore = class {
34550
34568
  * crops), and retention deletes it later — a manifest frozen at close would
34551
34569
  * advertise tiles that 404 and hide ones that exist.
34552
34570
  */
34553
- async listInfoByOwner(ownerKind, ownerId) {
34571
+ async listInfoByOwner(ownerKind, ownerId, deviceId) {
34554
34572
  return (await this.store.query.query({
34555
34573
  collection: MEDIA_COLLECTION,
34556
34574
  filter: {
34557
- where: {
34575
+ where: deviceId === void 0 ? {
34558
34576
  ownerKind,
34559
34577
  ownerId
34578
+ } : {
34579
+ ownerKind,
34580
+ ownerId,
34581
+ deviceId
34560
34582
  },
34561
34583
  orderBy: {
34562
34584
  field: "timestamp",
@@ -41024,11 +41046,12 @@ function isAfterCursor(row, cursor) {
41024
41046
  * `'stationary-promotion'` is the only member and means: the tracker decided
41025
41047
  * this subject had PARKED, handed it to the stationary registry, and the row
41026
41048
  * was written so its media, events and vector keep an owner (D161). It is not a
41027
- * quality signal and it never hides the track — a parked car is still a track,
41028
- * still on the events page, still swept by the same retention. It exists so a
41029
- * consumer that wants PASSAGES can say so exactly, instead of re-deriving
41030
- * "did it move" from a trajectory downstream and getting it wrong on the
41031
- * subject that stood still for one honest reason.
41049
+ * quality signal — and since 2026-08-15 it is NOT on the default read path:
41050
+ * the parked row exists to own media and serve the stationary registry, while
41051
+ * the events feed and the per-camera timeline exclude it by default
41052
+ * (`excludeStationaryPromotions` — operator decision: a parked object updates
41053
+ * the stationary record, it is not a passage). Retention and forensics read
41054
+ * the store WITHOUT the exclusion, so the row keeps its D161 cascade.
41032
41055
  *
41033
41056
  * An ABSENT value means "expired normally, or written before this marker
41034
41057
  * existed". Never read an absence as a positive — same rule as `hasFace`.
@@ -42210,11 +42233,13 @@ var TrackStore = class {
42210
42233
  async queryHistorical(params) {
42211
42234
  const limit = params.limit ?? 50;
42212
42235
  const timeBetween = params.since !== void 0 || params.until !== void 0 ? { firstSeen: [params.since ?? 0, params.until ?? Date.now()] } : {};
42236
+ const stationaryExclusion = params.excludeStationaryPromotions === true ? { whereNot: { closedReason: "stationary-promotion" } } : {};
42213
42237
  if (params.zone === void 0) return (await this.store.query.query({
42214
42238
  collection: TRACKS_COLLECTION,
42215
42239
  filter: {
42216
42240
  where: { deviceId: params.deviceId },
42217
42241
  ...Object.keys(timeBetween).length > 0 ? { whereBetween: timeBetween } : {},
42242
+ ...stationaryExclusion,
42218
42243
  orderBy: {
42219
42244
  field: "firstSeen",
42220
42245
  direction: "desc"
@@ -42228,6 +42253,7 @@ var TrackStore = class {
42228
42253
  collection: TRACKS_COLLECTION,
42229
42254
  filter: {
42230
42255
  where: { deviceId: params.deviceId },
42256
+ ...stationaryExclusion,
42231
42257
  whereBetween: {
42232
42258
  ...timeBetween,
42233
42259
  envMinX: [-1e6, bounds.maxX],
@@ -42246,6 +42272,7 @@ var TrackStore = class {
42246
42272
  collection: TRACKS_COLLECTION,
42247
42273
  filter: {
42248
42274
  where: { deviceId: params.deviceId },
42275
+ ...stationaryExclusion,
42249
42276
  ...Object.keys(timeBetween).length > 0 ? { whereBetween: timeBetween } : {},
42250
42277
  orderBy: {
42251
42278
  field: "firstSeen",
@@ -42328,10 +42355,12 @@ var TrackStore = class {
42328
42355
  tracks: [],
42329
42356
  nextCursor: null
42330
42357
  };
42358
+ const stationaryExclusion = params.excludeStationaryPromotions === true ? { whereNot: { closedReason: "stationary-promotion" } } : {};
42331
42359
  const fetchRange = async (deviceId, range, pageLimit) => this.store.query.query({
42332
42360
  collection: TRACKS_COLLECTION,
42333
42361
  filter: {
42334
42362
  where: { deviceId },
42363
+ ...stationaryExclusion,
42335
42364
  whereBetween: { lastSeen: range },
42336
42365
  orderBy: {
42337
42366
  field: "lastSeen",
@@ -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-DHm8hb9G.mjs")).catch((e) => {
33
+ return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-DyVoMMtp.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.80",
3
+ "version": "1.2.81",
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",