@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/backend-router.d.ts +4 -4
- package/dist/base-device.d.ts +4 -4
- package/dist/camera.d.ts +20 -20
- package/dist/detection.d.ts +28 -28
- package/dist/index.cjs +182 -22
- package/dist/index.d.ts +13 -11
- package/dist/index.js +180 -23
- package/dist/nvr.d.ts +204 -204
- package/dist/session-keeper.d.ts +63 -0
- package/dist/system.d.ts +495 -436
- package/dist/timeline.d.ts +115 -115
- package/dist/types.d.ts +25 -25
- package/package.json +4 -4
- package/dist/__tests__/base-device.spec.d.ts +0 -1
- package/dist/__tests__/race-endpoints.spec.d.ts +0 -1
- package/dist/__tests__/subscribe-event-multiplex.spec.d.ts +0 -13
package/dist/nvr.d.ts
CHANGED
|
@@ -1,272 +1,272 @@
|
|
|
1
1
|
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import type { StreamSourceEntry } from
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
25
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
|
|
152
|
+
active: boolean;
|
|
153
153
|
}
|
|
154
154
|
export interface LiveDetectionData {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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 =
|
|
173
|
+
export type LiveEventType = "motion" | "detection" | "audio" | "event" | "review";
|
|
174
174
|
interface LiveEventBase {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
-
|
|
186
|
-
|
|
185
|
+
type: "motion";
|
|
186
|
+
data: LiveMotionData;
|
|
187
187
|
}
|
|
188
188
|
export interface LiveDetectionEvent extends LiveEventBase {
|
|
189
|
-
|
|
190
|
-
|
|
189
|
+
type: "detection";
|
|
190
|
+
data: LiveDetectionData;
|
|
191
191
|
}
|
|
192
192
|
export interface LiveAudioEvent extends LiveEventBase {
|
|
193
|
-
|
|
194
|
-
|
|
193
|
+
type: "audio";
|
|
194
|
+
data: LiveAudioData;
|
|
195
195
|
}
|
|
196
196
|
export interface LiveGenericEvent extends LiveEventBase {
|
|
197
|
-
|
|
198
|
-
|
|
197
|
+
type: "event";
|
|
198
|
+
data: Record<string, unknown>;
|
|
199
199
|
}
|
|
200
200
|
export interface LiveReviewEvent extends LiveEventBase {
|
|
201
|
-
|
|
202
|
-
|
|
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
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
206
|
+
camera: string;
|
|
207
|
+
after: number;
|
|
208
|
+
before: number;
|
|
209
|
+
limit?: number;
|
|
210
|
+
offset?: number;
|
|
211
211
|
}
|
|
212
212
|
export interface NvrVideoClip {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
|
|
225
|
-
|
|
224
|
+
clips: NvrVideoClip[];
|
|
225
|
+
total: number;
|
|
226
226
|
}
|
|
227
227
|
export interface VideoClipUrlResult {
|
|
228
|
-
|
|
229
|
-
|
|
228
|
+
url: string;
|
|
229
|
+
mimeType: string;
|
|
230
230
|
}
|
|
231
231
|
export interface CameraDayData {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
232
|
+
events: NvrEvent[];
|
|
233
|
+
motion: MotionBucket[];
|
|
234
|
+
recordings: RecordingSegment[];
|
|
235
235
|
}
|
|
236
236
|
export interface CameraStatus {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
237
|
+
camera: string;
|
|
238
|
+
online: boolean;
|
|
239
|
+
recording: boolean;
|
|
240
|
+
detecting: boolean;
|
|
241
|
+
motionActive: boolean;
|
|
242
242
|
}
|
|
243
243
|
export interface EventsQuery {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
244
|
+
after?: number;
|
|
245
|
+
before?: number;
|
|
246
|
+
cameras?: string[];
|
|
247
|
+
limit?: number;
|
|
248
|
+
labels?: string[];
|
|
249
249
|
}
|
|
250
250
|
export interface MotionQuery {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
251
|
+
after: number;
|
|
252
|
+
before: number;
|
|
253
|
+
scale?: number;
|
|
254
|
+
cameras?: string[];
|
|
255
255
|
}
|
|
256
256
|
export interface RecordingsQuery {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
257
|
+
camera: string;
|
|
258
|
+
after: number;
|
|
259
|
+
before: number;
|
|
260
260
|
}
|
|
261
261
|
export interface NvrConfig {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
262
|
+
type: string;
|
|
263
|
+
version?: string;
|
|
264
|
+
raw?: unknown;
|
|
265
265
|
}
|
|
266
266
|
export interface ProviderConfigs {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
+
}
|