@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resilience.cjs","sources":["../../../packages/pro/src/resilience.ts"],"sourcesContent":["import { ATHOS_ERROR_CODES } from \"@athosjs/constants\";\nimport { createAthosError } from \"@athosjs/core\";\nimport type {\n\tCircuitBreaker,\n\tCircuitBreakerOptions,\n\tCircuitBreakerState,\n\tConcurrencyLimiter,\n\tConcurrencyLimiterOptions,\n\tResilientExecutor,\n\tResilientExecutorOptions,\n} from \"@athosjs/types/pro\";\n\nexport type {\n\tCircuitBreaker,\n\tCircuitBreakerOptions,\n\tCircuitBreakerState,\n\tConcurrencyLimiter,\n\tConcurrencyLimiterOptions,\n\tResilientExecutor,\n\tResilientExecutorOptions,\n};\n\ninterface QueueEntry<T> {\n\treadonly operation: (signal?: AbortSignal) => Promise<T> | T;\n\treadonly signal?: AbortSignal | undefined;\n\treadonly resolve: (value: T | PromiseLike<T>) => void;\n\treadonly reject: (reason?: unknown) => void;\n\tabortHandler?: (() => void) | undefined;\n}\n\nexport function createConcurrencyLimiter(options: ConcurrencyLimiterOptions): ConcurrencyLimiter {\n\tif (options.maxConcurrency < 1) {\n\t\tthrow createAthosError(\n\t\t\tATHOS_ERROR_CODES.OPERATION_OVERLOADED,\n\t\t\t\"Concurrency limiter requires maxConcurrency to be greater than zero.\",\n\t\t);\n\t}\n\n\tconst queue: QueueEntry<any>[] = [];\n\tlet queueOffset = 0;\n\tlet activeCount = 0;\n\tlet pendingCount = 0;\n\n\tconst pump = (): void => {\n\t\twhile (activeCount < options.maxConcurrency && pendingCount > 0) {\n\t\t\tconst next = queue[queueOffset];\n\t\t\tqueue[queueOffset] = undefined as any;\n\t\t\tqueueOffset++;\n\n\t\t\tif (!next) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tpendingCount -= 1;\n\n\t\t\tif (queueOffset > 1000) {\n\t\t\t\tqueue.splice(0, queueOffset);\n\t\t\t\tqueueOffset = 0;\n\t\t\t}\n\n\t\t\trunEntry(next);\n\t\t}\n\t};\n\n\tconst runEntry = (entry: QueueEntry<any>): void => {\n\t\tif (entry.signal && entry.abortHandler) {\n\t\t\tentry.signal.removeEventListener(\"abort\", entry.abortHandler);\n\t\t\tentry.abortHandler = undefined;\n\t\t}\n\n\t\tif (entry.signal?.aborted) {\n\t\t\tentry.reject(createOverloadedAbortError(\"Queued operation was aborted before execution.\"));\n\t\t\tpump();\n\t\t\treturn;\n\t\t}\n\n\t\tactiveCount += 1;\n\n\t\tconst result = entry.operation(entry.signal);\n\t\tif (result instanceof Promise) {\n\t\t\tresult.then(entry.resolve, entry.reject).finally(() => {\n\t\t\t\tactiveCount -= 1;\n\t\t\t\tpump();\n\t\t\t});\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tentry.resolve(result);\n\t\t\t} catch (e) {\n\t\t\t\tentry.reject(e);\n\t\t\t} finally {\n\t\t\t\tactiveCount -= 1;\n\t\t\t\tpump();\n\t\t\t}\n\t\t}\n\t};\n\n\treturn {\n\t\tget activeCount(): number {\n\t\t\treturn activeCount;\n\t\t},\n\t\tget pendingCount(): number {\n\t\t\treturn pendingCount;\n\t\t},\n\t\trun<T>(operation: (signal?: AbortSignal) => Promise<T> | T, runOptions: { signal?: AbortSignal } = {}): Promise<T> {\n\t\t\tif (runOptions.signal?.aborted) {\n\t\t\t\treturn Promise.reject(createOverloadedAbortError(\"Operation was aborted before entering the limiter.\"));\n\t\t\t}\n\n\t\t\tif (activeCount < options.maxConcurrency) {\n\t\t\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\t\t\trunEntry({\n\t\t\t\t\t\toperation,\n\t\t\t\t\t\tsignal: runOptions.signal,\n\t\t\t\t\t\tresolve,\n\t\t\t\t\t\treject,\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst maxQueueSize = options.maxQueueSize ?? Number.POSITIVE_INFINITY;\n\n\t\t\tif (pendingCount >= maxQueueSize) {\n\t\t\t\treturn Promise.reject(\n\t\t\t\t\tcreateAthosError(ATHOS_ERROR_CODES.OPERATION_OVERLOADED, \"Concurrency limiter queue is full.\", {\n\t\t\t\t\t\tdetails: {\n\t\t\t\t\t\t\tactiveCount,\n\t\t\t\t\t\t\tpendingCount,\n\t\t\t\t\t\t\tmaxConcurrency: options.maxConcurrency,\n\t\t\t\t\t\t\tmaxQueueSize,\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\t\tconst entry: QueueEntry<T> = {\n\t\t\t\t\toperation,\n\t\t\t\t\tsignal: runOptions.signal,\n\t\t\t\t\tresolve,\n\t\t\t\t\treject,\n\t\t\t\t};\n\n\t\t\t\tif (runOptions.signal) {\n\t\t\t\t\tconst onAbort = (): void => {\n\t\t\t\t\t\tconst idx = queue.indexOf(entry as any, queueOffset);\n\t\t\t\t\t\tif (idx !== -1) {\n\t\t\t\t\t\t\tqueue[idx] = undefined as any;\n\t\t\t\t\t\t\tpendingCount -= 1;\n\t\t\t\t\t\t\trunOptions.signal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\tentry.abortHandler = undefined;\n\t\t\t\t\t\t\treject(createOverloadedAbortError(\"Queued operation was aborted.\"));\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\n\t\t\t\t\tentry.abortHandler = onAbort;\n\t\t\t\t\trunOptions.signal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t}\n\n\t\t\t\tqueue.push(entry as QueueEntry<any>);\n\t\t\t\tpendingCount += 1;\n\t\t\t});\n\t\t},\n\t};\n}\n\nexport function createCircuitBreaker(options: CircuitBreakerOptions): CircuitBreaker {\n\tif (options.failureThreshold < 1) {\n\t\tthrow createAthosError(\n\t\t\tATHOS_ERROR_CODES.OPERATION_CIRCUIT_OPEN,\n\t\t\t\"Circuit breaker requires failureThreshold to be greater than zero.\",\n\t\t);\n\t}\n\n\tlet state: CircuitBreakerState = \"closed\";\n\tlet failureCount = 0;\n\tlet openedAt: number | undefined;\n\tlet halfOpenAttempts = 0;\n\n\tconst moveToOpen = (): void => {\n\t\tstate = \"open\";\n\t\topenedAt = Date.now();\n\t\thalfOpenAttempts = 0;\n\t};\n\n\tconst moveToClosed = (): void => {\n\t\tstate = \"closed\";\n\t\tfailureCount = 0;\n\t\topenedAt = undefined;\n\t\thalfOpenAttempts = 0;\n\t};\n\n\tconst moveToHalfOpenIfReady = (): void => {\n\t\tif (state === \"open\" && openedAt !== undefined && Date.now() - openedAt >= options.resetAfterMs) {\n\t\t\tstate = \"half-open\";\n\t\t\thalfOpenAttempts = 0;\n\t\t}\n\t};\n\n\treturn {\n\t\tget state(): CircuitBreakerState {\n\t\t\tmoveToHalfOpenIfReady();\n\t\t\treturn state;\n\t\t},\n\t\tget failureCount(): number {\n\t\t\treturn failureCount;\n\t\t},\n\t\tget openedAt(): number | undefined {\n\t\t\treturn openedAt;\n\t\t},\n\t\tasync execute<T>(\n\t\t\toperation: (signal?: AbortSignal) => Promise<T> | T,\n\t\t\trunOptions: { signal?: AbortSignal } = {},\n\t\t): Promise<T> {\n\t\t\tmoveToHalfOpenIfReady();\n\n\t\t\tif (state === \"open\") {\n\t\t\t\tthrow createAthosError(ATHOS_ERROR_CODES.OPERATION_CIRCUIT_OPEN, \"Circuit breaker is open.\", {\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\topenedAt,\n\t\t\t\t\t\tresetAfterMs: options.resetAfterMs,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (state === \"half-open\") {\n\t\t\t\tconst allowedAttempts = options.halfOpenMaxAttempts ?? 1;\n\n\t\t\t\tif (halfOpenAttempts >= allowedAttempts) {\n\t\t\t\t\tthrow createAthosError(\n\t\t\t\t\t\tATHOS_ERROR_CODES.OPERATION_CIRCUIT_OPEN,\n\t\t\t\t\t\t\"Circuit breaker half-open probe limit reached.\",\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdetails: {\n\t\t\t\t\t\t\t\topenedAt,\n\t\t\t\t\t\t\t\tallowedAttempts,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\thalfOpenAttempts += 1;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst result = await operation(runOptions.signal);\n\t\t\t\tmoveToClosed();\n\t\t\t\treturn result;\n\t\t\t} catch (error) {\n\t\t\t\tfailureCount += 1;\n\n\t\t\t\tif (state === \"half-open\" || failureCount >= options.failureThreshold) {\n\t\t\t\t\tmoveToOpen();\n\t\t\t\t}\n\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\t};\n}\n\nexport function createResilientExecutor(options: ResilientExecutorOptions = {}): ResilientExecutor {\n\treturn {\n\t\tasync execute<T>(\n\t\t\toperation: (signal?: AbortSignal) => Promise<T> | T,\n\t\t\trunOptions: { signal?: AbortSignal } = {},\n\t\t): Promise<T> {\n\t\t\tconst attempt = (signal?: AbortSignal): Promise<T> => {\n\t\t\t\tconst result = options.timeoutMs\n\t\t\t\t\t? withTimeout((timeoutSignal) => operation(timeoutSignal), options.timeoutMs, signal)\n\t\t\t\t\t: operation(signal);\n\n\t\t\t\treturn result instanceof Promise ? result : Promise.resolve(result);\n\t\t\t};\n\n\t\t\tconst executeWithBreaker = (signal?: AbortSignal): Promise<T> => {\n\t\t\t\tif (options.circuitBreaker) {\n\t\t\t\t\treturn options.circuitBreaker.execute((breakerSignal) => attempt(breakerSignal), {\n\t\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\treturn attempt(signal);\n\t\t\t};\n\n\t\t\tif (options.limiter) {\n\t\t\t\treturn options.limiter.run((limiterSignal) => executeWithBreaker(limiterSignal), runOptions);\n\t\t\t}\n\n\t\t\treturn executeWithBreaker(runOptions.signal);\n\t\t},\n\t};\n}\n\nfunction withTimeout<T>(\n\toperation: (signal?: AbortSignal) => Promise<T> | T,\n\ttimeoutMs: number,\n\tupstreamSignal?: AbortSignal,\n): Promise<T> {\n\tif (timeoutMs <= 0) {\n\t\treturn Promise.reject(\n\t\t\tcreateAthosError(ATHOS_ERROR_CODES.OPERATION_TIMEOUT, \"Operation timeout must be greater than zero.\"),\n\t\t);\n\t}\n\n\tif (upstreamSignal?.aborted) {\n\t\treturn Promise.reject(upstreamSignal.reason || new Error(\"Aborted\"));\n\t}\n\n\tconst controller = new AbortController();\n\tconst detach = upstreamSignal ? forwardAbort(upstreamSignal, controller) : undefined;\n\n\treturn new Promise<T>((resolve, reject) => {\n\t\tlet settled = false;\n\t\tconst timeout = setTimeout(() => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcontroller.abort();\n\t\t\treject(createAthosError(ATHOS_ERROR_CODES.OPERATION_TIMEOUT, \"Operation timed out.\", { details: { timeoutMs } }));\n\t\t}, timeoutMs);\n\n\t\tconst cleanup = (err?: any) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timeout);\n\t\t\tif (detach) detach();\n\t\t\tif (err) reject(err);\n\t\t};\n\n\t\ttry {\n\t\t\tconst res = operation(controller.signal);\n\t\t\tif (res instanceof Promise) {\n\t\t\t\tres.then((val) => {\n\t\t\t\t\tcleanup();\n\t\t\t\t\tresolve(val);\n\t\t\t\t}, cleanup);\n\t\t\t} else {\n\t\t\t\tcleanup();\n\t\t\t\tresolve(res);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tcleanup(e);\n\t\t}\n\t});\n}\n\nfunction forwardAbort(signal: AbortSignal | undefined, controller: AbortController): () => void {\n\tif (!signal) {\n\t\treturn () => undefined;\n\t}\n\n\tif (signal.aborted) {\n\t\tcontroller.abort();\n\t\treturn () => undefined;\n\t}\n\n\tconst onAbort = (): void => controller.abort();\n\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\treturn () => signal.removeEventListener(\"abort\", onAbort);\n}\n\nfunction createOverloadedAbortError(message: string) {\n\treturn createAthosError(ATHOS_ERROR_CODES.OPERATION_OVERLOADED, message);\n}\n"],"names":["createConcurrencyLimiter","options","maxConcurrency","createAthosError","ATHOS_ERROR_CODES","OPERATION_OVERLOADED","queue","queueOffset","activeCount","pendingCount","pump","next","undefined","splice","runEntry","entry","signal","abortHandler","removeEventListener","aborted","reject","createOverloadedAbortError","result","operation","Promise","then","resolve","finally","e","run","runOptions","maxQueueSize","Number","POSITIVE_INFINITY","details","onAbort","idx","indexOf","addEventListener","once","push","createCircuitBreaker","failureThreshold","OPERATION_CIRCUIT_OPEN","state","failureCount","openedAt","halfOpenAttempts","moveToOpen","Date","now","moveToClosed","moveToHalfOpenIfReady","resetAfterMs","execute","allowedAttempts","halfOpenMaxAttempts","error","createResilientExecutor","attempt","timeoutMs","withTimeout","timeoutSignal","executeWithBreaker","circuitBreaker","breakerSignal","limiter","limiterSignal","upstreamSignal","OPERATION_TIMEOUT","reason","Error","controller","AbortController","detach","forwardAbort","settled","timeout","setTimeout","abort","cleanup","err","clearTimeout","res","val","message"],"mappings":"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"}
|
package/resilience.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CircuitBreaker, CircuitBreakerOptions, CircuitBreakerState, ConcurrencyLimiter, ConcurrencyLimiterOptions, ResilientExecutor, ResilientExecutorOptions } from "@athosjs/types/pro";
|
|
2
|
+
export type { CircuitBreaker, CircuitBreakerOptions, CircuitBreakerState, ConcurrencyLimiter, ConcurrencyLimiterOptions, ResilientExecutor, ResilientExecutorOptions, };
|
|
3
|
+
export declare function createConcurrencyLimiter(options: ConcurrencyLimiterOptions): ConcurrencyLimiter;
|
|
4
|
+
export declare function createCircuitBreaker(options: CircuitBreakerOptions): CircuitBreaker;
|
|
5
|
+
export declare function createResilientExecutor(options?: ResilientExecutorOptions): ResilientExecutor;
|
|
6
|
+
//# sourceMappingURL=resilience.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resilience.d.ts","sourceRoot":"","sources":["../../../../../../packages/pro/src/resilience.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,cAAc,EACd,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,EAClB,yBAAyB,EACzB,iBAAiB,EACjB,wBAAwB,EACxB,MAAM,oBAAoB,CAAC;AAE5B,YAAY,EACX,cAAc,EACd,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,EAClB,yBAAyB,EACzB,iBAAiB,EACjB,wBAAwB,GACxB,CAAC;AAUF,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,yBAAyB,GAAG,kBAAkB,CAqI/F;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,cAAc,CA6FnF;AAED,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,wBAA6B,GAAG,iBAAiB,CA+BjG"}
|
package/resilience.js
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { ATHOS_ERROR_CODES } from '@athosjs/constants';
|
|
2
|
+
import { createAthosError } from '@athosjs/core';
|
|
3
|
+
|
|
4
|
+
function createConcurrencyLimiter(options) {
|
|
5
|
+
if (options.maxConcurrency < 1) {
|
|
6
|
+
throw createAthosError(ATHOS_ERROR_CODES.OPERATION_OVERLOADED, "Concurrency limiter requires maxConcurrency to be greater than zero.");
|
|
7
|
+
}
|
|
8
|
+
const queue = [];
|
|
9
|
+
let queueOffset = 0;
|
|
10
|
+
let activeCount = 0;
|
|
11
|
+
let pendingCount = 0;
|
|
12
|
+
const pump = ()=>{
|
|
13
|
+
while(activeCount < options.maxConcurrency && pendingCount > 0){
|
|
14
|
+
const next = queue[queueOffset];
|
|
15
|
+
queue[queueOffset] = undefined;
|
|
16
|
+
queueOffset++;
|
|
17
|
+
if (!next) {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
pendingCount -= 1;
|
|
21
|
+
if (queueOffset > 1000) {
|
|
22
|
+
queue.splice(0, queueOffset);
|
|
23
|
+
queueOffset = 0;
|
|
24
|
+
}
|
|
25
|
+
runEntry(next);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const runEntry = (entry)=>{
|
|
29
|
+
if (entry.signal && entry.abortHandler) {
|
|
30
|
+
entry.signal.removeEventListener("abort", entry.abortHandler);
|
|
31
|
+
entry.abortHandler = undefined;
|
|
32
|
+
}
|
|
33
|
+
if (entry.signal?.aborted) {
|
|
34
|
+
entry.reject(createOverloadedAbortError("Queued operation was aborted before execution."));
|
|
35
|
+
pump();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
activeCount += 1;
|
|
39
|
+
const result = entry.operation(entry.signal);
|
|
40
|
+
if (result instanceof Promise) {
|
|
41
|
+
result.then(entry.resolve, entry.reject).finally(()=>{
|
|
42
|
+
activeCount -= 1;
|
|
43
|
+
pump();
|
|
44
|
+
});
|
|
45
|
+
} else {
|
|
46
|
+
try {
|
|
47
|
+
entry.resolve(result);
|
|
48
|
+
} catch (e) {
|
|
49
|
+
entry.reject(e);
|
|
50
|
+
} finally{
|
|
51
|
+
activeCount -= 1;
|
|
52
|
+
pump();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
return {
|
|
57
|
+
get activeCount () {
|
|
58
|
+
return activeCount;
|
|
59
|
+
},
|
|
60
|
+
get pendingCount () {
|
|
61
|
+
return pendingCount;
|
|
62
|
+
},
|
|
63
|
+
run (operation, runOptions = {}) {
|
|
64
|
+
if (runOptions.signal?.aborted) {
|
|
65
|
+
return Promise.reject(createOverloadedAbortError("Operation was aborted before entering the limiter."));
|
|
66
|
+
}
|
|
67
|
+
if (activeCount < options.maxConcurrency) {
|
|
68
|
+
return new Promise((resolve, reject)=>{
|
|
69
|
+
runEntry({
|
|
70
|
+
operation,
|
|
71
|
+
signal: runOptions.signal,
|
|
72
|
+
resolve,
|
|
73
|
+
reject
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
const maxQueueSize = options.maxQueueSize ?? Number.POSITIVE_INFINITY;
|
|
78
|
+
if (pendingCount >= maxQueueSize) {
|
|
79
|
+
return Promise.reject(createAthosError(ATHOS_ERROR_CODES.OPERATION_OVERLOADED, "Concurrency limiter queue is full.", {
|
|
80
|
+
details: {
|
|
81
|
+
activeCount,
|
|
82
|
+
pendingCount,
|
|
83
|
+
maxConcurrency: options.maxConcurrency,
|
|
84
|
+
maxQueueSize
|
|
85
|
+
}
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
return new Promise((resolve, reject)=>{
|
|
89
|
+
const entry = {
|
|
90
|
+
operation,
|
|
91
|
+
signal: runOptions.signal,
|
|
92
|
+
resolve,
|
|
93
|
+
reject
|
|
94
|
+
};
|
|
95
|
+
if (runOptions.signal) {
|
|
96
|
+
const onAbort = ()=>{
|
|
97
|
+
const idx = queue.indexOf(entry, queueOffset);
|
|
98
|
+
if (idx !== -1) {
|
|
99
|
+
queue[idx] = undefined;
|
|
100
|
+
pendingCount -= 1;
|
|
101
|
+
runOptions.signal?.removeEventListener("abort", onAbort);
|
|
102
|
+
entry.abortHandler = undefined;
|
|
103
|
+
reject(createOverloadedAbortError("Queued operation was aborted."));
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
entry.abortHandler = onAbort;
|
|
107
|
+
runOptions.signal.addEventListener("abort", onAbort, {
|
|
108
|
+
once: true
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
queue.push(entry);
|
|
112
|
+
pendingCount += 1;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function createCircuitBreaker(options) {
|
|
118
|
+
if (options.failureThreshold < 1) {
|
|
119
|
+
throw createAthosError(ATHOS_ERROR_CODES.OPERATION_CIRCUIT_OPEN, "Circuit breaker requires failureThreshold to be greater than zero.");
|
|
120
|
+
}
|
|
121
|
+
let state = "closed";
|
|
122
|
+
let failureCount = 0;
|
|
123
|
+
let openedAt;
|
|
124
|
+
let halfOpenAttempts = 0;
|
|
125
|
+
const moveToOpen = ()=>{
|
|
126
|
+
state = "open";
|
|
127
|
+
openedAt = Date.now();
|
|
128
|
+
halfOpenAttempts = 0;
|
|
129
|
+
};
|
|
130
|
+
const moveToClosed = ()=>{
|
|
131
|
+
state = "closed";
|
|
132
|
+
failureCount = 0;
|
|
133
|
+
openedAt = undefined;
|
|
134
|
+
halfOpenAttempts = 0;
|
|
135
|
+
};
|
|
136
|
+
const moveToHalfOpenIfReady = ()=>{
|
|
137
|
+
if (state === "open" && openedAt !== undefined && Date.now() - openedAt >= options.resetAfterMs) {
|
|
138
|
+
state = "half-open";
|
|
139
|
+
halfOpenAttempts = 0;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
return {
|
|
143
|
+
get state () {
|
|
144
|
+
moveToHalfOpenIfReady();
|
|
145
|
+
return state;
|
|
146
|
+
},
|
|
147
|
+
get failureCount () {
|
|
148
|
+
return failureCount;
|
|
149
|
+
},
|
|
150
|
+
get openedAt () {
|
|
151
|
+
return openedAt;
|
|
152
|
+
},
|
|
153
|
+
async execute (operation, runOptions = {}) {
|
|
154
|
+
moveToHalfOpenIfReady();
|
|
155
|
+
if (state === "open") {
|
|
156
|
+
throw createAthosError(ATHOS_ERROR_CODES.OPERATION_CIRCUIT_OPEN, "Circuit breaker is open.", {
|
|
157
|
+
details: {
|
|
158
|
+
openedAt,
|
|
159
|
+
resetAfterMs: options.resetAfterMs
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
if (state === "half-open") {
|
|
164
|
+
const allowedAttempts = options.halfOpenMaxAttempts ?? 1;
|
|
165
|
+
if (halfOpenAttempts >= allowedAttempts) {
|
|
166
|
+
throw createAthosError(ATHOS_ERROR_CODES.OPERATION_CIRCUIT_OPEN, "Circuit breaker half-open probe limit reached.", {
|
|
167
|
+
details: {
|
|
168
|
+
openedAt,
|
|
169
|
+
allowedAttempts
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
halfOpenAttempts += 1;
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
const result = await operation(runOptions.signal);
|
|
177
|
+
moveToClosed();
|
|
178
|
+
return result;
|
|
179
|
+
} catch (error) {
|
|
180
|
+
failureCount += 1;
|
|
181
|
+
if (state === "half-open" || failureCount >= options.failureThreshold) {
|
|
182
|
+
moveToOpen();
|
|
183
|
+
}
|
|
184
|
+
throw error;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function createResilientExecutor(options = {}) {
|
|
190
|
+
return {
|
|
191
|
+
async execute (operation, runOptions = {}) {
|
|
192
|
+
const attempt = (signal)=>{
|
|
193
|
+
const result = options.timeoutMs ? withTimeout((timeoutSignal)=>operation(timeoutSignal), options.timeoutMs, signal) : operation(signal);
|
|
194
|
+
return result instanceof Promise ? result : Promise.resolve(result);
|
|
195
|
+
};
|
|
196
|
+
const executeWithBreaker = (signal)=>{
|
|
197
|
+
if (options.circuitBreaker) {
|
|
198
|
+
return options.circuitBreaker.execute((breakerSignal)=>attempt(breakerSignal), {
|
|
199
|
+
...signal === undefined ? {} : {
|
|
200
|
+
signal
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
return attempt(signal);
|
|
205
|
+
};
|
|
206
|
+
if (options.limiter) {
|
|
207
|
+
return options.limiter.run((limiterSignal)=>executeWithBreaker(limiterSignal), runOptions);
|
|
208
|
+
}
|
|
209
|
+
return executeWithBreaker(runOptions.signal);
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
function withTimeout(operation, timeoutMs, upstreamSignal) {
|
|
214
|
+
if (timeoutMs <= 0) {
|
|
215
|
+
return Promise.reject(createAthosError(ATHOS_ERROR_CODES.OPERATION_TIMEOUT, "Operation timeout must be greater than zero."));
|
|
216
|
+
}
|
|
217
|
+
if (upstreamSignal?.aborted) {
|
|
218
|
+
return Promise.reject(upstreamSignal.reason || new Error("Aborted"));
|
|
219
|
+
}
|
|
220
|
+
const controller = new AbortController();
|
|
221
|
+
const detach = upstreamSignal ? forwardAbort(upstreamSignal, controller) : undefined;
|
|
222
|
+
return new Promise((resolve, reject)=>{
|
|
223
|
+
let settled = false;
|
|
224
|
+
const timeout = setTimeout(()=>{
|
|
225
|
+
if (settled) return;
|
|
226
|
+
settled = true;
|
|
227
|
+
controller.abort();
|
|
228
|
+
reject(createAthosError(ATHOS_ERROR_CODES.OPERATION_TIMEOUT, "Operation timed out.", {
|
|
229
|
+
details: {
|
|
230
|
+
timeoutMs
|
|
231
|
+
}
|
|
232
|
+
}));
|
|
233
|
+
}, timeoutMs);
|
|
234
|
+
const cleanup = (err)=>{
|
|
235
|
+
if (settled) return;
|
|
236
|
+
settled = true;
|
|
237
|
+
clearTimeout(timeout);
|
|
238
|
+
if (detach) detach();
|
|
239
|
+
if (err) reject(err);
|
|
240
|
+
};
|
|
241
|
+
try {
|
|
242
|
+
const res = operation(controller.signal);
|
|
243
|
+
if (res instanceof Promise) {
|
|
244
|
+
res.then((val)=>{
|
|
245
|
+
cleanup();
|
|
246
|
+
resolve(val);
|
|
247
|
+
}, cleanup);
|
|
248
|
+
} else {
|
|
249
|
+
cleanup();
|
|
250
|
+
resolve(res);
|
|
251
|
+
}
|
|
252
|
+
} catch (e) {
|
|
253
|
+
cleanup(e);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
function forwardAbort(signal, controller) {
|
|
258
|
+
if (!signal) {
|
|
259
|
+
return ()=>undefined;
|
|
260
|
+
}
|
|
261
|
+
if (signal.aborted) {
|
|
262
|
+
controller.abort();
|
|
263
|
+
return ()=>undefined;
|
|
264
|
+
}
|
|
265
|
+
const onAbort = ()=>controller.abort();
|
|
266
|
+
signal.addEventListener("abort", onAbort, {
|
|
267
|
+
once: true
|
|
268
|
+
});
|
|
269
|
+
return ()=>signal.removeEventListener("abort", onAbort);
|
|
270
|
+
}
|
|
271
|
+
function createOverloadedAbortError(message) {
|
|
272
|
+
return createAthosError(ATHOS_ERROR_CODES.OPERATION_OVERLOADED, message);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export { createCircuitBreaker, createConcurrencyLimiter, createResilientExecutor };
|
|
276
|
+
//# sourceMappingURL=resilience.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resilience.js","sources":["../../../packages/pro/src/resilience.ts"],"sourcesContent":["import { ATHOS_ERROR_CODES } from \"@athosjs/constants\";\nimport { createAthosError } from \"@athosjs/core\";\nimport type {\n\tCircuitBreaker,\n\tCircuitBreakerOptions,\n\tCircuitBreakerState,\n\tConcurrencyLimiter,\n\tConcurrencyLimiterOptions,\n\tResilientExecutor,\n\tResilientExecutorOptions,\n} from \"@athosjs/types/pro\";\n\nexport type {\n\tCircuitBreaker,\n\tCircuitBreakerOptions,\n\tCircuitBreakerState,\n\tConcurrencyLimiter,\n\tConcurrencyLimiterOptions,\n\tResilientExecutor,\n\tResilientExecutorOptions,\n};\n\ninterface QueueEntry<T> {\n\treadonly operation: (signal?: AbortSignal) => Promise<T> | T;\n\treadonly signal?: AbortSignal | undefined;\n\treadonly resolve: (value: T | PromiseLike<T>) => void;\n\treadonly reject: (reason?: unknown) => void;\n\tabortHandler?: (() => void) | undefined;\n}\n\nexport function createConcurrencyLimiter(options: ConcurrencyLimiterOptions): ConcurrencyLimiter {\n\tif (options.maxConcurrency < 1) {\n\t\tthrow createAthosError(\n\t\t\tATHOS_ERROR_CODES.OPERATION_OVERLOADED,\n\t\t\t\"Concurrency limiter requires maxConcurrency to be greater than zero.\",\n\t\t);\n\t}\n\n\tconst queue: QueueEntry<any>[] = [];\n\tlet queueOffset = 0;\n\tlet activeCount = 0;\n\tlet pendingCount = 0;\n\n\tconst pump = (): void => {\n\t\twhile (activeCount < options.maxConcurrency && pendingCount > 0) {\n\t\t\tconst next = queue[queueOffset];\n\t\t\tqueue[queueOffset] = undefined as any;\n\t\t\tqueueOffset++;\n\n\t\t\tif (!next) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tpendingCount -= 1;\n\n\t\t\tif (queueOffset > 1000) {\n\t\t\t\tqueue.splice(0, queueOffset);\n\t\t\t\tqueueOffset = 0;\n\t\t\t}\n\n\t\t\trunEntry(next);\n\t\t}\n\t};\n\n\tconst runEntry = (entry: QueueEntry<any>): void => {\n\t\tif (entry.signal && entry.abortHandler) {\n\t\t\tentry.signal.removeEventListener(\"abort\", entry.abortHandler);\n\t\t\tentry.abortHandler = undefined;\n\t\t}\n\n\t\tif (entry.signal?.aborted) {\n\t\t\tentry.reject(createOverloadedAbortError(\"Queued operation was aborted before execution.\"));\n\t\t\tpump();\n\t\t\treturn;\n\t\t}\n\n\t\tactiveCount += 1;\n\n\t\tconst result = entry.operation(entry.signal);\n\t\tif (result instanceof Promise) {\n\t\t\tresult.then(entry.resolve, entry.reject).finally(() => {\n\t\t\t\tactiveCount -= 1;\n\t\t\t\tpump();\n\t\t\t});\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tentry.resolve(result);\n\t\t\t} catch (e) {\n\t\t\t\tentry.reject(e);\n\t\t\t} finally {\n\t\t\t\tactiveCount -= 1;\n\t\t\t\tpump();\n\t\t\t}\n\t\t}\n\t};\n\n\treturn {\n\t\tget activeCount(): number {\n\t\t\treturn activeCount;\n\t\t},\n\t\tget pendingCount(): number {\n\t\t\treturn pendingCount;\n\t\t},\n\t\trun<T>(operation: (signal?: AbortSignal) => Promise<T> | T, runOptions: { signal?: AbortSignal } = {}): Promise<T> {\n\t\t\tif (runOptions.signal?.aborted) {\n\t\t\t\treturn Promise.reject(createOverloadedAbortError(\"Operation was aborted before entering the limiter.\"));\n\t\t\t}\n\n\t\t\tif (activeCount < options.maxConcurrency) {\n\t\t\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\t\t\trunEntry({\n\t\t\t\t\t\toperation,\n\t\t\t\t\t\tsignal: runOptions.signal,\n\t\t\t\t\t\tresolve,\n\t\t\t\t\t\treject,\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst maxQueueSize = options.maxQueueSize ?? Number.POSITIVE_INFINITY;\n\n\t\t\tif (pendingCount >= maxQueueSize) {\n\t\t\t\treturn Promise.reject(\n\t\t\t\t\tcreateAthosError(ATHOS_ERROR_CODES.OPERATION_OVERLOADED, \"Concurrency limiter queue is full.\", {\n\t\t\t\t\t\tdetails: {\n\t\t\t\t\t\t\tactiveCount,\n\t\t\t\t\t\t\tpendingCount,\n\t\t\t\t\t\t\tmaxConcurrency: options.maxConcurrency,\n\t\t\t\t\t\t\tmaxQueueSize,\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\t\tconst entry: QueueEntry<T> = {\n\t\t\t\t\toperation,\n\t\t\t\t\tsignal: runOptions.signal,\n\t\t\t\t\tresolve,\n\t\t\t\t\treject,\n\t\t\t\t};\n\n\t\t\t\tif (runOptions.signal) {\n\t\t\t\t\tconst onAbort = (): void => {\n\t\t\t\t\t\tconst idx = queue.indexOf(entry as any, queueOffset);\n\t\t\t\t\t\tif (idx !== -1) {\n\t\t\t\t\t\t\tqueue[idx] = undefined as any;\n\t\t\t\t\t\t\tpendingCount -= 1;\n\t\t\t\t\t\t\trunOptions.signal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\tentry.abortHandler = undefined;\n\t\t\t\t\t\t\treject(createOverloadedAbortError(\"Queued operation was aborted.\"));\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\n\t\t\t\t\tentry.abortHandler = onAbort;\n\t\t\t\t\trunOptions.signal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t}\n\n\t\t\t\tqueue.push(entry as QueueEntry<any>);\n\t\t\t\tpendingCount += 1;\n\t\t\t});\n\t\t},\n\t};\n}\n\nexport function createCircuitBreaker(options: CircuitBreakerOptions): CircuitBreaker {\n\tif (options.failureThreshold < 1) {\n\t\tthrow createAthosError(\n\t\t\tATHOS_ERROR_CODES.OPERATION_CIRCUIT_OPEN,\n\t\t\t\"Circuit breaker requires failureThreshold to be greater than zero.\",\n\t\t);\n\t}\n\n\tlet state: CircuitBreakerState = \"closed\";\n\tlet failureCount = 0;\n\tlet openedAt: number | undefined;\n\tlet halfOpenAttempts = 0;\n\n\tconst moveToOpen = (): void => {\n\t\tstate = \"open\";\n\t\topenedAt = Date.now();\n\t\thalfOpenAttempts = 0;\n\t};\n\n\tconst moveToClosed = (): void => {\n\t\tstate = \"closed\";\n\t\tfailureCount = 0;\n\t\topenedAt = undefined;\n\t\thalfOpenAttempts = 0;\n\t};\n\n\tconst moveToHalfOpenIfReady = (): void => {\n\t\tif (state === \"open\" && openedAt !== undefined && Date.now() - openedAt >= options.resetAfterMs) {\n\t\t\tstate = \"half-open\";\n\t\t\thalfOpenAttempts = 0;\n\t\t}\n\t};\n\n\treturn {\n\t\tget state(): CircuitBreakerState {\n\t\t\tmoveToHalfOpenIfReady();\n\t\t\treturn state;\n\t\t},\n\t\tget failureCount(): number {\n\t\t\treturn failureCount;\n\t\t},\n\t\tget openedAt(): number | undefined {\n\t\t\treturn openedAt;\n\t\t},\n\t\tasync execute<T>(\n\t\t\toperation: (signal?: AbortSignal) => Promise<T> | T,\n\t\t\trunOptions: { signal?: AbortSignal } = {},\n\t\t): Promise<T> {\n\t\t\tmoveToHalfOpenIfReady();\n\n\t\t\tif (state === \"open\") {\n\t\t\t\tthrow createAthosError(ATHOS_ERROR_CODES.OPERATION_CIRCUIT_OPEN, \"Circuit breaker is open.\", {\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\topenedAt,\n\t\t\t\t\t\tresetAfterMs: options.resetAfterMs,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (state === \"half-open\") {\n\t\t\t\tconst allowedAttempts = options.halfOpenMaxAttempts ?? 1;\n\n\t\t\t\tif (halfOpenAttempts >= allowedAttempts) {\n\t\t\t\t\tthrow createAthosError(\n\t\t\t\t\t\tATHOS_ERROR_CODES.OPERATION_CIRCUIT_OPEN,\n\t\t\t\t\t\t\"Circuit breaker half-open probe limit reached.\",\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdetails: {\n\t\t\t\t\t\t\t\topenedAt,\n\t\t\t\t\t\t\t\tallowedAttempts,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\thalfOpenAttempts += 1;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst result = await operation(runOptions.signal);\n\t\t\t\tmoveToClosed();\n\t\t\t\treturn result;\n\t\t\t} catch (error) {\n\t\t\t\tfailureCount += 1;\n\n\t\t\t\tif (state === \"half-open\" || failureCount >= options.failureThreshold) {\n\t\t\t\t\tmoveToOpen();\n\t\t\t\t}\n\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\t};\n}\n\nexport function createResilientExecutor(options: ResilientExecutorOptions = {}): ResilientExecutor {\n\treturn {\n\t\tasync execute<T>(\n\t\t\toperation: (signal?: AbortSignal) => Promise<T> | T,\n\t\t\trunOptions: { signal?: AbortSignal } = {},\n\t\t): Promise<T> {\n\t\t\tconst attempt = (signal?: AbortSignal): Promise<T> => {\n\t\t\t\tconst result = options.timeoutMs\n\t\t\t\t\t? withTimeout((timeoutSignal) => operation(timeoutSignal), options.timeoutMs, signal)\n\t\t\t\t\t: operation(signal);\n\n\t\t\t\treturn result instanceof Promise ? result : Promise.resolve(result);\n\t\t\t};\n\n\t\t\tconst executeWithBreaker = (signal?: AbortSignal): Promise<T> => {\n\t\t\t\tif (options.circuitBreaker) {\n\t\t\t\t\treturn options.circuitBreaker.execute((breakerSignal) => attempt(breakerSignal), {\n\t\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\treturn attempt(signal);\n\t\t\t};\n\n\t\t\tif (options.limiter) {\n\t\t\t\treturn options.limiter.run((limiterSignal) => executeWithBreaker(limiterSignal), runOptions);\n\t\t\t}\n\n\t\t\treturn executeWithBreaker(runOptions.signal);\n\t\t},\n\t};\n}\n\nfunction withTimeout<T>(\n\toperation: (signal?: AbortSignal) => Promise<T> | T,\n\ttimeoutMs: number,\n\tupstreamSignal?: AbortSignal,\n): Promise<T> {\n\tif (timeoutMs <= 0) {\n\t\treturn Promise.reject(\n\t\t\tcreateAthosError(ATHOS_ERROR_CODES.OPERATION_TIMEOUT, \"Operation timeout must be greater than zero.\"),\n\t\t);\n\t}\n\n\tif (upstreamSignal?.aborted) {\n\t\treturn Promise.reject(upstreamSignal.reason || new Error(\"Aborted\"));\n\t}\n\n\tconst controller = new AbortController();\n\tconst detach = upstreamSignal ? forwardAbort(upstreamSignal, controller) : undefined;\n\n\treturn new Promise<T>((resolve, reject) => {\n\t\tlet settled = false;\n\t\tconst timeout = setTimeout(() => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcontroller.abort();\n\t\t\treject(createAthosError(ATHOS_ERROR_CODES.OPERATION_TIMEOUT, \"Operation timed out.\", { details: { timeoutMs } }));\n\t\t}, timeoutMs);\n\n\t\tconst cleanup = (err?: any) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timeout);\n\t\t\tif (detach) detach();\n\t\t\tif (err) reject(err);\n\t\t};\n\n\t\ttry {\n\t\t\tconst res = operation(controller.signal);\n\t\t\tif (res instanceof Promise) {\n\t\t\t\tres.then((val) => {\n\t\t\t\t\tcleanup();\n\t\t\t\t\tresolve(val);\n\t\t\t\t}, cleanup);\n\t\t\t} else {\n\t\t\t\tcleanup();\n\t\t\t\tresolve(res);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tcleanup(e);\n\t\t}\n\t});\n}\n\nfunction forwardAbort(signal: AbortSignal | undefined, controller: AbortController): () => void {\n\tif (!signal) {\n\t\treturn () => undefined;\n\t}\n\n\tif (signal.aborted) {\n\t\tcontroller.abort();\n\t\treturn () => undefined;\n\t}\n\n\tconst onAbort = (): void => controller.abort();\n\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\treturn () => signal.removeEventListener(\"abort\", onAbort);\n}\n\nfunction createOverloadedAbortError(message: string) {\n\treturn createAthosError(ATHOS_ERROR_CODES.OPERATION_OVERLOADED, message);\n}\n"],"names":["createConcurrencyLimiter","options","maxConcurrency","createAthosError","ATHOS_ERROR_CODES","OPERATION_OVERLOADED","queue","queueOffset","activeCount","pendingCount","pump","next","undefined","splice","runEntry","entry","signal","abortHandler","removeEventListener","aborted","reject","createOverloadedAbortError","result","operation","Promise","then","resolve","finally","e","run","runOptions","maxQueueSize","Number","POSITIVE_INFINITY","details","onAbort","idx","indexOf","addEventListener","once","push","createCircuitBreaker","failureThreshold","OPERATION_CIRCUIT_OPEN","state","failureCount","openedAt","halfOpenAttempts","moveToOpen","Date","now","moveToClosed","moveToHalfOpenIfReady","resetAfterMs","execute","allowedAttempts","halfOpenMaxAttempts","error","createResilientExecutor","attempt","timeoutMs","withTimeout","timeoutSignal","executeWithBreaker","circuitBreaker","breakerSignal","limiter","limiterSignal","upstreamSignal","OPERATION_TIMEOUT","reason","Error","controller","AbortController","detach","forwardAbort","settled","timeout","setTimeout","abort","cleanup","err","clearTimeout","res","val","message"],"mappings":"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"}
|
package/security.cjs
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var auth = require('@athosjs/auth');
|
|
4
|
+
var constants = require('@athosjs/constants');
|
|
5
|
+
var core = require('@athosjs/core');
|
|
6
|
+
var http = require('@athosjs/http');
|
|
7
|
+
|
|
8
|
+
function createProAuthenticationMiddleware(options) {
|
|
9
|
+
return async (context, next)=>{
|
|
10
|
+
const typedContext = context;
|
|
11
|
+
const existing = auth.getAuthentication(typedContext);
|
|
12
|
+
if (options.skipIfAuthenticated !== false && existing?.authenticated) {
|
|
13
|
+
return next();
|
|
14
|
+
}
|
|
15
|
+
const strategies = typeof options.strategies === "function" ? options.strategies(typedContext) : options.strategies;
|
|
16
|
+
const result = await auth.authenticate(typedContext, strategies, {
|
|
17
|
+
...options.stopOnFailure === undefined ? {} : {
|
|
18
|
+
stopOnFailure: options.stopOnFailure
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
auth.attachAuthentication(typedContext, result);
|
|
22
|
+
if (result.authenticated || !options.required) {
|
|
23
|
+
return next();
|
|
24
|
+
}
|
|
25
|
+
if (options.onUnauthenticated) {
|
|
26
|
+
return options.onUnauthenticated(typedContext, result);
|
|
27
|
+
}
|
|
28
|
+
return createAuthenticationFailureResponse(result);
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function createProAuthorizationMiddleware(options) {
|
|
32
|
+
return async (context, next)=>{
|
|
33
|
+
const typedContext = context;
|
|
34
|
+
const authState = auth.getAuthentication(typedContext);
|
|
35
|
+
let subject;
|
|
36
|
+
try {
|
|
37
|
+
subject = options.getSubject ? options.getSubject(typedContext) : auth.authenticatedSubject(authState ?? {
|
|
38
|
+
authenticated: false,
|
|
39
|
+
identity: undefined,
|
|
40
|
+
attempts: [],
|
|
41
|
+
challenges: []
|
|
42
|
+
});
|
|
43
|
+
} catch (error) {
|
|
44
|
+
if (error instanceof core.AthosError && error.code === constants.ATHOS_ERROR_CODES.AUTH_UNAUTHENTICATED) {
|
|
45
|
+
typedContext.state.set(auth.AUTH_STATE_KEY, authState);
|
|
46
|
+
if (options.onUnauthenticated) {
|
|
47
|
+
return options.onUnauthenticated(typedContext, error);
|
|
48
|
+
}
|
|
49
|
+
return createAuthenticationFailureResponse({
|
|
50
|
+
attempts: authState?.attempts ?? [],
|
|
51
|
+
challenges: authState?.challenges ?? [],
|
|
52
|
+
...authState?.failure ? {
|
|
53
|
+
failure: authState.failure
|
|
54
|
+
} : {}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
const decisionInput = {
|
|
60
|
+
subject,
|
|
61
|
+
...options.getResource ? {
|
|
62
|
+
resource: options.getResource(typedContext)
|
|
63
|
+
} : {},
|
|
64
|
+
...options.getEnvironment ? {
|
|
65
|
+
context: options.getEnvironment(typedContext)
|
|
66
|
+
} : {}
|
|
67
|
+
};
|
|
68
|
+
const decision = await auth.authorize(options.policy, decisionInput);
|
|
69
|
+
typedContext.state.set(constants.ATHOS_STATE_KEYS.AUTHZ, decision);
|
|
70
|
+
if (decision.allowed) {
|
|
71
|
+
return next();
|
|
72
|
+
}
|
|
73
|
+
if (options.onForbidden) {
|
|
74
|
+
return options.onForbidden(typedContext, decision);
|
|
75
|
+
}
|
|
76
|
+
return createAuthorizationFailureResponse(decision);
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function createAuthenticationFailureResponse(result) {
|
|
80
|
+
const response = http.json({
|
|
81
|
+
error: "unauthorized",
|
|
82
|
+
authenticated: false,
|
|
83
|
+
...result.failure ? {
|
|
84
|
+
failure: {
|
|
85
|
+
strategy: result.failure.strategy,
|
|
86
|
+
reason: result.failure.reason,
|
|
87
|
+
reasonCode: result.failure.reasonCode
|
|
88
|
+
}
|
|
89
|
+
} : {},
|
|
90
|
+
challenges: result.challenges
|
|
91
|
+
}, 401);
|
|
92
|
+
const headerValue = formatWwwAuthenticateHeader(result.challenges ?? []);
|
|
93
|
+
if (headerValue) {
|
|
94
|
+
response.headers.set?.("www-authenticate", headerValue);
|
|
95
|
+
}
|
|
96
|
+
return response;
|
|
97
|
+
}
|
|
98
|
+
function createAuthorizationFailureResponse(decision) {
|
|
99
|
+
return http.json({
|
|
100
|
+
error: "forbidden",
|
|
101
|
+
allowed: false,
|
|
102
|
+
...decision.policy ? {
|
|
103
|
+
policy: decision.policy
|
|
104
|
+
} : {},
|
|
105
|
+
...decision.code ? {
|
|
106
|
+
code: decision.code
|
|
107
|
+
} : {},
|
|
108
|
+
...decision.reason ? {
|
|
109
|
+
reason: decision.reason
|
|
110
|
+
} : {}
|
|
111
|
+
}, 403);
|
|
112
|
+
}
|
|
113
|
+
function formatWwwAuthenticateHeader(challenges) {
|
|
114
|
+
if (challenges.length === 0) {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
return challenges.map(formatChallengeInstruction).join(", ");
|
|
118
|
+
}
|
|
119
|
+
function formatChallengeInstruction(challenge) {
|
|
120
|
+
const parts = [
|
|
121
|
+
challenge.scheme
|
|
122
|
+
];
|
|
123
|
+
if (challenge.realm) {
|
|
124
|
+
parts.push(`realm="${escapeHeaderValue(challenge.realm)}"`);
|
|
125
|
+
}
|
|
126
|
+
if (challenge.params) {
|
|
127
|
+
for (const [key, value] of Object.entries(challenge.params)){
|
|
128
|
+
parts.push(`${key}="${escapeHeaderValue(value)}"`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return parts.join(" ");
|
|
132
|
+
}
|
|
133
|
+
function escapeHeaderValue(value) {
|
|
134
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
exports.createAuthenticationFailureResponse = createAuthenticationFailureResponse;
|
|
138
|
+
exports.createAuthorizationFailureResponse = createAuthorizationFailureResponse;
|
|
139
|
+
exports.createProAuthenticationMiddleware = createProAuthenticationMiddleware;
|
|
140
|
+
exports.createProAuthorizationMiddleware = createProAuthorizationMiddleware;
|
|
141
|
+
exports.formatWwwAuthenticateHeader = formatWwwAuthenticateHeader;
|
|
142
|
+
//# sourceMappingURL=security.cjs.map
|
package/security.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.cjs","sources":["../../../packages/pro/src/security.ts"],"sourcesContent":["import {\n\tAUTH_STATE_KEY,\n\ttype AuthenticationResult,\n\ttype AuthIdentity,\n\ttype AuthStateCarrier,\n\ttype AuthStrategy,\n\tattachAuthentication,\n\tauthenticate,\n\tauthenticatedSubject,\n\tauthorize,\n\ttype ChallengeInstruction,\n\tgetAuthentication,\n} from \"@athosjs/auth\";\nimport type { AuthorizationContext, AuthorizationDecision, AuthorizationPolicy } from \"@athosjs/authz\";\nimport { ATHOS_ERROR_CODES, ATHOS_STATE_KEYS } from \"@athosjs/constants\";\nimport { AthosError } from \"@athosjs/core\";\nimport { type HttpContext, type HttpMiddleware, type HttpResponse, json } from \"@athosjs/http\";\nimport type { ProAuthenticationMiddlewareOptions, ProAuthorizationMiddlewareOptions } from \"@athosjs/types/pro\";\n\nexport type { ProAuthenticationMiddlewareOptions, ProAuthorizationMiddlewareOptions };\n\nexport function createProAuthenticationMiddleware<\n\tTContext extends HttpContext & AuthStateCarrier,\n\tTIdentity extends AuthIdentity = AuthIdentity,\n>(options: ProAuthenticationMiddlewareOptions<TContext, TIdentity>): HttpMiddleware {\n\treturn async (context, next) => {\n\t\tconst typedContext = context as TContext;\n\t\tconst existing = getAuthentication<TIdentity>(typedContext);\n\n\t\tif (options.skipIfAuthenticated !== false && existing?.authenticated) {\n\t\t\treturn next();\n\t\t}\n\n\t\tconst strategies =\n\t\t\ttypeof options.strategies === \"function\"\n\t\t\t\t? (options.strategies as any)(typedContext)\n\t\t\t\t: (options.strategies as readonly AuthStrategy<TContext, TIdentity>[]);\n\t\tconst result = await authenticate(typedContext, strategies as any, {\n\t\t\t...(options.stopOnFailure === undefined ? {} : { stopOnFailure: options.stopOnFailure }),\n\t\t});\n\n\t\tattachAuthentication(typedContext, result);\n\n\t\tif (result.authenticated || !options.required) {\n\t\t\treturn next();\n\t\t}\n\n\t\tif (options.onUnauthenticated) {\n\t\t\treturn (options.onUnauthenticated as any)(typedContext, result) as any;\n\t\t}\n\n\t\treturn createAuthenticationFailureResponse(result);\n\t};\n}\n\nexport function createProAuthorizationMiddleware<\n\tTContext extends HttpContext & AuthStateCarrier,\n\tTIdentity extends AuthIdentity = AuthIdentity,\n\tTResource = unknown,\n\tTEnvironment = unknown,\n>(options: ProAuthorizationMiddlewareOptions<TContext, TIdentity, TResource, TEnvironment>): HttpMiddleware {\n\treturn async (context, next) => {\n\t\tconst typedContext = context as TContext;\n\t\tconst authState = getAuthentication<TIdentity>(typedContext);\n\n\t\tlet subject: TIdentity;\n\n\t\ttry {\n\t\t\tsubject = options.getSubject\n\t\t\t\t? options.getSubject(typedContext)\n\t\t\t\t: authenticatedSubject<TIdentity>((authState ?? {\n\t\t\t\t\t\tauthenticated: false,\n\t\t\t\t\t\tidentity: undefined as any,\n\t\t\t\t\t\tattempts: [],\n\t\t\t\t\t\tchallenges: [],\n\t\t\t\t\t}) as AuthStateCarrier);\n\t\t} catch (error) {\n\t\t\tif (error instanceof AthosError && error.code === ATHOS_ERROR_CODES.AUTH_UNAUTHENTICATED) {\n\t\t\t\ttypedContext.state.set(AUTH_STATE_KEY, authState);\n\n\t\t\t\tif (options.onUnauthenticated) {\n\t\t\t\t\treturn (options.onUnauthenticated as any)(typedContext, error) as any;\n\t\t\t\t}\n\n\t\t\t\treturn createAuthenticationFailureResponse({\n\t\t\t\t\tauthenticated: false,\n\t\t\t\t\tattempts: authState?.attempts ?? [],\n\t\t\t\t\tchallenges: authState?.challenges ?? [],\n\t\t\t\t\t...(authState?.failure ? { failure: authState.failure } : {}),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tthrow error;\n\t\t}\n\n\t\tconst decisionInput: AuthorizationContext<TIdentity, TResource, TEnvironment> = {\n\t\t\tsubject,\n\t\t\t...(options.getResource ? { resource: options.getResource(typedContext) } : {}),\n\t\t\t...(options.getEnvironment ? { context: options.getEnvironment(typedContext) } : {}),\n\t\t};\n\t\tconst decision = await authorize(\n\t\t\toptions.policy as AuthorizationPolicy<TIdentity, TResource, TEnvironment>,\n\t\t\tdecisionInput,\n\t\t);\n\n\t\ttypedContext.state.set(ATHOS_STATE_KEYS.AUTHZ, decision);\n\n\t\tif (decision.allowed) {\n\t\t\treturn next();\n\t\t}\n\n\t\tif (options.onForbidden) {\n\t\t\treturn (options.onForbidden as any)(typedContext, decision) as any;\n\t\t}\n\n\t\treturn createAuthorizationFailureResponse(decision);\n\t};\n}\n\nexport function createAuthenticationFailureResponse<TIdentity extends AuthIdentity = AuthIdentity>(\n\tresult: AuthenticationResult<TIdentity>,\n): HttpResponse {\n\tconst response = json(\n\t\t{\n\t\t\terror: \"unauthorized\",\n\t\t\tauthenticated: false,\n\t\t\t...(result.failure\n\t\t\t\t? {\n\t\t\t\t\t\tfailure: {\n\t\t\t\t\t\t\tstrategy: result.failure.strategy,\n\t\t\t\t\t\t\treason: result.failure.reason,\n\t\t\t\t\t\t\treasonCode: result.failure.reasonCode,\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t\tchallenges: result.challenges,\n\t\t},\n\t\t401,\n\t);\n\tconst headerValue = formatWwwAuthenticateHeader(result.challenges ?? []);\n\n\tif (headerValue) {\n\t\t(response.headers as any).set?.(\"www-authenticate\", headerValue);\n\t}\n\n\treturn response;\n}\n\nexport function createAuthorizationFailureResponse(decision: AuthorizationDecision): HttpResponse {\n\treturn json(\n\t\t{\n\t\t\terror: \"forbidden\",\n\t\t\tallowed: false,\n\t\t\t...(decision.policy ? { policy: decision.policy } : {}),\n\t\t\t...(decision.code ? { code: decision.code } : {}),\n\t\t\t...(decision.reason ? { reason: decision.reason } : {}),\n\t\t},\n\t\t403,\n\t);\n}\n\nexport function formatWwwAuthenticateHeader(challenges: readonly ChallengeInstruction[]): string | undefined {\n\tif (challenges.length === 0) {\n\t\treturn undefined;\n\t}\n\n\treturn challenges.map(formatChallengeInstruction).join(\", \");\n}\n\nfunction formatChallengeInstruction(challenge: ChallengeInstruction): string {\n\tconst parts = [challenge.scheme];\n\n\tif (challenge.realm) {\n\t\tparts.push(`realm=\"${escapeHeaderValue(challenge.realm)}\"`);\n\t}\n\n\tif (challenge.params) {\n\t\tfor (const [key, value] of Object.entries(challenge.params)) {\n\t\t\tparts.push(`${key}=\"${escapeHeaderValue(value)}\"`);\n\t\t}\n\t}\n\n\treturn parts.join(\" \");\n}\n\nfunction escapeHeaderValue(value: string): string {\n\treturn value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\n}\n"],"names":["createProAuthenticationMiddleware","options","context","next","typedContext","existing","getAuthentication","skipIfAuthenticated","authenticated","strategies","result","authenticate","stopOnFailure","undefined","attachAuthentication","required","onUnauthenticated","createAuthenticationFailureResponse","createProAuthorizationMiddleware","authState","subject","getSubject","authenticatedSubject","identity","attempts","challenges","error","AthosError","code","ATHOS_ERROR_CODES","AUTH_UNAUTHENTICATED","state","set","AUTH_STATE_KEY","failure","decisionInput","getResource","resource","getEnvironment","decision","authorize","policy","ATHOS_STATE_KEYS","AUTHZ","allowed","onForbidden","createAuthorizationFailureResponse","response","json","strategy","reason","reasonCode","headerValue","formatWwwAuthenticateHeader","headers","length","map","formatChallengeInstruction","join","challenge","parts","scheme","realm","push","escapeHeaderValue","params","key","value","Object","entries","replace"],"mappings":";;;;;;;AAqBO,SAASA,kCAGdC,OAAgE,EAAA;AACjE,IAAA,OAAO,OAAOC,OAAAA,EAASC,IAAAA,GAAAA;AACtB,QAAA,MAAMC,YAAAA,GAAeF,OAAAA;AACrB,QAAA,MAAMG,WAAWC,sBAAAA,CAA6BF,YAAAA,CAAAA;AAE9C,QAAA,IAAIH,OAAAA,CAAQM,mBAAmB,KAAK,KAAA,IAASF,UAAUG,aAAAA,EAAe;YACrE,OAAOL,IAAAA,EAAAA;AACR,QAAA;AAEA,QAAA,MAAMM,UAAAA,GACL,OAAOR,OAAAA,CAAQQ,UAAU,KAAK,UAAA,GAC3B,OAACR,CAAQQ,UAAU,CAASL,YAAAA,CAAAA,GAC3BH,OAAAA,CAAQQ,UAAU;AACvB,QAAA,MAAMC,MAAAA,GAAS,MAAMC,iBAAAA,CAAaP,YAAAA,EAAcK,UAAAA,EAAmB;AAClE,YAAA,GAAIR,OAAAA,CAAQW,aAAa,KAAKC,SAAAA,GAAY,EAAC,GAAI;AAAED,gBAAAA,aAAAA,EAAeX,QAAQW;;AACzE,SAAA,CAAA;AAEAE,QAAAA,yBAAAA,CAAqBV,YAAAA,EAAcM,MAAAA,CAAAA;AAEnC,QAAA,IAAIA,OAAOF,aAAa,IAAI,CAACP,OAAAA,CAAQc,QAAQ,EAAE;YAC9C,OAAOZ,IAAAA,EAAAA;AACR,QAAA;QAEA,IAAIF,OAAAA,CAAQe,iBAAiB,EAAE;AAC9B,YAAA,OAAO,OAACf,CAAQe,iBAAiB,CAASZ,YAAAA,EAAcM,MAAAA,CAAAA;AACzD,QAAA;AAEA,QAAA,OAAOO,mCAAAA,CAAoCP,MAAAA,CAAAA;AAC5C,IAAA,CAAA;AACD;AAEO,SAASQ,iCAKdjB,OAAwF,EAAA;AACzF,IAAA,OAAO,OAAOC,OAAAA,EAASC,IAAAA,GAAAA;AACtB,QAAA,MAAMC,YAAAA,GAAeF,OAAAA;AACrB,QAAA,MAAMiB,YAAYb,sBAAAA,CAA6BF,YAAAA,CAAAA;QAE/C,IAAIgB,OAAAA;QAEJ,IAAI;YACHA,OAAAA,GAAUnB,OAAAA,CAAQoB,UAAU,GACzBpB,OAAAA,CAAQoB,UAAU,CAACjB,YAAAA,CAAAA,GACnBkB,0BAAiCH,SAAAA,IAAa;gBAC9CX,aAAAA,EAAe,KAAA;gBACfe,QAAAA,EAAUV,SAAAA;AACVW,gBAAAA,QAAAA,EAAU,EAAE;AACZC,gBAAAA,UAAAA,EAAY;AACb,aAAA,CAAA;AACH,QAAA,CAAA,CAAE,OAAOC,KAAAA,EAAO;AACf,YAAA,IAAIA,iBAAiBC,eAAAA,IAAcD,KAAAA,CAAME,IAAI,KAAKC,2BAAAA,CAAkBC,oBAAoB,EAAE;AACzF1B,gBAAAA,YAAAA,CAAa2B,KAAK,CAACC,GAAG,CAACC,mBAAAA,EAAgBd,SAAAA,CAAAA;gBAEvC,IAAIlB,OAAAA,CAAQe,iBAAiB,EAAE;AAC9B,oBAAA,OAAO,OAACf,CAAQe,iBAAiB,CAASZ,YAAAA,EAAcsB,KAAAA,CAAAA;AACzD,gBAAA;AAEA,gBAAA,OAAOT,mCAAAA,CAAoC;oBAE1CO,QAAAA,EAAUL,SAAAA,EAAWK,YAAY,EAAE;oBACnCC,UAAAA,EAAYN,SAAAA,EAAWM,cAAc,EAAE;AACvC,oBAAA,GAAIN,WAAWe,OAAAA,GAAU;AAAEA,wBAAAA,OAAAA,EAASf,UAAUe;AAAQ,qBAAA,GAAI;AAC3D,iBAAA,CAAA;AACD,YAAA;YAEA,MAAMR,KAAAA;AACP,QAAA;AAEA,QAAA,MAAMS,aAAAA,GAA0E;AAC/Ef,YAAAA,OAAAA;YACA,GAAInB,OAAAA,CAAQmC,WAAW,GAAG;gBAAEC,QAAAA,EAAUpC,OAAAA,CAAQmC,WAAW,CAAChC,YAAAA;AAAc,aAAA,GAAI,EAAE;YAC9E,GAAIH,OAAAA,CAAQqC,cAAc,GAAG;gBAAEpC,OAAAA,EAASD,OAAAA,CAAQqC,cAAc,CAAClC,YAAAA;AAAc,aAAA,GAAI;AAClF,SAAA;AACA,QAAA,MAAMmC,QAAAA,GAAW,MAAMC,cAAAA,CACtBvC,OAAAA,CAAQwC,MAAM,EACdN,aAAAA,CAAAA;AAGD/B,QAAAA,YAAAA,CAAa2B,KAAK,CAACC,GAAG,CAACU,0BAAAA,CAAiBC,KAAK,EAAEJ,QAAAA,CAAAA;QAE/C,IAAIA,QAAAA,CAASK,OAAO,EAAE;YACrB,OAAOzC,IAAAA,EAAAA;AACR,QAAA;QAEA,IAAIF,OAAAA,CAAQ4C,WAAW,EAAE;AACxB,YAAA,OAAO,OAAC5C,CAAQ4C,WAAW,CAASzC,YAAAA,EAAcmC,QAAAA,CAAAA;AACnD,QAAA;AAEA,QAAA,OAAOO,kCAAAA,CAAmCP,QAAAA,CAAAA;AAC3C,IAAA,CAAA;AACD;AAEO,SAAStB,oCACfP,MAAuC,EAAA;AAEvC,IAAA,MAAMqC,WAAWC,SAAAA,CAChB;QACCtB,KAAAA,EAAO,cAAA;QACPlB,aAAAA,EAAe,KAAA;QACf,GAAIE,MAAAA,CAAOwB,OAAO,GACf;YACAA,OAAAA,EAAS;gBACRe,QAAAA,EAAUvC,MAAAA,CAAOwB,OAAO,CAACe,QAAQ;gBACjCC,MAAAA,EAAQxC,MAAAA,CAAOwB,OAAO,CAACgB,MAAM;gBAC7BC,UAAAA,EAAYzC,MAAAA,CAAOwB,OAAO,CAACiB;AAC5B;AACD,SAAA,GACC,EAAE;AACL1B,QAAAA,UAAAA,EAAYf,OAAOe;KACpB,EACA,GAAA,CAAA;AAED,IAAA,MAAM2B,WAAAA,GAAcC,2BAAAA,CAA4B3C,MAAAA,CAAOe,UAAU,IAAI,EAAE,CAAA;AAEvE,IAAA,IAAI2B,WAAAA,EAAa;AACfL,QAAAA,QAAAA,CAASO,OAAO,CAAStB,GAAG,GAAG,kBAAA,EAAoBoB,WAAAA,CAAAA;AACrD,IAAA;IAEA,OAAOL,QAAAA;AACR;AAEO,SAASD,mCAAmCP,QAA+B,EAAA;AACjF,IAAA,OAAOS,SAAAA,CACN;QACCtB,KAAAA,EAAO,WAAA;QACPkB,OAAAA,EAAS,KAAA;QACT,GAAIL,QAAAA,CAASE,MAAM,GAAG;AAAEA,YAAAA,MAAAA,EAAQF,SAASE;AAAO,SAAA,GAAI,EAAE;QACtD,GAAIF,QAAAA,CAASX,IAAI,GAAG;AAAEA,YAAAA,IAAAA,EAAMW,SAASX;AAAK,SAAA,GAAI,EAAE;QAChD,GAAIW,QAAAA,CAASW,MAAM,GAAG;AAAEA,YAAAA,MAAAA,EAAQX,SAASW;AAAO,SAAA,GAAI;KACrD,EACA,GAAA,CAAA;AAEF;AAEO,SAASG,4BAA4B5B,UAA2C,EAAA;IACtF,IAAIA,UAAAA,CAAW8B,MAAM,KAAK,CAAA,EAAG;QAC5B,OAAO1C,SAAAA;AACR,IAAA;AAEA,IAAA,OAAOY,UAAAA,CAAW+B,GAAG,CAACC,0BAAAA,CAAAA,CAA4BC,IAAI,CAAC,IAAA,CAAA;AACxD;AAEA,SAASD,2BAA2BE,SAA+B,EAAA;AAClE,IAAA,MAAMC,KAAAA,GAAQ;AAACD,QAAAA,SAAAA,CAAUE;AAAO,KAAA;IAEhC,IAAIF,SAAAA,CAAUG,KAAK,EAAE;QACpBF,KAAAA,CAAMG,IAAI,CAAC,CAAC,OAAO,EAAEC,kBAAkBL,SAAAA,CAAUG,KAAK,CAAA,CAAE,CAAC,CAAC,CAAA;AAC3D,IAAA;IAEA,IAAIH,SAAAA,CAAUM,MAAM,EAAE;QACrB,KAAK,MAAM,CAACC,GAAAA,EAAKC,KAAAA,CAAM,IAAIC,OAAOC,OAAO,CAACV,SAAAA,CAAUM,MAAM,CAAA,CAAG;YAC5DL,KAAAA,CAAMG,IAAI,CAAC,CAAA,EAAGG,GAAAA,CAAI,EAAE,EAAEF,iBAAAA,CAAkBG,KAAAA,CAAAA,CAAO,CAAC,CAAC,CAAA;AAClD,QAAA;AACD,IAAA;IAEA,OAAOP,KAAAA,CAAMF,IAAI,CAAC,GAAA,CAAA;AACnB;AAEA,SAASM,kBAAkBG,KAAa,EAAA;AACvC,IAAA,OAAOA,MAAMG,OAAO,CAAC,OAAO,MAAA,CAAA,CAAQA,OAAO,CAAC,IAAA,EAAM,KAAA,CAAA;AACnD;;;;;;;;"}
|
package/security.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type AuthenticationResult, type AuthIdentity, type AuthStateCarrier, type ChallengeInstruction } from "@athosjs/auth";
|
|
2
|
+
import type { AuthorizationDecision } from "@athosjs/authz";
|
|
3
|
+
import { type HttpContext, type HttpMiddleware, type HttpResponse } from "@athosjs/http";
|
|
4
|
+
import type { ProAuthenticationMiddlewareOptions, ProAuthorizationMiddlewareOptions } from "@athosjs/types/pro";
|
|
5
|
+
export type { ProAuthenticationMiddlewareOptions, ProAuthorizationMiddlewareOptions };
|
|
6
|
+
export declare function createProAuthenticationMiddleware<TContext extends HttpContext & AuthStateCarrier, TIdentity extends AuthIdentity = AuthIdentity>(options: ProAuthenticationMiddlewareOptions<TContext, TIdentity>): HttpMiddleware;
|
|
7
|
+
export declare function createProAuthorizationMiddleware<TContext extends HttpContext & AuthStateCarrier, TIdentity extends AuthIdentity = AuthIdentity, TResource = unknown, TEnvironment = unknown>(options: ProAuthorizationMiddlewareOptions<TContext, TIdentity, TResource, TEnvironment>): HttpMiddleware;
|
|
8
|
+
export declare function createAuthenticationFailureResponse<TIdentity extends AuthIdentity = AuthIdentity>(result: AuthenticationResult<TIdentity>): HttpResponse;
|
|
9
|
+
export declare function createAuthorizationFailureResponse(decision: AuthorizationDecision): HttpResponse;
|
|
10
|
+
export declare function formatWwwAuthenticateHeader(challenges: readonly ChallengeInstruction[]): string | undefined;
|
|
11
|
+
//# sourceMappingURL=security.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../../../../../packages/pro/src/security.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EAMrB,KAAK,oBAAoB,EAEzB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAwB,qBAAqB,EAAuB,MAAM,gBAAgB,CAAC;AAGvG,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,KAAK,YAAY,EAAQ,MAAM,eAAe,CAAC;AAC/F,OAAO,KAAK,EAAE,kCAAkC,EAAE,iCAAiC,EAAE,MAAM,oBAAoB,CAAC;AAEhH,YAAY,EAAE,kCAAkC,EAAE,iCAAiC,EAAE,CAAC;AAEtF,wBAAgB,iCAAiC,CAChD,QAAQ,SAAS,WAAW,GAAG,gBAAgB,EAC/C,SAAS,SAAS,YAAY,GAAG,YAAY,EAC5C,OAAO,EAAE,kCAAkC,CAAC,QAAQ,EAAE,SAAS,CAAC,GAAG,cAAc,CA6BlF;AAED,wBAAgB,gCAAgC,CAC/C,QAAQ,SAAS,WAAW,GAAG,gBAAgB,EAC/C,SAAS,SAAS,YAAY,GAAG,YAAY,EAC7C,SAAS,GAAG,OAAO,EACnB,YAAY,GAAG,OAAO,EACrB,OAAO,EAAE,iCAAiC,CAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,CAAC,GAAG,cAAc,CAyD1G;AAED,wBAAgB,mCAAmC,CAAC,SAAS,SAAS,YAAY,GAAG,YAAY,EAChG,MAAM,EAAE,oBAAoB,CAAC,SAAS,CAAC,GACrC,YAAY,CAyBd;AAED,wBAAgB,kCAAkC,CAAC,QAAQ,EAAE,qBAAqB,GAAG,YAAY,CAWhG;AAED,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,SAAS,oBAAoB,EAAE,GAAG,MAAM,GAAG,SAAS,CAM3G"}
|