@tak-ps/node-cot 2.8.0 → 3.0.0

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.
@@ -1,39 +1,85 @@
1
1
  import xmljs from 'xml-js';
2
+ import { Feature } from 'geojson';
3
+ import { AllGeoJSON } from "@turf/helpers";
2
4
  import Util from './util.js';
3
5
  import Color from './color.js';
4
6
  import PointOnFeature from '@turf/point-on-feature';
5
7
  import AJV from 'ajv';
6
8
  import fs from 'fs';
7
9
 
8
- const ajv = (new AJV({ allErrors: true })).compile(JSON.parse(fs.readFileSync(new URL('./schema.json', import.meta.url))));
10
+ const ajv = (new AJV({ allErrors: true }))
11
+ .compile(JSON.parse(String(fs.readFileSync(new URL('./schema.json', import.meta.url)))));
9
12
 
13
+ export interface Attributes {
14
+ version: string,
15
+ uid: string;
16
+ type: string;
17
+ how: string;
18
+ [k: string]: string;
19
+ }
20
+
21
+ export interface Detail {
22
+ contact?: { _attributes: { callsign: string } },
23
+ tog?: { _attributes: { enabled: string } },
24
+ strokeColor?: { _attributes: { value: number } },
25
+ strokeWeight?: { _attributes: { value: number } },
26
+ strokeStyle?: { _attributes: { value: string } },
27
+ labels_on?: { _attributes: { value: string } },
28
+ fillColor?: { _attributes: { value: number } },
29
+ link?: object[],
30
+ [k: string]: unknown
31
+ }
32
+
33
+ export interface Point {
34
+ _attributes: {
35
+ lat: string | number;
36
+ lon: string | number;
37
+ hae: string | number;
38
+ ce: string | number;
39
+ le: string | number;
40
+ [k: string]: string | number
41
+ }
42
+ }
43
+
44
+ export interface JSONCoT {
45
+ event: {
46
+ _attributes: Attributes,
47
+ detail: Detail,
48
+ point: Point,
49
+ [k: string]: unknown
50
+ },
51
+ [k: string]: unknown
52
+ }
10
53
 
11
54
  /**
12
55
  * Convert to and from an XML CoT message
13
56
  * @class
14
57
  *
15
- * @param {String|Object|Buffer} cot A string/buffer containing the XML representation or the xml-js object tree
58
+ * @param cot A string/buffer containing the XML representation or the xml-js object tree
16
59
  *
17
- * @prop {Object} raw Raw XML-JS representation of CoT
60
+ * @prop raw Raw XML-JS representation of CoT
18
61
  */
19
62
  export default class XMLCot {
20
- constructor(cot) {
21
- if (cot instanceof Buffer) String(cot);
63
+ raw: JSONCoT;
22
64
 
23
- if (typeof cot === 'string') {
24
- this.raw = xmljs.xml2js(cot, { compact: true });
65
+ constructor(cot: Buffer | JSONCoT | string) {
66
+ if (typeof cot === 'string' || cot instanceof Buffer) {
67
+ if (cot instanceof Buffer) cot = String(cot);
68
+
69
+ const raw: any = xmljs.xml2js(cot, { compact: true });
70
+ this.raw = raw as JSONCoT;
25
71
  } else {
26
72
  this.raw = cot;
27
73
  }
28
74
 
29
75
  // Attempt to cast all point to numerics
30
76
  for (const key of Object.keys(this.raw.event.point._attributes)) {
31
- if (!isNaN(parseFloat(this.raw.event.point._attributes[key]))) {
32
- this.raw.event.point._attributes[key] = parseFloat(this.raw.event.point._attributes[key]);
77
+ if (!isNaN(parseFloat(String(this.raw.event.point._attributes[key])))) {
78
+ this.raw.event.point._attributes[key] = parseFloat(String(this.raw.event.point._attributes[key]));
33
79
  }
34
80
  }
35
81
 
36
- if (!this.raw.event._attributes.uid) this.raw.event._attributes.uuid = Util.cot_uuid().uid;
82
+ if (!this.raw.event._attributes.uid) this.raw.event._attributes.uuid = Util.cot_uuid();
37
83
 
38
84
  ajv(this.raw);
39
85
  if (ajv.errors) throw new Error(ajv.errors[0].message);
@@ -46,11 +92,11 @@ export default class XMLCot {
46
92
  *
47
93
  * @return {XMLCot}
48
94
  */
49
- static from_geojson(feature) {
95
+ static from_geojson(feature: Feature) {
50
96
  if (feature.type !== 'Feature') throw new Error('Must be GeoJSON Feature');
51
97
  if (!feature.properties) throw new Error('Feature must have properties');
52
98
 
53
- const cot = {
99
+ const cot: JSONCoT = {
54
100
  event: {
55
101
  _attributes: Util.cot_event_attr(
56
102
  feature.properties.type || 'a-f-G',
@@ -64,15 +110,15 @@ export default class XMLCot {
64
110
  }
65
111
  };
66
112
 
67
- if (feature.id) cot.event._attributes.uid = feature.id;
113
+ if (feature.id) cot.event._attributes.uid = String(feature.id);
68
114
  if (feature.properties.callsign && !feature.id) cot.event._attributes.uid = feature.properties.callsign;
69
115
 
70
116
  if (!feature.geometry) throw new Error('Must have Geometry');
71
- if (!['Point', 'Polygon', 'LineString'].includes(feature.geometry.type)) throw new Error('Unsupported Geoemtry Type');
117
+ if (!['Point', 'Polygon', 'LineString'].includes(feature.geometry.type)) throw new Error('Unsupported Geometry Type');
72
118
 
73
119
  if (feature.geometry.type === 'Point') {
74
- cot.event.point._attributes.lon = feature.geometry.coordinates[0];
75
- cot.event.point._attributes.lat = feature.geometry.coordinates[1];
120
+ cot.event.point._attributes.lon = String(feature.geometry.coordinates[0]);
121
+ cot.event.point._attributes.lat = String(feature.geometry.coordinates[1]);
76
122
  } else if (['Polygon', 'LineString'].includes(feature.geometry.type)) {
77
123
  const stroke = new Color(feature.properties.stroke || -1761607936);
78
124
  if (feature.properties['stroke-opacity']) stroke.a = feature.properties['stroke-opacity'];
@@ -117,9 +163,9 @@ export default class XMLCot {
117
163
  cot.event.detail.labels_on = { _attributes: { value: 'false' } };
118
164
  cot.event.detail.tog = { _attributes: { enabled: '0' } };
119
165
 
120
- const centre = PointOnFeature(feature);
121
- cot.event.point._attributes.lon = centre.geometry.coordinates[0];
122
- cot.event.point._attributes.lat = centre.geometry.coordinates[1];
166
+ const centre = PointOnFeature(feature as AllGeoJSON);
167
+ cot.event.point._attributes.lon = String(centre.geometry.coordinates[0]);
168
+ cot.event.point._attributes.lat = String(centre.geometry.coordinates[1]);
123
169
  }
124
170
 
125
171
  return new XMLCot(cot);
@@ -127,16 +173,14 @@ export default class XMLCot {
127
173
 
128
174
  /**
129
175
  * Return a GeoJSON Feature from an XML CoT message
130
- *
131
- * @returns {Object}
132
176
  */
133
- to_geojson() {
177
+ to_geojson(): Feature {
134
178
  const raw = JSON.parse(JSON.stringify(this.raw));
135
179
  if (!raw.event.detail) raw.event.detail = {};
136
180
  if (!raw.event.detail.contact) raw.event.detail.contact = {};
137
181
  if (!raw.event.detail.contact._attributes) raw.event.detail.contact._attributes = {};
138
182
 
139
- const geojson = {
183
+ const geojson: Feature = {
140
184
  id: raw.event._attributes.uid,
141
185
  type: 'Feature',
142
186
  properties: {
@@ -174,6 +218,7 @@ export default class XMLCot {
174
218
  return new XMLCot({
175
219
  event: {
176
220
  _attributes: Util.cot_event_attr('t-x-c-t', 'h-g-i-g-o'),
221
+ detail: {},
177
222
  point: Util.cot_point()
178
223
  }
179
224
  });
package/package.json CHANGED
@@ -1,25 +1,37 @@
1
1
  {
2
2
  "name": "@tak-ps/node-cot",
3
3
  "type": "module",
4
- "version": "2.8.0",
4
+ "version": "3.0.0",
5
5
  "description": "Lightweight JavaScript library for parsing and manipulating TAK messages",
6
- "main": "index.js",
6
+ "main": "dist/index.js",
7
+ "types": "index.js",
7
8
  "scripts": {
8
- "test": "tape test/**.test.js",
9
- "lint": "eslint *.js lib/*.js test/*.js"
9
+ "test": "ts-node-test test/",
10
+ "lint": "eslint *.ts lib/*.ts",
11
+ "build": "tsc --build",
12
+ "pretest": "npm run lint"
10
13
  },
11
14
  "dependencies": {
15
+ "@turf/helpers": "^6.5.0",
12
16
  "@turf/point-on-feature": "^6.5.0",
17
+ "@types/color": "^3.0.3",
18
+ "@types/geojson": "^7946.0.10",
13
19
  "ajv": "^8.11.2",
14
20
  "color": "^4.2.3",
15
21
  "protobufjs": "^7.1.2",
16
- "uuid": "^9.0.0",
17
22
  "xml-js": "^1.6.11"
18
23
  },
19
24
  "devDependencies": {
20
- "eslint": "^8.28.0",
25
+ "@types/node": "^18.14.2",
26
+ "@types/tape": "^4.13.2",
27
+ "@typescript-eslint/eslint-plugin": "^5.54.0",
28
+ "@typescript-eslint/parser": "^5.54.0",
29
+ "eslint": "^8.35.0",
21
30
  "eslint-plugin-node": "^11.1.0",
22
- "tape": "^5.6.1"
31
+ "tape": "^5.6.1",
32
+ "ts-node": "^10.9.1",
33
+ "ts-node-test": "^0.3.0",
34
+ "typescript": "^4.9.5"
23
35
  },
24
36
  "repository": {
25
37
  "type": "git",
package/tsconfig.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "ts-node": {
3
+ "esm": true
4
+ },
5
+ "compilerOptions": {
6
+ "module": "es2022",
7
+ "esModuleInterop": true,
8
+ "target": "es2022",
9
+ "lib": ["es2022", "dom"],
10
+ "noImplicitAny": true,
11
+ "moduleResolution": "node",
12
+ "sourceMap": true,
13
+ "outDir": "dist",
14
+ "baseUrl": ".",
15
+ "paths": {
16
+ "*": [
17
+ "node_modules/*"
18
+ ]
19
+ }
20
+ },
21
+ "include": [
22
+ "index.ts",
23
+ "test/**/*",
24
+ "lib/**/*"
25
+ ]
26
+ }
package/lib/util.js DELETED
@@ -1,111 +0,0 @@
1
- import { v4 as uuidv4 } from 'uuid';
2
-
3
- /**
4
- * Helper functions for generating CoT data
5
- * @class
6
- */
7
- export default class Util {
8
- /**
9
- * Return an event._attributes object with as many defaults as possible
10
- *
11
- * @param {String} type CoT Type
12
- * @param {String} how CoT how
13
- * @param {Date|string|null} time Time of CoT Message - if omitted, current time is used
14
- * @param {Date|string|null} start Start Time of CoT - if omitted, current time is used
15
- * @param {Date|string|null|numeric} stale Expiration of CoT - if null now+20s is used. Alternative an integer representing the ms offset
16
- *
17
- * @returns {Object}
18
- */
19
- static cot_event_attr(type, how, time, start, stale) {
20
- if (!type) throw new Error('type param required');
21
- if (!how) throw new Error('how param required');
22
-
23
- return {
24
- ...Util.cot_version(),
25
- ...Util.cot_uuid(),
26
- type,
27
- how,
28
- ...Util.cot_date(time, start, stale)
29
- };
30
- }
31
-
32
- /**
33
- * Return an event.detail object with as many defaults as possible
34
- *
35
- * @param {String} [callsign=UNKNOWN] Display Callsign
36
- *
37
- * @returns {Object}
38
- */
39
- static cot_event_detail(callsign = 'UNKNOWN') {
40
- return {
41
- contact: {
42
- _attributes: { callsign }
43
- }
44
- };
45
- }
46
-
47
- /**
48
- * Generate a random UUID
49
- *
50
- * @returns {Object}
51
- */
52
- static cot_uuid() {
53
- return {
54
- uid: uuidv4()
55
- };
56
- }
57
-
58
- /**
59
- * Return the current version number this library supports
60
- *
61
- * @returns {Object}
62
- */
63
- static cot_version() {
64
- return {
65
- version: '2.0'
66
- };
67
- }
68
-
69
- /**
70
- * Generate Null Island CoT point object
71
- *
72
- * @returns {Object}
73
- */
74
- static cot_point() {
75
- return {
76
- '_attributes': {
77
- 'lat': '0.000000',
78
- 'lon': '0.000000',
79
- 'hae': '0.0',
80
- 'ce': '9999999.0',
81
- 'le': '9999999.0'
82
- }
83
- };
84
- }
85
-
86
- /**
87
- * Generate CoT date objects
88
- *
89
- * cot_date() - Time: now, Start: now, Stale: now + 20s
90
- *
91
- * @param {Date|string|null} time Time of CoT Message - if omitted, current time is used
92
- * @param {Date|string|null} start Start Time of CoT - if omitted, current time is used
93
- * @param {Date|string|null|numeric} stale Expiration of CoT - if null now+20s is used. Alternative an integer representing the ms offset
94
- *
95
- * @returns {Object}
96
- */
97
- static cot_date(time, start, stale) {
98
- const now = Date.now();
99
-
100
- time = new Date(time || now).toISOString();
101
- start = new Date(start || now).toISOString();
102
-
103
- if (!stale) {
104
- stale = new Date(+new Date(start) + 20 * 1000).toISOString();
105
- } else if (!isNaN(parseInt(stale))) {
106
- stale = new Date(+new Date(start) + stale).toISOString();
107
- }
108
-
109
- return { time, start, stale };
110
- }
111
- }