@apifuse/provider-sdk 2.1.0-beta.9 → 2.2.0-beta.10
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 +240 -0
- package/CHANGELOG.md +93 -0
- package/README.md +26 -10
- package/SUBMISSION.md +11 -12
- 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 +36 -0
- package/bin/apifuse-pack-smoke.ts +22 -81
- package/bin/apifuse-pack-types.ts +266 -0
- package/bin/apifuse-perf.ts +45 -127
- package/bin/apifuse-record.ts +53 -70
- package/bin/apifuse-submit-check.ts +2177 -353
- 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/auth-turn.v1.schema.json +89 -0
- package/dist/auth-turn/fixtures/invalid/empty-kind.json +4 -0
- package/dist/auth-turn/fixtures/invalid/expires-at-not-string.json +5 -0
- package/dist/auth-turn/fixtures/invalid/missing-kind.json +3 -0
- package/dist/auth-turn/fixtures/invalid/missing-turn-id.json +3 -0
- package/dist/auth-turn/fixtures/invalid/timing-unknown-field.json +7 -0
- package/dist/auth-turn/fixtures/invalid/turn-id-snake-case.json +4 -0
- package/dist/auth-turn/fixtures/invalid/unknown-top-level-field.json +5 -0
- package/dist/auth-turn/fixtures/valid/abort.json +8 -0
- package/dist/auth-turn/fixtures/valid/challenge.json +17 -0
- package/dist/auth-turn/fixtures/valid/complete.json +13 -0
- package/dist/auth-turn/fixtures/valid/form.json +14 -0
- package/dist/auth-turn/fixtures/valid/message.json +13 -0
- package/dist/auth-turn/fixtures/valid/multi_choice.json +15 -0
- package/dist/auth-turn/fixtures/valid/pending.json +5 -0
- package/dist/auth-turn/fixtures/valid/poll.json +9 -0
- package/dist/auth-turn/fixtures/valid/redirect.json +16 -0
- package/dist/auth-turn/fixtures/valid/retry.json +8 -0
- package/dist/auth-turn/fixtures/valid/unknown-kind.json +7 -0
- package/dist/auth-turn/index.d.ts +195 -0
- package/dist/auth-turn/index.js +133 -0
- package/dist/auth.d.ts +76 -0
- package/dist/auth.js +427 -0
- package/dist/ceremonies/index.d.ts +1 -1
- package/dist/ceremonies/index.js +14 -48
- 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 +47 -33
- package/dist/cli/prompt-assets.d.ts +80 -0
- package/dist/cli/prompt-assets.js +743 -0
- package/dist/cli/templates/provider/.agents/skills/fixtures-and-recording/SKILL.md.tpl +58 -0
- package/dist/cli/templates/provider/.agents/skills/health-checks-and-fail-closed/SKILL.md.tpl +65 -0
- package/dist/cli/templates/provider/.agents/skills/normalization-standards/SKILL.md.tpl +57 -0
- package/dist/cli/templates/provider/.agents/skills/pagination-and-counts/SKILL.md.tpl +52 -0
- package/dist/cli/templates/provider/.agents/skills/upstream-contract-verification/SKILL.md.tpl +45 -0
- package/dist/cli/templates/provider/.agents/skills/upstream-notes/README.md.tpl +13 -0
- package/dist/cli/templates/provider/.dockerignore.tpl +22 -0
- package/dist/cli/templates/provider/.gitignore.tpl +22 -0
- package/dist/cli/templates/provider/AGENTS.md.tpl +96 -0
- package/dist/cli/templates/provider/Dockerfile.tpl +7 -0
- package/dist/cli/templates/provider/README.md.tpl +163 -0
- package/dist/cli/templates/provider/dev.ts.tpl +5 -0
- package/dist/cli/templates/provider/domain/README.md.tpl +3 -0
- package/dist/cli/templates/provider/index.test.ts.tpl +13 -0
- package/dist/cli/templates/provider/index.ts.tpl +15 -0
- package/dist/cli/templates/provider/mappers/README.md.tpl +3 -0
- package/dist/cli/templates/provider/meta.ts.tpl +7 -0
- package/dist/cli/templates/provider/operations/index.ts.tpl +5 -0
- package/dist/cli/templates/provider/operations/ping.ts.tpl +24 -0
- package/dist/cli/templates/provider/schemas/ping.ts.tpl +24 -0
- package/dist/cli/templates/provider/start.ts.tpl +5 -0
- package/dist/cli/templates/provider/upstream/README.md.tpl +3 -0
- package/dist/config/loader.d.ts +149 -8
- package/dist/config/loader.js +378 -89
- 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 +5 -6
- package/dist/define.d.ts +13 -1
- package/dist/define.js +245 -178
- package/dist/dev.d.ts +1 -1
- package/dist/dev.js +1 -1
- package/dist/errors.d.ts +4 -1
- package/dist/errors.js +48 -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 +44 -41
- package/dist/index.js +39 -36
- package/dist/lint.d.ts +1 -0
- package/dist/lint.js +35 -15
- package/dist/provider.d.ts +11 -9
- package/dist/provider.js +9 -8
- 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 +1 -1
- package/dist/runtime/auth-flow.js +4 -2
- package/dist/runtime/browser.d.ts +1 -1
- package/dist/runtime/browser.js +214 -25
- 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 +15 -3
- package/dist/runtime/http.d.ts +2 -2
- package/dist/runtime/http.js +160 -344
- 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 +56 -19
- package/dist/runtime/keyring.js +1 -1
- package/dist/runtime/namespace.js +1 -1
- 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 +35 -0
- package/dist/runtime/proxy-nodemaven.js +128 -0
- package/dist/runtime/proxy-retry-policy.d.ts +40 -0
- package/dist/runtime/proxy-retry-policy.js +326 -0
- package/dist/runtime/proxy-telemetry.d.ts +2 -1
- package/dist/runtime/proxy-telemetry.js +55 -52
- package/dist/runtime/redis.d.ts +1 -1
- package/dist/runtime/redis.js +2 -4
- package/dist/runtime/request-options.d.ts +1 -1
- 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 +15 -4
- package/dist/runtime/stealth.d.ts +7 -4
- package/dist/runtime/stealth.js +257 -215
- 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 -3
- package/dist/server/index.js +6 -2
- package/dist/server/self-test-input-tokens.d.ts +1 -0
- package/dist/server/self-test-input-tokens.js +37 -0
- package/dist/server/self-test-redaction.d.ts +20 -0
- package/dist/server/self-test-redaction.js +70 -0
- package/dist/server/self-test-token.d.ts +30 -0
- package/dist/server/self-test-token.js +50 -0
- package/dist/server/self-test.d.ts +199 -0
- package/dist/server/self-test.js +1113 -0
- package/dist/server/serve.d.ts +14 -3
- package/dist/server/serve.js +135 -64
- package/dist/server/types.d.ts +10 -9
- package/dist/server/types.js +3 -7
- package/dist/stealth/profiles.d.ts +1 -1
- package/dist/stealth/profiles.js +5 -14
- 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 +1 -1
- package/dist/testing/run.js +12 -15
- package/dist/types.d.ts +237 -1
- package/dist/user-input.d.ts +30 -0
- package/dist/user-input.js +66 -0
- package/package.json +16 -5
- package/src/auth-turn/auth-turn.v1.schema.json +89 -0
- package/src/auth-turn/fixtures/invalid/empty-kind.json +4 -0
- package/src/auth-turn/fixtures/invalid/expires-at-not-string.json +5 -0
- package/src/auth-turn/fixtures/invalid/missing-kind.json +3 -0
- package/src/auth-turn/fixtures/invalid/missing-turn-id.json +3 -0
- package/src/auth-turn/fixtures/invalid/timing-unknown-field.json +7 -0
- package/src/auth-turn/fixtures/invalid/turn-id-snake-case.json +4 -0
- package/src/auth-turn/fixtures/invalid/unknown-top-level-field.json +5 -0
- package/src/auth-turn/fixtures/valid/abort.json +8 -0
- package/src/auth-turn/fixtures/valid/challenge.json +17 -0
- package/src/auth-turn/fixtures/valid/complete.json +13 -0
- package/src/auth-turn/fixtures/valid/form.json +14 -0
- package/src/auth-turn/fixtures/valid/message.json +13 -0
- package/src/auth-turn/fixtures/valid/multi_choice.json +15 -0
- package/src/auth-turn/fixtures/valid/pending.json +5 -0
- package/src/auth-turn/fixtures/valid/poll.json +9 -0
- package/src/auth-turn/fixtures/valid/redirect.json +16 -0
- package/src/auth-turn/fixtures/valid/retry.json +8 -0
- package/src/auth-turn/fixtures/valid/unknown-kind.json +7 -0
- package/src/auth-turn/index.ts +177 -0
- package/src/auth.ts +728 -0
- package/src/ceremonies/index.ts +33 -121
- package/src/cli/commands.ts +10 -0
- package/src/cli/create.ts +69 -99
- package/src/cli/prompt-assets.ts +865 -0
- package/src/cli/templates/provider/.agents/skills/fixtures-and-recording/SKILL.md.tpl +58 -0
- package/src/cli/templates/provider/.agents/skills/health-checks-and-fail-closed/SKILL.md.tpl +65 -0
- package/src/cli/templates/provider/.agents/skills/normalization-standards/SKILL.md.tpl +57 -0
- package/src/cli/templates/provider/.agents/skills/pagination-and-counts/SKILL.md.tpl +52 -0
- package/src/cli/templates/provider/.agents/skills/upstream-contract-verification/SKILL.md.tpl +45 -0
- package/src/cli/templates/provider/.agents/skills/upstream-notes/README.md.tpl +13 -0
- package/src/cli/templates/provider/AGENTS.md.tpl +96 -0
- package/src/cli/templates/provider/README.md.tpl +7 -4
- package/src/config/loader.ts +543 -208
- package/src/contract-serialization.ts +5 -11
- package/src/contract-types.ts +2 -2
- package/src/contract.ts +13 -28
- package/src/define.ts +397 -528
- package/src/dev.ts +4 -9
- package/src/errors.ts +58 -5
- 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 +64 -41
- package/src/lint.ts +122 -159
- package/src/provider.ts +40 -9
- package/src/public-schema-field-lint.ts +7 -33
- package/src/recipes/gov-api.ts +2 -5
- package/src/runtime/auth-flow.ts +6 -6
- package/src/runtime/browser.ts +320 -151
- 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 +23 -20
- package/src/runtime/http.ts +216 -539
- package/src/runtime/insights.ts +15 -53
- package/src/runtime/instrumentation.ts +78 -65
- package/src/runtime/keyring.ts +7 -19
- package/src/runtime/namespace.ts +2 -7
- 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 +178 -0
- package/src/runtime/proxy-retry-policy.ts +469 -0
- package/src/runtime/proxy-telemetry.ts +79 -77
- package/src/runtime/redis.ts +4 -12
- package/src/runtime/request-options.ts +4 -13
- package/src/runtime/secrets.ts +64 -0
- package/src/runtime/state.ts +41 -110
- package/src/runtime/stealth.ts +331 -369
- 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 +44 -3
- package/src/server/self-test-input-tokens.ts +46 -0
- package/src/server/self-test-redaction.ts +97 -0
- package/src/server/self-test-token.ts +70 -0
- package/src/server/self-test.ts +1450 -0
- package/src/server/serve.ts +206 -216
- package/src/server/types.ts +7 -19
- package/src/stealth/profiles.ts +10 -26
- package/src/stream.ts +8 -19
- package/src/testing/index.ts +2 -2
- package/src/testing/run.ts +24 -64
- package/src/types.ts +274 -1
- package/src/user-input.ts +118 -0
package/dist/runtime/browser.js
CHANGED
|
@@ -1,8 +1,109 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
-
import { ProviderError } from "../errors";
|
|
2
|
+
import { ProviderError } from "../errors.js";
|
|
3
3
|
const require = createRequire(import.meta.url);
|
|
4
4
|
const DEFAULT_WAIT_TIMEOUT_MS = 30_000;
|
|
5
5
|
const SELECTOR_POLL_INTERVAL_MS = 100;
|
|
6
|
+
const RESOURCE_POLICY_ROUTE_PATTERN = "**/*";
|
|
7
|
+
const DEFAULT_RESOURCE_METHODS = ["GET", "HEAD"];
|
|
8
|
+
function toResourceBody(body) {
|
|
9
|
+
if (body === undefined || typeof body === "string" || Buffer.isBuffer(body)) {
|
|
10
|
+
return body;
|
|
11
|
+
}
|
|
12
|
+
if (body instanceof ArrayBuffer) {
|
|
13
|
+
return Buffer.from(new Uint8Array(body));
|
|
14
|
+
}
|
|
15
|
+
return Buffer.from(body);
|
|
16
|
+
}
|
|
17
|
+
function isResourceMethod(method) {
|
|
18
|
+
return method === "GET" || method === "HEAD";
|
|
19
|
+
}
|
|
20
|
+
async function toResourceRequest(request) {
|
|
21
|
+
const method = request.method().toUpperCase();
|
|
22
|
+
if (!isResourceMethod(method)) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
headers: await request.allHeaders(),
|
|
27
|
+
method,
|
|
28
|
+
resourceType: request.resourceType(),
|
|
29
|
+
url: request.url(),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function toCdpResourceRequest(params) {
|
|
33
|
+
if (!isRecord(params)) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
const requestId = params.requestId;
|
|
37
|
+
const rawRequest = params.request;
|
|
38
|
+
if (typeof requestId !== "string" || !isRecord(rawRequest)) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const url = rawRequest.url;
|
|
42
|
+
const method = String(rawRequest.method ?? "").toUpperCase();
|
|
43
|
+
if (typeof url !== "string" || !isResourceMethod(method)) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
requestId,
|
|
48
|
+
request: {
|
|
49
|
+
headers: toCdpResourceHeaders(rawRequest.headers),
|
|
50
|
+
method,
|
|
51
|
+
resourceType: typeof params.resourceType === "string" ? params.resourceType : undefined,
|
|
52
|
+
url,
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function getCdpPausedRequestId(params) {
|
|
57
|
+
if (!isRecord(params) || typeof params.requestId !== "string") {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
return params.requestId;
|
|
61
|
+
}
|
|
62
|
+
function toCdpResourceHeaders(value) {
|
|
63
|
+
if (!isRecord(value)) {
|
|
64
|
+
return {};
|
|
65
|
+
}
|
|
66
|
+
const headers = {};
|
|
67
|
+
for (const [name, headerValue] of Object.entries(value)) {
|
|
68
|
+
if (typeof headerValue === "string") {
|
|
69
|
+
headers[name] = headerValue;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return headers;
|
|
73
|
+
}
|
|
74
|
+
function matchesResourceRoute(match, request) {
|
|
75
|
+
if (typeof match === "string") {
|
|
76
|
+
return request.url === match;
|
|
77
|
+
}
|
|
78
|
+
if (match instanceof RegExp) {
|
|
79
|
+
return match.test(request.url);
|
|
80
|
+
}
|
|
81
|
+
return match(request);
|
|
82
|
+
}
|
|
83
|
+
function toCdpFulfillParams(requestId, decision) {
|
|
84
|
+
const body = toResourceBody(decision.body);
|
|
85
|
+
return {
|
|
86
|
+
...(body === undefined ? {} : { body: Buffer.from(body).toString("base64") }),
|
|
87
|
+
...(decision.headers === undefined
|
|
88
|
+
? {}
|
|
89
|
+
: {
|
|
90
|
+
responseHeaders: Object.entries(decision.headers).map(([name, value]) => ({
|
|
91
|
+
name,
|
|
92
|
+
value,
|
|
93
|
+
})),
|
|
94
|
+
}),
|
|
95
|
+
requestId,
|
|
96
|
+
responseCode: decision.status ?? 200,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
async function fulfillResourceRoute(route, decision) {
|
|
100
|
+
const body = toResourceBody(decision.body);
|
|
101
|
+
await route.fulfill({
|
|
102
|
+
...(body === undefined ? {} : { body }),
|
|
103
|
+
...(decision.headers === undefined ? {} : { headers: decision.headers }),
|
|
104
|
+
status: decision.status ?? 200,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
6
107
|
function getDefaultCdpPoolUrl(env = process.env) {
|
|
7
108
|
return env.APIFUSE__CDP_POOL__URL;
|
|
8
109
|
}
|
|
@@ -256,6 +357,38 @@ class PlaywrightBrowserPage {
|
|
|
256
357
|
async close() {
|
|
257
358
|
await this.page.close();
|
|
258
359
|
}
|
|
360
|
+
async withResourcePolicy(policy, run) {
|
|
361
|
+
const allowedMethods = new Set(policy.allowedMethods ?? DEFAULT_RESOURCE_METHODS);
|
|
362
|
+
const handler = async (route) => {
|
|
363
|
+
const request = await toResourceRequest(route.request());
|
|
364
|
+
if (!request || !allowedMethods.has(request.method)) {
|
|
365
|
+
await route.abort("blockedbyclient");
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
for (const resourceRoute of policy.routes) {
|
|
369
|
+
if (!matchesResourceRoute(resourceRoute.match, request)) {
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
const decision = await resourceRoute.handle(request);
|
|
373
|
+
switch (decision.action) {
|
|
374
|
+
case "fulfill":
|
|
375
|
+
await fulfillResourceRoute(route, decision);
|
|
376
|
+
return;
|
|
377
|
+
case "block":
|
|
378
|
+
await route.abort("blockedbyclient");
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
await route.abort("blockedbyclient");
|
|
383
|
+
};
|
|
384
|
+
await this.page.route(RESOURCE_POLICY_ROUTE_PATTERN, handler);
|
|
385
|
+
try {
|
|
386
|
+
return await run();
|
|
387
|
+
}
|
|
388
|
+
finally {
|
|
389
|
+
await this.page.unroute(RESOURCE_POLICY_ROUTE_PATTERN, handler);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
259
392
|
}
|
|
260
393
|
class PlaywrightBrowserClient {
|
|
261
394
|
options;
|
|
@@ -442,16 +575,13 @@ function parsePoolAcquireResponse(value) {
|
|
|
442
575
|
code: "BROWSER_RUNTIME_UNSUPPORTED",
|
|
443
576
|
});
|
|
444
577
|
}
|
|
445
|
-
if (value.browserContextId !== undefined &&
|
|
446
|
-
typeof value.browserContextId !== "string") {
|
|
578
|
+
if (value.browserContextId !== undefined && typeof value.browserContextId !== "string") {
|
|
447
579
|
throw new ProviderError("CDP Pool returned an invalid acquire response", {
|
|
448
580
|
code: "BROWSER_RUNTIME_UNSUPPORTED",
|
|
449
581
|
});
|
|
450
582
|
}
|
|
451
583
|
return {
|
|
452
|
-
...(value.browserContextId
|
|
453
|
-
? { browserContextId: value.browserContextId }
|
|
454
|
-
: {}),
|
|
584
|
+
...(value.browserContextId ? { browserContextId: value.browserContextId } : {}),
|
|
455
585
|
pageId: value.pageId,
|
|
456
586
|
wsEndpoint: value.wsEndpoint,
|
|
457
587
|
};
|
|
@@ -473,9 +603,7 @@ function parseCdpFrameTreeNode(value) {
|
|
|
473
603
|
frame: {
|
|
474
604
|
id: frameId,
|
|
475
605
|
name: typeof value.frame.name === "string" ? value.frame.name : undefined,
|
|
476
|
-
parentId: typeof value.frame.parentId === "string"
|
|
477
|
-
? value.frame.parentId
|
|
478
|
-
: undefined,
|
|
606
|
+
parentId: typeof value.frame.parentId === "string" ? value.frame.parentId : undefined,
|
|
479
607
|
url: typeof value.frame.url === "string" ? value.frame.url : undefined,
|
|
480
608
|
},
|
|
481
609
|
...(childFrames ? { childFrames } : {}),
|
|
@@ -488,9 +616,7 @@ function getCdpExecutionContext(params) {
|
|
|
488
616
|
const contextId = params.context.id;
|
|
489
617
|
const auxData = params.context.auxData;
|
|
490
618
|
return {
|
|
491
|
-
frameId: isRecord(auxData) && typeof auxData.frameId === "string"
|
|
492
|
-
? auxData.frameId
|
|
493
|
-
: undefined,
|
|
619
|
+
frameId: isRecord(auxData) && typeof auxData.frameId === "string" ? auxData.frameId : undefined,
|
|
494
620
|
id: typeof contextId === "number" ? contextId : undefined,
|
|
495
621
|
};
|
|
496
622
|
}
|
|
@@ -641,8 +767,7 @@ class CdpPoolBrowserPage {
|
|
|
641
767
|
returnByValue: true,
|
|
642
768
|
});
|
|
643
769
|
if (result.exceptionDetails) {
|
|
644
|
-
throw new Error(String(result.exceptionDetails.text ??
|
|
645
|
-
"Browser evaluation failed"));
|
|
770
|
+
throw new Error(String(result.exceptionDetails.text ?? "Browser evaluation failed"));
|
|
646
771
|
}
|
|
647
772
|
return result.result?.value;
|
|
648
773
|
}
|
|
@@ -727,9 +852,7 @@ class CdpPoolBrowserPage {
|
|
|
727
852
|
this.closed = true;
|
|
728
853
|
try {
|
|
729
854
|
await this.release({
|
|
730
|
-
...(this.browserContextId
|
|
731
|
-
? { browserContextId: this.browserContextId }
|
|
732
|
-
: {}),
|
|
855
|
+
...(this.browserContextId ? { browserContextId: this.browserContextId } : {}),
|
|
733
856
|
pageId: this.pageId,
|
|
734
857
|
});
|
|
735
858
|
}
|
|
@@ -737,6 +860,77 @@ class CdpPoolBrowserPage {
|
|
|
737
860
|
await this.pageClient.close();
|
|
738
861
|
}
|
|
739
862
|
}
|
|
863
|
+
async withResourcePolicy(policy, run) {
|
|
864
|
+
const allowedMethods = new Set(policy.allowedMethods ?? DEFAULT_RESOURCE_METHODS);
|
|
865
|
+
const handlePausedRequest = (params) => {
|
|
866
|
+
void this.handleResourcePolicyPausedRequest(params, policy, allowedMethods);
|
|
867
|
+
};
|
|
868
|
+
const unsubscribe = this.pageClient.on("Fetch.requestPaused", handlePausedRequest);
|
|
869
|
+
try {
|
|
870
|
+
await this.pageClient.send("Fetch.enable", {
|
|
871
|
+
patterns: [{ requestStage: "Request", urlPattern: "*" }],
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
catch (error) {
|
|
875
|
+
unsubscribe();
|
|
876
|
+
throw new ProviderError("CDP browser target does not support BrowserPage.withResourcePolicy()", {
|
|
877
|
+
cause: error instanceof Error ? error : undefined,
|
|
878
|
+
code: "BROWSER_RUNTIME_UNSUPPORTED",
|
|
879
|
+
fix: "Use a Chromium CDP target with the Fetch domain enabled, or use the local Playwright browser runtime.",
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
try {
|
|
883
|
+
return await run();
|
|
884
|
+
}
|
|
885
|
+
finally {
|
|
886
|
+
unsubscribe();
|
|
887
|
+
await this.pageClient.send("Fetch.disable");
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
async handleResourcePolicyPausedRequest(params, policy, allowedMethods) {
|
|
891
|
+
const requestId = getCdpPausedRequestId(params);
|
|
892
|
+
if (requestId === null) {
|
|
893
|
+
return;
|
|
894
|
+
}
|
|
895
|
+
try {
|
|
896
|
+
const parsed = toCdpResourceRequest(params);
|
|
897
|
+
if (!parsed || !allowedMethods.has(parsed.request.method)) {
|
|
898
|
+
await this.failCdpResourceRequest(requestId);
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
for (const resourceRoute of policy.routes) {
|
|
902
|
+
if (!matchesResourceRoute(resourceRoute.match, parsed.request)) {
|
|
903
|
+
continue;
|
|
904
|
+
}
|
|
905
|
+
const decision = await resourceRoute.handle(parsed.request);
|
|
906
|
+
switch (decision.action) {
|
|
907
|
+
case "fulfill":
|
|
908
|
+
await this.pageClient.send("Fetch.fulfillRequest", toCdpFulfillParams(parsed.requestId, decision));
|
|
909
|
+
return;
|
|
910
|
+
case "block":
|
|
911
|
+
await this.failCdpResourceRequest(parsed.requestId);
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
await this.failCdpResourceRequest(parsed.requestId);
|
|
916
|
+
}
|
|
917
|
+
catch {
|
|
918
|
+
await this.failCdpResourceRequest(requestId);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
async failCdpResourceRequest(requestId) {
|
|
922
|
+
try {
|
|
923
|
+
await this.pageClient.send("Fetch.failRequest", {
|
|
924
|
+
errorReason: "BlockedByClient",
|
|
925
|
+
requestId,
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
catch (error) {
|
|
929
|
+
if (error instanceof Error) {
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
}
|
|
740
934
|
async initialize() {
|
|
741
935
|
if (this.initialized) {
|
|
742
936
|
return;
|
|
@@ -799,12 +993,8 @@ class CdpPoolBrowserClient {
|
|
|
799
993
|
}
|
|
800
994
|
async acquirePage(options) {
|
|
801
995
|
const acquireResult = parsePoolAcquireResponse(await this.poolClient.send("acquire", {
|
|
802
|
-
...(this.allowedHosts.length > 0
|
|
803
|
-
|
|
804
|
-
: {}),
|
|
805
|
-
...(options?.isolatedContext
|
|
806
|
-
? { isolationMode: "browserContext" }
|
|
807
|
-
: {}),
|
|
996
|
+
...(this.allowedHosts.length > 0 ? { allowedHosts: this.allowedHosts } : {}),
|
|
997
|
+
...(options?.isolatedContext ? { isolationMode: "browserContext" } : {}),
|
|
808
998
|
}));
|
|
809
999
|
const pageClient = new JsonRpcWebSocketClient(acquireResult.wsEndpoint);
|
|
810
1000
|
const page = new CdpPoolBrowserPage(acquireResult.pageId, acquireResult.browserContextId, pageClient, async (request) => {
|
|
@@ -1020,8 +1210,7 @@ async function solveRecaptchaChallenge(page, request) {
|
|
|
1020
1210
|
async function findRecaptchaFrame(frames, siteKey) {
|
|
1021
1211
|
for (const frame of frames) {
|
|
1022
1212
|
const url = await frame.url();
|
|
1023
|
-
const matchesRecaptcha = url.includes("google.com/recaptcha") ||
|
|
1024
|
-
url.includes("recaptcha.net/recaptcha");
|
|
1213
|
+
const matchesRecaptcha = url.includes("google.com/recaptcha") || url.includes("recaptcha.net/recaptcha");
|
|
1025
1214
|
const matchesSiteKey = !siteKey || url.includes(siteKey);
|
|
1026
1215
|
if (matchesRecaptcha && matchesSiteKey) {
|
|
1027
1216
|
return frame;
|
package/dist/runtime/cache.d.ts
CHANGED
package/dist/runtime/cache.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { providerCacheRedisUrlFromEnv } from "../config/loader";
|
|
3
|
-
import { createProviderRedisClient, ensureRedisReady, withRedisTimeout, } from "./redis";
|
|
2
|
+
import { providerCacheRedisUrlFromEnv } from "../config/loader.js";
|
|
3
|
+
import { createProviderRedisClient, ensureRedisReady, withRedisTimeout, } from "./redis.js";
|
|
4
4
|
const DEFAULT_PREFIX = "apifuse:provider-cache:v1";
|
|
5
5
|
const DEFAULT_MEMORY_MAX_ENTRIES = 1_000;
|
|
6
6
|
const DEFAULT_REDIS_TIMEOUT_MS = 150;
|
|
@@ -71,10 +71,7 @@ function normalizeKeyPart(value, extra) {
|
|
|
71
71
|
return value;
|
|
72
72
|
}
|
|
73
73
|
function stableHash(value) {
|
|
74
|
-
return createHash("sha256")
|
|
75
|
-
.update(JSON.stringify(value))
|
|
76
|
-
.digest("hex")
|
|
77
|
-
.slice(0, 32);
|
|
74
|
+
return createHash("sha256").update(JSON.stringify(value)).digest("hex").slice(0, 32);
|
|
78
75
|
}
|
|
79
76
|
function jitteredTtlMs(ttlMs, jitterPct) {
|
|
80
77
|
if (!jitterPct || jitterPct <= 0)
|
|
@@ -282,8 +279,7 @@ export function createProviderCache(options) {
|
|
|
282
279
|
async delete(key) {
|
|
283
280
|
backend.memory.delete(key);
|
|
284
281
|
const redis = backend.redis;
|
|
285
|
-
if (!redis ||
|
|
286
|
-
!(await ensureRedisReady(redis, DEFAULT_REDIS_TIMEOUT_MS))) {
|
|
282
|
+
if (!redis || !(await ensureRedisReady(redis, DEFAULT_REDIS_TIMEOUT_MS))) {
|
|
287
283
|
return;
|
|
288
284
|
}
|
|
289
285
|
await withRedisFallback(() => redis.del(key));
|
package/dist/runtime/choice.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CredentialContext, EnvContext, ProviderChoiceContext, ProviderRequestContext, ProviderRuntimeState } from "../types";
|
|
1
|
+
import type { CredentialContext, EnvContext, ProviderChoiceContext, ProviderRequestContext, ProviderRuntimeState } from "../types.js";
|
|
2
2
|
export declare const PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV = "APIFUSE__PROVIDER_RUNTIME__CHOICE_TOKEN_MASTER_SECRET";
|
|
3
3
|
export type CreateProviderChoiceContextOptions = {
|
|
4
4
|
readonly providerId: string;
|
package/dist/runtime/choice.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createCipheriv, createDecipheriv, createHash, createHmac, randomBytes, timingSafeEqual, } from "node:crypto";
|
|
2
|
-
import { assertFreshProviderChoiceIssuedAt, ProviderChoiceTokenError, } from "../choice-token";
|
|
3
|
-
import { ProviderError } from "../errors";
|
|
2
|
+
import { assertFreshProviderChoiceIssuedAt, ProviderChoiceTokenError, } from "../choice-token.js";
|
|
3
|
+
import { isProviderError, ProviderError } from "../errors.js";
|
|
4
4
|
export const PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV = "APIFUSE__PROVIDER_RUNTIME__CHOICE_TOKEN_MASTER_SECRET";
|
|
5
5
|
const PRIMARY_CHOICE_TOKEN_KID = "v1";
|
|
6
6
|
const MANAGED_CHOICE_TOKEN_VERSION = 1;
|
|
@@ -53,7 +53,7 @@ export function createProviderChoiceContext(options) {
|
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
55
|
function parse(parseOptions) {
|
|
56
|
-
const [actualPrefix, tokenKid, encodedIv, encryptedPayload, authTag, signature
|
|
56
|
+
const [actualPrefix, tokenKid, encodedIv, encryptedPayload, authTag, signature] = parseManagedChoiceTokenParts(parseOptions.token);
|
|
57
57
|
if (actualPrefix !== parseOptions.prefix ||
|
|
58
58
|
tokenKid !== kid ||
|
|
59
59
|
!encodedIv ||
|
|
@@ -68,13 +68,7 @@ export function createProviderChoiceContext(options) {
|
|
|
68
68
|
purpose: parseOptions.purpose,
|
|
69
69
|
kid: tokenKid,
|
|
70
70
|
});
|
|
71
|
-
const signedBody = [
|
|
72
|
-
parseOptions.prefix,
|
|
73
|
-
tokenKid,
|
|
74
|
-
encodedIv,
|
|
75
|
-
encryptedPayload,
|
|
76
|
-
authTag,
|
|
77
|
-
].join(".");
|
|
71
|
+
const signedBody = [parseOptions.prefix, tokenKid, encodedIv, encryptedPayload, authTag].join(".");
|
|
78
72
|
assertManagedChoiceSignature({
|
|
79
73
|
signedBody,
|
|
80
74
|
signature,
|
|
@@ -117,13 +111,11 @@ export function createProviderChoiceContext(options) {
|
|
|
117
111
|
export function createTestProviderChoiceContext(options) {
|
|
118
112
|
return createProviderChoiceContext({
|
|
119
113
|
...options,
|
|
120
|
-
masterSecret: options.masterSecret ??
|
|
121
|
-
"apifuse-test-provider-runtime-choice-token-master-secret",
|
|
114
|
+
masterSecret: options.masterSecret ?? "apifuse-test-provider-runtime-choice-token-master-secret",
|
|
122
115
|
});
|
|
123
116
|
}
|
|
124
117
|
function resolveChoiceMasterSecret(options) {
|
|
125
|
-
const configured = options.masterSecret ??
|
|
126
|
-
options.env?.get(PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV);
|
|
118
|
+
const configured = options.masterSecret ?? options.env?.get(PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV);
|
|
127
119
|
const trimmed = configured?.trim();
|
|
128
120
|
if (trimmed)
|
|
129
121
|
return trimmed;
|
|
@@ -165,13 +157,7 @@ function encryptManagedChoiceToken(options) {
|
|
|
165
157
|
]).toString("base64url");
|
|
166
158
|
const authTag = cipher.getAuthTag().toString("base64url");
|
|
167
159
|
const encodedIv = iv.toString("base64url");
|
|
168
|
-
const signedBody = [
|
|
169
|
-
options.prefix,
|
|
170
|
-
options.kid,
|
|
171
|
-
encodedIv,
|
|
172
|
-
encryptedPayload,
|
|
173
|
-
authTag,
|
|
174
|
-
].join(".");
|
|
160
|
+
const signedBody = [options.prefix, options.kid, encodedIv, encryptedPayload, authTag].join(".");
|
|
175
161
|
const signature = createHmac("sha256", options.keys.signing)
|
|
176
162
|
.update(signedBody)
|
|
177
163
|
.digest("base64url");
|
|
@@ -223,7 +209,25 @@ async function parseServerStoredChoice(options) {
|
|
|
223
209
|
storage,
|
|
224
210
|
contextState: options.contextState,
|
|
225
211
|
});
|
|
226
|
-
|
|
212
|
+
// Reading a server-stored choice back deserializes a persisted value. A
|
|
213
|
+
// corrupt/undecodable value would otherwise surface as a raw JSON.parse
|
|
214
|
+
// SyntaxError (or another unexpected throwable) that escapes the choice error
|
|
215
|
+
// taxonomy, gets masked as internal_error 500, and is treated as retryable by
|
|
216
|
+
// the hub -> reservation restart loop (2026-07-22 catchtable RCA, candidate A).
|
|
217
|
+
// Convert any non-branded throwable into a branded invalid_payload so it maps
|
|
218
|
+
// to a clean, non-retryable 400. Branded ProviderChoiceTokenError and genuine
|
|
219
|
+
// ProviderError (e.g. Redis-unavailable / state-unavailable) pass through so
|
|
220
|
+
// their category/retryable semantics are preserved.
|
|
221
|
+
let record;
|
|
222
|
+
try {
|
|
223
|
+
record = await namespace.get(optionsStateKey(options.handle.state_id));
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
if (error instanceof ProviderChoiceTokenError || isProviderError(error)) {
|
|
227
|
+
throw error;
|
|
228
|
+
}
|
|
229
|
+
throw new ProviderChoiceTokenError("invalid_payload", "Provider choice token state payload could not be decoded.");
|
|
230
|
+
}
|
|
227
231
|
if (!record) {
|
|
228
232
|
throw new ProviderChoiceTokenError("invalid_payload", "Provider choice token state payload is missing.");
|
|
229
233
|
}
|
|
@@ -345,9 +349,7 @@ function isManagedChoiceEnvelope(value) {
|
|
|
345
349
|
typeof value.issued_at_ms === "number" &&
|
|
346
350
|
"ttl_ms" in value &&
|
|
347
351
|
typeof value.ttl_ms === "number" &&
|
|
348
|
-
(!("binding" in value) ||
|
|
349
|
-
value.binding === undefined ||
|
|
350
|
-
isChoiceBinding(value.binding)));
|
|
352
|
+
(!("binding" in value) || value.binding === undefined || isChoiceBinding(value.binding)));
|
|
351
353
|
}
|
|
352
354
|
function isChoicePayload(value) {
|
|
353
355
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
@@ -355,29 +357,23 @@ function isChoicePayload(value) {
|
|
|
355
357
|
function isChoiceBinding(value) {
|
|
356
358
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
357
359
|
return false;
|
|
358
|
-
return ((!("connection_hash" in value) ||
|
|
359
|
-
typeof value.connection_hash === "string") &&
|
|
360
|
+
return ((!("connection_hash" in value) || typeof value.connection_hash === "string") &&
|
|
360
361
|
(!("credential_hash" in value) || typeof value.credential_hash === "string"));
|
|
361
362
|
}
|
|
362
363
|
function assertManagedChoiceEnvelope(envelope, options) {
|
|
363
|
-
if (envelope.provider_id !== options.providerId ||
|
|
364
|
-
envelope.purpose !== options.purpose) {
|
|
364
|
+
if (envelope.provider_id !== options.providerId || envelope.purpose !== options.purpose) {
|
|
365
365
|
throw new ProviderChoiceTokenError("invalid_payload", "Provider choice token payload is invalid.");
|
|
366
366
|
}
|
|
367
367
|
assertFreshProviderChoiceIssuedAt(envelope.issued_at_ms, {
|
|
368
368
|
// Clamp to the issuer's embedded TTL so a caller-supplied value cannot
|
|
369
369
|
// silently extend token validity past the deadline the issuer intended.
|
|
370
|
-
ttlMs: options.ttlMs != null
|
|
371
|
-
? Math.min(options.ttlMs, envelope.ttl_ms)
|
|
372
|
-
: envelope.ttl_ms,
|
|
370
|
+
ttlMs: options.ttlMs != null ? Math.min(options.ttlMs, envelope.ttl_ms) : envelope.ttl_ms,
|
|
373
371
|
nowMs: options.nowMs,
|
|
374
372
|
futureToleranceMs: options.futureToleranceMs,
|
|
375
373
|
});
|
|
376
374
|
}
|
|
377
375
|
function createChoiceBinding(options) {
|
|
378
|
-
const connectionHash = options.options?.connection
|
|
379
|
-
? hashRequiredConnection(options)
|
|
380
|
-
: undefined;
|
|
376
|
+
const connectionHash = options.options?.connection ? hashRequiredConnection(options) : undefined;
|
|
381
377
|
const credentialHash = options.options?.credentialKeys?.length
|
|
382
378
|
? hashCredentialKeys(options)
|
|
383
379
|
: undefined;
|
package/dist/runtime/env.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { EnvContext } from "../types";
|
|
1
|
+
import type { EnvContext } from "../types.js";
|
|
2
2
|
export declare function createEnvContext(allowedKeys?: string[]): EnvContext;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ProviderContext, ProviderDefinition } from "../types";
|
|
1
|
+
import type { ProviderContext, ProviderDefinition } from "../types.js";
|
|
2
2
|
export declare function isStreamingOperation(provider: ProviderDefinition, operationId: string): boolean;
|
|
3
3
|
/**
|
|
4
4
|
* Execute a provider operation by calling its handler.
|
package/dist/runtime/executor.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { ProviderError, SessionExpiredError } from "../errors";
|
|
2
|
-
import { parseSchema } from "../schema";
|
|
1
|
+
import { isSessionExpiredError, ProviderError, SessionExpiredError } from "../errors.js";
|
|
2
|
+
import { parseSchema } from "../schema.js";
|
|
3
|
+
import { assertRequiredSecretsPresent } from "./secrets.js";
|
|
3
4
|
export function isStreamingOperation(provider, operationId) {
|
|
4
5
|
const kind = provider.operations[operationId]?.transport?.kind ?? "json";
|
|
5
6
|
return kind !== "json";
|
|
@@ -23,6 +24,12 @@ export async function executeOperation(provider, operationId, ctx, input, _optio
|
|
|
23
24
|
fix: `Valid operations: ${Object.keys(provider.operations).join(", ")}`,
|
|
24
25
|
});
|
|
25
26
|
}
|
|
27
|
+
// SDK-owned secret presence gate (single source of truth): declared
|
|
28
|
+
// `required: true` secrets are validated here, before input parsing and the
|
|
29
|
+
// handler, so every invocation path (serve /v1, self-test probes, perf,
|
|
30
|
+
// record) fails with the same structured MISSING_SECRET error instead of a
|
|
31
|
+
// handler-specific crash. Providers must not re-check presence locally.
|
|
32
|
+
assertRequiredSecretsPresent(provider, ctx.env);
|
|
26
33
|
const validatedInput = await parseSchema(operation.input, input, `operations.${operationId}.input`);
|
|
27
34
|
const execute = () => ctx.trace.span(`handler:${operationId}`, () => Promise.resolve(operation.handler(ctx, validatedInput)));
|
|
28
35
|
let result;
|
|
@@ -39,7 +46,12 @@ export async function executeOperation(provider, operationId, ctx, input, _optio
|
|
|
39
46
|
// operation is safe to re-drive after refresh, which we signal by marking
|
|
40
47
|
// the surfaced error retryable; non-idempotent operations (the default)
|
|
41
48
|
// stay non-retryable so they are not auto-re-driven. See design.md §4.3 D3.
|
|
42
|
-
|
|
49
|
+
// Use the branded guard, not `instanceof`: a handler loaded through a
|
|
50
|
+
// duplicate/published SDK module can throw a correctly branded
|
|
51
|
+
// SessionExpiredError whose constructor identity differs from this
|
|
52
|
+
// executor's, which `instanceof` would miss — dropping the retryable
|
|
53
|
+
// upgrade and stranding an operation that opted into auth refresh.
|
|
54
|
+
if (isSessionExpiredError(error) && operation.retryOnAuthRefresh) {
|
|
43
55
|
throw new SessionExpiredError(error.message, { retryable: true });
|
|
44
56
|
}
|
|
45
57
|
throw error;
|
package/dist/runtime/http.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ProxyResolutionOptions } from "../config/loader";
|
|
2
|
-
import type { HttpClient, HttpRetrySummary } from "../types";
|
|
1
|
+
import type { ProxyResolutionOptions } from "../config/loader.js";
|
|
2
|
+
import type { HttpClient, HttpRetrySummary } from "../types.js";
|
|
3
3
|
export type HttpClientOptions = ProxyResolutionOptions & {
|
|
4
4
|
warn?: (message: string) => void;
|
|
5
5
|
userAgent?: string;
|