@athosjs/pro 0.1.4-alpha
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 +518 -0
- package/bootstrap.cjs +107 -0
- package/bootstrap.cjs.map +1 -0
- package/bootstrap.d.ts +8 -0
- package/bootstrap.d.ts.map +1 -0
- package/bootstrap.js +103 -0
- package/bootstrap.js.map +1 -0
- package/core/athos-application.cjs +362 -0
- package/core/athos-application.cjs.map +1 -0
- package/core/athos-application.d.ts +17 -0
- package/core/athos-application.d.ts.map +1 -0
- package/core/athos-application.js +357 -0
- package/core/athos-application.js.map +1 -0
- package/core/discovery.cjs +47 -0
- package/core/discovery.cjs.map +1 -0
- package/core/discovery.d.ts +6 -0
- package/core/discovery.d.ts.map +1 -0
- package/core/discovery.js +45 -0
- package/core/discovery.js.map +1 -0
- package/core/i18n.cjs +12 -0
- package/core/i18n.cjs.map +1 -0
- package/core/i18n.d.ts +4 -0
- package/core/i18n.d.ts.map +1 -0
- package/core/i18n.js +10 -0
- package/core/i18n.js.map +1 -0
- package/core/pipeline.cjs +251 -0
- package/core/pipeline.cjs.map +1 -0
- package/core/pipeline.d.ts +11 -0
- package/core/pipeline.d.ts.map +1 -0
- package/core/pipeline.js +246 -0
- package/core/pipeline.js.map +1 -0
- package/core/router-initializer.cjs +158 -0
- package/core/router-initializer.cjs.map +1 -0
- package/core/router-initializer.d.ts +26 -0
- package/core/router-initializer.d.ts.map +1 -0
- package/core/router-initializer.js +156 -0
- package/core/router-initializer.js.map +1 -0
- package/core/scanner.cjs +141 -0
- package/core/scanner.cjs.map +1 -0
- package/core/scanner.d.ts +7 -0
- package/core/scanner.d.ts.map +1 -0
- package/core/scanner.js +139 -0
- package/core/scanner.js.map +1 -0
- package/experimental.cjs +23 -0
- package/experimental.cjs.map +1 -0
- package/experimental.d.ts +9 -0
- package/experimental.d.ts.map +1 -0
- package/experimental.js +5 -0
- package/experimental.js.map +1 -0
- package/functions.cjs +67 -0
- package/functions.cjs.map +1 -0
- package/functions.d.ts +10 -0
- package/functions.d.ts.map +1 -0
- package/functions.js +62 -0
- package/functions.js.map +1 -0
- package/http-profile.cjs +48 -0
- package/http-profile.cjs.map +1 -0
- package/http-profile.d.ts +6 -0
- package/http-profile.d.ts.map +1 -0
- package/http-profile.js +44 -0
- package/http-profile.js.map +1 -0
- package/index.cjs +281 -0
- package/index.cjs.map +1 -0
- package/index.d.ts +23 -0
- package/index.d.ts.map +1 -0
- package/index.js +23 -0
- package/index.js.map +1 -0
- package/observability.cjs +191 -0
- package/observability.cjs.map +1 -0
- package/observability.d.ts +8 -0
- package/observability.d.ts.map +1 -0
- package/observability.js +188 -0
- package/observability.js.map +1 -0
- package/package.json +42 -0
- package/policies.cjs +30 -0
- package/policies.cjs.map +1 -0
- package/policies.d.ts +21 -0
- package/policies.d.ts.map +1 -0
- package/policies.js +27 -0
- package/policies.js.map +1 -0
- package/resilience.cjs +280 -0
- package/resilience.cjs.map +1 -0
- package/resilience.d.ts +6 -0
- package/resilience.d.ts.map +1 -0
- package/resilience.js +276 -0
- package/resilience.js.map +1 -0
- package/security.cjs +142 -0
- package/security.cjs.map +1 -0
- package/security.d.ts +11 -0
- package/security.d.ts.map +1 -0
- package/security.js +136 -0
- package/security.js.map +1 -0
- package/types.d.ts +3 -0
- package/types.d.ts.map +1 -0
package/observability.js
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { ATHOS_STATE_KEYS } from '@athosjs/constants';
|
|
2
|
+
import { ATHOS_LOGGER_TOKEN, ATHOS_TELEMETRY_SINK_TOKEN } from '@athosjs/observability';
|
|
3
|
+
|
|
4
|
+
function attachProObservability(application, options = {}) {
|
|
5
|
+
if (options.enabled === false && !options.logger && !options.telemetrySink) {
|
|
6
|
+
return application;
|
|
7
|
+
}
|
|
8
|
+
const hooks = createProObservabilityHooks(application.kernel, options);
|
|
9
|
+
if (Object.keys(hooks).length === 0) {
|
|
10
|
+
return application;
|
|
11
|
+
}
|
|
12
|
+
application.hooks(hooks);
|
|
13
|
+
return application;
|
|
14
|
+
}
|
|
15
|
+
function createProObservabilityHooks(kernel, options = {}) {
|
|
16
|
+
const logger = resolveLogger(kernel, options);
|
|
17
|
+
const telemetrySink = resolveTelemetrySink(kernel, options);
|
|
18
|
+
if (!logger && !telemetrySink) {
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
const emitTelemetry = createBufferedTelemetryEmitter(telemetrySink, logger, options.maxPendingTelemetryEvents ?? 1024);
|
|
22
|
+
const logRequests = options.logRequests ?? "errors";
|
|
23
|
+
const sampleRate = normalizeSampleRate(options.sampleRate);
|
|
24
|
+
const alwaysSampleErrors = options.alwaysSampleErrors ?? true;
|
|
25
|
+
return {
|
|
26
|
+
onRequest (context) {
|
|
27
|
+
const hasLogger = logRequests === "all" && logger;
|
|
28
|
+
const willSample = sampleRate > 0 && telemetrySink;
|
|
29
|
+
if (!hasLogger && !willSample) return;
|
|
30
|
+
if (willSample && sampleRate < 1 && Math.random() > sampleRate) return;
|
|
31
|
+
const attributes = createBaseRequestAttributes(context);
|
|
32
|
+
if (willSample) {
|
|
33
|
+
emitTelemetry({
|
|
34
|
+
name: "http.request.started",
|
|
35
|
+
timestamp: Date.now(),
|
|
36
|
+
attributes
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
if (hasLogger) {
|
|
40
|
+
logger.info(formatRequestLog("started", attributes));
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
onResponse (context, response) {
|
|
44
|
+
const hasLogger = logRequests === "all" && logger;
|
|
45
|
+
const willSample = sampleRate > 0 && telemetrySink;
|
|
46
|
+
if (!hasLogger && !willSample) return;
|
|
47
|
+
if (willSample && sampleRate < 1 && Math.random() > sampleRate) return;
|
|
48
|
+
const metrics = context.state.get(ATHOS_STATE_KEYS.HTTP_REQUEST_METRICS);
|
|
49
|
+
const attributes = {
|
|
50
|
+
requestId: context.request.requestId,
|
|
51
|
+
method: context.originalRequest.method,
|
|
52
|
+
path: context.originalRequest.path,
|
|
53
|
+
status: response.status
|
|
54
|
+
};
|
|
55
|
+
if (metrics) {
|
|
56
|
+
if (metrics.durationMs !== undefined) attributes.durationMs = metrics.durationMs;
|
|
57
|
+
if (metrics.bodyBytes !== undefined) attributes.bodyBytes = metrics.bodyBytes;
|
|
58
|
+
if (metrics.aborted !== undefined) attributes.aborted = metrics.aborted;
|
|
59
|
+
}
|
|
60
|
+
attributes.phase = context.execution.phase;
|
|
61
|
+
if (willSample) {
|
|
62
|
+
emitTelemetry({
|
|
63
|
+
name: "http.request.completed",
|
|
64
|
+
timestamp: Date.now(),
|
|
65
|
+
attributes
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
if (hasLogger) {
|
|
69
|
+
logger.info(formatRequestLog("completed", attributes));
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
onError (context, error) {
|
|
73
|
+
const hasLogger = logRequests !== "none" && logger;
|
|
74
|
+
const willSample = sampleRate > 0 || alwaysSampleErrors;
|
|
75
|
+
if (!hasLogger && !willSample) return;
|
|
76
|
+
const havenError = error;
|
|
77
|
+
const attributes = {
|
|
78
|
+
requestId: context.request.requestId,
|
|
79
|
+
method: context.originalRequest.method,
|
|
80
|
+
path: context.originalRequest.path,
|
|
81
|
+
status: havenError?.status,
|
|
82
|
+
code: havenError?.code
|
|
83
|
+
};
|
|
84
|
+
if (willSample) {
|
|
85
|
+
const event = {
|
|
86
|
+
name: "http.request.failed",
|
|
87
|
+
timestamp: Date.now(),
|
|
88
|
+
level: "error",
|
|
89
|
+
attributes
|
|
90
|
+
};
|
|
91
|
+
if (shouldSampleEvent(event, sampleRate, alwaysSampleErrors)) {
|
|
92
|
+
emitTelemetry(event);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (hasLogger) {
|
|
96
|
+
logger.error(formatRequestLog("failed", attributes));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function normalizeSampleRate(value) {
|
|
102
|
+
if (value === undefined) {
|
|
103
|
+
return 1;
|
|
104
|
+
}
|
|
105
|
+
if (Number.isNaN(value) || value < 0) {
|
|
106
|
+
return 0;
|
|
107
|
+
}
|
|
108
|
+
if (value > 1) {
|
|
109
|
+
return 1;
|
|
110
|
+
}
|
|
111
|
+
return value;
|
|
112
|
+
}
|
|
113
|
+
function shouldSampleEvent(event, sampleRate, alwaysSampleErrors) {
|
|
114
|
+
if (alwaysSampleErrors && event.level === "error") {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
if (sampleRate >= 1) {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
if (sampleRate <= 0) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
return deterministicSample(event) < sampleRate;
|
|
124
|
+
}
|
|
125
|
+
function resolveLogger(kernel, options) {
|
|
126
|
+
if (options.logger) {
|
|
127
|
+
return options.logger;
|
|
128
|
+
}
|
|
129
|
+
if (kernel.services.has(ATHOS_LOGGER_TOKEN)) {
|
|
130
|
+
return kernel.services.resolve(ATHOS_LOGGER_TOKEN);
|
|
131
|
+
}
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
function resolveTelemetrySink(kernel, options) {
|
|
135
|
+
if (options.telemetrySink) {
|
|
136
|
+
return options.telemetrySink;
|
|
137
|
+
}
|
|
138
|
+
if (kernel.services.has(ATHOS_TELEMETRY_SINK_TOKEN)) {
|
|
139
|
+
return kernel.services.resolve(ATHOS_TELEMETRY_SINK_TOKEN);
|
|
140
|
+
}
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
function createBufferedTelemetryEmitter(sink, logger, maxPendingTelemetryEvents) {
|
|
144
|
+
if (!sink) {
|
|
145
|
+
return ()=>undefined;
|
|
146
|
+
}
|
|
147
|
+
return (event)=>{
|
|
148
|
+
if (sink.emitBatch) {
|
|
149
|
+
sink.emitBatch([
|
|
150
|
+
event
|
|
151
|
+
]);
|
|
152
|
+
} else {
|
|
153
|
+
sink.emit(event);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function createBaseRequestAttributes(context) {
|
|
158
|
+
return {
|
|
159
|
+
requestId: context.request.requestId,
|
|
160
|
+
method: context.originalRequest.method,
|
|
161
|
+
path: context.originalRequest.path,
|
|
162
|
+
...context.route?.operationId ? {
|
|
163
|
+
operationId: context.route.operationId
|
|
164
|
+
} : {},
|
|
165
|
+
phase: context.execution.phase
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function formatRequestLog(stage, attributes) {
|
|
169
|
+
return `[${stage}] ${attributes.method} ${attributes.path} (${attributes.requestId})`;
|
|
170
|
+
}
|
|
171
|
+
function deterministicSample(event) {
|
|
172
|
+
const seed = [
|
|
173
|
+
event.name,
|
|
174
|
+
event.level ?? "info",
|
|
175
|
+
String(event.attributes?.requestId ?? ""),
|
|
176
|
+
String(event.attributes?.path ?? ""),
|
|
177
|
+
String(event.timestamp)
|
|
178
|
+
].join("|");
|
|
179
|
+
let hash = 2166136261;
|
|
180
|
+
for(let index = 0; index < seed.length; index += 1){
|
|
181
|
+
hash ^= seed.charCodeAt(index);
|
|
182
|
+
hash = Math.imul(hash, 16777619);
|
|
183
|
+
}
|
|
184
|
+
return (hash >>> 0) / 0xffffffff;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export { attachProObservability, createProObservabilityHooks };
|
|
188
|
+
//# sourceMappingURL=observability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observability.js","sources":["../../../packages/pro/src/observability.ts"],"sourcesContent":["import { ATHOS_STATE_KEYS } from \"@athosjs/constants\";\nimport type { ApplicationKernel } from \"@athosjs/core\";\nimport type { HttpContext, HttpHooks, HttpRequestMetrics } from \"@athosjs/http\";\nimport type { HttpApplication } from \"@athosjs/http/experimental\";\nimport {\n\ttype ExecutionTelemetryEvent,\n\ttype ExecutionTelemetrySink,\n\tATHOS_LOGGER_TOKEN,\n\tATHOS_TELEMETRY_SINK_TOKEN,\n\ttype AthosLogger,\n} from \"@athosjs/observability\";\nimport type { ProObservabilityOptions } from \"@athosjs/types/pro\";\n\nexport type { ProObservabilityOptions };\n\nexport function attachProObservability(\n\tapplication: HttpApplication,\n\toptions: ProObservabilityOptions = {},\n): HttpApplication {\n\tif (options.enabled === false && !options.logger && !options.telemetrySink) {\n\t\treturn application;\n\t}\n\tconst hooks = createProObservabilityHooks(application.kernel, options);\n\tif (Object.keys(hooks).length === 0) {\n\t\treturn application;\n\t}\n\tapplication.hooks(hooks);\n\treturn application;\n}\n\nexport function createProObservabilityHooks(\n\tkernel: ApplicationKernel,\n\toptions: ProObservabilityOptions = {},\n): HttpHooks {\n\tconst logger = resolveLogger(kernel, options);\n\tconst telemetrySink = resolveTelemetrySink(kernel, options);\n\tif (!logger && !telemetrySink) {\n\t\treturn {};\n\t}\n\tconst emitTelemetry = createBufferedTelemetryEmitter(\n\t\ttelemetrySink,\n\t\tlogger,\n\t\toptions.maxPendingTelemetryEvents ?? 1024,\n\t);\n\tconst logRequests = options.logRequests ?? \"errors\";\n\tconst sampleRate = normalizeSampleRate(options.sampleRate);\n\tconst alwaysSampleErrors = options.alwaysSampleErrors ?? true;\n\n\treturn {\n\t\tonRequest(context: HttpContext): void {\n\t\t\tconst hasLogger = logRequests === \"all\" && logger;\n\t\t\tconst willSample = sampleRate > 0 && telemetrySink;\n\n\t\t\tif (!hasLogger && !willSample) return;\n\n\t\t\tif (willSample && sampleRate < 1 && Math.random() > sampleRate) return;\n\n\t\t\tconst attributes = createBaseRequestAttributes(context);\n\n\t\t\tif (willSample) {\n\t\t\t\temitTelemetry({\n\t\t\t\t\tname: \"http.request.started\",\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\tattributes,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (hasLogger) {\n\t\t\t\tlogger.info(formatRequestLog(\"started\", attributes));\n\t\t\t}\n\t\t},\n\t\tonResponse(context: HttpContext, response): void {\n\t\t\tconst hasLogger = logRequests === \"all\" && logger;\n\t\t\tconst willSample = sampleRate > 0 && telemetrySink;\n\n\t\t\tif (!hasLogger && !willSample) return;\n\n\t\t\tif (willSample && sampleRate < 1 && Math.random() > sampleRate) return;\n\n\t\t\tconst metrics = (context.state as { get<T>(k: string | symbol): T | undefined }).get<HttpRequestMetrics>(\n\t\t\t\tATHOS_STATE_KEYS.HTTP_REQUEST_METRICS,\n\t\t\t);\n\t\t\tconst attributes: Record<string, unknown> = {\n\t\t\t\trequestId: context.request.requestId,\n\t\t\t\tmethod: context.originalRequest.method,\n\t\t\t\tpath: context.originalRequest.path,\n\t\t\t\tstatus: response.status,\n\t\t\t};\n\t\t\tif (metrics) {\n\t\t\t\tif (metrics.durationMs !== undefined) attributes.durationMs = metrics.durationMs;\n\t\t\t\tif (metrics.bodyBytes !== undefined) attributes.bodyBytes = metrics.bodyBytes;\n\t\t\t\tif (metrics.aborted !== undefined) attributes.aborted = metrics.aborted;\n\t\t\t}\n\t\t\tattributes.phase = context.execution.phase;\n\n\t\t\tif (willSample) {\n\t\t\t\temitTelemetry({\n\t\t\t\t\tname: \"http.request.completed\",\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\tattributes,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (hasLogger) {\n\t\t\t\tlogger.info(formatRequestLog(\"completed\", attributes));\n\t\t\t}\n\t\t},\n\t\tonError(context: HttpContext, error: unknown): void {\n\t\t\tconst hasLogger = logRequests !== \"none\" && logger;\n\t\t\tconst willSample = sampleRate > 0 || alwaysSampleErrors;\n\n\t\t\tif (!hasLogger && !willSample) return;\n\n\t\t\tconst havenError = error as Record<string, unknown>;\n\t\t\tconst attributes = {\n\t\t\t\trequestId: context.request.requestId,\n\t\t\t\tmethod: context.originalRequest.method,\n\t\t\t\tpath: context.originalRequest.path,\n\t\t\t\tstatus: havenError?.status,\n\t\t\t\tcode: havenError?.code,\n\t\t\t};\n\n\t\t\tif (willSample) {\n\t\t\t\tconst event: ExecutionTelemetryEvent = {\n\t\t\t\t\tname: \"http.request.failed\",\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\tattributes,\n\t\t\t\t};\n\t\t\t\tif (shouldSampleEvent(event, sampleRate, alwaysSampleErrors)) {\n\t\t\t\t\temitTelemetry(event);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (hasLogger) {\n\t\t\t\tlogger.error(formatRequestLog(\"failed\", attributes));\n\t\t\t}\n\t\t},\n\t};\n}\n\nfunction normalizeSampleRate(value: number | undefined): number {\n\tif (value === undefined) {\n\t\treturn 1;\n\t}\n\n\tif (Number.isNaN(value) || value < 0) {\n\t\treturn 0;\n\t}\n\n\tif (value > 1) {\n\t\treturn 1;\n\t}\n\n\treturn value;\n}\n\nfunction shouldSampleEvent(event: ExecutionTelemetryEvent, sampleRate: number, alwaysSampleErrors: boolean): boolean {\n\tif (alwaysSampleErrors && event.level === \"error\") {\n\t\treturn true;\n\t}\n\n\tif (sampleRate >= 1) {\n\t\treturn true;\n\t}\n\n\tif (sampleRate <= 0) {\n\t\treturn false;\n\t}\n\n\treturn deterministicSample(event) < sampleRate;\n}\n\nfunction resolveLogger(kernel: ApplicationKernel, options: ProObservabilityOptions): AthosLogger | undefined {\n\tif (options.logger) {\n\t\treturn options.logger as AthosLogger;\n\t}\n\n\tif (kernel.services.has(ATHOS_LOGGER_TOKEN)) {\n\t\treturn kernel.services.resolve(ATHOS_LOGGER_TOKEN);\n\t}\n\n\treturn undefined;\n}\n\nfunction resolveTelemetrySink(\n\tkernel: ApplicationKernel,\n\toptions: ProObservabilityOptions,\n): ExecutionTelemetrySink | undefined {\n\tif (options.telemetrySink) {\n\t\treturn options.telemetrySink as ExecutionTelemetrySink;\n\t}\n\n\tif (kernel.services.has(ATHOS_TELEMETRY_SINK_TOKEN)) {\n\t\treturn kernel.services.resolve(ATHOS_TELEMETRY_SINK_TOKEN);\n\t}\n\n\treturn undefined;\n}\n\nfunction createBufferedTelemetryEmitter(\n\tsink: ExecutionTelemetrySink | undefined,\n\tlogger: AthosLogger | undefined,\n\tmaxPendingTelemetryEvents: number,\n): (event: ExecutionTelemetryEvent) => void {\n\tif (!sink) {\n\t\treturn () => undefined;\n\t}\n\n\treturn (event: ExecutionTelemetryEvent) => {\n\t\tif (sink.emitBatch) {\n\t\t\tsink.emitBatch([event]);\n\t\t} else {\n\t\t\tsink.emit(event);\n\t\t}\n\t};\n}\n\nasync function flushTelemetryQueue(\n\tsink: ExecutionTelemetrySink,\n\tevents: readonly ExecutionTelemetryEvent[],\n\tlogger?: AthosLogger,\n): Promise<void> {\n\ttry {\n\t\tif (sink.emitBatch) {\n\t\t\tawait sink.emitBatch(events);\n\t\t} else {\n\t\t\tfor (const event of events) {\n\t\t\t\tawait sink.emit(event);\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tlogger?.warn(\n\t\t\t\"Failed to flush telemetry batch.\",\n\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t`events: ${events.length}`,\n\t\t);\n\t}\n}\n\nfunction createBaseRequestAttributes(context: HttpContext): Record<string, unknown> {\n\treturn {\n\t\trequestId: context.request.requestId,\n\t\tmethod: context.originalRequest.method,\n\t\tpath: context.originalRequest.path,\n\t\t...(context.route?.operationId ? { operationId: context.route.operationId } : {}),\n\t\tphase: context.execution.phase,\n\t};\n}\n\nfunction formatRequestLog(stage: string, attributes: Record<string, unknown>): string {\n\treturn `[${stage}] ${attributes.method} ${attributes.path} (${attributes.requestId})`;\n}\n\nfunction deterministicSample(event: ExecutionTelemetryEvent): number {\n\tconst seed = [\n\t\tevent.name,\n\t\tevent.level ?? \"info\",\n\t\tString(event.attributes?.requestId ?? \"\"),\n\t\tString(event.attributes?.path ?? \"\"),\n\t\tString(event.timestamp),\n\t].join(\"|\");\n\tlet hash = 2166136261;\n\n\tfor (let index = 0; index < seed.length; index += 1) {\n\t\thash ^= seed.charCodeAt(index);\n\t\thash = Math.imul(hash, 16777619);\n\t}\n\n\treturn (hash >>> 0) / 0xffffffff;\n}\n"],"names":["attachProObservability","application","options","enabled","logger","telemetrySink","hooks","createProObservabilityHooks","kernel","Object","keys","length","resolveLogger","resolveTelemetrySink","emitTelemetry","createBufferedTelemetryEmitter","maxPendingTelemetryEvents","logRequests","sampleRate","normalizeSampleRate","alwaysSampleErrors","onRequest","context","hasLogger","willSample","Math","random","attributes","createBaseRequestAttributes","name","timestamp","Date","now","info","formatRequestLog","onResponse","response","metrics","state","get","ATHOS_STATE_KEYS","HTTP_REQUEST_METRICS","requestId","request","method","originalRequest","path","status","durationMs","undefined","bodyBytes","aborted","phase","execution","onError","error","havenError","code","event","level","shouldSampleEvent","value","Number","isNaN","deterministicSample","services","has","ATHOS_LOGGER_TOKEN","resolve","ATHOS_TELEMETRY_SINK_TOKEN","sink","emitBatch","emit","route","operationId","stage","seed","String","join","hash","index","charCodeAt","imul"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@athosjs/pro",
|
|
3
|
+
"version": "0.1.4-alpha",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Opinionated pro product assembly for Athos",
|
|
7
|
+
"homepage": "https://athosjs-3695tqux2-neversonsilvas-projects.vercel.app",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/neverson-silva/athosjs.git"
|
|
11
|
+
},
|
|
12
|
+
"main": "./index.js",
|
|
13
|
+
"module": "./index.js",
|
|
14
|
+
"types": "./index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./index.d.ts",
|
|
18
|
+
"import": "./index.js",
|
|
19
|
+
"require": "./index.js",
|
|
20
|
+
"default": "./index.js"
|
|
21
|
+
},
|
|
22
|
+
"./experimental": {
|
|
23
|
+
"types": "./experimental.d.ts",
|
|
24
|
+
"import": "./experimental.js",
|
|
25
|
+
"require": "./experimental.js",
|
|
26
|
+
"default": "./experimental.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@athosjs/openapi": "0.1.4-alpha",
|
|
31
|
+
"@athosjs/http": "0.1.4-alpha",
|
|
32
|
+
"@athosjs/i18n": "0.1.4-alpha",
|
|
33
|
+
"@athosjs/core": "0.1.4-alpha",
|
|
34
|
+
"@athosjs/metadata": "0.1.4-alpha",
|
|
35
|
+
"@athosjs/runtime": "0.1.4-alpha",
|
|
36
|
+
"@athosjs/config": "0.1.4-alpha",
|
|
37
|
+
"@athosjs/observability": "0.1.4-alpha",
|
|
38
|
+
"@athosjs/constants": "0.1.4-alpha",
|
|
39
|
+
"@athosjs/di": "0.1.4-alpha",
|
|
40
|
+
"@athosjs/module": "0.1.4-alpha"
|
|
41
|
+
}
|
|
42
|
+
}
|
package/policies.cjs
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var metadata = require('@athosjs/metadata');
|
|
4
|
+
|
|
5
|
+
const USE_POLICY_METADATA_KEY = Symbol("athos:pro:usePolicy");
|
|
6
|
+
/**
|
|
7
|
+
* Attaches a policy class to a route handler.
|
|
8
|
+
*
|
|
9
|
+
* The policy is resolved from the DI container and its `check()` method
|
|
10
|
+
* is called before the route handler runs.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* @Controller()
|
|
15
|
+
* export class AdminController {
|
|
16
|
+
* @UsePolicy(AuthPolicy)
|
|
17
|
+
* @Get("/admin")
|
|
18
|
+
* getAdmin() { ... }
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/ function UsePolicy(policyClass) {
|
|
22
|
+
return (target, propertyKey, descriptor)=>{
|
|
23
|
+
metadata.defineMetadata(target.constructor, USE_POLICY_METADATA_KEY, policyClass, propertyKey);
|
|
24
|
+
return descriptor;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
exports.USE_POLICY_METADATA_KEY = USE_POLICY_METADATA_KEY;
|
|
29
|
+
exports.UsePolicy = UsePolicy;
|
|
30
|
+
//# sourceMappingURL=policies.cjs.map
|
package/policies.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policies.cjs","sources":["../../../packages/pro/src/policies.ts"],"sourcesContent":["import { defineMetadata } from \"@athosjs/metadata\";\n\nexport const USE_POLICY_METADATA_KEY = Symbol(\"athos:pro:usePolicy\");\n\n/**\n * Attaches a policy class to a route handler.\n *\n * The policy is resolved from the DI container and its `check()` method\n * is called before the route handler runs.\n *\n * @example\n * ```ts\n * @Controller()\n * export class AdminController {\n * @UsePolicy(AuthPolicy)\n * @Get(\"/admin\")\n * getAdmin() { ... }\n * }\n * ```\n */\nexport function UsePolicy(\n\tpolicyClass: new (...args: never[]) => { check(context: unknown): boolean | Promise<boolean> },\n): MethodDecorator {\n\treturn (target, propertyKey, descriptor) => {\n\t\tdefineMetadata(target.constructor, USE_POLICY_METADATA_KEY, policyClass, propertyKey);\n\t\treturn descriptor;\n\t};\n}\n"],"names":["USE_POLICY_METADATA_KEY","Symbol","UsePolicy","policyClass","target","propertyKey","descriptor","defineMetadata"],"mappings":";;;;AAEO,MAAMA,uBAAAA,GAA0BC,MAAAA,CAAO,qBAAA;AAE9C;;;;;;;;;;;;;;;IAgBO,SAASC,SAAAA,CACfC,WAA8F,EAAA;IAE9F,OAAO,CAACC,QAAQC,WAAAA,EAAaC,UAAAA,GAAAA;AAC5BC,QAAAA,uBAAAA,CAAeH,MAAAA,CAAO,WAAW,EAAEJ,uBAAAA,EAAyBG,WAAAA,EAAaE,WAAAA,CAAAA;QACzE,OAAOC,UAAAA;AACR,IAAA,CAAA;AACD;;;;;"}
|
package/policies.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare const USE_POLICY_METADATA_KEY: unique symbol;
|
|
2
|
+
/**
|
|
3
|
+
* Attaches a policy class to a route handler.
|
|
4
|
+
*
|
|
5
|
+
* The policy is resolved from the DI container and its `check()` method
|
|
6
|
+
* is called before the route handler runs.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* @Controller()
|
|
11
|
+
* export class AdminController {
|
|
12
|
+
* @UsePolicy(AuthPolicy)
|
|
13
|
+
* @Get("/admin")
|
|
14
|
+
* getAdmin() { ... }
|
|
15
|
+
* }
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare function UsePolicy(policyClass: new (...args: never[]) => {
|
|
19
|
+
check(context: unknown): boolean | Promise<boolean>;
|
|
20
|
+
}): MethodDecorator;
|
|
21
|
+
//# sourceMappingURL=policies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policies.d.ts","sourceRoot":"","sources":["../../../../../../packages/pro/src/policies.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,uBAAuB,eAAgC,CAAC;AAErE;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,SAAS,CACxB,WAAW,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK;IAAE,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,GAC5F,eAAe,CAKjB"}
|
package/policies.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { defineMetadata } from '@athosjs/metadata';
|
|
2
|
+
|
|
3
|
+
const USE_POLICY_METADATA_KEY = Symbol("athos:pro:usePolicy");
|
|
4
|
+
/**
|
|
5
|
+
* Attaches a policy class to a route handler.
|
|
6
|
+
*
|
|
7
|
+
* The policy is resolved from the DI container and its `check()` method
|
|
8
|
+
* is called before the route handler runs.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* @Controller()
|
|
13
|
+
* export class AdminController {
|
|
14
|
+
* @UsePolicy(AuthPolicy)
|
|
15
|
+
* @Get("/admin")
|
|
16
|
+
* getAdmin() { ... }
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*/ function UsePolicy(policyClass) {
|
|
20
|
+
return (target, propertyKey, descriptor)=>{
|
|
21
|
+
defineMetadata(target.constructor, USE_POLICY_METADATA_KEY, policyClass, propertyKey);
|
|
22
|
+
return descriptor;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { USE_POLICY_METADATA_KEY, UsePolicy };
|
|
27
|
+
//# sourceMappingURL=policies.js.map
|
package/policies.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policies.js","sources":["../../../packages/pro/src/policies.ts"],"sourcesContent":["import { defineMetadata } from \"@athosjs/metadata\";\n\nexport const USE_POLICY_METADATA_KEY = Symbol(\"athos:pro:usePolicy\");\n\n/**\n * Attaches a policy class to a route handler.\n *\n * The policy is resolved from the DI container and its `check()` method\n * is called before the route handler runs.\n *\n * @example\n * ```ts\n * @Controller()\n * export class AdminController {\n * @UsePolicy(AuthPolicy)\n * @Get(\"/admin\")\n * getAdmin() { ... }\n * }\n * ```\n */\nexport function UsePolicy(\n\tpolicyClass: new (...args: never[]) => { check(context: unknown): boolean | Promise<boolean> },\n): MethodDecorator {\n\treturn (target, propertyKey, descriptor) => {\n\t\tdefineMetadata(target.constructor, USE_POLICY_METADATA_KEY, policyClass, propertyKey);\n\t\treturn descriptor;\n\t};\n}\n"],"names":["USE_POLICY_METADATA_KEY","Symbol","UsePolicy","policyClass","target","propertyKey","descriptor","defineMetadata"],"mappings":";;AAEO,MAAMA,uBAAAA,GAA0BC,MAAAA,CAAO,qBAAA;AAE9C;;;;;;;;;;;;;;;IAgBO,SAASC,SAAAA,CACfC,WAA8F,EAAA;IAE9F,OAAO,CAACC,QAAQC,WAAAA,EAAaC,UAAAA,GAAAA;AAC5BC,QAAAA,cAAAA,CAAeH,MAAAA,CAAO,WAAW,EAAEJ,uBAAAA,EAAyBG,WAAAA,EAAaE,WAAAA,CAAAA;QACzE,OAAOC,UAAAA;AACR,IAAA,CAAA;AACD;;;;"}
|
package/resilience.cjs
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var constants = require('@athosjs/constants');
|
|
4
|
+
var core = require('@athosjs/core');
|
|
5
|
+
|
|
6
|
+
function createConcurrencyLimiter(options) {
|
|
7
|
+
if (options.maxConcurrency < 1) {
|
|
8
|
+
throw core.createAthosError(constants.ATHOS_ERROR_CODES.OPERATION_OVERLOADED, "Concurrency limiter requires maxConcurrency to be greater than zero.");
|
|
9
|
+
}
|
|
10
|
+
const queue = [];
|
|
11
|
+
let queueOffset = 0;
|
|
12
|
+
let activeCount = 0;
|
|
13
|
+
let pendingCount = 0;
|
|
14
|
+
const pump = ()=>{
|
|
15
|
+
while(activeCount < options.maxConcurrency && pendingCount > 0){
|
|
16
|
+
const next = queue[queueOffset];
|
|
17
|
+
queue[queueOffset] = undefined;
|
|
18
|
+
queueOffset++;
|
|
19
|
+
if (!next) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
pendingCount -= 1;
|
|
23
|
+
if (queueOffset > 1000) {
|
|
24
|
+
queue.splice(0, queueOffset);
|
|
25
|
+
queueOffset = 0;
|
|
26
|
+
}
|
|
27
|
+
runEntry(next);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
const runEntry = (entry)=>{
|
|
31
|
+
if (entry.signal && entry.abortHandler) {
|
|
32
|
+
entry.signal.removeEventListener("abort", entry.abortHandler);
|
|
33
|
+
entry.abortHandler = undefined;
|
|
34
|
+
}
|
|
35
|
+
if (entry.signal?.aborted) {
|
|
36
|
+
entry.reject(createOverloadedAbortError("Queued operation was aborted before execution."));
|
|
37
|
+
pump();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
activeCount += 1;
|
|
41
|
+
const result = entry.operation(entry.signal);
|
|
42
|
+
if (result instanceof Promise) {
|
|
43
|
+
result.then(entry.resolve, entry.reject).finally(()=>{
|
|
44
|
+
activeCount -= 1;
|
|
45
|
+
pump();
|
|
46
|
+
});
|
|
47
|
+
} else {
|
|
48
|
+
try {
|
|
49
|
+
entry.resolve(result);
|
|
50
|
+
} catch (e) {
|
|
51
|
+
entry.reject(e);
|
|
52
|
+
} finally{
|
|
53
|
+
activeCount -= 1;
|
|
54
|
+
pump();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
return {
|
|
59
|
+
get activeCount () {
|
|
60
|
+
return activeCount;
|
|
61
|
+
},
|
|
62
|
+
get pendingCount () {
|
|
63
|
+
return pendingCount;
|
|
64
|
+
},
|
|
65
|
+
run (operation, runOptions = {}) {
|
|
66
|
+
if (runOptions.signal?.aborted) {
|
|
67
|
+
return Promise.reject(createOverloadedAbortError("Operation was aborted before entering the limiter."));
|
|
68
|
+
}
|
|
69
|
+
if (activeCount < options.maxConcurrency) {
|
|
70
|
+
return new Promise((resolve, reject)=>{
|
|
71
|
+
runEntry({
|
|
72
|
+
operation,
|
|
73
|
+
signal: runOptions.signal,
|
|
74
|
+
resolve,
|
|
75
|
+
reject
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
const maxQueueSize = options.maxQueueSize ?? Number.POSITIVE_INFINITY;
|
|
80
|
+
if (pendingCount >= maxQueueSize) {
|
|
81
|
+
return Promise.reject(core.createAthosError(constants.ATHOS_ERROR_CODES.OPERATION_OVERLOADED, "Concurrency limiter queue is full.", {
|
|
82
|
+
details: {
|
|
83
|
+
activeCount,
|
|
84
|
+
pendingCount,
|
|
85
|
+
maxConcurrency: options.maxConcurrency,
|
|
86
|
+
maxQueueSize
|
|
87
|
+
}
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
return new Promise((resolve, reject)=>{
|
|
91
|
+
const entry = {
|
|
92
|
+
operation,
|
|
93
|
+
signal: runOptions.signal,
|
|
94
|
+
resolve,
|
|
95
|
+
reject
|
|
96
|
+
};
|
|
97
|
+
if (runOptions.signal) {
|
|
98
|
+
const onAbort = ()=>{
|
|
99
|
+
const idx = queue.indexOf(entry, queueOffset);
|
|
100
|
+
if (idx !== -1) {
|
|
101
|
+
queue[idx] = undefined;
|
|
102
|
+
pendingCount -= 1;
|
|
103
|
+
runOptions.signal?.removeEventListener("abort", onAbort);
|
|
104
|
+
entry.abortHandler = undefined;
|
|
105
|
+
reject(createOverloadedAbortError("Queued operation was aborted."));
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
entry.abortHandler = onAbort;
|
|
109
|
+
runOptions.signal.addEventListener("abort", onAbort, {
|
|
110
|
+
once: true
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
queue.push(entry);
|
|
114
|
+
pendingCount += 1;
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function createCircuitBreaker(options) {
|
|
120
|
+
if (options.failureThreshold < 1) {
|
|
121
|
+
throw core.createAthosError(constants.ATHOS_ERROR_CODES.OPERATION_CIRCUIT_OPEN, "Circuit breaker requires failureThreshold to be greater than zero.");
|
|
122
|
+
}
|
|
123
|
+
let state = "closed";
|
|
124
|
+
let failureCount = 0;
|
|
125
|
+
let openedAt;
|
|
126
|
+
let halfOpenAttempts = 0;
|
|
127
|
+
const moveToOpen = ()=>{
|
|
128
|
+
state = "open";
|
|
129
|
+
openedAt = Date.now();
|
|
130
|
+
halfOpenAttempts = 0;
|
|
131
|
+
};
|
|
132
|
+
const moveToClosed = ()=>{
|
|
133
|
+
state = "closed";
|
|
134
|
+
failureCount = 0;
|
|
135
|
+
openedAt = undefined;
|
|
136
|
+
halfOpenAttempts = 0;
|
|
137
|
+
};
|
|
138
|
+
const moveToHalfOpenIfReady = ()=>{
|
|
139
|
+
if (state === "open" && openedAt !== undefined && Date.now() - openedAt >= options.resetAfterMs) {
|
|
140
|
+
state = "half-open";
|
|
141
|
+
halfOpenAttempts = 0;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
return {
|
|
145
|
+
get state () {
|
|
146
|
+
moveToHalfOpenIfReady();
|
|
147
|
+
return state;
|
|
148
|
+
},
|
|
149
|
+
get failureCount () {
|
|
150
|
+
return failureCount;
|
|
151
|
+
},
|
|
152
|
+
get openedAt () {
|
|
153
|
+
return openedAt;
|
|
154
|
+
},
|
|
155
|
+
async execute (operation, runOptions = {}) {
|
|
156
|
+
moveToHalfOpenIfReady();
|
|
157
|
+
if (state === "open") {
|
|
158
|
+
throw core.createAthosError(constants.ATHOS_ERROR_CODES.OPERATION_CIRCUIT_OPEN, "Circuit breaker is open.", {
|
|
159
|
+
details: {
|
|
160
|
+
openedAt,
|
|
161
|
+
resetAfterMs: options.resetAfterMs
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
if (state === "half-open") {
|
|
166
|
+
const allowedAttempts = options.halfOpenMaxAttempts ?? 1;
|
|
167
|
+
if (halfOpenAttempts >= allowedAttempts) {
|
|
168
|
+
throw core.createAthosError(constants.ATHOS_ERROR_CODES.OPERATION_CIRCUIT_OPEN, "Circuit breaker half-open probe limit reached.", {
|
|
169
|
+
details: {
|
|
170
|
+
openedAt,
|
|
171
|
+
allowedAttempts
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
halfOpenAttempts += 1;
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
const result = await operation(runOptions.signal);
|
|
179
|
+
moveToClosed();
|
|
180
|
+
return result;
|
|
181
|
+
} catch (error) {
|
|
182
|
+
failureCount += 1;
|
|
183
|
+
if (state === "half-open" || failureCount >= options.failureThreshold) {
|
|
184
|
+
moveToOpen();
|
|
185
|
+
}
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function createResilientExecutor(options = {}) {
|
|
192
|
+
return {
|
|
193
|
+
async execute (operation, runOptions = {}) {
|
|
194
|
+
const attempt = (signal)=>{
|
|
195
|
+
const result = options.timeoutMs ? withTimeout((timeoutSignal)=>operation(timeoutSignal), options.timeoutMs, signal) : operation(signal);
|
|
196
|
+
return result instanceof Promise ? result : Promise.resolve(result);
|
|
197
|
+
};
|
|
198
|
+
const executeWithBreaker = (signal)=>{
|
|
199
|
+
if (options.circuitBreaker) {
|
|
200
|
+
return options.circuitBreaker.execute((breakerSignal)=>attempt(breakerSignal), {
|
|
201
|
+
...signal === undefined ? {} : {
|
|
202
|
+
signal
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
return attempt(signal);
|
|
207
|
+
};
|
|
208
|
+
if (options.limiter) {
|
|
209
|
+
return options.limiter.run((limiterSignal)=>executeWithBreaker(limiterSignal), runOptions);
|
|
210
|
+
}
|
|
211
|
+
return executeWithBreaker(runOptions.signal);
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
function withTimeout(operation, timeoutMs, upstreamSignal) {
|
|
216
|
+
if (timeoutMs <= 0) {
|
|
217
|
+
return Promise.reject(core.createAthosError(constants.ATHOS_ERROR_CODES.OPERATION_TIMEOUT, "Operation timeout must be greater than zero."));
|
|
218
|
+
}
|
|
219
|
+
if (upstreamSignal?.aborted) {
|
|
220
|
+
return Promise.reject(upstreamSignal.reason || new Error("Aborted"));
|
|
221
|
+
}
|
|
222
|
+
const controller = new AbortController();
|
|
223
|
+
const detach = upstreamSignal ? forwardAbort(upstreamSignal, controller) : undefined;
|
|
224
|
+
return new Promise((resolve, reject)=>{
|
|
225
|
+
let settled = false;
|
|
226
|
+
const timeout = setTimeout(()=>{
|
|
227
|
+
if (settled) return;
|
|
228
|
+
settled = true;
|
|
229
|
+
controller.abort();
|
|
230
|
+
reject(core.createAthosError(constants.ATHOS_ERROR_CODES.OPERATION_TIMEOUT, "Operation timed out.", {
|
|
231
|
+
details: {
|
|
232
|
+
timeoutMs
|
|
233
|
+
}
|
|
234
|
+
}));
|
|
235
|
+
}, timeoutMs);
|
|
236
|
+
const cleanup = (err)=>{
|
|
237
|
+
if (settled) return;
|
|
238
|
+
settled = true;
|
|
239
|
+
clearTimeout(timeout);
|
|
240
|
+
if (detach) detach();
|
|
241
|
+
if (err) reject(err);
|
|
242
|
+
};
|
|
243
|
+
try {
|
|
244
|
+
const res = operation(controller.signal);
|
|
245
|
+
if (res instanceof Promise) {
|
|
246
|
+
res.then((val)=>{
|
|
247
|
+
cleanup();
|
|
248
|
+
resolve(val);
|
|
249
|
+
}, cleanup);
|
|
250
|
+
} else {
|
|
251
|
+
cleanup();
|
|
252
|
+
resolve(res);
|
|
253
|
+
}
|
|
254
|
+
} catch (e) {
|
|
255
|
+
cleanup(e);
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
function forwardAbort(signal, controller) {
|
|
260
|
+
if (!signal) {
|
|
261
|
+
return ()=>undefined;
|
|
262
|
+
}
|
|
263
|
+
if (signal.aborted) {
|
|
264
|
+
controller.abort();
|
|
265
|
+
return ()=>undefined;
|
|
266
|
+
}
|
|
267
|
+
const onAbort = ()=>controller.abort();
|
|
268
|
+
signal.addEventListener("abort", onAbort, {
|
|
269
|
+
once: true
|
|
270
|
+
});
|
|
271
|
+
return ()=>signal.removeEventListener("abort", onAbort);
|
|
272
|
+
}
|
|
273
|
+
function createOverloadedAbortError(message) {
|
|
274
|
+
return core.createAthosError(constants.ATHOS_ERROR_CODES.OPERATION_OVERLOADED, message);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
exports.createCircuitBreaker = createCircuitBreaker;
|
|
278
|
+
exports.createConcurrencyLimiter = createConcurrencyLimiter;
|
|
279
|
+
exports.createResilientExecutor = createResilientExecutor;
|
|
280
|
+
//# sourceMappingURL=resilience.cjs.map
|