@atmosx/event-product-parser 2.0.1 → 2.0.12
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/CODE_OF_CONDUCT.md +10 -0
- package/CONTRIBUTING.md +30 -0
- package/README.md +1 -2
- package/SECURITY.md +21 -0
- package/dist/cjs/index.cjs +56 -48
- package/dist/esm/index.mjs +56 -48
- package/package.json +1 -6
- package/src/@parsers/text.ts +1 -1
- package/src/@submodules/database.ts +38 -0
- package/src/@submodules/utils.ts +0 -38
- package/src/bootstrap.ts +1 -9
- package/src/index.ts +24 -2
- package/test.js +62 -0
- package/tsconfig.json +14 -0
- package/tsup.config.ts +15 -0
- package/dist/index.d.mts +0 -1024
- package/dist/index.d.ts +0 -1024
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Code of Conduct
|
|
2
|
+
AtmosphericX is an independent, open source project and is **not** affiliated with, endorsed by, or sponsored by any government agency, meteorological organization, emergency management service, or official weather provider. This project may reference, parse, or process publicly available weather data, including but **not** limited to services operated by the National Weather Service (NWS) and the National Oceanic and Atmospheric Administration (NOAA). All trademarks, service marks, and data rights remain the property of their respective owners. AtmosphericX is provided "as is", without warranty of any kind, express or implied, including but **not** limited to:
|
|
3
|
+
|
|
4
|
+
- Accuracy or completeness of parsed weather data
|
|
5
|
+
- Fitness for a particular purpose
|
|
6
|
+
- Protection against sources and data retrieval
|
|
7
|
+
- Availability or reliability of upstream data sources
|
|
8
|
+
- Protection against service interruptions or data delays
|
|
9
|
+
|
|
10
|
+
AtmosphericX should **not** be relied upon for life safety decisions, emergency response coordination, aviation, marine navigation, or other critical applications. Always consult official sources for authoritative weather information. By using this software, you acknowledge and agree that the maintainers and contributors are **not** liable for any damages, losses, or consequences resulting from its use.
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
This project is community driven and primarily maintained by `k3yomi` and `StarflightWx`
|
|
4
|
+
|
|
5
|
+
## How to Contribute
|
|
6
|
+
|
|
7
|
+
At this time, contributions are limited to:
|
|
8
|
+
|
|
9
|
+
- **Corrections** – Fix broken TypeScript syntax, outdated operations, or errors.
|
|
10
|
+
- **Additions** – Add new functions, tools, or services.
|
|
11
|
+
- **Clarity Improvements** – Improve explanations in plain English.
|
|
12
|
+
- **Documentation** – Add helpful content to the documentation.
|
|
13
|
+
|
|
14
|
+
## What Will Not Be Accepted
|
|
15
|
+
|
|
16
|
+
- Content that introduces new packages.
|
|
17
|
+
- Anything that violates GitHub's Terms of Service.
|
|
18
|
+
|
|
19
|
+
## How to Submit
|
|
20
|
+
|
|
21
|
+
1. Fork the repository.
|
|
22
|
+
2. Create a new branch: `fix/description` or `add/description`.
|
|
23
|
+
3. Make your changes.
|
|
24
|
+
4. Open a pull request with a clear description of **what changed and why**.
|
|
25
|
+
|
|
26
|
+
## Requirements
|
|
27
|
+
|
|
28
|
+
- Use **plain English first, details later**.
|
|
29
|
+
- Provide **real working examples only** (do not make up syntax).
|
|
30
|
+
- Follow the existing **file structure and function signature format**.
|
package/README.md
CHANGED
package/SECURITY.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Reporting Vulnerabilities
|
|
4
|
+
If you discover a security vulnerability or issue in this repository such as exposed sensitive data, misconfigurations, or content that could cause harm. Please report it **immediately and responsibly**.
|
|
5
|
+
|
|
6
|
+
**Do not report security issues publicly.**
|
|
7
|
+
Instead, report privately by emailing **antimal@scriptkitty.cafe**, clearly describing the vulnerability.
|
|
8
|
+
|
|
9
|
+
## What to Include
|
|
10
|
+
When reporting a vulnerability, please include:
|
|
11
|
+
- A detailed description of the issue
|
|
12
|
+
- Affected files (include line numbers if possible)
|
|
13
|
+
- Any recommendations
|
|
14
|
+
|
|
15
|
+
## Response
|
|
16
|
+
All reports will be reviewed and addressed as quickly as possible.
|
|
17
|
+
This is a community driven project, so response times may vary. Please allow up to a few weeks for a full response.
|
|
18
|
+
|
|
19
|
+
## Scope
|
|
20
|
+
This repository **does not contain any credentials, keys, tokens, or personally identifiable information (PII)**.
|
|
21
|
+
If you discover any such data, report it immediately.
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -4910,18 +4910,6 @@ var definitions = {
|
|
|
4910
4910
|
dump_cache_complete: `Completed dumping all cached alert files.`
|
|
4911
4911
|
}
|
|
4912
4912
|
};
|
|
4913
|
-
process.on("uncaughtException", (err) => {
|
|
4914
|
-
if ((err == null ? void 0 : err.code) === "ETIMEDOUT") {
|
|
4915
|
-
return;
|
|
4916
|
-
}
|
|
4917
|
-
if ((err == null ? void 0 : err.code) === "ECONNRESET") {
|
|
4918
|
-
return;
|
|
4919
|
-
}
|
|
4920
|
-
if ((err == null ? void 0 : err.code) === "EHOSTUNREACH") {
|
|
4921
|
-
return;
|
|
4922
|
-
}
|
|
4923
|
-
throw err;
|
|
4924
|
-
});
|
|
4925
4913
|
|
|
4926
4914
|
// src/@parsers/text.ts
|
|
4927
4915
|
var TextParser = class {
|
|
@@ -6513,6 +6501,41 @@ var Database = class {
|
|
|
6513
6501
|
}
|
|
6514
6502
|
});
|
|
6515
6503
|
}
|
|
6504
|
+
/**
|
|
6505
|
+
* @function loadCollectionCache
|
|
6506
|
+
* @description
|
|
6507
|
+
* Loads cached stanzas from the database, validates them, and processes them through the event parser.
|
|
6508
|
+
* Only processes stanzas that are not marked to be ignored and match the CAP preferences.
|
|
6509
|
+
*
|
|
6510
|
+
* @static
|
|
6511
|
+
* @async
|
|
6512
|
+
* @returns {Promise<void>}
|
|
6513
|
+
*/
|
|
6514
|
+
static loadCollectionCache() {
|
|
6515
|
+
return __async(this, null, function* () {
|
|
6516
|
+
var _a;
|
|
6517
|
+
try {
|
|
6518
|
+
const settings2 = settings;
|
|
6519
|
+
if (settings2.noaa_weather_wire_service_settings.cache.enabled) {
|
|
6520
|
+
const maxRows = (_a = settings2.noaa_weather_wire_service_settings.cache.max_db_cache_size) != null ? _a : 5e3;
|
|
6521
|
+
const rows = yield cache.db.prepare(`SELECT * FROM stanzas ORDER BY rowid DESC LIMIT ?`).all(maxRows);
|
|
6522
|
+
utils_default.warn(definitions.messages.dump_cache.replace(`{count}`, rows.length.toString()), true);
|
|
6523
|
+
const eventsToProcess = rows.map((row) => {
|
|
6524
|
+
return JSON.parse(row.stanza);
|
|
6525
|
+
}).filter((validate) => {
|
|
6526
|
+
if (!validate) return false;
|
|
6527
|
+
const skip = validate.ignore || validate.isCap && !settings2.noaa_weather_wire_service_settings.preferences.cap_only || !validate.isCap && settings2.noaa_weather_wire_service_settings.preferences.cap_only || validate.isCap && !validate.isCapDescription;
|
|
6528
|
+
return !skip;
|
|
6529
|
+
});
|
|
6530
|
+
yield Promise.all(eventsToProcess.map((validate) => events_default.eventHandler(validate)));
|
|
6531
|
+
utils_default.warn(definitions.messages.dump_cache_complete, true);
|
|
6532
|
+
return;
|
|
6533
|
+
}
|
|
6534
|
+
} catch (error) {
|
|
6535
|
+
utils_default.warn(`Failed to load cache: ${error.stack}`);
|
|
6536
|
+
}
|
|
6537
|
+
});
|
|
6538
|
+
}
|
|
6516
6539
|
};
|
|
6517
6540
|
var database_default = Database;
|
|
6518
6541
|
|
|
@@ -6686,41 +6709,6 @@ var Utils = class _Utils {
|
|
|
6686
6709
|
cache.lastWarn = Date.now();
|
|
6687
6710
|
console.warn(`\x1B[33m[ATMOSX-PARSER]\x1B[0m [${(/* @__PURE__ */ new Date()).toLocaleString()}] ${message}`);
|
|
6688
6711
|
}
|
|
6689
|
-
/**
|
|
6690
|
-
* @function loadCollectionCache
|
|
6691
|
-
* @description
|
|
6692
|
-
* Loads cached stanzas from the database, validates them, and processes them through the event parser.
|
|
6693
|
-
* Only processes stanzas that are not marked to be ignored and match the CAP preferences.
|
|
6694
|
-
*
|
|
6695
|
-
* @static
|
|
6696
|
-
* @async
|
|
6697
|
-
* @returns {Promise<void>}
|
|
6698
|
-
*/
|
|
6699
|
-
static loadCollectionCache() {
|
|
6700
|
-
return __async(this, null, function* () {
|
|
6701
|
-
var _a;
|
|
6702
|
-
try {
|
|
6703
|
-
const settings2 = settings;
|
|
6704
|
-
if (settings2.noaa_weather_wire_service_settings.cache.enabled) {
|
|
6705
|
-
const maxRows = (_a = settings2.noaa_weather_wire_service_settings.cache.max_db_cache_size) != null ? _a : 5e3;
|
|
6706
|
-
const rows = yield cache.db.prepare(`SELECT * FROM stanzas ORDER BY rowid DESC LIMIT ?`).all(maxRows);
|
|
6707
|
-
this.warn(definitions.messages.dump_cache.replace(`{count}`, rows.length.toString()), true);
|
|
6708
|
-
const eventsToProcess = rows.map((row) => {
|
|
6709
|
-
return JSON.parse(row.stanza);
|
|
6710
|
-
}).filter((validate) => {
|
|
6711
|
-
if (!validate) return false;
|
|
6712
|
-
const skip = validate.ignore || validate.isCap && !settings2.noaa_weather_wire_service_settings.preferences.cap_only || !validate.isCap && settings2.noaa_weather_wire_service_settings.preferences.cap_only || validate.isCap && !validate.isCapDescription;
|
|
6713
|
-
return !skip;
|
|
6714
|
-
});
|
|
6715
|
-
yield Promise.all(eventsToProcess.map((validate) => events_default.eventHandler(validate)));
|
|
6716
|
-
this.warn(definitions.messages.dump_cache_complete, true);
|
|
6717
|
-
return;
|
|
6718
|
-
}
|
|
6719
|
-
} catch (error) {
|
|
6720
|
-
_Utils.warn(`Failed to load cache: ${error.stack}`);
|
|
6721
|
-
}
|
|
6722
|
-
});
|
|
6723
|
-
}
|
|
6724
6712
|
/**
|
|
6725
6713
|
* @function loadGeoJsonData
|
|
6726
6714
|
* @description
|
|
@@ -7338,6 +7326,7 @@ var eas_default = EAS;
|
|
|
7338
7326
|
// src/index.ts
|
|
7339
7327
|
var Manager = class {
|
|
7340
7328
|
constructor(metadata) {
|
|
7329
|
+
this.sigCatch();
|
|
7341
7330
|
this.start(metadata);
|
|
7342
7331
|
}
|
|
7343
7332
|
/**
|
|
@@ -7480,7 +7469,7 @@ var Manager = class {
|
|
|
7480
7469
|
(() => __async(this, null, function* () {
|
|
7481
7470
|
try {
|
|
7482
7471
|
yield xmpp_default.deploySession();
|
|
7483
|
-
yield
|
|
7472
|
+
yield database_default.loadCollectionCache();
|
|
7484
7473
|
} catch (err) {
|
|
7485
7474
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7486
7475
|
utils_default.warn(`Failed to initialize NWWS services: ${msg}`);
|
|
@@ -7537,6 +7526,25 @@ var Manager = class {
|
|
|
7537
7526
|
}
|
|
7538
7527
|
});
|
|
7539
7528
|
}
|
|
7529
|
+
/**
|
|
7530
|
+
* @function sigCatch
|
|
7531
|
+
* @description
|
|
7532
|
+
* Sets up a global handler for uncaught exceptions, ignoring specific error codes
|
|
7533
|
+
*
|
|
7534
|
+
* @async
|
|
7535
|
+
* @returns void
|
|
7536
|
+
*/
|
|
7537
|
+
sigCatch() {
|
|
7538
|
+
process.on("uncaughtException", (err) => {
|
|
7539
|
+
var _a;
|
|
7540
|
+
const ignored = ["ETIMEDOUT", "ECONNRESET", "EHOSTUNREACH", "STARTTLS_FAILURE"];
|
|
7541
|
+
if (ignored.includes(err == null ? void 0 : err.code)) {
|
|
7542
|
+
utils_default.warn(`XMPP Critical Error: ${(_a = err == null ? void 0 : err.code) != null ? _a : "Unknown error code"}. This may indicate a connection issue. Attempting to continue...`);
|
|
7543
|
+
return;
|
|
7544
|
+
}
|
|
7545
|
+
utils_default.warn(`Uncaught Exception: ${err instanceof Error ? err.stack || err.message : String(err)}`);
|
|
7546
|
+
});
|
|
7547
|
+
}
|
|
7540
7548
|
};
|
|
7541
7549
|
var index_default = Manager;
|
|
7542
7550
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/esm/index.mjs
CHANGED
|
@@ -4898,18 +4898,6 @@ var definitions = {
|
|
|
4898
4898
|
dump_cache_complete: `Completed dumping all cached alert files.`
|
|
4899
4899
|
}
|
|
4900
4900
|
};
|
|
4901
|
-
process.on("uncaughtException", (err) => {
|
|
4902
|
-
if ((err == null ? void 0 : err.code) === "ETIMEDOUT") {
|
|
4903
|
-
return;
|
|
4904
|
-
}
|
|
4905
|
-
if ((err == null ? void 0 : err.code) === "ECONNRESET") {
|
|
4906
|
-
return;
|
|
4907
|
-
}
|
|
4908
|
-
if ((err == null ? void 0 : err.code) === "EHOSTUNREACH") {
|
|
4909
|
-
return;
|
|
4910
|
-
}
|
|
4911
|
-
throw err;
|
|
4912
|
-
});
|
|
4913
4901
|
|
|
4914
4902
|
// src/@parsers/text.ts
|
|
4915
4903
|
var TextParser = class {
|
|
@@ -6501,6 +6489,41 @@ var Database = class {
|
|
|
6501
6489
|
}
|
|
6502
6490
|
});
|
|
6503
6491
|
}
|
|
6492
|
+
/**
|
|
6493
|
+
* @function loadCollectionCache
|
|
6494
|
+
* @description
|
|
6495
|
+
* Loads cached stanzas from the database, validates them, and processes them through the event parser.
|
|
6496
|
+
* Only processes stanzas that are not marked to be ignored and match the CAP preferences.
|
|
6497
|
+
*
|
|
6498
|
+
* @static
|
|
6499
|
+
* @async
|
|
6500
|
+
* @returns {Promise<void>}
|
|
6501
|
+
*/
|
|
6502
|
+
static loadCollectionCache() {
|
|
6503
|
+
return __async(this, null, function* () {
|
|
6504
|
+
var _a;
|
|
6505
|
+
try {
|
|
6506
|
+
const settings2 = settings;
|
|
6507
|
+
if (settings2.noaa_weather_wire_service_settings.cache.enabled) {
|
|
6508
|
+
const maxRows = (_a = settings2.noaa_weather_wire_service_settings.cache.max_db_cache_size) != null ? _a : 5e3;
|
|
6509
|
+
const rows = yield cache.db.prepare(`SELECT * FROM stanzas ORDER BY rowid DESC LIMIT ?`).all(maxRows);
|
|
6510
|
+
utils_default.warn(definitions.messages.dump_cache.replace(`{count}`, rows.length.toString()), true);
|
|
6511
|
+
const eventsToProcess = rows.map((row) => {
|
|
6512
|
+
return JSON.parse(row.stanza);
|
|
6513
|
+
}).filter((validate) => {
|
|
6514
|
+
if (!validate) return false;
|
|
6515
|
+
const skip = validate.ignore || validate.isCap && !settings2.noaa_weather_wire_service_settings.preferences.cap_only || !validate.isCap && settings2.noaa_weather_wire_service_settings.preferences.cap_only || validate.isCap && !validate.isCapDescription;
|
|
6516
|
+
return !skip;
|
|
6517
|
+
});
|
|
6518
|
+
yield Promise.all(eventsToProcess.map((validate) => events_default.eventHandler(validate)));
|
|
6519
|
+
utils_default.warn(definitions.messages.dump_cache_complete, true);
|
|
6520
|
+
return;
|
|
6521
|
+
}
|
|
6522
|
+
} catch (error) {
|
|
6523
|
+
utils_default.warn(`Failed to load cache: ${error.stack}`);
|
|
6524
|
+
}
|
|
6525
|
+
});
|
|
6526
|
+
}
|
|
6504
6527
|
};
|
|
6505
6528
|
var database_default = Database;
|
|
6506
6529
|
|
|
@@ -6674,41 +6697,6 @@ var Utils = class _Utils {
|
|
|
6674
6697
|
cache.lastWarn = Date.now();
|
|
6675
6698
|
console.warn(`\x1B[33m[ATMOSX-PARSER]\x1B[0m [${(/* @__PURE__ */ new Date()).toLocaleString()}] ${message}`);
|
|
6676
6699
|
}
|
|
6677
|
-
/**
|
|
6678
|
-
* @function loadCollectionCache
|
|
6679
|
-
* @description
|
|
6680
|
-
* Loads cached stanzas from the database, validates them, and processes them through the event parser.
|
|
6681
|
-
* Only processes stanzas that are not marked to be ignored and match the CAP preferences.
|
|
6682
|
-
*
|
|
6683
|
-
* @static
|
|
6684
|
-
* @async
|
|
6685
|
-
* @returns {Promise<void>}
|
|
6686
|
-
*/
|
|
6687
|
-
static loadCollectionCache() {
|
|
6688
|
-
return __async(this, null, function* () {
|
|
6689
|
-
var _a;
|
|
6690
|
-
try {
|
|
6691
|
-
const settings2 = settings;
|
|
6692
|
-
if (settings2.noaa_weather_wire_service_settings.cache.enabled) {
|
|
6693
|
-
const maxRows = (_a = settings2.noaa_weather_wire_service_settings.cache.max_db_cache_size) != null ? _a : 5e3;
|
|
6694
|
-
const rows = yield cache.db.prepare(`SELECT * FROM stanzas ORDER BY rowid DESC LIMIT ?`).all(maxRows);
|
|
6695
|
-
this.warn(definitions.messages.dump_cache.replace(`{count}`, rows.length.toString()), true);
|
|
6696
|
-
const eventsToProcess = rows.map((row) => {
|
|
6697
|
-
return JSON.parse(row.stanza);
|
|
6698
|
-
}).filter((validate) => {
|
|
6699
|
-
if (!validate) return false;
|
|
6700
|
-
const skip = validate.ignore || validate.isCap && !settings2.noaa_weather_wire_service_settings.preferences.cap_only || !validate.isCap && settings2.noaa_weather_wire_service_settings.preferences.cap_only || validate.isCap && !validate.isCapDescription;
|
|
6701
|
-
return !skip;
|
|
6702
|
-
});
|
|
6703
|
-
yield Promise.all(eventsToProcess.map((validate) => events_default.eventHandler(validate)));
|
|
6704
|
-
this.warn(definitions.messages.dump_cache_complete, true);
|
|
6705
|
-
return;
|
|
6706
|
-
}
|
|
6707
|
-
} catch (error) {
|
|
6708
|
-
_Utils.warn(`Failed to load cache: ${error.stack}`);
|
|
6709
|
-
}
|
|
6710
|
-
});
|
|
6711
|
-
}
|
|
6712
6700
|
/**
|
|
6713
6701
|
* @function loadGeoJsonData
|
|
6714
6702
|
* @description
|
|
@@ -7326,6 +7314,7 @@ var eas_default = EAS;
|
|
|
7326
7314
|
// src/index.ts
|
|
7327
7315
|
var Manager = class {
|
|
7328
7316
|
constructor(metadata) {
|
|
7317
|
+
this.sigCatch();
|
|
7329
7318
|
this.start(metadata);
|
|
7330
7319
|
}
|
|
7331
7320
|
/**
|
|
@@ -7468,7 +7457,7 @@ var Manager = class {
|
|
|
7468
7457
|
(() => __async(this, null, function* () {
|
|
7469
7458
|
try {
|
|
7470
7459
|
yield xmpp_default.deploySession();
|
|
7471
|
-
yield
|
|
7460
|
+
yield database_default.loadCollectionCache();
|
|
7472
7461
|
} catch (err) {
|
|
7473
7462
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7474
7463
|
utils_default.warn(`Failed to initialize NWWS services: ${msg}`);
|
|
@@ -7525,6 +7514,25 @@ var Manager = class {
|
|
|
7525
7514
|
}
|
|
7526
7515
|
});
|
|
7527
7516
|
}
|
|
7517
|
+
/**
|
|
7518
|
+
* @function sigCatch
|
|
7519
|
+
* @description
|
|
7520
|
+
* Sets up a global handler for uncaught exceptions, ignoring specific error codes
|
|
7521
|
+
*
|
|
7522
|
+
* @async
|
|
7523
|
+
* @returns void
|
|
7524
|
+
*/
|
|
7525
|
+
sigCatch() {
|
|
7526
|
+
process.on("uncaughtException", (err) => {
|
|
7527
|
+
var _a;
|
|
7528
|
+
const ignored = ["ETIMEDOUT", "ECONNRESET", "EHOSTUNREACH", "STARTTLS_FAILURE"];
|
|
7529
|
+
if (ignored.includes(err == null ? void 0 : err.code)) {
|
|
7530
|
+
utils_default.warn(`XMPP Critical Error: ${(_a = err == null ? void 0 : err.code) != null ? _a : "Unknown error code"}. This may indicate a connection issue. Attempting to continue...`);
|
|
7531
|
+
return;
|
|
7532
|
+
}
|
|
7533
|
+
utils_default.warn(`Uncaught Exception: ${err instanceof Error ? err.stack || err.message : String(err)}`);
|
|
7534
|
+
});
|
|
7535
|
+
}
|
|
7528
7536
|
};
|
|
7529
7537
|
var index_default = Manager;
|
|
7530
7538
|
export {
|
package/package.json
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atmosx/event-product-parser",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.012",
|
|
4
4
|
"description": "NOAA Weather Wire & NWS API Parser - Built for standalone and Project AtmosphericX Integration.",
|
|
5
5
|
"main": "dist/cjs/index.cjs",
|
|
6
6
|
"module": "dist/esm/index.mjs",
|
|
7
|
-
"types": "dist/index.d.ts",
|
|
8
|
-
"files": [
|
|
9
|
-
"dist/**/*",
|
|
10
|
-
"src/**/*"
|
|
11
|
-
],
|
|
12
7
|
"repository": {
|
|
13
8
|
"type": "git",
|
|
14
9
|
"url": "git+https://github.com/AtmosphericX/event-product-parser",
|
package/src/@parsers/text.ts
CHANGED
|
@@ -30,7 +30,7 @@ export class TextParser {
|
|
|
30
30
|
* @param {string[]} [removal=[]]
|
|
31
31
|
* @returns {string | null}
|
|
32
32
|
*/
|
|
33
|
-
public static textProductToString(message: string,value: string,removal: string[] = []): string | null {
|
|
33
|
+
public static textProductToString(message: string, value: string, removal: string[] = []): string | null {
|
|
34
34
|
const lines = message.split('\n');
|
|
35
35
|
for (const line of lines) {
|
|
36
36
|
if (line.includes(value)) {
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import * as loader from '../bootstrap';
|
|
15
15
|
import * as types from '../types';
|
|
16
16
|
import Utils from './utils';
|
|
17
|
+
import EventParser from '../@parsers/events';
|
|
17
18
|
|
|
18
19
|
export class Database {
|
|
19
20
|
|
|
@@ -157,6 +158,43 @@ export class Database {
|
|
|
157
158
|
Utils.warn(`Failed to load database: ${msg}`);
|
|
158
159
|
}
|
|
159
160
|
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* @function loadCollectionCache
|
|
164
|
+
* @description
|
|
165
|
+
* Loads cached stanzas from the database, validates them, and processes them through the event parser.
|
|
166
|
+
* Only processes stanzas that are not marked to be ignored and match the CAP preferences.
|
|
167
|
+
*
|
|
168
|
+
* @static
|
|
169
|
+
* @async
|
|
170
|
+
* @returns {Promise<void>}
|
|
171
|
+
*/
|
|
172
|
+
public static async loadCollectionCache(): Promise<void> {
|
|
173
|
+
try {
|
|
174
|
+
const settings = loader.settings as types.ClientSettingsTypes;
|
|
175
|
+
if (settings.noaa_weather_wire_service_settings.cache.enabled) {
|
|
176
|
+
const maxRows = settings.noaa_weather_wire_service_settings.cache.max_db_cache_size ?? 5000;
|
|
177
|
+
const rows = await loader.cache.db.prepare(`SELECT * FROM stanzas ORDER BY rowid DESC LIMIT ?`)
|
|
178
|
+
.all(maxRows) as { rowid: number; stanza: string }[];
|
|
179
|
+
Utils.warn(loader.definitions.messages.dump_cache.replace(`{count}`, rows.length.toString()), true);
|
|
180
|
+
const eventsToProcess = rows
|
|
181
|
+
.map(row => {return JSON.parse(row.stanza)})
|
|
182
|
+
.filter(validate => {
|
|
183
|
+
if (!validate) return false;
|
|
184
|
+
const skip = validate.ignore ||
|
|
185
|
+
(validate.isCap && !settings.noaa_weather_wire_service_settings.preferences.cap_only) ||
|
|
186
|
+
(!validate.isCap && settings.noaa_weather_wire_service_settings.preferences.cap_only) ||
|
|
187
|
+
(validate.isCap && !validate.isCapDescription);
|
|
188
|
+
return !skip;
|
|
189
|
+
});
|
|
190
|
+
await Promise.all(eventsToProcess.map(validate => EventParser.eventHandler(validate)));
|
|
191
|
+
Utils.warn(loader.definitions.messages.dump_cache_complete, true);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
} catch (error: any) {
|
|
195
|
+
Utils.warn(`Failed to load cache: ${error.stack}`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
160
198
|
}
|
|
161
199
|
|
|
162
200
|
export default Database;
|
package/src/@submodules/utils.ts
CHANGED
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
|
|
14
14
|
import * as loader from '../bootstrap';
|
|
15
15
|
import * as types from '../types';
|
|
16
|
-
import StanzaParser from '../@parsers/stanza';
|
|
17
16
|
import EventParser from '../@parsers/events';
|
|
18
17
|
import Xmpp from './xmpp';
|
|
19
18
|
|
|
@@ -52,43 +51,6 @@ export class Utils {
|
|
|
52
51
|
console.warn(`\x1b[33m[ATMOSX-PARSER]\x1b[0m [${new Date().toLocaleString()}] ${message}`);
|
|
53
52
|
}
|
|
54
53
|
|
|
55
|
-
/**
|
|
56
|
-
* @function loadCollectionCache
|
|
57
|
-
* @description
|
|
58
|
-
* Loads cached stanzas from the database, validates them, and processes them through the event parser.
|
|
59
|
-
* Only processes stanzas that are not marked to be ignored and match the CAP preferences.
|
|
60
|
-
*
|
|
61
|
-
* @static
|
|
62
|
-
* @async
|
|
63
|
-
* @returns {Promise<void>}
|
|
64
|
-
*/
|
|
65
|
-
public static async loadCollectionCache(): Promise<void> {
|
|
66
|
-
try {
|
|
67
|
-
const settings = loader.settings as types.ClientSettingsTypes;
|
|
68
|
-
if (settings.noaa_weather_wire_service_settings.cache.enabled) {
|
|
69
|
-
const maxRows = settings.noaa_weather_wire_service_settings.cache.max_db_cache_size ?? 5000;
|
|
70
|
-
const rows = await loader.cache.db.prepare(`SELECT * FROM stanzas ORDER BY rowid DESC LIMIT ?`)
|
|
71
|
-
.all(maxRows) as { rowid: number; stanza: string }[];
|
|
72
|
-
this.warn(loader.definitions.messages.dump_cache.replace(`{count}`, rows.length.toString()), true);
|
|
73
|
-
const eventsToProcess = rows
|
|
74
|
-
.map(row => {return JSON.parse(row.stanza)})
|
|
75
|
-
.filter(validate => {
|
|
76
|
-
if (!validate) return false;
|
|
77
|
-
const skip = validate.ignore ||
|
|
78
|
-
(validate.isCap && !settings.noaa_weather_wire_service_settings.preferences.cap_only) ||
|
|
79
|
-
(!validate.isCap && settings.noaa_weather_wire_service_settings.preferences.cap_only) ||
|
|
80
|
-
(validate.isCap && !validate.isCapDescription);
|
|
81
|
-
return !skip;
|
|
82
|
-
});
|
|
83
|
-
await Promise.all(eventsToProcess.map(validate => EventParser.eventHandler(validate)));
|
|
84
|
-
this.warn(loader.definitions.messages.dump_cache_complete, true);
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
} catch (error: any) {
|
|
88
|
-
Utils.warn(`Failed to load cache: ${error.stack}`);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
54
|
/**
|
|
93
55
|
* @function loadGeoJsonData
|
|
94
56
|
* @description
|
package/src/bootstrap.ts
CHANGED
|
@@ -179,12 +179,4 @@ export const definitions = {
|
|
|
179
179
|
dump_cache: `Found {count} cached events and will begin dumping them shortly. This may take a while depending on the number of cached events.`,
|
|
180
180
|
dump_cache_complete: `Completed dumping all cached alert files.`,
|
|
181
181
|
}
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
process.on('uncaughtException', (err: any) => {
|
|
186
|
-
if (err?.code === 'ETIMEDOUT') { return; }
|
|
187
|
-
if (err?.code === 'ECONNRESET') { return; }
|
|
188
|
-
if (err?.code === 'EHOSTUNREACH') { return; }
|
|
189
|
-
throw err;
|
|
190
|
-
})
|
|
182
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -27,7 +27,10 @@ import UGCParser from './@parsers/ugc';
|
|
|
27
27
|
export class Manager {
|
|
28
28
|
isNoaaWeatherWireService: boolean
|
|
29
29
|
job: any
|
|
30
|
-
constructor(metadata: types.ClientSettingsTypes) {
|
|
30
|
+
constructor(metadata: types.ClientSettingsTypes) {
|
|
31
|
+
this.sigCatch();
|
|
32
|
+
this.start(metadata)
|
|
33
|
+
}
|
|
31
34
|
|
|
32
35
|
/**
|
|
33
36
|
* @function setDisplayName
|
|
@@ -168,7 +171,7 @@ export class Manager {
|
|
|
168
171
|
(async () => {
|
|
169
172
|
try {
|
|
170
173
|
await Xmpp.deploySession();
|
|
171
|
-
await
|
|
174
|
+
await Database.loadCollectionCache();
|
|
172
175
|
} catch (err: unknown) {
|
|
173
176
|
const msg = err instanceof Error ? err.message : String(err);
|
|
174
177
|
Utils.warn(`Failed to initialize NWWS services: ${msg}`);
|
|
@@ -211,6 +214,25 @@ export class Manager {
|
|
|
211
214
|
this.isNoaaWeatherWireService = false;
|
|
212
215
|
}
|
|
213
216
|
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* @function sigCatch
|
|
220
|
+
* @description
|
|
221
|
+
* Sets up a global handler for uncaught exceptions, ignoring specific error codes
|
|
222
|
+
*
|
|
223
|
+
* @async
|
|
224
|
+
* @returns void
|
|
225
|
+
*/
|
|
226
|
+
private sigCatch() {
|
|
227
|
+
process.on('uncaughtException', (err: any) => {
|
|
228
|
+
const ignored = ['ETIMEDOUT', 'ECONNRESET', 'EHOSTUNREACH', 'STARTTLS_FAILURE'];
|
|
229
|
+
if (ignored.includes(err?.code)) {
|
|
230
|
+
Utils.warn(`XMPP Critical Error: ${err?.code ?? 'Unknown error code'}. This may indicate a connection issue. Attempting to continue...`);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
Utils.warn(`Uncaught Exception: ${err instanceof Error ? err.stack || err.message : String(err)}`);
|
|
234
|
+
})
|
|
235
|
+
}
|
|
214
236
|
}
|
|
215
237
|
|
|
216
238
|
export default Manager;
|
package/test.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const { AlertManager } = require(`@atmosx/event-product-parser`)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
const parser = new AlertManager({
|
|
5
|
+
database: `shapefile-manager.db`,
|
|
6
|
+
is_wire: true,
|
|
7
|
+
journal: true,
|
|
8
|
+
noaa_weather_wire_service_settings: {
|
|
9
|
+
reconnection_settings: {
|
|
10
|
+
enabled: true,
|
|
11
|
+
interval: 60,
|
|
12
|
+
},
|
|
13
|
+
credentials: {
|
|
14
|
+
username: `username123`,
|
|
15
|
+
password: `password123`,
|
|
16
|
+
nickname: "AtmosphericX Standalone Parser",
|
|
17
|
+
},
|
|
18
|
+
cache: {
|
|
19
|
+
enabled: false,
|
|
20
|
+
max_db_history: 5000,
|
|
21
|
+
max_db_cache_size: 1000,
|
|
22
|
+
},
|
|
23
|
+
preferences: {
|
|
24
|
+
disable_ugc: false,
|
|
25
|
+
disable_vtec: false,
|
|
26
|
+
disable_text: false,
|
|
27
|
+
cap_only: false,
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
national_weather_service_settings: {
|
|
31
|
+
interval: 15,
|
|
32
|
+
endpoint: `https://api.weather.gov/alerts/active`,
|
|
33
|
+
},
|
|
34
|
+
global_settings: {
|
|
35
|
+
parent_events_only: true,
|
|
36
|
+
better_event_parsing: true,
|
|
37
|
+
ignore_geometry_parsing: false,
|
|
38
|
+
shapefile_coordinates: true,
|
|
39
|
+
filtering: {
|
|
40
|
+
events: [`Severe Thunderstorm Warning`],
|
|
41
|
+
filtered_icao: ["PAFC"],
|
|
42
|
+
ignored_icao: [],
|
|
43
|
+
ignored_events: [`Xx`, `Test Message`],
|
|
44
|
+
ugc_filter: [],
|
|
45
|
+
state_filter: [],
|
|
46
|
+
check_expired: true,
|
|
47
|
+
ignore_test_products: true,
|
|
48
|
+
},
|
|
49
|
+
eas_settings: {
|
|
50
|
+
directory: null,
|
|
51
|
+
intro_wav: null,
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
parser.on(`onEvents`, (alerts) => {
|
|
57
|
+
for (const alert of alerts) {
|
|
58
|
+
if (alert.geometry != null) {
|
|
59
|
+
console.log(`[${ new Date(alert.properties.issued).toLocaleString()}] ${alert.properties.event} for ${alert.properties.locations} (ID: ${alert.properties.details.tracking})`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES6",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "Node",
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"declaration": true,
|
|
8
|
+
"declarationDir": "dist/types",
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"allowSyntheticDefaultImports": true,
|
|
11
|
+
"skipLibCheck": true
|
|
12
|
+
},
|
|
13
|
+
"include": ["src"]
|
|
14
|
+
}
|
package/tsup.config.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineConfig } from 'tsup'
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
entry: ['src/index.ts'],
|
|
5
|
+
outDir: 'dist',
|
|
6
|
+
format: ['esm', 'cjs'],
|
|
7
|
+
noExternal: ["@xmpp/client", "@xmpp/sasl", "@xmpp/sasl-plain"],
|
|
8
|
+
clean: true,
|
|
9
|
+
outExtension({ format }) {
|
|
10
|
+
return { js: format === 'esm' ? '.mjs' : '.cjs' }
|
|
11
|
+
},
|
|
12
|
+
esbuildOptions(options, context) {
|
|
13
|
+
options.outdir = `dist/${context.format}`
|
|
14
|
+
},
|
|
15
|
+
})
|