@apifuse/provider-sdk 2.1.0-beta.0 → 2.1.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/AUTHORING.md +218 -21
  2. package/CHANGELOG.md +54 -0
  3. package/README.md +147 -10
  4. package/SUBMISSION.md +87 -0
  5. package/bin/apifuse-check.ts +86 -4
  6. package/bin/apifuse-dev.ts +87 -13
  7. package/bin/apifuse-pack-check.ts +120 -0
  8. package/bin/apifuse-pack-smoke.ts +423 -0
  9. package/bin/apifuse-perf.ts +142 -49
  10. package/bin/apifuse-record.ts +182 -104
  11. package/bin/apifuse-submit-check.ts +2538 -0
  12. package/bin/apifuse.ts +1 -1
  13. package/dist/ceremonies/index.d.ts +41 -0
  14. package/dist/ceremonies/index.js +490 -0
  15. package/dist/choice-token.d.ts +24 -0
  16. package/dist/choice-token.js +74 -0
  17. package/dist/cli/commands.d.ts +10 -0
  18. package/dist/cli/commands.js +80 -0
  19. package/dist/cli/create.d.ts +47 -0
  20. package/dist/cli/create.js +762 -0
  21. package/dist/cli/templates/provider/.dockerignore.tpl +22 -0
  22. package/dist/cli/templates/provider/.gitignore.tpl +22 -0
  23. package/dist/cli/templates/provider/Dockerfile.tpl +7 -0
  24. package/dist/cli/templates/provider/README.md.tpl +160 -0
  25. package/dist/cli/templates/provider/dev.ts.tpl +5 -0
  26. package/dist/cli/templates/provider/domain/README.md.tpl +3 -0
  27. package/dist/cli/templates/provider/index.test.ts.tpl +13 -0
  28. package/dist/cli/templates/provider/index.ts.tpl +15 -0
  29. package/dist/cli/templates/provider/mappers/README.md.tpl +3 -0
  30. package/dist/cli/templates/provider/meta.ts.tpl +7 -0
  31. package/dist/cli/templates/provider/operations/index.ts.tpl +5 -0
  32. package/dist/cli/templates/provider/operations/ping.ts.tpl +24 -0
  33. package/dist/cli/templates/provider/schemas/ping.ts.tpl +24 -0
  34. package/dist/cli/templates/provider/start.ts.tpl +5 -0
  35. package/dist/cli/templates/provider/upstream/README.md.tpl +3 -0
  36. package/dist/config/loader.d.ts +107 -0
  37. package/dist/config/loader.js +935 -0
  38. package/dist/contract-json.d.ts +9 -0
  39. package/dist/contract-json.js +51 -0
  40. package/dist/contract-serialization.d.ts +4 -0
  41. package/dist/contract-serialization.js +78 -0
  42. package/dist/contract-types.d.ts +49 -0
  43. package/dist/contract-types.js +1 -0
  44. package/dist/contract.d.ts +6 -0
  45. package/dist/contract.js +155 -0
  46. package/dist/define.d.ts +97 -0
  47. package/dist/define.js +1320 -0
  48. package/dist/dev.d.ts +9 -0
  49. package/dist/dev.js +15 -0
  50. package/dist/errors.d.ts +59 -0
  51. package/dist/errors.js +97 -0
  52. package/dist/i18n/catalog.d.ts +29 -0
  53. package/dist/i18n/catalog.js +159 -0
  54. package/dist/i18n/index.d.ts +2 -0
  55. package/dist/i18n/index.js +2 -0
  56. package/dist/i18n/keys.d.ts +10 -0
  57. package/dist/i18n/keys.js +34 -0
  58. package/dist/index.d.ts +41 -0
  59. package/dist/index.js +37 -0
  60. package/dist/lint.d.ts +73 -0
  61. package/dist/lint.js +702 -0
  62. package/dist/observability.d.ts +5 -0
  63. package/dist/observability.js +39 -0
  64. package/dist/provider.d.ts +9 -0
  65. package/dist/provider.js +8 -0
  66. package/dist/public-schema-field-lint.d.ts +2 -0
  67. package/dist/public-schema-field-lint.js +158 -0
  68. package/dist/recipes/gov-api.d.ts +19 -0
  69. package/dist/recipes/gov-api.js +72 -0
  70. package/dist/recipes/rest-api.d.ts +21 -0
  71. package/dist/recipes/rest-api.js +115 -0
  72. package/dist/runtime/auth-flow.d.ts +14 -0
  73. package/dist/runtime/auth-flow.js +44 -0
  74. package/dist/runtime/browser.d.ts +25 -0
  75. package/dist/runtime/browser.js +1034 -0
  76. package/dist/runtime/cache.d.ts +10 -0
  77. package/dist/runtime/cache.js +372 -0
  78. package/dist/runtime/choice.d.ts +15 -0
  79. package/dist/runtime/choice.js +435 -0
  80. package/dist/runtime/credential.d.ts +8 -0
  81. package/dist/runtime/credential.js +61 -0
  82. package/dist/runtime/env.d.ts +2 -0
  83. package/dist/runtime/env.js +10 -0
  84. package/dist/runtime/executor.d.ts +16 -0
  85. package/dist/runtime/executor.js +51 -0
  86. package/dist/runtime/http.d.ts +8 -0
  87. package/dist/runtime/http.js +706 -0
  88. package/dist/runtime/insights.d.ts +9 -0
  89. package/dist/runtime/insights.js +324 -0
  90. package/dist/runtime/instrumentation.d.ts +8 -0
  91. package/dist/runtime/instrumentation.js +269 -0
  92. package/dist/runtime/key-derivation.d.ts +24 -0
  93. package/dist/runtime/key-derivation.js +73 -0
  94. package/dist/runtime/keyring.d.ts +25 -0
  95. package/dist/runtime/keyring.js +93 -0
  96. package/dist/runtime/namespace.d.ts +9 -0
  97. package/dist/runtime/namespace.js +19 -0
  98. package/dist/runtime/otlp.d.ts +39 -0
  99. package/dist/runtime/otlp.js +103 -0
  100. package/dist/runtime/perf.d.ts +12 -0
  101. package/dist/runtime/perf.js +52 -0
  102. package/dist/runtime/prevalidate.d.ts +12 -0
  103. package/dist/runtime/prevalidate.js +173 -0
  104. package/dist/runtime/provider.d.ts +2 -0
  105. package/dist/runtime/provider.js +11 -0
  106. package/dist/runtime/proxy-errors.d.ts +21 -0
  107. package/dist/runtime/proxy-errors.js +83 -0
  108. package/dist/runtime/proxy-telemetry.d.ts +8 -0
  109. package/dist/runtime/proxy-telemetry.js +174 -0
  110. package/dist/runtime/redis.d.ts +17 -0
  111. package/dist/runtime/redis.js +82 -0
  112. package/dist/runtime/request-options.d.ts +3 -0
  113. package/dist/runtime/request-options.js +42 -0
  114. package/dist/runtime/state.d.ts +17 -0
  115. package/dist/runtime/state.js +344 -0
  116. package/dist/runtime/stealth.d.ts +18 -0
  117. package/dist/runtime/stealth.js +834 -0
  118. package/dist/runtime/stt.d.ts +22 -0
  119. package/dist/runtime/stt.js +480 -0
  120. package/dist/runtime/trace.d.ts +26 -0
  121. package/dist/runtime/trace.js +142 -0
  122. package/dist/runtime/waterfall.d.ts +12 -0
  123. package/dist/runtime/waterfall.js +147 -0
  124. package/dist/schema.d.ts +74 -0
  125. package/dist/schema.js +243 -0
  126. package/dist/serve.d.ts +1 -0
  127. package/dist/serve.js +1 -0
  128. package/dist/server/index.d.ts +3 -0
  129. package/dist/server/index.js +2 -0
  130. package/dist/server/serve.d.ts +64 -0
  131. package/dist/server/serve.js +1110 -0
  132. package/dist/server/types.d.ts +136 -0
  133. package/dist/server/types.js +86 -0
  134. package/dist/stealth/profiles.d.ts +4 -0
  135. package/dist/stealth/profiles.js +259 -0
  136. package/dist/stream.d.ts +44 -0
  137. package/dist/stream.js +151 -0
  138. package/dist/testing/helpers.d.ts +23 -0
  139. package/dist/testing/helpers.js +95 -0
  140. package/dist/testing/index.d.ts +2 -0
  141. package/dist/testing/index.js +2 -0
  142. package/dist/testing/run.d.ts +34 -0
  143. package/dist/testing/run.js +303 -0
  144. package/dist/types.d.ts +1326 -0
  145. package/dist/types.js +61 -0
  146. package/dist/utils/date.d.ts +6 -0
  147. package/dist/utils/date.js +101 -0
  148. package/dist/utils/parse.d.ts +16 -0
  149. package/dist/utils/parse.js +51 -0
  150. package/dist/utils/text.d.ts +4 -0
  151. package/dist/utils/text.js +14 -0
  152. package/dist/utils/transform.d.ts +8 -0
  153. package/dist/utils/transform.js +48 -0
  154. package/package.json +57 -29
  155. package/src/ceremonies/index.ts +30 -3
  156. package/src/choice-token.ts +165 -0
  157. package/src/cli/commands.ts +34 -11
  158. package/src/cli/create.ts +214 -52
  159. package/src/cli/templates/provider/.dockerignore.tpl +22 -0
  160. package/src/cli/templates/provider/.gitignore.tpl +22 -0
  161. package/src/cli/templates/provider/README.md.tpl +134 -2
  162. package/src/cli/templates/provider/dev.ts.tpl +1 -1
  163. package/src/cli/templates/provider/domain/README.md.tpl +3 -0
  164. package/src/cli/templates/provider/index.ts.tpl +5 -44
  165. package/src/cli/templates/provider/mappers/README.md.tpl +3 -0
  166. package/src/cli/templates/provider/meta.ts.tpl +7 -0
  167. package/src/cli/templates/provider/operations/index.ts.tpl +5 -0
  168. package/src/cli/templates/provider/operations/ping.ts.tpl +24 -0
  169. package/src/cli/templates/provider/schemas/ping.ts.tpl +24 -0
  170. package/src/cli/templates/provider/start.ts.tpl +1 -1
  171. package/src/cli/templates/provider/upstream/README.md.tpl +3 -0
  172. package/src/config/loader.ts +1282 -7
  173. package/src/contract-json.ts +75 -0
  174. package/src/contract-serialization.ts +89 -0
  175. package/src/contract-types.ts +52 -0
  176. package/src/contract.ts +215 -0
  177. package/src/define.ts +1726 -48
  178. package/src/errors.ts +27 -0
  179. package/src/i18n/catalog.ts +277 -0
  180. package/src/i18n/index.ts +2 -0
  181. package/src/i18n/keys.ts +64 -0
  182. package/src/index.ts +174 -15
  183. package/src/lint.ts +547 -73
  184. package/src/observability.ts +41 -0
  185. package/src/provider.ts +104 -5
  186. package/src/public-schema-field-lint.ts +237 -0
  187. package/src/runtime/auth-flow.ts +7 -0
  188. package/src/runtime/browser.ts +762 -51
  189. package/src/runtime/cache.ts +528 -0
  190. package/src/runtime/choice.ts +760 -0
  191. package/src/runtime/executor.ts +32 -3
  192. package/src/runtime/http.ts +945 -185
  193. package/src/runtime/insights.ts +11 -11
  194. package/src/runtime/instrumentation.ts +12 -4
  195. package/src/runtime/key-derivation.ts +1 -1
  196. package/src/runtime/keyring.ts +4 -3
  197. package/src/runtime/proxy-errors.ts +132 -0
  198. package/src/runtime/proxy-telemetry.ts +253 -0
  199. package/src/runtime/redis.ts +116 -0
  200. package/src/runtime/request-options.ts +66 -0
  201. package/src/runtime/state.ts +563 -0
  202. package/src/runtime/stealth.ts +1159 -0
  203. package/src/runtime/stt.ts +629 -0
  204. package/src/runtime/trace.ts +1 -1
  205. package/src/schema.ts +363 -1
  206. package/src/server/serve.ts +1172 -76
  207. package/src/server/types.ts +37 -0
  208. package/src/stream.ts +210 -0
  209. package/src/testing/run.ts +31 -5
  210. package/src/types.ts +1118 -44
  211. package/src/composite.ts +0 -43
  212. package/src/runtime/tls.ts +0 -425
  213. package/src/types/playwright-stealth.d.ts +0 -9
@@ -8,8 +8,11 @@ import { pathToFileURL } from "node:url";
8
8
 
9
9
  import {
10
10
  type ApiFuseConfig,
11
+ createBypassProviderCache,
11
12
  createHttpClient,
12
- createTlsClient,
13
+ createProviderChoiceContext,
14
+ createStealthClient,
15
+ createSttClientFromEnv,
13
16
  executeOperation,
14
17
  getProviderBaseUrl,
15
18
  type HttpClient,
@@ -18,8 +21,8 @@ import {
18
21
  type ProviderDefinition,
19
22
  ProviderError,
20
23
  type Span,
21
- type TlsClient,
22
- type TlsResponse,
24
+ type StealthClient,
25
+ type StealthResponse,
23
26
  wrapWithInstrumentation,
24
27
  } from "../src";
25
28
  import {
@@ -27,6 +30,7 @@ import {
27
30
  groupSpansByName,
28
31
  type PerfStats,
29
32
  } from "../src/runtime/perf";
33
+ import { createMemoryProviderRuntimeState } from "../src/runtime/state";
30
34
  import {
31
35
  createTraceContext,
32
36
  resolveTraceContextOptions,
@@ -41,6 +45,7 @@ type CliArgs = {
41
45
  exportPath?: string;
42
46
  flame: boolean;
43
47
  operation: string;
48
+ params?: string;
44
49
  runs: number;
45
50
  warmup: number;
46
51
  };
@@ -79,6 +84,21 @@ const DEFAULT_RUNS = 10;
79
84
  const DEFAULT_WARMUP = 2;
80
85
  const DEFAULT_CONCURRENCY = 1;
81
86
  const BAR_WIDTH = 20;
87
+ const HELP_TEXT = `Usage: apifuse perf <provider-path> --operation <operation> [options]
88
+
89
+ Options:
90
+ --operation, -o <name> operation to profile (required)
91
+ --params, -p <json> JSON input template; falls back to fixtures.request or {}
92
+ --runs, -n <number> number of runs (default: 10)
93
+ --warmup <number> warmup runs (default: 2)
94
+ --concurrency, -c <n> concurrent requests (default: 1)
95
+ --compare-proxy run with proxy on/off and compare
96
+ --export <path> export results to JSON file
97
+ --flame generate flamegraph SVG
98
+ --help, -h show this help
99
+
100
+ Example:
101
+ apifuse perf providers/korea-air-quality --operation realtime --params '{"stationName":"jongno"}' --runs 5`;
82
102
 
83
103
  export async function main() {
84
104
  try {
@@ -92,7 +112,11 @@ export async function main() {
92
112
  const inputSchema = getOperationSchema(provider, operation, "input");
93
113
  const outputSchema = getOperationSchema(provider, operation, "output");
94
114
  const fixtureReplay = await loadFixtureReplay(providerDirectory);
95
- const inputTemplate = resolveInputTemplate(provider, inputSchema);
115
+ const inputTemplate = resolveInputTemplate(
116
+ provider,
117
+ inputSchema,
118
+ args.params,
119
+ );
96
120
 
97
121
  const directSuite = await runProfileSuite({
98
122
  args,
@@ -182,6 +206,7 @@ function parseArgs(argv: string[]): CliArgs {
182
206
  let compareProxy = false;
183
207
  let exportPath: string | undefined;
184
208
  let flame = false;
209
+ let params: string | undefined;
185
210
 
186
211
  for (let index = 0; index < argv.length; index += 1) {
187
212
  const arg = argv[index];
@@ -199,6 +224,11 @@ function parseArgs(argv: string[]): CliArgs {
199
224
  continue;
200
225
  }
201
226
 
227
+ if (arg === "--help" || arg === "-h") {
228
+ console.log(HELP_TEXT);
229
+ process.exit(0);
230
+ }
231
+
202
232
  if (arg === "--compare-proxy") {
203
233
  compareProxy = true;
204
234
  continue;
@@ -220,6 +250,22 @@ function parseArgs(argv: string[]): CliArgs {
220
250
  continue;
221
251
  }
222
252
 
253
+ if (arg === "--params" || arg === "-p") {
254
+ params = requireArgValue(argv, index, arg);
255
+ index += 1;
256
+ continue;
257
+ }
258
+
259
+ if (arg.startsWith("--params=")) {
260
+ params = arg.slice("--params=".length);
261
+ continue;
262
+ }
263
+
264
+ if (arg.startsWith("-p=")) {
265
+ params = arg.slice("-p=".length);
266
+ continue;
267
+ }
268
+
223
269
  if (arg.startsWith("-o=")) {
224
270
  operation = arg.slice("-o=".length);
225
271
  continue;
@@ -292,20 +338,7 @@ function parseArgs(argv: string[]): CliArgs {
292
338
  }
293
339
 
294
340
  if (!providerPath || !operation) {
295
- throw new Error(
296
- [
297
- "Usage: apifuse perf <provider-path> [options]",
298
- "",
299
- "Options:",
300
- " --operation, -o <name> operation to profile (required)",
301
- " --runs, -n <number> number of runs (default: 10)",
302
- " --warmup <number> warmup runs (default: 2)",
303
- " --concurrency, -c <n> concurrent requests (default: 1)",
304
- " --compare-proxy run with proxy on/off and compare",
305
- " --export <path> export results to JSON file",
306
- " --flame generate flamegraph SVG",
307
- ].join("\n"),
308
- );
341
+ throw new Error(HELP_TEXT);
309
342
  }
310
343
 
311
344
  return {
@@ -315,6 +348,7 @@ function parseArgs(argv: string[]): CliArgs {
315
348
  exportPath,
316
349
  flame,
317
350
  operation,
351
+ params,
318
352
  runs,
319
353
  warmup,
320
354
  };
@@ -409,7 +443,18 @@ function isSchema(value: unknown): value is { parse(input: unknown): unknown } {
409
443
  function resolveInputTemplate(
410
444
  provider: ProviderDefinition,
411
445
  inputSchema: { parse(input: unknown): unknown },
446
+ params: string | undefined,
412
447
  ): unknown {
448
+ if (params !== undefined) {
449
+ try {
450
+ return inputSchema.parse(JSON.parse(params));
451
+ } catch (error) {
452
+ throw new Error(
453
+ `Failed to parse --params JSON or validate input: ${error instanceof Error ? error.message : String(error)}`,
454
+ );
455
+ }
456
+ }
457
+
413
458
  const firstOp = Object.values(provider.operations)[0];
414
459
  if (firstOp?.fixtures?.request !== undefined) {
415
460
  return firstOp.fixtures.request;
@@ -440,12 +485,12 @@ async function loadFixtureReplay(
440
485
  }
441
486
 
442
487
  function assertProxyConfigured(config: ApiFuseConfig): void {
443
- if (config.proxy?.url || process.env.APIFUSE_PROXY_URL) {
488
+ if (config.proxy?.url || process.env.APIFUSE__PROXY__URL) {
444
489
  return;
445
490
  }
446
491
 
447
492
  throw new Error(
448
- "--compare-proxy requires a proxy URL in apifuse.config.ts or APIFUSE_PROXY_URL.",
493
+ "--compare-proxy requires a proxy URL in apifuse.config.ts or APIFUSE__PROXY__URL.",
449
494
  );
450
495
  }
451
496
 
@@ -637,30 +682,44 @@ function createBaseContext(options: {
637
682
  apifuseConfig,
638
683
  upstream,
639
684
  });
640
- const tls =
685
+ const stealth =
641
686
  options.forceFixtureReplay && options.fixtureReplay
642
- ? createFixtureTlsClient(options.fixtureReplay.rawText)
643
- : createTlsClient(getProviderBaseUrl(options.provider), {
687
+ ? createFixtureStealthClient(options.fixtureReplay.rawText)
688
+ : createStealthClient(getProviderBaseUrl(options.provider), {
644
689
  apifuseConfig,
645
690
  upstream,
646
691
  });
647
692
 
693
+ const env = {
694
+ get: (key: string) => process.env[key],
695
+ };
696
+ const credential = {
697
+ mode: "none" as const,
698
+ get: () => undefined,
699
+ getAll: () => ({}),
700
+ getAccessToken: () => undefined,
701
+ getScopes: () => [],
702
+ };
703
+ const state = createMemoryProviderRuntimeState();
648
704
  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
- },
705
+ env,
706
+ credential,
707
+ request: { headers: {} },
659
708
  http,
660
- tls,
709
+ cache: createBypassProviderCache({ providerId: options.provider.id }),
710
+ state,
711
+ stealth,
661
712
  browser: createBrowserStub(),
662
713
  trace: options.traceContext,
663
714
  auth: createAuthStub(),
715
+ stt: createSttClientFromEnv(options.provider.stt),
716
+ choice: createProviderChoiceContext({
717
+ providerId: options.provider.id,
718
+ env,
719
+ request: { headers: {} },
720
+ credential,
721
+ state,
722
+ }),
664
723
  };
665
724
  }
666
725
 
@@ -695,16 +754,25 @@ function createFixtureResponse(raw: unknown) {
695
754
  };
696
755
  }
697
756
 
698
- function createFixtureTlsClient(rawText: string): TlsClient {
699
- const createResponse = async (): Promise<TlsResponse> => ({
700
- status: 200,
701
- ok: true,
702
- headers: { "content-type": "application/json" },
703
- rawHeaders: [["content-type", "application/json"]],
704
- body: rawText,
705
- cookies: { get: () => undefined, getAll: () => ({}), toString: () => "" },
706
- json: async <T>() => JSON.parse(rawText) as T,
707
- });
757
+ function createFixtureStealthClient(rawText: string): StealthClient {
758
+ const createResponse = async (): Promise<StealthResponse> => {
759
+ const bodyBytes = new TextEncoder().encode(rawText);
760
+ return {
761
+ status: 200,
762
+ ok: true,
763
+ headers: { "content-type": "application/json" },
764
+ rawHeaders: [["content-type", "application/json"]],
765
+ body: rawText,
766
+ cookies: { get: () => undefined, getAll: () => ({}), toString: () => "" },
767
+ json: async <T>() => JSON.parse(rawText) as T,
768
+ arrayBuffer: async () =>
769
+ bodyBytes.buffer.slice(
770
+ bodyBytes.byteOffset,
771
+ bodyBytes.byteOffset + bodyBytes.byteLength,
772
+ ) as ArrayBuffer,
773
+ bytes: async () => new Uint8Array(bodyBytes),
774
+ };
775
+ };
708
776
 
709
777
  return {
710
778
  fetch: async () => createResponse(),
@@ -720,6 +788,7 @@ function createFixtureTlsClient(rawText: string): TlsClient {
720
788
  function createBrowserStub(): BrowserClient {
721
789
  return {
722
790
  engine: "playwright-stealth",
791
+ async close() {},
723
792
  async newPage() {
724
793
  throw new ProviderError(
725
794
  "Browser runtime is not supported by apifuse perf yet.",
@@ -728,6 +797,30 @@ function createBrowserStub(): BrowserClient {
728
797
  },
729
798
  );
730
799
  },
800
+ async rawPage() {
801
+ throw new ProviderError(
802
+ "Browser runtime is not supported by apifuse perf yet.",
803
+ {
804
+ code: "BROWSER_RUNTIME_UNSUPPORTED",
805
+ },
806
+ );
807
+ },
808
+ async withIsolatedContext() {
809
+ throw new ProviderError(
810
+ "Browser runtime is not supported by apifuse perf yet.",
811
+ {
812
+ code: "BROWSER_RUNTIME_UNSUPPORTED",
813
+ },
814
+ );
815
+ },
816
+ async solveChallenge() {
817
+ throw new ProviderError(
818
+ "Browser runtime is not supported by apifuse perf yet.",
819
+ {
820
+ code: "BROWSER_RUNTIME_UNSUPPORTED",
821
+ },
822
+ );
823
+ },
731
824
  };
732
825
  }
733
826
 
@@ -738,7 +831,7 @@ function buildInsights(
738
831
  ): string[] {
739
832
  const insights: string[] = [];
740
833
  const allSpans = runs.flatMap((run) => run.spans);
741
- const tlsSpans = allSpans.filter((span) => span.name === "tls.fetch");
834
+ const stealthSpans = allSpans.filter((span) => span.name === "stealth.fetch");
742
835
  const dnsSpans = allSpans.filter((span) => span.name === "dns");
743
836
  const transform = breakdown.find(
744
837
  (entry) => entry.name === "transformResponse",
@@ -746,7 +839,7 @@ function buildInsights(
746
839
  const responseSizes = allSpans
747
840
  .map((span) => span.attributes.response_size)
748
841
  .filter((value): value is number => typeof value === "number");
749
- const reuseFlags = tlsSpans
842
+ const reuseFlags = stealthSpans
750
843
  .map((span) => span.attributes.connection_reused)
751
844
  .filter((value): value is boolean => typeof value === "boolean");
752
845
 
@@ -756,8 +849,8 @@ function buildInsights(
756
849
  );
757
850
  insights.push(
758
851
  reusePercent >= 80
759
- ? `✓ TLS connection reuse: ${reusePercent}% (good)`
760
- : `⚠ TLS connection reuse: ${reusePercent}% — consider session reuse`,
852
+ ? `✓ Stealth connection reuse: ${reusePercent}% (good)`
853
+ : `⚠ Stealth connection reuse: ${reusePercent}% — consider session reuse`,
761
854
  );
762
855
  }
763
856
 
@@ -1091,7 +1184,7 @@ function cloneValue<T>(value: T): T {
1091
1184
 
1092
1185
  function handleCliError(error: unknown): never {
1093
1186
  const message = error instanceof Error ? error.message : String(error);
1094
- console.error(message);
1187
+ console.error(`[apifuse perf] ${message}`);
1095
1188
  process.exit(1);
1096
1189
  }
1097
1190