@atmosx/event-product-parser 2.0.16 → 3.0.1
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/README.md +248 -187
- package/dist/cjs/index.cjs +12218 -3139
- package/dist/esm/index.mjs +12401 -3327
- package/dist/index.d.mts +235 -0
- package/dist/index.d.ts +235 -0
- package/package.json +6 -4
- package/src/@building/building.clean.ts +30 -0
- package/src/@building/building.create.ts +42 -0
- package/src/@building/building.enhance.ts +56 -0
- package/src/@building/building.geometry.ts +48 -0
- package/src/@building/building.headers.ts +37 -0
- package/src/@building/building.office.ts +43 -0
- package/src/@building/building.polygon.ts +71 -0
- package/src/@building/building.properties.ts +89 -0
- package/src/@building/building.signature.ts +61 -0
- package/src/@building/building.tags.ts +24 -0
- package/src/@building/building.tracking.ts +68 -0
- package/src/@building/building.validate.ts +147 -0
- package/src/@core/core.getEvents.ts +25 -0
- package/src/@core/core.getNodes.ts +25 -0
- package/src/@core/core.getRandomEvent.ts +25 -0
- package/src/@core/core.getVersion.ts +25 -0
- package/src/@core/core.listener.ts +24 -0
- package/src/@core/core.setNode.ts +81 -0
- package/src/@core/core.start.ts +59 -0
- package/src/@core/core.stop.ts +32 -0
- package/src/@dictionaries/dictionaries.betterEventNames.ts +89 -0
- package/src/@dictionaries/dictionaries.eventActions.ts +28 -0
- package/src/@dictionaries/{awips.ts → dictionaries.eventAwipAbreviations.ts} +13 -7
- package/src/@dictionaries/dictionaries.eventCancelMessages.ts +30 -0
- package/src/@dictionaries/dictionaries.eventCauses.ts +36 -0
- package/src/@dictionaries/dictionaries.eventProducts.ts +25 -0
- package/src/@dictionaries/dictionaries.eventRecords.ts +25 -0
- package/src/@dictionaries/dictionaries.eventSeverity.ts +27 -0
- package/src/@dictionaries/dictionaries.eventStatus.ts +31 -0
- package/src/@dictionaries/{signatures.ts → dictionaries.eventTags.ts} +13 -68
- package/src/@dictionaries/dictionaries.eventTypes.ts +82 -0
- package/src/@dictionaries/dictionaries.eventsOffshore.ts +31 -0
- package/src/@dictionaries/dictionaries.hailStrings.ts +33 -0
- package/src/@dictionaries/{icao.ts → dictionaries.officeICAOs.ts} +13 -6
- package/src/@dictionaries/dictionaries.regExp.ts +28 -0
- package/src/@dictionaries/dictionaries.shapefileLinks.ts +36 -0
- package/src/@dictionaries/dictionaries.statusCorrelationText.ts +40 -0
- package/src/@dictionaries/dictionaries.testSignatures.ts +24 -0
- package/src/@dictionaries/dictionaries.transcribedMessageReplacements.ts +68 -0
- package/src/@events/events.api.ts +113 -0
- package/src/@events/events.text.ts +79 -0
- package/src/@events/events.ugc.ts +83 -0
- package/src/@events/events.vtec.ts +87 -0
- package/src/@manager/manager.mkEvent.ts +95 -0
- package/src/@manager/manager.rmEvent.ts +46 -0
- package/src/@manager/manager.setHash.ts +37 -0
- package/src/@manager/manager.updateNodes.ts +55 -0
- package/src/@manager/manager.updateWebhooks.ts +50 -0
- package/src/@modules/@database/database.cache.ts +48 -0
- package/src/@modules/@database/database.init.ts +45 -0
- package/src/@modules/@database/database.shapefiles.ts +96 -0
- package/src/@modules/@database/database.stanza.ts +48 -0
- package/src/@modules/@eas/eas.getFloatPCM16.ts +29 -0
- package/src/@modules/@eas/eas.getMergedPCM16.ts +32 -0
- package/src/@modules/@eas/eas.getPCM16.ts +52 -0
- package/src/@modules/@eas/eas.getPCMToFloat.ts +26 -0
- package/src/@modules/@eas/eas.getSampledPCM16.ts +36 -0
- package/src/@modules/@eas/eas.getWavPCM16.ts +52 -0
- package/src/@modules/@eas/eas.setAFSK.ts +52 -0
- package/src/@modules/@eas/eas.setAsciiToBits.ts +32 -0
- package/src/@modules/@eas/eas.setAttentionTone.ts +40 -0
- package/src/@modules/@eas/eas.setEasTone.ts +137 -0
- package/src/@modules/@eas/eas.setNoise.ts +31 -0
- package/src/@modules/@eas/eas.setRadioEffect.ts +49 -0
- package/src/@modules/@eas/eas.setSameHeader.ts +45 -0
- package/src/@modules/@stanza/stanza.getAwipsType.ts +46 -0
- package/src/@modules/@stanza/stanza.validate.ts +50 -0
- package/src/@modules/@utilities/utilities.createHttp.ts +85 -0
- package/src/@modules/@utilities/utilities.createWebhook.ts +100 -0
- package/src/@modules/@utilities/utilities.getFormattedTime.ts +43 -0
- package/src/@modules/@utilities/utilities.getSettings.ts +25 -0
- package/src/@modules/@utilities/utilities.getShapeNearestPoint.ts +114 -0
- package/src/@modules/@utilities/utilities.setCronSchedule.ts +65 -0
- package/src/@modules/@utilities/utilities.setEventEmit.ts +41 -0
- package/src/@modules/@utilities/utilities.setListener.ts +30 -0
- package/src/@modules/@utilities/utilities.setSettings.ts +42 -0
- package/src/@modules/@utilities/utilities.setSleep.ts +33 -0
- package/src/@modules/@utilities/utilities.setTimeoutAction.ts +59 -0
- package/src/@modules/@utilities/utilities.setWarning.ts +34 -0
- package/src/@modules/@xmpp/xmpp.xDeploy.ts +58 -0
- package/src/@modules/@xmpp/xmpp.xError.ts +29 -0
- package/src/@modules/@xmpp/xmpp.xOffline.ts +38 -0
- package/src/@modules/@xmpp/xmpp.xOnline.ts +45 -0
- package/src/@modules/@xmpp/xmpp.xReconnect.ts +64 -0
- package/src/@modules/@xmpp/xmpp.xStanza.ts +63 -0
- package/src/@parsers/@hvtec/hvtec.extract.ts +40 -0
- package/src/@parsers/@pvtec/pvtec.expires.ts +26 -0
- package/src/@parsers/@pvtec/pvtec.extract.ts +49 -0
- package/src/@parsers/@text/text.getDescriptionFromProduct.ts +53 -0
- package/src/@parsers/@text/text.getPolygonFromProduct.ts +32 -0
- package/src/@parsers/@text/text.getTextFromProduct.ts +43 -0
- package/src/@parsers/@text/text.getXML.ts +61 -0
- package/src/@parsers/@ugc/ugc.coordinates.ts +110 -0
- package/src/@parsers/@ugc/ugc.expiry.ts +32 -0
- package/src/@parsers/@ugc/ugc.extract.ts +37 -0
- package/src/@parsers/@ugc/ugc.header.ts +30 -0
- package/src/@parsers/@ugc/ugc.locations.ts +29 -0
- package/src/@parsers/@ugc/ugc.zones.ts +52 -0
- package/src/@types/type.event.ts +67 -0
- package/src/@types/type.properties.ts +75 -0
- package/src/@types/types.attributes.ts +28 -0
- package/src/@types/types.compiled.ts +35 -0
- package/src/@types/types.hash.ts +24 -0
- package/src/@types/types.hvtec.ts +25 -0
- package/src/@types/types.pvtec.ts +30 -0
- package/src/@types/types.settings.ts +76 -0
- package/src/@types/types.stanza.ts +37 -0
- package/src/@types/types.ugc.ts +24 -0
- package/src/@types/types.webhook.ts +26 -0
- package/src/bootstrap.ts +85 -163
- package/src/index.ts +47 -216
- package/test.js +78 -51
- package/tsup.config.ts +1 -0
- package/src/@dictionaries/events.ts +0 -168
- package/src/@parsers/@events/api.ts +0 -146
- package/src/@parsers/@events/cap.ts +0 -123
- package/src/@parsers/@events/text.ts +0 -104
- package/src/@parsers/@events/ugc.ts +0 -107
- package/src/@parsers/@events/vtec.ts +0 -76
- package/src/@parsers/events.ts +0 -392
- package/src/@parsers/hvtec.ts +0 -46
- package/src/@parsers/pvtec.ts +0 -72
- package/src/@parsers/stanza.ts +0 -97
- package/src/@parsers/text.ts +0 -165
- package/src/@parsers/ugc.ts +0 -247
- package/src/@submodules/database.ts +0 -201
- package/src/@submodules/eas.ts +0 -490
- package/src/@submodules/utils.ts +0 -191
- package/src/@submodules/xmpp.ts +0 -142
- package/src/types.ts +0 -259
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/*
|
|
2
|
+
_ _ _ __ __
|
|
3
|
+
/\ | | | | (_) \ \ / /
|
|
4
|
+
/ \ | |_ _ __ ___ ___ ___ _ __ | |__ ___ _ __ _ ___ \ V /
|
|
5
|
+
/ /\ \| __| '_ ` _ \ / _ \/ __| '_ \| '_ \ / _ \ '__| |/ __| > <
|
|
6
|
+
/ ____ \ |_| | | | | | (_) \__ \ |_) | | | | __/ | | | (__ / . \
|
|
7
|
+
/_/ \_\__|_| |_| |_|\___/|___/ .__/|_| |_|\___|_| |_|\___/_/ \_\
|
|
8
|
+
| |
|
|
9
|
+
|_|
|
|
10
|
+
|
|
11
|
+
Created with ♥ by the AtmosphericX Team (KiyoWx, StarflightWx, & CJ Ziegler)
|
|
12
|
+
Discord: https://atmosphericx-discord.scriptkitty.cafe
|
|
13
|
+
Ko-Fi: https://ko-fi.com/k3yomi
|
|
14
|
+
Documentation: http://localhost/documentation | https://atmosphericx.scriptkitty.cafe/documentation
|
|
15
|
+
|
|
16
|
+
Internal Package: @atmosx/event-product-parser
|
|
17
|
+
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
export const getPolygonFromProduct = (message: string): number[][] => {
|
|
21
|
+
const coordinates: [number, number][] = [];
|
|
22
|
+
const match = message.match(/LAT\.{3}LON\s+([\d\s]+)/i);
|
|
23
|
+
if (!match || !match[1]) return coordinates;
|
|
24
|
+
const coordStrings = match[1].replace(/\n/g, ' ').trim().split(/\s+/);
|
|
25
|
+
for (let i = 0; i < coordStrings.length - 1; i += 2) {
|
|
26
|
+
const lat = parseFloat(coordStrings[i]) / 100;
|
|
27
|
+
const lon = -parseFloat(coordStrings[i + 1]) / 100;
|
|
28
|
+
if (!isNaN(lat) && !isNaN(lon)) { coordinates.push([lon, lat]); }
|
|
29
|
+
}
|
|
30
|
+
if (coordinates.length > 2) { coordinates.push(coordinates[0]); }
|
|
31
|
+
return coordinates;
|
|
32
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/*
|
|
2
|
+
_ _ _ __ __
|
|
3
|
+
/\ | | | | (_) \ \ / /
|
|
4
|
+
/ \ | |_ _ __ ___ ___ ___ _ __ | |__ ___ _ __ _ ___ \ V /
|
|
5
|
+
/ /\ \| __| '_ ` _ \ / _ \/ __| '_ \| '_ \ / _ \ '__| |/ __| > <
|
|
6
|
+
/ ____ \ |_| | | | | | (_) \__ \ |_) | | | | __/ | | | (__ / . \
|
|
7
|
+
/_/ \_\__|_| |_| |_|\___/|___/ .__/|_| |_|\___|_| |_|\___/_/ \_\
|
|
8
|
+
| |
|
|
9
|
+
|_|
|
|
10
|
+
|
|
11
|
+
Created with ♥ by the AtmosphericX Team (KiyoWx, StarflightWx, & CJ Ziegler)
|
|
12
|
+
Discord: https://atmosphericx-discord.scriptkitty.cafe
|
|
13
|
+
Ko-Fi: https://ko-fi.com/k3yomi
|
|
14
|
+
Documentation: http://localhost/documentation | https://atmosphericx.scriptkitty.cafe/documentation
|
|
15
|
+
|
|
16
|
+
Internal Package: @atmosx/event-product-parser
|
|
17
|
+
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
interface GetTextFromProductOptions {
|
|
21
|
+
message: string
|
|
22
|
+
find: string[]
|
|
23
|
+
append?: string
|
|
24
|
+
removal?: string[]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const getTextFromProduct = (options: GetTextFromProductOptions): string => {
|
|
28
|
+
const lines = options.message.split(`\n`);
|
|
29
|
+
for (const line of lines) {
|
|
30
|
+
const matchedFind = options.find.find((find) => line.includes(find));
|
|
31
|
+
if (matchedFind) {
|
|
32
|
+
let result = line.slice(line.indexOf(matchedFind) + matchedFind.length).trim();
|
|
33
|
+
if (options.removal) {
|
|
34
|
+
for (const str of options.removal) {
|
|
35
|
+
result = result.toLowerCase().split(str.toLowerCase()).join('');
|
|
36
|
+
}
|
|
37
|
+
result = result.replace(matchedFind, '').replace('<', '').trim();
|
|
38
|
+
}
|
|
39
|
+
return result.toUpperCase() + (options?.append ?? ``);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/*
|
|
2
|
+
_ _ _ __ __
|
|
3
|
+
/\ | | | | (_) \ \ / /
|
|
4
|
+
/ \ | |_ _ __ ___ ___ ___ _ __ | |__ ___ _ __ _ ___ \ V /
|
|
5
|
+
/ /\ \| __| '_ ` _ \ / _ \/ __| '_ \| '_ \ / _ \ '__| |/ __| > <
|
|
6
|
+
/ ____ \ |_| | | | | | (_) \__ \ |_) | | | | __/ | | | (__ / . \
|
|
7
|
+
/_/ \_\__|_| |_| |_|\___/|___/ .__/|_| |_|\___|_| |_|\___/_/ \_\
|
|
8
|
+
| |
|
|
9
|
+
|_|
|
|
10
|
+
|
|
11
|
+
Created with ♥ by the AtmosphericX Team (KiyoWx, StarflightWx, & CJ Ziegler)
|
|
12
|
+
Discord: https://atmosphericx-discord.scriptkitty.cafe
|
|
13
|
+
Ko-Fi: https://ko-fi.com/k3yomi
|
|
14
|
+
Documentation: http://localhost/documentation | https://atmosphericx.scriptkitty.cafe/documentation
|
|
15
|
+
|
|
16
|
+
Internal Package: @atmosx/event-product-parser
|
|
17
|
+
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
interface GetXMLOptions {
|
|
21
|
+
message: string
|
|
22
|
+
targets: string[]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const getXML = (options: GetXMLOptions): Record<string, string> => {
|
|
26
|
+
const extracted: Record<string, any> = {};
|
|
27
|
+
const findValueByKey = (obj: any, searchKey: string) => {
|
|
28
|
+
const results = [];
|
|
29
|
+
if (obj === null || typeof obj !== 'object') {
|
|
30
|
+
return results;
|
|
31
|
+
}
|
|
32
|
+
const searchKeyLower = searchKey.toLowerCase();
|
|
33
|
+
for (const key in obj) {
|
|
34
|
+
if (obj.hasOwnProperty(key) && key.toLowerCase() === searchKeyLower) {
|
|
35
|
+
results.push(obj[key]);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (Array.isArray(obj)) {
|
|
39
|
+
for (const item of obj) {
|
|
40
|
+
if (item.valueName && item.valueName.toLowerCase() === searchKeyLower && item.value !== undefined) {
|
|
41
|
+
results.push(item.value);
|
|
42
|
+
}
|
|
43
|
+
const nestedResults = findValueByKey(item, searchKey);
|
|
44
|
+
results.push(...nestedResults);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
for (const key in obj) {
|
|
48
|
+
if (obj.hasOwnProperty(key)) {
|
|
49
|
+
const nestedResults = findValueByKey(obj[key], searchKey);
|
|
50
|
+
results.push(...nestedResults);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return results;
|
|
54
|
+
};
|
|
55
|
+
for (const key of options.targets) {
|
|
56
|
+
const values = findValueByKey(options.message, key);
|
|
57
|
+
const uniqueValues = [...new Set(values)];
|
|
58
|
+
extracted[key] = uniqueValues.length === 0 ? null : (uniqueValues.length === 1 ? uniqueValues[0] : uniqueValues);
|
|
59
|
+
}
|
|
60
|
+
return extracted;
|
|
61
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/*
|
|
2
|
+
_ _ _ __ __
|
|
3
|
+
/\ | | | | (_) \ \ / /
|
|
4
|
+
/ \ | |_ _ __ ___ ___ ___ _ __ | |__ ___ _ __ _ ___ \ V /
|
|
5
|
+
/ /\ \| __| '_ ` _ \ / _ \/ __| '_ \| '_ \ / _ \ '__| |/ __| > <
|
|
6
|
+
/ ____ \ |_| | | | | | (_) \__ \ |_) | | | | __/ | | | (__ / . \
|
|
7
|
+
/_/ \_\__|_| |_| |_|\___/|___/ .__/|_| |_|\___|_| |_|\___/_/ \_\
|
|
8
|
+
| |
|
|
9
|
+
|_|
|
|
10
|
+
|
|
11
|
+
Created with ♥ by the AtmosphericX Team (KiyoWx, StarflightWx, & CJ Ziegler)
|
|
12
|
+
Discord: https://atmosphericx-discord.scriptkitty.cafe
|
|
13
|
+
Ko-Fi: https://ko-fi.com/k3yomi
|
|
14
|
+
Documentation: http://localhost/documentation | https://atmosphericx.scriptkitty.cafe/documentation
|
|
15
|
+
|
|
16
|
+
Internal Package: @atmosx/event-product-parser
|
|
17
|
+
|
|
18
|
+
*/
|
|
19
|
+
import { union } from "polygon-clipping";
|
|
20
|
+
import { bootstrap } from "../../bootstrap";
|
|
21
|
+
|
|
22
|
+
interface CoordinatesOptions {
|
|
23
|
+
zones: string[]
|
|
24
|
+
isUnion: boolean
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface CoordinatesResponse {
|
|
28
|
+
type: `Polygon` | `MultiPolygon`
|
|
29
|
+
coordinates: any[]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const getZonePolygon = (options: CoordinatesOptions): CoordinatesResponse | null => {
|
|
33
|
+
const list = [...new Set(options.zones.map(z => z.trim()))].filter(z => z === 'XX000' ? false : true);
|
|
34
|
+
if (list.length === 0) return null;
|
|
35
|
+
|
|
36
|
+
const placeholders = list.map(() => "?").join(",");
|
|
37
|
+
const rows = bootstrap.database
|
|
38
|
+
.prepare(`SELECT geometry FROM shapefiles WHERE id IN (${placeholders})`)
|
|
39
|
+
.all(...list);
|
|
40
|
+
const polygons: any[] = [];
|
|
41
|
+
for (const row of rows) {
|
|
42
|
+
if (!row?.geometry) continue;
|
|
43
|
+
const geom = JSON.parse(row.geometry);
|
|
44
|
+
if (geom?.type === "Polygon") {
|
|
45
|
+
polygons.push(geom.coordinates);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (polygons.length === 0) return null;
|
|
50
|
+
|
|
51
|
+
if (options.isUnion) {
|
|
52
|
+
const unionFn = union as (...polys: any[]) => any;
|
|
53
|
+
const mergedCoords = unionFn(...polygons);
|
|
54
|
+
if (!mergedCoords || mergedCoords.length === 0) return null;
|
|
55
|
+
let maxArea = -1;
|
|
56
|
+
let bestPoly: any[] = [];
|
|
57
|
+
for (const poly of mergedCoords) {
|
|
58
|
+
const outerRing = poly[0];
|
|
59
|
+
let area = 0;
|
|
60
|
+
for (let i = 0; i < outerRing.length - 1; i++) {
|
|
61
|
+
const [x1, y1] = outerRing[i];
|
|
62
|
+
const [x2, y2] = outerRing[i + 1];
|
|
63
|
+
area += x1 * y2 - x2 * y1;
|
|
64
|
+
}
|
|
65
|
+
area = Math.abs(area / 2);
|
|
66
|
+
if (area > maxArea) {
|
|
67
|
+
maxArea = area;
|
|
68
|
+
bestPoly = poly;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (!bestPoly || bestPoly.length === 0) return null;
|
|
72
|
+
const outerRing = bestPoly[0];
|
|
73
|
+
const skip = Math.max(1, parseInt(String(bootstrap.settings.GlobalSettings.ShapefileSkipPoints), 10) || 1);
|
|
74
|
+
let skipped = outerRing.filter((_: any, idx: number) => idx % skip === 0);
|
|
75
|
+
if (skipped.length < 4) {
|
|
76
|
+
skipped = outerRing.slice();
|
|
77
|
+
}
|
|
78
|
+
const first = skipped[0];
|
|
79
|
+
const last = skipped[skipped.length - 1];
|
|
80
|
+
if (!first || !last || first[0] !== last[0] || first[1] !== last[1]) {
|
|
81
|
+
skipped.push([first[0], first[1]]);
|
|
82
|
+
}
|
|
83
|
+
return {type: "Polygon", coordinates: [skipped]};
|
|
84
|
+
} else {
|
|
85
|
+
const multi: any[] = [];
|
|
86
|
+
for (const polyCoords of polygons) {
|
|
87
|
+
if (Array.isArray(polyCoords) && Array.isArray(polyCoords[0])) {
|
|
88
|
+
multi.push(polyCoords);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (multi.length === 0) return null;
|
|
92
|
+
const skip = Math.max(1, parseInt(String(bootstrap.settings.GlobalSettings.ShapefileSkipPoints), 10) || 1);
|
|
93
|
+
if (skip > 1) {
|
|
94
|
+
for (let p = 0; p < multi.length; p++) {
|
|
95
|
+
for (let r = 0; r < multi[p].length; r++) {
|
|
96
|
+
const ring = multi[p][r];
|
|
97
|
+
let reduced = ring.filter((_: any, i: number) => i % skip === 0);
|
|
98
|
+
if (reduced.length < 4) reduced = ring.slice();
|
|
99
|
+
const first = reduced[0];
|
|
100
|
+
const last = reduced[reduced.length - 1];
|
|
101
|
+
if ( first && last && (first[0] !== last[0] || first[1] !== last[1])) {
|
|
102
|
+
reduced.push([first[0], first[1]]);
|
|
103
|
+
}
|
|
104
|
+
multi[p][r] = reduced;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return {type: "MultiPolygon", coordinates: multi};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/*
|
|
2
|
+
_ _ _ __ __
|
|
3
|
+
/\ | | | | (_) \ \ / /
|
|
4
|
+
/ \ | |_ _ __ ___ ___ ___ _ __ | |__ ___ _ __ _ ___ \ V /
|
|
5
|
+
/ /\ \| __| '_ ` _ \ / _ \/ __| '_ \| '_ \ / _ \ '__| |/ __| > <
|
|
6
|
+
/ ____ \ |_| | | | | | (_) \__ \ |_) | | | | __/ | | | (__ / . \
|
|
7
|
+
/_/ \_\__|_| |_| |_|\___/|___/ .__/|_| |_|\___|_| |_|\___/_/ \_\
|
|
8
|
+
| |
|
|
9
|
+
|_|
|
|
10
|
+
|
|
11
|
+
Created with ♥ by the AtmosphericX Team (KiyoWx, StarflightWx, & CJ Ziegler)
|
|
12
|
+
Discord: https://atmosphericx-discord.scriptkitty.cafe
|
|
13
|
+
Ko-Fi: https://ko-fi.com/k3yomi
|
|
14
|
+
Documentation: http://localhost/documentation | https://atmosphericx.scriptkitty.cafe/documentation
|
|
15
|
+
|
|
16
|
+
Internal Package: @atmosx/event-product-parser
|
|
17
|
+
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
export const getExpiry = (message: string): string | null => {
|
|
21
|
+
const match = message.match(/\b(\d{6})-/);
|
|
22
|
+
if (!match) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
const date = match?.[1];
|
|
26
|
+
const day = parseInt(date?.slice(0, 2), 10);
|
|
27
|
+
const hour = parseInt(date?.slice(2, 4), 10);
|
|
28
|
+
const minute = parseInt(date?.slice(4, 6), 10);
|
|
29
|
+
const now = new Date();
|
|
30
|
+
const expires = new Date(Date.UTC(now.getUTCFullYear(),now.getUTCMonth(),day,hour,minute));
|
|
31
|
+
return expires.toISOString();
|
|
32
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/*
|
|
2
|
+
_ _ _ __ __
|
|
3
|
+
/\ | | | | (_) \ \ / /
|
|
4
|
+
/ \ | |_ _ __ ___ ___ ___ _ __ | |__ ___ _ __ _ ___ \ V /
|
|
5
|
+
/ /\ \| __| '_ ` _ \ / _ \/ __| '_ \| '_ \ / _ \ '__| |/ __| > <
|
|
6
|
+
/ ____ \ |_| | | | | | (_) \__ \ |_) | | | | __/ | | | (__ / . \
|
|
7
|
+
/_/ \_\__|_| |_| |_|\___/|___/ .__/|_| |_|\___|_| |_|\___/_/ \_\
|
|
8
|
+
| |
|
|
9
|
+
|_|
|
|
10
|
+
|
|
11
|
+
Created with ♥ by the AtmosphericX Team (KiyoWx, StarflightWx, & CJ Ziegler)
|
|
12
|
+
Discord: https://atmosphericx-discord.scriptkitty.cafe
|
|
13
|
+
Ko-Fi: https://ko-fi.com/k3yomi
|
|
14
|
+
Documentation: http://localhost/documentation | https://atmosphericx.scriptkitty.cafe/documentation
|
|
15
|
+
|
|
16
|
+
Internal Package: @atmosx/event-product-parser
|
|
17
|
+
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { TypeUGC } from "../../@types/types.ugc";
|
|
21
|
+
import { getUGCHeader } from "./ugc.header"
|
|
22
|
+
import { getZones } from "./ugc.zones";
|
|
23
|
+
import { getExpiry } from './ugc.expiry';
|
|
24
|
+
import { getLocations } from "./ugc.locations";
|
|
25
|
+
|
|
26
|
+
export const ugcExtract = async (message: string): Promise<TypeUGC> => {
|
|
27
|
+
const head = getUGCHeader(message);
|
|
28
|
+
const ugcs = getZones(head)
|
|
29
|
+
const expires = getExpiry(message)
|
|
30
|
+
const areas = await getLocations(ugcs)
|
|
31
|
+
if (!head || ugcs?.length == 0) return;
|
|
32
|
+
return {
|
|
33
|
+
zones: ugcs,
|
|
34
|
+
locations: areas,
|
|
35
|
+
expires: expires
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/*
|
|
2
|
+
_ _ _ __ __
|
|
3
|
+
/\ | | | | (_) \ \ / /
|
|
4
|
+
/ \ | |_ _ __ ___ ___ ___ _ __ | |__ ___ _ __ _ ___ \ V /
|
|
5
|
+
/ /\ \| __| '_ ` _ \ / _ \/ __| '_ \| '_ \ / _ \ '__| |/ __| > <
|
|
6
|
+
/ ____ \ |_| | | | | | (_) \__ \ |_) | | | | __/ | | | (__ / . \
|
|
7
|
+
/_/ \_\__|_| |_| |_|\___/|___/ .__/|_| |_|\___|_| |_|\___/_/ \_\
|
|
8
|
+
| |
|
|
9
|
+
|_|
|
|
10
|
+
|
|
11
|
+
Created with ♥ by the AtmosphericX Team (KiyoWx, StarflightWx, & CJ Ziegler)
|
|
12
|
+
Discord: https://atmosphericx-discord.scriptkitty.cafe
|
|
13
|
+
Ko-Fi: https://ko-fi.com/k3yomi
|
|
14
|
+
Documentation: http://localhost/documentation | https://atmosphericx.scriptkitty.cafe/documentation
|
|
15
|
+
|
|
16
|
+
Internal Package: @atmosx/event-product-parser
|
|
17
|
+
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { regExp } from "../../@dictionaries/dictionaries.regExp"
|
|
21
|
+
|
|
22
|
+
export const getUGCHeader = (message: string): string | null => {
|
|
23
|
+
const start = message.search(regExp.ugc1)
|
|
24
|
+
const sub = message.substring(start)
|
|
25
|
+
const end = sub.search(regExp.ugc2)
|
|
26
|
+
const fin = sub.substring(0, end)
|
|
27
|
+
.replace(/\s+/g, '')
|
|
28
|
+
.slice(0, -1);
|
|
29
|
+
return fin ?? null
|
|
30
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/*
|
|
2
|
+
_ _ _ __ __
|
|
3
|
+
/\ | | | | (_) \ \ / /
|
|
4
|
+
/ \ | |_ _ __ ___ ___ ___ _ __ | |__ ___ _ __ _ ___ \ V /
|
|
5
|
+
/ /\ \| __| '_ ` _ \ / _ \/ __| '_ \| '_ \ / _ \ '__| |/ __| > <
|
|
6
|
+
/ ____ \ |_| | | | | | (_) \__ \ |_) | | | | __/ | | | (__ / . \
|
|
7
|
+
/_/ \_\__|_| |_| |_|\___/|___/ .__/|_| |_|\___|_| |_|\___/_/ \_\
|
|
8
|
+
| |
|
|
9
|
+
|_|
|
|
10
|
+
|
|
11
|
+
Created with ♥ by the AtmosphericX Team (KiyoWx, StarflightWx, & CJ Ziegler)
|
|
12
|
+
Discord: https://atmosphericx-discord.scriptkitty.cafe
|
|
13
|
+
Ko-Fi: https://ko-fi.com/k3yomi
|
|
14
|
+
Documentation: http://localhost/documentation | https://atmosphericx.scriptkitty.cafe/documentation
|
|
15
|
+
|
|
16
|
+
Internal Package: @atmosx/event-product-parser
|
|
17
|
+
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { bootstrap } from "../../bootstrap";
|
|
21
|
+
|
|
22
|
+
export const getLocations = async (zones: string[]): Promise<string[]> => {
|
|
23
|
+
const sites = Array.from(new Set(zones));
|
|
24
|
+
const placeholders = sites.map(() => '?').join(',');
|
|
25
|
+
const rows = await bootstrap.database
|
|
26
|
+
.prepare(`SELECT id, location FROM shapefiles WHERE id IN (${placeholders})`)
|
|
27
|
+
.all(...sites)
|
|
28
|
+
return rows.map((row: any) => row.location).sort();
|
|
29
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/*
|
|
2
|
+
_ _ _ __ __
|
|
3
|
+
/\ | | | | (_) \ \ / /
|
|
4
|
+
/ \ | |_ _ __ ___ ___ ___ _ __ | |__ ___ _ __ _ ___ \ V /
|
|
5
|
+
/ /\ \| __| '_ ` _ \ / _ \/ __| '_ \| '_ \ / _ \ '__| |/ __| > <
|
|
6
|
+
/ ____ \ |_| | | | | | (_) \__ \ |_) | | | | __/ | | | (__ / . \
|
|
7
|
+
/_/ \_\__|_| |_| |_|\___/|___/ .__/|_| |_|\___|_| |_|\___/_/ \_\
|
|
8
|
+
| |
|
|
9
|
+
|_|
|
|
10
|
+
|
|
11
|
+
Created with ♥ by the AtmosphericX Team (KiyoWx, StarflightWx, & CJ Ziegler)
|
|
12
|
+
Discord: https://atmosphericx-discord.scriptkitty.cafe
|
|
13
|
+
Ko-Fi: https://ko-fi.com/k3yomi
|
|
14
|
+
Documentation: http://localhost/documentation | https://atmosphericx.scriptkitty.cafe/documentation
|
|
15
|
+
|
|
16
|
+
Internal Package: @atmosx/event-product-parser
|
|
17
|
+
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
export const getZones = (header: string): string[] => {
|
|
21
|
+
const splits = header.split('-');
|
|
22
|
+
const zones: string[] = [];
|
|
23
|
+
let state = splits[0].substring(0, 2);
|
|
24
|
+
const format = splits[0].substring(2, 3);
|
|
25
|
+
for (const part of splits) {
|
|
26
|
+
if (/^[A-Z]/.test(part)) {
|
|
27
|
+
state = part.substring(0, 2);
|
|
28
|
+
if (part.includes('>')) {
|
|
29
|
+
const [start, end] = part.split('>');
|
|
30
|
+
const startNum = parseInt(start.substring(3), 10);
|
|
31
|
+
const endNum = parseInt(end, 10);
|
|
32
|
+
for (let j = startNum; j <= endNum; j++) {
|
|
33
|
+
zones.push(`${state}${format}${j.toString().padStart(3, '0')}`);
|
|
34
|
+
}
|
|
35
|
+
} else {
|
|
36
|
+
zones.push(part);
|
|
37
|
+
}
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (part.includes('>')) {
|
|
41
|
+
const [start, end] = part.split('>');
|
|
42
|
+
const startNum = parseInt(start, 10);
|
|
43
|
+
const endNum = parseInt(end, 10);
|
|
44
|
+
for (let j = startNum; j <= endNum; j++) {
|
|
45
|
+
zones.push(`${state}${format}${j.toString().padStart(3, '0')}`);
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
zones.push(`${state}${format}${part}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return zones.filter(item => item !== '');
|
|
52
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/*
|
|
2
|
+
_ _ _ __ __
|
|
3
|
+
/\ | | | | (_) \ \ / /
|
|
4
|
+
/ \ | |_ _ __ ___ ___ ___ _ __ | |__ ___ _ __ _ ___ \ V /
|
|
5
|
+
/ /\ \| __| '_ ` _ \ / _ \/ __| '_ \| '_ \ / _ \ '__| |/ __| > <
|
|
6
|
+
/ ____ \ |_| | | | | | (_) \__ \ |_) | | | | __/ | | | (__ / . \
|
|
7
|
+
/_/ \_\__|_| |_| |_|\___/|___/ .__/|_| |_|\___|_| |_|\___/_/ \_\
|
|
8
|
+
| |
|
|
9
|
+
|_|
|
|
10
|
+
|
|
11
|
+
Created with ♥ by the AtmosphericX Team (KiyoWx, StarflightWx, & CJ Ziegler)
|
|
12
|
+
Discord: https://atmosphericx-discord.scriptkitty.cafe
|
|
13
|
+
Ko-Fi: https://ko-fi.com/k3yomi
|
|
14
|
+
Documentation: http://localhost/documentation | https://atmosphericx.scriptkitty.cafe/documentation
|
|
15
|
+
|
|
16
|
+
Internal Package: @atmosx/event-product-parser
|
|
17
|
+
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { TypeEventProperties as BaseTypeEventProperties } from "./type.properties";
|
|
21
|
+
import { TypeHVTEC } from "../@types/types.hvtec";
|
|
22
|
+
import { TypePVTEC } from "./types.pvtec";
|
|
23
|
+
|
|
24
|
+
export type TypeEvent = {
|
|
25
|
+
type: string
|
|
26
|
+
geometry: {
|
|
27
|
+
type: string
|
|
28
|
+
coordinates: number[][]
|
|
29
|
+
}
|
|
30
|
+
properties: {
|
|
31
|
+
event: string
|
|
32
|
+
parent: string
|
|
33
|
+
status: string
|
|
34
|
+
issued: string
|
|
35
|
+
expires: string
|
|
36
|
+
status_metadata?: {
|
|
37
|
+
is_issued?: boolean
|
|
38
|
+
is_updated?: boolean
|
|
39
|
+
is_expired?: boolean
|
|
40
|
+
is_test?: boolean
|
|
41
|
+
}
|
|
42
|
+
metadata: {
|
|
43
|
+
ms: number
|
|
44
|
+
source: string
|
|
45
|
+
tracking: string
|
|
46
|
+
hash?: string
|
|
47
|
+
header: string
|
|
48
|
+
vtec: TypePVTEC | null
|
|
49
|
+
hvtec: TypeHVTEC[]
|
|
50
|
+
nodes?: {
|
|
51
|
+
id?: string | number
|
|
52
|
+
coordinates: [number, number]
|
|
53
|
+
nearest: [number, number]
|
|
54
|
+
miles: number | null
|
|
55
|
+
kilometers: number | null
|
|
56
|
+
proximity: boolean
|
|
57
|
+
}[],
|
|
58
|
+
filtered_proximity?: boolean
|
|
59
|
+
updated?: number
|
|
60
|
+
history: {
|
|
61
|
+
description: string
|
|
62
|
+
issued: string
|
|
63
|
+
status: string
|
|
64
|
+
}[]
|
|
65
|
+
}
|
|
66
|
+
} & BaseTypeEventProperties
|
|
67
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/*
|
|
2
|
+
_ _ _ __ __
|
|
3
|
+
/\ | | | | (_) \ \ / /
|
|
4
|
+
/ \ | |_ _ __ ___ ___ ___ _ __ | |__ ___ _ __ _ ___ \ V /
|
|
5
|
+
/ /\ \| __| '_ ` _ \ / _ \/ __| '_ \| '_ \ / _ \ '__| |/ __| > <
|
|
6
|
+
/ ____ \ |_| | | | | | (_) \__ \ |_) | | | | __/ | | | (__ / . \
|
|
7
|
+
/_/ \_\__|_| |_| |_|\___/|___/ .__/|_| |_|\___|_| |_|\___/_/ \_\
|
|
8
|
+
| |
|
|
9
|
+
|_|
|
|
10
|
+
|
|
11
|
+
Created with ♥ by the AtmosphericX Team (KiyoWx, StarflightWx, & CJ Ziegler)
|
|
12
|
+
Discord: https://atmosphericx-discord.scriptkitty.cafe
|
|
13
|
+
Ko-Fi: https://ko-fi.com/k3yomi
|
|
14
|
+
Documentation: http://localhost/documentation | https://atmosphericx.scriptkitty.cafe/documentation
|
|
15
|
+
|
|
16
|
+
Internal Package: @atmosx/event-product-parser
|
|
17
|
+
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { TypeAttributes } from "./types.attributes";
|
|
21
|
+
|
|
22
|
+
export type TypeEventProperties = {
|
|
23
|
+
locations: string
|
|
24
|
+
description: string
|
|
25
|
+
attributes: TypeAttributes
|
|
26
|
+
geocode: {
|
|
27
|
+
office: {
|
|
28
|
+
name: string
|
|
29
|
+
office: string
|
|
30
|
+
}
|
|
31
|
+
organization: string
|
|
32
|
+
ugc: string[]
|
|
33
|
+
polygon: string
|
|
34
|
+
polygon_generated: boolean
|
|
35
|
+
}
|
|
36
|
+
parameters: {
|
|
37
|
+
tags: string[]
|
|
38
|
+
instructions: string
|
|
39
|
+
source: string
|
|
40
|
+
hazards: string
|
|
41
|
+
impacts: string
|
|
42
|
+
estimated_hail_size: string
|
|
43
|
+
estimated_wind_gusts: string
|
|
44
|
+
damage_threat: string
|
|
45
|
+
tornado_threat: string
|
|
46
|
+
flood_threat: string
|
|
47
|
+
wind_threat: string
|
|
48
|
+
hail_threat: string
|
|
49
|
+
max_hail_inches?: string
|
|
50
|
+
max_wind_gusts_surface_knots?: string
|
|
51
|
+
max_tops_x100feet?: string
|
|
52
|
+
mean_storm_motion_vector?: string
|
|
53
|
+
particularly_dangerous_situation?: string
|
|
54
|
+
}
|
|
55
|
+
watch_parameters?: {
|
|
56
|
+
watch_number: string
|
|
57
|
+
watch_type: string
|
|
58
|
+
additional_tornadoes_probability: string
|
|
59
|
+
strong_tornadoes_probability: string
|
|
60
|
+
severe_wind_probability: string
|
|
61
|
+
severe_hail_probability: string
|
|
62
|
+
hail_2in_probability: string
|
|
63
|
+
combined_hail_wind_probability: string
|
|
64
|
+
max_hail_in: string
|
|
65
|
+
max_wind_surface: string
|
|
66
|
+
max_tops_x100feet: string
|
|
67
|
+
pds_watch: boolean
|
|
68
|
+
}
|
|
69
|
+
spc_parameters: {
|
|
70
|
+
spc_max_tornado: string
|
|
71
|
+
spc_max_hail: string
|
|
72
|
+
spc_max_wind: string
|
|
73
|
+
spc_watch_issuance: string
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/*
|
|
2
|
+
_ _ _ __ __
|
|
3
|
+
/\ | | | | (_) \ \ / /
|
|
4
|
+
/ \ | |_ _ __ ___ ___ ___ _ __ | |__ ___ _ __ _ ___ \ V /
|
|
5
|
+
/ /\ \| __| '_ ` _ \ / _ \/ __| '_ \| '_ \ / _ \ '__| |/ __| > <
|
|
6
|
+
/ ____ \ |_| | | | | | (_) \__ \ |_) | | | | __/ | | | (__ / . \
|
|
7
|
+
/_/ \_\__|_| |_| |_|\___/|___/ .__/|_| |_|\___|_| |_|\___/_/ \_\
|
|
8
|
+
| |
|
|
9
|
+
|_|
|
|
10
|
+
|
|
11
|
+
Created with ♥ by the AtmosphericX Team (KiyoWx, StarflightWx, & CJ Ziegler)
|
|
12
|
+
Discord: https://atmosphericx-discord.scriptkitty.cafe
|
|
13
|
+
Ko-Fi: https://ko-fi.com/k3yomi
|
|
14
|
+
Documentation: http://localhost/documentation | https://atmosphericx.scriptkitty.cafe/documentation
|
|
15
|
+
|
|
16
|
+
Internal Package: @atmosx/event-product-parser
|
|
17
|
+
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
export type TypeAttributes = {
|
|
22
|
+
xmlns: string
|
|
23
|
+
id: string
|
|
24
|
+
issue: string
|
|
25
|
+
ttaaii: string
|
|
26
|
+
cccc: string
|
|
27
|
+
awipsid: string
|
|
28
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/*
|
|
2
|
+
_ _ _ __ __
|
|
3
|
+
/\ | | | | (_) \ \ / /
|
|
4
|
+
/ \ | |_ _ __ ___ ___ ___ _ __ | |__ ___ _ __ _ ___ \ V /
|
|
5
|
+
/ /\ \| __| '_ ` _ \ / _ \/ __| '_ \| '_ \ / _ \ '__| |/ __| > <
|
|
6
|
+
/ ____ \ |_| | | | | | (_) \__ \ |_) | | | | __/ | | | (__ / . \
|
|
7
|
+
/_/ \_\__|_| |_| |_|\___/|___/ .__/|_| |_|\___|_| |_|\___/_/ \_\
|
|
8
|
+
| |
|
|
9
|
+
|_|
|
|
10
|
+
|
|
11
|
+
Created with ♥ by the AtmosphericX Team (KiyoWx, StarflightWx, & CJ Ziegler)
|
|
12
|
+
Discord: https://atmosphericx-discord.scriptkitty.cafe
|
|
13
|
+
Ko-Fi: https://ko-fi.com/k3yomi
|
|
14
|
+
Documentation: http://localhost/documentation | https://atmosphericx.scriptkitty.cafe/documentation
|
|
15
|
+
|
|
16
|
+
Internal Package: @atmosx/event-product-parser
|
|
17
|
+
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { TypeAttributes } from "./types.attributes"
|
|
21
|
+
|
|
22
|
+
export type TypeStanzaCompiled = {
|
|
23
|
+
message?: string
|
|
24
|
+
attributes?: TypeAttributes
|
|
25
|
+
isCapEvent?: boolean
|
|
26
|
+
isVTEC?: boolean
|
|
27
|
+
isUGC?: boolean
|
|
28
|
+
isCapAreaDescription?: boolean
|
|
29
|
+
isIgnored?: boolean
|
|
30
|
+
isNWWS?: boolean
|
|
31
|
+
getType?: {
|
|
32
|
+
type: string
|
|
33
|
+
prefix: string
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/*
|
|
2
|
+
_ _ _ __ __
|
|
3
|
+
/\ | | | | (_) \ \ / /
|
|
4
|
+
/ \ | |_ _ __ ___ ___ ___ _ __ | |__ ___ _ __ _ ___ \ V /
|
|
5
|
+
/ /\ \| __| '_ ` _ \ / _ \/ __| '_ \| '_ \ / _ \ '__| |/ __| > <
|
|
6
|
+
/ ____ \ |_| | | | | | (_) \__ \ |_) | | | | __/ | | | (__ / . \
|
|
7
|
+
/_/ \_\__|_| |_| |_|\___/|___/ .__/|_| |_|\___|_| |_|\___/_/ \_\
|
|
8
|
+
| |
|
|
9
|
+
|_|
|
|
10
|
+
|
|
11
|
+
Created with ♥ by the AtmosphericX Team (KiyoWx, StarflightWx, & CJ Ziegler)
|
|
12
|
+
Discord: https://atmosphericx-discord.scriptkitty.cafe
|
|
13
|
+
Ko-Fi: https://ko-fi.com/k3yomi
|
|
14
|
+
Documentation: http://localhost/documentation | https://atmosphericx.scriptkitty.cafe/documentation
|
|
15
|
+
|
|
16
|
+
Internal Package: @atmosx/event-product-parser
|
|
17
|
+
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
export type TypeHash = {
|
|
21
|
+
tracking: string
|
|
22
|
+
hashes: string[]
|
|
23
|
+
expires: string
|
|
24
|
+
}
|