@camera.ui/sdk 0.0.2 → 0.0.4

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 (76) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +3 -3
  3. package/dist/camera/events.js +2 -0
  4. package/dist/camera/index.js +3 -1
  5. package/dist/external.js +7 -0
  6. package/dist/index.d.ts +7132 -4248
  7. package/dist/index.js +3 -11
  8. package/dist/internal/contract-validators.js +21 -0
  9. package/dist/internal/index.d.ts +915 -0
  10. package/dist/internal/index.js +9 -0
  11. package/dist/internal/sensor-triggers.js +2 -0
  12. package/dist/internal/shared-utils.js +86 -0
  13. package/dist/internal/streaming-internal.js +1 -0
  14. package/dist/manager/index.js +1 -1
  15. package/dist/observable/index.js +419 -0
  16. package/dist/plugin/api.js +21 -0
  17. package/dist/plugin/contract.js +101 -114
  18. package/dist/plugin/helper.js +277 -0
  19. package/dist/plugin/index.js +4 -1
  20. package/dist/plugin/interfaces.js +51 -1
  21. package/dist/plugin/notifier.js +23 -0
  22. package/dist/plugin/oauth.js +1 -0
  23. package/dist/sensor/audio.js +103 -81
  24. package/dist/sensor/base.js +350 -318
  25. package/dist/sensor/battery.js +73 -59
  26. package/dist/sensor/classifier.js +117 -0
  27. package/dist/sensor/clip.js +30 -0
  28. package/dist/sensor/contact.js +37 -18
  29. package/dist/sensor/detection.js +4 -0
  30. package/dist/sensor/doorbell.js +52 -38
  31. package/dist/sensor/face.js +71 -86
  32. package/dist/sensor/garage.js +121 -0
  33. package/dist/sensor/humidity.js +52 -0
  34. package/dist/sensor/index.js +17 -11
  35. package/dist/sensor/leak.js +52 -0
  36. package/dist/sensor/licensePlate.js +70 -79
  37. package/dist/sensor/light.js +82 -38
  38. package/dist/sensor/lock.js +99 -0
  39. package/dist/sensor/motion.js +85 -70
  40. package/dist/sensor/object.js +73 -94
  41. package/dist/sensor/occupancy.js +52 -0
  42. package/dist/sensor/ptz.js +114 -100
  43. package/dist/sensor/securitySystem.js +98 -0
  44. package/dist/sensor/siren.js +75 -43
  45. package/dist/sensor/smoke.js +52 -0
  46. package/dist/sensor/spec.js +1 -0
  47. package/dist/sensor/switch.js +72 -0
  48. package/dist/sensor/temperature.js +52 -0
  49. package/dist/storage/index.js +1 -2
  50. package/dist/types.js +1 -0
  51. package/docs/.vitepress/config.ts +77 -0
  52. package/docs/.vitepress/theme/index.ts +5 -0
  53. package/docs/.vitepress/theme/style.css +117 -0
  54. package/docs/index.md +16 -0
  55. package/docs/logo.png +0 -0
  56. package/docs/public/apple-touch-icon.png +0 -0
  57. package/docs/public/favicon-16.ico +0 -0
  58. package/docs/public/favicon.ico +0 -0
  59. package/docs/public/logo.svg +1 -0
  60. package/examples/README.md +7 -0
  61. package/examples/getting-started.md +535 -0
  62. package/package.json +36 -23
  63. package/scripts/build-example-docs.mjs +62 -0
  64. package/tsconfig.node.json +3 -2
  65. package/typedoc.json +42 -0
  66. package/dist/sensor/guards.js +0 -133
  67. package/dist/sensor/types.js +0 -46
  68. package/dist/service/base.js +0 -96
  69. package/dist/service/index.js +0 -3
  70. /package/dist/camera/{types.js → enums.js} +0 -0
  71. /package/dist/{manager/types.js → camera/frames.js} +0 -0
  72. /package/dist/{plugin/types.js → internal/camera-config-internal.js} +0 -0
  73. /package/dist/{service/services.js → internal/camera-enums.js} +0 -0
  74. /package/dist/{service/types.js → internal/camera-wire.js} +0 -0
  75. /package/dist/{storage/schema.js → internal/manager-rpc.js} +0 -0
  76. /package/dist/{storage/storages.js → internal/sensor-rpc.js} +0 -0
@@ -1,120 +1,107 @@
1
- import { SensorType } from '../sensor/types.js';
2
1
  /**
3
- * Validate a plugin contract
4
- *
5
- * @param contract - The contract to validate
6
- *
7
- * @returns Whether the contract is valid
2
+ * Role a plugin plays in the system. The role decides which lifecycle hooks
3
+ * the host invokes and which contract validations apply (see helper.ts).
8
4
  */
9
- export function validateContract(contract) {
10
- if (!contract || typeof contract !== 'object') {
11
- return false;
12
- }
13
- const c = contract;
14
- // Required fields
15
- if (!Array.isArray(c.provides))
16
- return false;
17
- if (!Array.isArray(c.consumes))
18
- return false;
19
- if (typeof c.name !== 'string' || c.name.length === 0)
20
- return false;
21
- // Validate sensor types
22
- const validSensorTypes = Object.values(SensorType);
23
- for (const type of c.provides) {
24
- if (!validSensorTypes.includes(type)) {
25
- return false;
26
- }
27
- }
28
- for (const type of c.consumes) {
29
- if (!validSensorTypes.includes(type)) {
30
- return false;
31
- }
32
- }
33
- // Optional fields validation
34
- if (c.pythonVersion !== undefined) {
35
- if (!['3.10', '3.11', '3.12'].includes(c.pythonVersion)) {
36
- return false;
37
- }
38
- }
39
- if (c.dependencies !== undefined && !Array.isArray(c.dependencies)) {
40
- return false;
41
- }
42
- if (c.cameraController !== undefined && typeof c.cameraController !== 'boolean') {
43
- return false;
44
- }
45
- return true;
46
- }
5
+ export var PluginRole;
6
+ (function (PluginRole) {
7
+ /**
8
+ * Cloud-service integration that manages its own cameras end-to-end via a
9
+ * vendor account (e.g. a vendor SDK / cloud API). The hub owns camera
10
+ * creation, streaming and sensors; it cannot expose sensors for cameras
11
+ * owned by other plugins.
12
+ */
13
+ PluginRole["Hub"] = "hub";
14
+ /**
15
+ * Adds sensors to existing cameras without owning the camera itself.
16
+ * Typical use: a detection plugin that consumes another plugin's video
17
+ * frames and emits motion / object / face detections back into the system.
18
+ */
19
+ PluginRole["SensorProvider"] = "sensorProvider";
20
+ /**
21
+ * Manages cameras and their media streams (ONVIF, RTSP, generic IP, ...).
22
+ * The plugin is responsible for stream URLs, PTZ, snapshots, and the
23
+ * lifecycle hooks in BasePlugin. It does not produce sensors for foreign
24
+ * cameras.
25
+ */
26
+ PluginRole["CameraController"] = "cameraController";
27
+ /**
28
+ * Combined role: plugin both manages cameras and exposes sensors (its own
29
+ * cameras and, when consumes is set, also foreign cameras). Used by
30
+ * integrations that ship a complete camera + detection stack.
31
+ */
32
+ PluginRole["CameraAndSensorProvider"] = "cameraAndSensorProvider";
33
+ })(PluginRole || (PluginRole = {}));
47
34
  /**
48
- * Check if a plugin is a provider (provides any sensors)
49
- *
50
- * @param contract - The plugin contract to check
51
- *
52
- * @returns Whether the plugin is a provider
35
+ * Capability flags a plugin advertises in its contract. The host uses these
36
+ * to decide which RPC handlers to wire up and which UI affordances to show.
53
37
  */
54
- export function isProvider(contract) {
55
- return contract.provides.length > 0;
56
- }
38
+ export var PluginInterface;
39
+ (function (PluginInterface) {
40
+ /** Implements MotionDetectionInterface (video-based motion detection). */
41
+ PluginInterface["MotionDetection"] = "MotionDetection";
42
+ /** Implements ObjectDetectionInterface (e.g. person, vehicle, animal). */
43
+ PluginInterface["ObjectDetection"] = "ObjectDetection";
44
+ /** Implements AudioDetectionInterface (event/keyword audio detection). */
45
+ PluginInterface["AudioDetection"] = "AudioDetection";
46
+ /**
47
+ * Implements FaceDetectionInterface (face localisation + embeddings). The
48
+ * NVR owns matching against enrolled faces; the plugin only emits
49
+ * detections + embeddings.
50
+ */
51
+ PluginInterface["FaceDetection"] = "FaceDetection";
52
+ /** Implements LicensePlateDetectionInterface (plate localisation + OCR). */
53
+ PluginInterface["LicensePlateDetection"] = "LicensePlateDetection";
54
+ /**
55
+ * Implements ClassifierDetectionInterface (generic image classification
56
+ * emitting attribute/label pairs).
57
+ */
58
+ PluginInterface["ClassifierDetection"] = "ClassifierDetection";
59
+ /**
60
+ * Implements ClipDetectionInterface (CLIP image and text embeddings used
61
+ * for semantic search).
62
+ */
63
+ PluginInterface["ClipDetection"] = "ClipDetection";
64
+ /**
65
+ * Implements DiscoveryProvider — plugin can scan the network for new
66
+ * cameras and adopt them. Only valid for camera-controlling roles.
67
+ */
68
+ PluginInterface["DiscoveryProvider"] = "DiscoveryProvider";
69
+ /**
70
+ * Implements NVRInterface — persists events and recordings, and serves
71
+ * them back to the UI / mobile clients. Exactly one plugin per host
72
+ * fills this role at runtime.
73
+ */
74
+ PluginInterface["NVR"] = "NVR";
75
+ /**
76
+ * Implements NotifierInterface (getDevices, sendNotification, ...). Lets
77
+ * the central NotificationManager dispatch notifications to this plugin
78
+ * regardless of role — see notifier.ts.
79
+ */
80
+ PluginInterface["Notifier"] = "Notifier";
81
+ /**
82
+ * Implements the OAuthCapable base interface (getOAuthMetadata,
83
+ * getOAuthState, disconnect) plus at least one flow sub-interface below —
84
+ * see oauth.ts.
85
+ */
86
+ PluginInterface["OAuthCapable"] = "OAuthCapable";
87
+ /** Implements OAuthDeviceFlowCapable (RFC 8628 Device Authorization Grant). */
88
+ PluginInterface["OAuthDeviceFlow"] = "OAuthDeviceFlow";
89
+ /** Implements OAuthAuthCodeFlowCapable (Authorization Code Flow + PKCE). */
90
+ PluginInterface["OAuthAuthCodeFlow"] = "OAuthAuthCodeFlow";
91
+ /** Implements OAuthClientCredentialsCapable (user-supplied client_id + client_secret). */
92
+ PluginInterface["OAuthClientCredentials"] = "OAuthClientCredentials";
93
+ })(PluginInterface || (PluginInterface = {}));
57
94
  /**
58
- * Check if a plugin is a consumer (consumes any sensors)
59
- *
60
- * @param contract - The plugin contract to check
61
- *
62
- * @returns Whether the plugin is a consumer
95
+ * Permission a plugin requests so it can call a host-provided system feature.
96
+ * Each capability gates one outgoing SDK call — calls without the matching
97
+ * capability are rejected by the host.
63
98
  */
64
- export function isConsumer(contract) {
65
- return contract.consumes.length > 0;
66
- }
67
- /**
68
- * Check if a plugin is a pure hub (only consumes, doesn't provide)
69
- *
70
- * @param contract - The plugin contract to check
71
- *
72
- * @returns Whether the plugin is a pure hub
73
- */
74
- export function isHub(contract) {
75
- return contract.provides.length === 0 && contract.consumes.length > 0;
76
- }
77
- /**
78
- * Check if a plugin provides a specific sensor type
79
- *
80
- * @param contract - The plugin contract to check
81
- *
82
- * @param type - The sensor type to check
83
- *
84
- * @returns Whether the plugin provides the sensor type
85
- */
86
- export function providesSensor(contract, type) {
87
- return contract.provides.includes(type);
88
- }
89
- /**
90
- * Check if a plugin consumes a specific sensor type
91
- *
92
- * @param contract - The plugin contract to check
93
- *
94
- * @param type - The sensor type to check
95
- *
96
- * @returns Whether the plugin consumes the sensor type
97
- */
98
- export function consumesSensor(contract, type) {
99
- return contract.consumes.includes(type);
100
- }
101
- /**
102
- * Check if a plugin is a camera controller (can create/manage cameras)
103
- *
104
- * @param contract - The plugin contract to check
105
- *
106
- * @returns Whether the plugin is a camera controller
107
- */
108
- export function isCameraController(contract) {
109
- return contract.cameraController === true;
110
- }
111
- /**
112
- * Check if a plugin is qualified (provides, consumes, or is camera controller)
113
- *
114
- * @param contract - The plugin contract to check
115
- *
116
- * @returns Whether the plugin is qualified
117
- */
118
- export function isQualifiedContract(contract) {
119
- return contract.provides.length > 0 || contract.consumes.length > 0 || contract.cameraController === true;
120
- }
99
+ export var PluginCapability;
100
+ (function (PluginCapability) {
101
+ /**
102
+ * Grants the plugin permission to call `api.notificationManager.publish`.
103
+ * Without this capability the host silently drops published notifications
104
+ * and logs an error.
105
+ */
106
+ PluginCapability["PublishNotifications"] = "publishNotifications";
107
+ })(PluginCapability || (PluginCapability = {}));
@@ -0,0 +1,277 @@
1
+ import { PluginCapability, PluginInterface, PluginRole } from './contract.js';
2
+ /**
3
+ * Check the structural validity of an unknown contract object — required
4
+ * fields present, enum values inside the accepted sets — and return one
5
+ * human-readable error per problem found. Returns an empty array when the
6
+ * contract is valid.
7
+ *
8
+ * @param contract - Untrusted candidate contract (e.g. from manifest JSON).
9
+ *
10
+ * @returns Error messages, empty if the contract is valid.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * import { getContractValidationErrors } from '@camera.ui/sdk';
15
+ *
16
+ * const errors = getContractValidationErrors(rawManifest);
17
+ * if (errors.length) throw new Error(errors.join('\n'));
18
+ * ```
19
+ */
20
+ export function getContractValidationErrors(contract) {
21
+ const errors = [];
22
+ if (!contract || typeof contract !== 'object') {
23
+ errors.push('Contract must be an object. Got: ' + (contract === null ? 'null' : typeof contract));
24
+ return errors;
25
+ }
26
+ const c = contract;
27
+ const validRoles = Object.values(PluginRole);
28
+ // Import SensorType values dynamically to avoid circular dependency
29
+ const validSensorTypes = [
30
+ 'motion',
31
+ 'object',
32
+ 'audio',
33
+ 'face',
34
+ 'licensePlate',
35
+ 'classifier',
36
+ 'contact',
37
+ 'temperature',
38
+ 'humidity',
39
+ 'occupancy',
40
+ 'smoke',
41
+ 'leak',
42
+ 'light',
43
+ 'siren',
44
+ 'switch',
45
+ 'lock',
46
+ 'garage',
47
+ 'ptz',
48
+ 'securitySystem',
49
+ 'doorbell',
50
+ 'battery',
51
+ 'clip',
52
+ ];
53
+ // Check role
54
+ if (c.role === undefined) {
55
+ errors.push('Missing required field: "role"');
56
+ }
57
+ else if (typeof c.role !== 'string') {
58
+ errors.push(`Field "role" must be a string. Got: ${typeof c.role}`);
59
+ }
60
+ else if (!validRoles.includes(c.role)) {
61
+ errors.push(`Invalid role "${c.role}". Valid roles: ${validRoles.join(', ')}`);
62
+ }
63
+ // Check name
64
+ if (c.name === undefined) {
65
+ errors.push('Missing required field: "name"');
66
+ }
67
+ else if (typeof c.name !== 'string') {
68
+ errors.push(`Field "name" must be a string. Got: ${typeof c.name}`);
69
+ }
70
+ else if (c.name.length === 0) {
71
+ errors.push('Field "name" cannot be empty');
72
+ }
73
+ // Check provides
74
+ if (c.provides === undefined) {
75
+ errors.push('Missing required field: "provides"');
76
+ }
77
+ else if (!Array.isArray(c.provides)) {
78
+ errors.push(`Field "provides" must be an array. Got: ${typeof c.provides}`);
79
+ }
80
+ else {
81
+ for (const type of c.provides) {
82
+ if (!validSensorTypes.includes(type)) {
83
+ errors.push(`Invalid sensor type in "provides": "${type}". Valid types: ${validSensorTypes.join(', ')}`);
84
+ }
85
+ }
86
+ }
87
+ // Check consumes
88
+ if (c.consumes === undefined) {
89
+ errors.push('Missing required field: "consumes"');
90
+ }
91
+ else if (!Array.isArray(c.consumes)) {
92
+ errors.push(`Field "consumes" must be an array. Got: ${typeof c.consumes}`);
93
+ }
94
+ else {
95
+ for (const type of c.consumes) {
96
+ if (!validSensorTypes.includes(type)) {
97
+ errors.push(`Invalid sensor type in "consumes": "${type}". Valid types: ${validSensorTypes.join(', ')}`);
98
+ }
99
+ }
100
+ }
101
+ // Check interfaces
102
+ const validInterfaces = Object.values(PluginInterface);
103
+ if (c.interfaces === undefined) {
104
+ errors.push('Missing required field: "interfaces"');
105
+ }
106
+ else if (!Array.isArray(c.interfaces)) {
107
+ errors.push(`Field "interfaces" must be an array. Got: ${typeof c.interfaces}`);
108
+ }
109
+ else {
110
+ for (const iface of c.interfaces) {
111
+ if (!validInterfaces.includes(iface)) {
112
+ errors.push(`Invalid interface in "interfaces": "${iface}". Valid interfaces: ${validInterfaces.join(', ')}`);
113
+ }
114
+ }
115
+ }
116
+ // Check optional capabilities
117
+ const validCapabilities = Object.values(PluginCapability);
118
+ if (c.capabilities !== undefined) {
119
+ if (!Array.isArray(c.capabilities)) {
120
+ errors.push(`Field "capabilities" must be an array. Got: ${typeof c.capabilities}`);
121
+ }
122
+ else {
123
+ for (const cap of c.capabilities) {
124
+ if (!validCapabilities.includes(cap)) {
125
+ errors.push(`Invalid capability in "capabilities": "${cap}". Valid capabilities: ${validCapabilities.join(', ')}`);
126
+ }
127
+ }
128
+ }
129
+ }
130
+ // Check optional pythonVersion
131
+ if (c.pythonVersion !== undefined) {
132
+ if (!['3.11', '3.12'].includes(c.pythonVersion)) {
133
+ errors.push(`Invalid pythonVersion "${c.pythonVersion}". Valid versions: 3.11, 3.12`);
134
+ }
135
+ }
136
+ // Check optional dependencies
137
+ if (c.dependencies !== undefined && !Array.isArray(c.dependencies)) {
138
+ errors.push(`Field "dependencies" must be an array. Got: ${typeof c.dependencies}`);
139
+ }
140
+ return errors;
141
+ }
142
+ /**
143
+ * Enforce role-specific consistency rules on top of the structural check
144
+ * (e.g. SensorProvider plugins must declare at least one provided sensor;
145
+ * Hub plugins cannot expose sensors). Throws on the first violation.
146
+ *
147
+ * @param contract - Already-structurally-valid contract.
148
+ *
149
+ * @param pluginName - Optional plugin name; used to prefix error messages.
150
+ *
151
+ * @throws {Error} When the contract violates a role-specific rule.
152
+ *
153
+ * @example
154
+ * ```ts
155
+ * import { validateContractConsistency } from '@camera.ui/sdk';
156
+ *
157
+ * validateContractConsistency(contract, 'my-plugin');
158
+ * ```
159
+ */
160
+ export function validateContractConsistency(contract, pluginName) {
161
+ const prefix = pluginName ? `Plugin "${pluginName}": ` : '';
162
+ switch (contract.role) {
163
+ case PluginRole.Hub:
164
+ if (contract.provides.length > 0) {
165
+ throw new Error(`${prefix}Hub plugins cannot provide sensors.`);
166
+ }
167
+ break;
168
+ case PluginRole.SensorProvider:
169
+ if (contract.provides.length === 0) {
170
+ throw new Error(`${prefix}SensorProvider plugins must provide at least one sensor type.`);
171
+ }
172
+ break;
173
+ case PluginRole.CameraAndSensorProvider:
174
+ if (contract.provides.length === 0) {
175
+ throw new Error(`${prefix}CameraAndSensorProvider plugins must provide at least one sensor type.`);
176
+ }
177
+ break;
178
+ case PluginRole.CameraController:
179
+ // CameraController can have empty or filled provides array
180
+ // (sensors are only for its own cameras)
181
+ break;
182
+ }
183
+ }
184
+ /**
185
+ * Reports whether the plugin's role is Hub (vendor cloud integration that
186
+ * manages its own cameras end-to-end).
187
+ *
188
+ * @param contract - Plugin contract to inspect.
189
+ *
190
+ * @returns `true` if the role is {@link PluginRole.Hub}.
191
+ *
192
+ * @example
193
+ * ```ts
194
+ * import { isHub } from '@camera.ui/sdk';
195
+ *
196
+ * if (isHub(contract)) skipLocalDiscovery();
197
+ * ```
198
+ */
199
+ export function isHub(contract) {
200
+ return contract.role === PluginRole.Hub;
201
+ }
202
+ /**
203
+ * Reports whether the plugin is allowed to add sensors to cameras owned by
204
+ * other plugins (true for SensorProvider and CameraAndSensorProvider).
205
+ * Hub and pure CameraController plugins only see their own cameras.
206
+ *
207
+ * @param contract - Plugin contract to inspect.
208
+ *
209
+ * @returns `true` if the plugin may attach sensors to any camera.
210
+ *
211
+ * @example
212
+ * ```ts
213
+ * import { canProvideSensorsToAnyCameras } from '@camera.ui/sdk';
214
+ *
215
+ * if (canProvideSensorsToAnyCameras(contract)) listAllCameras();
216
+ * ```
217
+ */
218
+ export function canProvideSensorsToAnyCameras(contract) {
219
+ return contract.role === PluginRole.SensorProvider || contract.role === PluginRole.CameraAndSensorProvider;
220
+ }
221
+ /**
222
+ * Reports whether the plugin can create cameras (role is CameraController
223
+ * or CameraAndSensorProvider). Used to gate camera-creating operations such
224
+ * as DiscoveryProvider adoption.
225
+ *
226
+ * @param contract - Plugin contract to inspect.
227
+ *
228
+ * @returns `true` if the plugin may create cameras.
229
+ *
230
+ * @example
231
+ * ```ts
232
+ * import { canCreateCameras } from '@camera.ui/sdk';
233
+ *
234
+ * if (canCreateCameras(contract)) enableAdoption();
235
+ * ```
236
+ */
237
+ export function canCreateCameras(contract) {
238
+ return contract.role === PluginRole.CameraController || contract.role === PluginRole.CameraAndSensorProvider;
239
+ }
240
+ /**
241
+ * Reports whether the plugin implements the given capability.
242
+ *
243
+ * @param contract - Plugin contract to inspect.
244
+ *
245
+ * @param iface - Interface to check (e.g. `PluginInterface.DiscoveryProvider`).
246
+ *
247
+ * @returns `true` if `iface` is listed in the contract's `interfaces`.
248
+ *
249
+ * @example
250
+ * ```ts
251
+ * import { hasInterface, PluginInterface } from '@camera.ui/sdk';
252
+ *
253
+ * if (hasInterface(contract, PluginInterface.DiscoveryProvider)) startScan();
254
+ * ```
255
+ */
256
+ export function hasInterface(contract, iface) {
257
+ return contract.interfaces.includes(iface);
258
+ }
259
+ /**
260
+ * Reports whether the plugin requested the given capability.
261
+ *
262
+ * @param contract - Plugin contract to inspect.
263
+ *
264
+ * @param cap - Capability to check (e.g. `PluginCapability.PublishNotifications`).
265
+ *
266
+ * @returns `true` if `cap` is listed in the contract's `capabilities`.
267
+ *
268
+ * @example
269
+ * ```ts
270
+ * import { hasCapability, PluginCapability } from '@camera.ui/sdk';
271
+ *
272
+ * if (hasCapability(contract, PluginCapability.PublishNotifications)) allowPublish();
273
+ * ```
274
+ */
275
+ export function hasCapability(contract, cap) {
276
+ return contract.capabilities?.includes(cap) ?? false;
277
+ }
@@ -1,3 +1,6 @@
1
+ export * from './api.js';
1
2
  export * from './contract.js';
3
+ export * from './helper.js';
2
4
  export * from './interfaces.js';
3
- export * from './types.js';
5
+ export * from './notifier.js';
6
+ export * from './oauth.js';
@@ -1 +1,51 @@
1
- export {};
1
+ /**
2
+ * Base class every plugin extends. It wires up the three dependencies the
3
+ * host injects (logger, PluginAPI, DeviceStorage) and declares the lifecycle
4
+ * methods the host calls on the plugin.
5
+ *
6
+ * Lifecycle order: the host calls `configureCameras()` once at startup with
7
+ * every camera already assigned to this plugin, then calls `onCameraAdded()`
8
+ * / `onCameraReleased()` as the user adds or removes cameras at runtime.
9
+ *
10
+ * The generic `T` types `storage.values` so plugin code gets autocompletion
11
+ * for its own settings shape.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * export default class MyPlugin extends BasePlugin<MyStorage> {
16
+ * private state = new Map<string, MyState>();
17
+ *
18
+ * async configureCameras(cameras: CameraDevice[]): Promise<void> {
19
+ * for (const camera of cameras) await this.onCameraAdded(camera);
20
+ * }
21
+ *
22
+ * async onCameraAdded(camera: CameraDevice): Promise<void> {
23
+ * this.state.set(camera.id, await this.attach(camera));
24
+ * }
25
+ *
26
+ * async onCameraReleased(cameraId: string): Promise<void> {
27
+ * this.state.get(cameraId)?.dispose();
28
+ * this.state.delete(cameraId);
29
+ * }
30
+ * }
31
+ * ```
32
+ *
33
+ * @see /examples/getting-started — end-to-end walkthrough.
34
+ */
35
+ export class BasePlugin {
36
+ logger;
37
+ api;
38
+ storage;
39
+ constructor(logger, api, storage) {
40
+ this.logger = logger;
41
+ this.api = api;
42
+ this.storage = storage;
43
+ }
44
+ /**
45
+ * Override to register a JSON schema for the plugin-level settings form
46
+ * rendered in the UI. Default: no schema.
47
+ */
48
+ get storageSchema() {
49
+ return [];
50
+ }
51
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Generic notification types — domain-agnostic. The NotificationManager and
3
+ * notifier plugins talk over RPC and JSON-encode these types directly.
4
+ */
5
+ /**
6
+ * Severity classifies how urgent a Notification is. Notifiers map this to
7
+ * platform-specific delivery characteristics; the host bypasses user-configured
8
+ * Quiet Hours for `critical`.
9
+ */
10
+ export var Severity;
11
+ (function (Severity) {
12
+ /** Standard notification — default delivery (sound + banner). */
13
+ Severity["Info"] = "info";
14
+ /** Heightened attention; notifiers may use a different sound/colour. */
15
+ Severity["Warn"] = "warn";
16
+ /** Failure or action-required notification. */
17
+ Severity["Error"] = "error";
18
+ /**
19
+ * Highest-priority delivery on supporting notifiers; bypasses user-configured
20
+ * Quiet Hours on the host.
21
+ */
22
+ Severity["Critical"] = "critical";
23
+ })(Severity || (Severity = {}));
@@ -0,0 +1 @@
1
+ export {};