camstreamerlib 4.0.1 → 4.0.3

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.
@@ -26,11 +26,14 @@ export declare class PlaneTrackerAPI<Client extends IClient<TResponse, any>> ext
26
26
  protocol: "https" | "http" | "https_insecure";
27
27
  };
28
28
  units: "metric" | "imperial";
29
- trackingDomain: "adsb" | "dronetag";
30
29
  adsbSource: {
31
30
  ip: string;
31
+ enabled: boolean;
32
32
  port: number;
33
33
  };
34
+ dronetagSource: {
35
+ enabled: boolean;
36
+ };
34
37
  cameraCalibrationProcessConfig: {
35
38
  nightSkyCalibrationEnabled: boolean;
36
39
  scheduleNightSkyCalibrationTimestamp: number;
@@ -39,7 +42,6 @@ export declare class PlaneTrackerAPI<Client extends IClient<TResponse, any>> ext
39
42
  cameraConfig: {
40
43
  defaultCaptureSizeMeters: number;
41
44
  captureSizeExtensionMeters: number;
42
- maxZoomLevel?: number | undefined;
43
45
  };
44
46
  stream: {
45
47
  width: number;
@@ -1,32 +1,61 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.toSnakeCaseDeep = exports.toSnakeCase = exports.toCamelCaseDeep = exports.toCamelCase = void 0;
4
- const lodash_es_1 = require("lodash-es");
5
- const toCamelCase = (o) => (0, lodash_es_1.mapKeys)(o, camelCaseKey);
4
+ const toCamelCase = (o) => mapKeys(o, camelCase);
6
5
  exports.toCamelCase = toCamelCase;
7
6
  const toCamelCaseDeep = (o) => {
8
- return mapKeysDeep(o, camelCaseKey);
7
+ return mapKeysDeep(o, camelCase);
9
8
  };
10
9
  exports.toCamelCaseDeep = toCamelCaseDeep;
11
- const toSnakeCase = (o) => (0, lodash_es_1.mapKeys)(o, snakeCaseKey);
10
+ const toSnakeCase = (o) => mapKeys(o, snakeCase);
12
11
  exports.toSnakeCase = toSnakeCase;
13
12
  const toSnakeCaseDeep = (o) => {
14
- return mapKeysDeep(o, snakeCaseKey);
13
+ return mapKeysDeep(o, snakeCase);
15
14
  };
16
15
  exports.toSnakeCaseDeep = toSnakeCaseDeep;
17
- const camelCaseKey = (_, key) => (0, lodash_es_1.camelCase)(key);
18
- const snakeCaseKey = (_, key) => (0, lodash_es_1.snakeCase)(key);
16
+ const splitWords = (input) => {
17
+ if (!input)
18
+ return [];
19
+ return (input
20
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
21
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
22
+ .replace(/[^a-zA-Z0-9]+/g, ' ')
23
+ .trim()
24
+ .split(/\s+/)
25
+ .map((w) => w.toLowerCase())
26
+ .filter((w) => w.length > 0));
27
+ };
28
+ const camelCase = (key) => {
29
+ const words = splitWords(key);
30
+ if (words.length === 0)
31
+ return '';
32
+ const [first, ...rest] = words;
33
+ return first + rest.map((w) => w[0].toUpperCase() + w.slice(1)).join('');
34
+ };
35
+ const snakeCase = (key) => splitWords(key).join('_');
36
+ const isPlainObject = (value) => {
37
+ if (value === null || typeof value !== 'object')
38
+ return false;
39
+ const proto = Object.getPrototypeOf(value);
40
+ return proto === null || proto === Object.prototype;
41
+ };
42
+ const mapKeys = (obj, cb) => {
43
+ const result = {};
44
+ for (const key of Object.keys(obj)) {
45
+ result[cb(key)] = obj[key];
46
+ }
47
+ return result;
48
+ };
19
49
  const mapKeysDeep = (obj, cb) => {
20
50
  if (Array.isArray(obj)) {
21
- return obj.map((item) => {
22
- return mapKeysDeep(item, cb);
23
- });
51
+ return obj.map((item) => mapKeysDeep(item, cb));
24
52
  }
25
- if (typeof obj !== 'object' || (0, lodash_es_1.isPlainObject)(obj) === false) {
53
+ if (!isPlainObject(obj)) {
26
54
  return obj;
27
55
  }
28
- const result = (0, lodash_es_1.mapKeys)(obj, cb);
29
- return (0, lodash_es_1.mapValues)(result, (value) => {
30
- return mapKeysDeep(value, cb);
31
- });
56
+ const result = {};
57
+ for (const key of Object.keys(obj)) {
58
+ result[cb(key)] = mapKeysDeep(obj[key], cb);
59
+ }
60
+ return result;
32
61
  };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const transformers_1 = require("./transformers");
4
+ const globals_1 = require("@jest/globals");
5
+ (0, globals_1.describe)('transformers', () => {
6
+ (0, globals_1.describe)('toCamelCase', () => {
7
+ (0, globals_1.test)('converts snake_case keys to camelCase', () => {
8
+ (0, globals_1.expect)((0, transformers_1.toCamelCase)({ foo_bar: 1, baz_qux_quux: 2 })).toEqual({ fooBar: 1, bazQuxQuux: 2 });
9
+ });
10
+ (0, globals_1.test)('converts kebab-case keys to camelCase', () => {
11
+ (0, globals_1.expect)((0, transformers_1.toCamelCase)({ 'foo-bar': 1, 'baz-qux': 2 })).toEqual({ fooBar: 1, bazQux: 2 });
12
+ });
13
+ (0, globals_1.test)('preserves already-camelCase keys', () => {
14
+ (0, globals_1.expect)((0, transformers_1.toCamelCase)({ fooBar: 1 })).toEqual({ fooBar: 1 });
15
+ });
16
+ (0, globals_1.test)('converts PascalCase keys to camelCase', () => {
17
+ (0, globals_1.expect)((0, transformers_1.toCamelCase)({ FooBar: 1 })).toEqual({ fooBar: 1 });
18
+ });
19
+ (0, globals_1.test)('handles consecutive uppercase runs (acronyms)', () => {
20
+ (0, globals_1.expect)((0, transformers_1.toCamelCase)({ HTTPServer: 1, parseURL: 2 })).toEqual({ httpServer: 1, parseUrl: 2 });
21
+ });
22
+ (0, globals_1.test)('handles dot.case keys', () => {
23
+ (0, globals_1.expect)((0, transformers_1.toCamelCase)({ 'foo.bar.baz': 1 })).toEqual({ fooBarBaz: 1 });
24
+ });
25
+ (0, globals_1.test)('does not recurse into nested objects', () => {
26
+ const input = { outer_key: { inner_key: 1 } };
27
+ (0, globals_1.expect)((0, transformers_1.toCamelCase)(input)).toEqual({ outerKey: { inner_key: 1 } });
28
+ });
29
+ (0, globals_1.test)('handles empty object', () => {
30
+ (0, globals_1.expect)((0, transformers_1.toCamelCase)({})).toEqual({});
31
+ });
32
+ (0, globals_1.test)('handles keys with numbers', () => {
33
+ (0, globals_1.expect)((0, transformers_1.toCamelCase)({ foo_1_bar: 1, v2_api: 2 })).toEqual({ foo1Bar: 1, v2Api: 2 });
34
+ });
35
+ });
36
+ (0, globals_1.describe)('toCamelCaseDeep', () => {
37
+ (0, globals_1.test)('converts nested object keys recursively', () => {
38
+ const input = { outer_key: { inner_key: { deep_key: 1 } } };
39
+ (0, globals_1.expect)((0, transformers_1.toCamelCaseDeep)(input)).toEqual({ outerKey: { innerKey: { deepKey: 1 } } });
40
+ });
41
+ (0, globals_1.test)('converts keys inside arrays of objects', () => {
42
+ const input = { items_list: [{ item_name: 'a' }, { item_name: 'b' }] };
43
+ (0, globals_1.expect)((0, transformers_1.toCamelCaseDeep)(input)).toEqual({ itemsList: [{ itemName: 'a' }, { itemName: 'b' }] });
44
+ });
45
+ (0, globals_1.test)('leaves primitive array values untouched', () => {
46
+ const input = { values_list: [1, 2, 3] };
47
+ (0, globals_1.expect)((0, transformers_1.toCamelCaseDeep)(input)).toEqual({ valuesList: [1, 2, 3] });
48
+ });
49
+ (0, globals_1.test)('does not recurse into non-plain objects (e.g. Date)', () => {
50
+ const date = new Date(0);
51
+ const input = { created_at: date };
52
+ const result = (0, transformers_1.toCamelCaseDeep)(input);
53
+ (0, globals_1.expect)(result.createdAt).toBe(date);
54
+ });
55
+ (0, globals_1.test)('handles null values', () => {
56
+ (0, globals_1.expect)((0, transformers_1.toCamelCaseDeep)({ foo_bar: null })).toEqual({ fooBar: null });
57
+ });
58
+ });
59
+ (0, globals_1.describe)('toSnakeCase', () => {
60
+ (0, globals_1.test)('converts camelCase keys to snake_case', () => {
61
+ (0, globals_1.expect)((0, transformers_1.toSnakeCase)({ fooBar: 1, bazQuxQuux: 2 })).toEqual({ foo_bar: 1, baz_qux_quux: 2 });
62
+ });
63
+ (0, globals_1.test)('converts PascalCase keys to snake_case', () => {
64
+ (0, globals_1.expect)((0, transformers_1.toSnakeCase)({ FooBar: 1 })).toEqual({ foo_bar: 1 });
65
+ });
66
+ (0, globals_1.test)('converts kebab-case keys to snake_case', () => {
67
+ (0, globals_1.expect)((0, transformers_1.toSnakeCase)({ 'foo-bar': 1 })).toEqual({ foo_bar: 1 });
68
+ });
69
+ (0, globals_1.test)('preserves already-snake_case keys', () => {
70
+ (0, globals_1.expect)((0, transformers_1.toSnakeCase)({ foo_bar: 1 })).toEqual({ foo_bar: 1 });
71
+ });
72
+ (0, globals_1.test)('handles acronyms', () => {
73
+ (0, globals_1.expect)((0, transformers_1.toSnakeCase)({ HTTPServer: 1, parseURL: 2 })).toEqual({ http_server: 1, parse_url: 2 });
74
+ });
75
+ (0, globals_1.test)('does not recurse into nested objects', () => {
76
+ const input = { outerKey: { innerKey: 1 } };
77
+ (0, globals_1.expect)((0, transformers_1.toSnakeCase)(input)).toEqual({ outer_key: { innerKey: 1 } });
78
+ });
79
+ });
80
+ (0, globals_1.describe)('toSnakeCaseDeep', () => {
81
+ (0, globals_1.test)('converts nested object keys recursively', () => {
82
+ const input = { outerKey: { innerKey: { deepKey: 1 } } };
83
+ (0, globals_1.expect)((0, transformers_1.toSnakeCaseDeep)(input)).toEqual({ outer_key: { inner_key: { deep_key: 1 } } });
84
+ });
85
+ (0, globals_1.test)('converts keys inside arrays of objects', () => {
86
+ const input = { itemsList: [{ itemName: 'a' }, { itemName: 'b' }] };
87
+ (0, globals_1.expect)((0, transformers_1.toSnakeCaseDeep)(input)).toEqual({ items_list: [{ item_name: 'a' }, { item_name: 'b' }] });
88
+ });
89
+ (0, globals_1.test)('handles null and undefined values', () => {
90
+ (0, globals_1.expect)((0, transformers_1.toSnakeCaseDeep)({ fooBar: null, bazQux: undefined })).toEqual({
91
+ foo_bar: null,
92
+ baz_qux: undefined,
93
+ });
94
+ });
95
+ });
96
+ });
@@ -60,17 +60,26 @@ export declare const widgetSchema: z.ZodObject<{
60
60
  declare const labelOptionsSchema: z.ZodUnion<[z.ZodLiteral<"blank">, z.ZodLiteral<"registration">, z.ZodLiteral<"call_sign">, z.ZodLiteral<"flight_number">, z.ZodLiteral<"icao">]>;
61
61
  export type TLabelOption = z.infer<typeof labelOptionsSchema>;
62
62
  export declare const cameraSettingsSchema: z.ZodObject<{
63
- trackingDomain: z.ZodDefault<z.ZodUnion<[z.ZodLiteral<"adsb">, z.ZodLiteral<"dronetag">]>>;
64
63
  units: z.ZodDefault<z.ZodUnion<[z.ZodLiteral<"metric">, z.ZodLiteral<"imperial">]>>;
65
64
  adsbSource: z.ZodDefault<z.ZodObject<{
65
+ enabled: z.ZodDefault<z.ZodBoolean>;
66
66
  ip: z.ZodUnion<[z.ZodString, z.ZodLiteral<"">]>;
67
67
  port: z.ZodNumber;
68
68
  }, "strip", z.ZodTypeAny, {
69
69
  ip: string;
70
+ enabled: boolean;
70
71
  port: number;
71
72
  }, {
72
73
  ip: string;
73
74
  port: number;
75
+ enabled?: boolean | undefined;
76
+ }>>;
77
+ dronetagSource: z.ZodDefault<z.ZodObject<{
78
+ enabled: z.ZodDefault<z.ZodBoolean>;
79
+ }, "strip", z.ZodTypeAny, {
80
+ enabled: boolean;
81
+ }, {
82
+ enabled?: boolean | undefined;
74
83
  }>>;
75
84
  camera: z.ZodDefault<z.ZodObject<{
76
85
  protocol: z.ZodUnion<[z.ZodLiteral<"http">, z.ZodLiteral<"https">, z.ZodLiteral<"https_insecure">]>;
@@ -105,15 +114,12 @@ export declare const cameraSettingsSchema: z.ZodObject<{
105
114
  focusCalibrationPoints?: string | undefined;
106
115
  }>>;
107
116
  cameraConfig: z.ZodDefault<z.ZodObject<{
108
- maxZoomLevel: z.ZodOptional<z.ZodNumber>;
109
117
  defaultCaptureSizeMeters: z.ZodDefault<z.ZodNumber>;
110
118
  captureSizeExtensionMeters: z.ZodDefault<z.ZodNumber>;
111
119
  }, "strip", z.ZodTypeAny, {
112
120
  defaultCaptureSizeMeters: number;
113
121
  captureSizeExtensionMeters: number;
114
- maxZoomLevel?: number | undefined;
115
122
  }, {
116
- maxZoomLevel?: number | undefined;
117
123
  defaultCaptureSizeMeters?: number | undefined;
118
124
  captureSizeExtensionMeters?: number | undefined;
119
125
  }>>;
@@ -411,11 +417,14 @@ export declare const cameraSettingsSchema: z.ZodObject<{
411
417
  protocol: "https" | "http" | "https_insecure";
412
418
  };
413
419
  units: "metric" | "imperial";
414
- trackingDomain: "adsb" | "dronetag";
415
420
  adsbSource: {
416
421
  ip: string;
422
+ enabled: boolean;
417
423
  port: number;
418
424
  };
425
+ dronetagSource: {
426
+ enabled: boolean;
427
+ };
419
428
  cameraCalibrationProcessConfig: {
420
429
  nightSkyCalibrationEnabled: boolean;
421
430
  scheduleNightSkyCalibrationTimestamp: number;
@@ -424,7 +433,6 @@ export declare const cameraSettingsSchema: z.ZodObject<{
424
433
  cameraConfig: {
425
434
  defaultCaptureSizeMeters: number;
426
435
  captureSizeExtensionMeters: number;
427
- maxZoomLevel?: number | undefined;
428
436
  };
429
437
  stream: {
430
438
  width: number;
@@ -538,10 +546,13 @@ export declare const cameraSettingsSchema: z.ZodObject<{
538
546
  protocol: "https" | "http" | "https_insecure";
539
547
  } | undefined;
540
548
  units?: "metric" | "imperial" | undefined;
541
- trackingDomain?: "adsb" | "dronetag" | undefined;
542
549
  adsbSource?: {
543
550
  ip: string;
544
551
  port: number;
552
+ enabled?: boolean | undefined;
553
+ } | undefined;
554
+ dronetagSource?: {
555
+ enabled?: boolean | undefined;
545
556
  } | undefined;
546
557
  cameraCalibrationProcessConfig?: {
547
558
  nightSkyCalibrationEnabled: boolean;
@@ -549,7 +560,6 @@ export declare const cameraSettingsSchema: z.ZodObject<{
549
560
  focusCalibrationPoints?: string | undefined;
550
561
  } | undefined;
551
562
  cameraConfig?: {
552
- maxZoomLevel?: number | undefined;
553
563
  defaultCaptureSizeMeters?: number | undefined;
554
564
  captureSizeExtensionMeters?: number | undefined;
555
565
  } | undefined;
@@ -41,14 +41,19 @@ const identificationLabelSchema = zod_1.z.object({
41
41
  opacity: zod_1.z.number().positive(),
42
42
  });
43
43
  exports.cameraSettingsSchema = zod_1.z.object({
44
- trackingDomain: zod_1.z.union([zod_1.z.literal('adsb'), zod_1.z.literal('dronetag')]).default('adsb'),
45
44
  units: zod_1.z.union([zod_1.z.literal('metric'), zod_1.z.literal('imperial')]).default('imperial'),
46
45
  adsbSource: zod_1.z
47
46
  .object({
47
+ enabled: zod_1.z.boolean().default(true),
48
48
  ip: zod_1.z.union([zod_1.z.string().ip(), zod_1.z.literal('')]),
49
49
  port: zod_1.z.number().positive().lt(65535),
50
50
  })
51
- .default({ ip: '', port: 30334 }),
51
+ .default({ enabled: true, ip: '', port: 30334 }),
52
+ dronetagSource: zod_1.z
53
+ .object({
54
+ enabled: zod_1.z.boolean().default(false),
55
+ })
56
+ .default({ enabled: false }),
52
57
  camera: exports.connectionSchema.default({
53
58
  protocol: 'http',
54
59
  ip: '127.0.0.1',
@@ -69,12 +74,10 @@ exports.cameraSettingsSchema = zod_1.z.object({
69
74
  }),
70
75
  cameraConfig: zod_1.z
71
76
  .object({
72
- maxZoomLevel: zod_1.z.number().optional(),
73
77
  defaultCaptureSizeMeters: zod_1.z.number().positive().default(120),
74
78
  captureSizeExtensionMeters: zod_1.z.number().positive().default(80),
75
79
  })
76
80
  .default({
77
- maxZoomLevel: 30,
78
81
  defaultCaptureSizeMeters: 120,
79
82
  captureSizeExtensionMeters: 80,
80
83
  }),
@@ -1,25 +1,54 @@
1
- import { camelCase, snakeCase, isPlainObject, mapKeys, mapValues } from 'lodash-es';
2
- export const toCamelCase = (o) => mapKeys(o, camelCaseKey);
1
+ export const toCamelCase = (o) => mapKeys(o, camelCase);
3
2
  export const toCamelCaseDeep = (o) => {
4
- return mapKeysDeep(o, camelCaseKey);
3
+ return mapKeysDeep(o, camelCase);
5
4
  };
6
- export const toSnakeCase = (o) => mapKeys(o, snakeCaseKey);
5
+ export const toSnakeCase = (o) => mapKeys(o, snakeCase);
7
6
  export const toSnakeCaseDeep = (o) => {
8
- return mapKeysDeep(o, snakeCaseKey);
7
+ return mapKeysDeep(o, snakeCase);
8
+ };
9
+ const splitWords = (input) => {
10
+ if (!input)
11
+ return [];
12
+ return (input
13
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
14
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
15
+ .replace(/[^a-zA-Z0-9]+/g, ' ')
16
+ .trim()
17
+ .split(/\s+/)
18
+ .map((w) => w.toLowerCase())
19
+ .filter((w) => w.length > 0));
20
+ };
21
+ const camelCase = (key) => {
22
+ const words = splitWords(key);
23
+ if (words.length === 0)
24
+ return '';
25
+ const [first, ...rest] = words;
26
+ return first + rest.map((w) => w[0].toUpperCase() + w.slice(1)).join('');
27
+ };
28
+ const snakeCase = (key) => splitWords(key).join('_');
29
+ const isPlainObject = (value) => {
30
+ if (value === null || typeof value !== 'object')
31
+ return false;
32
+ const proto = Object.getPrototypeOf(value);
33
+ return proto === null || proto === Object.prototype;
34
+ };
35
+ const mapKeys = (obj, cb) => {
36
+ const result = {};
37
+ for (const key of Object.keys(obj)) {
38
+ result[cb(key)] = obj[key];
39
+ }
40
+ return result;
9
41
  };
10
- const camelCaseKey = (_, key) => camelCase(key);
11
- const snakeCaseKey = (_, key) => snakeCase(key);
12
42
  const mapKeysDeep = (obj, cb) => {
13
43
  if (Array.isArray(obj)) {
14
- return obj.map((item) => {
15
- return mapKeysDeep(item, cb);
16
- });
44
+ return obj.map((item) => mapKeysDeep(item, cb));
17
45
  }
18
- if (typeof obj !== 'object' || isPlainObject(obj) === false) {
46
+ if (!isPlainObject(obj)) {
19
47
  return obj;
20
48
  }
21
- const result = mapKeys(obj, cb);
22
- return mapValues(result, (value) => {
23
- return mapKeysDeep(value, cb);
24
- });
49
+ const result = {};
50
+ for (const key of Object.keys(obj)) {
51
+ result[cb(key)] = mapKeysDeep(obj[key], cb);
52
+ }
53
+ return result;
25
54
  };
@@ -0,0 +1,94 @@
1
+ import { toCamelCase, toCamelCaseDeep, toSnakeCase, toSnakeCaseDeep } from './transformers';
2
+ import { describe, test, expect } from '@jest/globals';
3
+ describe('transformers', () => {
4
+ describe('toCamelCase', () => {
5
+ test('converts snake_case keys to camelCase', () => {
6
+ expect(toCamelCase({ foo_bar: 1, baz_qux_quux: 2 })).toEqual({ fooBar: 1, bazQuxQuux: 2 });
7
+ });
8
+ test('converts kebab-case keys to camelCase', () => {
9
+ expect(toCamelCase({ 'foo-bar': 1, 'baz-qux': 2 })).toEqual({ fooBar: 1, bazQux: 2 });
10
+ });
11
+ test('preserves already-camelCase keys', () => {
12
+ expect(toCamelCase({ fooBar: 1 })).toEqual({ fooBar: 1 });
13
+ });
14
+ test('converts PascalCase keys to camelCase', () => {
15
+ expect(toCamelCase({ FooBar: 1 })).toEqual({ fooBar: 1 });
16
+ });
17
+ test('handles consecutive uppercase runs (acronyms)', () => {
18
+ expect(toCamelCase({ HTTPServer: 1, parseURL: 2 })).toEqual({ httpServer: 1, parseUrl: 2 });
19
+ });
20
+ test('handles dot.case keys', () => {
21
+ expect(toCamelCase({ 'foo.bar.baz': 1 })).toEqual({ fooBarBaz: 1 });
22
+ });
23
+ test('does not recurse into nested objects', () => {
24
+ const input = { outer_key: { inner_key: 1 } };
25
+ expect(toCamelCase(input)).toEqual({ outerKey: { inner_key: 1 } });
26
+ });
27
+ test('handles empty object', () => {
28
+ expect(toCamelCase({})).toEqual({});
29
+ });
30
+ test('handles keys with numbers', () => {
31
+ expect(toCamelCase({ foo_1_bar: 1, v2_api: 2 })).toEqual({ foo1Bar: 1, v2Api: 2 });
32
+ });
33
+ });
34
+ describe('toCamelCaseDeep', () => {
35
+ test('converts nested object keys recursively', () => {
36
+ const input = { outer_key: { inner_key: { deep_key: 1 } } };
37
+ expect(toCamelCaseDeep(input)).toEqual({ outerKey: { innerKey: { deepKey: 1 } } });
38
+ });
39
+ test('converts keys inside arrays of objects', () => {
40
+ const input = { items_list: [{ item_name: 'a' }, { item_name: 'b' }] };
41
+ expect(toCamelCaseDeep(input)).toEqual({ itemsList: [{ itemName: 'a' }, { itemName: 'b' }] });
42
+ });
43
+ test('leaves primitive array values untouched', () => {
44
+ const input = { values_list: [1, 2, 3] };
45
+ expect(toCamelCaseDeep(input)).toEqual({ valuesList: [1, 2, 3] });
46
+ });
47
+ test('does not recurse into non-plain objects (e.g. Date)', () => {
48
+ const date = new Date(0);
49
+ const input = { created_at: date };
50
+ const result = toCamelCaseDeep(input);
51
+ expect(result.createdAt).toBe(date);
52
+ });
53
+ test('handles null values', () => {
54
+ expect(toCamelCaseDeep({ foo_bar: null })).toEqual({ fooBar: null });
55
+ });
56
+ });
57
+ describe('toSnakeCase', () => {
58
+ test('converts camelCase keys to snake_case', () => {
59
+ expect(toSnakeCase({ fooBar: 1, bazQuxQuux: 2 })).toEqual({ foo_bar: 1, baz_qux_quux: 2 });
60
+ });
61
+ test('converts PascalCase keys to snake_case', () => {
62
+ expect(toSnakeCase({ FooBar: 1 })).toEqual({ foo_bar: 1 });
63
+ });
64
+ test('converts kebab-case keys to snake_case', () => {
65
+ expect(toSnakeCase({ 'foo-bar': 1 })).toEqual({ foo_bar: 1 });
66
+ });
67
+ test('preserves already-snake_case keys', () => {
68
+ expect(toSnakeCase({ foo_bar: 1 })).toEqual({ foo_bar: 1 });
69
+ });
70
+ test('handles acronyms', () => {
71
+ expect(toSnakeCase({ HTTPServer: 1, parseURL: 2 })).toEqual({ http_server: 1, parse_url: 2 });
72
+ });
73
+ test('does not recurse into nested objects', () => {
74
+ const input = { outerKey: { innerKey: 1 } };
75
+ expect(toSnakeCase(input)).toEqual({ outer_key: { innerKey: 1 } });
76
+ });
77
+ });
78
+ describe('toSnakeCaseDeep', () => {
79
+ test('converts nested object keys recursively', () => {
80
+ const input = { outerKey: { innerKey: { deepKey: 1 } } };
81
+ expect(toSnakeCaseDeep(input)).toEqual({ outer_key: { inner_key: { deep_key: 1 } } });
82
+ });
83
+ test('converts keys inside arrays of objects', () => {
84
+ const input = { itemsList: [{ itemName: 'a' }, { itemName: 'b' }] };
85
+ expect(toSnakeCaseDeep(input)).toEqual({ items_list: [{ item_name: 'a' }, { item_name: 'b' }] });
86
+ });
87
+ test('handles null and undefined values', () => {
88
+ expect(toSnakeCaseDeep({ fooBar: null, bazQux: undefined })).toEqual({
89
+ foo_bar: null,
90
+ baz_qux: undefined,
91
+ });
92
+ });
93
+ });
94
+ });
@@ -38,14 +38,19 @@ const identificationLabelSchema = z.object({
38
38
  opacity: z.number().positive(),
39
39
  });
40
40
  export const cameraSettingsSchema = z.object({
41
- trackingDomain: z.union([z.literal('adsb'), z.literal('dronetag')]).default('adsb'),
42
41
  units: z.union([z.literal('metric'), z.literal('imperial')]).default('imperial'),
43
42
  adsbSource: z
44
43
  .object({
44
+ enabled: z.boolean().default(true),
45
45
  ip: z.union([z.string().ip(), z.literal('')]),
46
46
  port: z.number().positive().lt(65535),
47
47
  })
48
- .default({ ip: '', port: 30334 }),
48
+ .default({ enabled: true, ip: '', port: 30334 }),
49
+ dronetagSource: z
50
+ .object({
51
+ enabled: z.boolean().default(false),
52
+ })
53
+ .default({ enabled: false }),
49
54
  camera: connectionSchema.default({
50
55
  protocol: 'http',
51
56
  ip: '127.0.0.1',
@@ -66,12 +71,10 @@ export const cameraSettingsSchema = z.object({
66
71
  }),
67
72
  cameraConfig: z
68
73
  .object({
69
- maxZoomLevel: z.number().optional(),
70
74
  defaultCaptureSizeMeters: z.number().positive().default(120),
71
75
  captureSizeExtensionMeters: z.number().positive().default(80),
72
76
  })
73
77
  .default({
74
- maxZoomLevel: 30,
75
78
  defaultCaptureSizeMeters: 120,
76
79
  captureSizeExtensionMeters: 80,
77
80
  }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "camstreamerlib",
3
- "version": "4.0.1",
3
+ "version": "4.0.3",
4
4
  "description": "Helper library for CamStreamer ACAP applications.",
5
5
  "prettier": "@camstreamer/prettier-config",
6
6
  "engine": {
@@ -10,7 +10,6 @@
10
10
  "adm-zip": "^0.5.9",
11
11
  "eventemitter2": "^5.0.1",
12
12
  "fast-xml-parser": "^5.3.6",
13
- "lodash-es": "^4.18.1",
14
13
  "type-fest": "^4.41.0",
15
14
  "undici": "^6.21.3",
16
15
  "ws": "^8.18.0",
@@ -21,7 +20,6 @@
21
20
  "@camstreamer/prettier-config": "^2.0.4",
22
21
  "@types/adm-zip": "^0.5.5",
23
22
  "@types/jest": "^28.0.0",
24
- "@types/lodash-es": "^4.17.12",
25
23
  "@types/node": "^18.19.127",
26
24
  "@types/ws": "^8.5.10",
27
25
  "@typescript-eslint/eslint-plugin": "^6.8.0",
@@ -26,11 +26,14 @@ export declare class PlaneTrackerAPI<Client extends IClient<TResponse, any>> ext
26
26
  protocol: "https" | "http" | "https_insecure";
27
27
  };
28
28
  units: "metric" | "imperial";
29
- trackingDomain: "adsb" | "dronetag";
30
29
  adsbSource: {
31
30
  ip: string;
31
+ enabled: boolean;
32
32
  port: number;
33
33
  };
34
+ dronetagSource: {
35
+ enabled: boolean;
36
+ };
34
37
  cameraCalibrationProcessConfig: {
35
38
  nightSkyCalibrationEnabled: boolean;
36
39
  scheduleNightSkyCalibrationTimestamp: number;
@@ -39,7 +42,6 @@ export declare class PlaneTrackerAPI<Client extends IClient<TResponse, any>> ext
39
42
  cameraConfig: {
40
43
  defaultCaptureSizeMeters: number;
41
44
  captureSizeExtensionMeters: number;
42
- maxZoomLevel?: number | undefined;
43
45
  };
44
46
  stream: {
45
47
  width: number;
@@ -0,0 +1 @@
1
+ export {};
@@ -60,17 +60,26 @@ export declare const widgetSchema: z.ZodObject<{
60
60
  declare const labelOptionsSchema: z.ZodUnion<[z.ZodLiteral<"blank">, z.ZodLiteral<"registration">, z.ZodLiteral<"call_sign">, z.ZodLiteral<"flight_number">, z.ZodLiteral<"icao">]>;
61
61
  export type TLabelOption = z.infer<typeof labelOptionsSchema>;
62
62
  export declare const cameraSettingsSchema: z.ZodObject<{
63
- trackingDomain: z.ZodDefault<z.ZodUnion<[z.ZodLiteral<"adsb">, z.ZodLiteral<"dronetag">]>>;
64
63
  units: z.ZodDefault<z.ZodUnion<[z.ZodLiteral<"metric">, z.ZodLiteral<"imperial">]>>;
65
64
  adsbSource: z.ZodDefault<z.ZodObject<{
65
+ enabled: z.ZodDefault<z.ZodBoolean>;
66
66
  ip: z.ZodUnion<[z.ZodString, z.ZodLiteral<"">]>;
67
67
  port: z.ZodNumber;
68
68
  }, "strip", z.ZodTypeAny, {
69
69
  ip: string;
70
+ enabled: boolean;
70
71
  port: number;
71
72
  }, {
72
73
  ip: string;
73
74
  port: number;
75
+ enabled?: boolean | undefined;
76
+ }>>;
77
+ dronetagSource: z.ZodDefault<z.ZodObject<{
78
+ enabled: z.ZodDefault<z.ZodBoolean>;
79
+ }, "strip", z.ZodTypeAny, {
80
+ enabled: boolean;
81
+ }, {
82
+ enabled?: boolean | undefined;
74
83
  }>>;
75
84
  camera: z.ZodDefault<z.ZodObject<{
76
85
  protocol: z.ZodUnion<[z.ZodLiteral<"http">, z.ZodLiteral<"https">, z.ZodLiteral<"https_insecure">]>;
@@ -105,15 +114,12 @@ export declare const cameraSettingsSchema: z.ZodObject<{
105
114
  focusCalibrationPoints?: string | undefined;
106
115
  }>>;
107
116
  cameraConfig: z.ZodDefault<z.ZodObject<{
108
- maxZoomLevel: z.ZodOptional<z.ZodNumber>;
109
117
  defaultCaptureSizeMeters: z.ZodDefault<z.ZodNumber>;
110
118
  captureSizeExtensionMeters: z.ZodDefault<z.ZodNumber>;
111
119
  }, "strip", z.ZodTypeAny, {
112
120
  defaultCaptureSizeMeters: number;
113
121
  captureSizeExtensionMeters: number;
114
- maxZoomLevel?: number | undefined;
115
122
  }, {
116
- maxZoomLevel?: number | undefined;
117
123
  defaultCaptureSizeMeters?: number | undefined;
118
124
  captureSizeExtensionMeters?: number | undefined;
119
125
  }>>;
@@ -411,11 +417,14 @@ export declare const cameraSettingsSchema: z.ZodObject<{
411
417
  protocol: "https" | "http" | "https_insecure";
412
418
  };
413
419
  units: "metric" | "imperial";
414
- trackingDomain: "adsb" | "dronetag";
415
420
  adsbSource: {
416
421
  ip: string;
422
+ enabled: boolean;
417
423
  port: number;
418
424
  };
425
+ dronetagSource: {
426
+ enabled: boolean;
427
+ };
419
428
  cameraCalibrationProcessConfig: {
420
429
  nightSkyCalibrationEnabled: boolean;
421
430
  scheduleNightSkyCalibrationTimestamp: number;
@@ -424,7 +433,6 @@ export declare const cameraSettingsSchema: z.ZodObject<{
424
433
  cameraConfig: {
425
434
  defaultCaptureSizeMeters: number;
426
435
  captureSizeExtensionMeters: number;
427
- maxZoomLevel?: number | undefined;
428
436
  };
429
437
  stream: {
430
438
  width: number;
@@ -538,10 +546,13 @@ export declare const cameraSettingsSchema: z.ZodObject<{
538
546
  protocol: "https" | "http" | "https_insecure";
539
547
  } | undefined;
540
548
  units?: "metric" | "imperial" | undefined;
541
- trackingDomain?: "adsb" | "dronetag" | undefined;
542
549
  adsbSource?: {
543
550
  ip: string;
544
551
  port: number;
552
+ enabled?: boolean | undefined;
553
+ } | undefined;
554
+ dronetagSource?: {
555
+ enabled?: boolean | undefined;
545
556
  } | undefined;
546
557
  cameraCalibrationProcessConfig?: {
547
558
  nightSkyCalibrationEnabled: boolean;
@@ -549,7 +560,6 @@ export declare const cameraSettingsSchema: z.ZodObject<{
549
560
  focusCalibrationPoints?: string | undefined;
550
561
  } | undefined;
551
562
  cameraConfig?: {
552
- maxZoomLevel?: number | undefined;
553
563
  defaultCaptureSizeMeters?: number | undefined;
554
564
  captureSizeExtensionMeters?: number | undefined;
555
565
  } | undefined;