@zhin.js/plugin-earthquake 0.0.0-stage → 0.1.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.
- package/README.md +115 -2
- package/commands//345/234/260/351/234/207/index.js +25 -0
- package/commands//345/234/260/351/234/207/manage-permission.js +20 -0
- package/commands//345/234/260/351/234/207//350/256/242/351/230/205/index.js +51 -0
- package/commands//345/234/260/351/234/207//350/256/242/351/230/205/347/212/266/346/200/201/index.js +41 -0
- package/commands//345/234/260/351/234/207//351/200/200/350/256/242/index.js +19 -0
- package/lib/config.d.ts +58 -0
- package/lib/config.d.ts.map +1 -0
- package/lib/config.js +138 -0
- package/lib/config.js.map +1 -0
- package/lib/conversation.d.ts +14 -0
- package/lib/conversation.d.ts.map +1 -0
- package/lib/conversation.js +54 -0
- package/lib/conversation.js.map +1 -0
- package/lib/dedupe.d.ts +60 -0
- package/lib/dedupe.d.ts.map +1 -0
- package/lib/dedupe.js +122 -0
- package/lib/dedupe.js.map +1 -0
- package/lib/filters.d.ts +26 -0
- package/lib/filters.d.ts.map +1 -0
- package/lib/filters.js +47 -0
- package/lib/filters.js.map +1 -0
- package/lib/format.d.ts +14 -0
- package/lib/format.d.ts.map +1 -0
- package/lib/format.js +66 -0
- package/lib/format.js.map +1 -0
- package/lib/http.d.ts +71 -0
- package/lib/http.d.ts.map +1 -0
- package/lib/http.js +138 -0
- package/lib/http.js.map +1 -0
- package/lib/index.d.ts +22 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +14 -0
- package/lib/index.js.map +1 -0
- package/lib/query-text.d.ts +11 -0
- package/lib/query-text.d.ts.map +1 -0
- package/lib/query-text.js +37 -0
- package/lib/query-text.js.map +1 -0
- package/lib/rate-limit.d.ts +17 -0
- package/lib/rate-limit.d.ts.map +1 -0
- package/lib/rate-limit.js +41 -0
- package/lib/rate-limit.js.map +1 -0
- package/lib/runtime.d.ts +4 -0
- package/lib/runtime.d.ts.map +1 -0
- package/lib/runtime.js +4 -0
- package/lib/runtime.js.map +1 -0
- package/lib/service.d.ts +77 -0
- package/lib/service.d.ts.map +1 -0
- package/lib/service.js +229 -0
- package/lib/service.js.map +1 -0
- package/lib/sources/cenc.d.ts +37 -0
- package/lib/sources/cenc.d.ts.map +1 -0
- package/lib/sources/cenc.js +110 -0
- package/lib/sources/cenc.js.map +1 -0
- package/lib/sources/feed-source.d.ts +51 -0
- package/lib/sources/feed-source.d.ts.map +1 -0
- package/lib/sources/feed-source.js +115 -0
- package/lib/sources/feed-source.js.map +1 -0
- package/lib/sources/parse-helpers.d.ts +10 -0
- package/lib/sources/parse-helpers.d.ts.map +1 -0
- package/lib/sources/parse-helpers.js +36 -0
- package/lib/sources/parse-helpers.js.map +1 -0
- package/lib/sources/usgs.d.ts +14 -0
- package/lib/sources/usgs.d.ts.map +1 -0
- package/lib/sources/usgs.js +81 -0
- package/lib/sources/usgs.js.map +1 -0
- package/lib/subscribe-args.d.ts +9 -0
- package/lib/subscribe-args.d.ts.map +1 -0
- package/lib/subscribe-args.js +40 -0
- package/lib/subscribe-args.js.map +1 -0
- package/lib/subscriptions.d.ts +36 -0
- package/lib/subscriptions.d.ts.map +1 -0
- package/lib/subscriptions.js +122 -0
- package/lib/subscriptions.js.map +1 -0
- package/lib/types.d.ts +36 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +2 -0
- package/lib/types.js.map +1 -0
- package/package.json +105 -3
- package/plugin.js +71 -0
- package/schema.json +153 -0
- package/skills/earthquake/SKILL.md +37 -0
- package/skills/earthquake/tools/recent_earthquakes/index.js +19 -0
package/lib/dedupe.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
export const DEFAULT_DEDUPE_TOLERANCE = Object.freeze({
|
|
2
|
+
timeToleranceMs: 60_000,
|
|
3
|
+
distanceToleranceKm: 100,
|
|
4
|
+
magnitudeTolerance: 1.0,
|
|
5
|
+
sameSourceTimeToleranceMs: 5_000,
|
|
6
|
+
sameSourceDistanceKm: 50,
|
|
7
|
+
});
|
|
8
|
+
const EARTH_RADIUS_KM = 6_371;
|
|
9
|
+
function toRadians(degrees) {
|
|
10
|
+
return (degrees * Math.PI) / 180;
|
|
11
|
+
}
|
|
12
|
+
/** Great-circle distance (haversine) in kilometres. */
|
|
13
|
+
export function distanceKm(a, b) {
|
|
14
|
+
const deltaLatitude = toRadians(b.latitude - a.latitude);
|
|
15
|
+
const deltaLongitude = toRadians(b.longitude - a.longitude);
|
|
16
|
+
const sinLatitude = Math.sin(deltaLatitude / 2);
|
|
17
|
+
const sinLongitude = Math.sin(deltaLongitude / 2);
|
|
18
|
+
const h = sinLatitude * sinLatitude
|
|
19
|
+
+ Math.cos(toRadians(a.latitude)) * Math.cos(toRadians(b.latitude)) * sinLongitude * sinLongitude;
|
|
20
|
+
return 2 * EARTH_RADIUS_KM * Math.asin(Math.min(1, Math.sqrt(h)));
|
|
21
|
+
}
|
|
22
|
+
/** True when two reports describe the same physical event. */
|
|
23
|
+
export function isSameEvent(a, b, tolerance = DEFAULT_DEDUPE_TOLERANCE) {
|
|
24
|
+
if (a.source === b.source) {
|
|
25
|
+
if (a.id === b.id)
|
|
26
|
+
return true;
|
|
27
|
+
if (Math.abs(a.time - b.time) > tolerance.sameSourceTimeToleranceMs)
|
|
28
|
+
return false;
|
|
29
|
+
return distanceKm(a, b) <= tolerance.sameSourceDistanceKm;
|
|
30
|
+
}
|
|
31
|
+
if (Math.abs(a.time - b.time) > tolerance.timeToleranceMs)
|
|
32
|
+
return false;
|
|
33
|
+
if (Math.abs(a.magnitude - b.magnitude) > tolerance.magnitudeTolerance)
|
|
34
|
+
return false;
|
|
35
|
+
return distanceKm(a, b) <= tolerance.distanceToleranceKm;
|
|
36
|
+
}
|
|
37
|
+
function sourceRank(source, preference) {
|
|
38
|
+
const index = preference.indexOf(source);
|
|
39
|
+
return index === -1 ? preference.length : index;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Merges reports into events. The primary report of each event is the one from
|
|
43
|
+
* the most preferred source. Output is sorted newest first.
|
|
44
|
+
*/
|
|
45
|
+
export function mergeReports(reports, preference, tolerance = DEFAULT_DEDUPE_TOLERANCE) {
|
|
46
|
+
// Visit preferred sources first so they become primaries.
|
|
47
|
+
const ordered = [...reports].sort((left, right) => {
|
|
48
|
+
const byRank = sourceRank(left.source, preference) - sourceRank(right.source, preference);
|
|
49
|
+
if (byRank !== 0)
|
|
50
|
+
return byRank;
|
|
51
|
+
return right.time - left.time;
|
|
52
|
+
});
|
|
53
|
+
const groups = [];
|
|
54
|
+
for (const report of ordered) {
|
|
55
|
+
let placed = false;
|
|
56
|
+
for (const group of groups) {
|
|
57
|
+
const sameSourceMember = group.find((member) => member.source === report.source);
|
|
58
|
+
if (sameSourceMember) {
|
|
59
|
+
// One report per source per event: a match here is a duplicate, otherwise keep looking.
|
|
60
|
+
if (isSameEvent(sameSourceMember, report, tolerance)) {
|
|
61
|
+
placed = true;
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (isSameEvent(group[0], report, tolerance)) {
|
|
67
|
+
group.push(report);
|
|
68
|
+
placed = true;
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (!placed) {
|
|
73
|
+
groups.push([report]);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const events = groups.map((group) => ({ primary: group[0], reports: Object.freeze([...group]) }));
|
|
77
|
+
events.sort((left, right) => right.primary.time - left.primary.time);
|
|
78
|
+
return events;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Remembers events that were already announced so the poller pushes each
|
|
82
|
+
* physical event once, even when the second source reports it minutes later.
|
|
83
|
+
* Bounded by count and age.
|
|
84
|
+
*/
|
|
85
|
+
export class SeenEventLog {
|
|
86
|
+
#entries = [];
|
|
87
|
+
#maxEntries;
|
|
88
|
+
#maxAgeMs;
|
|
89
|
+
#tolerance;
|
|
90
|
+
constructor(options = {}) {
|
|
91
|
+
this.#maxEntries = options.maxEntries ?? 2_000;
|
|
92
|
+
this.#maxAgeMs = options.maxAgeMs ?? 48 * 60 * 60_000;
|
|
93
|
+
this.#tolerance = options.tolerance ?? DEFAULT_DEDUPE_TOLERANCE;
|
|
94
|
+
}
|
|
95
|
+
get size() {
|
|
96
|
+
return this.#entries.length;
|
|
97
|
+
}
|
|
98
|
+
has(event) {
|
|
99
|
+
return event.reports.some((report) => this.#entries.some((seen) => isSameEvent(seen, report, this.#tolerance)));
|
|
100
|
+
}
|
|
101
|
+
/** Records every report of the event so later reports from either source match. */
|
|
102
|
+
add(event) {
|
|
103
|
+
for (const report of event.reports) {
|
|
104
|
+
const known = this.#entries.some((seen) => seen.source === report.source && seen.id === report.id);
|
|
105
|
+
if (!known)
|
|
106
|
+
this.#entries.push(report);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
prune(now) {
|
|
110
|
+
const oldest = now - this.#maxAgeMs;
|
|
111
|
+
for (let index = this.#entries.length - 1; index >= 0; index -= 1) {
|
|
112
|
+
if (this.#entries[index].time < oldest)
|
|
113
|
+
this.#entries.splice(index, 1);
|
|
114
|
+
}
|
|
115
|
+
const overflow = this.#entries.length - this.#maxEntries;
|
|
116
|
+
if (overflow > 0) {
|
|
117
|
+
this.#entries.sort((left, right) => left.time - right.time);
|
|
118
|
+
this.#entries.splice(0, overflow);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=dedupe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dedupe.js","sourceRoot":"","sources":["../src/dedupe.ts"],"names":[],"mappings":"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"}
|
package/lib/filters.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Magnitude and region filters shared by queries and subscriptions.
|
|
3
|
+
*
|
|
4
|
+
* A region is matched against every merged report's location text, so both the
|
|
5
|
+
* Chinese CENC wording ("四川宜宾市珙县") and the English USGS wording
|
|
6
|
+
* ("Hualien City, Taiwan") work. Matching is a case-insensitive substring test.
|
|
7
|
+
*
|
|
8
|
+
* `中国` / `国内` is a preset: CENC locations that start with a province-level
|
|
9
|
+
* name, plus USGS places mentioning China or Taiwan.
|
|
10
|
+
*/
|
|
11
|
+
import type { EarthquakeEvent } from './types.js';
|
|
12
|
+
export declare const CHINA_REGION_ALIASES: readonly string[];
|
|
13
|
+
export declare function normalizeRegion(region: string): string;
|
|
14
|
+
/** True when any report of the event matches any region; an empty list matches everything. */
|
|
15
|
+
export declare function matchesRegions(event: EarthquakeEvent, regions: readonly string[]): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Uses the largest reported magnitude so a quake reported as M5.0 by one source
|
|
18
|
+
* and M4.8 by another still reaches a "5 级以上" subscriber.
|
|
19
|
+
*/
|
|
20
|
+
export declare function eventMagnitude(event: EarthquakeEvent): number;
|
|
21
|
+
export interface EventFilter {
|
|
22
|
+
readonly minMagnitude: number;
|
|
23
|
+
readonly regions: readonly string[];
|
|
24
|
+
}
|
|
25
|
+
export declare function matchesFilter(event: EarthquakeEvent, filter: EventFilter): boolean;
|
|
26
|
+
//# sourceMappingURL=filters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filters.d.ts","sourceRoot":"","sources":["../src/filters.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,eAAe,EAAoB,MAAM,YAAY,CAAC;AAEpE,eAAO,MAAM,oBAAoB,mBAAuC,CAAC;AAazE,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEtD;AAkBD,8FAA8F;AAC9F,wBAAgB,cAAc,CAAC,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAI1F;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAE7D;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAGlF"}
|
package/lib/filters.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export const CHINA_REGION_ALIASES = Object.freeze(['中国', '国内', 'china']);
|
|
2
|
+
/** Province-level divisions, in the short form CENC uses as a location prefix. */
|
|
3
|
+
const CHINA_PROVINCE_PREFIXES = Object.freeze([
|
|
4
|
+
'北京', '天津', '河北', '山西', '内蒙古', '辽宁', '吉林', '黑龙江',
|
|
5
|
+
'上海', '江苏', '浙江', '安徽', '福建', '江西', '山东', '河南',
|
|
6
|
+
'湖北', '湖南', '广东', '广西', '海南', '重庆', '四川', '贵州',
|
|
7
|
+
'云南', '西藏', '陕西', '甘肃', '青海', '宁夏', '新疆', '台湾',
|
|
8
|
+
'香港', '澳门',
|
|
9
|
+
]);
|
|
10
|
+
const CHINA_ENGLISH_MARKERS = Object.freeze(['china', 'taiwan']);
|
|
11
|
+
export function normalizeRegion(region) {
|
|
12
|
+
return region.trim().toLowerCase();
|
|
13
|
+
}
|
|
14
|
+
function isChinaReport(report) {
|
|
15
|
+
const location = report.location.trim();
|
|
16
|
+
if (CHINA_PROVINCE_PREFIXES.some((prefix) => location.startsWith(prefix))) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
const lower = location.toLowerCase();
|
|
20
|
+
return CHINA_ENGLISH_MARKERS.some((marker) => lower.includes(marker));
|
|
21
|
+
}
|
|
22
|
+
function reportMatchesRegion(report, normalizedRegion) {
|
|
23
|
+
if (CHINA_REGION_ALIASES.includes(normalizedRegion)) {
|
|
24
|
+
return isChinaReport(report);
|
|
25
|
+
}
|
|
26
|
+
return report.location.toLowerCase().includes(normalizedRegion);
|
|
27
|
+
}
|
|
28
|
+
/** True when any report of the event matches any region; an empty list matches everything. */
|
|
29
|
+
export function matchesRegions(event, regions) {
|
|
30
|
+
const normalized = regions.map(normalizeRegion).filter((region) => region.length > 0);
|
|
31
|
+
if (normalized.length === 0)
|
|
32
|
+
return true;
|
|
33
|
+
return event.reports.some((report) => normalized.some((region) => reportMatchesRegion(report, region)));
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Uses the largest reported magnitude so a quake reported as M5.0 by one source
|
|
37
|
+
* and M4.8 by another still reaches a "5 级以上" subscriber.
|
|
38
|
+
*/
|
|
39
|
+
export function eventMagnitude(event) {
|
|
40
|
+
return Math.max(...event.reports.map((report) => report.magnitude));
|
|
41
|
+
}
|
|
42
|
+
export function matchesFilter(event, filter) {
|
|
43
|
+
if (eventMagnitude(event) < filter.minMagnitude)
|
|
44
|
+
return false;
|
|
45
|
+
return matchesRegions(event, filter.regions);
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=filters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filters.js","sourceRoot":"","sources":["../src/filters.ts"],"names":[],"mappings":"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"}
|
package/lib/format.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { EarthquakeEvent, SourceOutcome } from './types.js';
|
|
2
|
+
export declare function sourceLabel(source: string): string;
|
|
3
|
+
export declare function formatEventTime(time: number, timezone: string): string;
|
|
4
|
+
/** One event in 2–3 lines: headline, time/depth, sources (+ link when available). */
|
|
5
|
+
export declare function formatEvent(event: EarthquakeEvent, timezone: string): string;
|
|
6
|
+
export declare function formatSourceWarnings(outcomes: readonly SourceOutcome[]): string | undefined;
|
|
7
|
+
export declare function formatEventList(options: {
|
|
8
|
+
readonly title: string;
|
|
9
|
+
readonly events: readonly EarthquakeEvent[];
|
|
10
|
+
readonly timezone: string;
|
|
11
|
+
readonly hiddenCount?: number;
|
|
12
|
+
readonly footer?: readonly (string | undefined)[];
|
|
13
|
+
}): string;
|
|
14
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAOjE,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAatE;AAMD,qFAAqF;AACrF,wBAAgB,WAAW,CAAC,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAe5E;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,GAAG,MAAM,GAAG,SAAS,CAQ3F;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAAC;CACnD,GAAG,MAAM,CAUT"}
|
package/lib/format.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/** Plain-text rendering, safe for every adapter (no markup, no platform segments). */
|
|
2
|
+
import { eventMagnitude } from './filters.js';
|
|
3
|
+
const SOURCE_LABELS = Object.freeze({
|
|
4
|
+
cenc: 'CENC',
|
|
5
|
+
usgs: 'USGS',
|
|
6
|
+
});
|
|
7
|
+
export function sourceLabel(source) {
|
|
8
|
+
return SOURCE_LABELS[source] ?? source.toUpperCase();
|
|
9
|
+
}
|
|
10
|
+
export function formatEventTime(time, timezone) {
|
|
11
|
+
const formatter = new Intl.DateTimeFormat('zh-CN', {
|
|
12
|
+
timeZone: timezone,
|
|
13
|
+
year: 'numeric',
|
|
14
|
+
month: '2-digit',
|
|
15
|
+
day: '2-digit',
|
|
16
|
+
hour: '2-digit',
|
|
17
|
+
minute: '2-digit',
|
|
18
|
+
second: '2-digit',
|
|
19
|
+
hour12: false,
|
|
20
|
+
});
|
|
21
|
+
const parts = Object.fromEntries(formatter.formatToParts(new Date(time)).map((part) => [part.type, part.value]));
|
|
22
|
+
return `${parts.year}-${parts.month}-${parts.day} ${parts.hour}:${parts.minute}:${parts.second}`;
|
|
23
|
+
}
|
|
24
|
+
function formatMagnitude(value) {
|
|
25
|
+
return value.toFixed(1);
|
|
26
|
+
}
|
|
27
|
+
/** One event in 2–3 lines: headline, time/depth, sources (+ link when available). */
|
|
28
|
+
export function formatEvent(event, timezone) {
|
|
29
|
+
const primary = event.primary;
|
|
30
|
+
const headline = `M${formatMagnitude(eventMagnitude(event))} ${primary.location}`;
|
|
31
|
+
const details = [`${formatEventTime(primary.time, timezone)} (${timezone})`];
|
|
32
|
+
if (primary.depthKm !== undefined) {
|
|
33
|
+
details.push(`深度 ${Math.round(primary.depthKm)} km`);
|
|
34
|
+
}
|
|
35
|
+
details.push(`${primary.latitude.toFixed(2)}, ${primary.longitude.toFixed(2)}`);
|
|
36
|
+
const sources = event.reports.map((report) => `${sourceLabel(report.source)} M${formatMagnitude(report.magnitude)}`);
|
|
37
|
+
const lines = [headline, ` ${details.join(' · ')}`, ` 来源:${sources.join(' / ')}`];
|
|
38
|
+
const link = event.reports.find((report) => report.url)?.url;
|
|
39
|
+
if (link) {
|
|
40
|
+
lines.push(` ${link}`);
|
|
41
|
+
}
|
|
42
|
+
return lines.join('\n');
|
|
43
|
+
}
|
|
44
|
+
export function formatSourceWarnings(outcomes) {
|
|
45
|
+
const failed = outcomes.filter((outcome) => !outcome.ok);
|
|
46
|
+
if (failed.length === 0)
|
|
47
|
+
return undefined;
|
|
48
|
+
const parts = failed.map((outcome) => {
|
|
49
|
+
const reason = outcome.skipped ? '冷却中' : outcome.error ?? '失败';
|
|
50
|
+
return `${sourceLabel(outcome.source)}(${reason})`;
|
|
51
|
+
});
|
|
52
|
+
return `⚠️ 部分数据源不可用:${parts.join(';')}`;
|
|
53
|
+
}
|
|
54
|
+
export function formatEventList(options) {
|
|
55
|
+
const blocks = options.events.map((event, index) => `${index + 1}. ${formatEvent(event, options.timezone)}`);
|
|
56
|
+
const lines = [options.title, ...blocks];
|
|
57
|
+
if (options.hiddenCount && options.hiddenCount > 0) {
|
|
58
|
+
lines.push(`……另有 ${options.hiddenCount} 条未展示`);
|
|
59
|
+
}
|
|
60
|
+
for (const extra of options.footer ?? []) {
|
|
61
|
+
if (extra)
|
|
62
|
+
lines.push(extra);
|
|
63
|
+
}
|
|
64
|
+
return lines.join('\n');
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"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"}
|
package/lib/http.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resilient HTTP GET for public, key-less earthquake feeds.
|
|
3
|
+
*
|
|
4
|
+
* - Every attempt has its own timeout (AbortSignal.timeout).
|
|
5
|
+
* - Transient failures are retried with exponential backoff and jitter:
|
|
6
|
+
* network errors, timeouts, 408, 425, 429 and 5xx.
|
|
7
|
+
* - `Retry-After` (seconds or HTTP date) is honoured, capped by `maxDelayMs`.
|
|
8
|
+
* A server asking for a longer pause stops the retry loop and surfaces
|
|
9
|
+
* `retryAfterMs` so the caller can cool the source down instead of waiting.
|
|
10
|
+
* - Other 4xx responses fail immediately: retrying will not fix them.
|
|
11
|
+
* - Conditional requests (ETag / Last-Modified) avoid re-downloading unchanged feeds.
|
|
12
|
+
*/
|
|
13
|
+
export type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
|
|
14
|
+
export type SleepLike = (ms: number) => Promise<void>;
|
|
15
|
+
export interface RetryPolicy {
|
|
16
|
+
/** Per-attempt timeout in milliseconds. */
|
|
17
|
+
readonly timeoutMs: number;
|
|
18
|
+
/** Retries after the first attempt. */
|
|
19
|
+
readonly maxRetries: number;
|
|
20
|
+
/** First backoff step in milliseconds; doubles each retry. */
|
|
21
|
+
readonly baseDelayMs: number;
|
|
22
|
+
/** Ceiling for one backoff step and for honouring Retry-After. */
|
|
23
|
+
readonly maxDelayMs: number;
|
|
24
|
+
}
|
|
25
|
+
export interface HttpDependencies {
|
|
26
|
+
readonly fetch?: FetchLike;
|
|
27
|
+
readonly sleep?: SleepLike;
|
|
28
|
+
/** Returns a number in [0, 1); injected for deterministic tests. */
|
|
29
|
+
readonly random?: () => number;
|
|
30
|
+
/** Wall clock used to evaluate HTTP-date Retry-After values. */
|
|
31
|
+
readonly now?: () => number;
|
|
32
|
+
}
|
|
33
|
+
export declare class HttpRequestError extends Error {
|
|
34
|
+
readonly status?: number;
|
|
35
|
+
readonly retryable: boolean;
|
|
36
|
+
/** Server-requested pause that exceeded the in-request retry budget. */
|
|
37
|
+
readonly retryAfterMs?: number;
|
|
38
|
+
constructor(message: string, options: {
|
|
39
|
+
status?: number;
|
|
40
|
+
retryable: boolean;
|
|
41
|
+
retryAfterMs?: number;
|
|
42
|
+
cause?: unknown;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
export declare function isRetryableStatus(status: number): boolean;
|
|
46
|
+
/** Parses `Retry-After` as delta-seconds or an HTTP date. Returns undefined when absent or invalid. */
|
|
47
|
+
export declare function parseRetryAfter(value: string | null, now: number): number | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* Exponential backoff with "equal jitter": half of the step is fixed, half is random.
|
|
50
|
+
* This keeps a minimum spacing between retries while still spreading clients apart.
|
|
51
|
+
*/
|
|
52
|
+
export declare function computeBackoffDelay(retryIndex: number, policy: RetryPolicy, random: () => number): number;
|
|
53
|
+
export interface ConditionalValidators {
|
|
54
|
+
readonly etag?: string;
|
|
55
|
+
readonly lastModified?: string;
|
|
56
|
+
}
|
|
57
|
+
export interface GetResult {
|
|
58
|
+
/** 304 Not Modified: the caller should reuse its cached body. */
|
|
59
|
+
readonly notModified: boolean;
|
|
60
|
+
readonly body: string;
|
|
61
|
+
readonly validators: ConditionalValidators;
|
|
62
|
+
/** Attempts used, including the successful one. */
|
|
63
|
+
readonly attempts: number;
|
|
64
|
+
}
|
|
65
|
+
/** GET a URL as text with timeout, retries and optional conditional validators. */
|
|
66
|
+
export declare function getTextWithRetry(url: string, policy: RetryPolicy, options?: {
|
|
67
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
68
|
+
readonly validators?: ConditionalValidators;
|
|
69
|
+
readonly deps?: HttpDependencies;
|
|
70
|
+
}): Promise<GetResult>;
|
|
71
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AACjF,MAAM,MAAM,SAAS,GAAG,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAEtD,MAAM,WAAW,WAAW;IAC1B,2CAA2C;IAC3C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,uCAAuC;IACvC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,8DAA8D;IAC9D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,kEAAkE;IAClE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC;IAC3B,oEAAoE;IACpE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;IAC/B,gEAAgE;IAChE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B;AAED,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,wEAAwE;IACxE,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;gBAEnB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE;QACpC,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,OAAO,CAAC;QACnB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB;CAOF;AAID,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED,uGAAuG;AACvG,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAUrF;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,MAAM,GAAG,MAAM,CAKzG;AAgBD,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,SAAS;IACxB,iEAAiE;IACjE,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAC;IAC3C,mDAAmD;IACnD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,mFAAmF;AACnF,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,WAAW,EACnB,OAAO,GAAE;IACP,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACpD,QAAQ,CAAC,UAAU,CAAC,EAAE,qBAAqB,CAAC;IAC5C,QAAQ,CAAC,IAAI,CAAC,EAAE,gBAAgB,CAAC;CAC7B,GACL,OAAO,CAAC,SAAS,CAAC,CAyEpB"}
|
package/lib/http.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resilient HTTP GET for public, key-less earthquake feeds.
|
|
3
|
+
*
|
|
4
|
+
* - Every attempt has its own timeout (AbortSignal.timeout).
|
|
5
|
+
* - Transient failures are retried with exponential backoff and jitter:
|
|
6
|
+
* network errors, timeouts, 408, 425, 429 and 5xx.
|
|
7
|
+
* - `Retry-After` (seconds or HTTP date) is honoured, capped by `maxDelayMs`.
|
|
8
|
+
* A server asking for a longer pause stops the retry loop and surfaces
|
|
9
|
+
* `retryAfterMs` so the caller can cool the source down instead of waiting.
|
|
10
|
+
* - Other 4xx responses fail immediately: retrying will not fix them.
|
|
11
|
+
* - Conditional requests (ETag / Last-Modified) avoid re-downloading unchanged feeds.
|
|
12
|
+
*/
|
|
13
|
+
export class HttpRequestError extends Error {
|
|
14
|
+
status;
|
|
15
|
+
retryable;
|
|
16
|
+
/** Server-requested pause that exceeded the in-request retry budget. */
|
|
17
|
+
retryAfterMs;
|
|
18
|
+
constructor(message, options) {
|
|
19
|
+
super(message, { cause: options.cause });
|
|
20
|
+
this.name = 'HttpRequestError';
|
|
21
|
+
this.status = options.status;
|
|
22
|
+
this.retryable = options.retryable;
|
|
23
|
+
this.retryAfterMs = options.retryAfterMs;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const RETRYABLE_STATUS = new Set([408, 425, 429, 500, 502, 503, 504]);
|
|
27
|
+
export function isRetryableStatus(status) {
|
|
28
|
+
return RETRYABLE_STATUS.has(status);
|
|
29
|
+
}
|
|
30
|
+
/** Parses `Retry-After` as delta-seconds or an HTTP date. Returns undefined when absent or invalid. */
|
|
31
|
+
export function parseRetryAfter(value, now) {
|
|
32
|
+
if (value === null)
|
|
33
|
+
return undefined;
|
|
34
|
+
const trimmed = value.trim();
|
|
35
|
+
if (!trimmed)
|
|
36
|
+
return undefined;
|
|
37
|
+
if (/^\d+$/u.test(trimmed)) {
|
|
38
|
+
return Number(trimmed) * 1_000;
|
|
39
|
+
}
|
|
40
|
+
const date = Date.parse(trimmed);
|
|
41
|
+
if (Number.isNaN(date))
|
|
42
|
+
return undefined;
|
|
43
|
+
return Math.max(0, date - now);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Exponential backoff with "equal jitter": half of the step is fixed, half is random.
|
|
47
|
+
* This keeps a minimum spacing between retries while still spreading clients apart.
|
|
48
|
+
*/
|
|
49
|
+
export function computeBackoffDelay(retryIndex, policy, random) {
|
|
50
|
+
const exponential = policy.baseDelayMs * 2 ** retryIndex;
|
|
51
|
+
const step = Math.min(policy.maxDelayMs, exponential);
|
|
52
|
+
const half = step / 2;
|
|
53
|
+
return Math.round(half + random() * half);
|
|
54
|
+
}
|
|
55
|
+
const defaultSleep = (ms) => new Promise((resolve) => {
|
|
56
|
+
setTimeout(resolve, ms);
|
|
57
|
+
});
|
|
58
|
+
function describeFailure(error, timeoutMs) {
|
|
59
|
+
if (error instanceof Error) {
|
|
60
|
+
if (error.name === 'TimeoutError' || error.name === 'AbortError') {
|
|
61
|
+
return `请求超时(${timeoutMs}ms)`;
|
|
62
|
+
}
|
|
63
|
+
return `网络错误:${error.message}`;
|
|
64
|
+
}
|
|
65
|
+
return `网络错误:${String(error)}`;
|
|
66
|
+
}
|
|
67
|
+
/** GET a URL as text with timeout, retries and optional conditional validators. */
|
|
68
|
+
export async function getTextWithRetry(url, policy, options = {}) {
|
|
69
|
+
const fetchImpl = options.deps?.fetch ?? ((input, init) => fetch(input, init));
|
|
70
|
+
const sleep = options.deps?.sleep ?? defaultSleep;
|
|
71
|
+
const random = options.deps?.random ?? Math.random;
|
|
72
|
+
const now = options.deps?.now ?? Date.now;
|
|
73
|
+
const headers = { Accept: 'application/json', ...options.headers };
|
|
74
|
+
if (options.validators?.etag) {
|
|
75
|
+
headers['If-None-Match'] = options.validators.etag;
|
|
76
|
+
}
|
|
77
|
+
if (options.validators?.lastModified) {
|
|
78
|
+
headers['If-Modified-Since'] = options.validators.lastModified;
|
|
79
|
+
}
|
|
80
|
+
let lastError;
|
|
81
|
+
for (let attempt = 0; attempt <= policy.maxRetries; attempt += 1) {
|
|
82
|
+
if (attempt > 0) {
|
|
83
|
+
const retryAfter = lastError?.retryAfterMs;
|
|
84
|
+
const backoff = computeBackoffDelay(attempt - 1, policy, random);
|
|
85
|
+
const delay = retryAfter === undefined ? backoff : Math.max(backoff, retryAfter);
|
|
86
|
+
await sleep(delay);
|
|
87
|
+
}
|
|
88
|
+
const attemptNumber = attempt + 1;
|
|
89
|
+
let response;
|
|
90
|
+
try {
|
|
91
|
+
response = await fetchImpl(url, {
|
|
92
|
+
method: 'GET',
|
|
93
|
+
headers,
|
|
94
|
+
redirect: 'follow',
|
|
95
|
+
signal: AbortSignal.timeout(policy.timeoutMs),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
lastError = new HttpRequestError(describeFailure(error, policy.timeoutMs), { retryable: true, cause: error });
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (response.status === 304) {
|
|
103
|
+
return { notModified: true, body: '', validators: options.validators ?? {}, attempts: attemptNumber };
|
|
104
|
+
}
|
|
105
|
+
if (response.ok) {
|
|
106
|
+
let body;
|
|
107
|
+
try {
|
|
108
|
+
body = await response.text();
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
// A body cut off mid-stream (or a timeout while reading) is transient.
|
|
112
|
+
lastError = new HttpRequestError(describeFailure(error, policy.timeoutMs), { retryable: true, cause: error });
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
const validators = {
|
|
116
|
+
etag: response.headers.get('etag') ?? undefined,
|
|
117
|
+
lastModified: response.headers.get('last-modified') ?? undefined,
|
|
118
|
+
};
|
|
119
|
+
return { notModified: false, body, validators, attempts: attemptNumber };
|
|
120
|
+
}
|
|
121
|
+
const retryable = isRetryableStatus(response.status);
|
|
122
|
+
const retryAfterMs = parseRetryAfter(response.headers.get('retry-after'), now());
|
|
123
|
+
// Release the connection; the error body is not needed.
|
|
124
|
+
await response.body?.cancel().catch(() => undefined);
|
|
125
|
+
const message = `HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : ''}`;
|
|
126
|
+
if (!retryable) {
|
|
127
|
+
throw new HttpRequestError(message, { status: response.status, retryable: false });
|
|
128
|
+
}
|
|
129
|
+
if (retryAfterMs !== undefined && retryAfterMs > policy.maxDelayMs) {
|
|
130
|
+
// The server wants a longer pause than one request may wait: stop now and
|
|
131
|
+
// let the caller put the whole source on cooldown.
|
|
132
|
+
throw new HttpRequestError(message, { status: response.status, retryable: true, retryAfterMs });
|
|
133
|
+
}
|
|
134
|
+
lastError = new HttpRequestError(message, { status: response.status, retryable: true, retryAfterMs });
|
|
135
|
+
}
|
|
136
|
+
throw lastError ?? new HttpRequestError('请求失败', { retryable: true });
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=http.js.map
|
package/lib/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export { assertEarthquakeTimezone, DEFAULT_EARTHQUAKE_CONFIG, EARTHQUAKE_SOURCE_IDS, MAX_REGIONS_PER_SUBSCRIPTION, resolveEarthquakeConfig, USGS_FEEDS, } from './config.js';
|
|
2
|
+
export type { EarthquakeConfig, EarthquakeSourceId, ResolvedEarthquakeConfig, UsgsFeed } from './config.js';
|
|
3
|
+
export { DEFAULT_DEDUPE_TOLERANCE, distanceKm, isSameEvent, mergeReports, SeenEventLog } from './dedupe.js';
|
|
4
|
+
export type { DedupeTolerance } from './dedupe.js';
|
|
5
|
+
export { CHINA_REGION_ALIASES, eventMagnitude, matchesFilter, matchesRegions } from './filters.js';
|
|
6
|
+
export type { EventFilter } from './filters.js';
|
|
7
|
+
export { formatEvent, formatEventList, formatEventTime } from './format.js';
|
|
8
|
+
export { computeBackoffDelay, getTextWithRetry, HttpRequestError, isRetryableStatus, parseRetryAfter, } from './http.js';
|
|
9
|
+
export type { FetchLike, HttpDependencies, RetryPolicy, SleepLike } from './http.js';
|
|
10
|
+
export { SlidingWindowRateLimiter } from './rate-limit.js';
|
|
11
|
+
export { earthquakeServiceToken } from './runtime.js';
|
|
12
|
+
export { createDefaultSources, EarthquakeService, EarthquakeUnavailableError } from './service.js';
|
|
13
|
+
export type { EarthquakeServiceOptions, PollReport, PushSender, QueryOptions, QueryResult } from './service.js';
|
|
14
|
+
export { CENC_MIRROR_URL, CENC_OFFICIAL_URL, DEFAULT_CENC_URLS, parseBeijingTime, parseCencFeed } from './sources/cenc.js';
|
|
15
|
+
export { FeedSource } from './sources/feed-source.js';
|
|
16
|
+
export type { FeedSourceHealth, FeedSourceOptions } from './sources/feed-source.js';
|
|
17
|
+
export { parseUsgsFeed, usgsFeedUrl } from './sources/usgs.js';
|
|
18
|
+
export { parseSubscribeArgs } from './subscribe-args.js';
|
|
19
|
+
export { createMemoryState, SubscriptionLimitError, SubscriptionStore, subscriptionKey, } from './subscriptions.js';
|
|
20
|
+
export type { EarthquakeSubscription, KeyValueState } from './subscriptions.js';
|
|
21
|
+
export type { EarthquakeEvent, SourceEarthquake, SourceOutcome } from './types.js';
|
|
22
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,yBAAyB,EACzB,qBAAqB,EACrB,4BAA4B,EAC5B,uBAAuB,EACvB,UAAU,GACX,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5G,OAAO,EAAE,wBAAwB,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC5G,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnG,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,GAChB,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACrF,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AACnG,YAAY,EAAE,wBAAwB,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChH,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC3H,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,iBAAiB,EACjB,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAChF,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { assertEarthquakeTimezone, DEFAULT_EARTHQUAKE_CONFIG, EARTHQUAKE_SOURCE_IDS, MAX_REGIONS_PER_SUBSCRIPTION, resolveEarthquakeConfig, USGS_FEEDS, } from './config.js';
|
|
2
|
+
export { DEFAULT_DEDUPE_TOLERANCE, distanceKm, isSameEvent, mergeReports, SeenEventLog } from './dedupe.js';
|
|
3
|
+
export { CHINA_REGION_ALIASES, eventMagnitude, matchesFilter, matchesRegions } from './filters.js';
|
|
4
|
+
export { formatEvent, formatEventList, formatEventTime } from './format.js';
|
|
5
|
+
export { computeBackoffDelay, getTextWithRetry, HttpRequestError, isRetryableStatus, parseRetryAfter, } from './http.js';
|
|
6
|
+
export { SlidingWindowRateLimiter } from './rate-limit.js';
|
|
7
|
+
export { earthquakeServiceToken } from './runtime.js';
|
|
8
|
+
export { createDefaultSources, EarthquakeService, EarthquakeUnavailableError } from './service.js';
|
|
9
|
+
export { CENC_MIRROR_URL, CENC_OFFICIAL_URL, DEFAULT_CENC_URLS, parseBeijingTime, parseCencFeed } from './sources/cenc.js';
|
|
10
|
+
export { FeedSource } from './sources/feed-source.js';
|
|
11
|
+
export { parseUsgsFeed, usgsFeedUrl } from './sources/usgs.js';
|
|
12
|
+
export { parseSubscribeArgs } from './subscribe-args.js';
|
|
13
|
+
export { createMemoryState, SubscriptionLimitError, SubscriptionStore, subscriptionKey, } from './subscriptions.js';
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,yBAAyB,EACzB,qBAAqB,EACrB,4BAA4B,EAC5B,uBAAuB,EACvB,UAAU,GACX,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,wBAAwB,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE5G,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnG,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,GAChB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAEnG,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC3H,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,iBAAiB,EACjB,eAAe,GAChB,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { EarthquakeService } from './service.js';
|
|
2
|
+
export interface QueryTextOptions {
|
|
3
|
+
readonly limit?: number;
|
|
4
|
+
readonly minMagnitude?: number;
|
|
5
|
+
readonly region?: string;
|
|
6
|
+
readonly hours?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare const DEFAULT_QUERY_HOURS = 24;
|
|
9
|
+
export declare const MAX_QUERY_HOURS = 168;
|
|
10
|
+
export declare function renderRecentEarthquakes(service: EarthquakeService, options: QueryTextOptions): Promise<string>;
|
|
11
|
+
//# sourceMappingURL=query-text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-text.d.ts","sourceRoot":"","sources":["../src/query-text.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGtD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,mBAAmB,KAAK,CAAC;AACtC,eAAO,MAAM,eAAe,MAAM,CAAC;AAEnC,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CA6BpH"}
|