@apifuse/provider-sdk 2.0.0-beta.1 → 2.1.0-beta.0

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.
Files changed (78) hide show
  1. package/AUTHORING.md +102 -0
  2. package/CHANGELOG.md +14 -0
  3. package/README.md +100 -28
  4. package/bin/apifuse-check.ts +78 -71
  5. package/bin/apifuse-create.ts +12 -0
  6. package/bin/apifuse-dev.ts +24 -61
  7. package/bin/apifuse-pack-check.ts +47 -0
  8. package/bin/apifuse-perf.ts +33 -32
  9. package/bin/apifuse-record.ts +17 -7
  10. package/bin/apifuse-test.ts +6 -4
  11. package/bin/apifuse.ts +36 -35
  12. package/package.json +28 -9
  13. package/src/ceremonies/index.ts +747 -0
  14. package/src/cli/commands.ts +87 -0
  15. package/src/cli/create.ts +845 -0
  16. package/src/cli/templates/provider/Dockerfile.tpl +7 -0
  17. package/src/cli/templates/provider/README.md.tpl +28 -0
  18. package/src/cli/templates/provider/dev.ts.tpl +5 -0
  19. package/src/cli/templates/provider/index.test.ts.tpl +13 -0
  20. package/src/cli/templates/provider/index.ts.tpl +54 -0
  21. package/src/cli/templates/provider/start.ts.tpl +5 -0
  22. package/src/composite.ts +43 -0
  23. package/src/define.ts +527 -41
  24. package/src/dev.ts +2 -6
  25. package/src/errors.ts +42 -0
  26. package/src/index.ts +50 -38
  27. package/src/lint.ts +574 -0
  28. package/src/provider.ts +14 -0
  29. package/src/runtime/auth-flow.ts +67 -0
  30. package/src/runtime/credential.ts +95 -0
  31. package/src/runtime/env.ts +13 -0
  32. package/src/runtime/executor.ts +13 -14
  33. package/src/runtime/http.ts +10 -2
  34. package/src/runtime/insights.ts +3 -3
  35. package/src/runtime/key-derivation.ts +122 -0
  36. package/src/runtime/keyring.ts +148 -0
  37. package/src/runtime/namespace.ts +33 -0
  38. package/src/runtime/prevalidate.ts +252 -0
  39. package/src/runtime/tls.ts +20 -5
  40. package/src/runtime/waterfall.ts +0 -1
  41. package/src/schema.ts +77 -0
  42. package/src/serve.ts +1 -664
  43. package/src/server/index.ts +22 -0
  44. package/src/server/serve.ts +610 -0
  45. package/src/server/types.ts +78 -0
  46. package/src/stealth/profiles.ts +10 -93
  47. package/src/testing/run.ts +391 -32
  48. package/src/types.ts +364 -41
  49. package/bin/apifuse-init.ts +0 -387
  50. package/src/__tests__/auth.test.ts +0 -396
  51. package/src/__tests__/browser-auth.test.ts +0 -180
  52. package/src/__tests__/browser.test.ts +0 -632
  53. package/src/__tests__/define.test.ts +0 -225
  54. package/src/__tests__/errors.test.ts +0 -69
  55. package/src/__tests__/executor.test.ts +0 -214
  56. package/src/__tests__/http.test.ts +0 -238
  57. package/src/__tests__/insights.test.ts +0 -210
  58. package/src/__tests__/instrumentation.test.ts +0 -290
  59. package/src/__tests__/otlp.test.ts +0 -141
  60. package/src/__tests__/perf.test.ts +0 -60
  61. package/src/__tests__/providers-yaml.test.ts +0 -135
  62. package/src/__tests__/proxy.test.ts +0 -359
  63. package/src/__tests__/recipes.test.ts +0 -36
  64. package/src/__tests__/serve.test.ts +0 -233
  65. package/src/__tests__/session.test.ts +0 -231
  66. package/src/__tests__/state.test.ts +0 -100
  67. package/src/__tests__/stealth.test.ts +0 -57
  68. package/src/__tests__/testing.test.ts +0 -97
  69. package/src/__tests__/tls.test.ts +0 -345
  70. package/src/__tests__/types.test.ts +0 -142
  71. package/src/__tests__/utils.test.ts +0 -62
  72. package/src/__tests__/waterfall.test.ts +0 -270
  73. package/src/config/providers-yaml.ts +0 -370
  74. package/src/index.test.ts +0 -1
  75. package/src/protocol.ts +0 -183
  76. package/src/runtime/auth.ts +0 -245
  77. package/src/runtime/session.ts +0 -573
  78. package/src/runtime/state.ts +0 -124
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { execFileSync } from "node:child_process";
4
+ import { z } from "zod";
5
+
6
+ const PACK_RESULT_SCHEMA = z.array(
7
+ z.object({
8
+ filename: z.string(),
9
+ files: z
10
+ .array(
11
+ z.object({
12
+ path: z.string(),
13
+ }),
14
+ )
15
+ .optional(),
16
+ }),
17
+ );
18
+
19
+ const raw = execFileSync("npm", ["pack", "--json", "--dry-run"], {
20
+ cwd: process.cwd(),
21
+ encoding: "utf8",
22
+ });
23
+ const parsed = PACK_RESULT_SCHEMA.parse(JSON.parse(raw));
24
+ const first = parsed[0];
25
+
26
+ if (!first) {
27
+ throw new Error("npm pack --json --dry-run returned no package metadata.");
28
+ }
29
+
30
+ const filePaths = (first.files ?? []).map((file) => file.path);
31
+ const forbiddenMatches = filePaths.filter(
32
+ (path) =>
33
+ path.startsWith("src/__tests__/") ||
34
+ path === "src/index.test.ts" ||
35
+ path === "bin/apifuse-init.ts",
36
+ );
37
+
38
+ if (forbiddenMatches.length > 0) {
39
+ throw new Error(
40
+ `Packed artifact still includes forbidden files:\n${forbiddenMatches.join("\n")}`,
41
+ );
42
+ }
43
+
44
+ console.log(`Packed artifact OK: ${first.filename}`);
45
+ for (const filePath of filePaths) {
46
+ console.log(` - ${filePath}`);
47
+ }
@@ -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 = options.provider.auth
596
- ? await authManager.wrapWithAutoRefresh(ctx, () =>
597
- executeOperation(
598
- options.provider,
599
- options.operationName,
600
- ctx,
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: options.authManager.createAuthContext(),
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
 
@@ -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, "manifest.json"))
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
  },
@@ -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
- switch (command) {
8
- case "init": {
9
- const module = await import("./apifuse-init");
10
- await module.main();
11
- break;
12
- }
13
- case "dev": {
14
- const module = await import("./apifuse-dev");
15
- await module.main();
16
- break;
17
- }
18
- case "check": {
19
- const module = await import("./apifuse-check");
20
- await module.main();
21
- break;
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
- init <name> Create a new provider
44
- dev [path] Start dev server with hot reload
45
- check [path] Validate provider structure
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,9 +1,9 @@
1
1
  {
2
2
  "name": "@apifuse/provider-sdk",
3
- "version": "2.0.0-beta.1",
3
+ "version": "2.1.0-beta.0",
4
4
  "private": false,
5
5
  "type": "module",
6
- "description": "ApiFuse Provider SDK Build providers with zero architectural constraints",
6
+ "description": "ApiFuse Provider SDK \u2014 Build providers with zero architectural constraints",
7
7
  "license": "MIT",
8
8
  "main": "./src/index.ts",
9
9
  "types": "./src/index.ts",
@@ -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,25 @@
43
58
  "lint": "biome check",
44
59
  "lint:fix": "biome lint --write",
45
60
  "format": "biome format --write",
46
- "type-check": "tsc --noEmit",
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"
49
65
  },
50
66
  "devDependencies": {
51
- "@biomejs/biome": "^2.3.13",
67
+ "@biomejs/biome": "^2.4.12",
52
68
  "@clack/prompts": "^1.2.0",
53
69
  "@types/bun": "latest",
54
70
  "@types/node": "^25.1.0",
55
- "typescript": "^5.9.3"
71
+ "typescript": "^6.0.3"
56
72
  },
57
73
  "dependencies": {
58
- "hono": "^4.7.11",
74
+ "ajv": "^8.17",
75
+ "hono": "^4.12.14",
59
76
  "playwright": "^1.55.1",
60
77
  "playwright-stealth": "^0.0.1",
78
+ "re2-wasm": "^1.0",
79
+ "safe-regex": "^2.1",
61
80
  "tlsclientwrapper": "^4.2.0",
62
81
  "zod": "^4.3.6"
63
82
  }