camstreamerlib 4.0.32 → 4.0.33-10a29ef

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 (45) hide show
  1. package/README.md +9 -0
  2. package/cjs/PlaneTrackerAPI.d.ts +42 -9
  3. package/cjs/PlaneTrackerAPI.js +2 -1
  4. package/cjs/VapixAPI.js +1 -1
  5. package/cjs/browser.index.d.ts +2 -0
  6. package/cjs/browser.index.js +18 -0
  7. package/cjs/helpers.js +1 -1
  8. package/cjs/node/HttpRequestSender.js +8 -5
  9. package/cjs/node/HttpRequestSender.test.d.ts +1 -0
  10. package/cjs/node/HttpRequestSender.test.js +161 -0
  11. package/cjs/types/PlaneTrackerAPI.d.ts +205 -26
  12. package/cjs/types/PlaneTrackerAPI.js +59 -15
  13. package/cjs/types/PlaneTrackerAPI.test.d.ts +1 -0
  14. package/cjs/types/PlaneTrackerAPI.test.js +90 -0
  15. package/cjs/types/ws/PlaneTrackerEvents.d.ts +47 -0
  16. package/cjs/types/ws/PlaneTrackerEvents.js +15 -1
  17. package/cjs/ws/PlaneTrackerEvents.d.ts +1 -1
  18. package/cjs/ws/PlaneTrackerEvents.js +2 -2
  19. package/cjs/ws/PlaneTrackerEvents.test.d.ts +1 -0
  20. package/cjs/ws/PlaneTrackerEvents.test.js +27 -0
  21. package/esm/PlaneTrackerAPI.js +2 -1
  22. package/esm/VapixAPI.js +1 -1
  23. package/esm/browser.index.js +2 -0
  24. package/esm/helpers.js +1 -1
  25. package/esm/node/HttpRequestSender.js +9 -6
  26. package/esm/node/HttpRequestSender.test.js +136 -0
  27. package/esm/types/PlaneTrackerAPI.js +59 -15
  28. package/esm/types/PlaneTrackerAPI.test.js +88 -0
  29. package/esm/types/ws/PlaneTrackerEvents.js +14 -0
  30. package/esm/ws/PlaneTrackerEvents.js +3 -3
  31. package/esm/ws/PlaneTrackerEvents.test.js +25 -0
  32. package/examples/README.md +43 -0
  33. package/examples/camstreamerlib.js +13201 -0
  34. package/examples/camswitcher-events.html +137 -0
  35. package/examples/planetracker-events.html +186 -0
  36. package/examples/utils/example.css +102 -0
  37. package/package.json +110 -103
  38. package/types/PlaneTrackerAPI.d.ts +42 -9
  39. package/types/browser.index.d.ts +2 -0
  40. package/types/node/HttpRequestSender.test.d.ts +1 -0
  41. package/types/types/PlaneTrackerAPI.d.ts +205 -26
  42. package/types/types/PlaneTrackerAPI.test.d.ts +1 -0
  43. package/types/types/ws/PlaneTrackerEvents.d.ts +47 -0
  44. package/types/ws/PlaneTrackerEvents.d.ts +1 -1
  45. package/types/ws/PlaneTrackerEvents.test.d.ts +1 -0
@@ -0,0 +1,88 @@
1
+ import { cameraSettingsSchema } from './PlaneTrackerAPI';
2
+ import { describe, test, expect } from '@jest/globals';
3
+ const AIRCRAFT_LABEL = {
4
+ firstRow: 'icao',
5
+ secondRow: 'call_sign',
6
+ thirdRow: 'blank',
7
+ fourthRow: 'registration',
8
+ opacity: 50,
9
+ };
10
+ const DEFAULT_DRONE_LABEL = { firstRow: 'remote_id', opacity: 30 };
11
+ describe('cameraSettingsSchema identificationLabel', () => {
12
+ test('defaults both aircraft and drone labels', () => {
13
+ expect(cameraSettingsSchema.parse({}).identificationLabel).toEqual({
14
+ aircraft: {
15
+ firstRow: 'icao',
16
+ secondRow: 'call_sign',
17
+ thirdRow: 'blank',
18
+ fourthRow: 'blank',
19
+ opacity: 30,
20
+ },
21
+ drone: DEFAULT_DRONE_LABEL,
22
+ });
23
+ });
24
+ test('parses the aircraft and drone label', () => {
25
+ const identificationLabel = { aircraft: AIRCRAFT_LABEL, drone: { firstRow: 'blank', opacity: 70 } };
26
+ expect(cameraSettingsSchema.parse({ identificationLabel }).identificationLabel).toEqual(identificationLabel);
27
+ });
28
+ test('defaults a missing drone label', () => {
29
+ expect(cameraSettingsSchema.parse({ identificationLabel: { aircraft: AIRCRAFT_LABEL } }).identificationLabel).toEqual({ aircraft: AIRCRAFT_LABEL, drone: DEFAULT_DRONE_LABEL });
30
+ });
31
+ test('converts the legacy flat label to the aircraft label', () => {
32
+ expect(cameraSettingsSchema.parse({ identificationLabel: AIRCRAFT_LABEL }).identificationLabel).toEqual({
33
+ aircraft: AIRCRAFT_LABEL,
34
+ drone: DEFAULT_DRONE_LABEL,
35
+ });
36
+ });
37
+ test('returns an independent drone label for each legacy parse', () => {
38
+ const first = cameraSettingsSchema.parse({ identificationLabel: AIRCRAFT_LABEL });
39
+ first.identificationLabel.drone.opacity = 90;
40
+ const second = cameraSettingsSchema.parse({ identificationLabel: AIRCRAFT_LABEL });
41
+ expect(second.identificationLabel.drone).toEqual(DEFAULT_DRONE_LABEL);
42
+ });
43
+ test('rejects an unknown drone label row', () => {
44
+ const identificationLabel = { aircraft: AIRCRAFT_LABEL, drone: { firstRow: 'icao', opacity: 30 } };
45
+ expect(cameraSettingsSchema.safeParse({ identificationLabel }).success).toBe(false);
46
+ });
47
+ });
48
+ const CAPTURE_CONFIG = { defaultCaptureSizeMeters: 50, captureSizeExtensionMeters: 20 };
49
+ const DEFAULT_CAPTURE_CONFIG = { defaultCaptureSizeMeters: 120, captureSizeExtensionMeters: 80 };
50
+ describe('cameraSettingsSchema cameraConfig', () => {
51
+ test('defaults both aircraft and drone capture config', () => {
52
+ expect(cameraSettingsSchema.parse({}).cameraConfig).toEqual({
53
+ aircraft: DEFAULT_CAPTURE_CONFIG,
54
+ drone: DEFAULT_CAPTURE_CONFIG,
55
+ });
56
+ });
57
+ test('parses independent aircraft and drone capture config', () => {
58
+ const cameraConfig = {
59
+ aircraft: CAPTURE_CONFIG,
60
+ drone: { defaultCaptureSizeMeters: 5, captureSizeExtensionMeters: 3 },
61
+ };
62
+ expect(cameraSettingsSchema.parse({ cameraConfig }).cameraConfig).toEqual(cameraConfig);
63
+ });
64
+ test('defaults a missing drone capture config', () => {
65
+ expect(cameraSettingsSchema.parse({ cameraConfig: { aircraft: CAPTURE_CONFIG } }).cameraConfig).toEqual({
66
+ aircraft: CAPTURE_CONFIG,
67
+ drone: DEFAULT_CAPTURE_CONFIG,
68
+ });
69
+ });
70
+ test('copies the legacy flat capture config to both aircraft and drone', () => {
71
+ expect(cameraSettingsSchema.parse({ cameraConfig: CAPTURE_CONFIG }).cameraConfig).toEqual({
72
+ aircraft: CAPTURE_CONFIG,
73
+ drone: CAPTURE_CONFIG,
74
+ });
75
+ });
76
+ test('returns independent aircraft and drone objects', () => {
77
+ const legacy = cameraSettingsSchema.parse({ cameraConfig: CAPTURE_CONFIG }).cameraConfig;
78
+ legacy.drone.defaultCaptureSizeMeters = 1;
79
+ expect(legacy.aircraft).toEqual(CAPTURE_CONFIG);
80
+ const first = cameraSettingsSchema.parse({}).cameraConfig;
81
+ first.drone.defaultCaptureSizeMeters = 1;
82
+ expect(cameraSettingsSchema.parse({}).cameraConfig.drone).toEqual(DEFAULT_CAPTURE_CONFIG);
83
+ });
84
+ test('rejects a non-positive drone capture size', () => {
85
+ const cameraConfig = { aircraft: CAPTURE_CONFIG, drone: { ...CAPTURE_CONFIG, defaultCaptureSizeMeters: 0 } };
86
+ expect(cameraSettingsSchema.safeParse({ cameraConfig }).success).toBe(false);
87
+ });
88
+ });
@@ -3,6 +3,7 @@ import { blackListSchema, friendlyListSchema, priorityListSchema, trackingModeSc
3
3
  const wsApiFlightDataSchema = z.object({
4
4
  targetId: z.string(),
5
5
  icao: z.string(),
6
+ callsign: z.string().optional(),
6
7
  domain: domainIdSchema,
7
8
  categoryId: z.string(),
8
9
  groupId: z.string().optional(),
@@ -44,6 +45,18 @@ const apiUserSchema = userSchema.extend({
44
45
  ip: z.string(),
45
46
  userPriority: z.number(),
46
47
  });
48
+ export const USER_PRIORITY_MIN = 1;
49
+ export const USER_PRIORITY_MAX = 255;
50
+ const USER_PRIORITY_ERROR = `userPriority must be an integer between ${USER_PRIORITY_MIN} and ${USER_PRIORITY_MAX}, where ${USER_PRIORITY_MIN} is the highest priority`;
51
+ export const apiUserInputSchema = apiUserSchema.omit({ ip: true }).extend({
52
+ userId: z.string().nonempty(),
53
+ userName: z.string().nonempty(),
54
+ userPriority: z
55
+ .number()
56
+ .int(USER_PRIORITY_ERROR)
57
+ .min(USER_PRIORITY_MIN, USER_PRIORITY_ERROR)
58
+ .max(USER_PRIORITY_MAX, USER_PRIORITY_ERROR),
59
+ });
47
60
  export var EUserActions;
48
61
  (function (EUserActions) {
49
62
  EUserActions["TRACK_ICAO"] = "trackIcao.cgi";
@@ -67,6 +80,7 @@ const eventsDataSchema = z.union([
67
80
  type: z.literal('TRACKING_START'),
68
81
  icao: z.string(),
69
82
  targetId: z.string(),
83
+ callsign: z.string().optional(),
70
84
  domain: domainIdSchema,
71
85
  categoryId: z.string(),
72
86
  }),
@@ -1,10 +1,10 @@
1
1
  import { WsEvents } from '../internal/WsEvents';
2
- import { ptrEventsSchema } from '../types/ws/PlaneTrackerEvents';
2
+ import { ptrEventsSchema, apiUserInputSchema, } from '../types/ws/PlaneTrackerEvents';
3
3
  export class PlaneTrackerEvents extends WsEvents {
4
4
  _apiUser;
5
- constructor(ws, _apiUser) {
5
+ constructor(ws, apiUser) {
6
6
  super((data) => ptrEventsSchema.parse(data), ws);
7
- this._apiUser = _apiUser;
7
+ this._apiUser = apiUserInputSchema.parse(apiUser);
8
8
  this.ws.onOpen = this.sendInitMsg;
9
9
  }
10
10
  sendInitMsg = () => {
@@ -0,0 +1,25 @@
1
+ import { PlaneTrackerEvents } from './PlaneTrackerEvents';
2
+ import { USER_PRIORITY_MAX, USER_PRIORITY_MIN } from '../types/ws/PlaneTrackerEvents';
3
+ const createWsClient = () => ({
4
+ send: jest.fn(),
5
+ onOpen: undefined,
6
+ onMessage: undefined,
7
+ });
8
+ const VALID_USER = { userId: 'asd', userName: 'Asd', userPriority: 1 };
9
+ describe('PlaneTrackerEvents', () => {
10
+ it.each([0, -1, USER_PRIORITY_MAX + 1, 1.5])('rejects userPriority %p', (userPriority) => {
11
+ expect(() => new PlaneTrackerEvents(createWsClient(), { ...VALID_USER, userPriority })).toThrow(/userPriority must be an integer/);
12
+ });
13
+ it.each([USER_PRIORITY_MIN, USER_PRIORITY_MAX])('accepts userPriority %p', (userPriority) => {
14
+ expect(() => new PlaneTrackerEvents(createWsClient(), { ...VALID_USER, userPriority })).not.toThrow();
15
+ });
16
+ it('rejects an empty userId', () => {
17
+ expect(() => new PlaneTrackerEvents(createWsClient(), { ...VALID_USER, userId: '' })).toThrow();
18
+ });
19
+ it('sends USER_INFO once the socket opens', () => {
20
+ const ws = createWsClient();
21
+ new PlaneTrackerEvents(ws, VALID_USER);
22
+ ws.onOpen();
23
+ expect(ws.send).toHaveBeenCalledWith(JSON.stringify({ type: 'USER_INFO', ...VALID_USER }));
24
+ });
25
+ });
@@ -0,0 +1,43 @@
1
+ # Examples
2
+
3
+ Standalone browser examples that use CamStreamerLib directly from a `<script type="module">`,
4
+ without a bundler or a framework.
5
+
6
+ ## Build the browser bundle
7
+
8
+ ```bash
9
+ npm install
10
+ npm run build:browser
11
+ ```
12
+
13
+ ## Run
14
+
15
+ Modules can't be loaded over `file://`, so serve the folder over HTTP:
16
+
17
+ ```bash
18
+ npx serve examples
19
+ ```
20
+
21
+ Both pages connect to a camera through the Device Connect proxy. Fill in the device host
22
+ (`<MAC_ADDRESS>.device-connect.net`) and the device access token in the form, or hardcode them in the
23
+ `DEVICE_HOST` / `DEVICE_ACCESS_TOKEN` constants at the top of the page's script to have the form
24
+ prefilled. See [doc/Client.md](../doc/Client.md).
25
+
26
+ ## camswitcher-events.html
27
+
28
+ Logs every CamSwitcher event. `DeviceConnectClient` (HTTP) backs `CamSwitcherAPI` to obtain the
29
+ websocket authorization token; `DeviceConnectWsClient` (websocket) backs `CamSwitcherEvents`, which
30
+ validates and dispatches the events. Connection state is taken from the `authorization` event.
31
+
32
+ See [doc/ws/CamSwitcherEvents.md](../doc/ws/CamSwitcherEvents.md).
33
+
34
+ ## planetracker-events.html
35
+
36
+ Logs every PlaneTracker event. PlaneTracker needs no authorization token — `PlaneTrackerEvents`
37
+ announces the connecting user (`userId` / `userName` / `userPriority`, editable in the form) as the
38
+ init message instead, so only the websocket client is needed.
39
+
40
+ `CAMERA_POSITION` and `FLIGHT_LIST` arrive continuously, so by default they only update the summary
41
+ line above the log; tick the checkbox to log them as well.
42
+
43
+ See [doc/ws/PlaneTrackerEvents.md](../doc/ws/PlaneTrackerEvents.md).