@apifuse/provider-sdk 2.2.0-beta.2 → 2.2.0-beta.21
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/AUTHORING.md +487 -0
- package/CHANGELOG.md +90 -0
- package/README.md +49 -3
- package/SUBMISSION.md +1 -1
- package/bin/apifuse-check.ts +44 -59
- package/bin/apifuse-create.ts +1 -1
- package/bin/apifuse-dev.ts +27 -52
- package/bin/apifuse-pack-check.ts +14 -0
- package/bin/apifuse-pack-smoke.ts +36 -81
- package/bin/apifuse-pack-types.ts +305 -0
- package/bin/apifuse-perf.ts +45 -127
- package/bin/apifuse-record.ts +659 -111
- package/bin/apifuse-submit-check.ts +546 -48
- package/bin/apifuse-sync-assets.ts +117 -0
- package/bin/apifuse.ts +1 -1
- package/bin/submit-check-delimited-text.ts +50 -0
- package/bin/submit-check-xml-semantics.ts +204 -0
- package/bin/submit-check-xml.ts +134 -0
- package/dist/auth-turn/index.d.ts +3 -3
- package/dist/auth.d.ts +2 -2
- package/dist/auth.js +9 -18
- package/dist/ceremonies/index.d.ts +9 -1
- package/dist/ceremonies/index.js +65 -18
- package/dist/cli/commands.d.ts +1 -1
- package/dist/cli/commands.js +8 -0
- package/dist/cli/create.d.ts +3 -0
- package/dist/cli/create.js +34 -35
- package/dist/cli/prompt-assets.d.ts +80 -0
- package/dist/cli/prompt-assets.js +743 -0
- package/dist/cli/templates/provider/AGENTS.md.tpl +17 -8
- package/dist/config/loader.d.ts +176 -8
- package/dist/config/loader.js +424 -95
- package/dist/contract-serialization.d.ts +2 -2
- package/dist/contract-serialization.js +3 -6
- package/dist/contract-types.d.ts +2 -2
- package/dist/contract.d.ts +3 -3
- package/dist/contract.js +4 -6
- package/dist/define.d.ts +9 -1
- package/dist/define.js +304 -119
- package/dist/dev.d.ts +1 -1
- package/dist/dev.js +1 -1
- package/dist/error-resolution.d.ts +2 -0
- package/dist/error-resolution.js +90 -0
- package/dist/errors.d.ts +22 -1
- package/dist/errors.js +88 -0
- package/dist/fixture-sanitization.d.ts +26 -0
- package/dist/fixture-sanitization.js +216 -0
- package/dist/i18n/catalog.d.ts +2 -2
- package/dist/i18n/catalog.js +4 -10
- package/dist/i18n/index.d.ts +2 -2
- package/dist/i18n/index.js +2 -2
- package/dist/i18n/keys.d.ts +2 -2
- package/dist/index.d.ts +46 -42
- package/dist/index.js +41 -37
- package/dist/lint.d.ts +1 -1
- package/dist/lint.js +8 -15
- package/dist/native-address.d.ts +43 -0
- package/dist/native-address.js +281 -0
- package/dist/native-egress-policy.d.ts +31 -0
- package/dist/native-egress-policy.js +288 -0
- package/dist/observability.d.ts +5 -2
- package/dist/observability.js +48 -1
- package/dist/provider.d.ts +12 -11
- package/dist/provider.js +10 -9
- package/dist/public-schema-field-lint.d.ts +1 -1
- package/dist/recipes/gov-api.js +1 -1
- package/dist/runtime/auth-flow.d.ts +2 -1
- package/dist/runtime/auth-flow.js +4 -3
- package/dist/runtime/browser.d.ts +1 -1
- package/dist/runtime/browser.js +15 -29
- package/dist/runtime/cache.d.ts +1 -1
- package/dist/runtime/cache.js +4 -8
- package/dist/runtime/choice.d.ts +1 -1
- package/dist/runtime/choice.js +31 -35
- package/dist/runtime/credential.d.ts +1 -1
- package/dist/runtime/credential.js +1 -1
- package/dist/runtime/env.d.ts +1 -1
- package/dist/runtime/executor.d.ts +1 -1
- package/dist/runtime/executor.js +31 -4
- package/dist/runtime/http.d.ts +2 -2
- package/dist/runtime/http.js +387 -47
- package/dist/runtime/insights.d.ts +1 -1
- package/dist/runtime/insights.js +6 -13
- package/dist/runtime/instrumentation.d.ts +2 -2
- package/dist/runtime/instrumentation.js +345 -22
- package/dist/runtime/keyring.js +1 -1
- package/dist/runtime/namespace.js +1 -1
- package/dist/runtime/native-network.d.ts +127 -0
- package/dist/runtime/native-network.js +1298 -0
- package/dist/runtime/otlp.d.ts +1 -1
- package/dist/runtime/perf.d.ts +1 -1
- package/dist/runtime/provider.d.ts +1 -1
- package/dist/runtime/provider.js +1 -2
- package/dist/runtime/proxy-errors.d.ts +1 -1
- package/dist/runtime/proxy-errors.js +9 -7
- package/dist/runtime/proxy-nodemaven.d.ts +56 -0
- package/dist/runtime/proxy-nodemaven.js +146 -0
- package/dist/runtime/proxy-retry-policy.d.ts +2 -2
- package/dist/runtime/proxy-retry-policy.js +2 -2
- package/dist/runtime/proxy-telemetry.d.ts +2 -1
- package/dist/runtime/proxy-telemetry.js +55 -52
- package/dist/runtime/redirects.d.ts +29 -0
- package/dist/runtime/redirects.js +36 -0
- package/dist/runtime/redis.d.ts +1 -1
- package/dist/runtime/redis.js +2 -4
- package/dist/runtime/request-options.d.ts +68 -1
- package/dist/runtime/request-options.js +548 -0
- package/dist/runtime/secrets.d.ts +27 -0
- package/dist/runtime/secrets.js +51 -0
- package/dist/runtime/state.d.ts +2 -2
- package/dist/runtime/state.js +238 -26
- package/dist/runtime/stealth.d.ts +5 -3
- package/dist/runtime/stealth.js +423 -143
- package/dist/runtime/stt.d.ts +1 -1
- package/dist/runtime/stt.js +11 -15
- package/dist/runtime/trace.d.ts +2 -2
- package/dist/runtime/trace.js +2 -4
- package/dist/runtime/waterfall.d.ts +1 -1
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +7 -15
- package/dist/serve.d.ts +1 -1
- package/dist/serve.js +1 -1
- package/dist/server/index.d.ts +7 -7
- package/dist/server/index.js +6 -6
- package/dist/server/self-test-input-tokens.d.ts +2 -1
- package/dist/server/self-test-input-tokens.js +18 -14
- package/dist/server/self-test-redaction.d.ts +1 -1
- package/dist/server/self-test-redaction.js +1 -1
- package/dist/server/self-test.d.ts +104 -3
- package/dist/server/self-test.js +673 -115
- package/dist/server/serve.d.ts +116 -4
- package/dist/server/serve.js +799 -128
- package/dist/server/types.d.ts +34 -9
- package/dist/server/types.js +8 -1
- package/dist/stateful/errors.d.ts +14 -0
- package/dist/stateful/errors.js +14 -0
- package/dist/stateful/http-provider-event-emitter.d.ts +40 -0
- package/dist/stateful/http-provider-event-emitter.js +237 -0
- package/dist/stateful/http-session-owner-registry.d.ts +44 -0
- package/dist/stateful/http-session-owner-registry.js +210 -0
- package/dist/stateful/index.d.ts +18 -0
- package/dist/stateful/index.js +18 -0
- package/dist/stateful/provider-event-delivery-failures.d.ts +32 -0
- package/dist/stateful/provider-event-delivery-failures.js +43 -0
- package/dist/stateful/provider-event-pipeline-metrics.d.ts +46 -0
- package/dist/stateful/provider-event-pipeline-metrics.js +48 -0
- package/dist/stateful/provider-event-pipeline.d.ts +50 -0
- package/dist/stateful/provider-event-pipeline.js +1 -0
- package/dist/stateful/provider-events.d.ts +101 -0
- package/dist/stateful/provider-events.js +289 -0
- package/dist/stateful/session-key.d.ts +15 -0
- package/dist/stateful/session-key.js +86 -0
- package/dist/stateful/stateful-provider-adapter-context.d.ts +5 -0
- package/dist/stateful/stateful-provider-adapter-context.js +42 -0
- package/dist/stateful/stateful-provider-adapter-metrics.d.ts +15 -0
- package/dist/stateful/stateful-provider-adapter-metrics.js +21 -0
- package/dist/stateful/stateful-provider-adapter.d.ts +98 -0
- package/dist/stateful/stateful-provider-adapter.js +287 -0
- package/dist/stateful/stateful-provider-observability.d.ts +62 -0
- package/dist/stateful/stateful-provider-observability.js +161 -0
- package/dist/stateful/stateful-provider-owner-forwarder.d.ts +41 -0
- package/dist/stateful/stateful-provider-owner-forwarder.js +215 -0
- package/dist/stateful/stateful-provider-runtime-context.d.ts +32 -0
- package/dist/stateful/stateful-provider-runtime-context.js +60 -0
- package/dist/stateful/stateful-provider-runtime-executor.d.ts +34 -0
- package/dist/stateful/stateful-provider-runtime-executor.js +52 -0
- package/dist/stateful/stateful-provider-session-routing.d.ts +71 -0
- package/dist/stateful/stateful-provider-session-routing.js +353 -0
- package/dist/stateful/stateful-provider-session-runtime.d.ts +98 -0
- package/dist/stateful/stateful-provider-session-runtime.js +245 -0
- package/dist/stateful-signing.d.ts +18 -0
- package/dist/stateful-signing.js +27 -0
- package/dist/stealth/profiles.d.ts +1 -1
- package/dist/stealth/profiles.js +5 -14
- package/dist/stream-evidence.d.ts +74 -0
- package/dist/stream-evidence.js +785 -0
- package/dist/stream.d.ts +1 -1
- package/dist/testing/index.d.ts +2 -2
- package/dist/testing/index.js +2 -2
- package/dist/testing/run.d.ts +32 -2
- package/dist/testing/run.js +478 -28
- package/dist/types.d.ts +342 -14
- package/dist/types.js +1 -0
- package/dist/user-input.d.ts +30 -0
- package/dist/user-input.js +66 -0
- package/package.json +16 -5
- package/src/auth-turn/index.ts +1 -1
- package/src/auth.ts +28 -86
- package/src/ceremonies/index.ts +103 -78
- package/src/cli/commands.ts +10 -0
- package/src/cli/create.ts +42 -35
- package/src/cli/prompt-assets.ts +865 -0
- package/src/cli/templates/provider/AGENTS.md.tpl +17 -8
- package/src/config/loader.ts +652 -204
- package/src/contract-serialization.ts +5 -11
- package/src/contract-types.ts +2 -2
- package/src/contract.ts +12 -28
- package/src/define.ts +499 -498
- package/src/dev.ts +4 -9
- package/src/error-resolution.ts +91 -0
- package/src/errors.ts +110 -5
- package/src/fixture-sanitization.ts +247 -0
- package/src/i18n/catalog.ts +10 -32
- package/src/i18n/index.ts +2 -2
- package/src/i18n/keys.ts +5 -11
- package/src/index.ts +112 -42
- package/src/lint.ts +88 -152
- package/src/native-address.ts +340 -0
- package/src/native-egress-policy.ts +358 -0
- package/src/observability.ts +51 -1
- package/src/provider.ts +66 -11
- package/src/public-schema-field-lint.ts +7 -33
- package/src/recipes/gov-api.ts +2 -5
- package/src/runtime/auth-flow.ts +7 -7
- package/src/runtime/browser.ts +64 -187
- package/src/runtime/cache.ts +20 -67
- package/src/runtime/choice.ts +79 -132
- package/src/runtime/credential.ts +2 -2
- package/src/runtime/env.ts +1 -1
- package/src/runtime/executor.ts +43 -20
- package/src/runtime/http.ts +494 -57
- package/src/runtime/insights.ts +15 -53
- package/src/runtime/instrumentation.ts +495 -66
- package/src/runtime/keyring.ts +7 -19
- package/src/runtime/namespace.ts +2 -7
- package/src/runtime/native-network.ts +1686 -0
- package/src/runtime/otlp.ts +12 -23
- package/src/runtime/perf.ts +1 -1
- package/src/runtime/provider.ts +4 -9
- package/src/runtime/proxy-errors.ts +29 -42
- package/src/runtime/proxy-nodemaven.ts +221 -0
- package/src/runtime/proxy-retry-policy.ts +3 -3
- package/src/runtime/proxy-telemetry.ts +79 -77
- package/src/runtime/redirects.ts +66 -0
- package/src/runtime/redis.ts +4 -12
- package/src/runtime/request-options.ts +679 -9
- package/src/runtime/secrets.ts +64 -0
- package/src/runtime/state.ts +353 -133
- package/src/runtime/stealth.ts +505 -154
- package/src/runtime/stt.ts +38 -94
- package/src/runtime/trace.ts +14 -44
- package/src/runtime/waterfall.ts +5 -18
- package/src/schema.ts +23 -84
- package/src/serve.ts +1 -1
- package/src/server/index.ts +29 -7
- package/src/server/self-test-input-tokens.ts +29 -14
- package/src/server/self-test-redaction.ts +2 -2
- package/src/server/self-test.ts +857 -132
- package/src/server/serve.ts +1151 -328
- package/src/server/types.ts +12 -13
- package/src/stateful/README.md +146 -0
- package/src/stateful/errors.ts +23 -0
- package/src/stateful/http-provider-event-emitter.ts +314 -0
- package/src/stateful/http-session-owner-registry.ts +306 -0
- package/src/stateful/index.ts +18 -0
- package/src/stateful/provider-event-delivery-failures.ts +80 -0
- package/src/stateful/provider-event-pipeline-metrics.ts +95 -0
- package/src/stateful/provider-event-pipeline.ts +61 -0
- package/src/stateful/provider-events.ts +462 -0
- package/src/stateful/session-key.ts +111 -0
- package/src/stateful/stateful-provider-adapter-context.ts +59 -0
- package/src/stateful/stateful-provider-adapter-metrics.ts +48 -0
- package/src/stateful/stateful-provider-adapter.ts +562 -0
- package/src/stateful/stateful-provider-observability.ts +261 -0
- package/src/stateful/stateful-provider-owner-forwarder.ts +287 -0
- package/src/stateful/stateful-provider-runtime-context.ts +92 -0
- package/src/stateful/stateful-provider-runtime-executor.ts +96 -0
- package/src/stateful/stateful-provider-session-routing.ts +555 -0
- package/src/stateful/stateful-provider-session-runtime.ts +403 -0
- package/src/stateful-signing.ts +46 -0
- package/src/stealth/profiles.ts +10 -26
- package/src/stream-evidence.ts +988 -0
- package/src/stream.ts +8 -19
- package/src/testing/index.ts +10 -2
- package/src/testing/run.ts +653 -74
- package/src/types.ts +408 -28
- package/src/user-input.ts +118 -0
- package/dist/cli/templates/provider/CLAUDE.md.tpl +0 -1
- package/src/cli/templates/provider/CLAUDE.md.tpl +0 -1
- /package/dist/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
ProxyAttemptTelemetryEvent,
|
|
3
3
|
ProxyCacheStatus,
|
|
4
|
+
ProxyProtocol,
|
|
4
5
|
ProxyResolutionTelemetryEvent,
|
|
5
6
|
ProxyTelemetrySink,
|
|
7
|
+
ProxyVendorFailoverTelemetryEvent,
|
|
8
|
+
ProxyVendorName,
|
|
6
9
|
SmartproxyAllocatorBodyClass,
|
|
7
|
-
} from "../config/loader";
|
|
10
|
+
} from "../config/loader.js";
|
|
8
11
|
|
|
9
12
|
export const PROVIDER_TELEMETRY_HEADER = "X-ApiFuse-Provider-Telemetry";
|
|
10
13
|
|
|
11
14
|
type ProviderTelemetryHeader = {
|
|
12
15
|
v: 1;
|
|
13
16
|
proxy?: {
|
|
14
|
-
provider:
|
|
17
|
+
provider: ProxyVendorName;
|
|
18
|
+
protocol?: ProxyProtocol;
|
|
15
19
|
cacheStatus: ProxyCacheStatus;
|
|
16
20
|
cacheHit: boolean;
|
|
17
21
|
resolutionMs: number;
|
|
@@ -27,6 +31,10 @@ type ProviderTelemetryHeader = {
|
|
|
27
31
|
attempts: number;
|
|
28
32
|
refreshes?: number;
|
|
29
33
|
attemptSamples?: CompactProxyAttemptSample[];
|
|
34
|
+
/** Distinct vendors attempted across the resolution chain, in order seen. */
|
|
35
|
+
vendors?: ProxyVendorName[];
|
|
36
|
+
/** Cross-vendor failover events (bounded). */
|
|
37
|
+
failovers?: CompactVendorFailoverSample[];
|
|
30
38
|
};
|
|
31
39
|
};
|
|
32
40
|
|
|
@@ -41,8 +49,17 @@ type CompactProxyAttemptSample = {
|
|
|
41
49
|
d?: number;
|
|
42
50
|
};
|
|
43
51
|
|
|
52
|
+
type CompactVendorFailoverSample = {
|
|
53
|
+
v: ProxyVendorName;
|
|
54
|
+
nx?: ProxyVendorName;
|
|
55
|
+
p: "resolution" | "transport";
|
|
56
|
+
r: ProxyVendorFailoverTelemetryEvent["reason"];
|
|
57
|
+
a?: number;
|
|
58
|
+
};
|
|
59
|
+
|
|
44
60
|
const MAX_HEADER_BYTES = 4_096;
|
|
45
61
|
const MAX_PROXY_ATTEMPT_SAMPLES = 24;
|
|
62
|
+
const MAX_PROXY_FAILOVER_SAMPLES = 12;
|
|
46
63
|
|
|
47
64
|
const CACHE_STATUS_SEVERITY: Record<ProxyCacheStatus, number> = {
|
|
48
65
|
disabled: 0,
|
|
@@ -55,31 +72,18 @@ const CACHE_STATUS_SEVERITY: Record<ProxyCacheStatus, number> = {
|
|
|
55
72
|
allocator: 7,
|
|
56
73
|
};
|
|
57
74
|
|
|
58
|
-
function sumOptional(
|
|
59
|
-
left: number | undefined,
|
|
60
|
-
right: number | undefined,
|
|
61
|
-
): number | undefined {
|
|
75
|
+
function sumOptional(left: number | undefined, right: number | undefined): number | undefined {
|
|
62
76
|
const total = (left ?? 0) + (right ?? 0);
|
|
63
77
|
return total > 0 ? total : undefined;
|
|
64
78
|
}
|
|
65
79
|
|
|
66
|
-
function maxOptional(
|
|
67
|
-
left: number
|
|
68
|
-
right: number | undefined,
|
|
69
|
-
): number | undefined {
|
|
70
|
-
const values = [left, right].filter(
|
|
71
|
-
(value): value is number => typeof value === "number",
|
|
72
|
-
);
|
|
80
|
+
function maxOptional(left: number | undefined, right: number | undefined): number | undefined {
|
|
81
|
+
const values = [left, right].filter((value): value is number => typeof value === "number");
|
|
73
82
|
return values.length > 0 ? Math.max(...values) : undefined;
|
|
74
83
|
}
|
|
75
84
|
|
|
76
|
-
function worseStatus(
|
|
77
|
-
left:
|
|
78
|
-
right: ProxyCacheStatus,
|
|
79
|
-
): ProxyCacheStatus {
|
|
80
|
-
return CACHE_STATUS_SEVERITY[right] > CACHE_STATUS_SEVERITY[left]
|
|
81
|
-
? right
|
|
82
|
-
: left;
|
|
85
|
+
function worseStatus(left: ProxyCacheStatus, right: ProxyCacheStatus): ProxyCacheStatus {
|
|
86
|
+
return CACHE_STATUS_SEVERITY[right] > CACHE_STATUS_SEVERITY[left] ? right : left;
|
|
83
87
|
}
|
|
84
88
|
|
|
85
89
|
function encodeBase64Url(value: string): string {
|
|
@@ -89,17 +93,17 @@ function encodeBase64Url(value: string): string {
|
|
|
89
93
|
export class ProxyTelemetryCollector implements ProxyTelemetrySink {
|
|
90
94
|
#events: ProxyResolutionTelemetryEvent[] = [];
|
|
91
95
|
#attempts: ProxyAttemptTelemetryEvent[] = [];
|
|
96
|
+
#failovers: ProxyVendorFailoverTelemetryEvent[] = [];
|
|
92
97
|
|
|
93
98
|
recordProxyResolution(event: ProxyResolutionTelemetryEvent): void {
|
|
94
99
|
this.#events.push({
|
|
95
|
-
provider:
|
|
100
|
+
provider: event.provider,
|
|
101
|
+
...(event.protocol ? { protocol: event.protocol } : {}),
|
|
96
102
|
cacheStatus: event.cacheStatus,
|
|
97
103
|
cacheHit: event.cacheHit,
|
|
98
104
|
resolutionMs: Math.max(0, Math.floor(event.resolutionMs)),
|
|
99
105
|
allocatorMs:
|
|
100
|
-
event.allocatorMs === undefined
|
|
101
|
-
? undefined
|
|
102
|
-
: Math.max(0, Math.floor(event.allocatorMs)),
|
|
106
|
+
event.allocatorMs === undefined ? undefined : Math.max(0, Math.floor(event.allocatorMs)),
|
|
103
107
|
allocatorStatus:
|
|
104
108
|
event.allocatorStatus === undefined
|
|
105
109
|
? undefined
|
|
@@ -110,37 +114,38 @@ export class ProxyTelemetryCollector implements ProxyTelemetrySink {
|
|
|
110
114
|
? undefined
|
|
111
115
|
: Math.max(1, Math.floor(event.allocatorAttempts)),
|
|
112
116
|
lockWaitMs:
|
|
113
|
-
event.lockWaitMs === undefined
|
|
114
|
-
? undefined
|
|
115
|
-
: Math.max(0, Math.floor(event.lockWaitMs)),
|
|
117
|
+
event.lockWaitMs === undefined ? undefined : Math.max(0, Math.floor(event.lockWaitMs)),
|
|
116
118
|
redisReadMs:
|
|
117
|
-
event.redisReadMs === undefined
|
|
118
|
-
? undefined
|
|
119
|
-
: Math.max(0, Math.floor(event.redisReadMs)),
|
|
119
|
+
event.redisReadMs === undefined ? undefined : Math.max(0, Math.floor(event.redisReadMs)),
|
|
120
120
|
redisWriteMs:
|
|
121
|
-
event.redisWriteMs === undefined
|
|
122
|
-
? undefined
|
|
123
|
-
: Math.max(0, Math.floor(event.redisWriteMs)),
|
|
121
|
+
event.redisWriteMs === undefined ? undefined : Math.max(0, Math.floor(event.redisWriteMs)),
|
|
124
122
|
poolAgeMs:
|
|
125
|
-
event.poolAgeMs === undefined
|
|
126
|
-
? undefined
|
|
127
|
-
: Math.max(0, Math.floor(event.poolAgeMs)),
|
|
123
|
+
event.poolAgeMs === undefined ? undefined : Math.max(0, Math.floor(event.poolAgeMs)),
|
|
128
124
|
poolExpiresInMs:
|
|
129
125
|
event.poolExpiresInMs === undefined
|
|
130
126
|
? undefined
|
|
131
127
|
: Math.max(0, Math.floor(event.poolExpiresInMs)),
|
|
132
128
|
attempts: Math.max(1, Math.floor(event.attempts || 1)),
|
|
133
129
|
refreshes:
|
|
134
|
-
event.refreshes === undefined
|
|
135
|
-
|
|
136
|
-
|
|
130
|
+
event.refreshes === undefined ? undefined : Math.max(0, Math.floor(event.refreshes)),
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
recordProxyVendorFailover(event: ProxyVendorFailoverTelemetryEvent): void {
|
|
135
|
+
if (this.#failovers.length >= MAX_PROXY_FAILOVER_SAMPLES) return;
|
|
136
|
+
this.#failovers.push({
|
|
137
|
+
vendor: event.vendor,
|
|
138
|
+
...(event.nextVendor ? { nextVendor: event.nextVendor } : {}),
|
|
139
|
+
phase: event.phase,
|
|
140
|
+
reason: event.reason,
|
|
141
|
+
...(event.attempt === undefined ? {} : { attempt: Math.max(0, Math.floor(event.attempt)) }),
|
|
137
142
|
});
|
|
138
143
|
}
|
|
139
144
|
|
|
140
145
|
recordProxyAttempt(event: ProxyAttemptTelemetryEvent): void {
|
|
141
146
|
if (this.#attempts.length >= MAX_PROXY_ATTEMPT_SAMPLES) return;
|
|
142
147
|
this.#attempts.push({
|
|
143
|
-
provider:
|
|
148
|
+
provider: event.provider,
|
|
144
149
|
attempt: Math.max(1, Math.floor(event.attempt || 1)),
|
|
145
150
|
...(event.poolIndex === undefined
|
|
146
151
|
? {}
|
|
@@ -148,9 +153,7 @@ export class ProxyTelemetryCollector implements ProxyTelemetrySink {
|
|
|
148
153
|
...(event.proxyHash ? { proxyHash: event.proxyHash.slice(0, 16) } : {}),
|
|
149
154
|
outcome: event.outcome === "ok" ? "ok" : "error",
|
|
150
155
|
...(event.errorCode ? { errorCode: event.errorCode.slice(0, 80) } : {}),
|
|
151
|
-
...(event.status === undefined
|
|
152
|
-
? {}
|
|
153
|
-
: { status: Math.max(0, Math.floor(event.status)) }),
|
|
156
|
+
...(event.status === undefined ? {} : { status: Math.max(0, Math.floor(event.status)) }),
|
|
154
157
|
...(event.durationMs === undefined
|
|
155
158
|
? {}
|
|
156
159
|
: { durationMs: Math.max(0, Math.floor(event.durationMs)) }),
|
|
@@ -161,27 +164,29 @@ export class ProxyTelemetryCollector implements ProxyTelemetrySink {
|
|
|
161
164
|
const [first, ...rest] = this.#events;
|
|
162
165
|
if (!first) return undefined;
|
|
163
166
|
|
|
167
|
+
// The serving vendor/protocol is the last recorded resolution (a failed
|
|
168
|
+
// vendor records first, the vendor that served records last).
|
|
169
|
+
const serving = this.#events[this.#events.length - 1] ?? first;
|
|
170
|
+
const vendors: ProxyVendorName[] = [];
|
|
171
|
+
for (const event of this.#events) {
|
|
172
|
+
if (!vendors.includes(event.provider)) vendors.push(event.provider);
|
|
173
|
+
}
|
|
174
|
+
|
|
164
175
|
const aggregate = rest.reduce<ProxyResolutionTelemetryEvent>(
|
|
165
176
|
(acc, event) => ({
|
|
166
|
-
provider:
|
|
177
|
+
provider: event.provider,
|
|
167
178
|
cacheStatus: worseStatus(acc.cacheStatus, event.cacheStatus),
|
|
168
179
|
cacheHit: acc.cacheHit && event.cacheHit,
|
|
169
180
|
resolutionMs: acc.resolutionMs + event.resolutionMs,
|
|
170
181
|
allocatorMs: sumOptional(acc.allocatorMs, event.allocatorMs),
|
|
171
182
|
allocatorStatus: event.allocatorStatus ?? acc.allocatorStatus,
|
|
172
183
|
allocatorBodyClass: event.allocatorBodyClass ?? acc.allocatorBodyClass,
|
|
173
|
-
allocatorAttempts: sumOptional(
|
|
174
|
-
acc.allocatorAttempts,
|
|
175
|
-
event.allocatorAttempts,
|
|
176
|
-
),
|
|
184
|
+
allocatorAttempts: sumOptional(acc.allocatorAttempts, event.allocatorAttempts),
|
|
177
185
|
lockWaitMs: sumOptional(acc.lockWaitMs, event.lockWaitMs),
|
|
178
186
|
redisReadMs: sumOptional(acc.redisReadMs, event.redisReadMs),
|
|
179
187
|
redisWriteMs: sumOptional(acc.redisWriteMs, event.redisWriteMs),
|
|
180
188
|
poolAgeMs: maxOptional(acc.poolAgeMs, event.poolAgeMs),
|
|
181
|
-
poolExpiresInMs: maxOptional(
|
|
182
|
-
acc.poolExpiresInMs,
|
|
183
|
-
event.poolExpiresInMs,
|
|
184
|
-
),
|
|
189
|
+
poolExpiresInMs: maxOptional(acc.poolExpiresInMs, event.poolExpiresInMs),
|
|
185
190
|
attempts: acc.attempts + event.attempts,
|
|
186
191
|
refreshes: sumOptional(acc.refreshes, event.refreshes),
|
|
187
192
|
}),
|
|
@@ -190,13 +195,12 @@ export class ProxyTelemetryCollector implements ProxyTelemetrySink {
|
|
|
190
195
|
const payload: ProviderTelemetryHeader = {
|
|
191
196
|
v: 1,
|
|
192
197
|
proxy: {
|
|
193
|
-
provider:
|
|
198
|
+
provider: serving.provider,
|
|
199
|
+
...(serving.protocol ? { protocol: serving.protocol } : {}),
|
|
194
200
|
cacheStatus: aggregate.cacheStatus,
|
|
195
201
|
cacheHit: aggregate.cacheHit,
|
|
196
202
|
resolutionMs: aggregate.resolutionMs,
|
|
197
|
-
...(aggregate.allocatorMs !== undefined
|
|
198
|
-
? { allocatorMs: aggregate.allocatorMs }
|
|
199
|
-
: {}),
|
|
203
|
+
...(aggregate.allocatorMs !== undefined ? { allocatorMs: aggregate.allocatorMs } : {}),
|
|
200
204
|
...(aggregate.allocatorStatus !== undefined
|
|
201
205
|
? { allocatorStatus: aggregate.allocatorStatus }
|
|
202
206
|
: {}),
|
|
@@ -206,40 +210,38 @@ export class ProxyTelemetryCollector implements ProxyTelemetrySink {
|
|
|
206
210
|
...(aggregate.allocatorAttempts !== undefined
|
|
207
211
|
? { allocatorAttempts: aggregate.allocatorAttempts }
|
|
208
212
|
: {}),
|
|
209
|
-
...(aggregate.lockWaitMs !== undefined
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
...(aggregate.
|
|
213
|
-
? { redisReadMs: aggregate.redisReadMs }
|
|
214
|
-
: {}),
|
|
215
|
-
...(aggregate.redisWriteMs !== undefined
|
|
216
|
-
? { redisWriteMs: aggregate.redisWriteMs }
|
|
217
|
-
: {}),
|
|
218
|
-
...(aggregate.poolAgeMs !== undefined
|
|
219
|
-
? { poolAgeMs: aggregate.poolAgeMs }
|
|
220
|
-
: {}),
|
|
213
|
+
...(aggregate.lockWaitMs !== undefined ? { lockWaitMs: aggregate.lockWaitMs } : {}),
|
|
214
|
+
...(aggregate.redisReadMs !== undefined ? { redisReadMs: aggregate.redisReadMs } : {}),
|
|
215
|
+
...(aggregate.redisWriteMs !== undefined ? { redisWriteMs: aggregate.redisWriteMs } : {}),
|
|
216
|
+
...(aggregate.poolAgeMs !== undefined ? { poolAgeMs: aggregate.poolAgeMs } : {}),
|
|
221
217
|
...(aggregate.poolExpiresInMs !== undefined
|
|
222
218
|
? { poolExpiresInMs: aggregate.poolExpiresInMs }
|
|
223
219
|
: {}),
|
|
224
220
|
attempts: aggregate.attempts,
|
|
225
|
-
...(aggregate.refreshes !== undefined
|
|
226
|
-
? { refreshes: aggregate.refreshes }
|
|
227
|
-
: {}),
|
|
221
|
+
...(aggregate.refreshes !== undefined ? { refreshes: aggregate.refreshes } : {}),
|
|
228
222
|
...(this.#attempts.length > 0
|
|
229
223
|
? {
|
|
230
224
|
attemptSamples: this.#attempts.map((attempt, index) => ({
|
|
231
225
|
n: index + 1,
|
|
232
226
|
a: attempt.attempt,
|
|
233
|
-
...(attempt.poolIndex === undefined
|
|
234
|
-
? {}
|
|
235
|
-
: { i: attempt.poolIndex }),
|
|
227
|
+
...(attempt.poolIndex === undefined ? {} : { i: attempt.poolIndex }),
|
|
236
228
|
...(attempt.proxyHash ? { h: attempt.proxyHash } : {}),
|
|
237
229
|
o: attempt.outcome,
|
|
238
230
|
...(attempt.errorCode ? { c: attempt.errorCode } : {}),
|
|
239
231
|
...(attempt.status === undefined ? {} : { s: attempt.status }),
|
|
240
|
-
...(attempt.durationMs === undefined
|
|
241
|
-
|
|
242
|
-
|
|
232
|
+
...(attempt.durationMs === undefined ? {} : { d: attempt.durationMs }),
|
|
233
|
+
})),
|
|
234
|
+
}
|
|
235
|
+
: {}),
|
|
236
|
+
...(vendors.length > 1 ? { vendors } : {}),
|
|
237
|
+
...(this.#failovers.length > 0
|
|
238
|
+
? {
|
|
239
|
+
failovers: this.#failovers.map((failover) => ({
|
|
240
|
+
v: failover.vendor,
|
|
241
|
+
...(failover.nextVendor ? { nx: failover.nextVendor } : {}),
|
|
242
|
+
p: failover.phase,
|
|
243
|
+
r: failover.reason,
|
|
244
|
+
...(failover.attempt === undefined ? {} : { a: failover.attempt }),
|
|
243
245
|
})),
|
|
244
246
|
}
|
|
245
247
|
: {}),
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { HttpRedirectFailureReason } from "../types.js";
|
|
2
|
+
|
|
3
|
+
const REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);
|
|
4
|
+
|
|
5
|
+
export type RedirectHopDecision<TMethod extends string> =
|
|
6
|
+
| {
|
|
7
|
+
kind: "follow";
|
|
8
|
+
nextMethod: TMethod | "GET";
|
|
9
|
+
nextUrl: string;
|
|
10
|
+
}
|
|
11
|
+
| {
|
|
12
|
+
kind: "stop";
|
|
13
|
+
reason: HttpRedirectFailureReason;
|
|
14
|
+
nextUrl?: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function isRedirectStatus(status: number): boolean {
|
|
18
|
+
return REDIRECT_STATUSES.has(status);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Shared fetch-compatible redirect method rewriting for stealth and ctx.http. */
|
|
22
|
+
export function nextRedirectMethod<TMethod extends string>(
|
|
23
|
+
status: number,
|
|
24
|
+
method: TMethod,
|
|
25
|
+
): TMethod | "GET" {
|
|
26
|
+
if (status === 303 && method !== "HEAD") return "GET";
|
|
27
|
+
if ((status === 301 || status === 302) && method === "POST") return "GET";
|
|
28
|
+
return method;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Resolves a Location value against the response URL without issuing a request. */
|
|
32
|
+
export function resolveRedirectUrl(
|
|
33
|
+
location: string | undefined,
|
|
34
|
+
responseUrl: string,
|
|
35
|
+
): string | undefined {
|
|
36
|
+
return location ? new URL(location, responseUrl).toString() : undefined;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Shared post-response decision ordering for both redirect walkers. The
|
|
41
|
+
* caller-owned stop hook is checked first, matching stealth's pre-follow
|
|
42
|
+
* contract, then structural termination and loop checks run before follow.
|
|
43
|
+
*/
|
|
44
|
+
export function evaluateRedirectHop<TMethod extends string>(input: {
|
|
45
|
+
status: number;
|
|
46
|
+
method: TMethod;
|
|
47
|
+
nextUrl: string | undefined;
|
|
48
|
+
shouldStop: boolean;
|
|
49
|
+
redirectCount: number;
|
|
50
|
+
maxHops: number;
|
|
51
|
+
visitedRequests: ReadonlySet<string>;
|
|
52
|
+
}): RedirectHopDecision<TMethod> {
|
|
53
|
+
if (input.shouldStop) {
|
|
54
|
+
return { kind: "stop", reason: "stopped", ...(input.nextUrl ? { nextUrl: input.nextUrl } : {}) };
|
|
55
|
+
}
|
|
56
|
+
if (!input.nextUrl) return { kind: "stop", reason: "missing_location" };
|
|
57
|
+
if (input.redirectCount > input.maxHops) {
|
|
58
|
+
return { kind: "stop", reason: "max_hops", nextUrl: input.nextUrl };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const nextMethod = nextRedirectMethod(input.status, input.method);
|
|
62
|
+
if (input.visitedRequests.has(`${nextMethod} ${input.nextUrl}`)) {
|
|
63
|
+
return { kind: "stop", reason: "loop", nextUrl: input.nextUrl };
|
|
64
|
+
}
|
|
65
|
+
return { kind: "follow", nextMethod, nextUrl: input.nextUrl };
|
|
66
|
+
}
|
package/src/runtime/redis.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import Redis from "ioredis";
|
|
1
|
+
import { Redis } from "ioredis";
|
|
2
2
|
|
|
3
3
|
export type ProviderRedisClient = Redis;
|
|
4
4
|
|
|
@@ -43,9 +43,7 @@ export async function withRedisTimeout<T>(
|
|
|
43
43
|
}
|
|
44
44
|
}, options.timeoutMs);
|
|
45
45
|
});
|
|
46
|
-
const operationResult = options.onError
|
|
47
|
-
? operation().catch(options.onError)
|
|
48
|
-
: operation();
|
|
46
|
+
const operationResult = options.onError ? operation().catch(options.onError) : operation();
|
|
49
47
|
return await Promise.race([operationResult, timeout]);
|
|
50
48
|
} finally {
|
|
51
49
|
if (timeoutId) clearTimeout(timeoutId);
|
|
@@ -56,10 +54,7 @@ export function redisStatus(redis: ProviderRedisClient): string {
|
|
|
56
54
|
return redis.status;
|
|
57
55
|
}
|
|
58
56
|
|
|
59
|
-
async function waitForRedisReady(
|
|
60
|
-
redis: ProviderRedisClient,
|
|
61
|
-
timeoutMs: number,
|
|
62
|
-
): Promise<boolean> {
|
|
57
|
+
async function waitForRedisReady(redis: ProviderRedisClient, timeoutMs: number): Promise<boolean> {
|
|
63
58
|
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
64
59
|
let settled = false;
|
|
65
60
|
|
|
@@ -80,10 +75,7 @@ async function waitForRedisReady(
|
|
|
80
75
|
const onReady = () => finish(true);
|
|
81
76
|
const onUnavailable = () => finish(false);
|
|
82
77
|
|
|
83
|
-
timeoutId = setTimeout(
|
|
84
|
-
() => finish(redisStatus(redis) === "ready"),
|
|
85
|
-
timeoutMs,
|
|
86
|
-
);
|
|
78
|
+
timeoutId = setTimeout(() => finish(redisStatus(redis) === "ready"), timeoutMs);
|
|
87
79
|
redis.once("ready", onReady);
|
|
88
80
|
redis.once("close", onUnavailable);
|
|
89
81
|
redis.once("end", onUnavailable);
|