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.
- package/cjs/PlaneTrackerAPI.d.ts +4 -2
- package/cjs/internal/transformers.js +44 -15
- package/cjs/internal/transformers.test.d.ts +1 -0
- package/cjs/internal/transformers.test.js +96 -0
- package/cjs/types/PlaneTrackerAPI.d.ts +18 -8
- package/cjs/types/PlaneTrackerAPI.js +7 -4
- package/esm/internal/transformers.js +44 -15
- package/esm/internal/transformers.test.js +94 -0
- package/esm/types/PlaneTrackerAPI.js +7 -4
- package/package.json +1 -3
- package/types/PlaneTrackerAPI.d.ts +4 -2
- package/types/internal/transformers.test.d.ts +1 -0
- package/types/types/PlaneTrackerAPI.d.ts +18 -8
package/cjs/PlaneTrackerAPI.d.ts
CHANGED
|
@@ -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
|
|
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,
|
|
7
|
+
return mapKeysDeep(o, camelCase);
|
|
9
8
|
};
|
|
10
9
|
exports.toCamelCaseDeep = toCamelCaseDeep;
|
|
11
|
-
const toSnakeCase = (o) =>
|
|
10
|
+
const toSnakeCase = (o) => mapKeys(o, snakeCase);
|
|
12
11
|
exports.toSnakeCase = toSnakeCase;
|
|
13
12
|
const toSnakeCaseDeep = (o) => {
|
|
14
|
-
return mapKeysDeep(o,
|
|
13
|
+
return mapKeysDeep(o, snakeCase);
|
|
15
14
|
};
|
|
16
15
|
exports.toSnakeCaseDeep = toSnakeCaseDeep;
|
|
17
|
-
const
|
|
18
|
-
|
|
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 (
|
|
53
|
+
if (!isPlainObject(obj)) {
|
|
26
54
|
return obj;
|
|
27
55
|
}
|
|
28
|
-
const result =
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
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,
|
|
3
|
+
return mapKeysDeep(o, camelCase);
|
|
5
4
|
};
|
|
6
|
-
export const toSnakeCase = (o) => mapKeys(o,
|
|
5
|
+
export const toSnakeCase = (o) => mapKeys(o, snakeCase);
|
|
7
6
|
export const toSnakeCaseDeep = (o) => {
|
|
8
|
-
return mapKeysDeep(o,
|
|
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 (
|
|
46
|
+
if (!isPlainObject(obj)) {
|
|
19
47
|
return obj;
|
|
20
48
|
}
|
|
21
|
-
const result =
|
|
22
|
-
|
|
23
|
-
|
|
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.
|
|
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;
|