@rapidrest/core 1.14.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/AlertUtils.js +71 -56
- package/dist/lib/AlertUtils.js.map +1 -1
- package/dist/lib/ApiError.js +8 -1
- package/dist/lib/ApiError.js.map +1 -1
- package/dist/lib/CacheUtils.js +23 -0
- package/dist/lib/CacheUtils.js.map +1 -0
- package/dist/lib/ClassLoader.js +48 -44
- package/dist/lib/ClassLoader.js.map +1 -1
- package/dist/lib/FileUtils.js +133 -60
- package/dist/lib/FileUtils.js.map +1 -1
- package/dist/lib/JWTUtils.js +250 -145
- package/dist/lib/JWTUtils.js.map +1 -1
- package/dist/lib/Logger.js +39 -7
- package/dist/lib/Logger.js.map +1 -1
- package/dist/lib/MemoryStore.js +8 -6
- package/dist/lib/MemoryStore.js.map +1 -1
- package/dist/lib/MessagingUtils.js +71 -35
- package/dist/lib/MessagingUtils.js.map +1 -1
- package/dist/lib/NotificationsUtils.js +60 -8
- package/dist/lib/NotificationsUtils.js.map +1 -1
- package/dist/lib/OASUtils.js +67 -18
- package/dist/lib/OASUtils.js.map +1 -1
- package/dist/lib/ObjectFactory.js +151 -63
- package/dist/lib/ObjectFactory.js.map +1 -1
- package/dist/lib/ObjectUtils.js +48 -7
- package/dist/lib/ObjectUtils.js.map +1 -1
- package/dist/lib/StringUtils.js +55 -21
- package/dist/lib/StringUtils.js.map +1 -1
- package/dist/lib/TelemetryUtils.js +41 -5
- package/dist/lib/TelemetryUtils.js.map +1 -1
- package/dist/lib/UserUtils.js +5 -6
- package/dist/lib/UserUtils.js.map +1 -1
- package/dist/lib/ValidationUtils.js +16 -4
- package/dist/lib/ValidationUtils.js.map +1 -1
- package/dist/lib/index.js +2 -0
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/threads/ThreadPool.js +198 -58
- package/dist/lib/threads/ThreadPool.js.map +1 -1
- package/dist/types/AlertUtils.d.ts +4 -0
- package/dist/types/CacheUtils.d.ts +14 -0
- package/dist/types/ClassLoader.d.ts +10 -0
- package/dist/types/FileUtils.d.ts +23 -7
- package/dist/types/JWTUtils.d.ts +88 -18
- package/dist/types/MessagingUtils.d.ts +7 -1
- package/dist/types/NotificationsUtils.d.ts +30 -0
- package/dist/types/ObjectFactory.d.ts +25 -1
- package/dist/types/ObjectUtils.d.ts +14 -0
- package/dist/types/StringUtils.d.ts +8 -0
- package/dist/types/TelemetryUtils.d.ts +19 -1
- package/dist/types/ValidationUtils.d.ts +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/threads/ThreadPool.d.ts +34 -2
- package/package.json +1 -1
package/dist/lib/Logger.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Logger.js","sourceRoot":"","sources":["../../src/Logger.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,kDAAkD;AAClD,+EAA+E;AAC/E,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;AACvC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;AAC9C,MAAM,CAAC,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAE3E,
|
|
1
|
+
{"version":3,"file":"Logger.js","sourceRoot":"","sources":["../../src/Logger.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/MemoryStore.js
CHANGED
|
@@ -10,6 +10,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
10
10
|
///////////////////////////////////////////////////////////////////////////////
|
|
11
11
|
// Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
|
|
12
12
|
///////////////////////////////////////////////////////////////////////////////
|
|
13
|
+
import { CacheUtils } from "./CacheUtils.js";
|
|
13
14
|
import { Destroy } from "./decorators/ObjectDecorators.js";
|
|
14
15
|
/** How often the sweep interval reclaims expired, never-reloaded sessions. */
|
|
15
16
|
const SWEEP_INTERVAL_MS = 60000;
|
|
@@ -49,14 +50,15 @@ export class MemoryStore {
|
|
|
49
50
|
if (!this.entries.has(id) && this.entries.size >= this.maxSize) {
|
|
50
51
|
// Reclaim space by sweeping expired entries first, then evicting the oldest surviving entries
|
|
51
52
|
this.sweep();
|
|
52
|
-
while (this.entries.size >= this.maxSize) {
|
|
53
|
-
|
|
54
|
-
if (oldestId === undefined) {
|
|
55
|
-
break;
|
|
56
|
-
}
|
|
57
|
-
this.entries.delete(oldestId);
|
|
53
|
+
while (this.entries.size >= this.maxSize && this.entries.size > 0) {
|
|
54
|
+
CacheUtils.evictOldest(this.entries);
|
|
58
55
|
}
|
|
59
56
|
}
|
|
57
|
+
// Delete before re-inserting so a renewed entry moves to the end of the Map's iteration order. A `Map`
|
|
58
|
+
// does not reorder on `set()` of an already-present key, so without this, an actively-renewed entry stays
|
|
59
|
+
// at its original (oldest) position and `evictOldest()` would evict it ahead of a genuinely idle entry
|
|
60
|
+
// inserted later.
|
|
61
|
+
this.entries.delete(id);
|
|
60
62
|
this.entries.set(id, { data, expiresAt: Date.now() + ttlSeconds * 1000 });
|
|
61
63
|
}
|
|
62
64
|
delete(id) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MemoryStore.js","sourceRoot":"","sources":["../../src/MemoryStore.ts"],"names":[],"mappings":";;;;;;;;;AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,+EAA+E;AAC/E,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAE3D,8EAA8E;AAC9E,MAAM,iBAAiB,GAAG,KAAM,CAAC;AAOjC;;;;;GAKG;AACH,MAAM,OAAO,WAAW;IAWpB;QAVA,2CAA2C;QACpC,eAAU,GAAW,EAAE,CAAC;QAErB,YAAO,GAAkC,IAAI,GAAG,EAAE,CAAC;QAE7D,8CAA8C;QACvC,YAAO,GAAW,KAAK,CAAC;QAK3B,IAAI,CAAC,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,iBAAiB,CAAC,CAAC;QACrE,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,CAAC;IAC9B,CAAC;IAED,4FAA4F;IAErF,OAAO;QACV,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACnC,CAAC;IAEM,IAAI,CAAC,EAAU;QAClB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnC,IAAI,CAAC,KAAK,EAAE,CAAC;YACT,OAAO,SAAS,CAAC;QACrB,CAAC;QAED,2CAA2C;QAC3C,IAAI,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAChC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACxB,OAAO,SAAS,CAAC;QACrB,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC;IACtB,CAAC;IAEM,IAAI,CAAC,EAAU,EAAE,IAAyB,EAAE,aAAqB,IAAI,CAAC,UAAU;QACnF,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC7D,8FAA8F;YAC9F,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,
|
|
1
|
+
{"version":3,"file":"MemoryStore.js","sourceRoot":"","sources":["../../src/MemoryStore.ts"],"names":[],"mappings":";;;;;;;;;AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,+EAA+E;AAC/E,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAE3D,8EAA8E;AAC9E,MAAM,iBAAiB,GAAG,KAAM,CAAC;AAOjC;;;;;GAKG;AACH,MAAM,OAAO,WAAW;IAWpB;QAVA,2CAA2C;QACpC,eAAU,GAAW,EAAE,CAAC;QAErB,YAAO,GAAkC,IAAI,GAAG,EAAE,CAAC;QAE7D,8CAA8C;QACvC,YAAO,GAAW,KAAK,CAAC;QAK3B,IAAI,CAAC,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,iBAAiB,CAAC,CAAC;QACrE,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,CAAC;IAC9B,CAAC;IAED,4FAA4F;IAErF,OAAO;QACV,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACnC,CAAC;IAEM,IAAI,CAAC,EAAU;QAClB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnC,IAAI,CAAC,KAAK,EAAE,CAAC;YACT,OAAO,SAAS,CAAC;QACrB,CAAC;QAED,2CAA2C;QAC3C,IAAI,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAChC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACxB,OAAO,SAAS,CAAC;QACrB,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC;IACtB,CAAC;IAEM,IAAI,CAAC,EAAU,EAAE,IAAyB,EAAE,aAAqB,IAAI,CAAC,UAAU;QACnF,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC7D,8FAA8F;YAC9F,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;gBAChE,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACzC,CAAC;QACL,CAAC;QACD,uGAAuG;QACvG,0GAA0G;QAC1G,uGAAuG;QACvG,kBAAkB;QAClB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACxB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,GAAG,IAAI,EAAE,CAAC,CAAC;IAC9E,CAAC;IAEM,MAAM,CAAC,EAAU;QACpB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC5B,CAAC;IAEO,KAAK;QACT,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;YACtD,IAAI,KAAK,CAAC,SAAS,IAAI,GAAG;gBAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC/D,CAAC;IACL,CAAC;CACJ;AA7CU;IADN,OAAO;;;;0CAGP"}
|
|
@@ -12,7 +12,8 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
12
12
|
///////////////////////////////////////////////////////////////////////////////
|
|
13
13
|
import handlebars from "handlebars";
|
|
14
14
|
import { Config, Init, Logger } from "./decorators/ObjectDecorators.js";
|
|
15
|
-
import fs from "fs";
|
|
15
|
+
import fs from "fs/promises";
|
|
16
|
+
import { FileUtils } from "./FileUtils.js";
|
|
16
17
|
/**
|
|
17
18
|
* Simple utility class for sending templated messages via e-mail, SMS and more.
|
|
18
19
|
*/
|
|
@@ -24,6 +25,12 @@ export class MessagingUtils {
|
|
|
24
25
|
this.templates = {};
|
|
25
26
|
/** Cache of compiled Handlebars delegates keyed by "<templateName>:<field>". */
|
|
26
27
|
this._compiledTemplates = new Map();
|
|
28
|
+
/** Source string each `_compiledTemplates` entry was compiled from, keyed the same way. Lets `loadTemplate()`
|
|
29
|
+
* detect when an existing field's value has changed (e.g. a live config reload editing a template's subject/
|
|
30
|
+
* text/html) and recompile, instead of only ever compiling a field once and rendering stale content forever. */
|
|
31
|
+
this._compiledTemplateSources = new Map();
|
|
32
|
+
/** Names of templates already compiled into `_compiledTemplates` for this instance. See `loadTemplate()`. */
|
|
33
|
+
this._loadedTemplates = new Set();
|
|
27
34
|
}
|
|
28
35
|
async init() {
|
|
29
36
|
if (this.slackConfigs.length > 0) {
|
|
@@ -93,34 +100,59 @@ export class MessagingUtils {
|
|
|
93
100
|
* Loads the template with the given name and returns its contents as a string.
|
|
94
101
|
* @param name The name of the template to load.
|
|
95
102
|
*/
|
|
96
|
-
loadTemplate(name) {
|
|
103
|
+
async loadTemplate(name) {
|
|
97
104
|
if (!this.templates[name]) {
|
|
98
105
|
throw new Error(`No template found with name ${name}`);
|
|
99
106
|
}
|
|
100
107
|
const tplConfig = this.templates[name];
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
//
|
|
105
|
-
if (
|
|
106
|
-
|
|
108
|
+
// Load html/text from disk once per instance (I/O). Gated on this instance's own `_loadedTemplates` set
|
|
109
|
+
// rather than `tplConfig.loaded`: the latter lives on the (potentially shared, config-provided)
|
|
110
|
+
// `Template` object, so a second instance bound to the same config could otherwise see `loaded === true`
|
|
111
|
+
// set by another instance without ever reading the files into its own template config.
|
|
112
|
+
if (!this._loadedTemplates.has(name)) {
|
|
113
|
+
// Check if a path is specified for the HTML template. If so load it. Uses `fs/promises` (rather
|
|
114
|
+
// than the sync `existsSync`/`readFileSync`) so a template read on a live send path doesn't block
|
|
115
|
+
// the event loop for every other in-flight request.
|
|
116
|
+
if (tplConfig.htmlPath && (await FileUtils.exists(tplConfig.htmlPath))) {
|
|
117
|
+
tplConfig.html = await fs.readFile(tplConfig.htmlPath, { encoding: "utf-8" });
|
|
118
|
+
}
|
|
119
|
+
// Check if a path is specified for the text template. If so load it.
|
|
120
|
+
if (tplConfig.textPath && (await FileUtils.exists(tplConfig.textPath))) {
|
|
121
|
+
tplConfig.text = await fs.readFile(tplConfig.textPath, { encoding: "utf-8" });
|
|
122
|
+
}
|
|
123
|
+
tplConfig.loaded = true;
|
|
124
|
+
this._loadedTemplates.add(name);
|
|
107
125
|
}
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
|
|
126
|
+
// Compile and cache a Handlebars delegate for each field that's currently present and whose value has
|
|
127
|
+
// changed since it was last compiled for *this instance*, rather than gating compilation on the one-time
|
|
128
|
+
// "have we ever loaded this template" check above or on "have we ever compiled this field." Without
|
|
129
|
+
// comparing against the previously-compiled source, a field added after the first load (e.g. a live
|
|
130
|
+
// config reload that only sets `subject` once other fields already exist) would never get compiled,
|
|
131
|
+
// leaving sendEmail()/sendSlack()/sendSMS() to call an `undefined` delegate and throw - and a field whose
|
|
132
|
+
// *value* was edited in place (e.g. an admin updates an existing subject/text/html) would keep rendering
|
|
133
|
+
// the stale delegate forever.
|
|
134
|
+
const fields = [
|
|
135
|
+
["text", tplConfig.text],
|
|
136
|
+
["html", tplConfig.html],
|
|
137
|
+
["subject", tplConfig.subject],
|
|
138
|
+
["sms", tplConfig.sms],
|
|
139
|
+
["slack_text", tplConfig.slack_text],
|
|
140
|
+
];
|
|
141
|
+
for (const [field, value] of fields) {
|
|
142
|
+
const cacheKey = `${name}:${field}`;
|
|
143
|
+
if (value) {
|
|
144
|
+
if (this._compiledTemplateSources.get(cacheKey) !== value) {
|
|
145
|
+
this._compiledTemplates.set(cacheKey, handlebars.compile(value));
|
|
146
|
+
this._compiledTemplateSources.set(cacheKey, value);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
else if (this._compiledTemplateSources.has(cacheKey)) {
|
|
150
|
+
// The field was cleared/removed (e.g. a live config reload disabling it) - drop the stale
|
|
151
|
+
// compiled delegate rather than continuing to serve output from a value that's no longer current.
|
|
152
|
+
this._compiledTemplates.delete(cacheKey);
|
|
153
|
+
this._compiledTemplateSources.delete(cacheKey);
|
|
154
|
+
}
|
|
111
155
|
}
|
|
112
|
-
tplConfig.loaded = true;
|
|
113
|
-
// Compile and cache all Handlebars delegates on first load
|
|
114
|
-
if (tplConfig.text)
|
|
115
|
-
this._compiledTemplates.set(`${name}:text`, handlebars.compile(tplConfig.text));
|
|
116
|
-
if (tplConfig.html)
|
|
117
|
-
this._compiledTemplates.set(`${name}:html`, handlebars.compile(tplConfig.html));
|
|
118
|
-
if (tplConfig.subject)
|
|
119
|
-
this._compiledTemplates.set(`${name}:subject`, handlebars.compile(tplConfig.subject));
|
|
120
|
-
if (tplConfig.sms)
|
|
121
|
-
this._compiledTemplates.set(`${name}:sms`, handlebars.compile(tplConfig.sms));
|
|
122
|
-
if (tplConfig.slack_text)
|
|
123
|
-
this._compiledTemplates.set(`${name}:slack_text`, handlebars.compile(tplConfig.slack_text));
|
|
124
156
|
return tplConfig;
|
|
125
157
|
}
|
|
126
158
|
/**
|
|
@@ -134,7 +166,7 @@ export class MessagingUtils {
|
|
|
134
166
|
if (!this.smtpConfig) {
|
|
135
167
|
throw new Error("E-mail is not configured.");
|
|
136
168
|
}
|
|
137
|
-
const tplConfig = this.loadTemplate(templateName);
|
|
169
|
+
const tplConfig = await this.loadTemplate(templateName);
|
|
138
170
|
if (!tplConfig.enabled || !tplConfig.subject) {
|
|
139
171
|
return undefined;
|
|
140
172
|
}
|
|
@@ -174,21 +206,18 @@ export class MessagingUtils {
|
|
|
174
206
|
if (this.slackApps.length === 0) {
|
|
175
207
|
throw new Error("Slack is not configured.");
|
|
176
208
|
}
|
|
177
|
-
const tplConfig = this.loadTemplate(templateName);
|
|
209
|
+
const tplConfig = await this.loadTemplate(templateName);
|
|
178
210
|
if (!tplConfig.enabled || !tplConfig.slack_channel || !tplConfig.slack_text) {
|
|
179
211
|
return undefined;
|
|
180
212
|
}
|
|
181
213
|
// Render using cached compiled delegate
|
|
182
214
|
const message = this._compiledTemplates.get(`${templateName}:slack_text`)(templateVars);
|
|
183
|
-
|
|
184
|
-
//
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
});
|
|
190
|
-
result.push(res);
|
|
191
|
-
}
|
|
215
|
+
// Send to every configured Slack workspace concurrently - they're independent deliveries to different
|
|
216
|
+
// apps/tokens, so there's no reason to pay N round-trips of latency sequentially on this alerting path.
|
|
217
|
+
const result = await Promise.all(this.slackApps.map((app) => app.client.chat.postMessage({
|
|
218
|
+
channel: tplConfig.slack_channel,
|
|
219
|
+
text: message,
|
|
220
|
+
})));
|
|
192
221
|
return result;
|
|
193
222
|
}
|
|
194
223
|
/**
|
|
@@ -202,10 +231,17 @@ export class MessagingUtils {
|
|
|
202
231
|
if (!this.twilio) {
|
|
203
232
|
throw new Error("Twilio is not configured.");
|
|
204
233
|
}
|
|
205
|
-
const tplConfig = this.loadTemplate(templateName);
|
|
234
|
+
const tplConfig = await this.loadTemplate(templateName);
|
|
206
235
|
if (!tplConfig.enabled || !tplConfig.sms) {
|
|
207
236
|
return undefined;
|
|
208
237
|
}
|
|
238
|
+
// Mirrors sendEmail()'s equivalent guard: fail gracefully (rather than throwing a TypeError from
|
|
239
|
+
// dereferencing `this.templates.from.sms` below) when `from.sms` isn't configured, e.g. because
|
|
240
|
+
// `templates.from` was only ever set up for e-mail.
|
|
241
|
+
if (!this.templates?.from?.sms) {
|
|
242
|
+
this.logger?.warn("Unable to send SMS missing from.sms in message template");
|
|
243
|
+
return undefined;
|
|
244
|
+
}
|
|
209
245
|
// Render using cached compiled delegate
|
|
210
246
|
const message = this._compiledTemplates.get(`${templateName}:sms`)(templateVars);
|
|
211
247
|
// Send the message to the user. `options` is spread before the protected fields below so a caller cannot
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessagingUtils.js","sourceRoot":"","sources":["../../src/MessagingUtils.ts"],"names":[],"mappings":";;;;;;;;;AAAA,+EAA+E;AAC/E,kDAAkD;AAClD,+EAA+E;AAC/E,OAAO,UAAU,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"MessagingUtils.js","sourceRoot":"","sources":["../../src/MessagingUtils.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,16 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
1
10
|
///////////////////////////////////////////////////////////////////////////////
|
|
2
11
|
// Copyright (C) 2020-2026 Jean-Philippe Steinmetz
|
|
3
12
|
///////////////////////////////////////////////////////////////////////////////
|
|
13
|
+
import { Logger } from "./decorators/ObjectDecorators.js";
|
|
4
14
|
/**
|
|
5
15
|
* Utility functions for sending push notifications to registered clients.
|
|
6
16
|
*
|
|
@@ -11,6 +21,7 @@ export class NotificationUtils {
|
|
|
11
21
|
* Initializes the utility using the given redis connection.
|
|
12
22
|
*
|
|
13
23
|
* @param {any} redis The redis connection to publish to.
|
|
24
|
+
* @param {any} logger The logging utility to use.
|
|
14
25
|
*/
|
|
15
26
|
constructor(redis) {
|
|
16
27
|
if (!redis) {
|
|
@@ -18,19 +29,46 @@ export class NotificationUtils {
|
|
|
18
29
|
}
|
|
19
30
|
this.redis = redis;
|
|
20
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Publishes to the given redis channel, logging (rather than crashing the process via an unhandled rejection)
|
|
34
|
+
* if the underlying client's `publish()` rejects.
|
|
35
|
+
*/
|
|
36
|
+
publish(channel, payload) {
|
|
37
|
+
try {
|
|
38
|
+
const result = this.redis?.publish(channel, payload);
|
|
39
|
+
if (result && typeof result.catch === "function") {
|
|
40
|
+
result.catch((err) => {
|
|
41
|
+
this.logger?.error(`Failed to publish message to channel: ${channel}`);
|
|
42
|
+
this.logger?.debug(err);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
this.logger?.error(`Failed to publish message to channel: ${channel}`);
|
|
48
|
+
this.logger?.debug(err);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
21
51
|
/**
|
|
22
52
|
* Broadcasts a given message to all users.
|
|
23
53
|
*
|
|
54
|
+
* Subscribers must listen on `NotificationUtils.BROADCAST_CHANNEL` (rather than a bare `"allusers"`) to
|
|
55
|
+
* receive these messages.
|
|
56
|
+
*
|
|
24
57
|
* @param {any} type The type of message being sent.
|
|
25
58
|
* @param {string} action The action performed on the data (if applicable).
|
|
26
59
|
* @param {string} data The contents of the message to send.
|
|
27
60
|
*/
|
|
28
61
|
broadcastMessage(type, action, data) {
|
|
29
|
-
|
|
62
|
+
this.publish(NotificationUtils.BROADCAST_CHANNEL, JSON.stringify({ type, action, data }));
|
|
30
63
|
}
|
|
31
64
|
/**
|
|
32
65
|
* Sends a given message to the room or user with the specified uid(s).
|
|
33
66
|
*
|
|
67
|
+
* Each recipient's channel is `NotificationUtils.USER_CHANNEL_PREFIX + uid` (rather than the bare uid), so
|
|
68
|
+
* that no caller-supplied uid can collide with `NotificationUtils.BROADCAST_CHANNEL` or any other reserved
|
|
69
|
+
* channel and be delivered to unintended subscribers. Subscribers must listen on that prefixed channel name
|
|
70
|
+
* to receive messages sent to their uid.
|
|
71
|
+
*
|
|
34
72
|
* @param {string} uids The universally unique identifier of the room or user to send the message to.
|
|
35
73
|
* @param {string} type The type of message being sent.
|
|
36
74
|
* @param {string} action The action performed on the data (if applicable).
|
|
@@ -39,14 +77,28 @@ export class NotificationUtils {
|
|
|
39
77
|
sendMessage(uids, type, action, data) {
|
|
40
78
|
// Serialize once regardless of how many recipients
|
|
41
79
|
const payload = JSON.stringify({ type, action, data });
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
else {
|
|
48
|
-
void this.redis?.publish(uids, payload);
|
|
80
|
+
const targets = Array.isArray(uids) ? uids : [uids];
|
|
81
|
+
for (const uid of targets) {
|
|
82
|
+
this.publish(`${NotificationUtils.USER_CHANNEL_PREFIX}${uid}`, payload);
|
|
49
83
|
}
|
|
50
84
|
}
|
|
51
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* The redis channel `broadcastMessage()` publishes to. Namespaced (rather than a bare `"allusers"`) so it
|
|
88
|
+
* can never collide with a per-recipient channel `sendMessage()` derives from a caller-supplied uid - a
|
|
89
|
+
* client-facing "send a message to this uid" feature that passes its recipient straight through to
|
|
90
|
+
* `sendMessage()` must not be able to choose a uid that lands on the broadcast channel and reach every
|
|
91
|
+
* subscriber instead of the intended one recipient.
|
|
92
|
+
*/
|
|
93
|
+
NotificationUtils.BROADCAST_CHANNEL = "broadcast:allusers";
|
|
94
|
+
/**
|
|
95
|
+
* Prefix applied to every per-recipient channel in `sendMessage()`. Combined with `BROADCAST_CHANNEL`'s own
|
|
96
|
+
* distinct `"broadcast:"` prefix, this guarantees the two channel namespaces can never overlap regardless of
|
|
97
|
+
* what uid value a caller supplies.
|
|
98
|
+
*/
|
|
99
|
+
NotificationUtils.USER_CHANNEL_PREFIX = "user:";
|
|
100
|
+
__decorate([
|
|
101
|
+
Logger,
|
|
102
|
+
__metadata("design:type", Object)
|
|
103
|
+
], NotificationUtils.prototype, "logger", void 0);
|
|
52
104
|
//# sourceMappingURL=NotificationsUtils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotificationsUtils.js","sourceRoot":"","sources":["../../src/NotificationsUtils.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,kDAAkD;AAClD,+EAA+E;AAC/E;;;;GAIG;AACH,MAAM,OAAO,iBAAiB;
|
|
1
|
+
{"version":3,"file":"NotificationsUtils.js","sourceRoot":"","sources":["../../src/NotificationsUtils.ts"],"names":[],"mappings":";;;;;;;;;AAAA,+EAA+E;AAC/E,kDAAkD;AAClD,+EAA+E;AAC/E,OAAO,EAAE,MAAM,EAAE,MAAM,kCAAkC,CAAC;AAE1D;;;;GAIG;AACH,MAAM,OAAO,iBAAiB;IAsB1B;;;;;OAKG;IACH,YAAY,KAAU;QAClB,IAAI,CAAC,KAAK,EAAE,CAAC;YACT,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACnD,CAAC;QACD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,CAAC;IAED;;;OAGG;IACK,OAAO,CAAC,OAAe,EAAE,OAAe;QAC5C,IAAI,CAAC;YACD,MAAM,MAAM,GAAQ,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC1D,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;gBAC/C,MAAM,CAAC,KAAK,CAAC,CAAC,GAAQ,EAAE,EAAE;oBACtB,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,yCAAyC,OAAO,EAAE,CAAC,CAAC;oBACvE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;gBAC5B,CAAC,CAAC,CAAC;YACP,CAAC;QACL,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAChB,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,yCAAyC,OAAO,EAAE,CAAC,CAAC;YACvE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;QAC5B,CAAC;IACL,CAAC;IAED;;;;;;;;;OASG;IACI,gBAAgB,CAAC,IAAS,EAAE,MAAc,EAAE,IAAS;QACxD,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,iBAAiB,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC9F,CAAC;IAED;;;;;;;;;;;;OAYG;IACI,WAAW,CAAC,IAAuB,EAAE,IAAY,EAAE,MAAc,EAAE,IAAS;QAC/E,mDAAmD;QACnD,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QACvD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACpD,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;YACxB,IAAI,CAAC,OAAO,CAAC,GAAG,iBAAiB,CAAC,mBAAmB,GAAG,GAAG,EAAE,EAAE,OAAO,CAAC,CAAC;QAC5E,CAAC;IACL,CAAC;;AAvFD;;;;;;GAMG;AACoB,mCAAiB,GAAG,oBAAoB,CAAC;AAChE;;;;GAIG;AACoB,qCAAmB,GAAG,OAAO,CAAC;AAM7C;IADP,MAAM;;iDACc"}
|
package/dist/lib/OASUtils.js
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
///////////////////////////////////////////////////////////////////////////////
|
|
2
2
|
// Copyright (C) 2020-2026 Jean-Philippe Steinmetz
|
|
3
3
|
///////////////////////////////////////////////////////////////////////////////
|
|
4
|
-
import fs from "fs";
|
|
4
|
+
import fs from "fs/promises";
|
|
5
|
+
import { CacheUtils } from "./CacheUtils.js";
|
|
6
|
+
import { FileUtils } from "./FileUtils.js";
|
|
5
7
|
import { Logger } from "./Logger.js";
|
|
6
8
|
import path from "path";
|
|
7
9
|
import { Axios } from "axios";
|
|
8
10
|
import YAML from "js-yaml";
|
|
11
|
+
import { StringUtils } from "./StringUtils.js";
|
|
9
12
|
const logger = Logger();
|
|
10
13
|
/** Cache of parsed spec objects keyed by file path or URL, to avoid repeated I/O and parsing. */
|
|
11
14
|
const _specCache = new Map();
|
|
15
|
+
/** The maximum number of parsed specs to retain in `_specCache` before evicting the oldest entry. */
|
|
16
|
+
const _specCacheMaxSize = 100;
|
|
12
17
|
const _urlRegex = /^https?:\/\/[a-zA-Z0-9]*\.?[a-z].*/;
|
|
13
18
|
export class OASUtils {
|
|
14
19
|
/**
|
|
@@ -42,9 +47,13 @@ export class OASUtils {
|
|
|
42
47
|
path = path.substring(1, path.length);
|
|
43
48
|
}
|
|
44
49
|
let parts = path.split("/");
|
|
45
|
-
|
|
50
|
+
for (const part of parts) {
|
|
51
|
+
if (result === undefined || result === null) {
|
|
52
|
+
result = undefined;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
46
55
|
result = result[part];
|
|
47
|
-
}
|
|
56
|
+
}
|
|
48
57
|
}
|
|
49
58
|
else {
|
|
50
59
|
result = undefined;
|
|
@@ -88,8 +97,9 @@ export class OASUtils {
|
|
|
88
97
|
if (!spec.components || !spec.components.schemas) {
|
|
89
98
|
throw new Error("Invalid specification. No schemas found.");
|
|
90
99
|
}
|
|
91
|
-
// Compile regex once before the loop; escape special chars to handle schema names with dots/brackets
|
|
92
|
-
|
|
100
|
+
// Compile regex once before the loop; escape special chars to handle schema names with dots/brackets.
|
|
101
|
+
// Anchored so e.g. "User" doesn't also match "AdminUser"/"UserProfile".
|
|
102
|
+
const nameRegex = new RegExp("^" + StringUtils.escapeRegExp(name) + "$", "i");
|
|
93
103
|
for (const schemaName in spec.components.schemas) {
|
|
94
104
|
if (nameRegex.test(schemaName)) {
|
|
95
105
|
return spec.components.schemas[schemaName];
|
|
@@ -172,6 +182,10 @@ export class OASUtils {
|
|
|
172
182
|
// restrictions that a caller applies on a later call for the same `file` key. When a caller supplies either
|
|
173
183
|
// restriction, the other category is denied by default rather than left unchecked: e.g. a caller that only
|
|
174
184
|
// sets `allowedDirs` (believing `file` to be a local path) must not have a URL slip through ungated.
|
|
185
|
+
// `resolvedFile` (for a local path) is the realpath'd, already-validated path returned by
|
|
186
|
+
// `FileUtils.assertContained` - reading through it below (rather than the original `file`) closes the
|
|
187
|
+
// symlink-escape gap a purely lexical containment check would leave open.
|
|
188
|
+
let resolvedFile;
|
|
175
189
|
if (options?.allowedDirs || options?.allowedHosts) {
|
|
176
190
|
if (isUrl) {
|
|
177
191
|
const hostname = new URL(file).hostname;
|
|
@@ -183,23 +197,48 @@ export class OASUtils {
|
|
|
183
197
|
if (!options.allowedDirs) {
|
|
184
198
|
throw new Error(`File "${file}" is not within an allowed directory.`);
|
|
185
199
|
}
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
200
|
+
for (const dir of options.allowedDirs) {
|
|
201
|
+
try {
|
|
202
|
+
resolvedFile = await FileUtils.assertContained(dir, path.resolve(file));
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
// Not contained within this particular allowed directory - try the next one.
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (!resolvedFile) {
|
|
192
210
|
throw new Error(`File "${file}" is not within an allowed directory.`);
|
|
193
211
|
}
|
|
194
212
|
}
|
|
195
213
|
}
|
|
196
|
-
|
|
197
|
-
|
|
214
|
+
// Keyed by the resolved/validated path (when available) rather than the raw `file` argument, so two calls
|
|
215
|
+
// that pass the same relative `file` string but resolve it against different `allowedDirs` don't collide
|
|
216
|
+
// on the same cache entry and return each other's content.
|
|
217
|
+
const cacheKey = resolvedFile ?? file;
|
|
218
|
+
if (_specCache.has(cacheKey)) {
|
|
219
|
+
const cached = _specCache.get(cacheKey);
|
|
220
|
+
// Move to the end of the Map's iteration order so a frequently-reused spec isn't evicted ahead of
|
|
221
|
+
// one that's actually gone idle - Map.set() on an existing key doesn't reorder it by itself.
|
|
222
|
+
_specCache.delete(cacheKey);
|
|
223
|
+
_specCache.set(cacheKey, cached);
|
|
224
|
+
// Cloned before returning: callers routinely mutate a parsed OpenAPI spec in place (e.g. resolving
|
|
225
|
+
// `$ref`s), and returning the cached object directly would let that mutation silently corrupt every
|
|
226
|
+
// future cache hit for this same file/URL.
|
|
227
|
+
return structuredClone(cached);
|
|
198
228
|
}
|
|
199
229
|
let apiSpec = null;
|
|
200
|
-
|
|
230
|
+
const readFile = resolvedFile ?? file;
|
|
231
|
+
let fileExists = false;
|
|
232
|
+
try {
|
|
233
|
+
await fs.access(readFile);
|
|
234
|
+
fileExists = true;
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
fileExists = false;
|
|
238
|
+
}
|
|
239
|
+
if (fileExists) {
|
|
201
240
|
let fileType = path.extname(file);
|
|
202
|
-
let data = fs.
|
|
241
|
+
let data = await fs.readFile(readFile, "utf8");
|
|
203
242
|
if (fileType === ".yaml") {
|
|
204
243
|
logger.info("Loading YAML: " + file);
|
|
205
244
|
apiSpec = YAML.load(data);
|
|
@@ -214,7 +253,11 @@ export class OASUtils {
|
|
|
214
253
|
}
|
|
215
254
|
else if (_urlRegex.test(file)) {
|
|
216
255
|
const axios = new Axios({ baseURL: file });
|
|
217
|
-
|
|
256
|
+
// When `allowedHosts` is being enforced, redirects must not be followed automatically: axios's
|
|
257
|
+
// default behavior would otherwise let a request to an allow-listed host silently 30x to an
|
|
258
|
+
// arbitrary/internal one (e.g. cloud metadata endpoints), defeating the allow-list entirely. Disabling
|
|
259
|
+
// redirects makes axios reject on any 3xx response instead, surfacing as a normal request failure.
|
|
260
|
+
const response = await axios.get(file, options?.allowedHosts ? { maxRedirects: 0 } : undefined);
|
|
218
261
|
if (response && response.data) {
|
|
219
262
|
if (typeof response.data === "string") {
|
|
220
263
|
// Assume the file is a YAML first. If not we'll try JSON
|
|
@@ -240,9 +283,15 @@ export class OASUtils {
|
|
|
240
283
|
throw new Error("File not found: " + file);
|
|
241
284
|
}
|
|
242
285
|
if (apiSpec !== null) {
|
|
243
|
-
|
|
286
|
+
// Bound cache growth by evicting the oldest entry once at capacity (Map preserves insertion order).
|
|
287
|
+
if (_specCache.size >= _specCacheMaxSize) {
|
|
288
|
+
CacheUtils.evictOldest(_specCache);
|
|
289
|
+
}
|
|
290
|
+
_specCache.set(cacheKey, apiSpec);
|
|
244
291
|
}
|
|
245
|
-
|
|
292
|
+
// Cloned so the object backing the cache entry just stored above stays independent of whatever the
|
|
293
|
+
// caller does with the value they get back (see the cache-hit branch above for why that matters).
|
|
294
|
+
return structuredClone(apiSpec);
|
|
246
295
|
}
|
|
247
296
|
/** Clears the spec parse cache. Useful in tests to force re-loading of specs. */
|
|
248
297
|
static clearSpecCache() {
|
package/dist/lib/OASUtils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OASUtils.js","sourceRoot":"","sources":["../../src/OASUtils.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,kDAAkD;AAClD,+EAA+E;AAC/E,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"OASUtils.js","sourceRoot":"","sources":["../../src/OASUtils.ts"],"names":[],"mappings":"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"}
|