@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
package/AUTHORING.md CHANGED
@@ -18,6 +18,59 @@
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
+
35
+ ### Attempt tokens: the server carries the decisions
36
+
37
+ When a mutation needs more than one user decision (or one decision plus a
38
+ final go/no-go), do not make the agent re-send accumulated state across
39
+ rounds — weak models drop or corrupt it. Split the operation into a
40
+ **prepare/confirm pair** driven by a server-held attempt record:
41
+
42
+ - The prepare operation is non-destructive. A start call takes only the
43
+ scalar intent fields; every response returns a fresh `attempt_token`
44
+ referencing a server-side record (`ctx.choice.issue` with
45
+ `storage.mode: "server"`) that stores every settled decision. Continue
46
+ calls take `attempt_token` plus only the NEW answers.
47
+ - `needs_input` rounds list only the still-pending selections; settled
48
+ decisions may ride along in a display-only field but are never re-sent.
49
+ - When nothing is pending, the prepare operation returns `status: "ready"`
50
+ with a human-readable summary — the consumer's user-facing confirmation.
51
+ - The confirm operation is the only mutation and takes exactly
52
+ `{attempt_token}`. It re-validates everything live before executing and
53
+ returns `needs_input` (fresh token) instead of proceeding when upstream
54
+ drift invalidates a stored decision — never substitute a different option
55
+ for what the user picked.
56
+ - Expired or foreign tokens fail factually (nothing happened; start a new
57
+ attempt with the scalar fields) — no answer salvage from a dead token.
58
+ - **The provider must enforce consumption itself.** `ctx.choice` server
59
+ storage keeps tokens parseable until TTL — `parse` does not invalidate
60
+ them, so a confirm handler that only parses can be replayed into a second
61
+ booking or payment. After a successful execution, record the result under
62
+ the token's digest in `ctx.state` and make replays idempotent: a repeated
63
+ confirm returns the original created payload without touching upstream,
64
+ and later prepare rounds on the consumed token fail factually with the
65
+ existing reference. Record the result only after upstream success, so an
66
+ interrupted confirm stays retryable.
67
+
68
+ The invariant behind all of it: complex flow state is the system's job, not
69
+ the model's. The model carries exactly one opaque key between calls.
70
+ Reference implementations: `providers/catchtable` `reserve`/`reserve-confirm`
71
+ (including the consume-on-success guard) and `providers/modu-parking`
72
+ payment state tokens in the platform monorepo.
73
+
21
74
  ### Description template
22
75
 
23
76
  Every operation `description` MUST be at least 150 characters and follow this structure:
@@ -256,6 +309,59 @@ External contributors are expected to submit standalone Provider source plus:
256
309
  Maintainers own monorepo import under `providers/<id>/`, registry generation,
257
310
  deployment projection checks, and release workflows.
258
311
 
312
+ ### Declared secrets are SDK-enforced
313
+
314
+ Environment/secret presence validation is single-sourced in the SDK. Declare
315
+ every env secret the provider needs in `defineProvider`:
316
+
317
+ ```ts
318
+ secrets: [
319
+ {
320
+ name: "APIFUSE__PROVIDER__MY_PROVIDER__API_KEY",
321
+ required: true,
322
+ description: "Upstream API key from the vendor portal",
323
+ },
324
+ ],
325
+ ```
326
+
327
+ The runtime validates every `required: true` declaration before any operation
328
+ handler or auth-flow handler (except `abort`) runs. When a required secret is
329
+ unset or whitespace-only, the invocation fails with the canonical structured
330
+ error — code `MISSING_SECRET`, HTTP 400, `details.category:
331
+ "credential_unavailable"`, `retryable: false`, and a `fix` naming every missing
332
+ secret — across `/v1/{operation}`, self-test probes, `apifuse perf`, and
333
+ `apifuse record`. The server also emits a `provider_secrets_missing` warn log
334
+ at boot so unprovisioned deployments are visible immediately without crashing
335
+ the pod.
336
+
337
+ Provider-local presence re-validation is **deprecated**: do not write
338
+ `requireServiceKey`/`requireApiKey`-style guards that re-check `ctx.env.get()`
339
+ and throw a hand-rolled `CONFIGURATION_ERROR`/`MISSING_SECRET`. Those guards
340
+ are dead weight (the SDK gate runs first) and historically diverged into
341
+ inconsistent error shapes. The `sdk-owned-secret-presence` submit-check rule
342
+ flags them at warn level; acknowledge a deliberate exception with
343
+ `// @apifuse-allow sdk-owned-secret-presence: <reason>`.
344
+
345
+ ```ts
346
+ // Before (deprecated): provider-local double validation
347
+ function requireServiceKey(ctx: ProviderContext): string {
348
+ const value = ctx.env.get(SERVICE_KEY_ENV);
349
+ if (!value?.trim()) {
350
+ throw new ProviderError(`Missing required provider secret: ${SERVICE_KEY_ENV}`, {
351
+ code: "CONFIGURATION_ERROR",
352
+ });
353
+ }
354
+ return value;
355
+ }
356
+
357
+ // After: declare { name: SERVICE_KEY_ENV, required: true } and read directly.
358
+ const serviceKey = ctx.env.get(SERVICE_KEY_ENV);
359
+ ```
360
+
361
+ Note the asymmetry: the gate treats whitespace-only values as missing, but
362
+ `ctx.env.get()` still returns the raw value to handlers — trim at the point of
363
+ use if the upstream is whitespace-sensitive.
364
+
259
365
  ### Public local debugging checklist
260
366
 
261
367
  - Operation smoke requests use the provider server envelope:
@@ -264,6 +370,140 @@ deployment projection checks, and release workflows.
264
370
  - Credential-backed smoke requests pass local-only credential material in
265
371
  `connection.secrets`. Keep real values in shell env or `.env`, never in source
266
372
  or fixtures.
373
+ - Hand-written auth flows should use `ctx.auth` helpers and return exactly one
374
+ terminal/next turn from each handler: `ctx.auth.nextForm(...)` or
375
+ `ctx.auth.nextPoll(...)` to ask Gateway for the next user/system step,
376
+ `ctx.auth.complete(...)` to finish with `data.credential`, or
377
+ `ctx.auth.abort(...)` to stop safely. Keep abort `data` and `actionHint`
378
+ JSON-safe and secret-free; never include raw cookies, credentials, headers,
379
+ HTML, or upstream `Error` objects.
380
+
381
+ ```ts
382
+ export default defineProvider({
383
+ id: "example-provider",
384
+ version: "1.0.0",
385
+ runtime: "standard",
386
+ auth: {
387
+ mode: "credentials",
388
+ flow: {
389
+ async start(ctx) {
390
+ return ctx.auth.nextForm({
391
+ fields: {
392
+ email: { type: "email", labelKey: "auth.email.label" },
393
+ password: { type: "password", labelKey: "auth.password.label" },
394
+ },
395
+ hintKey: "auth.signIn",
396
+ });
397
+ },
398
+ async continue(ctx, input) {
399
+ const result = await loginWithSubmittedFields(ctx, input);
400
+ if (result.blocked) {
401
+ return ctx.auth.abort({
402
+ code: "account_action_required",
403
+ retry: "after_user_action",
404
+ actionHint: { kind: "open_provider_app" },
405
+ message: "Approve the login in the provider app.",
406
+ });
407
+ }
408
+ return ctx.auth.complete({
409
+ credential: { cookie: result.cookie },
410
+ metadata: { accountId: result.accountId },
411
+ });
412
+ },
413
+ },
414
+ },
415
+ credential: { keys: ["cookie"] },
416
+ // ...metadata and operations
417
+ });
418
+ ```
419
+
420
+ - Credentials auth providers should use `defineCredentialsAuth()` instead of
421
+ hand-writing `auth.flow.start/continue`. The helper exposes one happy path:
422
+ declare form `fields`, declare `credentialKeys`, and put upstream login/session
423
+ creation in `login(ctx, input)`. It returns both `auth` and `credential` for
424
+ `defineProvider()` and builds the complete turn as `data.credential`, which is
425
+ the only value Gateway persists onto the connection.
426
+
427
+ ```ts
428
+ import { defineCredentialsAuth, defineProvider } from "@apifuse/provider-sdk";
429
+
430
+ const credentialsAuth = defineCredentialsAuth({
431
+ fields: {
432
+ email: { type: "email", labelKey: "auth.email.label" },
433
+ password: { type: "password", labelKey: "auth.password.label" },
434
+ },
435
+ credentialKeys: ["cookie"] as const,
436
+ storesReusableSecret: true,
437
+ justification: "Session cookie is required for authenticated operations.",
438
+ async login(ctx, input) {
439
+ const cookie = await loginAndBuildSessionCookie(ctx, input);
440
+ return { credential: { cookie } };
441
+ },
442
+ });
443
+
444
+ export default defineProvider({
445
+ id: "example-provider",
446
+ version: "1.0.0",
447
+ runtime: "standard",
448
+ auth: credentialsAuth.auth,
449
+ credential: credentialsAuth.credential,
450
+ context: credentialsAuth.context,
451
+ // ...metadata and operations
452
+ });
453
+ ```
454
+
455
+ For OTP, MFA, CAPTCHA handoff, or user-approved login, return a challenge from
456
+ `login()` instead of hand-writing `contextPatch`, `poll`, and final credential
457
+ turns. SDK stores the pending challenge in auth-flow context, returns the next
458
+ form/pending turn, and still persists only the final `data.credential`.
459
+
460
+ ```ts
461
+ import {
462
+ credentialsAuthChallenge,
463
+ defineCredentialsAuth,
464
+ } from "@apifuse/provider-sdk";
465
+
466
+ const credentialsAuth = defineCredentialsAuth({
467
+ fields: {
468
+ email: { type: "email" },
469
+ password: { type: "password" },
470
+ },
471
+ credentialKeys: ["cookie"] as const,
472
+ async login(ctx, input) {
473
+ const result = await passwordLogin(ctx, input);
474
+ if (result.otpRequired) {
475
+ return credentialsAuthChallenge("otp", {
476
+ state: { transactionId: result.transactionId },
477
+ hintKey: "auth.otp.prompt",
478
+ });
479
+ }
480
+ if (result.manualApprovalRequired) {
481
+ return credentialsAuthChallenge("manualApproval", {
482
+ state: { transactionId: result.transactionId },
483
+ hintKey: "auth.manualApproval.openApp",
484
+ timing: { suggestedPollIntervalMs: 3000, maxWaitMs: 120000 },
485
+ });
486
+ }
487
+ return { credential: { cookie: result.cookie } };
488
+ },
489
+ challenges: {
490
+ otp: {
491
+ fields: { otp: { type: "otp", labelKey: "auth.otp.label" } },
492
+ async verify(ctx, input, state) {
493
+ const result = await verifyOtp(ctx, state.transactionId, input.otp);
494
+ return { credential: { cookie: result.cookie } };
495
+ },
496
+ },
497
+ manualApproval: {
498
+ async poll(ctx, state) {
499
+ const result = await checkApproval(ctx, state.transactionId);
500
+ if (!result.approved) return null;
501
+ return { credential: { cookie: result.cookie } };
502
+ },
503
+ },
504
+ },
505
+ });
506
+ ```
267
507
  - Auth-flow debugging starts with `/auth/start`, continues with
268
508
  `/auth/continue`, and carries returned `contextPatch` values into the next
269
509
  request's `context`.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,98 @@
1
1
  # @apifuse/provider-sdk Changelog
2
2
 
3
+ ## 2.2.0-beta.10
4
+
5
+ - Release candidate for main commit c41bd919739e0293ae8fa4d72a8a32f034cef4b8.
6
+
7
+ ## 2.2.0-beta.9
8
+
9
+ - Release candidate for main commit 5c78c8b (bundles #67 nodemaven required-secret + #68 transport vendor-advance).
10
+
11
+ ## 2.2.0-beta.8
12
+
13
+ - Release candidate for main commit 9e8a3f028ee78b9cab29d4aa3f5494ac9cffa65f.
14
+
15
+ ## 2.2.0-beta.7
16
+
17
+ - Release candidate for main commit 2ce4ea4bd36ce333eba8b3b474bf6e82b5e9216c.
18
+
19
+ ## 2.2.0-beta.6
20
+
21
+ - Release candidate for main commit 17f4e41d44efe7c148ef875b950be4f2c7df1294.
22
+
23
+ ## 2.2.0-beta.5
24
+
25
+ - Release candidate for main commit 82fa14e99a9af7edd44e3196aa3f4e87b4699edf.
26
+
27
+ ## 2.2.0-beta.4
28
+
29
+ - Release candidate for main commit 73f2c6ec429c2fbce8ac458a67111e4844b99178.
30
+
31
+ ## 2.2.0-beta.3
32
+
33
+ - Release candidate for main commit 74e8e18b502dd9b02dbf0d3e702f917570312fc0.
34
+
35
+ ## 2.2.0-beta.2
36
+
37
+ - Release candidate for main commit ceefad020a1038eade542fd3b128667b39625f6f.
38
+
39
+ ## 2.2.0-beta.1
40
+
41
+ - Release candidate for main commit 5056b8c89fe0fa8f10bafcd30f83bcc421d4b5c5.
42
+
43
+ ## 2.1.0-beta.22
44
+
45
+ - Release candidate for main commit af84b1e91c408b69773468fdaef80a01a36707cf.
46
+
47
+ ## 2.1.0-beta.21
48
+
49
+ - Release candidate for main commit 8124b3eb150cc7a73a86a95266b3747763a494ae.
50
+
51
+ ## 2.1.0-beta.20
52
+
53
+ - Release candidate for main commit 2a07cc5aef0d517c3b01d20445105f1669446bd3.
54
+
55
+ ## 2.1.0-beta.19
56
+
57
+ - Release candidate for main commit 71d76385f722b1202880f26b14ea3916a07852eb.
58
+
59
+ ## 2.1.0-beta.18
60
+
61
+ - Release candidate for main commit 46f93ecf8c2c8aab1afd0f23f5f62a87cbb45f6d.
62
+
63
+ ## 2.1.0-beta.17
64
+
65
+ - Release candidate for main commit c4466ffc8a30cb99740b50687a62b92cec9ba10e.
66
+
67
+ ## 2.1.0-beta.16
68
+
69
+ - Release candidate for main commit c0d8a1c4be519e1c1abc59e0304efc64f371634d.
70
+
71
+ ## Unreleased
72
+
73
+ - Add `arrayBuffer()` and `bytes()` to `HttpResponse` so `ctx.http` consumers can read binary-safe upstream bodies; internal response handling is now byte-first.
74
+ - Preserve identity-only operation `connectionId` values in `ProviderContext` without requiring credential material.
75
+
76
+ ## 2.1.0-beta.15
77
+
78
+ - Release candidate for main commit 4f51232d87828082f117dcc9f0c257a46f37c040.
79
+
80
+ ## 2.1.0-beta.14
81
+
82
+ - Release candidate for main commit 6eb132be6abf34ad9a70bfe28c6d26b36348ff4a.
83
+
84
+ ## 2.1.0-beta.13
85
+
86
+ - Release candidate for main commit d7b12716f54781df3e40206144c167844a485f8f.
87
+
88
+ ## 2.1.0-beta.12
89
+
90
+ - Release candidate for main commit 2bc1061c6a68facaa2efde08bee31bf5cd96945e.
91
+
92
+ ## 2.1.0-beta.11
93
+
94
+ - Release candidate for main commit b98ddc5024698f8c79e05e3295f7c8c8e8fe5a8a.
95
+
3
96
  ## 2.1.0-beta.9
4
97
 
5
98
  - Preserve raw stealth response bytes through the public SDK response wrapper.
package/README.md CHANGED
@@ -56,7 +56,7 @@ Removed legacy runtime paths are not supported:
56
56
  cd my-provider
57
57
  bun run check
58
58
  bun run test
59
- bun run submit-check
59
+ bun run submit-check -- --smoke
60
60
  bun run dev
61
61
  ```
62
62
 
@@ -119,17 +119,33 @@ the bad request path; provider/runtime failures include `code`, `message`, and
119
119
  local-only values through `connection.secrets`. Read them in handlers with
120
120
  `ctx.credential.get("key")` or `ctx.credential.getAccessToken()`.
121
121
  - **Provider env secrets**: declare `secrets[]`, set values in your shell or
122
- `.env`, and read only those names through `ctx.env.get("NAME")`.
122
+ `.env`, and read only those names through `ctx.env.get("NAME")`. The SDK
123
+ enforces presence of `required: true` declarations before handlers and auth
124
+ flows run, failing the invocation with a structured `MISSING_SECRET` error
125
+ (HTTP 400, category `credential_unavailable`) — do not re-check presence in
126
+ handlers.
127
+ - **Credentials auth flows**: prefer `defineCredentialsAuth()` over hand-written
128
+ `auth.flow`. Declare the form fields and credential keys once, then put the
129
+ upstream login/session creation in `login(ctx, input)`. Return
130
+ `credentialsAuthChallenge("otp" | "manualApproval" | ...)` for MFA, CAPTCHA
131
+ handoff, or user-approved login branches. The helper returns
132
+ `{ auth, credential, context }` for `defineProvider()` and always completes
133
+ with `data.credential`, which is the value Gateway persists onto the
134
+ connection.
123
135
  - **Auth flows**: call `/auth/start`, then `/auth/continue` with the same
124
136
  `flowId`; preserve any returned `contextPatch` in the next local request's
125
137
  `context` object.
126
138
  - **Stealth-sensitive providers**: use `ctx.http` for normal JSON/REST calls and
127
- `ctx.stealth.fetch()` when you need browser-like session or cookie control. `ctx.stealth.fetch()` uses the impit-backed browser stealth transport and accepts request
128
- controls for `params`, `proxy`, `timeout`, `profile`, `throwOnHttpError`, and
129
- `stealth.insecureSkipVerify`. Select an SDK stealth `profile` such as
130
- `chrome-146`; do not tune JA3, HTTP/2 SETTINGS, or pseudo-header order in
131
- provider code. Chrome/Firefox-style profiles are supported; use `ctx.browser`
132
- when Safari-specific behavior is required.
139
+ `ctx.stealth.fetch()` when you need browser-like session or cookie control.
140
+ `ctx.stealth.fetch()` uses the impit-backed browser stealth transport and
141
+ accepts request controls for `params`, `proxy`, `timeout`, `profile`,
142
+ `redirect`, `throwOnHttpError`, and `stealth.insecureSkipVerify`. For login
143
+ flows that must inspect intermediate `Location`/`Set-Cookie` headers, create
144
+ a session with `ctx.stealth.createSession()` and use `session.redirects.run()`;
145
+ inspect accumulated cookies through `session.cookies`. Select an SDK stealth
146
+ `profile` such as `chrome-146`; do not tune JA3, HTTP/2 SETTINGS, or
147
+ pseudo-header order in provider code. Chrome/Firefox-style profiles are
148
+ supported; use `ctx.browser` when Safari-specific behavior is required.
133
149
  - **Browser providers**: for TypeScript Providers use `runtime: "browser"` plus
134
150
  `browser.engine: "playwright-stealth"`; `nodriver` is a Python-runtime path.
135
151
  Install local browser assets with `bunx playwright install chromium` when
@@ -233,10 +249,10 @@ be replaced before recording fixtures.
233
249
  Standalone providers include a pre-submission script:
234
250
 
235
251
  ```bash
236
- bun run submit-check
252
+ bun run submit-check -- --smoke
237
253
  ```
238
254
 
239
- This runs the public review-readiness evaluator and writes `submission-report.md`. The report contains provider metadata, a 100-point readiness score, hard blockers, warnings, checklist evidence, and remediation. Blockers override the score; fix them before posting bounty evidence. The command is offline-safe by default and does not execute arbitrary upstream calls. Add local smoke notes to your assigned workspace PR after testing `/health` and `POST /v1/{operation}`. See [`SUBMISSION.md`](./SUBMISSION.md) for the full public-only bounty submission checklist shipped in the npm package.
255
+ This runs the public review-readiness evaluator and writes `submission-report.md`. The report contains provider metadata, a 100-point readiness score, hard blockers, warnings, checklist evidence, measured smoke details, and remediation. Blockers override the score; fix them before posting bounty evidence. `--smoke` boots the local dev server, checks `/health`, and POSTs every operation fixture. Set `APIFUSE__PROVIDER__*` env vars when live upstream credentials are available; without them, structured provider errors can still earn runtime-path smoke credit. See [`SUBMISSION.md`](./SUBMISSION.md) for the full public-only bounty submission checklist shipped in the npm package.
240
256
 
241
257
  ## Scope boundary
242
258
 
package/SUBMISSION.md CHANGED
@@ -9,11 +9,11 @@ bunx @apifuse/provider-sdk@beta create my-provider --yes
9
9
  cd my-provider
10
10
  bun run check
11
11
  bun run test
12
- bun run submit-check
12
+ bun run submit-check -- --smoke
13
13
  bun run dev
14
14
  ```
15
15
 
16
- `bun run submit-check` runs `apifuse submit-check . --markdown submission-report.md` in generated Providers.
16
+ `bun run submit-check -- --smoke` runs the generated `apifuse submit-check . --markdown submission-report.md` script with measured smoke enabled.
17
17
 
18
18
  ## What submit-check scores
19
19
 
@@ -50,22 +50,21 @@ Fix all blockers before submitting:
50
50
  - High-confidence secret or token material in source, README, package metadata, or fixtures.
51
51
  - SDK-native source blockers: prefixed Provider ids, `vendor/` SDK shims or imports, raw `.describe()` prose instead of `describeKey`, raw global `fetch()` calls, and excessive `as Type` assertions.
52
52
 
53
- Warnings do not fail the command, but they should be addressed when practical. For example, the generated starter `ping` operation warns because it is not a real upstream-backed bounty Operation. SDK-native warnings also flag moderate `as Type` assertion counts and credentialed Providers that never reference `ctx.credential`.
53
+ Warnings do not fail the command, but they should be addressed when practical. For example, the generated starter `ping` operation warns because it is not a real upstream-backed bounty Operation. SDK-native warnings also flag moderate `as Type` assertion counts, credentialed Providers that never reference `ctx.credential`, and provider-local re-validation of declared env secrets (`sdk-owned-secret-presence`, 0 points): the SDK already rejects invocations with a structured `MISSING_SECRET` error when a declared `required: true` secret is unset, so `requireServiceKey`-style presence guards are deprecated dead weight — delete the guard and read the value directly with `ctx.env.get()`, or acknowledge a deliberate exception with `// @apifuse-allow sdk-owned-secret-presence: <reason>`.
54
54
 
55
- ## Safe local smoke evidence
55
+ ## Measured local smoke
56
56
 
57
- `submit-check` does not call arbitrary live upstream APIs by default. After replacing starter logic, run the Provider locally and record a short evidence note:
57
+ `submit-check` does not call live upstream APIs by default. For smoke scoring,
58
+ run measured smoke so the CLI boots the Provider, checks `/health`, and POSTs
59
+ each operation fixture:
58
60
 
59
61
  ```bash
60
- bun run dev
61
- curl -s http://localhost:3900/health
62
- curl -s -X POST http://localhost:3900/v1/<operation> \
63
- -H 'Content-Type: application/json' \
64
- -d '{"requestId":"req_local_smoke","input":{...},"headers":{}}'
65
-
66
- bun run submit-check -- --smoke-note "GET /health and POST /v1/<operation> passed locally with redacted input."
62
+ bun run submit-check -- --smoke
67
63
  ```
68
64
 
65
+ `APIFUSE__PROVIDER__*` env vars enable live upstream calls. Their absence can
66
+ still produce 7/10 smoke credit when every operation routes and returns a
67
+ structured provider error envelope, but zero schema-valid successes are observed.
69
68
  Never paste real credentials, personal data, account numbers, access tokens, cookies, or unredacted upstream responses into workspace PR comments, chat, or reports.
70
69
 
71
70
  ## Submission evidence checklist
@@ -6,9 +6,14 @@ 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 {
10
+ formatPromptAssetIssues,
11
+ PROMPT_ASSET_SYNC_REMEDIATION,
12
+ verifyPromptAssets,
13
+ } from "../src/cli/prompt-assets.js";
14
+ import type { ProviderDefinition } from "../src/index.js";
15
+ import { lintProvider, type ProviderLintMode } from "../src/lint.js";
16
+ import { safeParseSchemaSync } from "../src/schema.js";
12
17
 
13
18
  const HELP_TEXT = `Usage: apifuse check [path]
14
19
  Example: apifuse check providers/korea-air-quality
@@ -24,9 +29,7 @@ export type RunChecksOptions = {
24
29
  lintMode?: ProviderLintMode;
25
30
  };
26
31
 
27
- type SafeParseResult =
28
- | { success: true; data: unknown }
29
- | { success: false; error: unknown };
32
+ type SafeParseResult = { success: true; data: unknown } | { success: false; error: unknown };
30
33
 
31
34
  export async function main() {
32
35
  const args = normalizeArgs(process.argv.slice(2));
@@ -63,7 +66,7 @@ function normalizeArgs(argv: string[]): string[] {
63
66
  return argv[0] === "check" ? argv.slice(1) : argv;
64
67
  }
65
68
 
66
- function resolveProviderRoot(inputPath: string): string {
69
+ export function resolveProviderRoot(inputPath: string): string {
67
70
  const resolvedInput = resolveFromParents(inputPath);
68
71
 
69
72
  if (!existsSync(resolvedInput)) {
@@ -131,9 +134,26 @@ export async function runChecks(
131
134
  checkProviderMetadata(provider),
132
135
  checkDockerfile(dockerfilePath),
133
136
  checkPackageJson(packageJsonPath),
137
+ checkPromptAssets(providerRoot),
134
138
  ];
135
139
  }
136
140
 
141
+ export const PROMPT_ASSETS_CHECK_MESSAGE =
142
+ "Agent prompt assets match the installed SDK version";
143
+
144
+ function checkPromptAssets(providerRoot: string): CheckResult {
145
+ const verification = verifyPromptAssets(providerRoot);
146
+ if (verification.ok) {
147
+ return { message: PROMPT_ASSETS_CHECK_MESSAGE, passed: true };
148
+ }
149
+
150
+ return {
151
+ message: PROMPT_ASSETS_CHECK_MESSAGE,
152
+ passed: false,
153
+ details: [...formatPromptAssetIssues(verification), PROMPT_ASSET_SYNC_REMEDIATION],
154
+ };
155
+ }
156
+
137
157
  function isScannableProviderSourceFile(relativePath: string): boolean {
138
158
  return (
139
159
  /\.(?:ts|tsx|js|jsx|mjs|cjs|sh|bash)$/.test(relativePath) ||
@@ -142,17 +162,12 @@ function isScannableProviderSourceFile(relativePath: string): boolean {
142
162
  );
143
163
  }
144
164
 
145
- function collectProviderSourceFiles(
146
- providerRoot: string,
147
- ): Record<string, string> {
165
+ function collectProviderSourceFiles(providerRoot: string): Record<string, string> {
148
166
  const sources: Record<string, string> = {};
149
- const skipDirectories = new Set([
150
- ".git",
151
- "node_modules",
152
- "dist",
153
- "build",
154
- ".next",
155
- ]);
167
+ // `.agents`/`.apifuse` are deliberately not skipped: managed content there
168
+ // is markdown/JSON (never matched by isScannableProviderSourceFile), and a
169
+ // planted `.ts`/`.sh` under those directories must stay in scanner scope.
170
+ const skipDirectories = new Set([".git", "node_modules", "dist", "build", ".next"]);
156
171
  const visit = (directory: string) => {
157
172
  for (const entry of readdirSync(directory, { withFileTypes: true })) {
158
173
  const path = resolve(directory, entry.name);
@@ -162,10 +177,7 @@ function collectProviderSourceFiles(
162
177
  }
163
178
  continue;
164
179
  }
165
- if (
166
- !entry.isFile() ||
167
- !isScannableProviderSourceFile(path.slice(providerRoot.length + 1))
168
- ) {
180
+ if (!entry.isFile() || !isScannableProviderSourceFile(path.slice(providerRoot.length + 1))) {
169
181
  continue;
170
182
  }
171
183
  sources[path.slice(providerRoot.length + 1)] = readFileSync(path, "utf8");
@@ -175,10 +187,7 @@ function collectProviderSourceFiles(
175
187
  return sources;
176
188
  }
177
189
 
178
- function checkIndex(
179
- indexPath: string,
180
- provider: ProviderDefinition | undefined,
181
- ): CheckResult {
190
+ function checkIndex(indexPath: string, provider: ProviderDefinition | undefined): CheckResult {
182
191
  if (!existsSync(indexPath)) {
183
192
  return {
184
193
  message: "index.ts exists and exports default defineProvider",
@@ -200,9 +209,7 @@ function checkIndex(
200
209
  };
201
210
  }
202
211
 
203
- function checkOperations(
204
- provider: ProviderDefinition | undefined,
205
- ): CheckResult {
212
+ function checkOperations(provider: ProviderDefinition | undefined): CheckResult {
206
213
  if (!provider) {
207
214
  return {
208
215
  message: "All operations have handler, input, output",
@@ -277,20 +284,14 @@ function checkSchemas(provider: ProviderDefinition | undefined): CheckResult {
277
284
  continue;
278
285
  }
279
286
 
280
- const requestResult = parseFixture(
281
- operation.input,
282
- operation.fixtures.request,
283
- );
287
+ const requestResult = parseFixture(operation.input, operation.fixtures.request);
284
288
  if (!requestResult.success) {
285
289
  failures.push(
286
290
  `${operationId}: request fixture invalid (${formatSchemaError(requestResult.error)})`,
287
291
  );
288
292
  }
289
293
 
290
- const responseResult = parseFixture(
291
- operation.output,
292
- operation.fixtures.response,
293
- );
294
+ const responseResult = parseFixture(operation.output, operation.fixtures.response);
294
295
  if (!responseResult.success) {
295
296
  failures.push(
296
297
  `${operationId}: response fixture invalid (${formatSchemaError(responseResult.error)})`,
@@ -317,13 +318,8 @@ function checkAuthoringLint(
317
318
  };
318
319
  }
319
320
 
320
- const diagnostics = lintProvider(
321
- { ...provider, providerSourceFiles },
322
- { mode: lintMode },
323
- );
324
- const errors = diagnostics.filter(
325
- (diagnostic) => diagnostic.level === "error",
326
- );
321
+ const diagnostics = lintProvider({ ...provider, providerSourceFiles }, { mode: lintMode });
322
+ const errors = diagnostics.filter((diagnostic) => diagnostic.level === "error");
327
323
  const details = diagnostics.map((diagnostic) => {
328
324
  const field = diagnostic.field ? `${diagnostic.field}: ` : "";
329
325
  return `${diagnostic.level.toUpperCase()} ${diagnostic.rule} ${field}${diagnostic.message}`;
@@ -336,9 +332,7 @@ function checkAuthoringLint(
336
332
  };
337
333
  }
338
334
 
339
- function checkProviderMetadata(
340
- provider: ProviderDefinition | undefined,
341
- ): CheckResult {
335
+ function checkProviderMetadata(provider: ProviderDefinition | undefined): CheckResult {
342
336
  if (!provider) {
343
337
  return {
344
338
  message: "Provider metadata is declared in defineProvider",
@@ -425,18 +419,12 @@ function checkPackageJson(packageJsonPath: string): CheckResult {
425
419
  }
426
420
  }
427
421
 
428
- function assertProviderDefinition(
429
- value: unknown,
430
- ): ProviderDefinition | undefined {
422
+ function assertProviderDefinition(value: unknown): ProviderDefinition | undefined {
431
423
  return isProviderDefinition(value) ? value : undefined;
432
424
  }
433
425
 
434
426
  function isProviderDefinition(value: unknown): value is ProviderDefinition {
435
- if (
436
- !isRecord(value) ||
437
- !isRecord(value.meta) ||
438
- !isRecord(value.operations)
439
- ) {
427
+ if (!isRecord(value) || !isRecord(value.meta) || !isRecord(value.operations)) {
440
428
  return false;
441
429
  }
442
430
 
@@ -457,8 +445,7 @@ function hasSchemaParser(value: unknown): boolean {
457
445
  return (
458
446
  isRecord(value) &&
459
447
  (typeof value.safeParse === "function" ||
460
- (isRecord(value["~standard"]) &&
461
- typeof value["~standard"].validate === "function"))
448
+ (isRecord(value["~standard"]) && typeof value["~standard"].validate === "function"))
462
449
  );
463
450
  }
464
451
 
@@ -470,9 +457,7 @@ function formatSchemaError(error: unknown): string {
470
457
  if (Array.isArray(error)) {
471
458
  return error
472
459
  .map((issue) =>
473
- isRecord(issue) && typeof issue.message === "string"
474
- ? issue.message
475
- : String(issue),
460
+ isRecord(issue) && typeof issue.message === "string" ? issue.message : String(issue),
476
461
  )
477
462
  .join(", ");
478
463
  }