@apifuse/provider-sdk 2.1.0-beta.2 → 2.1.0-beta.4
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 +172 -8
- package/CHANGELOG.md +15 -1
- package/README.md +29 -15
- package/SUBMISSION.md +86 -0
- package/bin/apifuse-dev.ts +12 -5
- package/bin/apifuse-pack-check.ts +17 -2
- package/bin/apifuse-pack-smoke.ts +133 -6
- package/bin/apifuse-perf.ts +19 -15
- package/bin/apifuse-record.ts +41 -53
- package/bin/apifuse-submit-check.ts +1052 -0
- package/bin/apifuse.ts +1 -1
- package/package.json +19 -9
- package/src/choice-token.ts +164 -0
- package/src/cli/commands.ts +24 -3
- package/src/cli/create.ts +166 -51
- package/src/cli/templates/provider/README.md.tpl +66 -7
- package/src/cli/templates/provider/dev.ts.tpl +1 -1
- package/src/cli/templates/provider/domain/README.md.tpl +3 -0
- package/src/cli/templates/provider/index.ts.tpl +5 -47
- package/src/cli/templates/provider/mappers/README.md.tpl +3 -0
- package/src/cli/templates/provider/meta.ts.tpl +7 -0
- package/src/cli/templates/provider/operations/index.ts.tpl +5 -0
- package/src/cli/templates/provider/operations/ping.ts.tpl +23 -0
- package/src/cli/templates/provider/schemas/ping.ts.tpl +16 -0
- package/src/cli/templates/provider/start.ts.tpl +1 -1
- package/src/cli/templates/provider/upstream/README.md.tpl +3 -0
- package/src/config/loader.ts +1206 -9
- package/src/define.ts +1648 -43
- package/src/errors.ts +12 -0
- package/src/i18n/catalog.ts +121 -0
- package/src/i18n/index.ts +2 -0
- package/src/i18n/keys.ts +64 -0
- package/src/index.ts +152 -8
- package/src/lint.ts +297 -42
- package/src/observability.ts +41 -0
- package/src/provider.ts +60 -3
- package/src/public-schema-field-lint.ts +237 -0
- package/src/runtime/auth-flow.ts +7 -0
- package/src/runtime/browser.ts +77 -21
- package/src/runtime/cache.ts +582 -0
- package/src/runtime/executor.ts +13 -1
- package/src/runtime/http.ts +939 -195
- package/src/runtime/insights.ts +11 -11
- package/src/runtime/instrumentation.ts +12 -4
- package/src/runtime/key-derivation.ts +1 -1
- package/src/runtime/keyring.ts +4 -3
- package/src/runtime/proxy-errors.ts +132 -0
- package/src/runtime/proxy-telemetry.ts +253 -0
- package/src/runtime/request-options.ts +66 -0
- package/src/runtime/state.ts +76 -0
- package/src/runtime/stealth.ts +1145 -0
- package/src/runtime/stt.ts +629 -0
- package/src/schema.ts +363 -1
- package/src/server/serve.ts +827 -60
- package/src/server/types.ts +35 -0
- package/src/stream.ts +210 -0
- package/src/testing/run.ts +17 -4
- package/src/types.ts +889 -50
- package/src/runtime/tls.ts +0 -434
- package/src/types/playwright-stealth.d.ts +0 -9
|
@@ -39,17 +39,19 @@ const PING_RESPONSE_SCHEMA = z.object({
|
|
|
39
39
|
error: z.unknown().optional(),
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
-
const KEEP_TEMP = process.env.
|
|
42
|
+
const KEEP_TEMP = process.env.APIFUSE__PACK_SMOKE__KEEP_TEMP === "1";
|
|
43
43
|
|
|
44
44
|
const tempRoot = mkdtempSync(
|
|
45
45
|
join(tmpdir(), "apifuse-provider-sdk-pack-smoke-"),
|
|
46
46
|
);
|
|
47
47
|
const packDir = join(tempRoot, "pack");
|
|
48
48
|
const consumerDir = join(tempRoot, "consumer");
|
|
49
|
+
const externalWorkspaceDir = join(tempRoot, "external-workspace");
|
|
49
50
|
|
|
50
51
|
try {
|
|
51
52
|
mkdirSync(packDir, { recursive: true });
|
|
52
53
|
mkdirSync(consumerDir, { recursive: true });
|
|
54
|
+
mkdirSync(join(externalWorkspaceDir, "providers"), { recursive: true });
|
|
53
55
|
|
|
54
56
|
const packed = packSdk(packDir);
|
|
55
57
|
const tarballPath = resolve(packDir, packed.filename);
|
|
@@ -93,9 +95,15 @@ try {
|
|
|
93
95
|
|
|
94
96
|
const generatedProviderDir = join(consumerDir, "dx-smoke");
|
|
95
97
|
run("bun", ["run", "check"], generatedProviderDir);
|
|
98
|
+
run("bun", ["run", "submit-check"], generatedProviderDir);
|
|
96
99
|
run("bun", ["run", "test"], generatedProviderDir);
|
|
97
100
|
assertGeneratedReadme(generatedProviderDir);
|
|
98
101
|
await smokeGeneratedDevServer(generatedProviderDir);
|
|
102
|
+
assertExternalWorkspaceTopology(
|
|
103
|
+
cliBin,
|
|
104
|
+
externalWorkspaceDir,
|
|
105
|
+
tarballSpecifier,
|
|
106
|
+
);
|
|
99
107
|
|
|
100
108
|
console.log(
|
|
101
109
|
`Provider SDK packed-artifact smoke passed: ${tarballPath} -> ${generatedProviderDir}`,
|
|
@@ -108,6 +116,103 @@ try {
|
|
|
108
116
|
}
|
|
109
117
|
}
|
|
110
118
|
|
|
119
|
+
function assertExternalWorkspaceTopology(
|
|
120
|
+
cliBin: string,
|
|
121
|
+
externalWorkspaceDir: string,
|
|
122
|
+
tarballSpecifier: string,
|
|
123
|
+
): void {
|
|
124
|
+
writeFileSync(
|
|
125
|
+
join(externalWorkspaceDir, "package.json"),
|
|
126
|
+
`${JSON.stringify(
|
|
127
|
+
{
|
|
128
|
+
private: true,
|
|
129
|
+
type: "module",
|
|
130
|
+
workspaces: ["providers/*"],
|
|
131
|
+
},
|
|
132
|
+
null,
|
|
133
|
+
2,
|
|
134
|
+
)}\n`,
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
run(
|
|
138
|
+
"bun",
|
|
139
|
+
[
|
|
140
|
+
cliBin,
|
|
141
|
+
"create",
|
|
142
|
+
"external-workspace-smoke",
|
|
143
|
+
"--yes",
|
|
144
|
+
"--json",
|
|
145
|
+
"--sdk-specifier",
|
|
146
|
+
tarballSpecifier,
|
|
147
|
+
],
|
|
148
|
+
externalWorkspaceDir,
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const generatedProviderDir = join(
|
|
152
|
+
externalWorkspaceDir,
|
|
153
|
+
"external-workspace-smoke",
|
|
154
|
+
);
|
|
155
|
+
const forbiddenProviderDir = join(
|
|
156
|
+
externalWorkspaceDir,
|
|
157
|
+
"providers",
|
|
158
|
+
"external-workspace-smoke",
|
|
159
|
+
);
|
|
160
|
+
if (!existsSync(generatedProviderDir)) {
|
|
161
|
+
throw new Error(
|
|
162
|
+
"Public create must generate a one-provider repository at <name>/ even when providers/ exists.",
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
if (existsSync(forbiddenProviderDir)) {
|
|
166
|
+
throw new Error(
|
|
167
|
+
"Public create must not generate providers/<name>/ in external bounty workspaces.",
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const packageJson = JSON.parse(
|
|
172
|
+
readFileSync(join(generatedProviderDir, "package.json"), "utf8"),
|
|
173
|
+
);
|
|
174
|
+
const sdkDependency = packageJson?.dependencies?.["@apifuse/provider-sdk"];
|
|
175
|
+
if (sdkDependency !== tarballSpecifier) {
|
|
176
|
+
throw new Error(
|
|
177
|
+
`Expected generated provider to depend on packed SDK ${tarballSpecifier}, got ${sdkDependency}`,
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
if (JSON.stringify(packageJson).includes("workspace:")) {
|
|
181
|
+
throw new Error(
|
|
182
|
+
"External bounty workspace scaffold must not contain workspace: dependencies.",
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
run("bun", ["install"], generatedProviderDir);
|
|
187
|
+
run("bun", ["run", "check"], generatedProviderDir);
|
|
188
|
+
run("bun", ["run", "submit-check"], generatedProviderDir);
|
|
189
|
+
run("bun", ["run", "test"], generatedProviderDir);
|
|
190
|
+
|
|
191
|
+
const monorepoAttempt = spawnSync(
|
|
192
|
+
"bun",
|
|
193
|
+
[cliBin, "create", "bad-monorepo-smoke", "--preset", "monorepo", "--yes"],
|
|
194
|
+
{
|
|
195
|
+
cwd: externalWorkspaceDir,
|
|
196
|
+
env: { ...process.env, APIFUSE__SDK__SPECIFIER: tarballSpecifier },
|
|
197
|
+
encoding: "utf8",
|
|
198
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
199
|
+
},
|
|
200
|
+
);
|
|
201
|
+
if (monorepoAttempt.status === 0) {
|
|
202
|
+
throw new Error(
|
|
203
|
+
"--preset monorepo must reject outside the private APIFuse monorepo.",
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
const rejectionOutput = `${monorepoAttempt.stdout}\n${monorepoAttempt.stderr}`;
|
|
207
|
+
if (
|
|
208
|
+
!rejectionOutput.includes(
|
|
209
|
+
"Monorepo preset is internal to the APIFuse repository",
|
|
210
|
+
)
|
|
211
|
+
) {
|
|
212
|
+
throw new Error(`Unexpected monorepo rejection output: ${rejectionOutput}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
111
216
|
function packSdk(destination: string): { filename: string } {
|
|
112
217
|
const raw = execFileSync(
|
|
113
218
|
"npm",
|
|
@@ -161,9 +266,14 @@ function assertGeneratedReadme(providerDir: string): void {
|
|
|
161
266
|
"Generated README is missing browser runtime troubleshooting guidance.",
|
|
162
267
|
);
|
|
163
268
|
}
|
|
164
|
-
if (!readme.includes("
|
|
269
|
+
if (!readme.includes("impit")) {
|
|
165
270
|
throw new Error(
|
|
166
|
-
"Generated README is missing
|
|
271
|
+
"Generated README is missing impit stealth runtime guidance.",
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
if (!readme.includes("bun run submit-check")) {
|
|
275
|
+
throw new Error(
|
|
276
|
+
"Generated README must document the submit-check pre-submission workflow.",
|
|
167
277
|
);
|
|
168
278
|
}
|
|
169
279
|
if (!readme.includes("bun run record -- --operation <operation>")) {
|
|
@@ -177,7 +287,8 @@ async function smokeGeneratedDevServer(providerDir: string): Promise<void> {
|
|
|
177
287
|
const port = await getAvailablePort();
|
|
178
288
|
const server = spawn("bun", ["run", "dev"], {
|
|
179
289
|
cwd: providerDir,
|
|
180
|
-
env: { ...process.env,
|
|
290
|
+
env: { ...process.env, APIFUSE__RUNTIME__PORT: String(port) },
|
|
291
|
+
detached: process.platform !== "win32",
|
|
181
292
|
stdio: ["ignore", "pipe", "pipe"],
|
|
182
293
|
});
|
|
183
294
|
let output = "";
|
|
@@ -280,11 +391,11 @@ async function stopServer(server: ChildProcess): Promise<void> {
|
|
|
280
391
|
if (server.exitCode !== null) {
|
|
281
392
|
return;
|
|
282
393
|
}
|
|
283
|
-
server
|
|
394
|
+
killProcessTree(server, "SIGTERM");
|
|
284
395
|
await new Promise<void>((resolvePromise) => {
|
|
285
396
|
const timeout = setTimeout(() => {
|
|
286
397
|
if (server.exitCode === null) {
|
|
287
|
-
server
|
|
398
|
+
killProcessTree(server, "SIGKILL");
|
|
288
399
|
}
|
|
289
400
|
resolvePromise();
|
|
290
401
|
}, 2_000);
|
|
@@ -294,3 +405,19 @@ async function stopServer(server: ChildProcess): Promise<void> {
|
|
|
294
405
|
});
|
|
295
406
|
});
|
|
296
407
|
}
|
|
408
|
+
|
|
409
|
+
function killProcessTree(server: ChildProcess, signal: NodeJS.Signals): void {
|
|
410
|
+
if (server.pid === undefined) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
try {
|
|
415
|
+
if (process.platform === "win32") {
|
|
416
|
+
server.kill(signal);
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
process.kill(-server.pid, signal);
|
|
420
|
+
} catch {
|
|
421
|
+
server.kill(signal);
|
|
422
|
+
}
|
|
423
|
+
}
|
package/bin/apifuse-perf.ts
CHANGED
|
@@ -8,8 +8,10 @@ import { pathToFileURL } from "node:url";
|
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
10
|
type ApiFuseConfig,
|
|
11
|
+
createBypassProviderCache,
|
|
11
12
|
createHttpClient,
|
|
12
|
-
|
|
13
|
+
createStealthClient,
|
|
14
|
+
createSttClientFromEnv,
|
|
13
15
|
executeOperation,
|
|
14
16
|
getProviderBaseUrl,
|
|
15
17
|
type HttpClient,
|
|
@@ -18,8 +20,8 @@ import {
|
|
|
18
20
|
type ProviderDefinition,
|
|
19
21
|
ProviderError,
|
|
20
22
|
type Span,
|
|
21
|
-
type
|
|
22
|
-
type
|
|
23
|
+
type StealthClient,
|
|
24
|
+
type StealthResponse,
|
|
23
25
|
wrapWithInstrumentation,
|
|
24
26
|
} from "../src";
|
|
25
27
|
import {
|
|
@@ -440,12 +442,12 @@ async function loadFixtureReplay(
|
|
|
440
442
|
}
|
|
441
443
|
|
|
442
444
|
function assertProxyConfigured(config: ApiFuseConfig): void {
|
|
443
|
-
if (config.proxy?.url || process.env.
|
|
445
|
+
if (config.proxy?.url || process.env.APIFUSE__PROXY__URL) {
|
|
444
446
|
return;
|
|
445
447
|
}
|
|
446
448
|
|
|
447
449
|
throw new Error(
|
|
448
|
-
"--compare-proxy requires a proxy URL in apifuse.config.ts or
|
|
450
|
+
"--compare-proxy requires a proxy URL in apifuse.config.ts or APIFUSE__PROXY__URL.",
|
|
449
451
|
);
|
|
450
452
|
}
|
|
451
453
|
|
|
@@ -637,10 +639,10 @@ function createBaseContext(options: {
|
|
|
637
639
|
apifuseConfig,
|
|
638
640
|
upstream,
|
|
639
641
|
});
|
|
640
|
-
const
|
|
642
|
+
const stealth =
|
|
641
643
|
options.forceFixtureReplay && options.fixtureReplay
|
|
642
|
-
?
|
|
643
|
-
:
|
|
644
|
+
? createFixtureStealthClient(options.fixtureReplay.rawText)
|
|
645
|
+
: createStealthClient(getProviderBaseUrl(options.provider), {
|
|
644
646
|
apifuseConfig,
|
|
645
647
|
upstream,
|
|
646
648
|
});
|
|
@@ -657,10 +659,12 @@ function createBaseContext(options: {
|
|
|
657
659
|
getScopes: () => [],
|
|
658
660
|
},
|
|
659
661
|
http,
|
|
660
|
-
|
|
662
|
+
cache: createBypassProviderCache({ providerId: options.provider.id }),
|
|
663
|
+
stealth,
|
|
661
664
|
browser: createBrowserStub(),
|
|
662
665
|
trace: options.traceContext,
|
|
663
666
|
auth: createAuthStub(),
|
|
667
|
+
stt: createSttClientFromEnv(options.provider.stt),
|
|
664
668
|
};
|
|
665
669
|
}
|
|
666
670
|
|
|
@@ -695,8 +699,8 @@ function createFixtureResponse(raw: unknown) {
|
|
|
695
699
|
};
|
|
696
700
|
}
|
|
697
701
|
|
|
698
|
-
function
|
|
699
|
-
const createResponse = async (): Promise<
|
|
702
|
+
function createFixtureStealthClient(rawText: string): StealthClient {
|
|
703
|
+
const createResponse = async (): Promise<StealthResponse> => ({
|
|
700
704
|
status: 200,
|
|
701
705
|
ok: true,
|
|
702
706
|
headers: { "content-type": "application/json" },
|
|
@@ -738,7 +742,7 @@ function buildInsights(
|
|
|
738
742
|
): string[] {
|
|
739
743
|
const insights: string[] = [];
|
|
740
744
|
const allSpans = runs.flatMap((run) => run.spans);
|
|
741
|
-
const
|
|
745
|
+
const stealthSpans = allSpans.filter((span) => span.name === "stealth.fetch");
|
|
742
746
|
const dnsSpans = allSpans.filter((span) => span.name === "dns");
|
|
743
747
|
const transform = breakdown.find(
|
|
744
748
|
(entry) => entry.name === "transformResponse",
|
|
@@ -746,7 +750,7 @@ function buildInsights(
|
|
|
746
750
|
const responseSizes = allSpans
|
|
747
751
|
.map((span) => span.attributes.response_size)
|
|
748
752
|
.filter((value): value is number => typeof value === "number");
|
|
749
|
-
const reuseFlags =
|
|
753
|
+
const reuseFlags = stealthSpans
|
|
750
754
|
.map((span) => span.attributes.connection_reused)
|
|
751
755
|
.filter((value): value is boolean => typeof value === "boolean");
|
|
752
756
|
|
|
@@ -756,8 +760,8 @@ function buildInsights(
|
|
|
756
760
|
);
|
|
757
761
|
insights.push(
|
|
758
762
|
reusePercent >= 80
|
|
759
|
-
? `✓
|
|
760
|
-
: `⚠
|
|
763
|
+
? `✓ Stealth connection reuse: ${reusePercent}% (good)`
|
|
764
|
+
: `⚠ Stealth connection reuse: ${reusePercent}% — consider session reuse`,
|
|
761
765
|
);
|
|
762
766
|
}
|
|
763
767
|
|
package/bin/apifuse-record.ts
CHANGED
|
@@ -7,13 +7,15 @@ import { basename, dirname, relative, resolve } from "node:path";
|
|
|
7
7
|
import { pathToFileURL } from "node:url";
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
|
+
createBypassProviderCache,
|
|
10
11
|
createHttpClient,
|
|
11
|
-
|
|
12
|
+
createStealthClient,
|
|
13
|
+
createSttClientFromEnv,
|
|
12
14
|
executeOperation,
|
|
13
15
|
type HttpClient,
|
|
14
16
|
type ProviderContext,
|
|
15
17
|
type ProviderDefinition,
|
|
16
|
-
type
|
|
18
|
+
type StealthClient,
|
|
17
19
|
} from "../src";
|
|
18
20
|
|
|
19
21
|
type CliArgs = {
|
|
@@ -37,6 +39,7 @@ export async function main() {
|
|
|
37
39
|
const parsedParams = parseParams(operation, args.params);
|
|
38
40
|
|
|
39
41
|
const capture = createCaptureContext(
|
|
42
|
+
provider,
|
|
40
43
|
resolveOperationBaseUrl(provider, operationName),
|
|
41
44
|
);
|
|
42
45
|
|
|
@@ -269,15 +272,18 @@ function resolveOperationBaseUrl(
|
|
|
269
272
|
return baseUrl;
|
|
270
273
|
}
|
|
271
274
|
|
|
272
|
-
function createCaptureContext(baseUrl: string) {
|
|
275
|
+
function createCaptureContext(provider: ProviderRuntime, baseUrl: string) {
|
|
273
276
|
let capturedRaw: unknown;
|
|
274
277
|
|
|
275
278
|
const http = proxyHttpClient(createHttpClient(baseUrl), (response) => {
|
|
276
279
|
capturedRaw = response.data;
|
|
277
280
|
});
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
281
|
+
const stealth = proxyStealthClient(
|
|
282
|
+
createStealthClient(baseUrl),
|
|
283
|
+
(response) => {
|
|
284
|
+
capturedRaw = normalizeCapturedStealthResponse(response);
|
|
285
|
+
},
|
|
286
|
+
);
|
|
281
287
|
|
|
282
288
|
const ctx: ProviderContext = {
|
|
283
289
|
env: {
|
|
@@ -291,7 +297,8 @@ function createCaptureContext(baseUrl: string) {
|
|
|
291
297
|
getScopes: () => [],
|
|
292
298
|
},
|
|
293
299
|
http,
|
|
294
|
-
|
|
300
|
+
cache: createBypassProviderCache({ providerId: provider.id }),
|
|
301
|
+
stealth,
|
|
295
302
|
browser: {
|
|
296
303
|
engine: "playwright-stealth",
|
|
297
304
|
newPage: async () => {
|
|
@@ -306,6 +313,7 @@ function createCaptureContext(baseUrl: string) {
|
|
|
306
313
|
throw new Error("Auth prompts are not available in apifuse record.");
|
|
307
314
|
},
|
|
308
315
|
},
|
|
316
|
+
stt: createSttClientFromEnv(provider.stt),
|
|
309
317
|
};
|
|
310
318
|
|
|
311
319
|
return {
|
|
@@ -335,59 +343,39 @@ function proxyHttpClient(
|
|
|
335
343
|
}) as HttpClient;
|
|
336
344
|
}
|
|
337
345
|
|
|
338
|
-
|
|
339
|
-
client: TlsClient,
|
|
340
|
-
onResponse: (response: Awaited<ReturnType<TlsClient["fetch"]>>) => void,
|
|
341
|
-
): TlsClient {
|
|
342
|
-
return new Proxy(client, {
|
|
343
|
-
get(target, prop, receiver) {
|
|
344
|
-
const value = Reflect.get(target, prop, receiver);
|
|
345
|
-
|
|
346
|
-
if (prop === "fetch" && typeof value === "function") {
|
|
347
|
-
return async (...args: unknown[]) => {
|
|
348
|
-
const response = await value.apply(target, args);
|
|
349
|
-
onResponse(response);
|
|
350
|
-
return response;
|
|
351
|
-
};
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
if (prop === "createSession" && typeof value === "function") {
|
|
355
|
-
return (...args: unknown[]) => {
|
|
356
|
-
const session = value.apply(target, args) as ReturnType<
|
|
357
|
-
TlsClient["createSession"]
|
|
358
|
-
>;
|
|
359
|
-
return proxyTlsSession(session, onResponse);
|
|
360
|
-
};
|
|
361
|
-
}
|
|
346
|
+
type StealthSession = ReturnType<StealthClient["createSession"]>;
|
|
362
347
|
|
|
363
|
-
|
|
348
|
+
function proxyStealthClient(
|
|
349
|
+
client: StealthClient,
|
|
350
|
+
onResponse: (response: Awaited<ReturnType<StealthClient["fetch"]>>) => void,
|
|
351
|
+
): StealthClient {
|
|
352
|
+
return {
|
|
353
|
+
fetch: async (...args: Parameters<StealthClient["fetch"]>) => {
|
|
354
|
+
const response = await client.fetch(...args);
|
|
355
|
+
onResponse(response);
|
|
356
|
+
return response;
|
|
364
357
|
},
|
|
365
|
-
|
|
358
|
+
createSession: (...args: Parameters<StealthClient["createSession"]>) =>
|
|
359
|
+
proxyStealthSession(client.createSession(...args), onResponse),
|
|
360
|
+
};
|
|
366
361
|
}
|
|
367
362
|
|
|
368
|
-
function
|
|
369
|
-
session:
|
|
370
|
-
onResponse: (response: Awaited<ReturnType<
|
|
371
|
-
) {
|
|
372
|
-
return
|
|
373
|
-
|
|
374
|
-
const
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
return async (...args: unknown[]) => {
|
|
378
|
-
const response = await value.apply(target, args);
|
|
379
|
-
onResponse(response);
|
|
380
|
-
return response;
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
return value;
|
|
363
|
+
function proxyStealthSession(
|
|
364
|
+
session: StealthSession,
|
|
365
|
+
onResponse: (response: Awaited<ReturnType<StealthClient["fetch"]>>) => void,
|
|
366
|
+
): StealthSession {
|
|
367
|
+
return {
|
|
368
|
+
fetch: async (...args: Parameters<StealthSession["fetch"]>) => {
|
|
369
|
+
const response = await session.fetch(...args);
|
|
370
|
+
onResponse(response);
|
|
371
|
+
return response;
|
|
385
372
|
},
|
|
386
|
-
|
|
373
|
+
close: () => session.close(),
|
|
374
|
+
};
|
|
387
375
|
}
|
|
388
376
|
|
|
389
|
-
function
|
|
390
|
-
response: Awaited<ReturnType<
|
|
377
|
+
function normalizeCapturedStealthResponse(
|
|
378
|
+
response: Awaited<ReturnType<StealthClient["fetch"]>>,
|
|
391
379
|
) {
|
|
392
380
|
try {
|
|
393
381
|
return JSON.parse(response.body);
|