@camstack/addon-decoder-nodeav 1.1.13 → 1.1.15

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.
Files changed (3) hide show
  1. package/dist/index.js +202 -13
  2. package/dist/index.mjs +202 -13
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  let node_crypto = require("node:crypto");
6
- //#region ../types/dist/event-category-CFZs3jI4.mjs
6
+ //#region ../types/dist/event-category-H4AVePnn.mjs
7
7
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
8
8
  EventCategory["SystemBoot"] = "system.boot";
9
9
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -153,6 +153,9 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
153
153
  EventCategory["RecordingSegmentWritten"] = "recording.segment.written";
154
154
  EventCategory["RecordingPolicyFallback"] = "recording.policy.fallback";
155
155
  EventCategory["RecordingRetentionCompleted"] = "recording.retention.completed";
156
+ /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
157
+ * thumb is a scrub gap the recorder's keyframe backfill covers. */
158
+ EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
156
159
  EventCategory["DetectionEvent"] = "detection.event";
157
160
  EventCategory["SessionTrackNew"] = "session.track.new";
158
161
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -7234,6 +7237,24 @@ var RecordingRetentionSchema = object({
7234
7237
  maxSizeGb: number().min(0).optional()
7235
7238
  });
7236
7239
  /**
7240
+ * Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
7241
+ * sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
7242
+ * previews at. Five graduated steps; absent on a config = `standard` (the
7243
+ * shipped default, matching `sheet-geometry`/`sheet-composer`).
7244
+ *
7245
+ * Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
7246
+ * Each window's index sidecar carries its own tile dims, so a camera whose
7247
+ * preset changed over time renders every historical window at the dims it was
7248
+ * written with.
7249
+ */
7250
+ var ScrubThumbnailPresetSchema = _enum([
7251
+ "minimal",
7252
+ "low",
7253
+ "standard",
7254
+ "high",
7255
+ "max"
7256
+ ]);
7257
+ /**
7237
7258
  * The full per-camera recording intent — the wire shape of a RecordingTarget.
7238
7259
  *
7239
7260
  * `mode` is the authoritative storage choice; `schedule`/`triggers`/`pre`/`post`
@@ -7270,7 +7291,14 @@ var RecordingConfigSchema = object({
7270
7291
  * derived into bands once via `migrateConfigToBands`.
7271
7292
  */
7272
7293
  bands: array(RecordingBandSchema).optional(),
7273
- retention: RecordingRetentionSchema.optional()
7294
+ retention: RecordingRetentionSchema.optional(),
7295
+ /**
7296
+ * Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
7297
+ * timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
7298
+ * windows only — existing sheets are immutable, and each window's index
7299
+ * carries its own tile dims so mixed-preset history renders correctly.
7300
+ */
7301
+ scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7274
7302
  });
7275
7303
  /**
7276
7304
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
@@ -9829,7 +9857,7 @@ object({
9829
9857
  deviceId: number(),
9830
9858
  timestamp: number()
9831
9859
  });
9832
- DeviceType.Button;
9860
+ DeviceType.Button, DeviceType.Camera;
9833
9861
  /**
9834
9862
  * Enum-state sensor — a string value picked from a finite option set.
9835
9863
  * Drives HA `sensor` entries with `state_class: enum` (HVAC action
@@ -14329,6 +14357,14 @@ var ConfigEntrySchema = object({
14329
14357
  value: unknown(),
14330
14358
  description: string().optional()
14331
14359
  });
14360
+ var DeviceLinkModeSchema = _enum(["auto", "manual"]);
14361
+ /** One resolved linked device — the compact projection consumers need. */
14362
+ var LinkedDeviceSchema = object({
14363
+ deviceId: number(),
14364
+ name: string(),
14365
+ location: string().nullable(),
14366
+ features: array(string())
14367
+ });
14332
14368
  var SavedDeviceRowSchema = object({
14333
14369
  /** Numeric id reserved at allocateDeviceId time. */
14334
14370
  id: number(),
@@ -14550,7 +14586,10 @@ method(object({
14550
14586
  }), _void(), {
14551
14587
  kind: "mutation",
14552
14588
  auth: "admin"
14553
- }), method(object({ addonId: string() }), array(SavedDeviceRowSchema)), method(object({ addonId: string().optional() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
14589
+ }), method(object({ addonId: string() }), array(SavedDeviceRowSchema)), method(object({ addonId: string().optional() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
14590
+ mode: DeviceLinkModeSchema,
14591
+ devices: array(LinkedDeviceSchema)
14592
+ })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
14554
14593
  deviceId: number(),
14555
14594
  values: record(string(), unknown())
14556
14595
  }), object({ success: literal(true) }), {
@@ -15819,6 +15858,61 @@ var EventKindSchema = _enum([
15819
15858
  "object",
15820
15859
  "audio"
15821
15860
  ]);
15861
+ /**
15862
+ * Spatial filter for `listTracks` — the rect + polygon variants of the shared
15863
+ * MaskShape vocabulary (see `mask-shape.ts`). Coordinates are NORMALIZED 0..1
15864
+ * of the camera frame (top-left origin), matching the drawing-plane editor.
15865
+ */
15866
+ var TrackZoneFilterSchema = discriminatedUnion("kind", [MaskRectShapeSchema, MaskPolygonShapeSchema]);
15867
+ /** Closed icon vocabulary so clients render a known glyph per kind. */
15868
+ var EventKindIconSchema = _enum([
15869
+ "motion",
15870
+ "audio",
15871
+ "person",
15872
+ "vehicle",
15873
+ "animal",
15874
+ "door",
15875
+ "pir",
15876
+ "smoke",
15877
+ "water",
15878
+ "button",
15879
+ "generic"
15880
+ ]);
15881
+ var EventKindCategorySchema = _enum([
15882
+ "motion",
15883
+ "audio",
15884
+ "detection",
15885
+ "sensor",
15886
+ "custom"
15887
+ ]);
15888
+ var EventKindDescriptorSchema = object({
15889
+ /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
15890
+ kind: string(),
15891
+ label: string(),
15892
+ /** Hex color for timeline/legend rendering. */
15893
+ color: string(),
15894
+ icon: EventKindIconSchema,
15895
+ category: EventKindCategorySchema,
15896
+ /** Which cap + device contributes this kind. For built-ins the camera
15897
+ * itself; for sensor kinds the LINKED source device. */
15898
+ source: object({
15899
+ capName: string(),
15900
+ deviceId: number()
15901
+ })
15902
+ });
15903
+ var SensorEventSchema = object({
15904
+ id: string(),
15905
+ /** The CAMERA the event is attributed to (a sensor linked to N cameras
15906
+ * yields N rows, one per camera). */
15907
+ deviceId: number(),
15908
+ /** The linked sensor device whose state changed. */
15909
+ sourceDeviceId: number(),
15910
+ /** Event kind id — matches an `EventKindDescriptor.kind`. */
15911
+ kind: string(),
15912
+ /** Snapshot of the sensor cap's runtime-state slice at the change. */
15913
+ value: record(string(), unknown()).nullable(),
15914
+ timestamp: number()
15915
+ });
15822
15916
  var TrackPositionSchema = object({
15823
15917
  x: number(),
15824
15918
  y: number(),
@@ -15832,6 +15926,30 @@ var TrackSnapshotSchema = object({
15832
15926
  mediaKey: string()
15833
15927
  });
15834
15928
  /**
15929
+ * Normalized 0..1 trajectory envelope (min/max over every position bbox,
15930
+ * divided by the track's detection-frame dims), computed at persist time.
15931
+ * Absent when the frame dims were unknown when the track was persisted
15932
+ * (legacy rows / dims-less sources) and on active (in-RAM) tracks.
15933
+ */
15934
+ var TrackEnvelopeSchema = object({
15935
+ minX: number(),
15936
+ minY: number(),
15937
+ maxX: number(),
15938
+ maxY: number()
15939
+ });
15940
+ /**
15941
+ * Row projection for track list queries. `full` (default) returns the
15942
+ * complete Track including the frame-rate `positions[]` history and the
15943
+ * `snapshots[]` references — megabytes across a page of tracks. `slim`
15944
+ * keeps every scalar the list surfaces actually render (ids, class(es),
15945
+ * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
15946
+ * zonesVisited, bestEventId, envelope) and returns `positions` /
15947
+ * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
15948
+ * `getTrack`. Mirrors the event-store `projection` convention
15949
+ * (`getObjectEvents` et al.).
15950
+ */
15951
+ var TrackProjectionSchema = _enum(["full", "slim"]);
15952
+ /**
15835
15953
  * One audio-classification label heard on the track's camera while the
15836
15954
  * track was alive, aggregated per label. An "episode" is one persisted
15837
15955
  * audio event (the confident-classification path: score ≥ the device's
@@ -15882,7 +16000,11 @@ var TrackSchema = object({
15882
16000
  /** Audio-classification labels heard on the camera during the track's
15883
16001
  * life (score ≥ device `classificationMinScore`), aggregated per label.
15884
16002
  * Absent on legacy rows / tracks with no confident audio. */
15885
- audioLabels: array(TrackAudioLabelSchema).readonly().optional()
16003
+ audioLabels: array(TrackAudioLabelSchema).readonly().optional(),
16004
+ /** Normalized 0..1 trajectory envelope (see {@link TrackEnvelopeSchema}).
16005
+ * Populated from the persisted envelope columns on historical reads;
16006
+ * absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
16007
+ envelope: TrackEnvelopeSchema.optional()
15886
16008
  });
15887
16009
  var BaseEventFields = {
15888
16010
  id: string(),
@@ -15992,11 +16114,13 @@ var MediaFileSchema = object({
15992
16114
  sizeBytes: number(),
15993
16115
  timestamp: number()
15994
16116
  });
16117
+ var DEFAULT_EVENT_QUERY_LIMIT = 1e3;
16118
+ var MAX_EVENT_QUERY_LIMIT = 5e3;
15995
16119
  var DeviceEventQueryInput = object({
15996
16120
  deviceId: number(),
15997
16121
  since: number().optional(),
15998
16122
  until: number().optional(),
15999
- limit: number().int().min(1).max(5e3).default(1e3),
16123
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT),
16000
16124
  /** `slim` drops heavy JSON fields (regions/bbox/zones) and carries an
16001
16125
  * optional `mediaUrl` (populated by B5). `full` (default) keeps today's
16002
16126
  * exact behaviour. Callers may omit this field — the store defaults to
@@ -16004,6 +16128,27 @@ var DeviceEventQueryInput = object({
16004
16128
  projection: _enum(["full", "slim"]).optional()
16005
16129
  });
16006
16130
  var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: string().optional() });
16131
+ var RecentTracksQueryInput = object({
16132
+ /** Devices to merge. An empty array yields `{ tracks: [], nextCursor: null }`. */
16133
+ deviceIds: array(number()),
16134
+ /** Window lower bound on `lastSeen` (inclusive). */
16135
+ since: number().optional(),
16136
+ /** Window upper bound on `lastSeen` (inclusive). */
16137
+ until: number().optional(),
16138
+ /** Page size. Default 200, max 1000. */
16139
+ limit: number().int().min(1).max(1e3).default(200),
16140
+ /** Opaque continuation cursor from a previous page's `nextCursor`.
16141
+ * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
16142
+ cursor: string().optional(),
16143
+ /** See {@link TrackProjectionSchema}. Default `full`. */
16144
+ projection: TrackProjectionSchema.optional()
16145
+ });
16146
+ var RecentTracksPageSchema = object({
16147
+ /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
16148
+ tracks: array(TrackSchema).readonly(),
16149
+ /** Cursor for the next page, or null when this page is the last. */
16150
+ nextCursor: string().nullable()
16151
+ });
16007
16152
  var KeyEventQueryInput = object({
16008
16153
  deviceId: number(),
16009
16154
  /** Window lower bound (track firstSeen ≥ since). */
@@ -16086,11 +16231,25 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
16086
16231
  deviceId: number(),
16087
16232
  since: number().optional(),
16088
16233
  until: number().optional(),
16089
- limit: number().optional()
16090
- }), array(TrackSchema).readonly()), method(object({ deviceId: number() }), _void(), {
16091
- kind: "mutation",
16092
- auth: "admin"
16093
- }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
16234
+ limit: number().optional(),
16235
+ /** Spatial filter only tracks whose trajectory intersects the zone
16236
+ * (normalized 0..1 rect or polygon). SQL-prefiltered on the persisted
16237
+ * envelope columns, then precisely tested per position. Tracks with
16238
+ * an unknown envelope (no frame dims at persist time) always match. */
16239
+ zone: TrackZoneFilterSchema.optional(),
16240
+ /** See {@link TrackProjectionSchema}. Default `full` (backward
16241
+ * compatible — omitting the field keeps today's exact behaviour). */
16242
+ projection: TrackProjectionSchema.optional()
16243
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
16244
+ kind: "mutation",
16245
+ auth: "admin"
16246
+ }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({
16247
+ deviceId: number(),
16248
+ since: number().optional(),
16249
+ until: number().optional(),
16250
+ kinds: array(string()).optional(),
16251
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
16252
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
16094
16253
  deviceId: number(),
16095
16254
  since: number(),
16096
16255
  until: number(),
@@ -19291,7 +19450,12 @@ var RecordingStorageUsageSchema = object({
19291
19450
  /**
19292
19451
  * Result of locating footage at a wall-clock instant for one device/profile.
19293
19452
  * `segment` carries the covering segment's window; `gap` reports the forward
19294
- * nearest covered edge (`null` past the end of footage / when none exists).
19453
+ * nearest covered edge (`null` past the end of footage / when none exists)
19454
+ * and the backward covered edge `prevEndMs` (exclusive end of the nearest
19455
+ * footage behind the epoch; `null` when none — optional so older providers
19456
+ * that omit it stay valid). `prevEndMs` lets a backward frame-step hop the
19457
+ * small inter-segment cracks (durMs under-covers the span to the next
19458
+ * startMs by ~11-17 ms) instead of no-opping at a segment head.
19295
19459
  */
19296
19460
  var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
19297
19461
  kind: literal("segment"),
@@ -19300,7 +19464,8 @@ var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
19300
19464
  bytes: number()
19301
19465
  }), object({
19302
19466
  kind: literal("gap"),
19303
- nearestEdgeMs: number().nullable()
19467
+ nearestEdgeMs: number().nullable(),
19468
+ prevEndMs: number().nullable().optional()
19304
19469
  })]);
19305
19470
  /** Raw bytes of one finalized footage segment (read off disk on the recording node). */
19306
19471
  var ReadSegmentBytesResultSchema = object({ data: _instanceof(Uint8Array) });
@@ -20869,6 +21034,12 @@ Object.freeze({
20869
21034
  addonId: null,
20870
21035
  access: "view"
20871
21036
  },
21037
+ "deviceManager.getLinkedDevices": {
21038
+ capName: "device-manager",
21039
+ capScope: "system",
21040
+ addonId: null,
21041
+ access: "view"
21042
+ },
20872
21043
  "deviceManager.getRoleDisplayDefaults": {
20873
21044
  capName: "device-manager",
20874
21045
  capScope: "system",
@@ -22423,6 +22594,12 @@ Object.freeze({
22423
22594
  addonId: null,
22424
22595
  access: "view"
22425
22596
  },
22597
+ "pipelineAnalytics.getSensorEvents": {
22598
+ capName: "pipeline-analytics",
22599
+ capScope: "device",
22600
+ addonId: null,
22601
+ access: "view"
22602
+ },
22426
22603
  "pipelineAnalytics.getTrack": {
22427
22604
  capName: "pipeline-analytics",
22428
22605
  capScope: "device",
@@ -22435,6 +22612,18 @@ Object.freeze({
22435
22612
  addonId: null,
22436
22613
  access: "view"
22437
22614
  },
22615
+ "pipelineAnalytics.listEventKinds": {
22616
+ capName: "pipeline-analytics",
22617
+ capScope: "device",
22618
+ addonId: null,
22619
+ access: "view"
22620
+ },
22621
+ "pipelineAnalytics.listRecentTracks": {
22622
+ capName: "pipeline-analytics",
22623
+ capScope: "device",
22624
+ addonId: null,
22625
+ access: "view"
22626
+ },
22438
22627
  "pipelineAnalytics.listTracks": {
22439
22628
  capName: "pipeline-analytics",
22440
22629
  capScope: "device",
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { randomUUID } from "node:crypto";
2
- //#region ../types/dist/event-category-CFZs3jI4.mjs
2
+ //#region ../types/dist/event-category-H4AVePnn.mjs
3
3
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4
4
  EventCategory["SystemBoot"] = "system.boot";
5
5
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -149,6 +149,9 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
149
149
  EventCategory["RecordingSegmentWritten"] = "recording.segment.written";
150
150
  EventCategory["RecordingPolicyFallback"] = "recording.policy.fallback";
151
151
  EventCategory["RecordingRetentionCompleted"] = "recording.retention.completed";
152
+ /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
153
+ * thumb is a scrub gap the recorder's keyframe backfill covers. */
154
+ EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
152
155
  EventCategory["DetectionEvent"] = "detection.event";
153
156
  EventCategory["SessionTrackNew"] = "session.track.new";
154
157
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -7230,6 +7233,24 @@ var RecordingRetentionSchema = object({
7230
7233
  maxSizeGb: number().min(0).optional()
7231
7234
  });
7232
7235
  /**
7236
+ * Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
7237
+ * sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
7238
+ * previews at. Five graduated steps; absent on a config = `standard` (the
7239
+ * shipped default, matching `sheet-geometry`/`sheet-composer`).
7240
+ *
7241
+ * Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
7242
+ * Each window's index sidecar carries its own tile dims, so a camera whose
7243
+ * preset changed over time renders every historical window at the dims it was
7244
+ * written with.
7245
+ */
7246
+ var ScrubThumbnailPresetSchema = _enum([
7247
+ "minimal",
7248
+ "low",
7249
+ "standard",
7250
+ "high",
7251
+ "max"
7252
+ ]);
7253
+ /**
7233
7254
  * The full per-camera recording intent — the wire shape of a RecordingTarget.
7234
7255
  *
7235
7256
  * `mode` is the authoritative storage choice; `schedule`/`triggers`/`pre`/`post`
@@ -7266,7 +7287,14 @@ var RecordingConfigSchema = object({
7266
7287
  * derived into bands once via `migrateConfigToBands`.
7267
7288
  */
7268
7289
  bands: array(RecordingBandSchema).optional(),
7269
- retention: RecordingRetentionSchema.optional()
7290
+ retention: RecordingRetentionSchema.optional(),
7291
+ /**
7292
+ * Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
7293
+ * timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
7294
+ * windows only — existing sheets are immutable, and each window's index
7295
+ * carries its own tile dims so mixed-preset history renders correctly.
7296
+ */
7297
+ scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7270
7298
  });
7271
7299
  /**
7272
7300
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
@@ -9825,7 +9853,7 @@ object({
9825
9853
  deviceId: number(),
9826
9854
  timestamp: number()
9827
9855
  });
9828
- DeviceType.Button;
9856
+ DeviceType.Button, DeviceType.Camera;
9829
9857
  /**
9830
9858
  * Enum-state sensor — a string value picked from a finite option set.
9831
9859
  * Drives HA `sensor` entries with `state_class: enum` (HVAC action
@@ -14325,6 +14353,14 @@ var ConfigEntrySchema = object({
14325
14353
  value: unknown(),
14326
14354
  description: string().optional()
14327
14355
  });
14356
+ var DeviceLinkModeSchema = _enum(["auto", "manual"]);
14357
+ /** One resolved linked device — the compact projection consumers need. */
14358
+ var LinkedDeviceSchema = object({
14359
+ deviceId: number(),
14360
+ name: string(),
14361
+ location: string().nullable(),
14362
+ features: array(string())
14363
+ });
14328
14364
  var SavedDeviceRowSchema = object({
14329
14365
  /** Numeric id reserved at allocateDeviceId time. */
14330
14366
  id: number(),
@@ -14546,7 +14582,10 @@ method(object({
14546
14582
  }), _void(), {
14547
14583
  kind: "mutation",
14548
14584
  auth: "admin"
14549
- }), method(object({ addonId: string() }), array(SavedDeviceRowSchema)), method(object({ addonId: string().optional() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
14585
+ }), method(object({ addonId: string() }), array(SavedDeviceRowSchema)), method(object({ addonId: string().optional() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
14586
+ mode: DeviceLinkModeSchema,
14587
+ devices: array(LinkedDeviceSchema)
14588
+ })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
14550
14589
  deviceId: number(),
14551
14590
  values: record(string(), unknown())
14552
14591
  }), object({ success: literal(true) }), {
@@ -15815,6 +15854,61 @@ var EventKindSchema = _enum([
15815
15854
  "object",
15816
15855
  "audio"
15817
15856
  ]);
15857
+ /**
15858
+ * Spatial filter for `listTracks` — the rect + polygon variants of the shared
15859
+ * MaskShape vocabulary (see `mask-shape.ts`). Coordinates are NORMALIZED 0..1
15860
+ * of the camera frame (top-left origin), matching the drawing-plane editor.
15861
+ */
15862
+ var TrackZoneFilterSchema = discriminatedUnion("kind", [MaskRectShapeSchema, MaskPolygonShapeSchema]);
15863
+ /** Closed icon vocabulary so clients render a known glyph per kind. */
15864
+ var EventKindIconSchema = _enum([
15865
+ "motion",
15866
+ "audio",
15867
+ "person",
15868
+ "vehicle",
15869
+ "animal",
15870
+ "door",
15871
+ "pir",
15872
+ "smoke",
15873
+ "water",
15874
+ "button",
15875
+ "generic"
15876
+ ]);
15877
+ var EventKindCategorySchema = _enum([
15878
+ "motion",
15879
+ "audio",
15880
+ "detection",
15881
+ "sensor",
15882
+ "custom"
15883
+ ]);
15884
+ var EventKindDescriptorSchema = object({
15885
+ /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
15886
+ kind: string(),
15887
+ label: string(),
15888
+ /** Hex color for timeline/legend rendering. */
15889
+ color: string(),
15890
+ icon: EventKindIconSchema,
15891
+ category: EventKindCategorySchema,
15892
+ /** Which cap + device contributes this kind. For built-ins the camera
15893
+ * itself; for sensor kinds the LINKED source device. */
15894
+ source: object({
15895
+ capName: string(),
15896
+ deviceId: number()
15897
+ })
15898
+ });
15899
+ var SensorEventSchema = object({
15900
+ id: string(),
15901
+ /** The CAMERA the event is attributed to (a sensor linked to N cameras
15902
+ * yields N rows, one per camera). */
15903
+ deviceId: number(),
15904
+ /** The linked sensor device whose state changed. */
15905
+ sourceDeviceId: number(),
15906
+ /** Event kind id — matches an `EventKindDescriptor.kind`. */
15907
+ kind: string(),
15908
+ /** Snapshot of the sensor cap's runtime-state slice at the change. */
15909
+ value: record(string(), unknown()).nullable(),
15910
+ timestamp: number()
15911
+ });
15818
15912
  var TrackPositionSchema = object({
15819
15913
  x: number(),
15820
15914
  y: number(),
@@ -15828,6 +15922,30 @@ var TrackSnapshotSchema = object({
15828
15922
  mediaKey: string()
15829
15923
  });
15830
15924
  /**
15925
+ * Normalized 0..1 trajectory envelope (min/max over every position bbox,
15926
+ * divided by the track's detection-frame dims), computed at persist time.
15927
+ * Absent when the frame dims were unknown when the track was persisted
15928
+ * (legacy rows / dims-less sources) and on active (in-RAM) tracks.
15929
+ */
15930
+ var TrackEnvelopeSchema = object({
15931
+ minX: number(),
15932
+ minY: number(),
15933
+ maxX: number(),
15934
+ maxY: number()
15935
+ });
15936
+ /**
15937
+ * Row projection for track list queries. `full` (default) returns the
15938
+ * complete Track including the frame-rate `positions[]` history and the
15939
+ * `snapshots[]` references — megabytes across a page of tracks. `slim`
15940
+ * keeps every scalar the list surfaces actually render (ids, class(es),
15941
+ * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
15942
+ * zonesVisited, bestEventId, envelope) and returns `positions` /
15943
+ * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
15944
+ * `getTrack`. Mirrors the event-store `projection` convention
15945
+ * (`getObjectEvents` et al.).
15946
+ */
15947
+ var TrackProjectionSchema = _enum(["full", "slim"]);
15948
+ /**
15831
15949
  * One audio-classification label heard on the track's camera while the
15832
15950
  * track was alive, aggregated per label. An "episode" is one persisted
15833
15951
  * audio event (the confident-classification path: score ≥ the device's
@@ -15878,7 +15996,11 @@ var TrackSchema = object({
15878
15996
  /** Audio-classification labels heard on the camera during the track's
15879
15997
  * life (score ≥ device `classificationMinScore`), aggregated per label.
15880
15998
  * Absent on legacy rows / tracks with no confident audio. */
15881
- audioLabels: array(TrackAudioLabelSchema).readonly().optional()
15999
+ audioLabels: array(TrackAudioLabelSchema).readonly().optional(),
16000
+ /** Normalized 0..1 trajectory envelope (see {@link TrackEnvelopeSchema}).
16001
+ * Populated from the persisted envelope columns on historical reads;
16002
+ * absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
16003
+ envelope: TrackEnvelopeSchema.optional()
15882
16004
  });
15883
16005
  var BaseEventFields = {
15884
16006
  id: string(),
@@ -15988,11 +16110,13 @@ var MediaFileSchema = object({
15988
16110
  sizeBytes: number(),
15989
16111
  timestamp: number()
15990
16112
  });
16113
+ var DEFAULT_EVENT_QUERY_LIMIT = 1e3;
16114
+ var MAX_EVENT_QUERY_LIMIT = 5e3;
15991
16115
  var DeviceEventQueryInput = object({
15992
16116
  deviceId: number(),
15993
16117
  since: number().optional(),
15994
16118
  until: number().optional(),
15995
- limit: number().int().min(1).max(5e3).default(1e3),
16119
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT),
15996
16120
  /** `slim` drops heavy JSON fields (regions/bbox/zones) and carries an
15997
16121
  * optional `mediaUrl` (populated by B5). `full` (default) keeps today's
15998
16122
  * exact behaviour. Callers may omit this field — the store defaults to
@@ -16000,6 +16124,27 @@ var DeviceEventQueryInput = object({
16000
16124
  projection: _enum(["full", "slim"]).optional()
16001
16125
  });
16002
16126
  var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: string().optional() });
16127
+ var RecentTracksQueryInput = object({
16128
+ /** Devices to merge. An empty array yields `{ tracks: [], nextCursor: null }`. */
16129
+ deviceIds: array(number()),
16130
+ /** Window lower bound on `lastSeen` (inclusive). */
16131
+ since: number().optional(),
16132
+ /** Window upper bound on `lastSeen` (inclusive). */
16133
+ until: number().optional(),
16134
+ /** Page size. Default 200, max 1000. */
16135
+ limit: number().int().min(1).max(1e3).default(200),
16136
+ /** Opaque continuation cursor from a previous page's `nextCursor`.
16137
+ * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
16138
+ cursor: string().optional(),
16139
+ /** See {@link TrackProjectionSchema}. Default `full`. */
16140
+ projection: TrackProjectionSchema.optional()
16141
+ });
16142
+ var RecentTracksPageSchema = object({
16143
+ /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
16144
+ tracks: array(TrackSchema).readonly(),
16145
+ /** Cursor for the next page, or null when this page is the last. */
16146
+ nextCursor: string().nullable()
16147
+ });
16003
16148
  var KeyEventQueryInput = object({
16004
16149
  deviceId: number(),
16005
16150
  /** Window lower bound (track firstSeen ≥ since). */
@@ -16082,11 +16227,25 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
16082
16227
  deviceId: number(),
16083
16228
  since: number().optional(),
16084
16229
  until: number().optional(),
16085
- limit: number().optional()
16086
- }), array(TrackSchema).readonly()), method(object({ deviceId: number() }), _void(), {
16087
- kind: "mutation",
16088
- auth: "admin"
16089
- }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
16230
+ limit: number().optional(),
16231
+ /** Spatial filter only tracks whose trajectory intersects the zone
16232
+ * (normalized 0..1 rect or polygon). SQL-prefiltered on the persisted
16233
+ * envelope columns, then precisely tested per position. Tracks with
16234
+ * an unknown envelope (no frame dims at persist time) always match. */
16235
+ zone: TrackZoneFilterSchema.optional(),
16236
+ /** See {@link TrackProjectionSchema}. Default `full` (backward
16237
+ * compatible — omitting the field keeps today's exact behaviour). */
16238
+ projection: TrackProjectionSchema.optional()
16239
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
16240
+ kind: "mutation",
16241
+ auth: "admin"
16242
+ }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({
16243
+ deviceId: number(),
16244
+ since: number().optional(),
16245
+ until: number().optional(),
16246
+ kinds: array(string()).optional(),
16247
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
16248
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
16090
16249
  deviceId: number(),
16091
16250
  since: number(),
16092
16251
  until: number(),
@@ -19287,7 +19446,12 @@ var RecordingStorageUsageSchema = object({
19287
19446
  /**
19288
19447
  * Result of locating footage at a wall-clock instant for one device/profile.
19289
19448
  * `segment` carries the covering segment's window; `gap` reports the forward
19290
- * nearest covered edge (`null` past the end of footage / when none exists).
19449
+ * nearest covered edge (`null` past the end of footage / when none exists)
19450
+ * and the backward covered edge `prevEndMs` (exclusive end of the nearest
19451
+ * footage behind the epoch; `null` when none — optional so older providers
19452
+ * that omit it stay valid). `prevEndMs` lets a backward frame-step hop the
19453
+ * small inter-segment cracks (durMs under-covers the span to the next
19454
+ * startMs by ~11-17 ms) instead of no-opping at a segment head.
19291
19455
  */
19292
19456
  var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
19293
19457
  kind: literal("segment"),
@@ -19296,7 +19460,8 @@ var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
19296
19460
  bytes: number()
19297
19461
  }), object({
19298
19462
  kind: literal("gap"),
19299
- nearestEdgeMs: number().nullable()
19463
+ nearestEdgeMs: number().nullable(),
19464
+ prevEndMs: number().nullable().optional()
19300
19465
  })]);
19301
19466
  /** Raw bytes of one finalized footage segment (read off disk on the recording node). */
19302
19467
  var ReadSegmentBytesResultSchema = object({ data: _instanceof(Uint8Array) });
@@ -20865,6 +21030,12 @@ Object.freeze({
20865
21030
  addonId: null,
20866
21031
  access: "view"
20867
21032
  },
21033
+ "deviceManager.getLinkedDevices": {
21034
+ capName: "device-manager",
21035
+ capScope: "system",
21036
+ addonId: null,
21037
+ access: "view"
21038
+ },
20868
21039
  "deviceManager.getRoleDisplayDefaults": {
20869
21040
  capName: "device-manager",
20870
21041
  capScope: "system",
@@ -22419,6 +22590,12 @@ Object.freeze({
22419
22590
  addonId: null,
22420
22591
  access: "view"
22421
22592
  },
22593
+ "pipelineAnalytics.getSensorEvents": {
22594
+ capName: "pipeline-analytics",
22595
+ capScope: "device",
22596
+ addonId: null,
22597
+ access: "view"
22598
+ },
22422
22599
  "pipelineAnalytics.getTrack": {
22423
22600
  capName: "pipeline-analytics",
22424
22601
  capScope: "device",
@@ -22431,6 +22608,18 @@ Object.freeze({
22431
22608
  addonId: null,
22432
22609
  access: "view"
22433
22610
  },
22611
+ "pipelineAnalytics.listEventKinds": {
22612
+ capName: "pipeline-analytics",
22613
+ capScope: "device",
22614
+ addonId: null,
22615
+ access: "view"
22616
+ },
22617
+ "pipelineAnalytics.listRecentTracks": {
22618
+ capName: "pipeline-analytics",
22619
+ capScope: "device",
22620
+ addonId: null,
22621
+ access: "view"
22622
+ },
22434
22623
  "pipelineAnalytics.listTracks": {
22435
22624
  capName: "pipeline-analytics",
22436
22625
  capScope: "device",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-decoder-nodeav",
3
- "version": "1.1.13",
3
+ "version": "1.1.15",
4
4
  "description": "Standalone in-process node-av decoder addon for CamStack",
5
5
  "keywords": [
6
6
  "camstack",