@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
package/src/runtime/otlp.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TraceSpan } from "../types";
|
|
1
|
+
import type { TraceSpan } from "../types.js";
|
|
2
2
|
|
|
3
3
|
export interface OTLPExportOptions {
|
|
4
4
|
endpoint: string;
|
|
@@ -32,10 +32,7 @@ function createTraceId(signature: string): string {
|
|
|
32
32
|
return traceId;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
function normalizeHexId(
|
|
36
|
-
value: string | undefined,
|
|
37
|
-
length: number,
|
|
38
|
-
): string | undefined {
|
|
35
|
+
function normalizeHexId(value: string | undefined, length: number): string | undefined {
|
|
39
36
|
if (!value) {
|
|
40
37
|
return undefined;
|
|
41
38
|
}
|
|
@@ -44,9 +41,7 @@ function normalizeHexId(
|
|
|
44
41
|
return normalized.padStart(length, "0").slice(-length);
|
|
45
42
|
}
|
|
46
43
|
|
|
47
|
-
function toAttributeValue(
|
|
48
|
-
value: unknown,
|
|
49
|
-
): Record<string, string | number | boolean> {
|
|
44
|
+
function toAttributeValue(value: unknown): Record<string, string | number | boolean> {
|
|
50
45
|
if (typeof value === "string") {
|
|
51
46
|
return { stringValue: value };
|
|
52
47
|
}
|
|
@@ -92,20 +87,16 @@ export function spansToOTLP(
|
|
|
92
87
|
}>;
|
|
93
88
|
}>;
|
|
94
89
|
} {
|
|
95
|
-
const traceId = createTraceId(
|
|
96
|
-
createBatchSignature(spans, resourceAttributes),
|
|
97
|
-
);
|
|
90
|
+
const traceId = createTraceId(createBatchSignature(spans, resourceAttributes));
|
|
98
91
|
|
|
99
92
|
return {
|
|
100
93
|
resourceSpans: [
|
|
101
94
|
{
|
|
102
95
|
resource: {
|
|
103
|
-
attributes: Object.entries(resourceAttributes ?? {}).map(
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}),
|
|
108
|
-
),
|
|
96
|
+
attributes: Object.entries(resourceAttributes ?? {}).map(([key, value]) => ({
|
|
97
|
+
key,
|
|
98
|
+
value: { stringValue: value },
|
|
99
|
+
})),
|
|
109
100
|
},
|
|
110
101
|
scopeSpans: [
|
|
111
102
|
{
|
|
@@ -122,12 +113,10 @@ export function spansToOTLP(
|
|
|
122
113
|
startTimeUnixNano: String(span.startedAt * 1_000_000),
|
|
123
114
|
endTimeUnixNano: String(span.endedAt * 1_000_000),
|
|
124
115
|
status: { code: span.status === "ok" ? 1 : 2 },
|
|
125
|
-
attributes: Object.entries(span.attributes ?? {}).map(
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}),
|
|
130
|
-
),
|
|
116
|
+
attributes: Object.entries(span.attributes ?? {}).map(([key, value]) => ({
|
|
117
|
+
key,
|
|
118
|
+
value: toAttributeValue(value),
|
|
119
|
+
})),
|
|
131
120
|
})),
|
|
132
121
|
},
|
|
133
122
|
],
|
package/src/runtime/perf.ts
CHANGED
package/src/runtime/provider.ts
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
|
-
import type { ProviderDefinition } from "../types";
|
|
1
|
+
import type { ProviderDefinition } from "../types.js";
|
|
2
2
|
|
|
3
|
-
export function getProviderBaseUrl(
|
|
4
|
-
provider
|
|
5
|
-
): string | undefined {
|
|
6
|
-
const operations = Object.values(
|
|
7
|
-
provider.operations as Record<string, unknown>,
|
|
8
|
-
);
|
|
3
|
+
export function getProviderBaseUrl(provider: ProviderDefinition): string | undefined {
|
|
4
|
+
const operations = Object.values(provider.operations as Record<string, unknown>);
|
|
9
5
|
|
|
10
6
|
for (const operation of operations) {
|
|
11
|
-
const baseUrl = (operation as { upstream?: { baseUrl?: unknown } }).upstream
|
|
12
|
-
?.baseUrl;
|
|
7
|
+
const baseUrl = (operation as { upstream?: { baseUrl?: unknown } }).upstream?.baseUrl;
|
|
13
8
|
|
|
14
9
|
if (typeof baseUrl === "string" && baseUrl.length > 0) {
|
|
15
10
|
return baseUrl;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TransportError } from "../errors";
|
|
1
|
+
import { TransportError } from "../errors.js";
|
|
2
2
|
|
|
3
3
|
export const PROXY_AUTH_IP_DENIED_CODE = "PROXY_AUTH_IP_DENIED";
|
|
4
4
|
export const PROXY_AUTH_IP_DENIED_MESSAGE =
|
|
@@ -19,10 +19,18 @@ const PROXY_AUTH_IP_DENIED_PATTERN =
|
|
|
19
19
|
/\b(?:source|egress|client)\s+ip\b.{0,120}\b(?:deny|denied|unauthori[sz]ed|not\s+authori[sz]ed|white\s*list|allow\s*list)\b|\b(?:white\s*list|allow\s*list)\b.{0,120}\b(?:source|egress|client)\s+ip\b/i;
|
|
20
20
|
const PROXY_EDGE_AUTH_REJECTED_PATTERN =
|
|
21
21
|
/\bauth\s+ip\s+err\b|\bproxy\b.{0,120}\bauth(?:entication)?\b.{0,120}\b(?:reject(?:ed)?|fail(?:ed)?|invalid|den(?:y|ied)|unauthori[sz]ed)\b|\bauth(?:entication)?\b.{0,120}\b(?:reject(?:ed)?|fail(?:ed)?|invalid|den(?:y|ied)|unauthori[sz]ed)\b.{0,120}\bproxy\b/i;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
// Vendor host tokens that can appear in upstream error strings. Adding a proxy
|
|
23
|
+
// vendor updates every classifier below in one place. `proxy` is the generic
|
|
24
|
+
// fallback so vendor-agnostic messages still classify.
|
|
25
|
+
const PROXY_VENDOR_ALTERNATION = "smartproxy|nodemaven|proxy";
|
|
26
|
+
const PROXY_POOL_STALE_MESSAGE_PATTERN = new RegExp(
|
|
27
|
+
`\\bproxy\\b.{0,120}\\b(?:pool|lease|expired|unavailable|exhausted|non[\\s-]?200\\s+code:\\s*(?:509|512))\\b|\\bnon[\\s-]?200\\s+code:\\s*(?:509|512)\\b.{0,120}\\bproxy\\b|\\b(?:${PROXY_VENDOR_ALTERNATION})\\b.{0,120}\\b(?:509|512)\\b`,
|
|
28
|
+
"i",
|
|
29
|
+
);
|
|
30
|
+
const PROXY_EDGE_TLS_REJECTED_MESSAGE_PATTERN = new RegExp(
|
|
31
|
+
`\\b(?:${PROXY_VENDOR_ALTERNATION})\\b.{0,160}\\b(?:495|ssl|tls|cert(?:ificate)?|handshake|edge|connect|non[\\s-]?200)\\b|\\b(?:495|ssl|tls|cert(?:ificate)?|handshake|edge|connect|non[\\s-]?200)\\b.{0,160}\\b(?:${PROXY_VENDOR_ALTERNATION})\\b`,
|
|
32
|
+
"i",
|
|
33
|
+
);
|
|
26
34
|
|
|
27
35
|
export function isProxyAuthIpDeniedMessage(message: string): boolean {
|
|
28
36
|
return PROXY_AUTH_IP_DENIED_PATTERN.test(message);
|
|
@@ -39,9 +47,7 @@ export function isProxyEdgeAuthRejectedMessage(message: string): boolean {
|
|
|
39
47
|
return PROXY_EDGE_AUTH_REJECTED_PATTERN.test(message);
|
|
40
48
|
}
|
|
41
49
|
|
|
42
|
-
export function createProxyEdgeAuthRejectedError(
|
|
43
|
-
cause?: Error,
|
|
44
|
-
): TransportError {
|
|
50
|
+
export function createProxyEdgeAuthRejectedError(cause?: Error): TransportError {
|
|
45
51
|
return new TransportError(PROXY_EDGE_AUTH_REJECTED_MESSAGE, {
|
|
46
52
|
code: PROXY_EDGE_AUTH_REJECTED_CODE,
|
|
47
53
|
cause,
|
|
@@ -52,10 +58,7 @@ export function isProxyPoolStaleStatus(status: number): boolean {
|
|
|
52
58
|
return PROXY_POOL_STALE_STATUS_CODES.has(status);
|
|
53
59
|
}
|
|
54
60
|
|
|
55
|
-
export function isProxyEdgeTlsRejectedResponse(
|
|
56
|
-
status: number,
|
|
57
|
-
evidence: string,
|
|
58
|
-
): boolean {
|
|
61
|
+
export function isProxyEdgeTlsRejectedResponse(status: number, evidence: string): boolean {
|
|
59
62
|
return (
|
|
60
63
|
PROXY_EDGE_TLS_REJECTED_STATUS_CODES.has(status) &&
|
|
61
64
|
PROXY_EDGE_TLS_REJECTED_MESSAGE_PATTERN.test(evidence)
|
|
@@ -67,10 +70,7 @@ export function isProxyPoolStaleMessage(message: string): boolean {
|
|
|
67
70
|
}
|
|
68
71
|
|
|
69
72
|
export function isProxyPoolRefreshableError(error: unknown): boolean {
|
|
70
|
-
if (
|
|
71
|
-
error instanceof TransportError &&
|
|
72
|
-
error.code === PROXY_AUTH_IP_DENIED_CODE
|
|
73
|
-
) {
|
|
73
|
+
if (error instanceof TransportError && error.code === PROXY_AUTH_IP_DENIED_CODE) {
|
|
74
74
|
return false;
|
|
75
75
|
}
|
|
76
76
|
|
|
@@ -89,39 +89,26 @@ export function isProxyPoolRefreshableError(error: unknown): boolean {
|
|
|
89
89
|
cause instanceof Error ? cause.message : "",
|
|
90
90
|
].join(" ");
|
|
91
91
|
return (
|
|
92
|
-
PROXY_POOL_STALE_MESSAGE_PATTERN.test(message) ||
|
|
93
|
-
PROXY_EDGE_AUTH_REJECTED_PATTERN.test(message)
|
|
92
|
+
PROXY_POOL_STALE_MESSAGE_PATTERN.test(message) || PROXY_EDGE_AUTH_REJECTED_PATTERN.test(message)
|
|
94
93
|
);
|
|
95
94
|
}
|
|
96
95
|
|
|
97
96
|
export const isProxyPoolStaleError = isProxyPoolRefreshableError;
|
|
98
97
|
|
|
99
|
-
export function createProxyPoolStaleError(
|
|
100
|
-
status
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
{
|
|
106
|
-
code: PROXY_POOL_STALE_CODE,
|
|
107
|
-
status,
|
|
108
|
-
cause,
|
|
109
|
-
},
|
|
110
|
-
);
|
|
98
|
+
export function createProxyPoolStaleError(status: number, cause?: Error): TransportError {
|
|
99
|
+
return new TransportError(`Proxy provider pool failed with status ${status}`, {
|
|
100
|
+
code: PROXY_POOL_STALE_CODE,
|
|
101
|
+
status,
|
|
102
|
+
cause,
|
|
103
|
+
});
|
|
111
104
|
}
|
|
112
105
|
|
|
113
|
-
export function createProxyEdgeTlsRejectedError(
|
|
114
|
-
status
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
{
|
|
120
|
-
code: PROXY_EDGE_TLS_REJECTED_CODE,
|
|
121
|
-
status,
|
|
122
|
-
cause,
|
|
123
|
-
},
|
|
124
|
-
);
|
|
106
|
+
export function createProxyEdgeTlsRejectedError(status: number, cause?: Error): TransportError {
|
|
107
|
+
return new TransportError(`Proxy edge TLS request was rejected with status ${status}`, {
|
|
108
|
+
code: PROXY_EDGE_TLS_REJECTED_CODE,
|
|
109
|
+
status,
|
|
110
|
+
cause,
|
|
111
|
+
});
|
|
125
112
|
}
|
|
126
113
|
|
|
127
114
|
export function createProxyPoolExhaustedError(cause?: Error): TransportError {
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
import type { ProviderProxyPolicy } from "../types.js";
|
|
4
|
+
|
|
5
|
+
export const NODEMAVEN_USERNAME_ENV = "APIFUSE__PROXY__NODEMAVEN_USERNAME";
|
|
6
|
+
export const NODEMAVEN_PASSWORD_ENV = "APIFUSE__PROXY__NODEMAVEN_PASSWORD";
|
|
7
|
+
export const NODEMAVEN_FILTER_ENV = "APIFUSE__PROXY__NODEMAVEN_FILTER";
|
|
8
|
+
|
|
9
|
+
export const NODEMAVEN_GATEWAY_HOST = "gate.nodemaven.com";
|
|
10
|
+
|
|
11
|
+
export type NodemavenCredentials = {
|
|
12
|
+
readonly username: string;
|
|
13
|
+
readonly password: string;
|
|
14
|
+
readonly filter?: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/** Both schemes tunnel bytes end-to-end, preserving the client TLS handshake. */
|
|
18
|
+
export type ProxyProtocol = "http" | "socks5";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* NodeMaven's fastest protocol: HTTP CONNECT. Benchmarks (KR, cold + warm)
|
|
22
|
+
* showed socks5 through the gateway adds ~500ms per request over http, so
|
|
23
|
+
* NodeMaven never defaults to socks5.
|
|
24
|
+
*/
|
|
25
|
+
export const NODEMAVEN_DEFAULT_PROTOCOL: ProxyProtocol = "http";
|
|
26
|
+
|
|
27
|
+
/** NodeMaven gateway port ranges per protocol (docs: HTTP 8080-9080, SOCKS5 1080-2080). */
|
|
28
|
+
const NODEMAVEN_PORTS: Record<ProxyProtocol, { min: number; max: number }> = {
|
|
29
|
+
http: { min: 8080, max: 9080 },
|
|
30
|
+
socks5: { min: 1080, max: 2080 },
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const NODEMAVEN_FILTERS = new Set(["medium", "high"]);
|
|
34
|
+
const DEFAULT_NODEMAVEN_FILTER = "medium";
|
|
35
|
+
const DEFAULT_NODEMAVEN_POOL_SIZE = 20;
|
|
36
|
+
export const NODEMAVEN_MAX_POOL_SIZE = 50;
|
|
37
|
+
/** NodeMaven sticky sessions persist up to 24h server-side, keyed by the sid. */
|
|
38
|
+
const NODEMAVEN_MAX_LIFETIME_MINUTES = 1440;
|
|
39
|
+
const SID_LENGTH = 10;
|
|
40
|
+
|
|
41
|
+
export function hasNodemavenCredentials(): boolean {
|
|
42
|
+
return Boolean(readNodemavenUsername() && readNodemavenPassword());
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function readNodemavenUsername(): string | undefined {
|
|
46
|
+
return process.env[NODEMAVEN_USERNAME_ENV]?.trim() || undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function readNodemavenPassword(): string | undefined {
|
|
50
|
+
return process.env[NODEMAVEN_PASSWORD_ENV]?.trim() || undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function resolveNodemavenFilter(value: string | undefined): string {
|
|
54
|
+
const raw = value?.trim().toLowerCase();
|
|
55
|
+
if (!raw) return DEFAULT_NODEMAVEN_FILTER;
|
|
56
|
+
if (!NODEMAVEN_FILTERS.has(raw)) {
|
|
57
|
+
throw new Error(`${NODEMAVEN_FILTER_ENV} must be "medium" or "high"`);
|
|
58
|
+
}
|
|
59
|
+
return raw;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function nodemavenPoolSize(policy: ProviderProxyPolicy): number {
|
|
63
|
+
return Math.min(
|
|
64
|
+
NODEMAVEN_MAX_POOL_SIZE,
|
|
65
|
+
Math.max(1, Math.floor(policy.session?.poolSize ?? DEFAULT_NODEMAVEN_POOL_SIZE)),
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function nodemavenLifetimeMinutes(policy: ProviderProxyPolicy): number {
|
|
70
|
+
const configured = policy.session?.lifetimeMinutes;
|
|
71
|
+
if (typeof configured !== "number" || !Number.isFinite(configured) || configured <= 0) {
|
|
72
|
+
return NODEMAVEN_MAX_LIFETIME_MINUTES;
|
|
73
|
+
}
|
|
74
|
+
return Math.min(NODEMAVEN_MAX_LIFETIME_MINUTES, Math.max(1, Math.floor(configured)));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export type NodemavenSessionWindow = {
|
|
78
|
+
refreshEpoch: number;
|
|
79
|
+
expiresAt: string;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/** Stable wall-clock window shared by every process deriving the same sticky sid. */
|
|
83
|
+
export function nodemavenSessionWindow(
|
|
84
|
+
policy: ProviderProxyPolicy,
|
|
85
|
+
now = Date.now(),
|
|
86
|
+
): NodemavenSessionWindow {
|
|
87
|
+
const lifetimeMs = nodemavenLifetimeMinutes(policy) * 60_000;
|
|
88
|
+
const refreshEpoch = Math.floor(now / lifetimeMs);
|
|
89
|
+
return {
|
|
90
|
+
refreshEpoch,
|
|
91
|
+
expiresAt: new Date((refreshEpoch + 1) * lifetimeMs).toISOString(),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** NodeMaven username tokens accept `[a-z0-9]`; slugify geo values to that set. */
|
|
96
|
+
function slugifyGeo(value: string | undefined): string | undefined {
|
|
97
|
+
if (!value) return undefined;
|
|
98
|
+
const slug = value
|
|
99
|
+
.trim()
|
|
100
|
+
.toLowerCase()
|
|
101
|
+
.replace(/[^a-z0-9]+/g, "");
|
|
102
|
+
return slug || undefined;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function isStickyAffinity(policy: ProviderProxyPolicy): boolean {
|
|
106
|
+
return (policy.session?.affinity ?? "request") !== "request";
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* A sticky sid is deterministic from the affinity key so every process serving
|
|
111
|
+
* the same connection derives the same egress IP without shared storage. A
|
|
112
|
+
* rotating sid is random per call (a fresh egress IP per request).
|
|
113
|
+
*/
|
|
114
|
+
function deriveSid(
|
|
115
|
+
policy: ProviderProxyPolicy,
|
|
116
|
+
affinityKey: string | undefined,
|
|
117
|
+
poolIndex: number,
|
|
118
|
+
refreshEpoch: number,
|
|
119
|
+
): string {
|
|
120
|
+
if (!isStickyAffinity(policy) || !affinityKey) {
|
|
121
|
+
return randomBytes(SID_LENGTH).toString("hex").slice(0, SID_LENGTH);
|
|
122
|
+
}
|
|
123
|
+
const digest = createHash("sha256")
|
|
124
|
+
.update(`${affinityKey}:${refreshEpoch}:${poolIndex}`)
|
|
125
|
+
.digest("hex");
|
|
126
|
+
// hex digits are a subset of the allowed [a-z0-9] sid charset.
|
|
127
|
+
return digest.slice(0, SID_LENGTH);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function selectPort(protocol: ProxyProtocol, sid: string, poolIndex: number): number {
|
|
131
|
+
const { min, max } = NODEMAVEN_PORTS[protocol];
|
|
132
|
+
const span = max - min + 1;
|
|
133
|
+
const hashInt = Number.parseInt(
|
|
134
|
+
createHash("sha256").update(`${sid}:${poolIndex}`).digest("hex").slice(0, 8),
|
|
135
|
+
16,
|
|
136
|
+
);
|
|
137
|
+
return min + (hashInt % span);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export type NodemavenSynthesisInput = {
|
|
141
|
+
policy: ProviderProxyPolicy;
|
|
142
|
+
/** Explicit credentials; synthesis never reads process-global state. */
|
|
143
|
+
credentials: NodemavenCredentials;
|
|
144
|
+
affinityKey: string | undefined;
|
|
145
|
+
protocol: ProxyProtocol;
|
|
146
|
+
poolIndex: number;
|
|
147
|
+
refreshEpoch: number;
|
|
148
|
+
/** ISO 3166-1 alpha-2, already resolved by the caller (falls back to env). */
|
|
149
|
+
country?: string;
|
|
150
|
+
/** Clock injection used by native sticky-expiry lifecycle tests. */
|
|
151
|
+
now?: number;
|
|
152
|
+
/** Stable caller-owned hard expiry when the refresh epoch is wall-clock-derived. */
|
|
153
|
+
expiresAt?: string;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
export type NodemavenSynthesis = {
|
|
157
|
+
url: string;
|
|
158
|
+
protocol: ProxyProtocol;
|
|
159
|
+
sticky: boolean;
|
|
160
|
+
sessionId: string;
|
|
161
|
+
expiresAt?: string;
|
|
162
|
+
diagnostics: Record<string, string | number | boolean>;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Synthesize a NodeMaven gateway proxy URL locally from static credentials.
|
|
167
|
+
* There is no allocation API — geo/session are encoded in the username.
|
|
168
|
+
*/
|
|
169
|
+
export function synthesizeNodemavenProxy(input: NodemavenSynthesisInput): NodemavenSynthesis {
|
|
170
|
+
const username = input.credentials.username.trim();
|
|
171
|
+
const password = input.credentials.password.trim();
|
|
172
|
+
if (!username || !password) {
|
|
173
|
+
throw new Error(
|
|
174
|
+
`NodeMaven credentials missing: set ${NODEMAVEN_USERNAME_ENV} and ${NODEMAVEN_PASSWORD_ENV}.`,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const filter = resolveNodemavenFilter(input.credentials.filter);
|
|
179
|
+
const sid = deriveSid(input.policy, input.affinityKey, input.poolIndex, input.refreshEpoch);
|
|
180
|
+
const port = selectPort(input.protocol, sid, input.poolIndex);
|
|
181
|
+
const lifetimeMinutes = nodemavenLifetimeMinutes(input.policy);
|
|
182
|
+
const sticky = isStickyAffinity(input.policy);
|
|
183
|
+
|
|
184
|
+
const country = slugifyGeo(input.country ?? input.policy.geo?.country);
|
|
185
|
+
const region = slugifyGeo(input.policy.geo?.subdivision);
|
|
186
|
+
const city = slugifyGeo(input.policy.geo?.city);
|
|
187
|
+
|
|
188
|
+
const tokens = [username];
|
|
189
|
+
if (country) tokens.push("country", country);
|
|
190
|
+
if (region) tokens.push("region", region);
|
|
191
|
+
if (city) tokens.push("city", city);
|
|
192
|
+
tokens.push("sid", sid);
|
|
193
|
+
tokens.push("filter", filter);
|
|
194
|
+
tokens.push("ipv4", "true");
|
|
195
|
+
const proxyUsername = tokens.join("-");
|
|
196
|
+
|
|
197
|
+
// Username tokens are [a-z0-9-] only, which survive URL encoding unchanged.
|
|
198
|
+
const url = `${input.protocol}://${proxyUsername}:${encodeURIComponent(password)}@${NODEMAVEN_GATEWAY_HOST}:${port}`;
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
url,
|
|
202
|
+
protocol: input.protocol,
|
|
203
|
+
sticky,
|
|
204
|
+
sessionId: sid,
|
|
205
|
+
...(sticky
|
|
206
|
+
? {
|
|
207
|
+
expiresAt:
|
|
208
|
+
input.expiresAt ??
|
|
209
|
+
new Date((input.now ?? Date.now()) + lifetimeMinutes * 60_000).toISOString(),
|
|
210
|
+
}
|
|
211
|
+
: {}),
|
|
212
|
+
diagnostics: {
|
|
213
|
+
vendor: "nodemaven",
|
|
214
|
+
protocol: input.protocol,
|
|
215
|
+
sticky,
|
|
216
|
+
filter,
|
|
217
|
+
lifetimeMinutes,
|
|
218
|
+
...(country ? { country } : {}),
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { ProviderError, TransportError } from "../errors";
|
|
2
|
-
import type { HttpMethod, HttpRetryOptions, RequestOptions } from "../types";
|
|
1
|
+
import { ProviderError, TransportError } from "../errors.js";
|
|
2
|
+
import type { HttpMethod, HttpRetryOptions, RequestOptions } from "../types.js";
|
|
3
3
|
import {
|
|
4
4
|
HttpRetryAfterPolicy,
|
|
5
5
|
HttpRetryDelayStrategy,
|
|
6
6
|
HttpRetryJitter,
|
|
7
7
|
HttpRetryPreset,
|
|
8
8
|
HttpRetryUnsafeMethodPolicy,
|
|
9
|
-
} from "../types";
|
|
9
|
+
} from "../types.js";
|
|
10
10
|
|
|
11
11
|
export type NormalizedProxyTransportRetryOptions = Required<
|
|
12
12
|
Pick<
|