@apifuse/provider-sdk 2.1.0-beta.9 → 2.2.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 (263) hide show
  1. package/AUTHORING.md +240 -0
  2. package/CHANGELOG.md +93 -0
  3. package/README.md +26 -10
  4. package/SUBMISSION.md +11 -12
  5. package/bin/apifuse-check.ts +44 -59
  6. package/bin/apifuse-create.ts +1 -1
  7. package/bin/apifuse-dev.ts +27 -52
  8. package/bin/apifuse-pack-check.ts +36 -0
  9. package/bin/apifuse-pack-smoke.ts +22 -81
  10. package/bin/apifuse-pack-types.ts +266 -0
  11. package/bin/apifuse-perf.ts +45 -127
  12. package/bin/apifuse-record.ts +53 -70
  13. package/bin/apifuse-submit-check.ts +2177 -353
  14. package/bin/apifuse-sync-assets.ts +117 -0
  15. package/bin/apifuse.ts +1 -1
  16. package/bin/submit-check-delimited-text.ts +50 -0
  17. package/bin/submit-check-xml-semantics.ts +204 -0
  18. package/bin/submit-check-xml.ts +134 -0
  19. package/dist/auth-turn/auth-turn.v1.schema.json +89 -0
  20. package/dist/auth-turn/fixtures/invalid/empty-kind.json +4 -0
  21. package/dist/auth-turn/fixtures/invalid/expires-at-not-string.json +5 -0
  22. package/dist/auth-turn/fixtures/invalid/missing-kind.json +3 -0
  23. package/dist/auth-turn/fixtures/invalid/missing-turn-id.json +3 -0
  24. package/dist/auth-turn/fixtures/invalid/timing-unknown-field.json +7 -0
  25. package/dist/auth-turn/fixtures/invalid/turn-id-snake-case.json +4 -0
  26. package/dist/auth-turn/fixtures/invalid/unknown-top-level-field.json +5 -0
  27. package/dist/auth-turn/fixtures/valid/abort.json +8 -0
  28. package/dist/auth-turn/fixtures/valid/challenge.json +17 -0
  29. package/dist/auth-turn/fixtures/valid/complete.json +13 -0
  30. package/dist/auth-turn/fixtures/valid/form.json +14 -0
  31. package/dist/auth-turn/fixtures/valid/message.json +13 -0
  32. package/dist/auth-turn/fixtures/valid/multi_choice.json +15 -0
  33. package/dist/auth-turn/fixtures/valid/pending.json +5 -0
  34. package/dist/auth-turn/fixtures/valid/poll.json +9 -0
  35. package/dist/auth-turn/fixtures/valid/redirect.json +16 -0
  36. package/dist/auth-turn/fixtures/valid/retry.json +8 -0
  37. package/dist/auth-turn/fixtures/valid/unknown-kind.json +7 -0
  38. package/dist/auth-turn/index.d.ts +195 -0
  39. package/dist/auth-turn/index.js +133 -0
  40. package/dist/auth.d.ts +76 -0
  41. package/dist/auth.js +427 -0
  42. package/dist/ceremonies/index.d.ts +1 -1
  43. package/dist/ceremonies/index.js +14 -48
  44. package/dist/cli/commands.d.ts +1 -1
  45. package/dist/cli/commands.js +8 -0
  46. package/dist/cli/create.d.ts +3 -0
  47. package/dist/cli/create.js +47 -33
  48. package/dist/cli/prompt-assets.d.ts +80 -0
  49. package/dist/cli/prompt-assets.js +743 -0
  50. package/dist/cli/templates/provider/.agents/skills/fixtures-and-recording/SKILL.md.tpl +58 -0
  51. package/dist/cli/templates/provider/.agents/skills/health-checks-and-fail-closed/SKILL.md.tpl +65 -0
  52. package/dist/cli/templates/provider/.agents/skills/normalization-standards/SKILL.md.tpl +57 -0
  53. package/dist/cli/templates/provider/.agents/skills/pagination-and-counts/SKILL.md.tpl +52 -0
  54. package/dist/cli/templates/provider/.agents/skills/upstream-contract-verification/SKILL.md.tpl +45 -0
  55. package/dist/cli/templates/provider/.agents/skills/upstream-notes/README.md.tpl +13 -0
  56. package/dist/cli/templates/provider/.dockerignore.tpl +22 -0
  57. package/dist/cli/templates/provider/.gitignore.tpl +22 -0
  58. package/dist/cli/templates/provider/AGENTS.md.tpl +96 -0
  59. package/dist/cli/templates/provider/Dockerfile.tpl +7 -0
  60. package/dist/cli/templates/provider/README.md.tpl +163 -0
  61. package/dist/cli/templates/provider/dev.ts.tpl +5 -0
  62. package/dist/cli/templates/provider/domain/README.md.tpl +3 -0
  63. package/dist/cli/templates/provider/index.test.ts.tpl +13 -0
  64. package/dist/cli/templates/provider/index.ts.tpl +15 -0
  65. package/dist/cli/templates/provider/mappers/README.md.tpl +3 -0
  66. package/dist/cli/templates/provider/meta.ts.tpl +7 -0
  67. package/dist/cli/templates/provider/operations/index.ts.tpl +5 -0
  68. package/dist/cli/templates/provider/operations/ping.ts.tpl +24 -0
  69. package/dist/cli/templates/provider/schemas/ping.ts.tpl +24 -0
  70. package/dist/cli/templates/provider/start.ts.tpl +5 -0
  71. package/dist/cli/templates/provider/upstream/README.md.tpl +3 -0
  72. package/dist/config/loader.d.ts +149 -8
  73. package/dist/config/loader.js +378 -89
  74. package/dist/contract-serialization.d.ts +2 -2
  75. package/dist/contract-serialization.js +3 -6
  76. package/dist/contract-types.d.ts +2 -2
  77. package/dist/contract.d.ts +3 -3
  78. package/dist/contract.js +5 -6
  79. package/dist/define.d.ts +13 -1
  80. package/dist/define.js +245 -178
  81. package/dist/dev.d.ts +1 -1
  82. package/dist/dev.js +1 -1
  83. package/dist/errors.d.ts +4 -1
  84. package/dist/errors.js +48 -0
  85. package/dist/i18n/catalog.d.ts +2 -2
  86. package/dist/i18n/catalog.js +4 -10
  87. package/dist/i18n/index.d.ts +2 -2
  88. package/dist/i18n/index.js +2 -2
  89. package/dist/i18n/keys.d.ts +2 -2
  90. package/dist/index.d.ts +44 -41
  91. package/dist/index.js +39 -36
  92. package/dist/lint.d.ts +1 -0
  93. package/dist/lint.js +35 -15
  94. package/dist/provider.d.ts +11 -9
  95. package/dist/provider.js +9 -8
  96. package/dist/public-schema-field-lint.d.ts +1 -1
  97. package/dist/recipes/gov-api.js +1 -1
  98. package/dist/runtime/auth-flow.d.ts +1 -1
  99. package/dist/runtime/auth-flow.js +4 -2
  100. package/dist/runtime/browser.d.ts +1 -1
  101. package/dist/runtime/browser.js +214 -25
  102. package/dist/runtime/cache.d.ts +1 -1
  103. package/dist/runtime/cache.js +4 -8
  104. package/dist/runtime/choice.d.ts +1 -1
  105. package/dist/runtime/choice.js +31 -35
  106. package/dist/runtime/credential.d.ts +1 -1
  107. package/dist/runtime/credential.js +1 -1
  108. package/dist/runtime/env.d.ts +1 -1
  109. package/dist/runtime/executor.d.ts +1 -1
  110. package/dist/runtime/executor.js +15 -3
  111. package/dist/runtime/http.d.ts +2 -2
  112. package/dist/runtime/http.js +160 -344
  113. package/dist/runtime/insights.d.ts +1 -1
  114. package/dist/runtime/insights.js +6 -13
  115. package/dist/runtime/instrumentation.d.ts +2 -2
  116. package/dist/runtime/instrumentation.js +56 -19
  117. package/dist/runtime/keyring.js +1 -1
  118. package/dist/runtime/namespace.js +1 -1
  119. package/dist/runtime/otlp.d.ts +1 -1
  120. package/dist/runtime/perf.d.ts +1 -1
  121. package/dist/runtime/provider.d.ts +1 -1
  122. package/dist/runtime/provider.js +1 -2
  123. package/dist/runtime/proxy-errors.d.ts +1 -1
  124. package/dist/runtime/proxy-errors.js +9 -7
  125. package/dist/runtime/proxy-nodemaven.d.ts +35 -0
  126. package/dist/runtime/proxy-nodemaven.js +128 -0
  127. package/dist/runtime/proxy-retry-policy.d.ts +40 -0
  128. package/dist/runtime/proxy-retry-policy.js +326 -0
  129. package/dist/runtime/proxy-telemetry.d.ts +2 -1
  130. package/dist/runtime/proxy-telemetry.js +55 -52
  131. package/dist/runtime/redis.d.ts +1 -1
  132. package/dist/runtime/redis.js +2 -4
  133. package/dist/runtime/request-options.d.ts +1 -1
  134. package/dist/runtime/secrets.d.ts +27 -0
  135. package/dist/runtime/secrets.js +51 -0
  136. package/dist/runtime/state.d.ts +2 -2
  137. package/dist/runtime/state.js +15 -4
  138. package/dist/runtime/stealth.d.ts +7 -4
  139. package/dist/runtime/stealth.js +257 -215
  140. package/dist/runtime/stt.d.ts +1 -1
  141. package/dist/runtime/stt.js +11 -15
  142. package/dist/runtime/trace.d.ts +2 -2
  143. package/dist/runtime/trace.js +2 -4
  144. package/dist/runtime/waterfall.d.ts +1 -1
  145. package/dist/schema.d.ts +1 -1
  146. package/dist/schema.js +7 -15
  147. package/dist/serve.d.ts +1 -1
  148. package/dist/serve.js +1 -1
  149. package/dist/server/index.d.ts +7 -3
  150. package/dist/server/index.js +6 -2
  151. package/dist/server/self-test-input-tokens.d.ts +1 -0
  152. package/dist/server/self-test-input-tokens.js +37 -0
  153. package/dist/server/self-test-redaction.d.ts +20 -0
  154. package/dist/server/self-test-redaction.js +70 -0
  155. package/dist/server/self-test-token.d.ts +30 -0
  156. package/dist/server/self-test-token.js +50 -0
  157. package/dist/server/self-test.d.ts +199 -0
  158. package/dist/server/self-test.js +1113 -0
  159. package/dist/server/serve.d.ts +14 -3
  160. package/dist/server/serve.js +135 -64
  161. package/dist/server/types.d.ts +10 -9
  162. package/dist/server/types.js +3 -7
  163. package/dist/stealth/profiles.d.ts +1 -1
  164. package/dist/stealth/profiles.js +5 -14
  165. package/dist/stream.d.ts +1 -1
  166. package/dist/testing/index.d.ts +2 -2
  167. package/dist/testing/index.js +2 -2
  168. package/dist/testing/run.d.ts +1 -1
  169. package/dist/testing/run.js +12 -15
  170. package/dist/types.d.ts +237 -1
  171. package/dist/user-input.d.ts +30 -0
  172. package/dist/user-input.js +66 -0
  173. package/package.json +16 -5
  174. package/src/auth-turn/auth-turn.v1.schema.json +89 -0
  175. package/src/auth-turn/fixtures/invalid/empty-kind.json +4 -0
  176. package/src/auth-turn/fixtures/invalid/expires-at-not-string.json +5 -0
  177. package/src/auth-turn/fixtures/invalid/missing-kind.json +3 -0
  178. package/src/auth-turn/fixtures/invalid/missing-turn-id.json +3 -0
  179. package/src/auth-turn/fixtures/invalid/timing-unknown-field.json +7 -0
  180. package/src/auth-turn/fixtures/invalid/turn-id-snake-case.json +4 -0
  181. package/src/auth-turn/fixtures/invalid/unknown-top-level-field.json +5 -0
  182. package/src/auth-turn/fixtures/valid/abort.json +8 -0
  183. package/src/auth-turn/fixtures/valid/challenge.json +17 -0
  184. package/src/auth-turn/fixtures/valid/complete.json +13 -0
  185. package/src/auth-turn/fixtures/valid/form.json +14 -0
  186. package/src/auth-turn/fixtures/valid/message.json +13 -0
  187. package/src/auth-turn/fixtures/valid/multi_choice.json +15 -0
  188. package/src/auth-turn/fixtures/valid/pending.json +5 -0
  189. package/src/auth-turn/fixtures/valid/poll.json +9 -0
  190. package/src/auth-turn/fixtures/valid/redirect.json +16 -0
  191. package/src/auth-turn/fixtures/valid/retry.json +8 -0
  192. package/src/auth-turn/fixtures/valid/unknown-kind.json +7 -0
  193. package/src/auth-turn/index.ts +177 -0
  194. package/src/auth.ts +728 -0
  195. package/src/ceremonies/index.ts +33 -121
  196. package/src/cli/commands.ts +10 -0
  197. package/src/cli/create.ts +69 -99
  198. package/src/cli/prompt-assets.ts +865 -0
  199. package/src/cli/templates/provider/.agents/skills/fixtures-and-recording/SKILL.md.tpl +58 -0
  200. package/src/cli/templates/provider/.agents/skills/health-checks-and-fail-closed/SKILL.md.tpl +65 -0
  201. package/src/cli/templates/provider/.agents/skills/normalization-standards/SKILL.md.tpl +57 -0
  202. package/src/cli/templates/provider/.agents/skills/pagination-and-counts/SKILL.md.tpl +52 -0
  203. package/src/cli/templates/provider/.agents/skills/upstream-contract-verification/SKILL.md.tpl +45 -0
  204. package/src/cli/templates/provider/.agents/skills/upstream-notes/README.md.tpl +13 -0
  205. package/src/cli/templates/provider/AGENTS.md.tpl +96 -0
  206. package/src/cli/templates/provider/README.md.tpl +7 -4
  207. package/src/config/loader.ts +543 -208
  208. package/src/contract-serialization.ts +5 -11
  209. package/src/contract-types.ts +2 -2
  210. package/src/contract.ts +13 -28
  211. package/src/define.ts +397 -528
  212. package/src/dev.ts +4 -9
  213. package/src/errors.ts +58 -5
  214. package/src/i18n/catalog.ts +10 -32
  215. package/src/i18n/index.ts +2 -2
  216. package/src/i18n/keys.ts +5 -11
  217. package/src/index.ts +64 -41
  218. package/src/lint.ts +122 -159
  219. package/src/provider.ts +40 -9
  220. package/src/public-schema-field-lint.ts +7 -33
  221. package/src/recipes/gov-api.ts +2 -5
  222. package/src/runtime/auth-flow.ts +6 -6
  223. package/src/runtime/browser.ts +320 -151
  224. package/src/runtime/cache.ts +20 -67
  225. package/src/runtime/choice.ts +79 -132
  226. package/src/runtime/credential.ts +2 -2
  227. package/src/runtime/env.ts +1 -1
  228. package/src/runtime/executor.ts +23 -20
  229. package/src/runtime/http.ts +216 -539
  230. package/src/runtime/insights.ts +15 -53
  231. package/src/runtime/instrumentation.ts +78 -65
  232. package/src/runtime/keyring.ts +7 -19
  233. package/src/runtime/namespace.ts +2 -7
  234. package/src/runtime/otlp.ts +12 -23
  235. package/src/runtime/perf.ts +1 -1
  236. package/src/runtime/provider.ts +4 -9
  237. package/src/runtime/proxy-errors.ts +29 -42
  238. package/src/runtime/proxy-nodemaven.ts +178 -0
  239. package/src/runtime/proxy-retry-policy.ts +469 -0
  240. package/src/runtime/proxy-telemetry.ts +79 -77
  241. package/src/runtime/redis.ts +4 -12
  242. package/src/runtime/request-options.ts +4 -13
  243. package/src/runtime/secrets.ts +64 -0
  244. package/src/runtime/state.ts +41 -110
  245. package/src/runtime/stealth.ts +331 -369
  246. package/src/runtime/stt.ts +38 -94
  247. package/src/runtime/trace.ts +14 -44
  248. package/src/runtime/waterfall.ts +5 -18
  249. package/src/schema.ts +23 -84
  250. package/src/serve.ts +1 -1
  251. package/src/server/index.ts +44 -3
  252. package/src/server/self-test-input-tokens.ts +46 -0
  253. package/src/server/self-test-redaction.ts +97 -0
  254. package/src/server/self-test-token.ts +70 -0
  255. package/src/server/self-test.ts +1450 -0
  256. package/src/server/serve.ts +206 -216
  257. package/src/server/types.ts +7 -19
  258. package/src/stealth/profiles.ts +10 -26
  259. package/src/stream.ts +8 -19
  260. package/src/testing/index.ts +2 -2
  261. package/src/testing/run.ts +24 -64
  262. package/src/types.ts +274 -1
  263. package/src/user-input.ts +118 -0
@@ -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
 
@@ -36,6 +36,9 @@ const requiredPaths = [
36
36
  "bin/apifuse-submit-check.ts",
37
37
  "SUBMISSION.md",
38
38
  "src/cli/create.ts",
39
+ "src/cli/templates/provider/.dockerignore.tpl",
40
+ "src/cli/templates/provider/.gitignore.tpl",
41
+ "src/cli/templates/provider/Dockerfile.tpl",
39
42
  "src/cli/templates/provider/index.ts.tpl",
40
43
  "src/cli/templates/provider/README.md.tpl",
41
44
  "src/cli/templates/provider/meta.ts.tpl",
@@ -45,6 +48,39 @@ const requiredPaths = [
45
48
  "src/cli/templates/provider/operations/ping.ts.tpl",
46
49
  "src/cli/templates/provider/schemas/ping.ts.tpl",
47
50
  "src/cli/templates/provider/upstream/README.md.tpl",
51
+ "src/cli/templates/provider/AGENTS.md.tpl",
52
+ "src/cli/templates/provider/.agents/skills/normalization-standards/SKILL.md.tpl",
53
+ "src/cli/templates/provider/.agents/skills/upstream-contract-verification/SKILL.md.tpl",
54
+ "src/cli/templates/provider/.agents/skills/fixtures-and-recording/SKILL.md.tpl",
55
+ "src/cli/templates/provider/.agents/skills/pagination-and-counts/SKILL.md.tpl",
56
+ "src/cli/templates/provider/.agents/skills/health-checks-and-fail-closed/SKILL.md.tpl",
57
+ "src/cli/templates/provider/.agents/skills/upstream-notes/README.md.tpl",
58
+ "dist/cli/templates/provider/.dockerignore.tpl",
59
+ "dist/cli/templates/provider/.gitignore.tpl",
60
+ "dist/cli/templates/provider/Dockerfile.tpl",
61
+ "dist/cli/templates/provider/index.ts.tpl",
62
+ "dist/cli/templates/provider/README.md.tpl",
63
+ "dist/cli/templates/provider/meta.ts.tpl",
64
+ "dist/cli/templates/provider/domain/README.md.tpl",
65
+ "dist/cli/templates/provider/mappers/README.md.tpl",
66
+ "dist/cli/templates/provider/operations/index.ts.tpl",
67
+ "dist/cli/templates/provider/operations/ping.ts.tpl",
68
+ "dist/cli/templates/provider/schemas/ping.ts.tpl",
69
+ "dist/cli/templates/provider/upstream/README.md.tpl",
70
+ "dist/cli/templates/provider/AGENTS.md.tpl",
71
+ "dist/cli/templates/provider/.agents/skills/normalization-standards/SKILL.md.tpl",
72
+ "dist/cli/templates/provider/.agents/skills/upstream-contract-verification/SKILL.md.tpl",
73
+ "dist/cli/templates/provider/.agents/skills/fixtures-and-recording/SKILL.md.tpl",
74
+ "dist/cli/templates/provider/.agents/skills/pagination-and-counts/SKILL.md.tpl",
75
+ "dist/cli/templates/provider/.agents/skills/health-checks-and-fail-closed/SKILL.md.tpl",
76
+ "dist/cli/templates/provider/.agents/skills/upstream-notes/README.md.tpl",
77
+ "dist/auth-turn/index.js",
78
+ "dist/auth-turn/index.d.ts",
79
+ "dist/auth-turn/auth-turn.v1.schema.json",
80
+ "dist/auth-turn/fixtures/valid/abort.json",
81
+ "dist/auth-turn/fixtures/valid/complete.json",
82
+ "dist/auth-turn/fixtures/invalid/unknown-top-level-field.json",
83
+ "src/auth-turn/auth-turn.v1.schema.json",
48
84
  ];
49
85
  const forbiddenMatches = filePaths.filter(
50
86
  (path) =>
@@ -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 {
@@ -0,0 +1,266 @@
1
+ #!/usr/bin/env bun
2
+
3
+ // Guards the published artifact against the nodenext type-resolution regression
4
+ // shipped in 2.2.0-beta.1/beta.2: emitted dist/*.d.ts (and dist/*.js) carried
5
+ // EXTENSIONLESS relative imports, so external provider repos compiling with
6
+ // moduleResolution nodenext could not resolve them. With skipLibCheck the
7
+ // errors were masked and types silently degraded — re-exported ProviderError
8
+ // lost its inherited Error members (name/message) and re-exported zod stopped
9
+ // inferring .refine() callback params (~17 false type errors in
10
+ // apifuse-provider-tabelog, see its PR #16).
11
+ //
12
+ // Three layers, all against the packed tarball (what consumers actually get):
13
+ // 1. @arethetypeswrong/cli — resolution/module-kind audit of every exports
14
+ // subpath, including internal resolution of relative specifiers in d.ts.
15
+ // 2. A fixture consumer compiled with real tsc under moduleResolution
16
+ // nodenext and skipLibCheck:false, asserting ProviderError keeps
17
+ // inherited Error members and zod re-exports keep .refine() inference.
18
+ // 3. A Node (not bun) ESM runtime import — node does not tolerate
19
+ // extensionless relative specifiers, bun does.
20
+ //
21
+ // A deliberate negative control proves the fixture compiler actually fails on
22
+ // type errors, so this check cannot rot into a false-positive green.
23
+
24
+ import { execFileSync, spawnSync } from "node:child_process";
25
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
26
+ import { tmpdir } from "node:os";
27
+ import { join, resolve } from "node:path";
28
+ import { z } from "zod";
29
+
30
+ const PACK_RESULT_SCHEMA = z.array(
31
+ z.object({
32
+ filename: z.string(),
33
+ }),
34
+ );
35
+
36
+ const KEEP_TEMP = process.env.APIFUSE__PACK_TYPES__KEEP_TEMP === "1";
37
+ const sdkRoot = process.cwd();
38
+
39
+ const tempRoot = mkdtempSync(join(tmpdir(), "apifuse-provider-sdk-pack-types-"));
40
+ const packDir = join(tempRoot, "pack");
41
+ const consumerDir = join(tempRoot, "consumer");
42
+
43
+ try {
44
+ mkdirSync(packDir, { recursive: true });
45
+ mkdirSync(consumerDir, { recursive: true });
46
+
47
+ const packed = packSdk(packDir);
48
+ const tarballPath = resolve(packDir, packed.filename);
49
+
50
+ runAreTheTypesWrong(tarballPath);
51
+ setUpFixtureConsumer(consumerDir, tarballPath);
52
+ run("bun", ["install"], consumerDir);
53
+ compileFixtureConsumer(consumerDir);
54
+ assertNegativeControlFails(consumerDir);
55
+ runNodeRuntimeImport(consumerDir);
56
+
57
+ console.log(`Packed artifact types OK under nodenext: ${tarballPath}`);
58
+ } finally {
59
+ if (KEEP_TEMP) {
60
+ console.log(`Keeping pack-types temp directory: ${tempRoot}`);
61
+ } else {
62
+ rmSync(tempRoot, { recursive: true, force: true });
63
+ }
64
+ }
65
+
66
+ function runAreTheTypesWrong(tarballPath: string): void {
67
+ // esm-only profile: this package publishes ESM only, so CJS-consumer
68
+ // resolution failures are expected and out of scope for this guard.
69
+ run(
70
+ "bun",
71
+ [join(sdkRoot, "node_modules", ".bin", "attw"), tarballPath, "--profile", "esm-only"],
72
+ sdkRoot,
73
+ );
74
+ }
75
+
76
+ function setUpFixtureConsumer(consumerDir: string, tarballPath: string): void {
77
+ writeFileSync(
78
+ join(consumerDir, "package.json"),
79
+ `${JSON.stringify(
80
+ {
81
+ private: true,
82
+ type: "module",
83
+ dependencies: {
84
+ "@apifuse/provider-sdk": `file:${tarballPath}`,
85
+ },
86
+ devDependencies: {
87
+ // Pinned to TS 5.x independently of the repo's own TypeScript 7
88
+ // toolchain: this must reflect what external provider polyrepos
89
+ // (e.g. apifuse-provider-tabelog) run in their CI.
90
+ typescript: "^5.9.3",
91
+ // skipLibCheck:false type-checks transitive deps too (ioredis
92
+ // references Buffer), so the fixture needs node types like any
93
+ // real provider repo.
94
+ "@types/node": "^25.9.3",
95
+ },
96
+ },
97
+ null,
98
+ 2,
99
+ )}\n`,
100
+ );
101
+
102
+ writeFileSync(
103
+ join(consumerDir, "tsconfig.json"),
104
+ `${JSON.stringify(
105
+ {
106
+ compilerOptions: {
107
+ target: "ES2022",
108
+ module: "nodenext",
109
+ moduleResolution: "nodenext",
110
+ strict: true,
111
+ // skipLibCheck must stay off: it is exactly what masked the
112
+ // extensionless-d.ts regression downstream.
113
+ skipLibCheck: false,
114
+ noEmit: true,
115
+ },
116
+ include: ["consumer.ts"],
117
+ },
118
+ null,
119
+ 2,
120
+ )}\n`,
121
+ );
122
+
123
+ writeFileSync(
124
+ join(consumerDir, "consumer.ts"),
125
+ [
126
+ 'import { ProviderError, SessionExpiredError, z } from "@apifuse/provider-sdk";',
127
+ 'import { defineCredentialsAuth } from "@apifuse/provider-sdk/provider";',
128
+ 'import { extractProviderContract } from "@apifuse/provider-sdk/contract";',
129
+ 'import { AUTH_TURN_SCHEMA } from "@apifuse/provider-sdk/auth-turn";',
130
+ 'import { serve } from "@apifuse/provider-sdk/server";',
131
+ 'import { runStandardTests } from "@apifuse/provider-sdk/testing";',
132
+ "",
133
+ "// ProviderError must keep its inherited Error members under nodenext.",
134
+ "// When dist d.ts imports fail to resolve, the class type degrades and",
135
+ "// name/message disappear (the tabelog false-error mode).",
136
+ 'const providerError = new ProviderError("boom");',
137
+ "const inheritedName: string = providerError.name;",
138
+ "const inheritedMessage: string = providerError.message;",
139
+ "const inheritedStack: string | undefined = providerError.stack;",
140
+ "const isError: Error = providerError;",
141
+ "const sessionExpired: ProviderError = new SessionExpiredError();",
142
+ "",
143
+ "// Re-exported zod must keep .refine() callback parameter inference.",
144
+ "const refined = z.object({ shopId: z.string() }).refine((value) => value.shopId.length > 0);",
145
+ 'const refinedString = z.string().refine((value) => value.startsWith("tabelog:"));',
146
+ "",
147
+ "export const witnesses = {",
148
+ " inheritedName,",
149
+ " inheritedMessage,",
150
+ " inheritedStack,",
151
+ " isError,",
152
+ " sessionExpired,",
153
+ " refined,",
154
+ " refinedString,",
155
+ " defineCredentialsAuth,",
156
+ " extractProviderContract,",
157
+ " AUTH_TURN_SCHEMA,",
158
+ " serve,",
159
+ " runStandardTests,",
160
+ "};",
161
+ "",
162
+ ].join("\n"),
163
+ );
164
+
165
+ writeFileSync(
166
+ join(consumerDir, "negative-control.ts"),
167
+ [
168
+ 'import { ProviderError } from "@apifuse/provider-sdk";',
169
+ "",
170
+ "// Intentionally wrong: name is a string. If this file compiles, the",
171
+ "// fixture consumer is not actually type-checking and the guard is void.",
172
+ 'export const mustNotCompile: number = new ProviderError("boom").name;',
173
+ "",
174
+ ].join("\n"),
175
+ );
176
+ }
177
+
178
+ function compileFixtureConsumer(consumerDir: string): void {
179
+ run(
180
+ "bun",
181
+ [join(consumerDir, "node_modules", ".bin", "tsc"), "-p", "tsconfig.json"],
182
+ consumerDir,
183
+ );
184
+ }
185
+
186
+ function assertNegativeControlFails(consumerDir: string): void {
187
+ const result = spawnSync(
188
+ "bun",
189
+ [
190
+ join(consumerDir, "node_modules", ".bin", "tsc"),
191
+ "--target",
192
+ "ES2022",
193
+ "--module",
194
+ "nodenext",
195
+ "--moduleResolution",
196
+ "nodenext",
197
+ "--strict",
198
+ "--noEmit",
199
+ "negative-control.ts",
200
+ ],
201
+ { cwd: consumerDir, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] },
202
+ );
203
+ if (result.status === 0) {
204
+ throw new Error(
205
+ "Negative control compiled cleanly: the fixture consumer is not detecting type errors, so the nodenext guard proves nothing.",
206
+ );
207
+ }
208
+ if (!`${result.stdout}\n${result.stderr}`.includes("TS2322")) {
209
+ throw new Error(
210
+ `Negative control failed for an unexpected reason (wanted TS2322):\n${result.stdout}\n${result.stderr}`,
211
+ );
212
+ }
213
+ }
214
+
215
+ function runNodeRuntimeImport(consumerDir: string): void {
216
+ // Node, not bun: bun resolves extensionless relative specifiers in
217
+ // published dist/*.js, node correctly refuses them (ERR_MODULE_NOT_FOUND).
218
+ run(
219
+ "node",
220
+ [
221
+ "--input-type=module",
222
+ "-e",
223
+ [
224
+ 'const sdk = await import("@apifuse/provider-sdk");',
225
+ 'const error = new sdk.ProviderError("boom");',
226
+ 'if (error.name !== "ProviderError" || error.message !== "boom" || !(error instanceof Error)) {',
227
+ ' throw new Error("ProviderError runtime shape is wrong: " + JSON.stringify({ name: error.name, message: error.message }));',
228
+ "}",
229
+ 'console.log("node ESM runtime import OK");',
230
+ ].join("\n"),
231
+ ],
232
+ consumerDir,
233
+ );
234
+ }
235
+
236
+ function packSdk(destination: string): { filename: string } {
237
+ const raw = execFileSync("npm", ["pack", "--json", "--pack-destination", destination], {
238
+ cwd: sdkRoot,
239
+ encoding: "utf8",
240
+ stdio: ["ignore", "pipe", "inherit"],
241
+ });
242
+ const parsed = PACK_RESULT_SCHEMA.parse(JSON.parse(raw));
243
+ const first = parsed[0];
244
+ if (!first) {
245
+ throw new Error("npm pack --json returned no package metadata.");
246
+ }
247
+ return first;
248
+ }
249
+
250
+ function run(command: string, args: string[], cwd: string): void {
251
+ const result = spawnSync(command, args, {
252
+ cwd,
253
+ env: process.env,
254
+ stdio: "inherit",
255
+ });
256
+
257
+ if (result.error) {
258
+ throw result.error;
259
+ }
260
+
261
+ if (result.status !== 0) {
262
+ throw new Error(
263
+ `Command failed (${[command, ...args].join(" ")}) in ${cwd} with exit code ${result.status}`,
264
+ );
265
+ }
266
+ }