@qase/mcp-server 1.1.8 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/README.md +75 -542
- package/build/auth/client-context.d.ts +12 -0
- package/build/auth/client-context.d.ts.map +1 -0
- package/build/auth/client-context.js +12 -0
- package/build/auth/client-context.js.map +1 -0
- package/build/auth/jwks-verifier.d.ts +15 -0
- package/build/auth/jwks-verifier.d.ts.map +1 -0
- package/build/auth/jwks-verifier.js +31 -0
- package/build/auth/jwks-verifier.js.map +1 -0
- package/build/auth/mcp-guard.d.ts +15 -0
- package/build/auth/mcp-guard.d.ts.map +1 -0
- package/build/auth/mcp-guard.js +44 -0
- package/build/auth/mcp-guard.js.map +1 -0
- package/build/auth/oauth-config.d.ts +40 -0
- package/build/auth/oauth-config.d.ts.map +1 -0
- package/build/auth/oauth-config.js +34 -0
- package/build/auth/oauth-config.js.map +1 -0
- package/build/auth/proxy-provider.d.ts +31 -0
- package/build/auth/proxy-provider.d.ts.map +1 -0
- package/build/auth/proxy-provider.js +87 -0
- package/build/auth/proxy-provider.js.map +1 -0
- package/build/auth/token-type.d.ts +7 -0
- package/build/auth/token-type.d.ts.map +1 -0
- package/build/auth/token-type.js +10 -0
- package/build/auth/token-type.js.map +1 -0
- package/build/cache/circuit-breaker.d.ts +33 -0
- package/build/cache/circuit-breaker.d.ts.map +1 -0
- package/build/cache/circuit-breaker.js +68 -0
- package/build/cache/circuit-breaker.js.map +1 -0
- package/build/cache/index.d.ts +24 -0
- package/build/cache/index.d.ts.map +1 -0
- package/build/cache/index.js +86 -0
- package/build/cache/index.js.map +1 -0
- package/build/cache/keys.d.ts +25 -0
- package/build/cache/keys.d.ts.map +1 -0
- package/build/cache/keys.js +36 -0
- package/build/cache/keys.js.map +1 -0
- package/build/cache/layered.d.ts +31 -0
- package/build/cache/layered.d.ts.map +1 -0
- package/build/cache/layered.js +88 -0
- package/build/cache/layered.js.map +1 -0
- package/build/cache/memory.d.ts +32 -0
- package/build/cache/memory.d.ts.map +1 -0
- package/build/cache/memory.js +108 -0
- package/build/cache/memory.js.map +1 -0
- package/build/cache/metrics.d.ts +31 -0
- package/build/cache/metrics.d.ts.map +1 -0
- package/build/cache/metrics.js +95 -0
- package/build/cache/metrics.js.map +1 -0
- package/build/cache/prom-format.d.ts +7 -0
- package/build/cache/prom-format.d.ts.map +1 -0
- package/build/cache/prom-format.js +16 -0
- package/build/cache/prom-format.js.map +1 -0
- package/build/cache/redis-bus.d.ts +29 -0
- package/build/cache/redis-bus.d.ts.map +1 -0
- package/build/cache/redis-bus.js +50 -0
- package/build/cache/redis-bus.js.map +1 -0
- package/build/cache/redis.d.ts +32 -0
- package/build/cache/redis.d.ts.map +1 -0
- package/build/cache/redis.js +89 -0
- package/build/cache/redis.js.map +1 -0
- package/build/cache/types.d.ts +38 -0
- package/build/cache/types.d.ts.map +1 -0
- package/build/cache/types.js +2 -0
- package/build/cache/types.js.map +1 -0
- package/build/client/index.d.ts +10 -3
- package/build/client/index.d.ts.map +1 -1
- package/build/client/index.js +75 -50
- package/build/client/index.js.map +1 -1
- package/build/http/agent.d.ts +16 -0
- package/build/http/agent.d.ts.map +1 -0
- package/build/http/agent.js +16 -0
- package/build/http/agent.js.map +1 -0
- package/build/http/dedupe.d.ts +14 -0
- package/build/http/dedupe.d.ts.map +1 -0
- package/build/http/dedupe.js +69 -0
- package/build/http/dedupe.js.map +1 -0
- package/build/http/index.d.ts +6 -0
- package/build/http/index.d.ts.map +1 -0
- package/build/http/index.js +4 -0
- package/build/http/index.js.map +1 -0
- package/build/http/retry.d.ts +18 -0
- package/build/http/retry.d.ts.map +1 -0
- package/build/http/retry.js +52 -0
- package/build/http/retry.js.map +1 -0
- package/build/index.d.ts +1 -18
- package/build/index.d.ts.map +1 -1
- package/build/index.js +95 -63
- package/build/index.js.map +1 -1
- package/build/operations-v2/composites/ci-report.d.ts +2 -0
- package/build/operations-v2/composites/ci-report.d.ts.map +1 -0
- package/build/operations-v2/composites/ci-report.js +99 -0
- package/build/operations-v2/composites/ci-report.js.map +1 -0
- package/build/operations-v2/composites/regression-run.d.ts +2 -0
- package/build/operations-v2/composites/regression-run.d.ts.map +1 -0
- package/build/operations-v2/composites/regression-run.js +81 -0
- package/build/operations-v2/composites/regression-run.js.map +1 -0
- package/build/operations-v2/composites/triage-defect.d.ts +2 -0
- package/build/operations-v2/composites/triage-defect.d.ts.map +1 -0
- package/build/operations-v2/composites/triage-defect.js +67 -0
- package/build/operations-v2/composites/triage-defect.js.map +1 -0
- package/build/operations-v2/escape/api.d.ts +2 -0
- package/build/operations-v2/escape/api.d.ts.map +1 -0
- package/build/operations-v2/escape/api.js +34 -0
- package/build/operations-v2/escape/api.js.map +1 -0
- package/build/operations-v2/index.d.ts +21 -0
- package/build/operations-v2/index.d.ts.map +1 -0
- package/build/operations-v2/index.js +27 -0
- package/build/operations-v2/index.js.map +1 -0
- package/build/operations-v2/meta/discover.d.ts +9 -0
- package/build/operations-v2/meta/discover.d.ts.map +1 -0
- package/build/operations-v2/meta/discover.js +79 -0
- package/build/operations-v2/meta/discover.js.map +1 -0
- package/build/{operations/search.d.ts → operations-v2/qql/index.d.ts} +1 -1
- package/build/operations-v2/qql/index.d.ts.map +1 -0
- package/build/{operations/search.js → operations-v2/qql/index.js} +34 -9
- package/build/operations-v2/qql/index.js.map +1 -0
- package/build/operations-v2/read/get.d.ts +2 -0
- package/build/operations-v2/read/get.d.ts.map +1 -0
- package/build/operations-v2/read/get.js +115 -0
- package/build/operations-v2/read/get.js.map +1 -0
- package/build/operations-v2/read/project-context.d.ts +2 -0
- package/build/operations-v2/read/project-context.d.ts.map +1 -0
- package/build/operations-v2/read/project-context.js +102 -0
- package/build/operations-v2/read/project-context.js.map +1 -0
- package/build/operations-v2/write/attachments.d.ts +2 -0
- package/build/operations-v2/write/attachments.d.ts.map +1 -0
- package/build/operations-v2/write/attachments.js +58 -0
- package/build/operations-v2/write/attachments.js.map +1 -0
- package/build/operations-v2/write/case-fields.d.ts +206 -0
- package/build/operations-v2/write/case-fields.d.ts.map +1 -0
- package/build/operations-v2/write/case-fields.js +79 -0
- package/build/operations-v2/write/case-fields.js.map +1 -0
- package/build/operations-v2/write/cases-bulk.d.ts +2 -0
- package/build/operations-v2/write/cases-bulk.d.ts.map +1 -0
- package/build/operations-v2/write/cases-bulk.js +54 -0
- package/build/operations-v2/write/cases-bulk.js.map +1 -0
- package/build/operations-v2/write/cases.d.ts +2 -0
- package/build/operations-v2/write/cases.d.ts.map +1 -0
- package/build/operations-v2/write/cases.js +52 -0
- package/build/operations-v2/write/cases.js.map +1 -0
- package/build/operations-v2/write/defects.d.ts +2 -0
- package/build/operations-v2/write/defects.d.ts.map +1 -0
- package/build/operations-v2/write/defects.js +81 -0
- package/build/operations-v2/write/defects.js.map +1 -0
- package/build/operations-v2/write/environments.d.ts +2 -0
- package/build/operations-v2/write/environments.d.ts.map +1 -0
- package/build/operations-v2/write/environments.js +56 -0
- package/build/operations-v2/write/environments.js.map +1 -0
- package/build/operations-v2/write/external-issues.d.ts +2 -0
- package/build/operations-v2/write/external-issues.d.ts.map +1 -0
- package/build/operations-v2/write/external-issues.js +96 -0
- package/build/operations-v2/write/external-issues.js.map +1 -0
- package/build/operations-v2/write/milestones.d.ts +2 -0
- package/build/operations-v2/write/milestones.d.ts.map +1 -0
- package/build/operations-v2/write/milestones.js +56 -0
- package/build/operations-v2/write/milestones.js.map +1 -0
- package/build/operations-v2/write/plans.d.ts +2 -0
- package/build/operations-v2/write/plans.d.ts.map +1 -0
- package/build/operations-v2/write/plans.js +55 -0
- package/build/operations-v2/write/plans.js.map +1 -0
- package/build/operations-v2/write/results.d.ts +2 -0
- package/build/operations-v2/write/results.d.ts.map +1 -0
- package/build/operations-v2/write/results.js +72 -0
- package/build/operations-v2/write/results.js.map +1 -0
- package/build/operations-v2/write/runs.d.ts +2 -0
- package/build/operations-v2/write/runs.d.ts.map +1 -0
- package/build/operations-v2/write/runs.js +84 -0
- package/build/operations-v2/write/runs.js.map +1 -0
- package/build/operations-v2/write/shared-steps.d.ts +2 -0
- package/build/operations-v2/write/shared-steps.d.ts.map +1 -0
- package/build/operations-v2/write/shared-steps.js +60 -0
- package/build/operations-v2/write/shared-steps.js.map +1 -0
- package/build/operations-v2/write/suites.d.ts +2 -0
- package/build/operations-v2/write/suites.d.ts.map +1 -0
- package/build/operations-v2/write/suites.js +63 -0
- package/build/operations-v2/write/suites.js.map +1 -0
- package/build/prompts/index.d.ts +31 -0
- package/build/prompts/index.d.ts.map +1 -0
- package/build/prompts/index.js +229 -0
- package/build/prompts/index.js.map +1 -0
- package/build/transports/sse.d.ts.map +1 -1
- package/build/transports/sse.js +6 -0
- package/build/transports/sse.js.map +1 -1
- package/build/transports/streamableHttp.d.ts +6 -1
- package/build/transports/streamableHttp.d.ts.map +1 -1
- package/build/transports/streamableHttp.js +128 -16
- package/build/transports/streamableHttp.js.map +1 -1
- package/build/utils/auth-context.d.ts +8 -0
- package/build/utils/auth-context.d.ts.map +1 -1
- package/build/utils/auth-context.js +18 -0
- package/build/utils/auth-context.js.map +1 -1
- package/build/utils/case-enums.d.ts +5 -3
- package/build/utils/case-enums.d.ts.map +1 -1
- package/build/utils/case-enums.js +77 -59
- package/build/utils/case-enums.js.map +1 -1
- package/build/utils/output-schemas.d.ts +16 -0
- package/build/utils/output-schemas.d.ts.map +1 -0
- package/build/utils/output-schemas.js +82 -0
- package/build/utils/output-schemas.js.map +1 -0
- package/build/utils/registry.d.ts +42 -9
- package/build/utils/registry.d.ts.map +1 -1
- package/build/utils/registry.js +64 -11
- package/build/utils/registry.js.map +1 -1
- package/build/utils/response-shape.d.ts +23 -0
- package/build/utils/response-shape.d.ts.map +1 -0
- package/build/utils/response-shape.js +73 -0
- package/build/utils/response-shape.js.map +1 -0
- package/build/utils/rich-response.d.ts +58 -0
- package/build/utils/rich-response.d.ts.map +1 -0
- package/build/utils/rich-response.js +93 -0
- package/build/utils/rich-response.js.map +1 -0
- package/build/utils/server-context.d.ts +65 -0
- package/build/utils/server-context.d.ts.map +1 -0
- package/build/utils/server-context.js +67 -0
- package/build/utils/server-context.js.map +1 -0
- package/build/version.d.ts +1 -1
- package/build/version.js +1 -1
- package/docs/connect.md +81 -0
- package/docs/migration.md +351 -0
- package/docs/self-run.md +270 -0
- package/docs/tools.md +111 -0
- package/docs/troubleshooting.md +144 -0
- package/package.json +11 -2
- package/server.json +8 -2
- package/build/operations/attachments.d.ts +0 -8
- package/build/operations/attachments.d.ts.map +0 -1
- package/build/operations/attachments.js +0 -146
- package/build/operations/attachments.js.map +0 -1
- package/build/operations/authors.d.ts +0 -8
- package/build/operations/authors.d.ts.map +0 -1
- package/build/operations/authors.js +0 -70
- package/build/operations/authors.js.map +0 -1
- package/build/operations/cases.d.ts +0 -8
- package/build/operations/cases.d.ts.map +0 -1
- package/build/operations/cases.js +0 -370
- package/build/operations/cases.js.map +0 -1
- package/build/operations/configurations.d.ts +0 -10
- package/build/operations/configurations.d.ts.map +0 -1
- package/build/operations/configurations.js +0 -125
- package/build/operations/configurations.js.map +0 -1
- package/build/operations/custom-fields.d.ts +0 -8
- package/build/operations/custom-fields.d.ts.map +0 -1
- package/build/operations/custom-fields.js +0 -172
- package/build/operations/custom-fields.js.map +0 -1
- package/build/operations/defects.d.ts +0 -8
- package/build/operations/defects.d.ts.map +0 -1
- package/build/operations/defects.js +0 -256
- package/build/operations/defects.js.map +0 -1
- package/build/operations/environments.d.ts +0 -8
- package/build/operations/environments.d.ts.map +0 -1
- package/build/operations/environments.js +0 -154
- package/build/operations/environments.js.map +0 -1
- package/build/operations/milestones.d.ts +0 -8
- package/build/operations/milestones.d.ts.map +0 -1
- package/build/operations/milestones.js +0 -155
- package/build/operations/milestones.js.map +0 -1
- package/build/operations/plans.d.ts +0 -8
- package/build/operations/plans.d.ts.map +0 -1
- package/build/operations/plans.js +0 -158
- package/build/operations/plans.js.map +0 -1
- package/build/operations/projects.d.ts +0 -8
- package/build/operations/projects.d.ts.map +0 -1
- package/build/operations/projects.js +0 -192
- package/build/operations/projects.js.map +0 -1
- package/build/operations/results.d.ts +0 -8
- package/build/operations/results.d.ts.map +0 -1
- package/build/operations/results.js +0 -214
- package/build/operations/results.js.map +0 -1
- package/build/operations/runs.d.ts +0 -8
- package/build/operations/runs.d.ts.map +0 -1
- package/build/operations/runs.js +0 -224
- package/build/operations/runs.js.map +0 -1
- package/build/operations/search.d.ts.map +0 -1
- package/build/operations/search.js.map +0 -1
- package/build/operations/shared-parameters.d.ts +0 -10
- package/build/operations/shared-parameters.d.ts.map +0 -1
- package/build/operations/shared-parameters.js +0 -93
- package/build/operations/shared-parameters.js.map +0 -1
- package/build/operations/shared-steps.d.ts +0 -8
- package/build/operations/shared-steps.d.ts.map +0 -1
- package/build/operations/shared-steps.js +0 -160
- package/build/operations/shared-steps.js.map +0 -1
- package/build/operations/suites.d.ts +0 -8
- package/build/operations/suites.d.ts.map +0 -1
- package/build/operations/suites.js +0 -169
- package/build/operations/suites.js.map +0 -1
- package/build/operations/system-fields.d.ts +0 -10
- package/build/operations/system-fields.d.ts.map +0 -1
- package/build/operations/system-fields.js +0 -47
- package/build/operations/system-fields.js.map +0 -1
- package/build/operations/users.d.ts +0 -10
- package/build/operations/users.d.ts.map +0 -1
- package/build/operations/users.js +0 -78
- package/build/operations/users.js.map +0 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { CacheBackend } from './types.js';
|
|
2
|
+
import { CircuitBreaker } from './circuit-breaker.js';
|
|
3
|
+
export interface RedisLikeClient {
|
|
4
|
+
get(key: string): Promise<string | null>;
|
|
5
|
+
set(key: string, value: string, mode: 'PX', ttlMs: number): Promise<'OK' | null>;
|
|
6
|
+
del(...keys: string[]): Promise<number>;
|
|
7
|
+
scan(cursor: string, matchKey: 'MATCH', pattern: string, countKey: 'COUNT', count: number): Promise<[string, string[]]>;
|
|
8
|
+
unlink(...keys: string[]): Promise<number>;
|
|
9
|
+
quit(): Promise<'OK'>;
|
|
10
|
+
}
|
|
11
|
+
export interface RedisCacheOptions {
|
|
12
|
+
circuitBreaker?: CircuitBreaker;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Redis-backed CacheBackend with graceful degradation.
|
|
16
|
+
*
|
|
17
|
+
* All upstream calls run through an optional circuit breaker. On any failure
|
|
18
|
+
* the breaker records it, the qase_mcp_cache_errors_total{tier="l2"} counter
|
|
19
|
+
* increments, and the read path returns undefined (miss). Write paths swallow
|
|
20
|
+
* errors so the caller is never blocked on a Redis outage.
|
|
21
|
+
*/
|
|
22
|
+
export declare class RedisCache implements CacheBackend {
|
|
23
|
+
private readonly client;
|
|
24
|
+
private readonly cb;
|
|
25
|
+
constructor(client: RedisLikeClient, opts?: RedisCacheOptions);
|
|
26
|
+
get<T>(key: string): Promise<T | undefined>;
|
|
27
|
+
set<T>(key: string, value: T, ttlMs: number): Promise<void>;
|
|
28
|
+
delete(key: string): Promise<void>;
|
|
29
|
+
deleteByPrefix(prefix: string): Promise<void>;
|
|
30
|
+
close(): Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=redis.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../../src/cache/redis.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IACjF,GAAG,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,IAAI,CACF,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,OAAO,EACjB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,OAAO,EACjB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IAC/B,MAAM,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED;;;;;;;GAOG;AACH,qBAAa,UAAW,YAAW,YAAY;IAI3C,OAAO,CAAC,QAAQ,CAAC,MAAM;IAHzB,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAiB;gBAGjB,MAAM,EAAE,eAAe,EACxC,IAAI,GAAE,iBAAsB;IAiBxB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAqB3C,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ3D,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQlC,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAO7B"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { CircuitBreaker } from './circuit-breaker.js';
|
|
2
|
+
import { getMetrics } from './metrics.js';
|
|
3
|
+
/**
|
|
4
|
+
* Redis-backed CacheBackend with graceful degradation.
|
|
5
|
+
*
|
|
6
|
+
* All upstream calls run through an optional circuit breaker. On any failure
|
|
7
|
+
* the breaker records it, the qase_mcp_cache_errors_total{tier="l2"} counter
|
|
8
|
+
* increments, and the read path returns undefined (miss). Write paths swallow
|
|
9
|
+
* errors so the caller is never blocked on a Redis outage.
|
|
10
|
+
*/
|
|
11
|
+
export class RedisCache {
|
|
12
|
+
client;
|
|
13
|
+
cb;
|
|
14
|
+
constructor(client, opts = {}) {
|
|
15
|
+
this.client = client;
|
|
16
|
+
this.cb =
|
|
17
|
+
opts.circuitBreaker ??
|
|
18
|
+
new CircuitBreaker({
|
|
19
|
+
name: 'redis',
|
|
20
|
+
failureThreshold: 5,
|
|
21
|
+
openDurationMs: 30_000,
|
|
22
|
+
onStateChange: (s) => getMetrics().setGauge('qase_mcp_circuit_breaker_state', { name: 'redis' }, s === 'closed' ? 0 : s === 'half_open' ? 1 : 2),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
async get(key) {
|
|
26
|
+
try {
|
|
27
|
+
const raw = await this.cb.exec(() => this.client.get(key));
|
|
28
|
+
if (raw === null) {
|
|
29
|
+
getMetrics().incCounter('qase_mcp_cache_misses_total', { tier: 'l2' });
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const v = JSON.parse(raw);
|
|
34
|
+
getMetrics().incCounter('qase_mcp_cache_hits_total', { tier: 'l2' });
|
|
35
|
+
return v;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
getMetrics().incCounter('qase_mcp_cache_misses_total', { tier: 'l2' });
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
getMetrics().incCounter('qase_mcp_cache_errors_total', { tier: 'l2' });
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async set(key, value, ttlMs) {
|
|
48
|
+
try {
|
|
49
|
+
await this.cb.exec(() => this.client.set(key, JSON.stringify(value), 'PX', ttlMs));
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
getMetrics().incCounter('qase_mcp_cache_errors_total', { tier: 'l2' });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async delete(key) {
|
|
56
|
+
try {
|
|
57
|
+
await this.cb.exec(() => this.client.del(key).then(() => undefined));
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
getMetrics().incCounter('qase_mcp_cache_errors_total', { tier: 'l2' });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async deleteByPrefix(prefix) {
|
|
64
|
+
try {
|
|
65
|
+
await this.cb.exec(async () => {
|
|
66
|
+
const pattern = `${prefix}*`;
|
|
67
|
+
let cursor = '0';
|
|
68
|
+
do {
|
|
69
|
+
const [next, keys] = await this.client.scan(cursor, 'MATCH', pattern, 'COUNT', 200);
|
|
70
|
+
if (keys.length > 0)
|
|
71
|
+
await this.client.unlink(...keys);
|
|
72
|
+
cursor = next;
|
|
73
|
+
} while (cursor !== '0');
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
getMetrics().incCounter('qase_mcp_cache_errors_total', { tier: 'l2' });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async close() {
|
|
81
|
+
try {
|
|
82
|
+
await this.client.quit();
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// best-effort on shutdown
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=redis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis.js","sourceRoot":"","sources":["../../src/cache/redis.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache abstraction. Implementations: InMemoryCache (Plan 1),
|
|
3
|
+
* LayeredCache wrapping an optional RedisCache (Plan 2).
|
|
4
|
+
*/
|
|
5
|
+
export interface CacheBackend {
|
|
6
|
+
/**
|
|
7
|
+
* Look up a cached value. Returns undefined on miss or expiration.
|
|
8
|
+
* Must never cross tenant boundaries — all isolation happens through the key.
|
|
9
|
+
*/
|
|
10
|
+
get<T>(key: string): Promise<T | undefined>;
|
|
11
|
+
/**
|
|
12
|
+
* Write a value with a TTL in milliseconds. Overwrites any existing entry.
|
|
13
|
+
*/
|
|
14
|
+
set<T>(key: string, value: T, ttlMs: number): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Remove a single entry. Silently no-op if key is absent.
|
|
17
|
+
*/
|
|
18
|
+
delete(key: string): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Remove every entry whose key starts with the given prefix.
|
|
21
|
+
* Used for tenant-scoped invalidation and schema-version rollovers.
|
|
22
|
+
*/
|
|
23
|
+
deleteByPrefix(prefix: string): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Release underlying resources (timers, network connections).
|
|
26
|
+
*/
|
|
27
|
+
close(): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Cross-instance invalidation bus. Implemented in Plan 2 with Redis Pub/Sub;
|
|
31
|
+
* unused in Plan 1.
|
|
32
|
+
*/
|
|
33
|
+
export interface InvalidationBus {
|
|
34
|
+
publish(prefix: string): Promise<void>;
|
|
35
|
+
subscribe(handler: (prefix: string) => void): Promise<void>;
|
|
36
|
+
close(): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/cache/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAE5C;;OAEG;IACH,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE5D;;OAEG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnC;;;OAGG;IACH,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9C;;OAEG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,SAAS,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/cache/types.ts"],"names":[],"mappings":""}
|
package/build/client/index.d.ts
CHANGED
|
@@ -8,7 +8,13 @@
|
|
|
8
8
|
* - Environment-based configuration
|
|
9
9
|
*/
|
|
10
10
|
import { ProjectsApi, CasesApi, SuitesApi, RunsApi, ResultsApi, PlansApi, MilestonesApi, DefectsApi, EnvironmentsApi, AttachmentsApi, SharedStepsApi, AuthorsApi, CustomFieldsApi, SearchApi, ConfigurationsApi, SystemFieldsApi, UsersApi, SharedParametersApi } from 'qase-api-client';
|
|
11
|
-
import { AxiosRequestConfig } from 'axios';
|
|
11
|
+
import { AxiosInstance, AxiosRequestConfig } from 'axios';
|
|
12
|
+
/**
|
|
13
|
+
* Build the User-Agent / source string sent to the Qase API. Qase uses this to
|
|
14
|
+
* attribute the request source. Defaults to `qase-mcp`; the hosted deployment
|
|
15
|
+
* sets `QASE_MCP_SOURCE=qase-mcp-hosted` to distinguish it from the self-run CLI.
|
|
16
|
+
*/
|
|
17
|
+
export declare function getUserAgent(): string;
|
|
12
18
|
/**
|
|
13
19
|
* Configuration for the Qase API client
|
|
14
20
|
*/
|
|
@@ -40,7 +46,8 @@ declare class QaseApiClient {
|
|
|
40
46
|
readonly sharedParameters: SharedParametersApi;
|
|
41
47
|
private readonly token;
|
|
42
48
|
private readonly host;
|
|
43
|
-
|
|
49
|
+
private readonly axiosInstance;
|
|
50
|
+
constructor(config: ApiClientConfig, axiosInstance?: AxiosInstance);
|
|
44
51
|
/**
|
|
45
52
|
* Make a direct API call for endpoints not fully covered by the SDK.
|
|
46
53
|
*/
|
|
@@ -63,5 +70,5 @@ export declare function getApiClient(): QaseApiClient;
|
|
|
63
70
|
* @internal
|
|
64
71
|
*/
|
|
65
72
|
export declare function resetClientInstance(): void;
|
|
66
|
-
export
|
|
73
|
+
export { QaseApiClient };
|
|
67
74
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAEL,WAAW,EACX,QAAQ,EACR,SAAS,EACT,OAAO,EACP,UAAU,EACV,QAAQ,EACR,aAAa,EACb,UAAU,EACV,eAAe,EACf,cAAc,EACd,cAAc,EACd,UAAU,EACV,eAAe,EACf,SAAS,EACT,iBAAiB,EACjB,eAAe,EACf,QAAQ,EACR,mBAAmB,EACpB,MAAM,iBAAiB,CAAC;AACzB,OAAc,EAAE,kBAAkB,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAEL,WAAW,EACX,QAAQ,EACR,SAAS,EACT,OAAO,EACP,UAAU,EACV,QAAQ,EACR,aAAa,EACb,UAAU,EACV,eAAe,EACf,cAAc,EACd,cAAc,EACd,UAAU,EACV,eAAe,EACf,SAAS,EACT,iBAAiB,EACjB,eAAe,EACf,QAAQ,EACR,mBAAmB,EACpB,MAAM,iBAAiB,CAAC;AACzB,OAAc,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAQjE;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAGrC;AAED;;GAEG;AACH,UAAU,eAAe;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,cAAM,aAAa;IACjB,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,eAAe,CAAC;IACtC,QAAQ,CAAC,WAAW,EAAE,cAAc,CAAC;IACrC,QAAQ,CAAC,WAAW,EAAE,cAAc,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,eAAe,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,iBAAiB,CAAC;IAC3C,QAAQ,CAAC,YAAY,EAAE,eAAe,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,gBAAgB,EAAE,mBAAmB,CAAC;IAE/C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;gBAElC,MAAM,EAAE,eAAe,EAAE,aAAa,CAAC,EAAE,aAAa;IAkElE;;OAEG;IACG,OAAO,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,CAAC,CAAC;CAoBnF;AA8BD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,IAAI,aAAa,CAY5C;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED,OAAO,EAAE,aAAa,EAAE,CAAC"}
|
package/build/client/index.js
CHANGED
|
@@ -9,10 +9,21 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { Configuration, ProjectsApi, CasesApi, SuitesApi, RunsApi, ResultsApi, PlansApi, MilestonesApi, DefectsApi, EnvironmentsApi, AttachmentsApi, SharedStepsApi, AuthorsApi, CustomFieldsApi, SearchApi, ConfigurationsApi, SystemFieldsApi, UsersApi, SharedParametersApi, } from 'qase-api-client';
|
|
11
11
|
import axios from 'axios';
|
|
12
|
+
import { createKeepAliveAgent, attachRetry, attachInflightDedupe } from '../http/index.js';
|
|
13
|
+
import { isJwt } from '../auth/token-type.js';
|
|
12
14
|
import FormData from 'form-data';
|
|
13
|
-
import { requestTokenStorage } from '../utils/auth-context.js';
|
|
15
|
+
import { requestTokenStorage, getEffectiveToken } from '../utils/auth-context.js';
|
|
16
|
+
import { getServer } from '../utils/server-context.js';
|
|
14
17
|
import { VERSION } from '../version.js';
|
|
15
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Build the User-Agent / source string sent to the Qase API. Qase uses this to
|
|
20
|
+
* attribute the request source. Defaults to `qase-mcp`; the hosted deployment
|
|
21
|
+
* sets `QASE_MCP_SOURCE=qase-mcp-hosted` to distinguish it from the self-run CLI.
|
|
22
|
+
*/
|
|
23
|
+
export function getUserAgent() {
|
|
24
|
+
const source = process.env.QASE_MCP_SOURCE?.trim() || 'qase-mcp';
|
|
25
|
+
return `${source}/${VERSION}`;
|
|
26
|
+
}
|
|
16
27
|
/**
|
|
17
28
|
* Wrapper around qase-api-client that preserves the `client.resource.method()` interface.
|
|
18
29
|
*/
|
|
@@ -37,50 +48,82 @@ class QaseApiClient {
|
|
|
37
48
|
sharedParameters;
|
|
38
49
|
token;
|
|
39
50
|
host;
|
|
40
|
-
|
|
51
|
+
axiosInstance;
|
|
52
|
+
constructor(config, axiosInstance) {
|
|
41
53
|
this.token = config.token;
|
|
42
54
|
this.host = config.host;
|
|
55
|
+
const jwtToken = isJwt(config.token);
|
|
56
|
+
const agent = createKeepAliveAgent({ maxSockets: 20 });
|
|
57
|
+
this.axiosInstance =
|
|
58
|
+
axiosInstance ??
|
|
59
|
+
axios.create({ httpsAgent: agent, headers: { 'User-Agent': getUserAgent() } });
|
|
60
|
+
// For JWTs, forward verbatim as Authorization: Bearer on every request.
|
|
61
|
+
// Opaque tokens keep using the Qase `Token` header via Configuration.apiKey.
|
|
62
|
+
if (jwtToken) {
|
|
63
|
+
this.axiosInstance.interceptors.request.use((req) => {
|
|
64
|
+
req.headers = req.headers ?? {};
|
|
65
|
+
req.headers['Authorization'] = `Bearer ${config.token}`;
|
|
66
|
+
return req;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
// Tag each Qase API request with the MCP client identity (which AI host/model
|
|
70
|
+
// is using the connector) for backend metrics. Sourced per-request from the
|
|
71
|
+
// MCP `initialize` clientInfo, read via serverStorage during tool execution.
|
|
72
|
+
this.axiosInstance.interceptors.request.use((req) => {
|
|
73
|
+
const client = getServer()?.getClientVersion();
|
|
74
|
+
if (client?.name) {
|
|
75
|
+
req.headers = req.headers ?? {};
|
|
76
|
+
req.headers['X-MCP-Client-Name'] = client.name;
|
|
77
|
+
if (client.version) {
|
|
78
|
+
req.headers['X-MCP-Client-Version'] = client.version;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return req;
|
|
82
|
+
});
|
|
83
|
+
attachRetry(this.axiosInstance);
|
|
84
|
+
attachInflightDedupe(this.axiosInstance);
|
|
85
|
+
const basePath = `${config.host}/v1`;
|
|
43
86
|
const cfg = new Configuration({
|
|
44
|
-
apiKey: config.token,
|
|
45
|
-
basePath
|
|
87
|
+
...(jwtToken ? {} : { apiKey: config.token }),
|
|
88
|
+
basePath,
|
|
46
89
|
formDataCtor: FormData,
|
|
47
|
-
baseOptions: {
|
|
48
|
-
headers: { 'User-Agent': USER_AGENT },
|
|
49
|
-
},
|
|
50
90
|
});
|
|
51
|
-
this.projects = new ProjectsApi(cfg);
|
|
52
|
-
this.cases = new CasesApi(cfg);
|
|
53
|
-
this.suites = new SuitesApi(cfg);
|
|
54
|
-
this.runs = new RunsApi(cfg);
|
|
55
|
-
this.results = new ResultsApi(cfg);
|
|
56
|
-
this.plans = new PlansApi(cfg);
|
|
57
|
-
this.milestones = new MilestonesApi(cfg);
|
|
58
|
-
this.defects = new DefectsApi(cfg);
|
|
59
|
-
this.environment = new EnvironmentsApi(cfg);
|
|
60
|
-
this.attachments = new AttachmentsApi(cfg);
|
|
61
|
-
this.sharedSteps = new SharedStepsApi(cfg);
|
|
62
|
-
this.authors = new AuthorsApi(cfg);
|
|
63
|
-
this.customFields = new CustomFieldsApi(cfg);
|
|
64
|
-
this.search = new SearchApi(cfg);
|
|
65
|
-
this.configurations = new ConfigurationsApi(cfg);
|
|
66
|
-
this.systemFields = new SystemFieldsApi(cfg);
|
|
67
|
-
this.users = new UsersApi(cfg);
|
|
68
|
-
this.sharedParameters = new SharedParametersApi(cfg);
|
|
91
|
+
this.projects = new ProjectsApi(cfg, basePath, this.axiosInstance);
|
|
92
|
+
this.cases = new CasesApi(cfg, basePath, this.axiosInstance);
|
|
93
|
+
this.suites = new SuitesApi(cfg, basePath, this.axiosInstance);
|
|
94
|
+
this.runs = new RunsApi(cfg, basePath, this.axiosInstance);
|
|
95
|
+
this.results = new ResultsApi(cfg, basePath, this.axiosInstance);
|
|
96
|
+
this.plans = new PlansApi(cfg, basePath, this.axiosInstance);
|
|
97
|
+
this.milestones = new MilestonesApi(cfg, basePath, this.axiosInstance);
|
|
98
|
+
this.defects = new DefectsApi(cfg, basePath, this.axiosInstance);
|
|
99
|
+
this.environment = new EnvironmentsApi(cfg, basePath, this.axiosInstance);
|
|
100
|
+
this.attachments = new AttachmentsApi(cfg, basePath, this.axiosInstance);
|
|
101
|
+
this.sharedSteps = new SharedStepsApi(cfg, basePath, this.axiosInstance);
|
|
102
|
+
this.authors = new AuthorsApi(cfg, basePath, this.axiosInstance);
|
|
103
|
+
this.customFields = new CustomFieldsApi(cfg, basePath, this.axiosInstance);
|
|
104
|
+
this.search = new SearchApi(cfg, basePath, this.axiosInstance);
|
|
105
|
+
this.configurations = new ConfigurationsApi(cfg, basePath, this.axiosInstance);
|
|
106
|
+
this.systemFields = new SystemFieldsApi(cfg, basePath, this.axiosInstance);
|
|
107
|
+
this.users = new UsersApi(cfg, basePath, this.axiosInstance);
|
|
108
|
+
this.sharedParameters = new SharedParametersApi(cfg, basePath, this.axiosInstance);
|
|
69
109
|
}
|
|
70
110
|
/**
|
|
71
111
|
* Make a direct API call for endpoints not fully covered by the SDK.
|
|
72
112
|
*/
|
|
73
113
|
async request(path, options = {}) {
|
|
74
|
-
const
|
|
114
|
+
const authHeaders = isJwt(this.token)
|
|
115
|
+
? { Authorization: `Bearer ${this.token}` }
|
|
116
|
+
: { Token: this.token };
|
|
117
|
+
const { headers: optionHeaders, ...restOptions } = options;
|
|
118
|
+
const response = await this.axiosInstance.request({
|
|
75
119
|
method: options.method || 'GET',
|
|
76
120
|
url: `${this.host}${path}`,
|
|
77
121
|
headers: {
|
|
78
|
-
|
|
122
|
+
...authHeaders,
|
|
79
123
|
'Content-Type': 'application/json',
|
|
80
|
-
|
|
81
|
-
...options.headers,
|
|
124
|
+
...optionHeaders,
|
|
82
125
|
},
|
|
83
|
-
...
|
|
126
|
+
...restOptions,
|
|
84
127
|
});
|
|
85
128
|
return response.data;
|
|
86
129
|
}
|
|
@@ -96,25 +139,6 @@ function getHost() {
|
|
|
96
139
|
}
|
|
97
140
|
return `https://${domain}`;
|
|
98
141
|
}
|
|
99
|
-
/**
|
|
100
|
-
* Get the effective API token for the current request.
|
|
101
|
-
*
|
|
102
|
-
* Priority:
|
|
103
|
-
* 1. Per-request Bearer token from Authorization header (AsyncLocalStorage)
|
|
104
|
-
* 2. Shared QASE_API_TOKEN environment variable
|
|
105
|
-
*/
|
|
106
|
-
function getEffectiveToken() {
|
|
107
|
-
const requestToken = requestTokenStorage.getStore();
|
|
108
|
-
if (requestToken) {
|
|
109
|
-
return requestToken;
|
|
110
|
-
}
|
|
111
|
-
const envToken = process.env.QASE_API_TOKEN;
|
|
112
|
-
if (!envToken) {
|
|
113
|
-
throw new Error('QASE_API_TOKEN environment variable is required. ' +
|
|
114
|
-
'Get your token from: https://app.qase.io/user/api/token');
|
|
115
|
-
}
|
|
116
|
-
return envToken;
|
|
117
|
-
}
|
|
118
142
|
/**
|
|
119
143
|
* Get full API client config (token + host).
|
|
120
144
|
*/
|
|
@@ -154,4 +178,5 @@ export function getApiClient() {
|
|
|
154
178
|
export function resetClientInstance() {
|
|
155
179
|
clientInstance = null;
|
|
156
180
|
}
|
|
181
|
+
export { QaseApiClient };
|
|
157
182
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,aAAa,EACb,WAAW,EACX,QAAQ,EACR,SAAS,EACT,OAAO,EACP,UAAU,EACV,QAAQ,EACR,aAAa,EACb,UAAU,EACV,eAAe,EACf,cAAc,EACd,cAAc,EACd,UAAU,EACV,eAAe,EACf,SAAS,EACT,iBAAiB,EACjB,eAAe,EACf,QAAQ,EACR,mBAAmB,GACpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as https from 'https';
|
|
2
|
+
export interface KeepAliveAgentOptions {
|
|
3
|
+
/** Max concurrent sockets per host. Default: 20. */
|
|
4
|
+
maxSockets?: number;
|
|
5
|
+
/** Idle timeout before closing an unused connection. Default: 60s. */
|
|
6
|
+
keepAliveMsecs?: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Create an https.Agent with connection pooling enabled.
|
|
10
|
+
*
|
|
11
|
+
* Removes the TLS handshake (~50–150ms per request) from every call after
|
|
12
|
+
* the first, which is the single biggest source of per-request latency when
|
|
13
|
+
* talking to api.qase.io.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createKeepAliveAgent(opts?: KeepAliveAgentOptions): https.Agent;
|
|
16
|
+
//# sourceMappingURL=agent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/http/agent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,MAAM,WAAW,qBAAqB;IACpC,oDAAoD;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,GAAE,qBAA0B,GAAG,KAAK,CAAC,KAAK,CAMlF"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as https from 'https';
|
|
2
|
+
/**
|
|
3
|
+
* Create an https.Agent with connection pooling enabled.
|
|
4
|
+
*
|
|
5
|
+
* Removes the TLS handshake (~50–150ms per request) from every call after
|
|
6
|
+
* the first, which is the single biggest source of per-request latency when
|
|
7
|
+
* talking to api.qase.io.
|
|
8
|
+
*/
|
|
9
|
+
export function createKeepAliveAgent(opts = {}) {
|
|
10
|
+
return new https.Agent({
|
|
11
|
+
keepAlive: true,
|
|
12
|
+
keepAliveMsecs: opts.keepAliveMsecs ?? 60_000,
|
|
13
|
+
maxSockets: opts.maxSockets ?? 20,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/http/agent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAS/B;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAA8B,EAAE;IACnE,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC;QACrB,SAAS,EAAE,IAAI;QACf,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,MAAM;QAC7C,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,EAAE;KAClC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AxiosInstance } from 'axios';
|
|
2
|
+
/**
|
|
3
|
+
* Attach an in-flight deduplication adapter.
|
|
4
|
+
*
|
|
5
|
+
* When two concurrent GETs have the same URL + sorted query, the second
|
|
6
|
+
* caller piggy-backs on the first request's promise instead of issuing a new
|
|
7
|
+
* network call. Applied per-axios-instance — each tenant gets its own
|
|
8
|
+
* instance in `getApiClient()`, so dedup never crosses tenants.
|
|
9
|
+
*
|
|
10
|
+
* Mutating requests (POST/PATCH/PUT/DELETE) are never deduped — they are
|
|
11
|
+
* not safe to collapse and may have different bodies.
|
|
12
|
+
*/
|
|
13
|
+
export declare function attachInflightDedupe(instance: AxiosInstance): void;
|
|
14
|
+
//# sourceMappingURL=dedupe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dedupe.d.ts","sourceRoot":"","sources":["../../src/http/dedupe.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,aAAa,EAAqC,MAAM,OAAO,CAAC;AAEhF;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI,CAwClE"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
/**
|
|
3
|
+
* Attach an in-flight deduplication adapter.
|
|
4
|
+
*
|
|
5
|
+
* When two concurrent GETs have the same URL + sorted query, the second
|
|
6
|
+
* caller piggy-backs on the first request's promise instead of issuing a new
|
|
7
|
+
* network call. Applied per-axios-instance — each tenant gets its own
|
|
8
|
+
* instance in `getApiClient()`, so dedup never crosses tenants.
|
|
9
|
+
*
|
|
10
|
+
* Mutating requests (POST/PATCH/PUT/DELETE) are never deduped — they are
|
|
11
|
+
* not safe to collapse and may have different bodies.
|
|
12
|
+
*/
|
|
13
|
+
export function attachInflightDedupe(instance) {
|
|
14
|
+
const inflight = new Map();
|
|
15
|
+
// Capture whatever adapter was set on the instance at attach time.
|
|
16
|
+
// - If MockAdapter is installed first → capturedAdapter is the mock function.
|
|
17
|
+
// - If nothing was set (fresh axios.create()) → capturedAdapter is undefined;
|
|
18
|
+
// we fall back to axios's built-in http adapter via getAdapter.
|
|
19
|
+
const capturedAdapter = instance.defaults.adapter;
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
21
|
+
let resolvedAdapter;
|
|
22
|
+
if (typeof capturedAdapter === 'function') {
|
|
23
|
+
resolvedAdapter = capturedAdapter;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
// axios ≥1.7 exposes getAdapter('http') which returns the actual function.
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
|
+
resolvedAdapter = axios.getAdapter?.('http');
|
|
29
|
+
if (typeof resolvedAdapter !== 'function') {
|
|
30
|
+
// Last-resort fallback: skip dedup rather than crash the server.
|
|
31
|
+
console.error('[dedupe] WARNING: could not resolve HTTP adapter; in-flight deduplication disabled.');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
instance.defaults.adapter = (config) => {
|
|
36
|
+
if (!isDedupable(config)) {
|
|
37
|
+
return resolvedAdapter(config);
|
|
38
|
+
}
|
|
39
|
+
const key = dedupeKey(config);
|
|
40
|
+
const existing = inflight.get(key);
|
|
41
|
+
if (existing)
|
|
42
|
+
return existing;
|
|
43
|
+
const promise = resolvedAdapter(config).finally(() => {
|
|
44
|
+
inflight.delete(key);
|
|
45
|
+
});
|
|
46
|
+
inflight.set(key, promise);
|
|
47
|
+
return promise;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function isDedupable(config) {
|
|
51
|
+
const method = (config.method ?? 'get').toLowerCase();
|
|
52
|
+
return method === 'get' || method === 'head';
|
|
53
|
+
}
|
|
54
|
+
function dedupeKey(config) {
|
|
55
|
+
const url = config.url ?? '';
|
|
56
|
+
const base = config.baseURL ?? '';
|
|
57
|
+
const params = config.params ? stableStringify(config.params) : '';
|
|
58
|
+
return `${config.method ?? 'get'}|${base}|${url}|${params}`;
|
|
59
|
+
}
|
|
60
|
+
function stableStringify(obj) {
|
|
61
|
+
if (obj === null || typeof obj !== 'object')
|
|
62
|
+
return JSON.stringify(obj);
|
|
63
|
+
if (Array.isArray(obj))
|
|
64
|
+
return `[${obj.map(stableStringify).join(',')}]`;
|
|
65
|
+
const record = obj;
|
|
66
|
+
const keys = Object.keys(record).sort();
|
|
67
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(record[k])}`).join(',')}}`;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=dedupe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dedupe.js","sourceRoot":"","sources":["../../src/http/dedupe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { createKeepAliveAgent } from './agent.js';
|
|
2
|
+
export type { KeepAliveAgentOptions } from './agent.js';
|
|
3
|
+
export { attachRetry } from './retry.js';
|
|
4
|
+
export type { RetryOptions } from './retry.js';
|
|
5
|
+
export { attachInflightDedupe } from './dedupe.js';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/http/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,YAAY,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/http/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAElD,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { AxiosInstance } from 'axios';
|
|
2
|
+
export interface RetryOptions {
|
|
3
|
+
/** Maximum retry attempts (does not count the initial attempt). Default: 3. */
|
|
4
|
+
maxRetries?: number;
|
|
5
|
+
/** Base delay for exponential backoff in ms. Default: 200. */
|
|
6
|
+
baseDelayMs?: number;
|
|
7
|
+
/** Max jitter in ms added to each backoff delay. Default: 100. */
|
|
8
|
+
maxJitterMs?: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Attach a retry interceptor with jittered exponential backoff.
|
|
12
|
+
*
|
|
13
|
+
* Policy (from ADR-0001, Decision → Special Mentions → Infrastructure):
|
|
14
|
+
* - Idempotent (GET/HEAD/OPTIONS/DELETE): retry 429/502/503/504 + network errors
|
|
15
|
+
* - Mutating (POST/PATCH/PUT): retry only 429 + pre-send timeouts
|
|
16
|
+
*/
|
|
17
|
+
export declare function attachRetry(instance: AxiosInstance, opts?: RetryOptions): void;
|
|
18
|
+
//# sourceMappingURL=retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/http/retry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAkC,MAAM,OAAO,CAAC;AAEtE,MAAM,WAAW,YAAY;IAC3B,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAWD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,aAAa,EAAE,IAAI,GAAE,YAAiB,GAAG,IAAI,CAqBlF"}
|