@orion-js/echoes 4.4.1 → 4.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/echo/index.d.ts +4 -4
- package/dist/errors.d.ts +26 -0
- package/dist/events/PulseManager.d.ts +2 -2
- package/dist/index.cjs +540 -188
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.js +515 -175
- package/dist/index.js.map +1 -1
- package/dist/request/getSignature.d.ts +1 -1
- package/dist/requestsHandler/index.d.ts +18 -1
- package/dist/runtime.d.ts +49 -0
- package/dist/schema.d.ts +42 -0
- package/dist/startService/KafkaManager.d.ts +2 -2
- package/dist/types.d.ts +48 -19
- package/package.json +8 -12
package/dist/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
// src/startService/index.ts
|
|
2
|
-
import { registerRoute } from "@orion-js/http";
|
|
3
|
-
|
|
4
1
|
// src/config.ts
|
|
5
2
|
var config = {};
|
|
6
3
|
var config_default = config;
|
|
7
4
|
|
|
8
|
-
// src/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
// src/publish/index.ts
|
|
6
|
+
async function publish(options) {
|
|
7
|
+
if (!config_default.eventBus) {
|
|
8
|
+
throw new Error("You must initialize echoes configuration to use publish");
|
|
9
|
+
}
|
|
10
|
+
return await config_default.eventBus.publish(options);
|
|
11
|
+
}
|
|
12
12
|
|
|
13
13
|
// src/echo/deserialize.ts
|
|
14
14
|
function deserialize_default(serializedJavascript) {
|
|
@@ -21,14 +21,426 @@ function deserialize_default(serializedJavascript) {
|
|
|
21
21
|
|
|
22
22
|
// src/publish/serialize.ts
|
|
23
23
|
import serialize from "serialize-javascript";
|
|
24
|
-
|
|
24
|
+
function clonePropertyDescriptor(descriptor, references) {
|
|
25
|
+
const clonedDescriptor = { ...descriptor, configurable: true };
|
|
26
|
+
if ("value" in clonedDescriptor) {
|
|
27
|
+
clonedDescriptor.value = cloneForSerialization(clonedDescriptor.value, references);
|
|
28
|
+
}
|
|
29
|
+
return clonedDescriptor;
|
|
30
|
+
}
|
|
31
|
+
function copyOwnProperties(source, target, references, shouldCopy = () => true) {
|
|
32
|
+
for (const key of Reflect.ownKeys(source)) {
|
|
33
|
+
if (!shouldCopy(key)) continue;
|
|
34
|
+
const descriptor = Object.getOwnPropertyDescriptor(source, key);
|
|
35
|
+
if (!descriptor) continue;
|
|
36
|
+
Object.defineProperty(target, key, clonePropertyDescriptor(descriptor, references));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function preserveCustomToJSON(source, target, references) {
|
|
40
|
+
const toJSON = source.toJSON;
|
|
41
|
+
if (typeof toJSON !== "function") return;
|
|
42
|
+
Object.defineProperty(target, "toJSON", {
|
|
43
|
+
configurable: true,
|
|
44
|
+
enumerable: false,
|
|
45
|
+
writable: true,
|
|
46
|
+
value(key) {
|
|
47
|
+
return cloneForSerialization(toJSON.call(source, key), references);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function cloneArrayBufferView(value, references) {
|
|
52
|
+
const sourceBuffer = value.buffer;
|
|
53
|
+
let clonedBuffer = references.get(sourceBuffer);
|
|
54
|
+
const shouldCopyBufferProperties = !clonedBuffer;
|
|
55
|
+
if (!clonedBuffer) {
|
|
56
|
+
clonedBuffer = sourceBuffer.slice(0);
|
|
57
|
+
references.set(sourceBuffer, clonedBuffer);
|
|
58
|
+
}
|
|
59
|
+
const clone = value instanceof DataView ? new DataView(clonedBuffer, value.byteOffset, value.byteLength) : new value.constructor(clonedBuffer, value.byteOffset, value.length);
|
|
60
|
+
references.set(value, clone);
|
|
61
|
+
if (shouldCopyBufferProperties) {
|
|
62
|
+
copyOwnProperties(sourceBuffer, clonedBuffer, references);
|
|
63
|
+
}
|
|
64
|
+
copyOwnProperties(value, clone, references, (key) => {
|
|
65
|
+
if (typeof key !== "string") return true;
|
|
66
|
+
const index = Number(key);
|
|
67
|
+
return !(Number.isInteger(index) && index >= 0 && index < value.length && String(index) === key);
|
|
68
|
+
});
|
|
69
|
+
return clone;
|
|
70
|
+
}
|
|
71
|
+
function cloneForSerialization(value, references = /* @__PURE__ */ new WeakMap()) {
|
|
72
|
+
if (value === null || typeof value !== "object" && typeof value !== "function") return value;
|
|
73
|
+
if (typeof value === "function") return value;
|
|
74
|
+
const source = value;
|
|
75
|
+
if (references.has(source)) return references.get(source);
|
|
76
|
+
if (value instanceof Date) {
|
|
77
|
+
const clone2 = new Date(value.getTime());
|
|
78
|
+
Object.setPrototypeOf(clone2, Object.getPrototypeOf(value));
|
|
79
|
+
references.set(source, clone2);
|
|
80
|
+
return clone2;
|
|
81
|
+
}
|
|
82
|
+
if (value instanceof RegExp) {
|
|
83
|
+
const clone2 = new RegExp(value.source, value.flags);
|
|
84
|
+
clone2.lastIndex = value.lastIndex;
|
|
85
|
+
Object.setPrototypeOf(clone2, Object.getPrototypeOf(value));
|
|
86
|
+
references.set(source, clone2);
|
|
87
|
+
return clone2;
|
|
88
|
+
}
|
|
89
|
+
if (value instanceof Map) {
|
|
90
|
+
const clone2 = /* @__PURE__ */ new Map();
|
|
91
|
+
references.set(source, clone2);
|
|
92
|
+
for (const [key, entry] of value) {
|
|
93
|
+
Map.prototype.set.call(
|
|
94
|
+
clone2,
|
|
95
|
+
cloneForSerialization(key, references),
|
|
96
|
+
cloneForSerialization(entry, references)
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
Object.setPrototypeOf(clone2, Object.getPrototypeOf(value));
|
|
100
|
+
return clone2;
|
|
101
|
+
}
|
|
102
|
+
if (value instanceof Set) {
|
|
103
|
+
const clone2 = /* @__PURE__ */ new Set();
|
|
104
|
+
references.set(source, clone2);
|
|
105
|
+
for (const entry of value) {
|
|
106
|
+
Set.prototype.add.call(clone2, cloneForSerialization(entry, references));
|
|
107
|
+
}
|
|
108
|
+
Object.setPrototypeOf(clone2, Object.getPrototypeOf(value));
|
|
109
|
+
return clone2;
|
|
110
|
+
}
|
|
111
|
+
if (value instanceof URL) {
|
|
112
|
+
const clone2 = new URL(value.toString());
|
|
113
|
+
Object.setPrototypeOf(clone2, Object.getPrototypeOf(value));
|
|
114
|
+
references.set(source, clone2);
|
|
115
|
+
return clone2;
|
|
116
|
+
}
|
|
117
|
+
if (Buffer.isBuffer(value)) {
|
|
118
|
+
const clone2 = Buffer.from(value);
|
|
119
|
+
references.set(source, clone2);
|
|
120
|
+
return clone2;
|
|
121
|
+
}
|
|
122
|
+
if (ArrayBuffer.isView(value)) {
|
|
123
|
+
return cloneArrayBufferView(value, references);
|
|
124
|
+
}
|
|
125
|
+
if (value instanceof ArrayBuffer || typeof SharedArrayBuffer !== "undefined" && value instanceof SharedArrayBuffer) {
|
|
126
|
+
const clone2 = value.slice(0);
|
|
127
|
+
references.set(source, clone2);
|
|
128
|
+
copyOwnProperties(value, clone2, references);
|
|
129
|
+
return clone2;
|
|
130
|
+
}
|
|
131
|
+
if (Array.isArray(value)) {
|
|
132
|
+
const clone2 = new Array(value.length);
|
|
133
|
+
Object.setPrototypeOf(clone2, Object.getPrototypeOf(value));
|
|
134
|
+
references.set(source, clone2);
|
|
135
|
+
copyOwnProperties(value, clone2, references, (key) => key !== "length");
|
|
136
|
+
return clone2;
|
|
137
|
+
}
|
|
138
|
+
const clone = Object.create(Object.getPrototypeOf(value));
|
|
139
|
+
references.set(source, clone);
|
|
140
|
+
copyOwnProperties(value, clone, references);
|
|
141
|
+
preserveCustomToJSON(source, clone, references);
|
|
142
|
+
return clone;
|
|
143
|
+
}
|
|
25
144
|
function serialize_default(data) {
|
|
26
|
-
const
|
|
27
|
-
const serialized = serialize(cloned, { ignoreFunction: true });
|
|
145
|
+
const serialized = serialize(cloneForSerialization(data), { ignoreFunction: true });
|
|
28
146
|
return serialized;
|
|
29
147
|
}
|
|
30
148
|
|
|
149
|
+
// src/runtime.ts
|
|
150
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
151
|
+
import { randomUUID } from "crypto";
|
|
152
|
+
|
|
153
|
+
// src/errors.ts
|
|
154
|
+
var EchoesUserError = class extends Error {
|
|
155
|
+
isEchoesError = true;
|
|
156
|
+
isOrionError = true;
|
|
157
|
+
isUserError = true;
|
|
158
|
+
code;
|
|
159
|
+
extra;
|
|
160
|
+
constructor(code, message, extra) {
|
|
161
|
+
if (!message) {
|
|
162
|
+
message = code;
|
|
163
|
+
code = "error";
|
|
164
|
+
}
|
|
165
|
+
super(message);
|
|
166
|
+
this.name = "EchoesUserError";
|
|
167
|
+
this.code = code;
|
|
168
|
+
this.extra = extra;
|
|
169
|
+
}
|
|
170
|
+
getInfo() {
|
|
171
|
+
return { error: this.code, message: this.message, extra: this.extra };
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
var EchoesValidationError = class extends Error {
|
|
175
|
+
isEchoesError = true;
|
|
176
|
+
isOrionError = true;
|
|
177
|
+
isValidationError = true;
|
|
178
|
+
code = "validationError";
|
|
179
|
+
validationErrors;
|
|
180
|
+
labels;
|
|
181
|
+
constructor(validationErrors, labels = {}) {
|
|
182
|
+
const printableErrors = Object.entries(validationErrors).map(([key, value]) => `${key}: ${value}`).join(", ");
|
|
183
|
+
super(`Validation Error: {${printableErrors}}`);
|
|
184
|
+
this.name = "EchoesValidationError";
|
|
185
|
+
this.validationErrors = validationErrors;
|
|
186
|
+
this.labels = Object.fromEntries(
|
|
187
|
+
Object.keys(validationErrors).filter((key) => labels[key]).map((key) => [key, labels[key]])
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
getInfo() {
|
|
191
|
+
return {
|
|
192
|
+
error: this.code,
|
|
193
|
+
message: "Validation Error",
|
|
194
|
+
validationErrors: this.validationErrors,
|
|
195
|
+
labels: this.labels
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
// src/runtime.ts
|
|
201
|
+
var defaultLogger = {
|
|
202
|
+
debug: (message, metadata) => metadata === void 0 ? console.debug(message) : console.debug(message, metadata),
|
|
203
|
+
info: (message, metadata) => metadata === void 0 ? console.info(message) : console.info(message, metadata),
|
|
204
|
+
warn: (message, metadata) => metadata === void 0 ? console.warn(message) : console.warn(message, metadata),
|
|
205
|
+
error: (message, metadata) => metadata === void 0 ? console.error(message) : console.error(message, metadata)
|
|
206
|
+
};
|
|
207
|
+
var runtime = {};
|
|
208
|
+
var contextStorage = new AsyncLocalStorage();
|
|
209
|
+
function configureEchoesRuntime(adapter) {
|
|
210
|
+
const previous = runtime;
|
|
211
|
+
runtime = { ...runtime, ...adapter };
|
|
212
|
+
return () => {
|
|
213
|
+
runtime = previous;
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
function getEchoesRuntime() {
|
|
217
|
+
return runtime;
|
|
218
|
+
}
|
|
219
|
+
function getEchoesLogger() {
|
|
220
|
+
return runtime.logger || defaultLogger;
|
|
221
|
+
}
|
|
222
|
+
function getEchoesContext() {
|
|
223
|
+
return contextStorage.getStore();
|
|
224
|
+
}
|
|
225
|
+
async function runWithEchoesContext(context, callback) {
|
|
226
|
+
const contextWithId = { contextId: context.contextId || randomUUID(), ...context };
|
|
227
|
+
return await contextStorage.run(contextWithId, async () => {
|
|
228
|
+
if (runtime.runWithContext) {
|
|
229
|
+
return await runtime.runWithContext(contextWithId, callback);
|
|
230
|
+
}
|
|
231
|
+
return await callback();
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
function isSimpleSchemaLike(schema) {
|
|
235
|
+
return !!schema && typeof schema === "object" && typeof schema.clean === "function" && typeof schema.validate === "function";
|
|
236
|
+
}
|
|
237
|
+
function cloneValue(value) {
|
|
238
|
+
if (value === null || typeof value !== "object") return value;
|
|
239
|
+
if (value instanceof Date) return new Date(value.getTime());
|
|
240
|
+
if (Buffer.isBuffer(value)) return Buffer.from(value);
|
|
241
|
+
if (Array.isArray(value)) return value.map(cloneValue);
|
|
242
|
+
const prototype = Object.getPrototypeOf(value);
|
|
243
|
+
if (prototype !== Object.prototype && prototype !== null) return value;
|
|
244
|
+
const result = {};
|
|
245
|
+
for (const [key, child] of Object.entries(value)) {
|
|
246
|
+
result[key] = cloneValue(child);
|
|
247
|
+
}
|
|
248
|
+
return result;
|
|
249
|
+
}
|
|
250
|
+
async function cleanSimpleSchema(schema, value) {
|
|
251
|
+
const cloned = cloneValue(value);
|
|
252
|
+
return await schema.clean(cloned, { mutate: false });
|
|
253
|
+
}
|
|
254
|
+
async function cleanEchoesSchema(schema, value) {
|
|
255
|
+
if (isSimpleSchemaLike(schema)) {
|
|
256
|
+
return await cleanSimpleSchema(schema, value);
|
|
257
|
+
}
|
|
258
|
+
if (runtime.schema) {
|
|
259
|
+
return await runtime.schema.clean(schema, value);
|
|
260
|
+
}
|
|
261
|
+
throw new Error(
|
|
262
|
+
"Echoes received a schema it cannot execute. Use a SimpleSchema-compatible object or configure an Echoes schema adapter."
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
async function parseEchoesSchema(schema, value) {
|
|
266
|
+
if (isSimpleSchemaLike(schema)) {
|
|
267
|
+
const cleaned = await cleanSimpleSchema(schema, value);
|
|
268
|
+
await schema.validate(cleaned);
|
|
269
|
+
return cleaned;
|
|
270
|
+
}
|
|
271
|
+
if (runtime.schema) {
|
|
272
|
+
return await runtime.schema.parse(schema, value);
|
|
273
|
+
}
|
|
274
|
+
throw new Error(
|
|
275
|
+
"Echoes received a schema it cannot execute. Use a SimpleSchema-compatible object or configure an Echoes schema adapter."
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
function createEchoesValidationError(info) {
|
|
279
|
+
return runtime.createValidationError ? runtime.createValidationError(info) : new EchoesValidationError(info.validationErrors || {}, info.labels);
|
|
280
|
+
}
|
|
281
|
+
function createEchoesUserError(info) {
|
|
282
|
+
return runtime.createUserError ? runtime.createUserError(info) : new EchoesUserError(info.error, info.message, info.extra);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// src/request/getSignature.ts
|
|
286
|
+
import { createHmac } from "crypto";
|
|
287
|
+
|
|
288
|
+
// src/request/getPassword.ts
|
|
289
|
+
function getEchoesPassword() {
|
|
290
|
+
var _a, _b;
|
|
291
|
+
const secret = ((_b = (_a = config_default) == null ? void 0 : _a.requests) == null ? void 0 : _b.key) || process.env.echoes_password || process.env.ECHOES_PASSWORD;
|
|
292
|
+
if (!secret) {
|
|
293
|
+
getEchoesLogger().warn(
|
|
294
|
+
'Warning: no secret key found for echoes requests. Init echoes or set the env var "echoes_password" or process.env.ECHOES_PASSWORD'
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
return secret;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// src/request/getSignature.ts
|
|
301
|
+
function getSignature_default(_body) {
|
|
302
|
+
const password = getEchoesPassword();
|
|
303
|
+
return createHmac("sha1", password || "").update("").digest("hex");
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// src/request/getURL.ts
|
|
307
|
+
function getURL_default(serviceName) {
|
|
308
|
+
var _a, _b, _c;
|
|
309
|
+
if (serviceName.startsWith("http")) return serviceName;
|
|
310
|
+
const url = (_c = (_b = (_a = config_default) == null ? void 0 : _a.requests) == null ? void 0 : _b.services) == null ? void 0 : _c[serviceName];
|
|
311
|
+
if (!url) {
|
|
312
|
+
throw new Error(`No URL found in echoes config for service ${serviceName}`);
|
|
313
|
+
}
|
|
314
|
+
return url;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// src/request/makeRequest.ts
|
|
318
|
+
import http from "http";
|
|
319
|
+
import https from "https";
|
|
320
|
+
async function executeWithRetries(callback, retries = 0, delayMs = 200) {
|
|
321
|
+
try {
|
|
322
|
+
return await callback();
|
|
323
|
+
} catch (error) {
|
|
324
|
+
if (retries <= 0) throw error;
|
|
325
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
326
|
+
return await executeWithRetries(callback, retries - 1, delayMs);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
async function postJSON(urlValue, data, timeout, redirectsRemaining = 5) {
|
|
330
|
+
const url = new URL(urlValue);
|
|
331
|
+
const body = JSON.stringify(data);
|
|
332
|
+
const transport = url.protocol === "https:" ? https : http;
|
|
333
|
+
return await new Promise((resolve, reject) => {
|
|
334
|
+
const request2 = transport.request(
|
|
335
|
+
url,
|
|
336
|
+
{
|
|
337
|
+
method: "POST",
|
|
338
|
+
headers: {
|
|
339
|
+
"User-Agent": "Orionjs-Echoes/1.1",
|
|
340
|
+
"Content-Type": "application/json",
|
|
341
|
+
"Content-Length": Buffer.byteLength(body)
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
(response) => {
|
|
345
|
+
const chunks = [];
|
|
346
|
+
const statusCode = response.statusCode || 0;
|
|
347
|
+
if (statusCode >= 300 && statusCode < 400 && response.headers.location) {
|
|
348
|
+
response.resume();
|
|
349
|
+
if (redirectsRemaining <= 0) {
|
|
350
|
+
reject(new Error("Echoes request exceeded the redirect limit"));
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
resolve(
|
|
354
|
+
postJSON(
|
|
355
|
+
new URL(response.headers.location, url).toString(),
|
|
356
|
+
data,
|
|
357
|
+
timeout,
|
|
358
|
+
redirectsRemaining - 1
|
|
359
|
+
)
|
|
360
|
+
);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
response.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
|
364
|
+
response.on("end", () => {
|
|
365
|
+
try {
|
|
366
|
+
const responseBody = Buffer.concat(chunks).toString("utf8");
|
|
367
|
+
if (statusCode < 200 || statusCode >= 300) {
|
|
368
|
+
reject(new Error(`Request failed with status code ${statusCode}`));
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
resolve({
|
|
372
|
+
statusCode,
|
|
373
|
+
data: responseBody ? JSON.parse(responseBody) : {}
|
|
374
|
+
});
|
|
375
|
+
} catch (error) {
|
|
376
|
+
reject(error);
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
);
|
|
381
|
+
request2.on("error", reject);
|
|
382
|
+
if (timeout) {
|
|
383
|
+
request2.setTimeout(timeout, () => {
|
|
384
|
+
request2.destroy(new Error(`Echoes request timed out after ${timeout}ms`));
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
request2.end(body);
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
var makeRequest = async (options) => {
|
|
391
|
+
return await executeWithRetries(
|
|
392
|
+
() => postJSON(options.url, options.data, options.timeout),
|
|
393
|
+
options.retries || 0
|
|
394
|
+
);
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
// src/request/index.ts
|
|
398
|
+
async function request(options) {
|
|
399
|
+
var _a, _b;
|
|
400
|
+
const { method, service, params } = options;
|
|
401
|
+
const serializedParams = serialize_default(params);
|
|
402
|
+
const date = /* @__PURE__ */ new Date();
|
|
403
|
+
const body = { method, service, serializedParams, date };
|
|
404
|
+
const signature = getSignature_default(body);
|
|
405
|
+
try {
|
|
406
|
+
const requestMaker = ((_b = (_a = config_default) == null ? void 0 : _a.requests) == null ? void 0 : _b.makeRequest) || makeRequest;
|
|
407
|
+
const requestOptions = {
|
|
408
|
+
url: getURL_default(service),
|
|
409
|
+
retries: options.retries,
|
|
410
|
+
timeout: options.timeout,
|
|
411
|
+
data: {
|
|
412
|
+
body,
|
|
413
|
+
signature
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
const result = await requestMaker(requestOptions);
|
|
417
|
+
if (result.statusCode !== 200) {
|
|
418
|
+
throw new Error(`Wrong status code ${result.statusCode}`);
|
|
419
|
+
}
|
|
420
|
+
const data = result.data;
|
|
421
|
+
if (data.error) {
|
|
422
|
+
const info = data.errorInfo;
|
|
423
|
+
if (info) {
|
|
424
|
+
if (data.isValidationError) {
|
|
425
|
+
throw createEchoesValidationError(info);
|
|
426
|
+
}
|
|
427
|
+
if (data.isUserError) {
|
|
428
|
+
throw createEchoesUserError(info);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
throw new Error(`${data.error}`);
|
|
432
|
+
}
|
|
433
|
+
const response = deserialize_default(data.result);
|
|
434
|
+
return response;
|
|
435
|
+
} catch (error) {
|
|
436
|
+
const caught = error;
|
|
437
|
+
if (caught.isOrionError || caught.isEchoesError) throw caught;
|
|
438
|
+
throw new Error(`Echoes request network error calling ${service}/${method}: ${caught.message}`);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
31
442
|
// src/startService/KafkaManager.ts
|
|
443
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
32
444
|
var HEARTBEAT_INTERVAL_SECONDS = 5;
|
|
33
445
|
var CHECK_JOIN_CONSUMER_INTERVAL_SECONDS = 30;
|
|
34
446
|
var DEFAULT_PARTITIONS_CONSUMED_CONCURRENTLY = 4;
|
|
@@ -46,11 +458,21 @@ var KafkaManager = class {
|
|
|
46
458
|
producerConnected = false;
|
|
47
459
|
interval;
|
|
48
460
|
constructor(options) {
|
|
49
|
-
this.kafka = new Kafka(options.client);
|
|
50
461
|
this.options = options;
|
|
51
462
|
}
|
|
52
463
|
async start(options) {
|
|
53
464
|
var _a;
|
|
465
|
+
let kafkaModule;
|
|
466
|
+
try {
|
|
467
|
+
kafkaModule = await import("kafkajs");
|
|
468
|
+
} catch (error) {
|
|
469
|
+
const wrapped = new Error(
|
|
470
|
+
"Echoes Kafka transport requires kafkajs to be installed in the application"
|
|
471
|
+
);
|
|
472
|
+
wrapped.cause = error;
|
|
473
|
+
throw wrapped;
|
|
474
|
+
}
|
|
475
|
+
this.kafka = new kafkaModule.Kafka(this.options.client);
|
|
54
476
|
this.onEvent = options.onEvent;
|
|
55
477
|
this.subscriptions = new Map(
|
|
56
478
|
options.subscriptions.map((subscription) => [subscription.topic, subscription])
|
|
@@ -68,7 +490,7 @@ var KafkaManager = class {
|
|
|
68
490
|
this.consumer = this.kafka.consumer(this.options.consumer);
|
|
69
491
|
this.consumerStarted = await this.conditionalStart();
|
|
70
492
|
if (this.consumerStarted) return;
|
|
71
|
-
|
|
493
|
+
getEchoesLogger().info("Echoes: Delaying consumer group join, waiting for conditions to be met");
|
|
72
494
|
this.interval = setInterval(async () => {
|
|
73
495
|
this.consumerStarted = await this.conditionalStart();
|
|
74
496
|
if (this.consumerStarted) clearInterval(this.interval);
|
|
@@ -86,7 +508,7 @@ var KafkaManager = class {
|
|
|
86
508
|
{
|
|
87
509
|
value: serialize_default({ params: options.params }),
|
|
88
510
|
headers: {
|
|
89
|
-
"echoes-event-id":
|
|
511
|
+
"echoes-event-id": randomUUID2()
|
|
90
512
|
}
|
|
91
513
|
}
|
|
92
514
|
]
|
|
@@ -94,6 +516,7 @@ var KafkaManager = class {
|
|
|
94
516
|
}
|
|
95
517
|
async close() {
|
|
96
518
|
var _a, _b;
|
|
519
|
+
const logger = getEchoesLogger();
|
|
97
520
|
logger.warn("Echoes: Stopping Kafka transport");
|
|
98
521
|
if (this.interval) clearInterval(this.interval);
|
|
99
522
|
await Promise.all([
|
|
@@ -104,6 +527,7 @@ var KafkaManager = class {
|
|
|
104
527
|
this.producerConnected = false;
|
|
105
528
|
}
|
|
106
529
|
async checkJoinConsumerGroupConditions() {
|
|
530
|
+
const logger = getEchoesLogger();
|
|
107
531
|
const admin = this.kafka.admin();
|
|
108
532
|
try {
|
|
109
533
|
await admin.connect();
|
|
@@ -156,6 +580,7 @@ var KafkaManager = class {
|
|
|
156
580
|
return false;
|
|
157
581
|
}
|
|
158
582
|
async handleMessage(params) {
|
|
583
|
+
const logger = getEchoesLogger();
|
|
159
584
|
const subscription = this.subscriptions.get(params.topic);
|
|
160
585
|
if (!subscription) {
|
|
161
586
|
logger.warn(`Echoes: Received a message for an unknown topic: ${params.topic}, ignoring it`);
|
|
@@ -193,6 +618,9 @@ var KafkaManager = class {
|
|
|
193
618
|
createReceivedEvent(params) {
|
|
194
619
|
var _a, _b, _c, _d;
|
|
195
620
|
const { message, topic, partition } = params;
|
|
621
|
+
if (!message.value) {
|
|
622
|
+
throw new Error(`Echoes received an empty Kafka message for ${topic}`);
|
|
623
|
+
}
|
|
196
624
|
const data = deserialize_default(message.value.toString());
|
|
197
625
|
const retries = Number.parseInt(((_b = (_a = message.headers) == null ? void 0 : _a.retries) == null ? void 0 : _b.toString()) || "0", 10);
|
|
198
626
|
const timestamp = Number.parseInt(message.timestamp || "", 10);
|
|
@@ -209,6 +637,7 @@ var KafkaManager = class {
|
|
|
209
637
|
}
|
|
210
638
|
async handleRetries(subscription, params, error) {
|
|
211
639
|
var _a, _b;
|
|
640
|
+
const logger = getEchoesLogger();
|
|
212
641
|
const { message, topic } = params;
|
|
213
642
|
const retries = Number.parseInt(((_b = (_a = message == null ? void 0 : message.headers) == null ? void 0 : _a.retries) == null ? void 0 : _b.toString()) || "0", 10);
|
|
214
643
|
if (subscription.attemptsBeforeDeadLetter === void 0 || subscription.attemptsBeforeDeadLetter === null) {
|
|
@@ -217,6 +646,7 @@ var KafkaManager = class {
|
|
|
217
646
|
const maxRetries = subscription.attemptsBeforeDeadLetter || 0;
|
|
218
647
|
const exceededMaxRetries = retries >= maxRetries;
|
|
219
648
|
const nextTopic = exceededMaxRetries ? `DLQ-${topic}` : topic;
|
|
649
|
+
if (!message.value) throw error;
|
|
220
650
|
await this.producer.send({
|
|
221
651
|
topic: nextTopic,
|
|
222
652
|
messages: [
|
|
@@ -243,7 +673,6 @@ var KafkaManager = class {
|
|
|
243
673
|
var KafkaManager_default = KafkaManager;
|
|
244
674
|
|
|
245
675
|
// src/events/EventBus.ts
|
|
246
|
-
import { logger as logger2 } from "@orion-js/logger";
|
|
247
676
|
var EventBus = class {
|
|
248
677
|
echoes;
|
|
249
678
|
transports;
|
|
@@ -303,9 +732,10 @@ var EventBus = class {
|
|
|
303
732
|
await Promise.all(this.startedTransports.map((transport) => transport.close()));
|
|
304
733
|
}
|
|
305
734
|
async handleEvent(event) {
|
|
735
|
+
const logger = getEchoesLogger();
|
|
306
736
|
const echo2 = this.echoes[event.topic];
|
|
307
737
|
if (!echo2 || echo2.type !== "event") {
|
|
308
|
-
|
|
738
|
+
logger.warn(`Echoes: Received a message for an unknown topic: ${event.topic}, ignoring it`);
|
|
309
739
|
return;
|
|
310
740
|
}
|
|
311
741
|
if (event.transport === "kafka" && event.context) {
|
|
@@ -433,6 +863,14 @@ function createEventBus(options) {
|
|
|
433
863
|
});
|
|
434
864
|
}
|
|
435
865
|
|
|
866
|
+
// src/requestsHandler/checkSignature.ts
|
|
867
|
+
function checkSignature_default(body, signature) {
|
|
868
|
+
const generatedSignature = getSignature_default(body);
|
|
869
|
+
if (generatedSignature !== signature) {
|
|
870
|
+
throw new Error("Echoes invalid signature");
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
|
|
436
874
|
// src/requestsHandler/getEcho.ts
|
|
437
875
|
function getEcho_default(method) {
|
|
438
876
|
const echo2 = config_default.echoes[method];
|
|
@@ -445,72 +883,29 @@ function getEcho_default(method) {
|
|
|
445
883
|
return echo2;
|
|
446
884
|
}
|
|
447
885
|
|
|
448
|
-
// src/request/getSignature.ts
|
|
449
|
-
import JSSHA from "jssha";
|
|
450
|
-
|
|
451
|
-
// src/request/getPassword.ts
|
|
452
|
-
import { logger as logger3 } from "@orion-js/logger";
|
|
453
|
-
import { internalGetEnv } from "@orion-js/env";
|
|
454
|
-
function getEchoesPassword() {
|
|
455
|
-
var _a, _b;
|
|
456
|
-
const secret = ((_b = (_a = config_default) == null ? void 0 : _a.requests) == null ? void 0 : _b.key) || internalGetEnv("echoes_password", "ECHOES_PASSWORD");
|
|
457
|
-
if (!secret) {
|
|
458
|
-
logger3.warn(
|
|
459
|
-
'Warning: no secret key found for echoes requests. Init echoes or set the env var "echoes_password" or process.env.ECHOES_PASSWORD'
|
|
460
|
-
);
|
|
461
|
-
}
|
|
462
|
-
return secret;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
// src/request/getSignature.ts
|
|
466
|
-
function getSignature_default(body) {
|
|
467
|
-
const password = getEchoesPassword();
|
|
468
|
-
const shaObj = new JSSHA("SHA-1", "TEXT");
|
|
469
|
-
shaObj.setHMACKey(password, "TEXT");
|
|
470
|
-
shaObj.update(body);
|
|
471
|
-
return shaObj.getHMAC("HEX");
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
// src/requestsHandler/checkSignature.ts
|
|
475
|
-
function checkSignature_default(body, signature) {
|
|
476
|
-
const generatedSignature = getSignature_default(body);
|
|
477
|
-
if (generatedSignature !== signature) {
|
|
478
|
-
throw new Error("Echoes invalid signature");
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
886
|
// src/requestsHandler/index.ts
|
|
483
|
-
|
|
484
|
-
var requestsHandler_default = (options) => route({
|
|
887
|
+
var requestsHandler_default = (options) => ({
|
|
485
888
|
method: "post",
|
|
486
889
|
path: options.requests.handlerPath || "/echoes-services",
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
limit: "10mb"
|
|
490
|
-
},
|
|
491
|
-
async resolve(req) {
|
|
890
|
+
bodyLimit: "10mb",
|
|
891
|
+
async handle(requestBody) {
|
|
492
892
|
try {
|
|
493
|
-
const { body, signature } =
|
|
893
|
+
const { body, signature } = requestBody;
|
|
494
894
|
checkSignature_default(body, signature);
|
|
495
895
|
const { method, serializedParams } = body;
|
|
496
896
|
const echo2 = getEcho_default(method);
|
|
497
897
|
const result = await echo2.onRequest(serializedParams);
|
|
498
|
-
return {
|
|
499
|
-
body: {
|
|
500
|
-
result: serialize_default(result)
|
|
501
|
-
}
|
|
502
|
-
};
|
|
898
|
+
return { result: serialize_default(result) };
|
|
503
899
|
} catch (error) {
|
|
504
|
-
|
|
505
|
-
|
|
900
|
+
const caught = error;
|
|
901
|
+
if (!caught.getInfo) {
|
|
902
|
+
getEchoesLogger().error("Error at echo requests handler:", { error: caught });
|
|
506
903
|
}
|
|
507
904
|
return {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
isUserError: !!error.isUserError
|
|
513
|
-
}
|
|
905
|
+
error: caught.message,
|
|
906
|
+
errorInfo: caught.getInfo ? caught.getInfo() : null,
|
|
907
|
+
isValidationError: !!caught.isValidationError,
|
|
908
|
+
isUserError: !!caught.isUserError
|
|
514
909
|
};
|
|
515
910
|
}
|
|
516
911
|
}
|
|
@@ -522,7 +917,13 @@ async function startService(options) {
|
|
|
522
917
|
config_default.echoes = options.echoes;
|
|
523
918
|
if (options.requests) {
|
|
524
919
|
config_default.requests = options.requests;
|
|
525
|
-
|
|
920
|
+
const registerHandler = options.requests.registerHandler || getEchoesRuntime().registerRequestHandler;
|
|
921
|
+
if (!registerHandler) {
|
|
922
|
+
throw new Error(
|
|
923
|
+
"Echoes requests require requests.registerHandler in standalone servers. Orion applications can import @orion-js/echoes-orion once during startup."
|
|
924
|
+
);
|
|
925
|
+
}
|
|
926
|
+
await registerHandler(requestsHandler_default(options));
|
|
526
927
|
}
|
|
527
928
|
const nextEventBus = createEventBus(options);
|
|
528
929
|
if (nextEventBus) {
|
|
@@ -533,115 +934,22 @@ async function startService(options) {
|
|
|
533
934
|
}
|
|
534
935
|
async function stopService() {
|
|
535
936
|
if (eventBus) {
|
|
536
|
-
|
|
937
|
+
const logger = getEchoesLogger();
|
|
938
|
+
logger.info("Stopping Echoes...");
|
|
537
939
|
await eventBus.close();
|
|
538
940
|
eventBus = null;
|
|
539
941
|
config_default.eventBus = void 0;
|
|
540
|
-
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
// src/publish/index.ts
|
|
545
|
-
async function publish(options) {
|
|
546
|
-
if (!config_default.eventBus) {
|
|
547
|
-
throw new Error("You must initialize echoes configuration to use publish");
|
|
548
|
-
}
|
|
549
|
-
return await config_default.eventBus.publish(options);
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
// src/request/getURL.ts
|
|
553
|
-
function getURL_default(serviceName) {
|
|
554
|
-
var _a, _b;
|
|
555
|
-
if (serviceName.startsWith("http")) return serviceName;
|
|
556
|
-
const url = (_b = (_a = config_default) == null ? void 0 : _a.requests) == null ? void 0 : _b.services[serviceName];
|
|
557
|
-
if (!url) {
|
|
558
|
-
throw new Error(`No URL found in echoes config for service ${serviceName}`);
|
|
559
|
-
}
|
|
560
|
-
return url;
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
// src/request/makeRequest.ts
|
|
564
|
-
import axios from "axios";
|
|
565
|
-
import { executeWithRetries } from "@orion-js/helpers";
|
|
566
|
-
var makeRequest = async (options) => {
|
|
567
|
-
const result = await executeWithRetries(
|
|
568
|
-
async () => {
|
|
569
|
-
return await axios({
|
|
570
|
-
method: "post",
|
|
571
|
-
url: options.url,
|
|
572
|
-
timeout: options.timeout,
|
|
573
|
-
headers: {
|
|
574
|
-
"User-Agent": "Orionjs-Echoes/1.1"
|
|
575
|
-
},
|
|
576
|
-
data: options.data
|
|
577
|
-
});
|
|
578
|
-
},
|
|
579
|
-
options.retries,
|
|
580
|
-
200
|
|
581
|
-
);
|
|
582
|
-
return {
|
|
583
|
-
data: result.data,
|
|
584
|
-
statusCode: result.status
|
|
585
|
-
};
|
|
586
|
-
};
|
|
587
|
-
|
|
588
|
-
// src/request/index.ts
|
|
589
|
-
import { ValidationError } from "@orion-js/schema";
|
|
590
|
-
import { UserError } from "@orion-js/helpers";
|
|
591
|
-
async function request(options) {
|
|
592
|
-
var _a, _b;
|
|
593
|
-
const { method, service, params } = options;
|
|
594
|
-
const serializedParams = serialize_default(params);
|
|
595
|
-
const date = /* @__PURE__ */ new Date();
|
|
596
|
-
const body = { method, service, serializedParams, date };
|
|
597
|
-
const signature = getSignature_default(body);
|
|
598
|
-
try {
|
|
599
|
-
const requestMaker = ((_b = (_a = config_default) == null ? void 0 : _a.requests) == null ? void 0 : _b.makeRequest) || makeRequest;
|
|
600
|
-
const requestOptions = {
|
|
601
|
-
url: getURL_default(service),
|
|
602
|
-
retries: options.retries,
|
|
603
|
-
timeout: options.timeout,
|
|
604
|
-
data: {
|
|
605
|
-
body,
|
|
606
|
-
signature
|
|
607
|
-
}
|
|
608
|
-
};
|
|
609
|
-
const result = await requestMaker(requestOptions);
|
|
610
|
-
if (result.statusCode !== 200) {
|
|
611
|
-
throw new Error(`Wrong status code ${result.statusCode}`);
|
|
612
|
-
}
|
|
613
|
-
const data = result.data;
|
|
614
|
-
if (data.error) {
|
|
615
|
-
const info = data.errorInfo;
|
|
616
|
-
if (info) {
|
|
617
|
-
if (data.isValidationError) {
|
|
618
|
-
throw new ValidationError(info.validationErrors);
|
|
619
|
-
}
|
|
620
|
-
if (data.isUserError) {
|
|
621
|
-
throw new UserError(info.error, info.message, info.extra);
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
throw new Error(`${data.error}`);
|
|
625
|
-
}
|
|
626
|
-
const response = deserialize_default(data.result);
|
|
627
|
-
return response;
|
|
628
|
-
} catch (error) {
|
|
629
|
-
if (error.isOrionError) throw error;
|
|
630
|
-
throw new Error(`Echoes request network error calling ${service}/${method}: ${error.message}`);
|
|
942
|
+
logger.info("Echoes stopped");
|
|
631
943
|
}
|
|
632
944
|
}
|
|
633
945
|
|
|
634
|
-
// src/service/index.ts
|
|
635
|
-
import { runWithOrionAsyncContext } from "@orion-js/logger";
|
|
636
|
-
|
|
637
946
|
// src/echo/index.ts
|
|
638
|
-
import { clean, cleanAndValidate } from "@orion-js/schema";
|
|
639
947
|
var echo = function createNewEcho(options) {
|
|
640
948
|
const resolve = async (params, context) => {
|
|
641
|
-
const cleaned = options.params ? await
|
|
949
|
+
const cleaned = options.params ? await parseEchoesSchema(options.params, params) : params ?? {};
|
|
642
950
|
const result = await options.resolve(cleaned, context);
|
|
643
951
|
if (options.returns) {
|
|
644
|
-
return await
|
|
952
|
+
return await cleanEchoesSchema(options.returns, result);
|
|
645
953
|
}
|
|
646
954
|
return result;
|
|
647
955
|
};
|
|
@@ -668,6 +976,9 @@ var echo = function createNewEcho(options) {
|
|
|
668
976
|
onMessage: async (messageData) => {
|
|
669
977
|
var _a, _b, _c, _d;
|
|
670
978
|
const { message } = messageData;
|
|
979
|
+
if (!message.value) {
|
|
980
|
+
throw new Error(`Echoes received an empty Kafka message for ${messageData.topic}`);
|
|
981
|
+
}
|
|
671
982
|
const data = deserialize_default(message.value.toString());
|
|
672
983
|
const retries = Number.parseInt(((_b = (_a = message.headers) == null ? void 0 : _a.retries) == null ? void 0 : _b.toString()) || "0", 10);
|
|
673
984
|
const timestamp = Number(message.timestamp);
|
|
@@ -698,15 +1009,21 @@ function createEchoEvent(options) {
|
|
|
698
1009
|
return echo({ ...options, type: "event" });
|
|
699
1010
|
}
|
|
700
1011
|
|
|
1012
|
+
// src/schema.ts
|
|
1013
|
+
function typedEchoesSchema(schema) {
|
|
1014
|
+
return schema;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
701
1017
|
// src/service/index.ts
|
|
702
|
-
import { getInstance, Service } from "@orion-js/services";
|
|
703
1018
|
var serviceMetadata = /* @__PURE__ */ new WeakMap();
|
|
704
1019
|
var echoesMetadata = /* @__PURE__ */ new WeakMap();
|
|
705
1020
|
var echoEntriesByClass = /* @__PURE__ */ new Map();
|
|
1021
|
+
var standaloneInstances = /* @__PURE__ */ new WeakMap();
|
|
706
1022
|
var pendingEchoEntries = {};
|
|
707
1023
|
function Echoes() {
|
|
708
1024
|
return (target, context) => {
|
|
709
|
-
|
|
1025
|
+
var _a, _b;
|
|
1026
|
+
(_b = (_a = getEchoesRuntime()).decorateService) == null ? void 0 : _b.call(_a, target, context);
|
|
710
1027
|
serviceMetadata.set(target, { _serviceType: "echoes" });
|
|
711
1028
|
if (Object.keys(pendingEchoEntries).length > 0) {
|
|
712
1029
|
echoEntriesByClass.set(target, pendingEchoEntries);
|
|
@@ -723,7 +1040,7 @@ function EchoEvent(options = {}) {
|
|
|
723
1040
|
return createEchoEvent({
|
|
724
1041
|
...options,
|
|
725
1042
|
resolve: async (params, contextData) => {
|
|
726
|
-
return await
|
|
1043
|
+
return await runWithEchoesContext(
|
|
727
1044
|
{
|
|
728
1045
|
controllerType: "echo",
|
|
729
1046
|
echoName: propertyKey,
|
|
@@ -752,7 +1069,7 @@ function EchoRequest(options = {}) {
|
|
|
752
1069
|
return createEchoRequest({
|
|
753
1070
|
...options,
|
|
754
1071
|
resolve: async (params, contextData) => {
|
|
755
|
-
return await
|
|
1072
|
+
return await runWithEchoesContext(
|
|
756
1073
|
{
|
|
757
1074
|
controllerType: "echo",
|
|
758
1075
|
echoName: propertyKey,
|
|
@@ -782,7 +1099,18 @@ function initializeEchoesIfNeeded(instance) {
|
|
|
782
1099
|
echoesMetadata.set(instance, echoes);
|
|
783
1100
|
}
|
|
784
1101
|
function getServiceEchoes(target) {
|
|
785
|
-
|
|
1102
|
+
let instance;
|
|
1103
|
+
if (typeof target !== "function") {
|
|
1104
|
+
instance = target;
|
|
1105
|
+
} else if (getEchoesRuntime().getInstance) {
|
|
1106
|
+
instance = getEchoesRuntime().getInstance(target);
|
|
1107
|
+
} else {
|
|
1108
|
+
instance = standaloneInstances.get(target);
|
|
1109
|
+
if (!instance) {
|
|
1110
|
+
instance = new target();
|
|
1111
|
+
standaloneInstances.set(target, instance);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
786
1114
|
if (!serviceMetadata.has(instance.constructor)) {
|
|
787
1115
|
throw new Error("You must pass a class decorated with @Echoes to getServiceEchoes");
|
|
788
1116
|
}
|
|
@@ -798,13 +1126,25 @@ export {
|
|
|
798
1126
|
EchoEvent,
|
|
799
1127
|
EchoRequest,
|
|
800
1128
|
Echoes,
|
|
1129
|
+
EchoesUserError,
|
|
1130
|
+
EchoesValidationError,
|
|
1131
|
+
cleanEchoesSchema,
|
|
1132
|
+
configureEchoesRuntime,
|
|
801
1133
|
createEchoEvent,
|
|
802
1134
|
createEchoRequest,
|
|
1135
|
+
createEchoesUserError,
|
|
1136
|
+
createEchoesValidationError,
|
|
803
1137
|
echo,
|
|
1138
|
+
getEchoesContext,
|
|
1139
|
+
getEchoesLogger,
|
|
1140
|
+
getEchoesRuntime,
|
|
804
1141
|
getServiceEchoes,
|
|
1142
|
+
parseEchoesSchema,
|
|
805
1143
|
publish,
|
|
806
1144
|
request,
|
|
1145
|
+
runWithEchoesContext,
|
|
807
1146
|
startService,
|
|
808
|
-
stopService
|
|
1147
|
+
stopService,
|
|
1148
|
+
typedEchoesSchema
|
|
809
1149
|
};
|
|
810
1150
|
//# sourceMappingURL=index.js.map
|