@apifuse/provider-sdk 2.2.0-beta.3 → 2.2.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/AUTHORING.md +14 -0
  2. package/CHANGELOG.md +4 -0
  3. package/bin/apifuse-check.ts +18 -58
  4. package/bin/apifuse-create.ts +1 -1
  5. package/bin/apifuse-dev.ts +27 -52
  6. package/bin/apifuse-pack-smoke.ts +22 -81
  7. package/bin/apifuse-pack-types.ts +266 -0
  8. package/bin/apifuse-perf.ts +45 -127
  9. package/bin/apifuse-record.ts +23 -61
  10. package/bin/apifuse-submit-check.ts +26 -21
  11. package/bin/apifuse.ts +1 -1
  12. package/bin/submit-check-xml.ts +1 -1
  13. package/dist/auth-turn/index.d.ts +1 -1
  14. package/dist/auth.d.ts +2 -2
  15. package/dist/auth.js +9 -18
  16. package/dist/ceremonies/index.d.ts +1 -1
  17. package/dist/ceremonies/index.js +8 -18
  18. package/dist/config/loader.d.ts +2 -2
  19. package/dist/config/loader.js +19 -42
  20. package/dist/contract-serialization.d.ts +2 -2
  21. package/dist/contract-serialization.js +3 -6
  22. package/dist/contract-types.d.ts +2 -2
  23. package/dist/contract.d.ts +3 -3
  24. package/dist/contract.js +4 -6
  25. package/dist/define.d.ts +8 -1
  26. package/dist/define.js +57 -115
  27. package/dist/dev.d.ts +1 -1
  28. package/dist/dev.js +1 -1
  29. package/dist/errors.d.ts +1 -1
  30. package/dist/errors.js +1 -3
  31. package/dist/i18n/catalog.d.ts +2 -2
  32. package/dist/i18n/catalog.js +4 -10
  33. package/dist/i18n/index.d.ts +2 -2
  34. package/dist/i18n/index.js +2 -2
  35. package/dist/i18n/keys.d.ts +2 -2
  36. package/dist/index.d.ts +43 -42
  37. package/dist/index.js +38 -37
  38. package/dist/lint.js +8 -15
  39. package/dist/provider.d.ts +11 -11
  40. package/dist/provider.js +9 -9
  41. package/dist/public-schema-field-lint.d.ts +1 -1
  42. package/dist/recipes/gov-api.js +1 -1
  43. package/dist/runtime/auth-flow.d.ts +1 -1
  44. package/dist/runtime/auth-flow.js +3 -3
  45. package/dist/runtime/browser.d.ts +1 -1
  46. package/dist/runtime/browser.js +15 -29
  47. package/dist/runtime/cache.d.ts +1 -1
  48. package/dist/runtime/cache.js +4 -8
  49. package/dist/runtime/choice.d.ts +1 -1
  50. package/dist/runtime/choice.js +12 -34
  51. package/dist/runtime/credential.d.ts +1 -1
  52. package/dist/runtime/credential.js +1 -1
  53. package/dist/runtime/env.d.ts +1 -1
  54. package/dist/runtime/executor.d.ts +1 -1
  55. package/dist/runtime/executor.js +2 -2
  56. package/dist/runtime/http.d.ts +2 -2
  57. package/dist/runtime/http.js +5 -5
  58. package/dist/runtime/insights.d.ts +1 -1
  59. package/dist/runtime/insights.js +6 -13
  60. package/dist/runtime/instrumentation.d.ts +2 -2
  61. package/dist/runtime/instrumentation.js +5 -15
  62. package/dist/runtime/keyring.js +1 -1
  63. package/dist/runtime/namespace.js +1 -1
  64. package/dist/runtime/otlp.d.ts +1 -1
  65. package/dist/runtime/perf.d.ts +1 -1
  66. package/dist/runtime/provider.d.ts +1 -1
  67. package/dist/runtime/provider.js +1 -2
  68. package/dist/runtime/proxy-errors.d.ts +1 -1
  69. package/dist/runtime/proxy-errors.js +3 -5
  70. package/dist/runtime/proxy-retry-policy.d.ts +2 -2
  71. package/dist/runtime/proxy-retry-policy.js +2 -2
  72. package/dist/runtime/proxy-telemetry.d.ts +1 -1
  73. package/dist/runtime/proxy-telemetry.js +16 -48
  74. package/dist/runtime/redis.d.ts +1 -1
  75. package/dist/runtime/redis.js +2 -4
  76. package/dist/runtime/request-options.d.ts +1 -1
  77. package/dist/runtime/state.d.ts +2 -2
  78. package/dist/runtime/state.js +3 -3
  79. package/dist/runtime/stealth.d.ts +2 -2
  80. package/dist/runtime/stealth.js +6 -6
  81. package/dist/runtime/stt.d.ts +1 -1
  82. package/dist/runtime/stt.js +11 -15
  83. package/dist/runtime/trace.d.ts +2 -2
  84. package/dist/runtime/trace.js +2 -4
  85. package/dist/runtime/waterfall.d.ts +1 -1
  86. package/dist/schema.d.ts +1 -1
  87. package/dist/schema.js +7 -15
  88. package/dist/serve.d.ts +1 -1
  89. package/dist/serve.js +1 -1
  90. package/dist/server/index.d.ts +7 -7
  91. package/dist/server/index.js +6 -6
  92. package/dist/server/self-test-redaction.d.ts +1 -1
  93. package/dist/server/self-test-redaction.js +1 -1
  94. package/dist/server/self-test.d.ts +3 -3
  95. package/dist/server/self-test.js +3 -3
  96. package/dist/server/serve.d.ts +3 -3
  97. package/dist/server/serve.js +36 -49
  98. package/dist/server/types.js +2 -7
  99. package/dist/stealth/profiles.d.ts +1 -1
  100. package/dist/stealth/profiles.js +5 -14
  101. package/dist/stream.d.ts +1 -1
  102. package/dist/testing/index.d.ts +2 -2
  103. package/dist/testing/index.js +2 -2
  104. package/dist/testing/run.d.ts +1 -1
  105. package/dist/testing/run.js +7 -14
  106. package/dist/types.d.ts +57 -0
  107. package/dist/user-input.d.ts +30 -0
  108. package/dist/user-input.js +66 -0
  109. package/package.json +6 -4
  110. package/src/auth-turn/index.ts +1 -1
  111. package/src/auth.ts +28 -86
  112. package/src/ceremonies/index.ts +24 -78
  113. package/src/config/loader.ts +58 -176
  114. package/src/contract-serialization.ts +5 -11
  115. package/src/contract-types.ts +2 -2
  116. package/src/contract.ts +12 -28
  117. package/src/define.ts +174 -495
  118. package/src/dev.ts +4 -9
  119. package/src/errors.ts +4 -11
  120. package/src/i18n/catalog.ts +10 -32
  121. package/src/i18n/index.ts +2 -2
  122. package/src/i18n/keys.ts +5 -11
  123. package/src/index.ts +44 -42
  124. package/src/lint.ts +89 -159
  125. package/src/provider.ts +12 -11
  126. package/src/public-schema-field-lint.ts +7 -33
  127. package/src/recipes/gov-api.ts +2 -5
  128. package/src/runtime/auth-flow.ts +5 -7
  129. package/src/runtime/browser.ts +64 -187
  130. package/src/runtime/cache.ts +20 -67
  131. package/src/runtime/choice.ts +55 -129
  132. package/src/runtime/credential.ts +2 -2
  133. package/src/runtime/env.ts +1 -1
  134. package/src/runtime/executor.ts +9 -19
  135. package/src/runtime/http.ts +7 -7
  136. package/src/runtime/insights.ts +15 -53
  137. package/src/runtime/instrumentation.ts +19 -58
  138. package/src/runtime/keyring.ts +7 -19
  139. package/src/runtime/namespace.ts +2 -7
  140. package/src/runtime/otlp.ts +12 -23
  141. package/src/runtime/perf.ts +1 -1
  142. package/src/runtime/provider.ts +4 -9
  143. package/src/runtime/proxy-errors.ts +17 -38
  144. package/src/runtime/proxy-retry-policy.ts +3 -3
  145. package/src/runtime/proxy-telemetry.ts +23 -72
  146. package/src/runtime/redis.ts +4 -12
  147. package/src/runtime/request-options.ts +4 -13
  148. package/src/runtime/state.ts +30 -109
  149. package/src/runtime/stealth.ts +8 -8
  150. package/src/runtime/stt.ts +38 -94
  151. package/src/runtime/trace.ts +14 -44
  152. package/src/runtime/waterfall.ts +5 -18
  153. package/src/schema.ts +23 -84
  154. package/src/serve.ts +1 -1
  155. package/src/server/index.ts +7 -7
  156. package/src/server/self-test-redaction.ts +2 -2
  157. package/src/server/self-test.ts +5 -5
  158. package/src/server/serve.ts +87 -242
  159. package/src/server/types.ts +6 -19
  160. package/src/stealth/profiles.ts +10 -26
  161. package/src/stream.ts +8 -19
  162. package/src/testing/index.ts +2 -2
  163. package/src/testing/run.ts +19 -67
  164. package/src/types.ts +58 -0
  165. package/src/user-input.ts +118 -0
package/AUTHORING.md CHANGED
@@ -18,6 +18,20 @@
18
18
 
19
19
  Provider code is the declaration input to the internal platform registry. The public SDK owns provider authoring/runtime ergonomics; internal docs, deploy, and discovery projections are built downstream from those declarations. `bun run lint:providers` enforces provider authoring standards.
20
20
 
21
+ ### User-input round-trips: never dead-end (`needs_input`)
22
+
23
+ A mutation MUST NOT fail for a problem the end user can resolve by choosing among live options (a required menu/course selection, a form question, a stale-but-recoverable state token). Throwing an error there strands the consuming agent: consumer error-shaping layers routinely strip error metadata, and a model that only sees "error" narrates failure to the user instead of relaying the choice.
24
+
25
+ Return the official success-shaped contract from `src/user-input.ts` instead (`ProviderNeedsInputPayload`, guard `isProviderNeedsInputPayload`):
26
+
27
+ - `status: "needs_input"` plus `required_selections` — only the still-pending questions, with human-readable `label`s and `valid_options` the agent relays verbatim. The agent never chooses for the user; anything with no real choice (agreement checkboxes, single-option required groups) is the provider's job to auto-answer.
28
+ - `selected_options` — selections already settled, echoed so the retry keeps them (copy them back and add the user's new answers).
29
+ - a freshly minted provider state token in the same response, so the retry never races an expired token.
30
+
31
+ No retry templates, next-action routing, or other agent choreography: provider payloads carry upstream-backed data only, and the consumer owns how the ask is phrased and how the retry call is shaped.
32
+
33
+ Declare the union in the operation `output` schema (`z.union([CreatedSchema, NeedsInputSchema])`). Reserve hard errors for genuinely unrecoverable flows (payment-gated, unsupported input kinds) and state the concrete reason in the error `message` itself, not only in `details`. Reference implementation: `providers/catchtable` `reserve` in the platform monorepo.
34
+
21
35
  ### Description template
22
36
 
23
37
  Every operation `description` MUST be at least 150 characters and follow this structure:
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # @apifuse/provider-sdk Changelog
2
2
 
3
+ ## 2.2.0-beta.4
4
+
5
+ - Release candidate for main commit 73f2c6ec429c2fbce8ac458a67111e4844b99178.
6
+
3
7
  ## 2.2.0-beta.3
4
8
 
5
9
  - Release candidate for main commit 74e8e18b502dd9b02dbf0d3e702f917570312fc0.
@@ -6,9 +6,9 @@ import { pathToFileURL } from "node:url";
6
6
 
7
7
  import { z } from "zod";
8
8
 
9
- import type { ProviderDefinition } from "../src";
10
- import { lintProvider, type ProviderLintMode } from "../src/lint";
11
- import { safeParseSchemaSync } from "../src/schema";
9
+ import type { ProviderDefinition } from "../src/index.js";
10
+ import { lintProvider, type ProviderLintMode } from "../src/lint.js";
11
+ import { safeParseSchemaSync } from "../src/schema.js";
12
12
 
13
13
  const HELP_TEXT = `Usage: apifuse check [path]
14
14
  Example: apifuse check providers/korea-air-quality
@@ -24,9 +24,7 @@ export type RunChecksOptions = {
24
24
  lintMode?: ProviderLintMode;
25
25
  };
26
26
 
27
- type SafeParseResult =
28
- | { success: true; data: unknown }
29
- | { success: false; error: unknown };
27
+ type SafeParseResult = { success: true; data: unknown } | { success: false; error: unknown };
30
28
 
31
29
  export async function main() {
32
30
  const args = normalizeArgs(process.argv.slice(2));
@@ -142,17 +140,9 @@ function isScannableProviderSourceFile(relativePath: string): boolean {
142
140
  );
143
141
  }
144
142
 
145
- function collectProviderSourceFiles(
146
- providerRoot: string,
147
- ): Record<string, string> {
143
+ function collectProviderSourceFiles(providerRoot: string): Record<string, string> {
148
144
  const sources: Record<string, string> = {};
149
- const skipDirectories = new Set([
150
- ".git",
151
- "node_modules",
152
- "dist",
153
- "build",
154
- ".next",
155
- ]);
145
+ const skipDirectories = new Set([".git", "node_modules", "dist", "build", ".next"]);
156
146
  const visit = (directory: string) => {
157
147
  for (const entry of readdirSync(directory, { withFileTypes: true })) {
158
148
  const path = resolve(directory, entry.name);
@@ -162,10 +152,7 @@ function collectProviderSourceFiles(
162
152
  }
163
153
  continue;
164
154
  }
165
- if (
166
- !entry.isFile() ||
167
- !isScannableProviderSourceFile(path.slice(providerRoot.length + 1))
168
- ) {
155
+ if (!entry.isFile() || !isScannableProviderSourceFile(path.slice(providerRoot.length + 1))) {
169
156
  continue;
170
157
  }
171
158
  sources[path.slice(providerRoot.length + 1)] = readFileSync(path, "utf8");
@@ -175,10 +162,7 @@ function collectProviderSourceFiles(
175
162
  return sources;
176
163
  }
177
164
 
178
- function checkIndex(
179
- indexPath: string,
180
- provider: ProviderDefinition | undefined,
181
- ): CheckResult {
165
+ function checkIndex(indexPath: string, provider: ProviderDefinition | undefined): CheckResult {
182
166
  if (!existsSync(indexPath)) {
183
167
  return {
184
168
  message: "index.ts exists and exports default defineProvider",
@@ -200,9 +184,7 @@ function checkIndex(
200
184
  };
201
185
  }
202
186
 
203
- function checkOperations(
204
- provider: ProviderDefinition | undefined,
205
- ): CheckResult {
187
+ function checkOperations(provider: ProviderDefinition | undefined): CheckResult {
206
188
  if (!provider) {
207
189
  return {
208
190
  message: "All operations have handler, input, output",
@@ -277,20 +259,14 @@ function checkSchemas(provider: ProviderDefinition | undefined): CheckResult {
277
259
  continue;
278
260
  }
279
261
 
280
- const requestResult = parseFixture(
281
- operation.input,
282
- operation.fixtures.request,
283
- );
262
+ const requestResult = parseFixture(operation.input, operation.fixtures.request);
284
263
  if (!requestResult.success) {
285
264
  failures.push(
286
265
  `${operationId}: request fixture invalid (${formatSchemaError(requestResult.error)})`,
287
266
  );
288
267
  }
289
268
 
290
- const responseResult = parseFixture(
291
- operation.output,
292
- operation.fixtures.response,
293
- );
269
+ const responseResult = parseFixture(operation.output, operation.fixtures.response);
294
270
  if (!responseResult.success) {
295
271
  failures.push(
296
272
  `${operationId}: response fixture invalid (${formatSchemaError(responseResult.error)})`,
@@ -317,13 +293,8 @@ function checkAuthoringLint(
317
293
  };
318
294
  }
319
295
 
320
- const diagnostics = lintProvider(
321
- { ...provider, providerSourceFiles },
322
- { mode: lintMode },
323
- );
324
- const errors = diagnostics.filter(
325
- (diagnostic) => diagnostic.level === "error",
326
- );
296
+ const diagnostics = lintProvider({ ...provider, providerSourceFiles }, { mode: lintMode });
297
+ const errors = diagnostics.filter((diagnostic) => diagnostic.level === "error");
327
298
  const details = diagnostics.map((diagnostic) => {
328
299
  const field = diagnostic.field ? `${diagnostic.field}: ` : "";
329
300
  return `${diagnostic.level.toUpperCase()} ${diagnostic.rule} ${field}${diagnostic.message}`;
@@ -336,9 +307,7 @@ function checkAuthoringLint(
336
307
  };
337
308
  }
338
309
 
339
- function checkProviderMetadata(
340
- provider: ProviderDefinition | undefined,
341
- ): CheckResult {
310
+ function checkProviderMetadata(provider: ProviderDefinition | undefined): CheckResult {
342
311
  if (!provider) {
343
312
  return {
344
313
  message: "Provider metadata is declared in defineProvider",
@@ -425,18 +394,12 @@ function checkPackageJson(packageJsonPath: string): CheckResult {
425
394
  }
426
395
  }
427
396
 
428
- function assertProviderDefinition(
429
- value: unknown,
430
- ): ProviderDefinition | undefined {
397
+ function assertProviderDefinition(value: unknown): ProviderDefinition | undefined {
431
398
  return isProviderDefinition(value) ? value : undefined;
432
399
  }
433
400
 
434
401
  function isProviderDefinition(value: unknown): value is ProviderDefinition {
435
- if (
436
- !isRecord(value) ||
437
- !isRecord(value.meta) ||
438
- !isRecord(value.operations)
439
- ) {
402
+ if (!isRecord(value) || !isRecord(value.meta) || !isRecord(value.operations)) {
440
403
  return false;
441
404
  }
442
405
 
@@ -457,8 +420,7 @@ function hasSchemaParser(value: unknown): boolean {
457
420
  return (
458
421
  isRecord(value) &&
459
422
  (typeof value.safeParse === "function" ||
460
- (isRecord(value["~standard"]) &&
461
- typeof value["~standard"].validate === "function"))
423
+ (isRecord(value["~standard"]) && typeof value["~standard"].validate === "function"))
462
424
  );
463
425
  }
464
426
 
@@ -470,9 +432,7 @@ function formatSchemaError(error: unknown): string {
470
432
  if (Array.isArray(error)) {
471
433
  return error
472
434
  .map((issue) =>
473
- isRecord(issue) && typeof issue.message === "string"
474
- ? issue.message
475
- : String(issue),
435
+ isRecord(issue) && typeof issue.message === "string" ? issue.message : String(issue),
476
436
  )
477
437
  .join(", ");
478
438
  }
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
- import { main as runMain } from "../src/cli/create";
3
+ import { main as runMain } from "../src/cli/create.js";
4
4
 
5
5
  export { runMain as main };
6
6
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { existsSync } from "node:fs";
4
4
  import { dirname, relative, resolve } from "node:path";
5
- import type { ProviderDefinition } from "../src";
5
+ import type { ProviderDefinition } from "../src/index.js";
6
6
  import {
7
7
  createBrowserClient,
8
8
  createCredentialContext,
@@ -14,10 +14,10 @@ import {
14
14
  createSttClientFromEnv,
15
15
  PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV,
16
16
  ProviderError,
17
- } from "../src";
18
- import { createMemoryProviderRuntimeState } from "../src/runtime/state";
19
- import { createTraceContext } from "../src/runtime/trace";
20
- import type { BrowserClient, ProviderContext } from "../src/types";
17
+ } from "../src/index.js";
18
+ import { createMemoryProviderRuntimeState } from "../src/runtime/state.js";
19
+ import { createTraceContext } from "../src/runtime/trace.js";
20
+ import type { BrowserClient, ProviderContext } from "../src/types.js";
21
21
 
22
22
  const HELP_TEXT = `Usage: apifuse dev [path]
23
23
  Example: apifuse dev providers/korea-air-quality
@@ -33,12 +33,9 @@ export async function main() {
33
33
 
34
34
  const providerPath = resolveProviderPath(args[0] ?? ".");
35
35
  const providerModule = await import(resolve(providerPath, "index.ts"));
36
- const provider = assertProviderDefinition(
37
- providerModule.default,
38
- providerPath,
39
- );
36
+ const provider = assertProviderDefinition(providerModule.default, providerPath);
40
37
 
41
- const { startDevServer } = await import("../src/dev");
38
+ const { startDevServer } = await import("../src/dev.js");
42
39
  const port = Number(process.env.APIFUSE__RUNTIME__PORT) || 3900;
43
40
 
44
41
  startDevServer(provider, { port });
@@ -57,8 +54,7 @@ export async function main() {
57
54
 
58
55
  const firstOperation = Object.keys(provider.operations)[0];
59
56
  if (firstOperation) {
60
- const sampleInput =
61
- provider.operations[firstOperation]?.fixtures?.request ?? {};
57
+ const sampleInput = provider.operations[firstOperation]?.fixtures?.request ?? {};
62
58
  const sampleBody = JSON.stringify({
63
59
  requestId: `req_local_${firstOperation}`,
64
60
  input: sampleInput,
@@ -149,9 +145,7 @@ function renderHotReloadCommand(providerPath: string, port: number): string {
149
145
  const devEntry = resolve(providerPath, "dev.ts");
150
146
  if (existsSync(devEntry)) {
151
147
  const relativeDevEntry = relative(process.cwd(), devEntry) || "dev.ts";
152
- const portPrefix = process.env.APIFUSE__RUNTIME__PORT
153
- ? `APIFUSE__RUNTIME__PORT=${port} `
154
- : "";
148
+ const portPrefix = process.env.APIFUSE__RUNTIME__PORT ? `APIFUSE__RUNTIME__PORT=${port} ` : "";
155
149
  return `${portPrefix}bun --hot ${relativeDevEntry}`;
156
150
  }
157
151
  return "rerun `apifuse dev` after edits (no dev.ts entrypoint found)";
@@ -166,40 +160,28 @@ function createUnsupportedBrowserStub(): BrowserClient {
166
160
  engine: "playwright-stealth",
167
161
  async close() {},
168
162
  async newPage() {
169
- throw new ProviderError(
170
- "Browser runtime is not enabled for this provider",
171
- {
172
- code: "BROWSER_RUNTIME_UNSUPPORTED",
173
- fix: 'Set provider runtime to "browser" to use ctx.browser',
174
- },
175
- );
163
+ throw new ProviderError("Browser runtime is not enabled for this provider", {
164
+ code: "BROWSER_RUNTIME_UNSUPPORTED",
165
+ fix: 'Set provider runtime to "browser" to use ctx.browser',
166
+ });
176
167
  },
177
168
  async rawPage() {
178
- throw new ProviderError(
179
- "Browser runtime is not enabled for this provider",
180
- {
181
- code: "BROWSER_RUNTIME_UNSUPPORTED",
182
- fix: 'Set provider runtime to "browser" and APIFUSE__CDP_POOL__URL to use ctx.browser.rawPage',
183
- },
184
- );
169
+ throw new ProviderError("Browser runtime is not enabled for this provider", {
170
+ code: "BROWSER_RUNTIME_UNSUPPORTED",
171
+ fix: 'Set provider runtime to "browser" and APIFUSE__CDP_POOL__URL to use ctx.browser.rawPage',
172
+ });
185
173
  },
186
174
  async withIsolatedContext() {
187
- throw new ProviderError(
188
- "Browser runtime is not enabled for this provider",
189
- {
190
- code: "BROWSER_RUNTIME_UNSUPPORTED",
191
- fix: 'Set provider runtime to "browser" to use ctx.browser.withIsolatedContext',
192
- },
193
- );
175
+ throw new ProviderError("Browser runtime is not enabled for this provider", {
176
+ code: "BROWSER_RUNTIME_UNSUPPORTED",
177
+ fix: 'Set provider runtime to "browser" to use ctx.browser.withIsolatedContext',
178
+ });
194
179
  },
195
180
  async solveChallenge() {
196
- throw new ProviderError(
197
- "Browser runtime is not enabled for this provider",
198
- {
199
- code: "BROWSER_RUNTIME_UNSUPPORTED",
200
- fix: 'Set provider runtime to "browser" to use ctx.browser.solveChallenge',
201
- },
202
- );
181
+ throw new ProviderError("Browser runtime is not enabled for this provider", {
182
+ code: "BROWSER_RUNTIME_UNSUPPORTED",
183
+ fix: 'Set provider runtime to "browser" to use ctx.browser.solveChallenge',
184
+ });
203
185
  },
204
186
  };
205
187
  }
@@ -216,10 +198,7 @@ function createUnsupportedAuthStub() {
216
198
  };
217
199
  }
218
200
 
219
- function assertProviderDefinition(
220
- value: unknown,
221
- providerPath: string,
222
- ): ProviderDefinition {
201
+ function assertProviderDefinition(value: unknown, providerPath: string): ProviderDefinition {
223
202
  if (!isProviderDefinition(value)) {
224
203
  throw new Error(
225
204
  `Expected ${resolve(providerPath, "index.ts")} to export default defineProvider(...)`,
@@ -230,11 +209,7 @@ function assertProviderDefinition(
230
209
  }
231
210
 
232
211
  function isProviderDefinition(value: unknown): value is ProviderDefinition {
233
- if (
234
- !isRecord(value) ||
235
- !isRecord(value.meta) ||
236
- !isRecord(value.operations)
237
- ) {
212
+ if (!isRecord(value) || !isRecord(value.meta) || !isRecord(value.operations)) {
238
213
  return false;
239
214
  }
240
215
 
@@ -1,19 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
- import {
4
- type ChildProcess,
5
- execFileSync,
6
- spawn,
7
- spawnSync,
8
- } from "node:child_process";
9
- import {
10
- existsSync,
11
- mkdirSync,
12
- mkdtempSync,
13
- readFileSync,
14
- rmSync,
15
- writeFileSync,
16
- } from "node:fs";
3
+ import { type ChildProcess, execFileSync, spawn, spawnSync } from "node:child_process";
4
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
17
5
  import { createServer } from "node:net";
18
6
  import { tmpdir } from "node:os";
19
7
  import { join, resolve } from "node:path";
@@ -41,9 +29,7 @@ const PING_RESPONSE_SCHEMA = z.object({
41
29
 
42
30
  const KEEP_TEMP = process.env.APIFUSE__PACK_SMOKE__KEEP_TEMP === "1";
43
31
 
44
- const tempRoot = mkdtempSync(
45
- join(tmpdir(), "apifuse-provider-sdk-pack-smoke-"),
46
- );
32
+ const tempRoot = mkdtempSync(join(tmpdir(), "apifuse-provider-sdk-pack-smoke-"));
47
33
  const packDir = join(tempRoot, "pack");
48
34
  const consumerDir = join(tempRoot, "consumer");
49
35
  const externalWorkspaceDir = join(tempRoot, "external-workspace");
@@ -81,15 +67,7 @@ try {
81
67
 
82
68
  run(
83
69
  "bun",
84
- [
85
- cliBin,
86
- "create",
87
- "dx-smoke",
88
- "--yes",
89
- "--json",
90
- "--sdk-specifier",
91
- tarballSpecifier,
92
- ],
70
+ [cliBin, "create", "dx-smoke", "--yes", "--json", "--sdk-specifier", tarballSpecifier],
93
71
  consumerDir,
94
72
  );
95
73
 
@@ -99,11 +77,7 @@ try {
99
77
  run("bun", ["run", "test"], generatedProviderDir);
100
78
  assertGeneratedReadme(generatedProviderDir);
101
79
  await smokeGeneratedDevServer(generatedProviderDir);
102
- assertExternalWorkspaceTopology(
103
- cliBin,
104
- externalWorkspaceDir,
105
- tarballSpecifier,
106
- );
80
+ assertExternalWorkspaceTopology(cliBin, externalWorkspaceDir, tarballSpecifier);
107
81
 
108
82
  console.log(
109
83
  `Provider SDK packed-artifact smoke passed: ${tarballPath} -> ${generatedProviderDir}`,
@@ -148,15 +122,8 @@ function assertExternalWorkspaceTopology(
148
122
  externalWorkspaceDir,
149
123
  );
150
124
 
151
- const generatedProviderDir = join(
152
- externalWorkspaceDir,
153
- "external-workspace-smoke",
154
- );
155
- const forbiddenProviderDir = join(
156
- externalWorkspaceDir,
157
- "providers",
158
- "external-workspace-smoke",
159
- );
125
+ const generatedProviderDir = join(externalWorkspaceDir, "external-workspace-smoke");
126
+ const forbiddenProviderDir = join(externalWorkspaceDir, "providers", "external-workspace-smoke");
160
127
  if (!existsSync(generatedProviderDir)) {
161
128
  throw new Error(
162
129
  "Public create must generate a one-provider repository at <name>/ even when providers/ exists.",
@@ -168,9 +135,7 @@ function assertExternalWorkspaceTopology(
168
135
  );
169
136
  }
170
137
 
171
- const packageJson = JSON.parse(
172
- readFileSync(join(generatedProviderDir, "package.json"), "utf8"),
173
- );
138
+ const packageJson = JSON.parse(readFileSync(join(generatedProviderDir, "package.json"), "utf8"));
174
139
  const sdkDependency = packageJson?.dependencies?.["@apifuse/provider-sdk"];
175
140
  if (sdkDependency !== tarballSpecifier) {
176
141
  throw new Error(
@@ -178,9 +143,7 @@ function assertExternalWorkspaceTopology(
178
143
  );
179
144
  }
180
145
  if (JSON.stringify(packageJson).includes("workspace:")) {
181
- throw new Error(
182
- "External bounty workspace scaffold must not contain workspace: dependencies.",
183
- );
146
+ throw new Error("External bounty workspace scaffold must not contain workspace: dependencies.");
184
147
  }
185
148
 
186
149
  run("bun", ["install"], generatedProviderDir);
@@ -199,30 +162,20 @@ function assertExternalWorkspaceTopology(
199
162
  },
200
163
  );
201
164
  if (monorepoAttempt.status === 0) {
202
- throw new Error(
203
- "--preset monorepo must reject outside the private APIFuse monorepo.",
204
- );
165
+ throw new Error("--preset monorepo must reject outside the private APIFuse monorepo.");
205
166
  }
206
167
  const rejectionOutput = `${monorepoAttempt.stdout}\n${monorepoAttempt.stderr}`;
207
- if (
208
- !rejectionOutput.includes(
209
- "Monorepo preset is internal to the APIFuse repository",
210
- )
211
- ) {
168
+ if (!rejectionOutput.includes("Monorepo preset is internal to the APIFuse repository")) {
212
169
  throw new Error(`Unexpected monorepo rejection output: ${rejectionOutput}`);
213
170
  }
214
171
  }
215
172
 
216
173
  function packSdk(destination: string): { filename: string } {
217
- const raw = execFileSync(
218
- "npm",
219
- ["pack", "--json", "--pack-destination", destination],
220
- {
221
- cwd: process.cwd(),
222
- encoding: "utf8",
223
- stdio: ["ignore", "pipe", "inherit"],
224
- },
225
- );
174
+ const raw = execFileSync("npm", ["pack", "--json", "--pack-destination", destination], {
175
+ cwd: process.cwd(),
176
+ encoding: "utf8",
177
+ stdio: ["ignore", "pipe", "inherit"],
178
+ });
226
179
  const parsed = PACK_RESULT_SCHEMA.parse(JSON.parse(raw));
227
180
  const first = parsed[0];
228
181
  if (!first) {
@@ -252,29 +205,19 @@ function run(command: string, args: string[], cwd: string): void {
252
205
  function assertGeneratedReadme(providerDir: string): void {
253
206
  const readme = readFileSync(join(providerDir, "README.md"), "utf8");
254
207
  if (!readme.includes('"requestId":"req_local_ping"')) {
255
- throw new Error(
256
- "Generated README is missing requestId in local smoke docs.",
257
- );
208
+ throw new Error("Generated README is missing requestId in local smoke docs.");
258
209
  }
259
210
  if (readme.includes('"connection":null')) {
260
- throw new Error(
261
- "Generated README must not document connection:null for no-auth local smoke.",
262
- );
211
+ throw new Error("Generated README must not document connection:null for no-auth local smoke.");
263
212
  }
264
213
  if (!readme.includes("bunx playwright install chromium")) {
265
- throw new Error(
266
- "Generated README is missing browser runtime troubleshooting guidance.",
267
- );
214
+ throw new Error("Generated README is missing browser runtime troubleshooting guidance.");
268
215
  }
269
216
  if (!readme.includes("impit")) {
270
- throw new Error(
271
- "Generated README is missing impit stealth runtime guidance.",
272
- );
217
+ throw new Error("Generated README is missing impit stealth runtime guidance.");
273
218
  }
274
219
  if (!readme.includes("bun run submit-check")) {
275
- throw new Error(
276
- "Generated README must document the submit-check pre-submission workflow.",
277
- );
220
+ throw new Error("Generated README must document the submit-check pre-submission workflow.");
278
221
  }
279
222
  if (!readme.includes("bun run record -- --operation <operation>")) {
280
223
  throw new Error(
@@ -368,9 +311,7 @@ async function waitForHttp(
368
311
 
369
312
  while (Date.now() < deadline) {
370
313
  if (server.exitCode !== null) {
371
- throw new Error(
372
- `Dev server exited early with code ${server.exitCode}\n${getOutput()}`,
373
- );
314
+ throw new Error(`Dev server exited early with code ${server.exitCode}\n${getOutput()}`);
374
315
  }
375
316
 
376
317
  try {