@apifuse/provider-sdk 2.2.0-beta.58 → 2.2.0-beta.60
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 +25 -0
- package/CHANGELOG.md +8 -0
- package/README.md +18 -2
- package/bin/apifuse-dev.ts +2 -1
- package/bin/apifuse-pack-types.ts +49 -0
- package/bin/apifuse-perf.ts +41 -4
- package/bin/apifuse-record.ts +9 -2
- package/dist/cli/templates/provider/README.md.tpl +15 -3
- package/dist/config/loader.d.ts +7 -0
- package/dist/config/loader.js +43 -18
- package/dist/declaration-validation.d.ts +2 -1
- package/dist/declaration-validation.js +39 -0
- package/dist/define.d.ts +4 -4
- package/dist/define.js +25 -1
- package/dist/engine.d.ts +16 -0
- package/dist/engine.js +59 -3
- package/dist/error-resolution.js +13 -0
- package/dist/index.d.ts +7 -4
- package/dist/index.js +4 -2
- package/dist/lint.js +38 -17
- package/dist/provider.d.ts +3 -3
- package/dist/provider.js +2 -2
- package/dist/runtime/bounded-expiring-map.d.ts +21 -0
- package/dist/runtime/bounded-expiring-map.js +64 -0
- package/dist/runtime/browser.js +2 -1
- package/dist/runtime/cache.d.ts +2 -0
- package/dist/runtime/cache.js +11 -3
- package/dist/runtime/diagnostic-env-values.d.ts +5 -0
- package/dist/runtime/diagnostic-env-values.js +57 -0
- package/dist/runtime/diagnostic-env.d.ts +11 -0
- package/dist/runtime/diagnostic-env.js +35 -0
- package/dist/runtime/diagnostic-redactor.d.ts +49 -0
- package/dist/runtime/diagnostic-redactor.js +569 -0
- package/dist/runtime/egress-lease.d.ts +65 -0
- package/dist/runtime/egress-lease.js +236 -0
- package/dist/runtime/env.js +2 -1
- package/dist/runtime/http.js +1 -0
- package/dist/runtime/instrumentation.js +3 -0
- package/dist/runtime/ocr.js +2 -1
- package/dist/runtime/otlp.js +5 -4
- package/dist/runtime/proxy-nodemaven.js +3 -2
- package/dist/runtime/proxy-retry-policy.d.ts +4 -1
- package/dist/runtime/proxy-retry-policy.js +11 -2
- package/dist/runtime/request-options.d.ts +7 -0
- package/dist/runtime/request-options.js +62 -22
- package/dist/runtime/request-telemetry.d.ts +7 -9
- package/dist/runtime/resolver-config.d.ts +7 -0
- package/dist/runtime/resolver-config.js +8 -0
- package/dist/runtime/resolver-public.d.ts +3 -1
- package/dist/runtime/resolver-public.js +1 -1
- package/dist/runtime/resolver-shared.d.ts +9 -0
- package/dist/runtime/resolver-shared.js +57 -0
- package/dist/runtime/resolver-telemetry.d.ts +134 -0
- package/dist/runtime/resolver-telemetry.js +204 -0
- package/dist/runtime/resolver-usage.d.ts +24 -0
- package/dist/runtime/resolver-usage.js +60 -0
- package/dist/runtime/resolver-vendors/bindings.d.ts +5 -0
- package/dist/runtime/resolver-vendors/bindings.js +5 -0
- package/dist/runtime/resolver-vendors/browser.js +0 -1
- package/dist/runtime/resolver-vendors/capsolver.d.ts +2 -2
- package/dist/runtime/resolver-vendors/capsolver.js +21 -9
- package/dist/runtime/resolver-vendors/hypersolutions.d.ts +27 -0
- package/dist/runtime/resolver-vendors/hypersolutions.js +369 -0
- package/dist/runtime/resolver-vendors/twocaptcha.d.ts +2 -2
- package/dist/runtime/resolver-vendors/twocaptcha.js +26 -10
- package/dist/runtime/resolver-vendors/types.d.ts +35 -5
- package/dist/runtime/resolver-vendors/types.js +20 -10
- package/dist/runtime/resolver.d.ts +5 -1
- package/dist/runtime/resolver.js +400 -111
- package/dist/runtime/stealth-akamai-sbsd.d.ts +54 -0
- package/dist/runtime/stealth-akamai-sbsd.js +142 -0
- package/dist/runtime/stealth.js +547 -53
- package/dist/runtime/stt.js +3 -2
- package/dist/runtime/trace.d.ts +5 -0
- package/dist/runtime/trace.js +115 -20
- package/dist/schema.d.ts +20 -0
- package/dist/schema.js +72 -2
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.js +1 -0
- package/dist/server/self-test-redaction.js +8 -33
- package/dist/server/self-test.d.ts +4 -0
- package/dist/server/self-test.js +10 -1
- package/dist/server/sensitive-values.d.ts +26 -0
- package/dist/server/sensitive-values.js +131 -0
- package/dist/server/serve-implementation.d.ts +7 -1
- package/dist/server/serve-implementation.js +394 -125
- package/dist/server/trace-output.d.ts +2 -1
- package/dist/server/trace-output.js +46 -14
- package/dist/server/types.d.ts +6 -0
- package/dist/server/types.js +14 -0
- package/dist/stealth/profiles.d.ts +7 -1
- package/dist/stealth/profiles.js +9 -0
- package/dist/testing/run.js +6 -0
- package/dist/trace-sanitization.d.ts +4 -3
- package/dist/trace-sanitization.js +45 -22
- package/dist/types.d.ts +106 -10
- package/package.json +2 -1
- package/src/cli/templates/provider/README.md.tpl +15 -3
- package/src/config/loader.ts +47 -18
- package/src/declaration-validation.ts +47 -1
- package/src/define.ts +45 -3
- package/src/engine.ts +78 -3
- package/src/error-resolution.ts +13 -0
- package/src/index.ts +34 -1
- package/src/lint.ts +44 -18
- package/src/provider.ts +10 -0
- package/src/runtime/bounded-expiring-map.ts +67 -0
- package/src/runtime/browser.ts +2 -1
- package/src/runtime/cache.ts +13 -3
- package/src/runtime/diagnostic-env-values.ts +56 -0
- package/src/runtime/diagnostic-env.ts +55 -0
- package/src/runtime/diagnostic-redactor.ts +662 -0
- package/src/runtime/egress-lease.ts +341 -0
- package/src/runtime/env.ts +2 -1
- package/src/runtime/http.ts +1 -0
- package/src/runtime/instrumentation.ts +3 -0
- package/src/runtime/ocr.ts +2 -1
- package/src/runtime/otlp.ts +5 -4
- package/src/runtime/proxy-nodemaven.ts +3 -2
- package/src/runtime/proxy-retry-policy.ts +12 -1
- package/src/runtime/request-options.ts +76 -28
- package/src/runtime/request-telemetry.ts +5 -7
- package/src/runtime/resolver-config.ts +12 -0
- package/src/runtime/resolver-public.ts +21 -0
- package/src/runtime/resolver-shared.ts +75 -0
- package/src/runtime/resolver-telemetry.ts +362 -0
- package/src/runtime/resolver-usage.ts +96 -0
- package/src/runtime/resolver-vendors/bindings.ts +5 -0
- package/src/runtime/resolver-vendors/browser.ts +0 -1
- package/src/runtime/resolver-vendors/capsolver.ts +22 -3
- package/src/runtime/resolver-vendors/hypersolutions.ts +480 -0
- package/src/runtime/resolver-vendors/twocaptcha.ts +26 -12
- package/src/runtime/resolver-vendors/types.ts +60 -12
- package/src/runtime/resolver.ts +468 -126
- package/src/runtime/stealth-akamai-sbsd.ts +207 -0
- package/src/runtime/stealth.ts +727 -95
- package/src/runtime/stt.ts +3 -2
- package/src/runtime/trace.ts +168 -26
- package/src/schema.ts +80 -2
- package/src/server/index.ts +21 -0
- package/src/server/self-test-redaction.ts +10 -40
- package/src/server/self-test.ts +13 -0
- package/src/server/sensitive-values.ts +200 -0
- package/src/server/serve-implementation.ts +561 -136
- package/src/server/trace-output.ts +77 -17
- package/src/server/types.ts +14 -0
- package/src/stealth/profiles.ts +10 -0
- package/src/testing/run.ts +6 -0
- package/src/trace-sanitization.ts +70 -23
- package/src/types.ts +124 -19
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { readDiagnosticEnv } from "./diagnostic-env.js";
|
|
2
|
+
import { createCipheriv, createDecipheriv, createHash, randomBytes } from "node:crypto";
|
|
3
|
+
import { SDKError } from "../errors.js";
|
|
4
|
+
/**
|
|
5
|
+
* AES-256-GCM key for the engine ceremony egress lease handle (ADR-0010 v1.1).
|
|
6
|
+
* Engine host only: the `APIFUSE__ENGINE__` family is rejected from provider
|
|
7
|
+
* secrets and filtered from provider env projections. Supply at least 32 random
|
|
8
|
+
* bytes (for example `openssl rand -base64 32`). Rotating the key fails
|
|
9
|
+
* verification of every outstanding handle (`EGRESS_LEASE_INVALID`, 409): the
|
|
10
|
+
* affected ceremonies restart; they do not expire.
|
|
11
|
+
*/
|
|
12
|
+
export const APIFUSE__ENGINE__CEREMONY_LEASE_KEY = "APIFUSE__ENGINE__CEREMONY_LEASE_KEY";
|
|
13
|
+
/** Internal option key shared only by the server assembler and stealth runtime. */
|
|
14
|
+
export const ENGINE_CEREMONY_EGRESS_LEASE = Symbol.for("@apifuse/provider-sdk/engine-ceremony-egress-lease");
|
|
15
|
+
const HANDLE_VERSION = 1;
|
|
16
|
+
const HANDLE_MAX_BYTES = 16_384;
|
|
17
|
+
const AEAD_NONCE_BYTES = 12;
|
|
18
|
+
const AEAD_TAG_BYTES = 16;
|
|
19
|
+
const AEAD_ALGORITHM = "aes-256-gcm";
|
|
20
|
+
const AEAD_KEY_DOMAIN = "apifuse:ceremony-egress-lease:v1\0";
|
|
21
|
+
const LEASE_KEY_MIN_BYTES = 32;
|
|
22
|
+
/** The caller presented a handle this engine cannot verify for this ceremony (409). */
|
|
23
|
+
function invalidLease() {
|
|
24
|
+
throw new SDKError("The engine ceremony egress lease is invalid", {
|
|
25
|
+
code: "EGRESS_LEASE_INVALID",
|
|
26
|
+
fix: "Restart the authentication ceremony to obtain a new engine-owned egress lease.",
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/** The engine violated its own binding invariant (500); never attributable to the caller. */
|
|
30
|
+
function bindingFault(message) {
|
|
31
|
+
throw new SDKError(message, {
|
|
32
|
+
code: "EGRESS_LEASE_BINDING_INVALID",
|
|
33
|
+
fix: "This is an SDK or provider-runtime fault; the ceremony lease cannot bind the selected egress.",
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function encryptionKey(key) {
|
|
37
|
+
return createHash("sha256").update(AEAD_KEY_DOMAIN, "utf8").update(key, "utf8").digest();
|
|
38
|
+
}
|
|
39
|
+
/** Fixed field order and JSON.stringify's compact form are the canonical AAD encoding. */
|
|
40
|
+
function scopeAad(scope) {
|
|
41
|
+
return Buffer.from(JSON.stringify({
|
|
42
|
+
version: HANDLE_VERSION,
|
|
43
|
+
tenantId: scope.tenantId,
|
|
44
|
+
providerId: scope.providerId,
|
|
45
|
+
flowId: scope.flowId,
|
|
46
|
+
affinityKey: scope.affinityKey,
|
|
47
|
+
}), "utf8");
|
|
48
|
+
}
|
|
49
|
+
function encodeHandle(payload, key, scope) {
|
|
50
|
+
const nonce = randomBytes(AEAD_NONCE_BYTES);
|
|
51
|
+
const cipher = createCipheriv(AEAD_ALGORITHM, encryptionKey(key), nonce, {
|
|
52
|
+
authTagLength: AEAD_TAG_BYTES,
|
|
53
|
+
});
|
|
54
|
+
cipher.setAAD(scopeAad(scope));
|
|
55
|
+
const ciphertext = Buffer.concat([
|
|
56
|
+
cipher.update(JSON.stringify(payload), "utf8"),
|
|
57
|
+
cipher.final(),
|
|
58
|
+
]);
|
|
59
|
+
return [
|
|
60
|
+
`v${HANDLE_VERSION}`,
|
|
61
|
+
nonce.toString("base64url"),
|
|
62
|
+
ciphertext.toString("base64url"),
|
|
63
|
+
cipher.getAuthTag().toString("base64url"),
|
|
64
|
+
].join(".");
|
|
65
|
+
}
|
|
66
|
+
function isFiniteInteger(value) {
|
|
67
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
68
|
+
}
|
|
69
|
+
function isPositiveFinite(value) {
|
|
70
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
71
|
+
}
|
|
72
|
+
function decodeHandle(handle, key, scope) {
|
|
73
|
+
if (Buffer.byteLength(handle, "utf8") > HANDLE_MAX_BYTES)
|
|
74
|
+
return invalidLease();
|
|
75
|
+
const parts = handle.split(".");
|
|
76
|
+
if (parts.length !== 4 ||
|
|
77
|
+
parts[0] !== `v${HANDLE_VERSION}` ||
|
|
78
|
+
parts.slice(1).some((part) => !/^[A-Za-z0-9_-]+$/u.test(part))) {
|
|
79
|
+
return invalidLease();
|
|
80
|
+
}
|
|
81
|
+
let parsed;
|
|
82
|
+
try {
|
|
83
|
+
const nonce = Buffer.from(parts[1], "base64url");
|
|
84
|
+
const ciphertext = Buffer.from(parts[2], "base64url");
|
|
85
|
+
const tag = Buffer.from(parts[3], "base64url");
|
|
86
|
+
if (nonce.byteLength !== AEAD_NONCE_BYTES ||
|
|
87
|
+
tag.byteLength !== AEAD_TAG_BYTES ||
|
|
88
|
+
ciphertext.byteLength === 0) {
|
|
89
|
+
return invalidLease();
|
|
90
|
+
}
|
|
91
|
+
const decipher = createDecipheriv(AEAD_ALGORITHM, encryptionKey(key), nonce, {
|
|
92
|
+
authTagLength: AEAD_TAG_BYTES,
|
|
93
|
+
});
|
|
94
|
+
decipher.setAAD(scopeAad(scope));
|
|
95
|
+
decipher.setAuthTag(tag);
|
|
96
|
+
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
97
|
+
parsed = JSON.parse(plaintext.toString("utf8"));
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return invalidLease();
|
|
101
|
+
}
|
|
102
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
103
|
+
return invalidLease();
|
|
104
|
+
const candidate = parsed;
|
|
105
|
+
if (candidate.version !== HANDLE_VERSION ||
|
|
106
|
+
(candidate.vendor !== "smartproxy" && candidate.vendor !== "nodemaven") ||
|
|
107
|
+
typeof candidate.proxyUrl !== "string" ||
|
|
108
|
+
!candidate.proxyUrl ||
|
|
109
|
+
!isFiniteInteger(candidate.poolIndex) ||
|
|
110
|
+
typeof candidate.affinityKey !== "string" ||
|
|
111
|
+
!candidate.affinityKey ||
|
|
112
|
+
!isFiniteInteger(candidate.refreshEpoch) ||
|
|
113
|
+
typeof candidate.tenantId !== "string" ||
|
|
114
|
+
!candidate.tenantId ||
|
|
115
|
+
typeof candidate.providerId !== "string" ||
|
|
116
|
+
!candidate.providerId ||
|
|
117
|
+
typeof candidate.flowId !== "string" ||
|
|
118
|
+
!candidate.flowId ||
|
|
119
|
+
!isFiniteInteger(candidate.mintedAtMs) ||
|
|
120
|
+
!isFiniteInteger(candidate.expiresAtMs) ||
|
|
121
|
+
candidate.expiresAtMs <= candidate.mintedAtMs ||
|
|
122
|
+
!isPositiveFinite(candidate.lifetimeMinutes)) {
|
|
123
|
+
return invalidLease();
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
const proxy = new URL(candidate.proxyUrl);
|
|
127
|
+
if (proxy.protocol !== "http:" && proxy.protocol !== "socks5:")
|
|
128
|
+
return invalidLease();
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return invalidLease();
|
|
132
|
+
}
|
|
133
|
+
return candidate;
|
|
134
|
+
}
|
|
135
|
+
function bindingOf(payload) {
|
|
136
|
+
return {
|
|
137
|
+
vendor: payload.vendor,
|
|
138
|
+
proxyUrl: payload.proxyUrl,
|
|
139
|
+
poolIndex: payload.poolIndex,
|
|
140
|
+
affinityKey: payload.affinityKey,
|
|
141
|
+
refreshEpoch: payload.refreshEpoch,
|
|
142
|
+
lifetimeMinutes: payload.lifetimeMinutes,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
export function createCeremonyEgressLeaseRuntime(options) {
|
|
146
|
+
const environment = options.environment ?? process.env;
|
|
147
|
+
const now = options.now ?? Date.now;
|
|
148
|
+
if (typeof options.tenantId !== "string" || !options.tenantId.trim()) {
|
|
149
|
+
throw new SDKError("A tenant is required for an engine ceremony egress lease", {
|
|
150
|
+
code: "EGRESS_LEASE_INVALID",
|
|
151
|
+
fix: "Restart the authentication ceremony with its tenant identity.",
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
const scope = {
|
|
155
|
+
tenantId: options.tenantId,
|
|
156
|
+
providerId: options.providerId,
|
|
157
|
+
flowId: options.flowId,
|
|
158
|
+
affinityKey: options.affinityKey,
|
|
159
|
+
};
|
|
160
|
+
const key = readDiagnosticEnv(APIFUSE__ENGINE__CEREMONY_LEASE_KEY, environment)?.trim();
|
|
161
|
+
if (!key) {
|
|
162
|
+
throw new SDKError("The engine ceremony egress lease key is not configured", {
|
|
163
|
+
code: "EGRESS_LEASE_KEY_MISSING",
|
|
164
|
+
fix: `Configure ${APIFUSE__ENGINE__CEREMONY_LEASE_KEY} in the engine host.`,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
// The key is the whole authenticity boundary of a handle (a verified handle's proxyUrl
|
|
168
|
+
// becomes the ceremony egress), so the documented minimum is enforced, not advisory.
|
|
169
|
+
if (Buffer.byteLength(key, "utf8") < LEASE_KEY_MIN_BYTES) {
|
|
170
|
+
throw new SDKError(`The engine ceremony egress lease key is shorter than ${LEASE_KEY_MIN_BYTES} bytes`, {
|
|
171
|
+
code: "EGRESS_LEASE_KEY_WEAK",
|
|
172
|
+
fix: `Set ${APIFUSE__ENGINE__CEREMONY_LEASE_KEY} to at least ${LEASE_KEY_MIN_BYTES} random bytes (for example \`openssl rand -base64 32\`).`,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
let payload;
|
|
176
|
+
let currentHandle;
|
|
177
|
+
let generation = 0;
|
|
178
|
+
const dropBinding = () => {
|
|
179
|
+
if (!payload)
|
|
180
|
+
return false;
|
|
181
|
+
payload = undefined;
|
|
182
|
+
currentHandle = undefined;
|
|
183
|
+
generation += 1;
|
|
184
|
+
return true;
|
|
185
|
+
};
|
|
186
|
+
const dropExpiredBinding = () => payload !== undefined && now() >= payload.expiresAtMs ? dropBinding() : false;
|
|
187
|
+
if (options.handle !== undefined) {
|
|
188
|
+
options.onHandle?.(options.handle);
|
|
189
|
+
// Scope mismatch fails GCM authentication because scope is AAD; there is
|
|
190
|
+
// deliberately no post-decrypt scope comparison fallback.
|
|
191
|
+
payload = decodeHandle(options.handle, key, scope);
|
|
192
|
+
currentHandle = options.handle;
|
|
193
|
+
// An expired but authentic handle is not a fault: the ceremony continues on a
|
|
194
|
+
// freshly selected endpoint and the next turn receives the new handle.
|
|
195
|
+
dropExpiredBinding();
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
get binding() {
|
|
199
|
+
return payload ? bindingOf(payload) : undefined;
|
|
200
|
+
},
|
|
201
|
+
dropExpiredBinding,
|
|
202
|
+
dropBinding,
|
|
203
|
+
get generation() {
|
|
204
|
+
return generation;
|
|
205
|
+
},
|
|
206
|
+
bind(binding) {
|
|
207
|
+
if (payload) {
|
|
208
|
+
// The stealth runtime reuses the bound endpoint while a binding exists and
|
|
209
|
+
// never rebinds it; a second bind is an engine invariant break.
|
|
210
|
+
return bindingFault("The ceremony egress lease is already bound");
|
|
211
|
+
}
|
|
212
|
+
if (binding.affinityKey !== options.affinityKey ||
|
|
213
|
+
!binding.proxyUrl ||
|
|
214
|
+
(binding.vendor !== "smartproxy" && binding.vendor !== "nodemaven") ||
|
|
215
|
+
!isFiniteInteger(binding.poolIndex) ||
|
|
216
|
+
!isPositiveFinite(binding.lifetimeMinutes)) {
|
|
217
|
+
return bindingFault("The selected egress cannot be bound to the ceremony lease");
|
|
218
|
+
}
|
|
219
|
+
const mintedAtMs = now();
|
|
220
|
+
payload = {
|
|
221
|
+
version: HANDLE_VERSION,
|
|
222
|
+
tenantId: scope.tenantId,
|
|
223
|
+
providerId: options.providerId,
|
|
224
|
+
flowId: options.flowId,
|
|
225
|
+
...binding,
|
|
226
|
+
mintedAtMs,
|
|
227
|
+
expiresAtMs: mintedAtMs + binding.lifetimeMinutes * 60_000,
|
|
228
|
+
};
|
|
229
|
+
currentHandle = encodeHandle(payload, key, scope);
|
|
230
|
+
options.onHandle?.(currentHandle);
|
|
231
|
+
},
|
|
232
|
+
handle() {
|
|
233
|
+
return currentHandle;
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
}
|
package/dist/runtime/env.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readDiagnosticEnv } from "./diagnostic-env.js";
|
|
1
2
|
import { isEngineOwnedEnvName } from "../engine.js";
|
|
2
3
|
/**
|
|
3
4
|
* A mixed-case spelling of an engine-owned name is an alias: on Windows it resolves to the
|
|
@@ -16,7 +17,7 @@ export function createEnvContext(allowedKeys) {
|
|
|
16
17
|
if (isEngineOwnedEnvAlias(key)) {
|
|
17
18
|
return undefined;
|
|
18
19
|
}
|
|
19
|
-
return
|
|
20
|
+
return readDiagnosticEnv(key);
|
|
20
21
|
},
|
|
21
22
|
};
|
|
22
23
|
}
|
package/dist/runtime/http.js
CHANGED
|
@@ -498,6 +498,7 @@ async function resolveNativeProxy(options, clientOptions, warn, proxyAttemptOffs
|
|
|
498
498
|
// supported here, so a socks5 policy fails loudly rather than downgrading.
|
|
499
499
|
transportProtocols: ["http"],
|
|
500
500
|
telemetry: clientOptions.telemetry,
|
|
501
|
+
engineCredentials: clientOptions.engineCredentials,
|
|
501
502
|
});
|
|
502
503
|
if (resolvedProxy.shouldWarn) {
|
|
503
504
|
warn("[provider-sdk] Provider requested proxy routing, but no proxy URL was configured. Continuing without proxy.");
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerDiagnosticSensitiveValues } from "./diagnostic-redactor.js";
|
|
1
2
|
import { readableBytes, readableLines, readableTextChunks } from "../stream.js";
|
|
2
3
|
import { parseHttpRequestInvocation, isSensitiveKey, redactSensitiveError, redactSensitiveText, redactUrlQueryParams, requestOptionsFromHttpInvocation, serializeRequestUrl, } from "./request-options.js";
|
|
3
4
|
import { createTraceContext, getTraceRecorder, } from "./trace.js";
|
|
@@ -381,6 +382,7 @@ function wrapStealthRedirects(redirects, trace) {
|
|
|
381
382
|
run(...args) {
|
|
382
383
|
const diagnosticArgs = [args[0].url, args[0]];
|
|
383
384
|
const diagnostics = snapshotRequestDiagnostics("stealth", "fetch", diagnosticArgs);
|
|
385
|
+
registerDiagnosticSensitiveValues(trace, diagnostics.sensitiveValues);
|
|
384
386
|
let result;
|
|
385
387
|
try {
|
|
386
388
|
result = redirects.run(...args);
|
|
@@ -511,6 +513,7 @@ function wrapNamespace(namespace, target, trace, shouldInstrument) {
|
|
|
511
513
|
return Reflect.apply(value, namespaceTarget, args);
|
|
512
514
|
}
|
|
513
515
|
const requestDiagnostics = snapshotRequestDiagnostics(namespace, methodName, args);
|
|
516
|
+
registerDiagnosticSensitiveValues(trace, requestDiagnostics.sensitiveValues);
|
|
514
517
|
// Invoke first and decide by the RETURN VALUE. `runSpan` always
|
|
515
518
|
// returns a Promise, so unconditionally span-wrapping every member
|
|
516
519
|
// silently rewrote synchronous contracts: `ctx.state.namespace()`
|
package/dist/runtime/ocr.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readDiagnosticEnv } from "./diagnostic-env.js";
|
|
1
2
|
import { ProviderError, TransportError } from "../errors.js";
|
|
2
3
|
import { CLOUDFLARE_ACCOUNT_ID_ENV } from "./stt.js";
|
|
3
4
|
import { createTimeoutController, isTimeoutLikeError } from "./timeout.js";
|
|
@@ -55,7 +56,7 @@ export function createUnsupportedOcrClient(reason) {
|
|
|
55
56
|
});
|
|
56
57
|
}
|
|
57
58
|
function normalizedEnvValue(env, key) {
|
|
58
|
-
const value = env
|
|
59
|
+
const value = readDiagnosticEnv(key, env)?.trim();
|
|
59
60
|
return value ? value : undefined;
|
|
60
61
|
}
|
|
61
62
|
export function createOcrClientFromEnv(config, env = process.env) {
|
package/dist/runtime/otlp.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readDiagnosticEnv } from "./diagnostic-env.js";
|
|
1
2
|
import { AsyncResource } from "node:async_hooks";
|
|
2
3
|
export const OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT";
|
|
3
4
|
export const OTEL_EXPORTER_OTLP_ENDPOINT = "OTEL_EXPORTER_OTLP_ENDPOINT";
|
|
@@ -118,12 +119,12 @@ export function resolveOTLPExportOptions(explicit, env = process.env) {
|
|
|
118
119
|
{ source: EXPLICIT_ENDPOINT_SOURCE, value: explicit.endpoint, appendPath: false },
|
|
119
120
|
{
|
|
120
121
|
source: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
|
|
121
|
-
value:
|
|
122
|
+
value: readDiagnosticEnv(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, env),
|
|
122
123
|
appendPath: false,
|
|
123
124
|
},
|
|
124
125
|
{
|
|
125
126
|
source: OTEL_EXPORTER_OTLP_ENDPOINT,
|
|
126
|
-
value:
|
|
127
|
+
value: readDiagnosticEnv(OTEL_EXPORTER_OTLP_ENDPOINT, env),
|
|
127
128
|
appendPath: true,
|
|
128
129
|
},
|
|
129
130
|
];
|
|
@@ -137,9 +138,9 @@ export function resolveOTLPExportOptions(explicit, env = process.env) {
|
|
|
137
138
|
return { status: "invalid", source: candidate.source, reason: parsed.reason };
|
|
138
139
|
}
|
|
139
140
|
const endpoint = candidate.appendPath ? appendTracesPath(parsed.url) : candidate.value;
|
|
140
|
-
const tracesHeaders = presentValue(env
|
|
141
|
+
const tracesHeaders = presentValue(readDiagnosticEnv(OTEL_EXPORTER_OTLP_TRACES_HEADERS, env));
|
|
141
142
|
const headersSource = tracesHeaders !== undefined ? OTEL_EXPORTER_OTLP_TRACES_HEADERS : OTEL_EXPORTER_OTLP_HEADERS;
|
|
142
|
-
const envHeaders = parseHeaderList(tracesHeaders ?? presentValue(env
|
|
143
|
+
const envHeaders = parseHeaderList(tracesHeaders ?? presentValue(readDiagnosticEnv(OTEL_EXPORTER_OTLP_HEADERS, env)));
|
|
143
144
|
if (!headersAreSendable(envHeaders)) {
|
|
144
145
|
return { status: "invalid", source: headersSource, reason: INVALID_HEADERS };
|
|
145
146
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readDiagnosticEnv } from "./diagnostic-env.js";
|
|
1
2
|
import { createHash, randomBytes } from "node:crypto";
|
|
2
3
|
export const NODEMAVEN_USERNAME_ENV = "APIFUSE__PROXY__NODEMAVEN_USERNAME";
|
|
3
4
|
export const NODEMAVEN_PASSWORD_ENV = "APIFUSE__PROXY__NODEMAVEN_PASSWORD";
|
|
@@ -25,10 +26,10 @@ export function hasNodemavenCredentials() {
|
|
|
25
26
|
return Boolean(readNodemavenUsername() && readNodemavenPassword());
|
|
26
27
|
}
|
|
27
28
|
function readNodemavenUsername() {
|
|
28
|
-
return
|
|
29
|
+
return readDiagnosticEnv(NODEMAVEN_USERNAME_ENV)?.trim() || undefined;
|
|
29
30
|
}
|
|
30
31
|
function readNodemavenPassword() {
|
|
31
|
-
return
|
|
32
|
+
return readDiagnosticEnv(NODEMAVEN_PASSWORD_ENV)?.trim() || undefined;
|
|
32
33
|
}
|
|
33
34
|
function resolveNodemavenFilter(value) {
|
|
34
35
|
const raw = value?.trim().toLowerCase();
|
|
@@ -21,7 +21,10 @@ export declare function normalizeProxyTransportRetryOptions(retry: RequestOption
|
|
|
21
21
|
label?: RetryPolicyLabel;
|
|
22
22
|
}): NormalizedProxyTransportRetryOptions | undefined;
|
|
23
23
|
export declare function validateUnsafeProxyTransportRetryMethods(options: NormalizedProxyTransportRetryOptions, label?: RetryPolicyLabel): void;
|
|
24
|
-
export declare function isProxyTransportRetryMethod(method: HttpMethod | string, options: NormalizedProxyTransportRetryOptions
|
|
24
|
+
export declare function isProxyTransportRetryMethod(method: HttpMethod | string, options: NormalizedProxyTransportRetryOptions, request?: {
|
|
25
|
+
readonly body?: unknown;
|
|
26
|
+
readonly headers?: Readonly<Record<string, string | readonly string[] | undefined>>;
|
|
27
|
+
}): boolean;
|
|
25
28
|
export declare function shouldRetryProxyTransportError(error: unknown, options: NormalizedProxyTransportRetryOptions): boolean;
|
|
26
29
|
export declare function shouldRetryProxyTransportAttempt(input: {
|
|
27
30
|
error: unknown;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ProviderError, TransportError } from "../errors.js";
|
|
2
2
|
import { HttpRetryAfterPolicy, HttpRetryDelayStrategy, HttpRetryJitter, HttpRetryPreset, HttpRetryUnsafeMethodPolicy, } from "../types.js";
|
|
3
|
+
import { isSensitiveKey } from "./request-options.js";
|
|
3
4
|
export const DEFAULT_PROXY_TRANSPORT_RETRY_METHODS = ["GET", "HEAD", "OPTIONS"];
|
|
4
5
|
export const DEFAULT_PROXY_TRANSPORT_RETRY_ERROR_CODES = [
|
|
5
6
|
"transport_network_error",
|
|
@@ -250,10 +251,18 @@ export function validateUnsafeProxyTransportRetryMethods(options, label = "Proxy
|
|
|
250
251
|
return;
|
|
251
252
|
throw new ProviderError(`${label} retry methods include unsafe method(s): ${unsafeMethods.join(", ")}`, { code: "retry_unsafe_method" });
|
|
252
253
|
}
|
|
253
|
-
export function isProxyTransportRetryMethod(method, options) {
|
|
254
|
-
|
|
254
|
+
export function isProxyTransportRetryMethod(method, options, request) {
|
|
255
|
+
const methodAllowed = options.methods
|
|
255
256
|
.map((allowedMethod) => allowedMethod.toUpperCase())
|
|
256
257
|
.includes(method.toUpperCase());
|
|
258
|
+
if (!methodAllowed || request === undefined)
|
|
259
|
+
return methodAllowed;
|
|
260
|
+
if (request.body !== undefined)
|
|
261
|
+
return false;
|
|
262
|
+
return !Object.keys(request.headers ?? {}).some((name) => {
|
|
263
|
+
const normalized = name.toLowerCase();
|
|
264
|
+
return normalized === "cookie" || normalized === "proxy-authorization" || isSensitiveKey(name);
|
|
265
|
+
});
|
|
257
266
|
}
|
|
258
267
|
export function shouldRetryProxyTransportError(error, options) {
|
|
259
268
|
const code = retryErrorCode(error);
|
|
@@ -51,6 +51,13 @@ export declare function redactUrlQueryParams(url: string, sensitiveParamNames: r
|
|
|
51
51
|
* separate, structurally redacted URL for diagnostics and observability.
|
|
52
52
|
*/
|
|
53
53
|
export declare function serializeRequestUrl(url: string, params?: RequestParams, sensitiveParams?: Record<string, string>): SerializedRequestUrl;
|
|
54
|
+
export declare function normalizePercentEscapes(value: string): string;
|
|
55
|
+
export type SensitiveValueVariant = {
|
|
56
|
+
value: string;
|
|
57
|
+
requiresTokenBoundary: boolean;
|
|
58
|
+
caseInsensitive: boolean;
|
|
59
|
+
};
|
|
60
|
+
export declare function allSensitiveValueVariants(sensitiveValues: readonly string[]): SensitiveValueVariant[];
|
|
54
61
|
/** Scrubs raw and URL-encoded secret values from diagnostic text. */
|
|
55
62
|
export declare function redactSensitiveText(text: string, sensitiveValues: readonly string[], requestUrl?: string, redactedUrl?: string): string;
|
|
56
63
|
/**
|
|
@@ -108,13 +108,33 @@ export function serializeRequestUrl(url, params, sensitiveParams) {
|
|
|
108
108
|
sensitiveValues: [...sensitiveValues],
|
|
109
109
|
};
|
|
110
110
|
}
|
|
111
|
-
const MIN_UNSCOPED_SENSITIVE_VALUE_LENGTH =
|
|
112
|
-
function normalizePercentEscapes(value) {
|
|
111
|
+
const MIN_UNSCOPED_SENSITIVE_VALUE_LENGTH = 8;
|
|
112
|
+
export function normalizePercentEscapes(value) {
|
|
113
113
|
return value.replace(/%[\da-f]{2}/gi, (percentEscape) => percentEscape.toUpperCase());
|
|
114
114
|
}
|
|
115
115
|
function sensitiveValueVariants(value) {
|
|
116
116
|
if (!value || value === REDACTED_QUERY_VALUE)
|
|
117
117
|
return [];
|
|
118
|
+
// Owner-visible policy: short credentials are literal, case-sensitive tokens only.
|
|
119
|
+
// Encoded short needles collide with benign cursors and ordinary diagnostic text.
|
|
120
|
+
if (value.length < 8)
|
|
121
|
+
return [value];
|
|
122
|
+
// These ASCII characters are identical under NFC, JSON, URI-component and
|
|
123
|
+
// form encoding. Avoid constructing those identical variants for common tokens.
|
|
124
|
+
if (/^[A-Za-z0-9._*-]+$/.test(value)) {
|
|
125
|
+
const base64 = Buffer.from(value, "utf8").toString("base64");
|
|
126
|
+
const base64Url = base64.replaceAll("+", "-").replaceAll("/", "_");
|
|
127
|
+
return [
|
|
128
|
+
...new Set([
|
|
129
|
+
value,
|
|
130
|
+
base64,
|
|
131
|
+
base64.replace(/=+$/, ""),
|
|
132
|
+
base64Url,
|
|
133
|
+
base64Url.replace(/=+$/, ""),
|
|
134
|
+
]),
|
|
135
|
+
];
|
|
136
|
+
}
|
|
137
|
+
const normalized = value.normalize("NFC");
|
|
118
138
|
const formEncoded = new URLSearchParams({ value }).toString().slice("value=".length);
|
|
119
139
|
let componentEncoded;
|
|
120
140
|
try {
|
|
@@ -125,12 +145,31 @@ function sensitiveValueVariants(value) {
|
|
|
125
145
|
// still safe to scrub, and URLSearchParams supplies its well-formed form.
|
|
126
146
|
componentEncoded = value;
|
|
127
147
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
148
|
+
const base64 = Buffer.from(value, "utf8").toString("base64");
|
|
149
|
+
const base64Url = base64.replaceAll("+", "-").replaceAll("/", "_");
|
|
150
|
+
const jsonEscaped = JSON.stringify(value).slice(1, -1);
|
|
151
|
+
return [
|
|
152
|
+
...new Set([
|
|
153
|
+
value,
|
|
154
|
+
normalized,
|
|
155
|
+
componentEncoded,
|
|
156
|
+
formEncoded,
|
|
157
|
+
jsonEscaped,
|
|
158
|
+
base64,
|
|
159
|
+
base64.replace(/=+$/, ""),
|
|
160
|
+
base64Url,
|
|
161
|
+
base64Url.replace(/=+$/, ""),
|
|
162
|
+
].map(normalizePercentEscapes)),
|
|
163
|
+
];
|
|
164
|
+
}
|
|
165
|
+
export function allSensitiveValueVariants(sensitiveValues) {
|
|
131
166
|
const variants = new Map();
|
|
132
167
|
for (const sensitiveValue of sensitiveValues) {
|
|
133
168
|
const requiresTokenBoundary = sensitiveValue.length < MIN_UNSCOPED_SENSITIVE_VALUE_LENGTH;
|
|
169
|
+
// Long values are unlikely to be ordinary prose, so case folding is worth
|
|
170
|
+
// the extra over-redaction protection. Values of 4-7 characters remain
|
|
171
|
+
// case-sensitive because they commonly occur as words and identifiers.
|
|
172
|
+
const caseInsensitive = sensitiveValue.length >= 8;
|
|
134
173
|
for (const value of sensitiveValueVariants(sensitiveValue)) {
|
|
135
174
|
const existing = variants.get(value);
|
|
136
175
|
variants.set(value, {
|
|
@@ -138,6 +177,7 @@ function allSensitiveValueVariants(sensitiveValues) {
|
|
|
138
177
|
requiresTokenBoundary: existing
|
|
139
178
|
? existing.requiresTokenBoundary && requiresTokenBoundary
|
|
140
179
|
: requiresTokenBoundary,
|
|
180
|
+
caseInsensitive: existing ? existing.caseInsensitive || caseInsensitive : caseInsensitive,
|
|
141
181
|
});
|
|
142
182
|
}
|
|
143
183
|
}
|
|
@@ -147,24 +187,24 @@ function isTokenCharacter(value) {
|
|
|
147
187
|
return value !== undefined && /[\p{L}\p{N}]/u.test(value);
|
|
148
188
|
}
|
|
149
189
|
function replaceSensitiveVariant(text, variant) {
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
return `\\${match}`;
|
|
159
|
-
});
|
|
160
|
-
return text.replace(new RegExp(pattern, "gu"), (match, offset, source) => {
|
|
161
|
-
const end = offset + match.length;
|
|
190
|
+
const normalized = normalizePercentEscapes(text);
|
|
191
|
+
const comparable = variant.caseInsensitive ? normalized.toLowerCase() : text;
|
|
192
|
+
const needle = variant.caseInsensitive ? variant.value.toLowerCase() : variant.value;
|
|
193
|
+
let cursor = 0;
|
|
194
|
+
let output = "";
|
|
195
|
+
for (let index = comparable.indexOf(needle); index !== -1; index = comparable.indexOf(needle, index + 1)) {
|
|
196
|
+
const end = index + needle.length;
|
|
162
197
|
if (variant.requiresTokenBoundary &&
|
|
163
|
-
(isTokenCharacter(
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
198
|
+
(isTokenCharacter(comparable[index - 1]) || isTokenCharacter(comparable[end])))
|
|
199
|
+
continue;
|
|
200
|
+
// Unicode folding can change offsets; suppress the field instead of slicing a secret.
|
|
201
|
+
if (comparable.length !== text.length)
|
|
202
|
+
return REDACTED_QUERY_VALUE;
|
|
203
|
+
output += text.slice(cursor, index) + REDACTED_QUERY_VALUE;
|
|
204
|
+
cursor = end;
|
|
205
|
+
index = end - 1;
|
|
206
|
+
}
|
|
207
|
+
return cursor ? output + text.slice(cursor) : text;
|
|
168
208
|
}
|
|
169
209
|
/** Scrubs raw and URL-encoded secret values from diagnostic text. */
|
|
170
210
|
export function redactSensitiveText(text, sensitiveValues, requestUrl, redactedUrl) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ProxyTelemetrySink } from "../config/loader.js";
|
|
2
2
|
import type { ProxyTelemetryLogPayload } from "./proxy-telemetry.js";
|
|
3
|
+
import type { ResolverTelemetryLogPayload } from "./resolver-telemetry.js";
|
|
3
4
|
import type { Span, TraceContext } from "./trace.js";
|
|
4
5
|
export declare const PROVIDER_TELEMETRY_HEADER = "X-ApiFuse-Provider-Telemetry";
|
|
5
6
|
export type TelemetryKey = "proxy" | "resolver" | "http" | "stealth" | "native" | "browser" | "ocr" | "stt" | "cache" | "state" | "events";
|
|
@@ -21,17 +22,13 @@ export declare function tenantOpaqueKeys(value: string[]): TenantOpaqueKeys;
|
|
|
21
22
|
* URLs, hostnames, free text, unknown values, and escape-hatch properties reduce to `never`.
|
|
22
23
|
*/
|
|
23
24
|
export type GatewayIngestible<T> = 0 extends 1 & T ? never : T extends object ? keyof T extends never ? never : {
|
|
24
|
-
[K in keyof T]: 0 extends 1 & T[K] ? never : unknown extends T[K] ? never : [NonNullable<T[K]>] extends [number | boolean | ClosedEnum<string>] ? T[K] : [NonNullable<T[K]>] extends [readonly (infer U)[]] ? 0 extends 1 & U ? never : unknown extends U ? never : [U] extends [
|
|
25
|
-
number | boolean | ClosedEnum<string> | GatewayIngestible<U>
|
|
26
|
-
] ? T[K] : never : [NonNullable<T[K]>] extends [object] ? [NonNullable<T[K]>] extends [GatewayIngestible<NonNullable<T[K]>>] ? T[K] : never : never;
|
|
25
|
+
[K in keyof T]: 0 extends 1 & T[K] ? never : unknown extends T[K] ? never : [NonNullable<T[K]>] extends [number | boolean | ClosedEnum<string>] ? T[K] : [NonNullable<T[K]>] extends [readonly (infer U)[]] ? 0 extends 1 & U ? never : unknown extends U ? never : [U] extends [number | boolean | ClosedEnum<string> | GatewayIngestible<U>] ? T[K] : never : [NonNullable<T[K]>] extends [object] ? [NonNullable<T[K]>] extends [GatewayIngestible<NonNullable<T[K]>>] ? T[K] : never : never;
|
|
27
26
|
} : never;
|
|
28
27
|
/** Compile-time projection for tenant-visible, identity-neutral metadata. */
|
|
29
28
|
export type TenantNeutral<T> = 0 extends 1 & T ? never : T extends object ? {
|
|
30
|
-
[K in keyof T]: K extends `vendor${string}` | "provider" | "engine" | "model" | `${string}Host` ? never : 0 extends 1 & T[K] ? never : unknown extends T[K] ? never : [NonNullable<T[K]>] extends [
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
] ? K extends "keys" ? T[K] : never : [NonNullable<T[K]>] extends [number | boolean | ClosedEnum<string>] ? T[K] : [NonNullable<T[K]>] extends [object] ? [NonNullable<T[K]>] extends [TenantNeutral<NonNullable<T[K]>>] ? T[K] : never : never;
|
|
29
|
+
[K in keyof T]: K extends `vendor${string}` | "provider" | "engine" | "model" | `${string}Host` ? never : 0 extends 1 & T[K] ? never : unknown extends T[K] ? never : [NonNullable<T[K]>] extends [string[] & {
|
|
30
|
+
readonly __tenantOpaqueCacheKeys: true;
|
|
31
|
+
}] ? K extends "keys" ? T[K] : never : [NonNullable<T[K]>] extends [number | boolean | ClosedEnum<string>] ? T[K] : [NonNullable<T[K]>] extends [object] ? [NonNullable<T[K]>] extends [TenantNeutral<NonNullable<T[K]>>] ? T[K] : never : never;
|
|
35
32
|
} : never;
|
|
36
33
|
export interface SpanIndex {
|
|
37
34
|
readonly spans: readonly Span[];
|
|
@@ -46,7 +43,8 @@ export interface TelemetryContributor<Log extends object, Header extends object>
|
|
|
46
43
|
}
|
|
47
44
|
export type RequestTelemetryLogPayload = {
|
|
48
45
|
proxy?: ProxyTelemetryLogPayload;
|
|
49
|
-
|
|
46
|
+
resolver?: ResolverTelemetryLogPayload;
|
|
47
|
+
} & Partial<Record<Exclude<TelemetryKey, "proxy" | "resolver">, object>>;
|
|
50
48
|
/**
|
|
51
49
|
* Bounded structural defence in depth against casts and accidental free text.
|
|
52
50
|
* Closed-enum brands are erased at runtime; the type-level guard is the contract,
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
+
import type { ProviderChallengeKind } from "../types.js";
|
|
1
2
|
export declare const APIFUSE__RESOLVER__2CAPTCHA__API_KEY = "APIFUSE__RESOLVER__2CAPTCHA__API_KEY";
|
|
2
3
|
export declare const APIFUSE__RESOLVER__CAPSOLVER__API_KEY = "APIFUSE__RESOLVER__CAPSOLVER__API_KEY";
|
|
3
4
|
export declare const APIFUSE__RESOLVER__CAPMONSTER__API_KEY = "APIFUSE__RESOLVER__CAPMONSTER__API_KEY";
|
|
5
|
+
export declare const APIFUSE__RESOLVER__HYPERSOLUTIONS__API_KEY = "APIFUSE__RESOLVER__HYPERSOLUTIONS__API_KEY";
|
|
4
6
|
export declare const APIFUSE__RESOLVER__TIMEOUT_MS = "APIFUSE__RESOLVER__TIMEOUT_MS";
|
|
5
7
|
export declare const APIFUSE__CDP_POOL__URL = "APIFUSE__CDP_POOL__URL";
|
|
6
8
|
export declare const DEFAULT_RESOLVER_TIMEOUT_MS = 180000;
|
|
9
|
+
/**
|
|
10
|
+
* SBSD is solved on the provider's own admitted session, so the transport profile must be
|
|
11
|
+
* declared up front. Other kinds, including `akamai_sensor`, keep the profile optional.
|
|
12
|
+
*/
|
|
13
|
+
export declare function kindRequiresClientProfile(kind: ProviderChallengeKind): boolean;
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
export const APIFUSE__RESOLVER__2CAPTCHA__API_KEY = "APIFUSE__RESOLVER__2CAPTCHA__API_KEY";
|
|
2
2
|
export const APIFUSE__RESOLVER__CAPSOLVER__API_KEY = "APIFUSE__RESOLVER__CAPSOLVER__API_KEY";
|
|
3
3
|
export const APIFUSE__RESOLVER__CAPMONSTER__API_KEY = "APIFUSE__RESOLVER__CAPMONSTER__API_KEY";
|
|
4
|
+
export const APIFUSE__RESOLVER__HYPERSOLUTIONS__API_KEY = "APIFUSE__RESOLVER__HYPERSOLUTIONS__API_KEY";
|
|
4
5
|
export const APIFUSE__RESOLVER__TIMEOUT_MS = "APIFUSE__RESOLVER__TIMEOUT_MS";
|
|
5
6
|
export const APIFUSE__CDP_POOL__URL = "APIFUSE__CDP_POOL__URL";
|
|
6
7
|
export const DEFAULT_RESOLVER_TIMEOUT_MS = 180_000;
|
|
8
|
+
/**
|
|
9
|
+
* SBSD is solved on the provider's own admitted session, so the transport profile must be
|
|
10
|
+
* declared up front. Other kinds, including `akamai_sensor`, keep the profile optional.
|
|
11
|
+
*/
|
|
12
|
+
export function kindRequiresClientProfile(kind) {
|
|
13
|
+
return kind === "akamai_sbsd";
|
|
14
|
+
}
|
|
@@ -1 +1,3 @@
|
|
|
1
|
-
export { APIFUSE__CDP_POOL__URL, APIFUSE__RESOLVER__2CAPTCHA__API_KEY, APIFUSE__RESOLVER__CAPMONSTER__API_KEY, APIFUSE__RESOLVER__CAPSOLVER__API_KEY, APIFUSE__RESOLVER__TIMEOUT_MS, bindResolverSignal, createResolverClient, createResolverClientFromEnv, createUnsupportedResolverClient, DEFAULT_RESOLVER_VENDOR_PREFERENCE, DEFAULT_RESOLVER_TIMEOUT_MS, getResolverSolutionSource, invalidateCachedResolverSolution, invalidateResolverSolution, RESOLVER_ADAPTER_REGISTRY, RESOLVER_INSTRUMENTATION_METADATA, resolveProviderResolverVendors, type ResolverAdapterFactory, type ResolverInstrumentationMetadata, type ResolverRuntimeOptions, type ResolverSolutionSource, } from "./resolver.js";
|
|
1
|
+
export { APIFUSE__CDP_POOL__URL, APIFUSE__RESOLVER__2CAPTCHA__API_KEY, APIFUSE__RESOLVER__CAPMONSTER__API_KEY, APIFUSE__RESOLVER__CAPSOLVER__API_KEY, APIFUSE__RESOLVER__HYPERSOLUTIONS__API_KEY, APIFUSE__RESOLVER__TIMEOUT_MS, bindResolverSignal, createResolverClient, createResolverClientFromEnv, createUnsupportedResolverClient, DEFAULT_RESOLVER_VENDOR_PREFERENCE, DEFAULT_RESOLVER_TIMEOUT_MS, getResolverSolutionSource, invalidateCachedResolverSolution, invalidateResolverSolution, RESOLVER_ADAPTER_REGISTRY, RESOLVER_INSTRUMENTATION_METADATA, resolveProviderResolverVendors, type ResolverAdapterFactory, type ResolverInstrumentationMetadata, type ResolverRuntimeOptions, type ResolverSolutionSource, } from "./resolver.js";
|
|
2
|
+
export { type ResolverCacheReadTelemetryEvent, type ResolverCacheWriteTelemetryEvent, type ResolverFailoverTelemetryEvent, type ResolverIdentityTelemetryEvent, type ResolverOutcomeTelemetryEvent, type ResolverTelemetryAttemptOutcome, type ResolverTelemetryCacheStatus, type ResolverTelemetryCacheWriteReason, type ResolverTelemetryErrorClass, type ResolverTelemetryIdentitySource, type ResolverTelemetryOutcome, type ResolverTelemetryPhase, type ResolverTelemetrySink, type ResolverVendorAttemptTelemetryEvent, } from "./resolver-telemetry.js";
|
|
3
|
+
export type { ResolverPaidUsageContext, ResolverVendorUnavailableReason, } from "./resolver-vendors/types.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { APIFUSE__CDP_POOL__URL, APIFUSE__RESOLVER__2CAPTCHA__API_KEY, APIFUSE__RESOLVER__CAPMONSTER__API_KEY, APIFUSE__RESOLVER__CAPSOLVER__API_KEY, APIFUSE__RESOLVER__TIMEOUT_MS, bindResolverSignal, createResolverClient, createResolverClientFromEnv, createUnsupportedResolverClient, DEFAULT_RESOLVER_VENDOR_PREFERENCE, DEFAULT_RESOLVER_TIMEOUT_MS, getResolverSolutionSource, invalidateCachedResolverSolution, invalidateResolverSolution, RESOLVER_ADAPTER_REGISTRY, RESOLVER_INSTRUMENTATION_METADATA, resolveProviderResolverVendors, } from "./resolver.js";
|
|
1
|
+
export { APIFUSE__CDP_POOL__URL, APIFUSE__RESOLVER__2CAPTCHA__API_KEY, APIFUSE__RESOLVER__CAPMONSTER__API_KEY, APIFUSE__RESOLVER__CAPSOLVER__API_KEY, APIFUSE__RESOLVER__HYPERSOLUTIONS__API_KEY, APIFUSE__RESOLVER__TIMEOUT_MS, bindResolverSignal, createResolverClient, createResolverClientFromEnv, createUnsupportedResolverClient, DEFAULT_RESOLVER_VENDOR_PREFERENCE, DEFAULT_RESOLVER_TIMEOUT_MS, getResolverSolutionSource, invalidateCachedResolverSolution, invalidateResolverSolution, RESOLVER_ADAPTER_REGISTRY, RESOLVER_INSTRUMENTATION_METADATA, resolveProviderResolverVendors, } from "./resolver.js";
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import type { ChallengeSolution, ProviderChallenge, ResolverContext } from "../types.js";
|
|
2
2
|
import type { TraceRecorder } from "./trace.js";
|
|
3
|
+
import type { ResolverTelemetrySink } from "./resolver-telemetry.js";
|
|
4
|
+
type ResolverTelemetryBinding = (sink: ResolverTelemetrySink) => ResolverContext;
|
|
5
|
+
/** Internal contract: reconstruct a chain with a new request sink, without mutating the host chain. */
|
|
6
|
+
export declare function registerResolverTelemetryBinding(resolver: ResolverContext, bind: ResolverTelemetryBinding): void;
|
|
7
|
+
/** Preserve reconstruction across SDK wrappers without capturing their current request sink. */
|
|
8
|
+
export declare function inheritResolverTelemetryBinding(resolver: ResolverContext, wrapper: ResolverContext): void;
|
|
9
|
+
/** SDK chains expose each vendor invocation; opaque host resolvers expose one custom invocation. */
|
|
10
|
+
export declare function bindResolverTelemetry(resolver: ResolverContext, sink: ResolverTelemetrySink | undefined): ResolverContext;
|
|
3
11
|
export declare const RESOLVER_INSTRUMENTATION_METADATA: unique symbol;
|
|
4
12
|
/**
|
|
5
13
|
* Internal solve surface. The instrumentation layer threads its recorder as a
|
|
@@ -12,3 +20,4 @@ export type ResolverSolveWithRecorder = {
|
|
|
12
20
|
solve(challenge: ProviderChallenge, signal?: AbortSignal, traceRecorder?: TraceRecorder): Promise<ChallengeSolution>;
|
|
13
21
|
};
|
|
14
22
|
export declare function createUnsupportedResolverClient(reason?: string): ResolverContext;
|
|
23
|
+
export {};
|