@camstack/sdk 1.2.127 → 1.2.129

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/nvr.d.ts CHANGED
@@ -1,272 +1,272 @@
1
1
  /**
2
- * NVR types — system-agnostic types used by providers and the SDK client.
3
- *
4
- * These are the canonical types from the proxy's providers/types.ts,
5
- * now owned by the SDK so both app and proxy can share them.
6
- */
7
- import type { StreamSourceEntry } from '@camstack/types';
2
+ * NVR types — system-agnostic types used by providers and the SDK client.
3
+ *
4
+ * These are the canonical types from the proxy's providers/types.ts,
5
+ * now owned by the SDK so both app and proxy can share them.
6
+ */
7
+ import type { StreamSourceEntry } from "@camstack/types";
8
8
  export type { StreamSourceEntry };
9
9
  export interface NvrCamera {
10
- name: string;
11
- /** Display label (may differ from internal name). */
12
- label?: string;
13
- enabled: boolean;
14
- hasAudio: boolean;
15
- hasPtz: boolean;
16
- /** Available stream names for this camera (e.g. ["cam_main", "cam_sub"]). */
17
- streams: string[];
18
- /** Available streaming options. */
19
- streamOptions: StreamSourceEntry[];
20
- /** Accessories/actions available on this camera (siren, floodlight, PTZ, etc.). */
21
- accessories?: CameraAccessory[];
10
+ name: string;
11
+ /** Display label (may differ from internal name). */
12
+ label?: string;
13
+ enabled: boolean;
14
+ hasAudio: boolean;
15
+ hasPtz: boolean;
16
+ /** Available stream names for this camera (e.g. ["cam_main", "cam_sub"]). */
17
+ streams: string[];
18
+ /** Available streaming options. */
19
+ streamOptions: StreamSourceEntry[];
20
+ /** Accessories/actions available on this camera (siren, floodlight, PTZ, etc.). */
21
+ accessories?: CameraAccessory[];
22
22
  }
23
23
  /**
24
- * A camera accessory — a controllable feature attached to a camera.
25
- * Declared by the provider, rendered dynamically in the UI.
26
- */
24
+ * A camera accessory — a controllable feature attached to a camera.
25
+ * Declared by the provider, rendered dynamically in the UI.
26
+ */
27
27
  export interface CameraAccessory {
28
- /** Unique accessory ID (e.g. "siren", "floodlight", "ptz", "ir_led"). */
29
- id: string;
30
- /** Display label (e.g. "Siren", "Floodlight"). */
31
- label: string;
32
- /** Accessory type determines UI rendering. */
33
- type: 'toggle' | 'button' | 'ptz' | 'slider';
34
- /** Lucide icon name (e.g. "siren", "lightbulb", "move", "sun"). */
35
- icon: string;
36
- /** Current state (for toggles: boolean, for sliders: number). */
37
- state?: unknown;
38
- /** For slider type: min/max/step. */
39
- min?: number;
40
- max?: number;
41
- step?: number;
28
+ /** Unique accessory ID (e.g. "siren", "floodlight", "ptz", "ir_led"). */
29
+ id: string;
30
+ /** Display label (e.g. "Siren", "Floodlight"). */
31
+ label: string;
32
+ /** Accessory type determines UI rendering. */
33
+ type: "toggle" | "button" | "ptz" | "slider";
34
+ /** Lucide icon name (e.g. "siren", "lightbulb", "move", "sun"). */
35
+ icon: string;
36
+ /** Current state (for toggles: boolean, for sliders: number). */
37
+ state?: unknown;
38
+ /** For slider type: min/max/step. */
39
+ min?: number;
40
+ max?: number;
41
+ step?: number;
42
42
  }
43
43
  export interface StreamInfo {
44
- name: string;
45
- /** Camera this stream belongs to (if determinable). */
46
- camera?: string;
44
+ name: string;
45
+ /** Camera this stream belongs to (if determinable). */
46
+ camera?: string;
47
47
  }
48
48
  /** Normalized bounding box in 0–1 range (provider-agnostic). */
49
49
  export interface NormalizedBoundingBox {
50
- x1: number;
51
- y1: number;
52
- x2: number;
53
- y2: number;
50
+ x1: number;
51
+ y1: number;
52
+ x2: number;
53
+ y2: number;
54
54
  }
55
55
  /** Detection data attached to an event. */
56
56
  export interface DetectionData {
57
- label: string;
58
- subLabel?: string;
59
- /** Confidence score 0–1. */
60
- score: number;
61
- /** Best confidence score observed during the event. */
62
- topScore?: number;
63
- /** Bounding box of the detected object, normalized 0–1. */
64
- boundingBox?: NormalizedBoundingBox;
65
- /** Detection region, normalized 0–1. */
66
- region?: NormalizedBoundingBox;
67
- /** Area as fraction of frame area (0–1). */
68
- area?: number;
69
- /** Named zones the object is currently in. */
70
- currentZones?: string[];
71
- /** Named zones the object has entered during this event. */
72
- enteredZones?: string[];
73
- /** Attributes detected (e.g., { face: 0.64, glasses: 0.82 }). */
74
- attributes?: Record<string, number>;
75
- /** Current estimated speed. */
76
- currentSpeed?: number;
77
- /** Average estimated speed over the event. */
78
- averageSpeed?: number;
79
- /** Velocity angle in degrees. */
80
- velocityAngle?: number;
81
- /** License plate recognition result. */
82
- licensePlate?: {
83
- text: string;
84
- score: number;
85
- };
57
+ label: string;
58
+ subLabel?: string;
59
+ /** Confidence score 0–1. */
60
+ score: number;
61
+ /** Best confidence score observed during the event. */
62
+ topScore?: number;
63
+ /** Bounding box of the detected object, normalized 0–1. */
64
+ boundingBox?: NormalizedBoundingBox;
65
+ /** Detection region, normalized 0–1. */
66
+ region?: NormalizedBoundingBox;
67
+ /** Area as fraction of frame area (0–1). */
68
+ area?: number;
69
+ /** Named zones the object is currently in. */
70
+ currentZones?: string[];
71
+ /** Named zones the object has entered during this event. */
72
+ enteredZones?: string[];
73
+ /** Attributes detected (e.g., { face: 0.64, glasses: 0.82 }). */
74
+ attributes?: Record<string, number>;
75
+ /** Current estimated speed. */
76
+ currentSpeed?: number;
77
+ /** Average estimated speed over the event. */
78
+ averageSpeed?: number;
79
+ /** Velocity angle in degrees. */
80
+ velocityAngle?: number;
81
+ /** License plate recognition result. */
82
+ licensePlate?: {
83
+ text: string;
84
+ score: number;
85
+ };
86
86
  }
87
87
  /** Snapshot metadata associated with an event. */
88
88
  export interface EventSnapshotData {
89
- boundingBox?: NormalizedBoundingBox;
90
- score?: number;
91
- /** Area as fraction of frame area (0–1). */
92
- area?: number;
93
- attributes?: Record<string, number>;
89
+ boundingBox?: NormalizedBoundingBox;
90
+ score?: number;
91
+ /** Area as fraction of frame area (0–1). */
92
+ area?: number;
93
+ attributes?: Record<string, number>;
94
94
  }
95
95
  export interface NvrEvent {
96
- id: string;
97
- camera: string;
98
- label: string;
99
- startTime: number;
100
- endTime: number | null;
101
- severity?: string;
102
- hasClip: boolean;
103
- hasSnapshot: boolean;
104
- thumbnailPath?: string;
105
- snapshotPath?: string;
106
- clipPath?: string;
107
- detection?: DetectionData;
108
- snapshot?: EventSnapshotData;
109
- falsePositive?: boolean;
110
- retainIndefinitely?: boolean;
111
- raw?: Record<string, unknown>;
96
+ id: string;
97
+ camera: string;
98
+ label: string;
99
+ startTime: number;
100
+ endTime: number | null;
101
+ severity?: string;
102
+ hasClip: boolean;
103
+ hasSnapshot: boolean;
104
+ thumbnailPath?: string;
105
+ snapshotPath?: string;
106
+ clipPath?: string;
107
+ detection?: DetectionData;
108
+ snapshot?: EventSnapshotData;
109
+ falsePositive?: boolean;
110
+ retainIndefinitely?: boolean;
111
+ raw?: Record<string, unknown>;
112
112
  }
113
113
  export interface RecordingSegment {
114
- id: string;
115
- camera: string;
116
- startTime: number;
117
- endTime: number;
118
- duration: number;
119
- motion?: number;
120
- objects?: number;
121
- dBFS?: number;
114
+ id: string;
115
+ camera: string;
116
+ startTime: number;
117
+ endTime: number;
118
+ duration: number;
119
+ motion?: number;
120
+ objects?: number;
121
+ dBFS?: number;
122
122
  }
123
123
  export interface MotionBucket {
124
- camera: string;
125
- startTime: number;
126
- motion?: number;
127
- audio?: number;
124
+ camera: string;
125
+ startTime: number;
126
+ motion?: number;
127
+ audio?: number;
128
128
  }
129
129
  /** Single object detection from a live frame. */
130
130
  export interface LiveDetection {
131
- /** Detection class — the object type (person, car, dog, face, plate, etc.) */
132
- className: string;
133
- /** Recognized identity — face name, license plate text, sub-label. */
134
- label?: string;
135
- /** Detection confidence 0–1. */
136
- score: number;
137
- /** Label/recognition confidence 0–1 — takes precedence over score when available. */
138
- labelScore?: number;
139
- /** Named zones where the object currently is. */
140
- zones?: string[];
141
- /** Tracking/correlation ID across frames. */
142
- id?: string;
143
- /** Bounding box as fractions 0–1 of frame dimensions. */
144
- boundingBox?: [number, number, number, number] | {
145
- x: number;
146
- y: number;
147
- w: number;
148
- h: number;
149
- };
131
+ /** Detection class — the object type (person, car, dog, face, plate, etc.) */
132
+ className: string;
133
+ /** Recognized identity — face name, license plate text, sub-label. */
134
+ label?: string;
135
+ /** Detection confidence 0–1. */
136
+ score: number;
137
+ /** Label/recognition confidence 0–1 — takes precedence over score when available. */
138
+ labelScore?: number;
139
+ /** Named zones where the object currently is. */
140
+ zones?: string[];
141
+ /** Tracking/correlation ID across frames. */
142
+ id?: string;
143
+ /** Bounding box as fractions 0–1 of frame dimensions. */
144
+ boundingBox?: [number, number, number, number] | {
145
+ x: number;
146
+ y: number;
147
+ w: number;
148
+ h: number;
149
+ };
150
150
  }
151
151
  export interface LiveMotionData {
152
- active: boolean;
152
+ active: boolean;
153
153
  }
154
154
  export interface LiveDetectionData {
155
- detections: LiveDetection[];
156
- /** Input frame dimensions [width, height] in pixels (for reference). */
157
- inputDimensions?: [number, number];
158
- /** Event ID from the NVR. */
159
- eventId?: string;
160
- /** Provider-specific extra data. */
161
- [key: string]: unknown;
155
+ detections: LiveDetection[];
156
+ /** Input frame dimensions [width, height] in pixels (for reference). */
157
+ inputDimensions?: [number, number];
158
+ /** Event ID from the NVR. */
159
+ eventId?: string;
160
+ /** Provider-specific extra data. */
161
+ [key: string]: unknown;
162
162
  }
163
163
  export interface LiveAudioData {
164
- /** Audio metric type (e.g. "dBFS", "rms", "audioDetected", "audioLevel"). */
165
- metric: string;
166
- /** Numeric value. */
167
- value?: number;
168
- /** dBFS level. */
169
- dBFS?: number;
170
- /** RMS level. */
171
- rms?: number;
164
+ /** Audio metric type (e.g. "dBFS", "rms", "audioDetected", "audioLevel"). */
165
+ metric: string;
166
+ /** Numeric value. */
167
+ value?: number;
168
+ /** dBFS level. */
169
+ dBFS?: number;
170
+ /** RMS level. */
171
+ rms?: number;
172
172
  }
173
- export type LiveEventType = 'motion' | 'detection' | 'audio' | 'event' | 'review';
173
+ export type LiveEventType = "motion" | "detection" | "audio" | "event" | "review";
174
174
  interface LiveEventBase {
175
- camera: string;
176
- timestamp: number;
177
- /** Provider info — populated by onAllEvents subscription. */
178
- provider?: {
179
- id: string;
180
- name: string;
181
- type: string;
182
- };
175
+ camera: string;
176
+ timestamp: number;
177
+ /** Provider info — populated by onAllEvents subscription. */
178
+ provider?: {
179
+ id: string;
180
+ name: string;
181
+ type: string;
182
+ };
183
183
  }
184
184
  export interface LiveMotionEvent extends LiveEventBase {
185
- type: 'motion';
186
- data: LiveMotionData;
185
+ type: "motion";
186
+ data: LiveMotionData;
187
187
  }
188
188
  export interface LiveDetectionEvent extends LiveEventBase {
189
- type: 'detection';
190
- data: LiveDetectionData;
189
+ type: "detection";
190
+ data: LiveDetectionData;
191
191
  }
192
192
  export interface LiveAudioEvent extends LiveEventBase {
193
- type: 'audio';
194
- data: LiveAudioData;
193
+ type: "audio";
194
+ data: LiveAudioData;
195
195
  }
196
196
  export interface LiveGenericEvent extends LiveEventBase {
197
- type: 'event';
198
- data: Record<string, unknown>;
197
+ type: "event";
198
+ data: Record<string, unknown>;
199
199
  }
200
200
  export interface LiveReviewEvent extends LiveEventBase {
201
- type: 'review';
202
- data: Record<string, unknown>;
201
+ type: "review";
202
+ data: Record<string, unknown>;
203
203
  }
204
204
  export type LiveEvent = LiveMotionEvent | LiveDetectionEvent | LiveAudioEvent | LiveGenericEvent | LiveReviewEvent;
205
205
  export interface VideoClipsQuery {
206
- camera: string;
207
- after: number;
208
- before: number;
209
- limit?: number;
210
- offset?: number;
206
+ camera: string;
207
+ after: number;
208
+ before: number;
209
+ limit?: number;
210
+ offset?: number;
211
211
  }
212
212
  export interface NvrVideoClip {
213
- id: string;
214
- camera: string;
215
- startTime: number;
216
- endTime: number;
217
- duration: number;
218
- label?: string;
219
- hasClip: boolean;
220
- hasSnapshot: boolean;
221
- source: 'event' | 'recording';
213
+ id: string;
214
+ camera: string;
215
+ startTime: number;
216
+ endTime: number;
217
+ duration: number;
218
+ label?: string;
219
+ hasClip: boolean;
220
+ hasSnapshot: boolean;
221
+ source: "event" | "recording";
222
222
  }
223
223
  export interface VideoClipsResult {
224
- clips: NvrVideoClip[];
225
- total: number;
224
+ clips: NvrVideoClip[];
225
+ total: number;
226
226
  }
227
227
  export interface VideoClipUrlResult {
228
- url: string;
229
- mimeType: string;
228
+ url: string;
229
+ mimeType: string;
230
230
  }
231
231
  export interface CameraDayData {
232
- events: NvrEvent[];
233
- motion: MotionBucket[];
234
- recordings: RecordingSegment[];
232
+ events: NvrEvent[];
233
+ motion: MotionBucket[];
234
+ recordings: RecordingSegment[];
235
235
  }
236
236
  export interface CameraStatus {
237
- camera: string;
238
- online: boolean;
239
- recording: boolean;
240
- detecting: boolean;
241
- motionActive: boolean;
237
+ camera: string;
238
+ online: boolean;
239
+ recording: boolean;
240
+ detecting: boolean;
241
+ motionActive: boolean;
242
242
  }
243
243
  export interface EventsQuery {
244
- after?: number;
245
- before?: number;
246
- cameras?: string[];
247
- limit?: number;
248
- labels?: string[];
244
+ after?: number;
245
+ before?: number;
246
+ cameras?: string[];
247
+ limit?: number;
248
+ labels?: string[];
249
249
  }
250
250
  export interface MotionQuery {
251
- after: number;
252
- before: number;
253
- scale?: number;
254
- cameras?: string[];
251
+ after: number;
252
+ before: number;
253
+ scale?: number;
254
+ cameras?: string[];
255
255
  }
256
256
  export interface RecordingsQuery {
257
- camera: string;
258
- after: number;
259
- before: number;
257
+ camera: string;
258
+ after: number;
259
+ before: number;
260
260
  }
261
261
  export interface NvrConfig {
262
- type: string;
263
- version?: string;
264
- raw?: unknown;
262
+ type: string;
263
+ version?: string;
264
+ raw?: unknown;
265
265
  }
266
266
  export interface ProviderConfigs {
267
- cameras: Array<{
268
- id: string;
269
- name: string;
270
- }>;
271
- enabledDetectionSources?: string[];
267
+ cameras: Array<{
268
+ id: string;
269
+ name: string;
270
+ }>;
271
+ enabledDetectionSources?: string[];
272
272
  }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Keeps a refreshing CamStack session alive without the user noticing.
3
+ *
4
+ * The hub hands a refreshing client a short access token (default 2 h) and a
5
+ * rotating refresh token. This schedules `auth.refreshSession` shortly before
6
+ * the access token expires, hands every rotated pair to the app to persist,
7
+ * and reports the end of the session only when the hub REFUSES the refresh —
8
+ * a network failure is retried, never treated as a sign-out.
9
+ *
10
+ * Transport-agnostic: the caller supplies `refresh` (normally
11
+ * `System.refreshSession`), so the keeper outlives a System that is rebuilt.
12
+ */
13
+ export interface SessionTokens {
14
+ /** The access token (Bearer). */
15
+ readonly token: string;
16
+ /** The rotating refresh token, when the session is refreshable. */
17
+ readonly refreshToken?: string;
18
+ /** When the access token expires (epoch ms); decoded from the JWT when absent. */
19
+ readonly expiresAt?: number;
20
+ }
21
+ export interface SessionKeeperOptions {
22
+ /** Swap a refresh token for a new pair (`System.refreshSession`). */
23
+ readonly refresh: (refreshToken: string) => Promise<SessionTokens>;
24
+ /** Persist / apply a rotated pair (also called after `ensureFresh`). */
25
+ readonly onTokens: (tokens: SessionTokens) => void;
26
+ /** The hub refused the refresh: the session is over — sign the user out. */
27
+ readonly onExpired: () => void;
28
+ /** Refresh this long before the access token expires (default 5 min, at most half its life). */
29
+ readonly marginMs?: number;
30
+ readonly now?: () => number;
31
+ /** Timer hook (tests); returns a cancel function. */
32
+ readonly setTimer?: (fn: () => void, ms: number) => () => void;
33
+ }
34
+ /** Decode a JWT's `exp` (seconds) to epoch ms, or `null`. Never throws. */
35
+ export declare function jwtExpiresAt(token: string): number | null;
36
+ /** Is `error` the hub saying the session is over (as opposed to a transport failure)? */
37
+ export declare function isSessionRefusal(error: unknown): boolean;
38
+ export declare class SessionKeeper {
39
+ private readonly options;
40
+ private current;
41
+ private cancel;
42
+ private inflight;
43
+ private readonly now;
44
+ private readonly setTimer;
45
+ constructor(options: SessionKeeperOptions);
46
+ /** Take over `tokens` (a login, a restore from storage) and schedule its refresh. */
47
+ adopt(tokens: SessionTokens): void;
48
+ /** The tokens the keeper holds now. */
49
+ tokens(): SessionTokens | null;
50
+ /**
51
+ * Refresh now if the access token expires within `marginMs` (default: the
52
+ * keeper's margin) — call it before reconnecting after the app was in the
53
+ * background. Resolves when done; concurrent callers share one refresh.
54
+ */
55
+ ensureFresh(marginMs?: number): Promise<void>;
56
+ /** Refresh immediately. `null` when there is nothing to refresh or it failed. */
57
+ refreshNow(): Promise<SessionTokens | null>;
58
+ stop(): void;
59
+ private margin;
60
+ private expiresAtOf;
61
+ private schedule;
62
+ private arm;
63
+ }