@apifuse/provider-sdk 2.0.0-beta.1 → 2.1.0-beta.1
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 +93 -0
- package/CHANGELOG.md +21 -0
- package/README.md +133 -28
- package/bin/apifuse-check.ts +78 -71
- package/bin/apifuse-create.ts +12 -0
- package/bin/apifuse-dev.ts +24 -61
- package/bin/apifuse-pack-check.ts +87 -0
- package/bin/apifuse-pack-smoke.ts +122 -0
- package/bin/apifuse-perf.ts +33 -32
- package/bin/apifuse-record.ts +17 -7
- package/bin/apifuse-test.ts +6 -4
- package/bin/apifuse.ts +36 -35
- package/package.json +29 -9
- package/src/ceremonies/index.ts +768 -0
- package/src/cli/commands.ts +87 -0
- package/src/cli/create.ts +845 -0
- package/src/cli/templates/provider/Dockerfile.tpl +7 -0
- package/src/cli/templates/provider/README.md.tpl +41 -0
- package/src/cli/templates/provider/dev.ts.tpl +5 -0
- package/src/cli/templates/provider/index.test.ts.tpl +13 -0
- package/src/cli/templates/provider/index.ts.tpl +58 -0
- package/src/cli/templates/provider/start.ts.tpl +5 -0
- package/src/config/loader.ts +61 -1
- package/src/define.ts +565 -41
- package/src/dev.ts +2 -6
- package/src/errors.ts +42 -0
- package/src/index.ts +44 -38
- package/src/lint.ts +574 -0
- package/src/provider.ts +13 -0
- package/src/runtime/auth-flow.ts +67 -0
- package/src/runtime/credential.ts +95 -0
- package/src/runtime/env.ts +13 -0
- package/src/runtime/executor.ts +13 -14
- package/src/runtime/http.ts +36 -12
- package/src/runtime/insights.ts +3 -3
- package/src/runtime/key-derivation.ts +122 -0
- package/src/runtime/keyring.ts +148 -0
- package/src/runtime/namespace.ts +33 -0
- package/src/runtime/prevalidate.ts +252 -0
- package/src/runtime/tls.ts +41 -17
- package/src/runtime/waterfall.ts +0 -1
- package/src/schema.ts +77 -0
- package/src/serve.ts +1 -664
- package/src/server/index.ts +22 -0
- package/src/server/serve.ts +624 -0
- package/src/server/types.ts +78 -0
- package/src/stealth/profiles.ts +10 -93
- package/src/testing/run.ts +391 -32
- package/src/types.ts +390 -41
- package/bin/apifuse-init.ts +0 -387
- package/src/__tests__/auth.test.ts +0 -396
- package/src/__tests__/browser-auth.test.ts +0 -180
- package/src/__tests__/browser.test.ts +0 -632
- package/src/__tests__/define.test.ts +0 -225
- package/src/__tests__/errors.test.ts +0 -69
- package/src/__tests__/executor.test.ts +0 -214
- package/src/__tests__/http.test.ts +0 -238
- package/src/__tests__/insights.test.ts +0 -210
- package/src/__tests__/instrumentation.test.ts +0 -290
- package/src/__tests__/otlp.test.ts +0 -141
- package/src/__tests__/perf.test.ts +0 -60
- package/src/__tests__/providers-yaml.test.ts +0 -135
- package/src/__tests__/proxy.test.ts +0 -359
- package/src/__tests__/recipes.test.ts +0 -36
- package/src/__tests__/serve.test.ts +0 -233
- package/src/__tests__/session.test.ts +0 -231
- package/src/__tests__/state.test.ts +0 -100
- package/src/__tests__/stealth.test.ts +0 -57
- package/src/__tests__/testing.test.ts +0 -97
- package/src/__tests__/tls.test.ts +0 -345
- package/src/__tests__/types.test.ts +0 -142
- package/src/__tests__/utils.test.ts +0 -62
- package/src/__tests__/waterfall.test.ts +0 -270
- package/src/config/providers-yaml.ts +0 -370
- package/src/index.test.ts +0 -1
- package/src/protocol.ts +0 -183
- package/src/runtime/auth.ts +0 -245
- package/src/runtime/session.ts +0 -573
- package/src/runtime/state.ts +0 -124
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { execFileSync } from "node:child_process";
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
|
|
7
|
+
const PACK_RESULT_SCHEMA = z.array(
|
|
8
|
+
z.object({
|
|
9
|
+
filename: z.string(),
|
|
10
|
+
files: z
|
|
11
|
+
.array(
|
|
12
|
+
z.object({
|
|
13
|
+
path: z.string(),
|
|
14
|
+
}),
|
|
15
|
+
)
|
|
16
|
+
.optional(),
|
|
17
|
+
}),
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const raw = execFileSync("npm", ["pack", "--json", "--dry-run"], {
|
|
21
|
+
cwd: process.cwd(),
|
|
22
|
+
encoding: "utf8",
|
|
23
|
+
});
|
|
24
|
+
const parsed = PACK_RESULT_SCHEMA.parse(JSON.parse(raw));
|
|
25
|
+
const first = parsed[0];
|
|
26
|
+
|
|
27
|
+
if (!first) {
|
|
28
|
+
throw new Error("npm pack --json --dry-run returned no package metadata.");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const filePaths = (first.files ?? []).map((file) => file.path);
|
|
32
|
+
const requiredPaths = [
|
|
33
|
+
"bin/apifuse.ts",
|
|
34
|
+
"bin/apifuse-create.ts",
|
|
35
|
+
"bin/apifuse-pack-smoke.ts",
|
|
36
|
+
"src/cli/create.ts",
|
|
37
|
+
"src/cli/templates/provider/index.ts.tpl",
|
|
38
|
+
"src/cli/templates/provider/README.md.tpl",
|
|
39
|
+
];
|
|
40
|
+
const forbiddenMatches = filePaths.filter(
|
|
41
|
+
(path) =>
|
|
42
|
+
path.startsWith("src/__tests__/") ||
|
|
43
|
+
path === "src/index.test.ts" ||
|
|
44
|
+
path === "bin/apifuse-init.ts",
|
|
45
|
+
);
|
|
46
|
+
const missingRequiredPaths = requiredPaths.filter(
|
|
47
|
+
(path) => !filePaths.includes(path),
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
if (forbiddenMatches.length > 0) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`Packed artifact still includes forbidden files:\n${forbiddenMatches.join("\n")}`,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (missingRequiredPaths.length > 0) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Packed artifact is missing required public SDK files:\n${missingRequiredPaths.join("\n")}`,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const packageJsonInput: unknown = JSON.parse(
|
|
63
|
+
readFileSync("package.json", "utf8"),
|
|
64
|
+
);
|
|
65
|
+
const packageJson = z
|
|
66
|
+
.object({
|
|
67
|
+
dependencies: z.record(z.string(), z.string()).optional(),
|
|
68
|
+
devDependencies: z.record(z.string(), z.string()).optional(),
|
|
69
|
+
})
|
|
70
|
+
.parse(packageJsonInput);
|
|
71
|
+
|
|
72
|
+
if (!packageJson.dependencies?.["@clack/prompts"]) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
"@clack/prompts is imported by the public create CLI and must be listed in dependencies.",
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (packageJson.devDependencies?.["@clack/prompts"]) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
"@clack/prompts must not be devDependency-only because the published create CLI imports it at runtime.",
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
console.log(`Packed artifact OK: ${first.filename}`);
|
|
85
|
+
for (const filePath of filePaths) {
|
|
86
|
+
console.log(` - ${filePath}`);
|
|
87
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
4
|
+
import {
|
|
5
|
+
existsSync,
|
|
6
|
+
mkdirSync,
|
|
7
|
+
mkdtempSync,
|
|
8
|
+
rmSync,
|
|
9
|
+
writeFileSync,
|
|
10
|
+
} from "node:fs";
|
|
11
|
+
import { tmpdir } from "node:os";
|
|
12
|
+
import { join, resolve } from "node:path";
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
|
|
15
|
+
const PACK_RESULT_SCHEMA = z.array(
|
|
16
|
+
z.object({
|
|
17
|
+
filename: z.string(),
|
|
18
|
+
}),
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const KEEP_TEMP = process.env.APIFUSE_PACK_SMOKE_KEEP_TEMP === "1";
|
|
22
|
+
|
|
23
|
+
const tempRoot = mkdtempSync(
|
|
24
|
+
join(tmpdir(), "apifuse-provider-sdk-pack-smoke-"),
|
|
25
|
+
);
|
|
26
|
+
const packDir = join(tempRoot, "pack");
|
|
27
|
+
const consumerDir = join(tempRoot, "consumer");
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
mkdirSync(packDir, { recursive: true });
|
|
31
|
+
mkdirSync(consumerDir, { recursive: true });
|
|
32
|
+
|
|
33
|
+
const packed = packSdk(packDir);
|
|
34
|
+
const tarballPath = resolve(packDir, packed.filename);
|
|
35
|
+
const tarballSpecifier = `file:${tarballPath}`;
|
|
36
|
+
|
|
37
|
+
writeFileSync(
|
|
38
|
+
join(consumerDir, "package.json"),
|
|
39
|
+
`${JSON.stringify(
|
|
40
|
+
{
|
|
41
|
+
private: true,
|
|
42
|
+
type: "module",
|
|
43
|
+
dependencies: {
|
|
44
|
+
"@apifuse/provider-sdk": tarballSpecifier,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
null,
|
|
48
|
+
2,
|
|
49
|
+
)}\n`,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
run("bun", ["install"], consumerDir);
|
|
53
|
+
|
|
54
|
+
const cliBin = join(consumerDir, "node_modules", ".bin", "apifuse");
|
|
55
|
+
if (!existsSync(cliBin)) {
|
|
56
|
+
throw new Error(`Expected CLI bin at ${cliBin}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
run(
|
|
60
|
+
"bun",
|
|
61
|
+
[
|
|
62
|
+
cliBin,
|
|
63
|
+
"create",
|
|
64
|
+
"dx-smoke",
|
|
65
|
+
"--yes",
|
|
66
|
+
"--json",
|
|
67
|
+
"--sdk-specifier",
|
|
68
|
+
tarballSpecifier,
|
|
69
|
+
],
|
|
70
|
+
consumerDir,
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const generatedProviderDir = join(consumerDir, "dx-smoke");
|
|
74
|
+
run("bun", ["run", "check"], generatedProviderDir);
|
|
75
|
+
run("bun", ["run", "test"], generatedProviderDir);
|
|
76
|
+
|
|
77
|
+
console.log(
|
|
78
|
+
`Provider SDK packed-artifact smoke passed: ${tarballPath} -> ${generatedProviderDir}`,
|
|
79
|
+
);
|
|
80
|
+
} finally {
|
|
81
|
+
if (KEEP_TEMP) {
|
|
82
|
+
console.log(`Keeping smoke temp directory: ${tempRoot}`);
|
|
83
|
+
} else {
|
|
84
|
+
rmSync(tempRoot, { recursive: true, force: true });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function packSdk(destination: string): { filename: string } {
|
|
89
|
+
const raw = execFileSync(
|
|
90
|
+
"npm",
|
|
91
|
+
["pack", "--json", "--pack-destination", destination],
|
|
92
|
+
{
|
|
93
|
+
cwd: process.cwd(),
|
|
94
|
+
encoding: "utf8",
|
|
95
|
+
stdio: ["ignore", "pipe", "inherit"],
|
|
96
|
+
},
|
|
97
|
+
);
|
|
98
|
+
const parsed = PACK_RESULT_SCHEMA.parse(JSON.parse(raw));
|
|
99
|
+
const first = parsed[0];
|
|
100
|
+
if (!first) {
|
|
101
|
+
throw new Error("npm pack --json returned no package metadata.");
|
|
102
|
+
}
|
|
103
|
+
return first;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function run(command: string, args: string[], cwd: string): void {
|
|
107
|
+
const result = spawnSync(command, args, {
|
|
108
|
+
cwd,
|
|
109
|
+
env: process.env,
|
|
110
|
+
stdio: "inherit",
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
if (result.error) {
|
|
114
|
+
throw result.error;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (result.status !== 0) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
`Command failed (${[command, ...args].join(" ")}) in ${cwd} with exit code ${result.status}`,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
package/bin/apifuse-perf.ts
CHANGED
|
@@ -8,10 +8,7 @@ import { pathToFileURL } from "node:url";
|
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
10
|
type ApiFuseConfig,
|
|
11
|
-
createAuthManager,
|
|
12
11
|
createHttpClient,
|
|
13
|
-
createSessionStore,
|
|
14
|
-
createStateContext,
|
|
15
12
|
createTlsClient,
|
|
16
13
|
executeOperation,
|
|
17
14
|
getProviderBaseUrl,
|
|
@@ -83,9 +80,9 @@ const DEFAULT_WARMUP = 2;
|
|
|
83
80
|
const DEFAULT_CONCURRENCY = 1;
|
|
84
81
|
const BAR_WIDTH = 20;
|
|
85
82
|
|
|
86
|
-
async function main() {
|
|
83
|
+
export async function main() {
|
|
87
84
|
try {
|
|
88
|
-
const args = parseArgs(process.argv.slice(2));
|
|
85
|
+
const args = parseArgs(normalizeArgs(process.argv.slice(2)));
|
|
89
86
|
const providerDirectory = resolve(process.cwd(), args.providerPath);
|
|
90
87
|
const providerEntry = resolveProviderEntry(providerDirectory);
|
|
91
88
|
const provider = await loadProvider(providerEntry);
|
|
@@ -172,6 +169,10 @@ async function main() {
|
|
|
172
169
|
}
|
|
173
170
|
}
|
|
174
171
|
|
|
172
|
+
function normalizeArgs(argv: string[]): string[] {
|
|
173
|
+
return argv[0] === "perf" ? argv.slice(1) : argv;
|
|
174
|
+
}
|
|
175
|
+
|
|
175
176
|
function parseArgs(argv: string[]): CliArgs {
|
|
176
177
|
let providerPath: string | undefined;
|
|
177
178
|
let operation: string | undefined;
|
|
@@ -568,19 +569,12 @@ async function executeProfileRun(options: {
|
|
|
568
569
|
const traceContext = createTraceContext(
|
|
569
570
|
resolveTraceContextOptions(options.config.trace),
|
|
570
571
|
);
|
|
571
|
-
const session = createSessionStore({
|
|
572
|
-
backend: options.config.session?.storage,
|
|
573
|
-
databasePath: options.config.session?.path,
|
|
574
|
-
} as Parameters<typeof createSessionStore>[0]);
|
|
575
|
-
const authManager = createAuthManager(options.provider.auth, session);
|
|
576
572
|
const baseContext = createBaseContext({
|
|
577
|
-
authManager,
|
|
578
573
|
config: options.config,
|
|
579
574
|
provider: options.provider,
|
|
580
575
|
fixtureReplay: options.fixtureReplay,
|
|
581
576
|
forceFixtureReplay: options.forceFixtureReplay,
|
|
582
577
|
proxyEnabled: options.proxyEnabled,
|
|
583
|
-
session,
|
|
584
578
|
traceContext,
|
|
585
579
|
});
|
|
586
580
|
const ctx = wrapWithInstrumentation(baseContext);
|
|
@@ -592,21 +586,12 @@ async function executeProfileRun(options: {
|
|
|
592
586
|
const normalizedInput = await ctx.trace.span("normalizeRequest", async () =>
|
|
593
587
|
options.inputSchema.parse(input),
|
|
594
588
|
);
|
|
595
|
-
const result =
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
normalizedInput,
|
|
602
|
-
),
|
|
603
|
-
)
|
|
604
|
-
: await executeOperation(
|
|
605
|
-
options.provider,
|
|
606
|
-
options.operationName,
|
|
607
|
-
ctx,
|
|
608
|
-
normalizedInput,
|
|
609
|
-
);
|
|
589
|
+
const result = await executeOperation(
|
|
590
|
+
options.provider,
|
|
591
|
+
options.operationName,
|
|
592
|
+
ctx,
|
|
593
|
+
normalizedInput,
|
|
594
|
+
);
|
|
610
595
|
|
|
611
596
|
return ctx.trace.span("transformResponse", async () =>
|
|
612
597
|
options.outputSchema.parse(result),
|
|
@@ -633,13 +618,11 @@ async function executeProfileRun(options: {
|
|
|
633
618
|
}
|
|
634
619
|
|
|
635
620
|
function createBaseContext(options: {
|
|
636
|
-
authManager: ReturnType<typeof createAuthManager>;
|
|
637
621
|
config: ApiFuseConfig;
|
|
638
622
|
provider: ProviderDefinition;
|
|
639
623
|
fixtureReplay: FixtureReplay | null;
|
|
640
624
|
forceFixtureReplay: boolean;
|
|
641
625
|
proxyEnabled: boolean;
|
|
642
|
-
session: ReturnType<typeof createSessionStore>;
|
|
643
626
|
traceContext: ReturnType<typeof createTraceContext>;
|
|
644
627
|
}): ProviderContext {
|
|
645
628
|
const upstream = {
|
|
@@ -663,13 +646,31 @@ function createBaseContext(options: {
|
|
|
663
646
|
});
|
|
664
647
|
|
|
665
648
|
return {
|
|
649
|
+
env: {
|
|
650
|
+
get: (key: string) => process.env[key],
|
|
651
|
+
},
|
|
652
|
+
credential: {
|
|
653
|
+
mode: "none",
|
|
654
|
+
get: () => undefined,
|
|
655
|
+
getAll: () => ({}),
|
|
656
|
+
getAccessToken: () => undefined,
|
|
657
|
+
getScopes: () => [],
|
|
658
|
+
},
|
|
666
659
|
http,
|
|
667
660
|
tls,
|
|
668
661
|
browser: createBrowserStub(),
|
|
669
|
-
session: options.session,
|
|
670
|
-
state: createStateContext(),
|
|
671
662
|
trace: options.traceContext,
|
|
672
|
-
auth:
|
|
663
|
+
auth: createAuthStub(),
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
function createAuthStub() {
|
|
668
|
+
return {
|
|
669
|
+
requestField: async (name: string) => {
|
|
670
|
+
throw new ProviderError(`Auth prompt is unavailable for ${name}`, {
|
|
671
|
+
code: "AUTH_PROMPT_UNAVAILABLE",
|
|
672
|
+
});
|
|
673
|
+
},
|
|
673
674
|
};
|
|
674
675
|
}
|
|
675
676
|
|
package/bin/apifuse-record.ts
CHANGED
|
@@ -8,8 +8,6 @@ import { pathToFileURL } from "node:url";
|
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
10
|
createHttpClient,
|
|
11
|
-
createSessionStore,
|
|
12
|
-
createStateContext,
|
|
13
11
|
createTlsClient,
|
|
14
12
|
executeOperation,
|
|
15
13
|
type HttpClient,
|
|
@@ -30,8 +28,8 @@ type ProviderRuntime = ProviderDefinition;
|
|
|
30
28
|
|
|
31
29
|
type MutableRecord = Record<string, unknown>;
|
|
32
30
|
|
|
33
|
-
async function main() {
|
|
34
|
-
const args = parseArgs(process.argv.slice(2));
|
|
31
|
+
export async function main() {
|
|
32
|
+
const args = parseArgs(normalizeArgs(process.argv.slice(2)));
|
|
35
33
|
const location = resolveProviderLocation(args.providerPath);
|
|
36
34
|
const provider = await loadProvider(location.rootDir);
|
|
37
35
|
const operationName = resolveOperationName(provider, args.operation);
|
|
@@ -81,6 +79,10 @@ async function main() {
|
|
|
81
79
|
void result;
|
|
82
80
|
}
|
|
83
81
|
|
|
82
|
+
function normalizeArgs(argv: string[]): string[] {
|
|
83
|
+
return argv[0] === "record" ? argv.slice(1) : argv;
|
|
84
|
+
}
|
|
85
|
+
|
|
84
86
|
function parseArgs(argv: string[]): CliArgs {
|
|
85
87
|
let providerPath: string | undefined;
|
|
86
88
|
let operation: string | undefined;
|
|
@@ -197,7 +199,7 @@ function findProviderRoot(startDirectory: string): string | undefined {
|
|
|
197
199
|
function looksLikeProviderRoot(directory: string): boolean {
|
|
198
200
|
return (
|
|
199
201
|
existsSync(resolve(directory, "index.ts")) &&
|
|
200
|
-
existsSync(resolve(directory, "
|
|
202
|
+
existsSync(resolve(directory, "package.json"))
|
|
201
203
|
);
|
|
202
204
|
}
|
|
203
205
|
|
|
@@ -278,6 +280,16 @@ function createCaptureContext(baseUrl: string) {
|
|
|
278
280
|
});
|
|
279
281
|
|
|
280
282
|
const ctx: ProviderContext = {
|
|
283
|
+
env: {
|
|
284
|
+
get: (key) => process.env[key],
|
|
285
|
+
},
|
|
286
|
+
credential: {
|
|
287
|
+
mode: "none",
|
|
288
|
+
get: () => undefined,
|
|
289
|
+
getAll: () => ({}),
|
|
290
|
+
getAccessToken: () => undefined,
|
|
291
|
+
getScopes: () => [],
|
|
292
|
+
},
|
|
281
293
|
http,
|
|
282
294
|
tls,
|
|
283
295
|
browser: {
|
|
@@ -286,8 +298,6 @@ function createCaptureContext(baseUrl: string) {
|
|
|
286
298
|
throw new Error("Browser client is not available in apifuse record.");
|
|
287
299
|
},
|
|
288
300
|
},
|
|
289
|
-
session: createSessionStore(),
|
|
290
|
-
state: createStateContext(),
|
|
291
301
|
trace: {
|
|
292
302
|
span: async (_name, fn) => fn(),
|
|
293
303
|
},
|
package/bin/apifuse-test.ts
CHANGED
|
@@ -44,9 +44,9 @@ type ActionableError = {
|
|
|
44
44
|
type: "zod";
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
-
async function main() {
|
|
47
|
+
export async function main() {
|
|
48
48
|
try {
|
|
49
|
-
const args = parseArgs(process.argv.slice(2));
|
|
49
|
+
const args = parseArgs(normalizeArgs(process.argv.slice(2)));
|
|
50
50
|
const location = resolveProviderLocation(args.providerPath);
|
|
51
51
|
|
|
52
52
|
if (args.isVerbose && !args.isJson) {
|
|
@@ -111,6 +111,10 @@ async function main() {
|
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
function normalizeArgs(argv: string[]): string[] {
|
|
115
|
+
return argv[0] === "test" ? argv.slice(1) : argv;
|
|
116
|
+
}
|
|
117
|
+
|
|
114
118
|
function parseArgs(argv: string[]): CliArgs {
|
|
115
119
|
let providerPath: string | undefined;
|
|
116
120
|
let isJson = false;
|
|
@@ -219,7 +223,6 @@ function autoDetectSingleProvider(
|
|
|
219
223
|
[
|
|
220
224
|
`Could not find a provider under ${originalInput}.`,
|
|
221
225
|
"Expected a directory containing:",
|
|
222
|
-
" - manifest.json",
|
|
223
226
|
" - index.ts",
|
|
224
227
|
" - __tests__/index.test.ts",
|
|
225
228
|
].join("\n"),
|
|
@@ -264,7 +267,6 @@ function collectProviderRoots(directory: string): string[] {
|
|
|
264
267
|
|
|
265
268
|
function looksLikeProviderRoot(directory: string): boolean {
|
|
266
269
|
return [
|
|
267
|
-
resolve(directory, "manifest.json"),
|
|
268
270
|
resolve(directory, "index.ts"),
|
|
269
271
|
resolve(directory, "__tests__", "index.test.ts"),
|
|
270
272
|
].every((filePath) => existsSync(filePath));
|
package/bin/apifuse.ts
CHANGED
|
@@ -1,51 +1,52 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
3
|
import packageJson from "../package.json";
|
|
4
|
+
import { COMMAND_MANIFEST, COMMAND_ORDER } from "../src/cli/commands";
|
|
4
5
|
|
|
5
6
|
const command = process.argv[2];
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
case "--help":
|
|
24
|
-
case "-h":
|
|
25
|
-
case undefined:
|
|
26
|
-
printHelp();
|
|
27
|
-
break;
|
|
28
|
-
case "--version":
|
|
29
|
-
case "-v":
|
|
30
|
-
console.log(packageJson.version);
|
|
31
|
-
break;
|
|
32
|
-
default:
|
|
33
|
-
console.error(`Unknown command: ${command}`);
|
|
34
|
-
printHelp();
|
|
35
|
-
process.exit(1);
|
|
8
|
+
if (command === undefined || command === "--help" || command === "-h") {
|
|
9
|
+
printHelp();
|
|
10
|
+
process.exit(0);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (command === "--version" || command === "-v") {
|
|
14
|
+
console.log(packageJson.version);
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const manifest = COMMAND_MANIFEST[command as keyof typeof COMMAND_MANIFEST];
|
|
19
|
+
|
|
20
|
+
if (!manifest) {
|
|
21
|
+
console.error(`Unknown command: ${command}`);
|
|
22
|
+
printHelp();
|
|
23
|
+
process.exit(1);
|
|
36
24
|
}
|
|
37
25
|
|
|
26
|
+
const module = await import(manifest.modulePath);
|
|
27
|
+
await module.main();
|
|
28
|
+
|
|
38
29
|
function printHelp() {
|
|
39
30
|
console.log(`
|
|
40
31
|
apifuse - ApiFuse Provider SDK CLI
|
|
41
32
|
|
|
42
|
-
Commands
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
33
|
+
Commands:`);
|
|
34
|
+
for (const name of COMMAND_ORDER) {
|
|
35
|
+
const item = COMMAND_MANIFEST[name];
|
|
36
|
+
console.log(` ${item.name.padEnd(8)} ${item.summary}`);
|
|
37
|
+
}
|
|
46
38
|
|
|
39
|
+
console.log(`
|
|
40
|
+
Examples:`);
|
|
41
|
+
for (const name of COMMAND_ORDER) {
|
|
42
|
+
const item = COMMAND_MANIFEST[name];
|
|
43
|
+
for (const example of item.examples.slice(0, 1)) {
|
|
44
|
+
console.log(` ${example}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
console.log(`
|
|
47
49
|
Options:
|
|
48
50
|
--help Show this help
|
|
49
|
-
--version Show version
|
|
50
|
-
`);
|
|
51
|
+
--version Show version`);
|
|
51
52
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apifuse/provider-sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0-beta.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "ApiFuse Provider SDK — Build providers with zero architectural constraints",
|
|
@@ -12,8 +12,14 @@
|
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
14
14
|
"src",
|
|
15
|
+
"!src/__tests__",
|
|
16
|
+
"!src/__tests__/**",
|
|
17
|
+
"!src/**/*.test.ts",
|
|
18
|
+
"!src/index.test.ts",
|
|
15
19
|
"bin",
|
|
16
|
-
"README.md"
|
|
20
|
+
"README.md",
|
|
21
|
+
"AUTHORING.md",
|
|
22
|
+
"CHANGELOG.md"
|
|
17
23
|
],
|
|
18
24
|
"keywords": [
|
|
19
25
|
"apifuse",
|
|
@@ -24,7 +30,6 @@
|
|
|
24
30
|
"hono"
|
|
25
31
|
],
|
|
26
32
|
"bin": {
|
|
27
|
-
"apifuse-init": "./bin/apifuse-init.ts",
|
|
28
33
|
"apifuse": "./bin/apifuse.ts"
|
|
29
34
|
},
|
|
30
35
|
"exports": {
|
|
@@ -33,6 +38,16 @@
|
|
|
33
38
|
"import": "./src/index.ts",
|
|
34
39
|
"types": "./src/index.ts"
|
|
35
40
|
},
|
|
41
|
+
"./provider": {
|
|
42
|
+
"default": "./src/provider.ts",
|
|
43
|
+
"import": "./src/provider.ts",
|
|
44
|
+
"types": "./src/provider.ts"
|
|
45
|
+
},
|
|
46
|
+
"./server": {
|
|
47
|
+
"default": "./src/server/index.ts",
|
|
48
|
+
"import": "./src/server/index.ts",
|
|
49
|
+
"types": "./src/server/index.ts"
|
|
50
|
+
},
|
|
36
51
|
"./testing": {
|
|
37
52
|
"default": "./src/testing/index.ts",
|
|
38
53
|
"import": "./src/testing/index.ts",
|
|
@@ -43,21 +58,26 @@
|
|
|
43
58
|
"lint": "biome check",
|
|
44
59
|
"lint:fix": "biome lint --write",
|
|
45
60
|
"format": "biome format --write",
|
|
46
|
-
"type-check": "
|
|
61
|
+
"type-check": "tsgo --noEmit",
|
|
47
62
|
"test": "bun test",
|
|
48
|
-
"check": "bun run lint && bun run type-check"
|
|
63
|
+
"check": "bun run lint && bun run type-check",
|
|
64
|
+
"pack:check": "bun bin/apifuse-pack-check.ts",
|
|
65
|
+
"pack:smoke": "bun bin/apifuse-pack-smoke.ts"
|
|
49
66
|
},
|
|
50
67
|
"devDependencies": {
|
|
51
|
-
"@biomejs/biome": "^2.
|
|
52
|
-
"@clack/prompts": "^1.2.0",
|
|
68
|
+
"@biomejs/biome": "^2.4.12",
|
|
53
69
|
"@types/bun": "latest",
|
|
54
70
|
"@types/node": "^25.1.0",
|
|
55
|
-
"typescript": "^
|
|
71
|
+
"typescript": "^6.0.3"
|
|
56
72
|
},
|
|
57
73
|
"dependencies": {
|
|
58
|
-
"
|
|
74
|
+
"@clack/prompts": "^1.2.0",
|
|
75
|
+
"ajv": "^8.17",
|
|
76
|
+
"hono": "^4.12.14",
|
|
59
77
|
"playwright": "^1.55.1",
|
|
60
78
|
"playwright-stealth": "^0.0.1",
|
|
79
|
+
"re2-wasm": "^1.0",
|
|
80
|
+
"safe-regex": "^2.1",
|
|
61
81
|
"tlsclientwrapper": "^4.2.0",
|
|
62
82
|
"zod": "^4.3.6"
|
|
63
83
|
}
|