@apifuse/provider-sdk 2.2.0-beta.4 → 2.2.0-beta.41
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 +501 -13
- package/CHANGELOG.md +169 -1
- package/README.md +72 -14
- package/SUBMISSION.md +1 -1
- package/bin/apifuse-check.ts +150 -5
- package/bin/apifuse-dev.ts +38 -5
- package/bin/apifuse-migrate-shape.ts +84 -0
- package/bin/apifuse-pack-check.ts +22 -2
- package/bin/apifuse-pack-smoke.ts +57 -2
- package/bin/apifuse-pack-types.ts +356 -38
- package/bin/apifuse-perf.ts +14 -13
- package/bin/apifuse-record.ts +691 -68
- package/bin/apifuse-submit-check.ts +2301 -282
- package/bin/apifuse-sync-assets.ts +117 -0
- package/bin/submit-check-delimited-text.ts +50 -0
- package/dist/auth-turn/index.d.ts +3 -3
- package/dist/auth-turn/index.js +1 -1
- package/dist/auth.d.ts +14 -0
- package/dist/auth.js +67 -0
- package/dist/ceremonies/index.d.ts +16 -0
- package/dist/ceremonies/index.js +141 -36
- package/dist/cli/commands.d.ts +1 -1
- package/dist/cli/commands.js +16 -0
- package/dist/cli/create.d.ts +3 -0
- package/dist/cli/create.js +40 -35
- package/dist/cli/migrate-provider-shape.d.ts +52 -0
- package/dist/cli/migrate-provider-shape.js +515 -0
- package/dist/cli/prompt-assets.d.ts +80 -0
- package/dist/cli/prompt-assets.js +743 -0
- package/dist/cli/templates/provider/AGENTS.md.tpl +17 -8
- package/dist/cli/templates/provider/Dockerfile.tpl +1 -1
- package/dist/cli/templates/provider/README.md.tpl +4 -4
- package/dist/cli/templates/provider/index.ts.tpl +6 -3
- package/dist/cli/templates/provider/operations/ping.ts.tpl +2 -1
- package/dist/cli/templates/provider/provider.json.tpl +6 -0
- package/dist/config/loader.d.ts +177 -16
- package/dist/config/loader.js +424 -127
- package/dist/contract-serialization.js +4 -8
- package/dist/contract-types.d.ts +1 -0
- package/dist/contract.js +3 -0
- package/dist/declaration-validation.d.ts +32 -0
- package/dist/declaration-validation.js +207 -0
- package/dist/define.d.ts +51 -25
- package/dist/define.js +774 -39
- package/dist/error-observability.d.ts +7 -0
- package/dist/error-observability.js +61 -0
- package/dist/error-resolution.d.ts +4 -0
- package/dist/error-resolution.js +122 -0
- package/dist/errors.d.ts +33 -0
- package/dist/errors.js +40 -0
- package/dist/fixture-sanitization.d.ts +28 -0
- package/dist/fixture-sanitization.js +227 -0
- package/dist/health-scenario.d.ts +1842 -0
- package/dist/health-scenario.js +624 -0
- package/dist/index.d.ts +19 -9
- package/dist/index.js +10 -6
- package/dist/lint.d.ts +6 -1
- package/dist/lint.js +362 -3
- package/dist/native-address.d.ts +43 -0
- package/dist/native-address.js +281 -0
- package/dist/native-egress-policy.d.ts +31 -0
- package/dist/native-egress-policy.js +288 -0
- package/dist/observability.d.ts +5 -2
- package/dist/observability.js +48 -1
- package/dist/provider.d.ts +8 -2
- package/dist/provider.js +3 -1
- package/dist/runtime/auth-flow.d.ts +5 -1
- package/dist/runtime/auth-flow.js +6 -0
- package/dist/runtime/browser.d.ts +1 -0
- package/dist/runtime/browser.js +492 -49
- package/dist/runtime/cache.d.ts +1 -0
- package/dist/runtime/cache.js +169 -15
- package/dist/runtime/choice-wordlist.d.ts +9 -0
- package/dist/runtime/choice-wordlist.js +138 -0
- package/dist/runtime/choice.d.ts +13 -1
- package/dist/runtime/choice.js +490 -102
- package/dist/runtime/executor.d.ts +2 -2
- package/dist/runtime/executor.js +37 -3
- package/dist/runtime/http.d.ts +1 -0
- package/dist/runtime/http.js +515 -53
- package/dist/runtime/instrumentation.d.ts +2 -2
- package/dist/runtime/instrumentation.js +366 -8
- package/dist/runtime/native-network-errors.d.ts +33 -0
- package/dist/runtime/native-network-errors.js +69 -0
- package/dist/runtime/native-network.d.ts +96 -0
- package/dist/runtime/native-network.js +1232 -0
- package/dist/runtime/ocr.d.ts +29 -0
- package/dist/runtime/ocr.js +440 -0
- package/dist/runtime/proxy-errors.js +6 -2
- package/dist/runtime/proxy-nodemaven.d.ts +56 -0
- package/dist/runtime/proxy-nodemaven.js +146 -0
- package/dist/runtime/proxy-telemetry.d.ts +80 -1
- package/dist/runtime/proxy-telemetry.js +154 -47
- package/dist/runtime/redirects.d.ts +29 -0
- package/dist/runtime/redirects.js +36 -0
- package/dist/runtime/redis.d.ts +1 -1
- package/dist/runtime/redis.js +4 -2
- package/dist/runtime/request-options.d.ts +68 -1
- package/dist/runtime/request-options.js +548 -0
- package/dist/runtime/resolver-config.d.ts +6 -0
- package/dist/runtime/resolver-config.js +6 -0
- package/dist/runtime/resolver-public.d.ts +1 -0
- package/dist/runtime/resolver-public.js +1 -0
- package/dist/runtime/resolver-shared.d.ts +3 -0
- package/dist/runtime/resolver-shared.js +12 -0
- package/dist/runtime/resolver-vendors/bindings.d.ts +48 -0
- package/dist/runtime/resolver-vendors/bindings.js +40 -0
- package/dist/runtime/resolver-vendors/browser.d.ts +22 -0
- package/dist/runtime/resolver-vendors/browser.js +377 -0
- package/dist/runtime/resolver-vendors/capsolver.d.ts +22 -0
- package/dist/runtime/resolver-vendors/capsolver.js +526 -0
- package/dist/runtime/resolver-vendors/hosts.d.ts +2 -0
- package/dist/runtime/resolver-vendors/hosts.js +33 -0
- package/dist/runtime/resolver-vendors/twocaptcha.d.ts +24 -0
- package/dist/runtime/resolver-vendors/twocaptcha.js +407 -0
- package/dist/runtime/resolver-vendors/types.d.ts +94 -0
- package/dist/runtime/resolver-vendors/types.js +96 -0
- package/dist/runtime/resolver.d.ts +60 -0
- package/dist/runtime/resolver.js +737 -0
- package/dist/runtime/secrets.d.ts +27 -0
- package/dist/runtime/secrets.js +51 -0
- package/dist/runtime/state.d.ts +3 -0
- package/dist/runtime/state.js +277 -71
- package/dist/runtime/stealth-cookies.d.ts +20 -0
- package/dist/runtime/stealth-cookies.js +111 -0
- package/dist/runtime/stealth.d.ts +28 -3
- package/dist/runtime/stealth.js +519 -255
- package/dist/runtime/stt.js +1 -12
- package/dist/runtime/timeout.d.ts +5 -0
- package/dist/runtime/timeout.js +12 -0
- package/dist/runtime/trace-config.d.ts +12 -0
- package/dist/runtime/trace-config.js +61 -0
- package/dist/serve.d.ts +1 -1
- package/dist/serve.js +1 -1
- package/dist/server/error-observability.d.ts +1 -0
- package/dist/server/error-observability.js +1 -0
- package/dist/server/index.d.ts +6 -3
- package/dist/server/index.js +3 -3
- package/dist/server/self-test-input-tokens.d.ts +2 -1
- package/dist/server/self-test-input-tokens.js +18 -14
- package/dist/server/self-test.d.ts +114 -0
- package/dist/server/self-test.js +787 -148
- package/dist/server/serve-implementation.d.ts +225 -0
- package/dist/server/serve-implementation.js +2242 -0
- package/dist/server/serve.d.ts +1 -70
- package/dist/server/serve.js +1 -1130
- package/dist/server/trace-output.d.ts +4 -0
- package/dist/server/trace-output.js +20 -0
- package/dist/server/types.d.ts +30 -5
- package/dist/server/types.js +13 -1
- package/dist/stateful/errors.d.ts +19 -0
- package/dist/stateful/errors.js +24 -0
- package/dist/stateful/http-provider-event-emitter.d.ts +40 -0
- package/dist/stateful/http-provider-event-emitter.js +237 -0
- package/dist/stateful/http-session-owner-registry.d.ts +44 -0
- package/dist/stateful/http-session-owner-registry.js +210 -0
- package/dist/stateful/index.d.ts +18 -0
- package/dist/stateful/index.js +18 -0
- package/dist/stateful/provider-event-delivery-failures.d.ts +32 -0
- package/dist/stateful/provider-event-delivery-failures.js +43 -0
- package/dist/stateful/provider-event-pipeline-metrics.d.ts +46 -0
- package/dist/stateful/provider-event-pipeline-metrics.js +48 -0
- package/dist/stateful/provider-event-pipeline.d.ts +50 -0
- package/dist/stateful/provider-event-pipeline.js +1 -0
- package/dist/stateful/provider-events.d.ts +101 -0
- package/dist/stateful/provider-events.js +289 -0
- package/dist/stateful/session-key.d.ts +15 -0
- package/dist/stateful/session-key.js +86 -0
- package/dist/stateful/stateful-provider-adapter-context.d.ts +5 -0
- package/dist/stateful/stateful-provider-adapter-context.js +42 -0
- package/dist/stateful/stateful-provider-adapter-metrics.d.ts +15 -0
- package/dist/stateful/stateful-provider-adapter-metrics.js +21 -0
- package/dist/stateful/stateful-provider-adapter.d.ts +98 -0
- package/dist/stateful/stateful-provider-adapter.js +287 -0
- package/dist/stateful/stateful-provider-observability.d.ts +62 -0
- package/dist/stateful/stateful-provider-observability.js +161 -0
- package/dist/stateful/stateful-provider-owner-forwarder.d.ts +41 -0
- package/dist/stateful/stateful-provider-owner-forwarder.js +215 -0
- package/dist/stateful/stateful-provider-runtime-context.d.ts +32 -0
- package/dist/stateful/stateful-provider-runtime-context.js +60 -0
- package/dist/stateful/stateful-provider-runtime-executor.d.ts +34 -0
- package/dist/stateful/stateful-provider-runtime-executor.js +52 -0
- package/dist/stateful/stateful-provider-session-routing.d.ts +67 -0
- package/dist/stateful/stateful-provider-session-routing.js +345 -0
- package/dist/stateful/stateful-provider-session-runtime.d.ts +98 -0
- package/dist/stateful/stateful-provider-session-runtime.js +245 -0
- package/dist/stateful-signing.d.ts +18 -0
- package/dist/stateful-signing.js +27 -0
- package/dist/stealth/profiles.js +16 -7
- package/dist/stream-evidence.d.ts +74 -0
- package/dist/stream-evidence.js +785 -0
- package/dist/stream.js +7 -1
- package/dist/testing/index.d.ts +2 -1
- package/dist/testing/index.js +2 -1
- package/dist/testing/run.d.ts +32 -2
- package/dist/testing/run.js +489 -21
- package/dist/trace-sanitization.d.ts +5 -0
- package/dist/trace-sanitization.js +45 -0
- package/dist/types.d.ts +563 -33
- package/dist/types.js +1 -0
- package/package.json +44 -5
- package/src/auth-turn/index.ts +1 -1
- package/src/auth.ts +118 -0
- package/src/ceremonies/index.ts +189 -46
- package/src/cli/commands.ts +20 -0
- package/src/cli/create.ts +48 -35
- package/src/cli/migrate-provider-shape.ts +701 -0
- package/src/cli/prompt-assets.ts +865 -0
- package/src/cli/templates/provider/AGENTS.md.tpl +17 -8
- package/src/cli/templates/provider/Dockerfile.tpl +1 -1
- package/src/cli/templates/provider/README.md.tpl +4 -4
- package/src/cli/templates/provider/index.ts.tpl +6 -3
- package/src/cli/templates/provider/operations/ping.ts.tpl +2 -1
- package/src/cli/templates/provider/provider.json.tpl +6 -0
- package/src/config/loader.ts +665 -163
- package/src/contract-serialization.ts +5 -7
- package/src/contract-types.ts +1 -0
- package/src/contract.ts +3 -0
- package/src/declaration-validation.ts +266 -0
- package/src/define.ts +1003 -88
- package/src/error-observability.ts +64 -0
- package/src/error-resolution.ts +127 -0
- package/src/errors.ts +68 -0
- package/src/fixture-sanitization.ts +264 -0
- package/src/health-scenario.ts +875 -0
- package/src/index.ts +205 -8
- package/src/lint.ts +408 -4
- package/src/native-address.ts +340 -0
- package/src/native-egress-policy.ts +358 -0
- package/src/observability.ts +51 -1
- package/src/provider.ts +134 -0
- package/src/runtime/auth-flow.ts +12 -0
- package/src/runtime/browser.ts +661 -63
- package/src/runtime/cache.ts +189 -14
- package/src/runtime/choice-wordlist.ts +145 -0
- package/src/runtime/choice.ts +631 -120
- package/src/runtime/executor.ts +53 -8
- package/src/runtime/http.ts +641 -61
- package/src/runtime/instrumentation.ts +520 -15
- package/src/runtime/native-network-errors.ts +99 -0
- package/src/runtime/native-network.ts +1605 -0
- package/src/runtime/ocr.ts +523 -0
- package/src/runtime/proxy-errors.ts +12 -4
- package/src/runtime/proxy-nodemaven.ts +221 -0
- package/src/runtime/proxy-telemetry.ts +244 -75
- package/src/runtime/redirects.ts +66 -0
- package/src/runtime/redis.ts +7 -2
- package/src/runtime/request-options.ts +680 -1
- package/src/runtime/resolver-config.ts +6 -0
- package/src/runtime/resolver-public.ts +20 -0
- package/src/runtime/resolver-shared.ts +17 -0
- package/src/runtime/resolver-vendors/bindings.ts +56 -0
- package/src/runtime/resolver-vendors/browser.ts +533 -0
- package/src/runtime/resolver-vendors/capsolver.ts +700 -0
- package/src/runtime/resolver-vendors/hosts.ts +38 -0
- package/src/runtime/resolver-vendors/twocaptcha.ts +539 -0
- package/src/runtime/resolver-vendors/types.ts +212 -0
- package/src/runtime/resolver.ts +1103 -0
- package/src/runtime/secrets.ts +64 -0
- package/src/runtime/state.ts +394 -77
- package/src/runtime/stealth-cookies.ts +132 -0
- package/src/runtime/stealth.ts +675 -289
- package/src/runtime/stt.ts +1 -19
- package/src/runtime/timeout.ts +18 -0
- package/src/runtime/trace-config.ts +77 -0
- package/src/serve.ts +6 -1
- package/src/server/error-observability.ts +1 -0
- package/src/server/index.ts +39 -2
- package/src/server/self-test-input-tokens.ts +29 -14
- package/src/server/self-test.ts +1030 -175
- package/src/server/serve-implementation.ts +3338 -0
- package/src/server/serve.ts +1 -1626
- package/src/server/trace-output.ts +32 -0
- package/src/server/types.ts +13 -1
- package/src/stateful/README.md +146 -0
- package/src/stateful/errors.ts +35 -0
- package/src/stateful/http-provider-event-emitter.ts +314 -0
- package/src/stateful/http-session-owner-registry.ts +306 -0
- package/src/stateful/index.ts +18 -0
- package/src/stateful/provider-event-delivery-failures.ts +80 -0
- package/src/stateful/provider-event-pipeline-metrics.ts +95 -0
- package/src/stateful/provider-event-pipeline.ts +61 -0
- package/src/stateful/provider-events.ts +462 -0
- package/src/stateful/session-key.ts +111 -0
- package/src/stateful/stateful-provider-adapter-context.ts +59 -0
- package/src/stateful/stateful-provider-adapter-metrics.ts +48 -0
- package/src/stateful/stateful-provider-adapter.ts +562 -0
- package/src/stateful/stateful-provider-observability.ts +261 -0
- package/src/stateful/stateful-provider-owner-forwarder.ts +287 -0
- package/src/stateful/stateful-provider-runtime-context.ts +92 -0
- package/src/stateful/stateful-provider-runtime-executor.ts +96 -0
- package/src/stateful/stateful-provider-session-routing.ts +546 -0
- package/src/stateful/stateful-provider-session-runtime.ts +403 -0
- package/src/stateful-signing.ts +46 -0
- package/src/stealth/profiles.ts +17 -7
- package/src/stream-evidence.ts +988 -0
- package/src/stream.ts +8 -1
- package/src/testing/index.ts +10 -1
- package/src/testing/run.ts +658 -15
- package/src/trace-sanitization.ts +63 -0
- package/src/types.ts +698 -65
- package/dist/cli/templates/provider/CLAUDE.md.tpl +0 -1
- package/src/cli/templates/provider/CLAUDE.md.tpl +0 -1
- /package/dist/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `apifuse migrate-shape [path] [--check] [--json]`
|
|
5
|
+
*
|
|
6
|
+
* Applies the provider authoring-shape migration (single-phase
|
|
7
|
+
* `defineProvider({...operations})` to the two-phase declaration builder) to
|
|
8
|
+
* a provider's `index.ts`. This is the source half of a breaking SDK bump:
|
|
9
|
+
* the pin bump and this transform must land in the same commit, because the
|
|
10
|
+
* single-phase shape default-exports a builder function under 2.2.0-beta.37+
|
|
11
|
+
* and the module stops loading.
|
|
12
|
+
*
|
|
13
|
+
* Exit codes: 0 migrated or already two-phase; 1 skipped (the transform
|
|
14
|
+
* refuses to guess) or the file is missing. `--check` reports without
|
|
15
|
+
* writing. A skip is a hard stop for fan-out callers — never pair a pin bump
|
|
16
|
+
* with a skipped migration.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
20
|
+
import { resolve } from "node:path";
|
|
21
|
+
|
|
22
|
+
import { migrateProviderShape } from "../src/cli/migrate-provider-shape.js";
|
|
23
|
+
|
|
24
|
+
export async function main(): Promise<void> {
|
|
25
|
+
const args = process.argv.slice(3);
|
|
26
|
+
const check = args.includes("--check");
|
|
27
|
+
const json = args.includes("--json");
|
|
28
|
+
const positional = args.filter((argument) => !argument.startsWith("--"));
|
|
29
|
+
const providerRoot = resolve(positional[0] ?? ".");
|
|
30
|
+
const indexPath = resolve(providerRoot, "index.ts");
|
|
31
|
+
|
|
32
|
+
const report = (payload: Record<string, unknown>, humanText: string): void => {
|
|
33
|
+
if (json) {
|
|
34
|
+
console.log(JSON.stringify({ schemaVersion: 1, indexPath, ...payload }));
|
|
35
|
+
} else {
|
|
36
|
+
console.log(humanText);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
if (!existsSync(indexPath)) {
|
|
41
|
+
report(
|
|
42
|
+
{ status: "skipped", reason: "index.ts not found" },
|
|
43
|
+
`migrate-shape: ${indexPath} not found.`,
|
|
44
|
+
);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const sourceText = readFileSync(indexPath, "utf8");
|
|
49
|
+
const result = migrateProviderShape(sourceText, indexPath);
|
|
50
|
+
|
|
51
|
+
if (result.status === "skipped") {
|
|
52
|
+
report(
|
|
53
|
+
{ status: "skipped", reason: result.reason },
|
|
54
|
+
`migrate-shape: skipped — ${result.reason}\nThis provider needs a manual migration; do not bump its SDK pin without one.`,
|
|
55
|
+
);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (result.status === "unchanged") {
|
|
60
|
+
report(
|
|
61
|
+
{ status: "unchanged" },
|
|
62
|
+
"migrate-shape: already two-phase; nothing to do.",
|
|
63
|
+
);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (check) {
|
|
68
|
+
report(
|
|
69
|
+
{ status: "would-migrate", kind: result.kind },
|
|
70
|
+
`migrate-shape: would migrate (${result.kind}). Run without --check to write.`,
|
|
71
|
+
);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
writeFileSync(indexPath, result.code, "utf8");
|
|
76
|
+
report(
|
|
77
|
+
{ status: "migrated", kind: result.kind },
|
|
78
|
+
`migrate-shape: migrated (${result.kind}). Review the diff, then run \`apifuse check\` and \`bun test\`.`,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (import.meta.main) {
|
|
83
|
+
await main();
|
|
84
|
+
}
|
|
@@ -48,6 +48,13 @@ const requiredPaths = [
|
|
|
48
48
|
"src/cli/templates/provider/operations/ping.ts.tpl",
|
|
49
49
|
"src/cli/templates/provider/schemas/ping.ts.tpl",
|
|
50
50
|
"src/cli/templates/provider/upstream/README.md.tpl",
|
|
51
|
+
"src/cli/templates/provider/AGENTS.md.tpl",
|
|
52
|
+
"src/cli/templates/provider/.agents/skills/normalization-standards/SKILL.md.tpl",
|
|
53
|
+
"src/cli/templates/provider/.agents/skills/upstream-contract-verification/SKILL.md.tpl",
|
|
54
|
+
"src/cli/templates/provider/.agents/skills/fixtures-and-recording/SKILL.md.tpl",
|
|
55
|
+
"src/cli/templates/provider/.agents/skills/pagination-and-counts/SKILL.md.tpl",
|
|
56
|
+
"src/cli/templates/provider/.agents/skills/health-checks-and-fail-closed/SKILL.md.tpl",
|
|
57
|
+
"src/cli/templates/provider/.agents/skills/upstream-notes/README.md.tpl",
|
|
51
58
|
"dist/cli/templates/provider/.dockerignore.tpl",
|
|
52
59
|
"dist/cli/templates/provider/.gitignore.tpl",
|
|
53
60
|
"dist/cli/templates/provider/Dockerfile.tpl",
|
|
@@ -60,6 +67,13 @@ const requiredPaths = [
|
|
|
60
67
|
"dist/cli/templates/provider/operations/ping.ts.tpl",
|
|
61
68
|
"dist/cli/templates/provider/schemas/ping.ts.tpl",
|
|
62
69
|
"dist/cli/templates/provider/upstream/README.md.tpl",
|
|
70
|
+
"dist/cli/templates/provider/AGENTS.md.tpl",
|
|
71
|
+
"dist/cli/templates/provider/.agents/skills/normalization-standards/SKILL.md.tpl",
|
|
72
|
+
"dist/cli/templates/provider/.agents/skills/upstream-contract-verification/SKILL.md.tpl",
|
|
73
|
+
"dist/cli/templates/provider/.agents/skills/fixtures-and-recording/SKILL.md.tpl",
|
|
74
|
+
"dist/cli/templates/provider/.agents/skills/pagination-and-counts/SKILL.md.tpl",
|
|
75
|
+
"dist/cli/templates/provider/.agents/skills/health-checks-and-fail-closed/SKILL.md.tpl",
|
|
76
|
+
"dist/cli/templates/provider/.agents/skills/upstream-notes/README.md.tpl",
|
|
63
77
|
"dist/auth-turn/index.js",
|
|
64
78
|
"dist/auth-turn/index.d.ts",
|
|
65
79
|
"dist/auth-turn/auth-turn.v1.schema.json",
|
|
@@ -142,9 +156,9 @@ function assertPublicSmokeDocs(label: string, content: string): void {
|
|
|
142
156
|
);
|
|
143
157
|
}
|
|
144
158
|
|
|
145
|
-
if (!content.includes("
|
|
159
|
+
if (!content.includes("wreq-js")) {
|
|
146
160
|
throw new Error(
|
|
147
|
-
`${label} must include
|
|
161
|
+
`${label} must include wreq-js stealth runtime guidance for TLS/browser bounties.`,
|
|
148
162
|
);
|
|
149
163
|
}
|
|
150
164
|
|
|
@@ -154,6 +168,12 @@ function assertPublicSmokeDocs(label: string, content: string): void {
|
|
|
154
168
|
);
|
|
155
169
|
}
|
|
156
170
|
|
|
171
|
+
if (!content.includes("Chrome, Firefox, and Safari")) {
|
|
172
|
+
throw new Error(
|
|
173
|
+
`${label} must document the browser families supported by the TypeScript stealth runtime.`,
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
157
177
|
if (
|
|
158
178
|
!content.includes('browser.engine: "playwright-stealth"') ||
|
|
159
179
|
!content.includes("nodriver")
|
|
@@ -59,6 +59,21 @@ try {
|
|
|
59
59
|
);
|
|
60
60
|
|
|
61
61
|
run("bun", ["install"], consumerDir);
|
|
62
|
+
run(
|
|
63
|
+
"bun",
|
|
64
|
+
[
|
|
65
|
+
"--eval",
|
|
66
|
+
[
|
|
67
|
+
'import { resolveProxy } from "@apifuse/provider-sdk";',
|
|
68
|
+
'if (typeof resolveProxy !== "function") throw new Error("resolveProxy is not exported");',
|
|
69
|
+
'const resolved = await resolveProxy({ proxy: "http://127.0.0.1:8080" });',
|
|
70
|
+
'if (resolved.url !== "http://127.0.0.1:8080") throw new Error("resolveProxy returned the wrong URL");',
|
|
71
|
+
'console.log("packed root resolveProxy export OK");',
|
|
72
|
+
].join("\n"),
|
|
73
|
+
],
|
|
74
|
+
consumerDir,
|
|
75
|
+
);
|
|
76
|
+
smokePackedStealthNative(consumerDir);
|
|
62
77
|
|
|
63
78
|
const cliBin = join(consumerDir, "node_modules", ".bin", "apifuse");
|
|
64
79
|
if (!existsSync(cliBin)) {
|
|
@@ -202,6 +217,43 @@ function run(command: string, args: string[], cwd: string): void {
|
|
|
202
217
|
}
|
|
203
218
|
}
|
|
204
219
|
|
|
220
|
+
function smokePackedStealthNative(consumerDir: string): void {
|
|
221
|
+
run(
|
|
222
|
+
"bun",
|
|
223
|
+
[
|
|
224
|
+
"--eval",
|
|
225
|
+
[
|
|
226
|
+
'import { createServer } from "node:http";',
|
|
227
|
+
'import { createStealthClient } from "@apifuse/provider-sdk/runtime/stealth";',
|
|
228
|
+
"const server = createServer((_request, response) => {",
|
|
229
|
+
' response.setHeader("set-cookie", "pack_native_cookie=landed; Path=/");',
|
|
230
|
+
' response.end("packed native stealth ok");',
|
|
231
|
+
"});",
|
|
232
|
+
"await new Promise((resolve, reject) => {",
|
|
233
|
+
' server.once("error", reject);',
|
|
234
|
+
' server.listen(0, "127.0.0.1", resolve);',
|
|
235
|
+
"});",
|
|
236
|
+
"const address = server.address();",
|
|
237
|
+
'if (!address || typeof address === "string") throw new Error("Local server has no TCP address");',
|
|
238
|
+
'const baseUrl = "http://127.0.0.1:" + address.port;',
|
|
239
|
+
'const session = createStealthClient(baseUrl).createSession({ profile: "safari-17" });',
|
|
240
|
+
"try {",
|
|
241
|
+
' const response = await session.fetch("/native");',
|
|
242
|
+
' if (response.body !== "packed native stealth ok") throw new Error("Unexpected stealth body: " + response.body);',
|
|
243
|
+
' if (session.cookies.get("pack_native_cookie", baseUrl + "/native") !== "landed") {',
|
|
244
|
+
' throw new Error("Packed stealth Set-Cookie did not land in the SDK jar");',
|
|
245
|
+
" }",
|
|
246
|
+
' console.log("packed native Safari stealth request OK");',
|
|
247
|
+
"} finally {",
|
|
248
|
+
" session.close();",
|
|
249
|
+
" await new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));",
|
|
250
|
+
"}",
|
|
251
|
+
].join("\n"),
|
|
252
|
+
],
|
|
253
|
+
consumerDir,
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
205
257
|
function assertGeneratedReadme(providerDir: string): void {
|
|
206
258
|
const readme = readFileSync(join(providerDir, "README.md"), "utf8");
|
|
207
259
|
if (!readme.includes('"requestId":"req_local_ping"')) {
|
|
@@ -213,8 +265,11 @@ function assertGeneratedReadme(providerDir: string): void {
|
|
|
213
265
|
if (!readme.includes("bunx playwright install chromium")) {
|
|
214
266
|
throw new Error("Generated README is missing browser runtime troubleshooting guidance.");
|
|
215
267
|
}
|
|
216
|
-
if (!readme.includes("
|
|
217
|
-
throw new Error("Generated README is missing
|
|
268
|
+
if (!readme.includes("wreq-js")) {
|
|
269
|
+
throw new Error("Generated README is missing wreq-js stealth runtime guidance.");
|
|
270
|
+
}
|
|
271
|
+
if (!readme.includes("Chrome, Firefox, and Safari")) {
|
|
272
|
+
throw new Error("Generated README is missing supported stealth browser families.");
|
|
218
273
|
}
|
|
219
274
|
if (!readme.includes("bun run submit-check")) {
|
|
220
275
|
throw new Error("Generated README must document the submit-check pre-submission workflow.");
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
// 3. A Node (not bun) ESM runtime import — node does not tolerate
|
|
19
19
|
// extensionless relative specifiers, bun does.
|
|
20
20
|
//
|
|
21
|
-
//
|
|
21
|
+
// Deliberate negative controls prove the fixture compiler actually fails on
|
|
22
22
|
// type errors, so this check cannot rot into a false-positive green.
|
|
23
23
|
|
|
24
24
|
import { execFileSync, spawnSync } from "node:child_process";
|
|
@@ -36,6 +36,263 @@ const PACK_RESULT_SCHEMA = z.array(
|
|
|
36
36
|
const KEEP_TEMP = process.env.APIFUSE__PACK_TYPES__KEEP_TEMP === "1";
|
|
37
37
|
const sdkRoot = process.cwd();
|
|
38
38
|
|
|
39
|
+
const NEGATIVE_CONTROLS = [
|
|
40
|
+
{
|
|
41
|
+
filename: "negative-control-provider-error-name.ts",
|
|
42
|
+
expectedCode: "TS2322",
|
|
43
|
+
description: "ProviderError.name remains a string",
|
|
44
|
+
source: [
|
|
45
|
+
'import { ProviderError } from "@apifuse/provider-sdk";',
|
|
46
|
+
"",
|
|
47
|
+
"// Intentionally wrong: name is a string. If this file compiles, the",
|
|
48
|
+
"// fixture consumer is not actually type-checking and the guard is void.",
|
|
49
|
+
'export const mustNotCompile: number = new ProviderError("boom").name;',
|
|
50
|
+
"",
|
|
51
|
+
].join("\n"),
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
filename: "negative-control-unknown-resolver-vendor.ts",
|
|
55
|
+
expectedCode: "TS2322",
|
|
56
|
+
description: "ProviderResolverConfig rejects an unknown vendor",
|
|
57
|
+
source: [
|
|
58
|
+
'import type { ProviderResolverConfig } from "@apifuse/provider-sdk";',
|
|
59
|
+
"",
|
|
60
|
+
"export const mustNotCompile: ProviderResolverConfig = {",
|
|
61
|
+
'\tvendors: ["unknown-vendor"],',
|
|
62
|
+
'\tkinds: ["turnstile"],',
|
|
63
|
+
"};",
|
|
64
|
+
"",
|
|
65
|
+
].join("\n"),
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
filename: "negative-control-define-provider-resolver-vendor.ts",
|
|
69
|
+
expectedCode: "TS2322",
|
|
70
|
+
description: "defineProvider rejects an unknown resolver vendor",
|
|
71
|
+
source: [
|
|
72
|
+
'import { defineProvider, z } from "@apifuse/provider-sdk";',
|
|
73
|
+
"",
|
|
74
|
+
"export const mustNotCompile = defineProvider({",
|
|
75
|
+
'\tid: "invalid-resolver-vendor",',
|
|
76
|
+
'\tversion: "1.0.0",',
|
|
77
|
+
'\truntime: "standard",',
|
|
78
|
+
'\tresolver: { vendors: ["unknown-vendor"], kinds: ["turnstile"] },',
|
|
79
|
+
'\tmeta: { displayName: "Invalid Resolver Vendor", descriptionKey: "meta.description", category: "test" },',
|
|
80
|
+
"})({",
|
|
81
|
+
"\toperations: {",
|
|
82
|
+
"\t\tprobe: {",
|
|
83
|
+
"\t\t\tinput: z.object({}),",
|
|
84
|
+
"\t\t\toutput: z.object({ ok: z.boolean() }),",
|
|
85
|
+
"\t\t\thandler: async () => ({ ok: true }),",
|
|
86
|
+
'\t\t\thealthCheckUnsupported: { reason: "type fixture" },',
|
|
87
|
+
"\t\t},",
|
|
88
|
+
"\t},",
|
|
89
|
+
"});",
|
|
90
|
+
"",
|
|
91
|
+
].join("\n"),
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
filename: "negative-control-unknown-challenge-kind.ts",
|
|
95
|
+
expectedCode: "TS2820",
|
|
96
|
+
description: "ProviderChallenge rejects an unknown challenge kind",
|
|
97
|
+
source: [
|
|
98
|
+
'import type { ProviderChallenge } from "@apifuse/provider-sdk";',
|
|
99
|
+
"",
|
|
100
|
+
"export const mustNotCompile: ProviderChallenge = {",
|
|
101
|
+
'\tkind: "funcaptcha",',
|
|
102
|
+
'\tsiteKey: "key",',
|
|
103
|
+
'\tpageUrl: "https://example.com",',
|
|
104
|
+
"};",
|
|
105
|
+
"",
|
|
106
|
+
].join("\n"),
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
filename: "negative-control-provider-challenge-kind.ts",
|
|
110
|
+
expectedCode: "TS2820",
|
|
111
|
+
description: "ProviderChallengeKind rejects an unknown challenge kind",
|
|
112
|
+
source: [
|
|
113
|
+
'import type { ProviderChallengeKind } from "@apifuse/provider-sdk";',
|
|
114
|
+
"",
|
|
115
|
+
'export const mustNotCompile: ProviderChallengeKind = "funcaptcha";',
|
|
116
|
+
"",
|
|
117
|
+
].join("\n"),
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
filename: "negative-control-cookie-solution-token-read.ts",
|
|
121
|
+
expectedCode: "TS2339",
|
|
122
|
+
description: "ChallengeSolution requires narrowing before reading token",
|
|
123
|
+
source: [
|
|
124
|
+
'import type { ChallengeSolution } from "@apifuse/provider-sdk";',
|
|
125
|
+
"",
|
|
126
|
+
"export function mustNotCompile(solution: ChallengeSolution): string {",
|
|
127
|
+
"\treturn solution.token;",
|
|
128
|
+
"}",
|
|
129
|
+
"",
|
|
130
|
+
].join("\n"),
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
filename: "negative-control-cookie-challenge-site-key.ts",
|
|
134
|
+
expectedCode: "TS2353",
|
|
135
|
+
description: "cookie-family challenges reject token-family fields",
|
|
136
|
+
source: [
|
|
137
|
+
'import type { ProviderChallenge } from "@apifuse/provider-sdk";',
|
|
138
|
+
"",
|
|
139
|
+
"export const mustNotCompile: ProviderChallenge = {",
|
|
140
|
+
'\tkind: "cloudflare_interstitial",',
|
|
141
|
+
'\tpageUrl: "https://example.com",',
|
|
142
|
+
'\tsiteKey: "not-allowed",',
|
|
143
|
+
"};",
|
|
144
|
+
"",
|
|
145
|
+
].join("\n"),
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
filename: "negative-control-aws-waf-site-key-type.ts",
|
|
149
|
+
expectedCode: "TS2322",
|
|
150
|
+
description: "aws_waf siteKey must be a string",
|
|
151
|
+
source: [
|
|
152
|
+
'import type { ProviderChallenge } from "@apifuse/provider-sdk";',
|
|
153
|
+
"",
|
|
154
|
+
"export const mustNotCompile: ProviderChallenge = {",
|
|
155
|
+
'\tkind: "aws_waf",',
|
|
156
|
+
'\tpageUrl: "https://example.com",',
|
|
157
|
+
"\tsiteKey: 123,",
|
|
158
|
+
"};",
|
|
159
|
+
"",
|
|
160
|
+
].join("\n"),
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
filename: "negative-control-aws-waf-context-type.ts",
|
|
164
|
+
expectedCode: "TS2322",
|
|
165
|
+
description: "aws_waf context must be a string",
|
|
166
|
+
source: [
|
|
167
|
+
'import type { ProviderChallenge } from "@apifuse/provider-sdk";',
|
|
168
|
+
"",
|
|
169
|
+
"export const mustNotCompile: ProviderChallenge = {",
|
|
170
|
+
'\tkind: "aws_waf",',
|
|
171
|
+
'\tpageUrl: "https://example.com",',
|
|
172
|
+
"\tcontext: 123,",
|
|
173
|
+
"};",
|
|
174
|
+
"",
|
|
175
|
+
].join("\n"),
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
filename: "negative-control-aws-waf-unknown-field.ts",
|
|
179
|
+
expectedCode: "TS2353",
|
|
180
|
+
description: "aws_waf rejects fields it does not declare",
|
|
181
|
+
source: [
|
|
182
|
+
'import type { ProviderChallenge } from "@apifuse/provider-sdk";',
|
|
183
|
+
"",
|
|
184
|
+
"export const mustNotCompile: ProviderChallenge = {",
|
|
185
|
+
'\tkind: "aws_waf",',
|
|
186
|
+
'\tpageUrl: "https://example.com",',
|
|
187
|
+
"\tunknownField: 1,",
|
|
188
|
+
"};",
|
|
189
|
+
"",
|
|
190
|
+
].join("\n"),
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
filename: "negative-control-recaptcha-v3-action.ts",
|
|
194
|
+
expectedCode: "TS2322",
|
|
195
|
+
description: "recaptcha_v3 challenges require action",
|
|
196
|
+
source: [
|
|
197
|
+
'import type { ProviderChallenge } from "@apifuse/provider-sdk";',
|
|
198
|
+
"",
|
|
199
|
+
"export const mustNotCompile: ProviderChallenge = {",
|
|
200
|
+
'\tkind: "recaptcha_v3",',
|
|
201
|
+
'\tsiteKey: "key",',
|
|
202
|
+
'\tpageUrl: "https://example.com",',
|
|
203
|
+
"};",
|
|
204
|
+
"",
|
|
205
|
+
].join("\n"),
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
filename: "negative-control-browser-cookie-expires.ts",
|
|
209
|
+
expectedCode: "TS2322",
|
|
210
|
+
description: "BrowserCookie.expires remains optional for session cookies",
|
|
211
|
+
source: [
|
|
212
|
+
'import type { BrowserCookie } from "@apifuse/provider-sdk";',
|
|
213
|
+
"",
|
|
214
|
+
"export function mustNotCompile(cookie: BrowserCookie): number {",
|
|
215
|
+
"\treturn cookie.expires;",
|
|
216
|
+
"}",
|
|
217
|
+
"",
|
|
218
|
+
].join("\n"),
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
filename: "negative-control-resolver-runtime-identity.ts",
|
|
222
|
+
expectedCode: "TS2353",
|
|
223
|
+
description: "ResolverRuntimeOptions does not accept caller-synthesized identity",
|
|
224
|
+
source: [
|
|
225
|
+
'import type { ResolverRuntimeOptions } from "@apifuse/provider-sdk";',
|
|
226
|
+
"",
|
|
227
|
+
"export const mustNotCompile: ResolverRuntimeOptions = {",
|
|
228
|
+
'\tidentity: "caller-controlled",',
|
|
229
|
+
"};",
|
|
230
|
+
"",
|
|
231
|
+
].join("\n"),
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
filename: "negative-control-resolver-runtime-adapter-factories.ts",
|
|
235
|
+
expectedCode: "TS2353",
|
|
236
|
+
description: "ResolverRuntimeOptions does not accept caller-supplied adapter factories",
|
|
237
|
+
source: [
|
|
238
|
+
'import type { ResolverRuntimeOptions } from "@apifuse/provider-sdk";',
|
|
239
|
+
"",
|
|
240
|
+
"export const mustNotCompile: ResolverRuntimeOptions = {",
|
|
241
|
+
"\tadapterFactories: {},",
|
|
242
|
+
"};",
|
|
243
|
+
"",
|
|
244
|
+
].join("\n"),
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
filename: "negative-control-resolver-default-user-agent-test-seam.ts",
|
|
248
|
+
expectedCode: "TS2305",
|
|
249
|
+
description: "the resolver subpath does not expose its default user-agent test seam",
|
|
250
|
+
source: [
|
|
251
|
+
'import { swapResolverDefaultUserAgentForTests } from "@apifuse/provider-sdk/runtime/resolver";',
|
|
252
|
+
"",
|
|
253
|
+
"export const mustNotCompile = swapResolverDefaultUserAgentForTests;",
|
|
254
|
+
"",
|
|
255
|
+
].join("\n"),
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
filename: "negative-control-cache-reset-test-seam.ts",
|
|
259
|
+
expectedCode: "TS2305",
|
|
260
|
+
description: "the package root does not expose its cache-reset test seam",
|
|
261
|
+
source: [
|
|
262
|
+
'import { resetProviderCacheForTests } from "@apifuse/provider-sdk";',
|
|
263
|
+
"",
|
|
264
|
+
"export const mustNotCompile = resetProviderCacheForTests;",
|
|
265
|
+
"",
|
|
266
|
+
].join("\n"),
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
filename: "negative-control-resolver-runtime-allowed-hosts.ts",
|
|
270
|
+
expectedCode: "TS2322",
|
|
271
|
+
description: "ResolverRuntimeOptions.allowedHosts accepts only host strings",
|
|
272
|
+
source: [
|
|
273
|
+
'import type { ResolverRuntimeOptions } from "@apifuse/provider-sdk";',
|
|
274
|
+
"",
|
|
275
|
+
"export const mustNotCompile: ResolverRuntimeOptions = {",
|
|
276
|
+
'\tallowedHosts: ["example.com", 42],',
|
|
277
|
+
"};",
|
|
278
|
+
"",
|
|
279
|
+
].join("\n"),
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
filename: "negative-control-resolver-signal.ts",
|
|
283
|
+
expectedCode: "TS2345",
|
|
284
|
+
description: "ResolverContext.solve accepts only AbortSignal as its optional signal",
|
|
285
|
+
source: [
|
|
286
|
+
'import type { ProviderChallenge, ResolverContext } from "@apifuse/provider-sdk";',
|
|
287
|
+
"",
|
|
288
|
+
"declare const resolver: ResolverContext;",
|
|
289
|
+
"declare const challenge: ProviderChallenge;",
|
|
290
|
+
"resolver.solve(challenge, { aborted: false });",
|
|
291
|
+
"",
|
|
292
|
+
].join("\n"),
|
|
293
|
+
},
|
|
294
|
+
] as const;
|
|
295
|
+
|
|
39
296
|
const tempRoot = mkdtempSync(join(tmpdir(), "apifuse-provider-sdk-pack-types-"));
|
|
40
297
|
const packDir = join(tempRoot, "pack");
|
|
41
298
|
const consumerDir = join(tempRoot, "consumer");
|
|
@@ -123,12 +380,16 @@ function setUpFixtureConsumer(consumerDir: string, tarballPath: string): void {
|
|
|
123
380
|
writeFileSync(
|
|
124
381
|
join(consumerDir, "consumer.ts"),
|
|
125
382
|
[
|
|
126
|
-
'import { ProviderError, SessionExpiredError, z } from "@apifuse/provider-sdk";',
|
|
383
|
+
'import { defineProvider, ProviderError, resolveProxy, SessionExpiredError, z } from "@apifuse/provider-sdk";',
|
|
384
|
+
'import { invalidateResolverSolution } from "@apifuse/provider-sdk/runtime/resolver";',
|
|
385
|
+
'import type { BrowserCookie, ChallengeSolution, NativeContext, NativeNetworkClient, NativeNetworkConnection, NativeProviderConfig, NativeTcpEgressGrant, ProviderChallenge, ProviderContext, ProviderFileRef, ProviderFilesContext, ProviderResolvedFile, ProviderResolverConfig, ResolverContext, ResolverRuntimeOptions } from "@apifuse/provider-sdk";',
|
|
386
|
+
'import type { ProxyProtocol, ProxyResolutionOptions, ProxyResolutionSource, ProxyVendorName, RequestOptions, ResolvedProxyConfig } from "@apifuse/provider-sdk";',
|
|
127
387
|
'import { defineCredentialsAuth } from "@apifuse/provider-sdk/provider";',
|
|
388
|
+
'import type { NativeNetworkClient as ProviderEntryNativeNetworkClient, ProviderFilesContext as ProviderEntryFilesContext } from "@apifuse/provider-sdk/provider";',
|
|
128
389
|
'import { extractProviderContract } from "@apifuse/provider-sdk/contract";',
|
|
129
390
|
'import { AUTH_TURN_SCHEMA } from "@apifuse/provider-sdk/auth-turn";',
|
|
130
391
|
'import { serve } from "@apifuse/provider-sdk/server";',
|
|
131
|
-
'import { runStandardTests } from "@apifuse/provider-sdk/testing";',
|
|
392
|
+
'import { resetProviderCacheForTests, runStandardTests } from "@apifuse/provider-sdk/testing";',
|
|
132
393
|
"",
|
|
133
394
|
"// ProviderError must keep its inherited Error members under nodenext.",
|
|
134
395
|
"// When dist d.ts imports fail to resolve, the class type degrades and",
|
|
@@ -143,6 +404,44 @@ function setUpFixtureConsumer(consumerDir: string, tarballPath: string): void {
|
|
|
143
404
|
"// Re-exported zod must keep .refine() callback parameter inference.",
|
|
144
405
|
"const refined = z.object({ shopId: z.string() }).refine((value) => value.shopId.length > 0);",
|
|
145
406
|
'const refinedString = z.string().refine((value) => value.startsWith("tabelog:"));',
|
|
407
|
+
'const proxyOptions: ProxyResolutionOptions = { proxyPolicy: { mode: "disabled" } };',
|
|
408
|
+
'const proxyProtocol: ProxyProtocol = "http";',
|
|
409
|
+
"const proxyResult: Promise<ResolvedProxyConfig> = resolveProxy(proxyOptions);",
|
|
410
|
+
'const proxySource: ProxyResolutionSource = "smartproxy-allocator";',
|
|
411
|
+
'const proxyVendor: ProxyVendorName = "smartproxy";',
|
|
412
|
+
'const requestFile: ProviderFileRef = { type: "request_file", id: "photo", filename: "photo.jpg", mime_type: "image/jpeg", size: 4 };',
|
|
413
|
+
'const resolvedFile: ProviderResolvedFile = { type: "request_file", id: requestFile.id, filename: requestFile.filename, size: requestFile.size, sha256: requestFile.sha256, mimeType: requestFile.mime_type, arrayBuffer: async () => new ArrayBuffer(0), bytes: async () => new Uint8Array(), stream: () => new ReadableStream<Uint8Array>() };',
|
|
414
|
+
"const files: ProviderFilesContext = { has: () => true, resolve: async () => resolvedFile };",
|
|
415
|
+
"const providerEntryFiles: ProviderEntryFilesContext = files;",
|
|
416
|
+
"const connection: NativeNetworkConnection = { read: async () => null, write: async () => {}, close: async () => {} };",
|
|
417
|
+
"const network: NativeNetworkClient = { connectTcp: async () => connection, connectTls: async () => connection, grantTcpEgress: () => ({ revoke() {} }) };",
|
|
418
|
+
"const providerEntryNetwork: ProviderEntryNativeNetworkClient = network;",
|
|
419
|
+
"const nativeContext: NativeContext = { network };",
|
|
420
|
+
'const grant: NativeTcpEgressGrant = network.grantTcpEgress({ sourceHost: "booking-loco.kakao.com", sourcePort: 443, host: "loco.kakao.com", port: 5228, tls: "disabled" });',
|
|
421
|
+
'const nativeConfig: NativeProviderConfig = { network: { tcp: [{ host: "booking-loco.kakao.com", ports: [443], tls: "required" }] } };',
|
|
422
|
+
"const providerContext = undefined as unknown as ProviderContext;",
|
|
423
|
+
"const optionalFiles: ProviderFilesContext | undefined = providerContext.files;",
|
|
424
|
+
"const native: NativeContext = providerContext.native;",
|
|
425
|
+
"export const providerResolver: ResolverContext = providerContext.resolver;",
|
|
426
|
+
'export const resolverRuntimeOptions: ResolverRuntimeOptions = { allowedHosts: ["example.com"], cache: providerContext.cache };',
|
|
427
|
+
"",
|
|
428
|
+
'export const turnstileChallenge: ProviderChallenge = { kind: "turnstile", siteKey: "key", pageUrl: "https://example.com" };',
|
|
429
|
+
"export const abortableResolverSolve = providerResolver.solve(turnstileChallenge, new AbortController().signal);",
|
|
430
|
+
'export const recaptchaV2Challenge: ProviderChallenge = { kind: "recaptcha_v2", siteKey: "key", pageUrl: "https://example.com" };',
|
|
431
|
+
'export const recaptchaV3Challenge: ProviderChallenge = { kind: "recaptcha_v3", siteKey: "key", pageUrl: "https://example.com", action: "login" };',
|
|
432
|
+
'export const hcaptchaChallenge: ProviderChallenge = { kind: "hcaptcha", siteKey: "key", pageUrl: "https://example.com" };',
|
|
433
|
+
'export const cloudflareInterstitialChallenge: ProviderChallenge = { kind: "cloudflare_interstitial", pageUrl: "https://example.com", blockedHtml: "<html></html>" };',
|
|
434
|
+
'export const awsWafChallenge: ProviderChallenge = { kind: "aws_waf", pageUrl: "https://example.com", captchaScript: "script" };',
|
|
435
|
+
'export const tokenSolution: ChallengeSolution = { form: "token", token: "solved-token" };',
|
|
436
|
+
'export const cookieSolution: ChallengeSolution = { form: "cookies", cookies: { cf_clearance: "clearance" }, userAgent: "fixture-agent" };',
|
|
437
|
+
"export const invalidation = invalidateResolverSolution(providerContext.resolver, awsWafChallenge, cookieSolution);",
|
|
438
|
+
'export const resolverConfig: ProviderResolverConfig = { vendors: ["browser", "capsolver"], kinds: ["cloudflare_interstitial", "turnstile"] };',
|
|
439
|
+
'export const resolverProvider = defineProvider({ id: "pack-types-resolver", version: "1.0.0", runtime: "standard", resolver: resolverConfig, meta: { displayName: "Pack Types Resolver", descriptionKey: "meta.description", category: "test" } })({ operations: { probe: { input: z.object({}), output: z.object({ ok: z.boolean() }), handler: async () => ({ ok: true }), healthCheckUnsupported: { reason: "type fixture" } } } });',
|
|
440
|
+
"export const resolverContext: ResolverContext = { solve: async () => tokenSolution };",
|
|
441
|
+
'export const browserCookie: BrowserCookie = { name: "persistent-id", value: "persistent-token", domain: "example.com", path: "/", expires: 1786698176, httpOnly: true, secure: true };',
|
|
442
|
+
'const browserPage = undefined as unknown as Awaited<ReturnType<ProviderContext["browser"]["newPage"]>>;',
|
|
443
|
+
"export const browserCookies: Promise<readonly BrowserCookie[]> = browserPage.cookies();",
|
|
444
|
+
'const queryCredentialOptions: RequestOptions = { sensitiveParams: { serviceKey: "type-test-key" } };',
|
|
146
445
|
"",
|
|
147
446
|
"export const witnesses = {",
|
|
148
447
|
" inheritedName,",
|
|
@@ -152,27 +451,39 @@ function setUpFixtureConsumer(consumerDir: string, tarballPath: string): void {
|
|
|
152
451
|
" sessionExpired,",
|
|
153
452
|
" refined,",
|
|
154
453
|
" refinedString,",
|
|
454
|
+
" proxyResult,",
|
|
455
|
+
" proxyProtocol,",
|
|
456
|
+
" proxySource,",
|
|
457
|
+
" proxyVendor,",
|
|
458
|
+
" requestFile,",
|
|
459
|
+
" resolvedFile,",
|
|
460
|
+
" files,",
|
|
461
|
+
" providerEntryFiles,",
|
|
462
|
+
" connection,",
|
|
463
|
+
" network,",
|
|
464
|
+
" providerEntryNetwork,",
|
|
465
|
+
" nativeContext,",
|
|
466
|
+
" grant,",
|
|
467
|
+
" nativeConfig,",
|
|
468
|
+
" optionalFiles,",
|
|
469
|
+
" native,",
|
|
470
|
+
" browserCookie,",
|
|
471
|
+
" browserCookies,",
|
|
472
|
+
" queryCredentialOptions,",
|
|
155
473
|
" defineCredentialsAuth,",
|
|
156
474
|
" extractProviderContract,",
|
|
157
475
|
" AUTH_TURN_SCHEMA,",
|
|
158
476
|
" serve,",
|
|
477
|
+
" resetProviderCacheForTests,",
|
|
159
478
|
" runStandardTests,",
|
|
160
479
|
"};",
|
|
161
480
|
"",
|
|
162
481
|
].join("\n"),
|
|
163
482
|
);
|
|
164
483
|
|
|
165
|
-
|
|
166
|
-
join(consumerDir,
|
|
167
|
-
|
|
168
|
-
'import { ProviderError } from "@apifuse/provider-sdk";',
|
|
169
|
-
"",
|
|
170
|
-
"// Intentionally wrong: name is a string. If this file compiles, the",
|
|
171
|
-
"// fixture consumer is not actually type-checking and the guard is void.",
|
|
172
|
-
'export const mustNotCompile: number = new ProviderError("boom").name;',
|
|
173
|
-
"",
|
|
174
|
-
].join("\n"),
|
|
175
|
-
);
|
|
484
|
+
for (const negativeControl of NEGATIVE_CONTROLS) {
|
|
485
|
+
writeFileSync(join(consumerDir, negativeControl.filename), negativeControl.source);
|
|
486
|
+
}
|
|
176
487
|
}
|
|
177
488
|
|
|
178
489
|
function compileFixtureConsumer(consumerDir: string): void {
|
|
@@ -184,31 +495,38 @@ function compileFixtureConsumer(consumerDir: string): void {
|
|
|
184
495
|
}
|
|
185
496
|
|
|
186
497
|
function assertNegativeControlFails(consumerDir: string): void {
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
if (result.status === 0) {
|
|
204
|
-
throw new Error(
|
|
205
|
-
"Negative control compiled cleanly: the fixture consumer is not detecting type errors, so the nodenext guard proves nothing.",
|
|
206
|
-
);
|
|
207
|
-
}
|
|
208
|
-
if (!`${result.stdout}\n${result.stderr}`.includes("TS2322")) {
|
|
209
|
-
throw new Error(
|
|
210
|
-
`Negative control failed for an unexpected reason (wanted TS2322):\n${result.stdout}\n${result.stderr}`,
|
|
498
|
+
for (const negativeControl of NEGATIVE_CONTROLS) {
|
|
499
|
+
const result = spawnSync(
|
|
500
|
+
"bun",
|
|
501
|
+
[
|
|
502
|
+
join(consumerDir, "node_modules", ".bin", "tsc"),
|
|
503
|
+
"--target",
|
|
504
|
+
"ES2022",
|
|
505
|
+
"--module",
|
|
506
|
+
"nodenext",
|
|
507
|
+
"--moduleResolution",
|
|
508
|
+
"nodenext",
|
|
509
|
+
"--strict",
|
|
510
|
+
"--noEmit",
|
|
511
|
+
negativeControl.filename,
|
|
512
|
+
],
|
|
513
|
+
{ cwd: consumerDir, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] },
|
|
211
514
|
);
|
|
515
|
+
if (result.status === 0) {
|
|
516
|
+
throw new Error(
|
|
517
|
+
'Negative control "' +
|
|
518
|
+
negativeControl.description +
|
|
519
|
+
'" (' +
|
|
520
|
+
negativeControl.filename +
|
|
521
|
+
") compiled cleanly: the fixture consumer is not detecting its error, so the guard proves nothing.",
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
const output = `${result.stdout}\n${result.stderr}`;
|
|
525
|
+
if (!output.includes(negativeControl.expectedCode)) {
|
|
526
|
+
throw new Error(
|
|
527
|
+
`Negative control "${negativeControl.description}" (${negativeControl.filename}) failed for an unexpected reason (wanted ${negativeControl.expectedCode}):\n${output}`,
|
|
528
|
+
);
|
|
529
|
+
}
|
|
212
530
|
}
|
|
213
531
|
}
|
|
214
532
|
|