@apifuse/provider-sdk 2.2.0-beta.7 → 2.2.0-beta.70

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 (478) hide show
  1. package/AUTHORING.md +943 -51
  2. package/CHANGELOG.md +273 -1
  3. package/LICENSE +21 -0
  4. package/README.md +117 -29
  5. package/SUBMISSION.md +3 -2
  6. package/bin/apifuse-check.ts +293 -13
  7. package/bin/apifuse-dev.ts +42 -19
  8. package/bin/apifuse-migrate-deployment.ts +200 -0
  9. package/bin/apifuse-migrate-operation-declaration.ts +55 -0
  10. package/bin/apifuse-migrate-shape.ts +202 -0
  11. package/bin/apifuse-pack-check.ts +19 -6
  12. package/bin/apifuse-pack-smoke.ts +57 -2
  13. package/bin/apifuse-pack-types.ts +736 -38
  14. package/bin/apifuse-perf.ts +59 -23
  15. package/bin/apifuse-record.ts +923 -94
  16. package/bin/apifuse-submit-check.ts +2313 -281
  17. package/bin/apifuse-test.ts +3 -1
  18. package/bin/submit-check-delimited-text.ts +50 -0
  19. package/dist/auth-turn/index.d.ts +3 -3
  20. package/dist/auth-turn/index.js +1 -1
  21. package/dist/auth.d.ts +14 -0
  22. package/dist/auth.js +67 -0
  23. package/dist/ceremonies/index.d.ts +16 -0
  24. package/dist/ceremonies/index.js +141 -36
  25. package/dist/cli/commands.d.ts +1 -1
  26. package/dist/cli/commands.js +30 -0
  27. package/dist/cli/create.d.ts +1 -1
  28. package/dist/cli/create.js +8 -2
  29. package/dist/cli/deployment-intent.d.ts +178 -0
  30. package/dist/cli/deployment-intent.js +277 -0
  31. package/dist/cli/local-connection.d.ts +111 -0
  32. package/dist/cli/local-connection.js +151 -0
  33. package/dist/cli/migrate-deployment-intent.d.ts +132 -0
  34. package/dist/cli/migrate-deployment-intent.js +1552 -0
  35. package/dist/cli/migrate-operation-declaration.d.ts +70 -0
  36. package/dist/cli/migrate-operation-declaration.js +2206 -0
  37. package/dist/cli/migrate-operation-shape.d.ts +44 -0
  38. package/dist/cli/migrate-operation-shape.js +113 -0
  39. package/dist/cli/migrate-provider-shape.d.ts +56 -0
  40. package/dist/cli/migrate-provider-shape.js +580 -0
  41. package/dist/cli/resolver-runtime.d.ts +21 -0
  42. package/dist/cli/resolver-runtime.js +31 -0
  43. package/dist/cli/templates/provider/.agents/skills/health-checks-and-fail-closed/SKILL.md.tpl +141 -23
  44. package/dist/cli/templates/provider/Dockerfile.tpl +1 -1
  45. package/dist/cli/templates/provider/README.md.tpl +41 -19
  46. package/dist/cli/templates/provider/index.ts.tpl +6 -3
  47. package/dist/cli/templates/provider/operations/ping.ts.tpl +4 -1
  48. package/dist/cli/templates/provider/provider.json.tpl +6 -0
  49. package/dist/config/loader.d.ts +202 -16
  50. package/dist/config/loader.js +491 -141
  51. package/dist/contract-serialization.js +4 -8
  52. package/dist/contract-types.d.ts +12 -5
  53. package/dist/contract.js +24 -10
  54. package/dist/declaration-validation.d.ts +34 -0
  55. package/dist/declaration-validation.js +305 -0
  56. package/dist/define.d.ts +71 -29
  57. package/dist/define.js +807 -69
  58. package/dist/dev.d.ts +5 -0
  59. package/dist/dev.js +8 -1
  60. package/dist/engine.d.ts +129 -0
  61. package/dist/engine.js +209 -0
  62. package/dist/error-observability.d.ts +7 -0
  63. package/dist/error-observability.js +61 -0
  64. package/dist/error-resolution.d.ts +18 -0
  65. package/dist/error-resolution.js +243 -0
  66. package/dist/errors.d.ts +74 -0
  67. package/dist/errors.js +99 -0
  68. package/dist/fixture-sanitization.d.ts +28 -0
  69. package/dist/fixture-sanitization.js +371 -0
  70. package/dist/handle-meta.d.ts +90 -0
  71. package/dist/handle-meta.js +73 -0
  72. package/dist/handle.d.ts +226 -0
  73. package/dist/handle.js +345 -0
  74. package/dist/health-scenario.d.ts +1901 -0
  75. package/dist/health-scenario.js +653 -0
  76. package/dist/i18n/catalog.js +21 -1
  77. package/dist/i18n/error-messages.d.ts +86 -0
  78. package/dist/i18n/error-messages.js +192 -0
  79. package/dist/i18n/index.d.ts +1 -0
  80. package/dist/i18n/index.js +1 -0
  81. package/dist/i18n/keys.d.ts +12 -0
  82. package/dist/i18n/keys.js +76 -5
  83. package/dist/i18n/operation-locale-namespace.d.ts +2 -0
  84. package/dist/i18n/operation-locale-namespace.js +7 -0
  85. package/dist/index.d.ts +40 -15
  86. package/dist/index.js +29 -11
  87. package/dist/lint.d.ts +43 -11
  88. package/dist/lint.js +2512 -177
  89. package/dist/native-address.d.ts +43 -0
  90. package/dist/native-address.js +281 -0
  91. package/dist/native-egress-policy.d.ts +31 -0
  92. package/dist/native-egress-policy.js +288 -0
  93. package/dist/observability.d.ts +5 -2
  94. package/dist/observability.js +48 -1
  95. package/dist/provider.d.ts +18 -8
  96. package/dist/provider.js +11 -6
  97. package/dist/runtime/auth-flow.d.ts +6 -1
  98. package/dist/runtime/auth-flow.js +8 -0
  99. package/dist/runtime/bounded-expiring-map.d.ts +21 -0
  100. package/dist/runtime/bounded-expiring-map.js +64 -0
  101. package/dist/runtime/browser-telemetry-binding.d.ts +5 -0
  102. package/dist/runtime/browser-telemetry-binding.js +83 -0
  103. package/dist/runtime/browser-telemetry.d.ts +76 -0
  104. package/dist/runtime/browser-telemetry.js +165 -0
  105. package/dist/runtime/browser.d.ts +4 -0
  106. package/dist/runtime/browser.js +688 -83
  107. package/dist/runtime/cache-telemetry.d.ts +71 -0
  108. package/dist/runtime/cache-telemetry.js +177 -0
  109. package/dist/runtime/cache.d.ts +3 -0
  110. package/dist/runtime/cache.js +245 -23
  111. package/dist/runtime/capability-response.d.ts +3 -0
  112. package/dist/runtime/capability-response.js +56 -0
  113. package/dist/runtime/capability-telemetry.d.ts +46 -0
  114. package/dist/runtime/capability-telemetry.js +68 -0
  115. package/dist/runtime/chrome149-header-order.d.ts +58 -0
  116. package/dist/runtime/chrome149-header-order.js +289 -0
  117. package/dist/runtime/diagnostic-env-values.d.ts +5 -0
  118. package/dist/runtime/diagnostic-env-values.js +57 -0
  119. package/dist/runtime/diagnostic-env.d.ts +14 -0
  120. package/dist/runtime/diagnostic-env.js +44 -0
  121. package/dist/runtime/diagnostic-redactor.d.ts +54 -0
  122. package/dist/runtime/diagnostic-redactor.js +571 -0
  123. package/dist/runtime/egress-lease.d.ts +65 -0
  124. package/dist/runtime/egress-lease.js +236 -0
  125. package/dist/runtime/engine-mode.d.ts +74 -0
  126. package/dist/runtime/engine-mode.js +152 -0
  127. package/dist/runtime/env.js +14 -1
  128. package/dist/runtime/executor.d.ts +3 -2
  129. package/dist/runtime/executor.js +23 -26
  130. package/dist/runtime/handle-wordlist.d.ts +43 -0
  131. package/dist/runtime/handle-wordlist.js +256 -0
  132. package/dist/runtime/handle.d.ts +71 -0
  133. package/dist/runtime/handle.js +908 -0
  134. package/dist/runtime/http-telemetry-binding.d.ts +10 -0
  135. package/dist/runtime/http-telemetry-binding.js +152 -0
  136. package/dist/runtime/http-telemetry-guard.d.ts +16 -0
  137. package/dist/runtime/http-telemetry-guard.js +202 -0
  138. package/dist/runtime/http-telemetry.d.ts +104 -0
  139. package/dist/runtime/http-telemetry.js +190 -0
  140. package/dist/runtime/http.d.ts +34 -0
  141. package/dist/runtime/http.js +680 -121
  142. package/dist/runtime/insights.js +2 -2
  143. package/dist/runtime/instrumentation.d.ts +2 -2
  144. package/dist/runtime/instrumentation.js +462 -19
  145. package/dist/runtime/native-network-errors.d.ts +33 -0
  146. package/dist/runtime/native-network-errors.js +69 -0
  147. package/dist/runtime/native-network.d.ts +105 -0
  148. package/dist/runtime/native-network.js +1433 -0
  149. package/dist/runtime/native-telemetry.d.ts +142 -0
  150. package/dist/runtime/native-telemetry.js +231 -0
  151. package/dist/runtime/ocr-telemetry.d.ts +82 -0
  152. package/dist/runtime/ocr-telemetry.js +192 -0
  153. package/dist/runtime/ocr.d.ts +29 -0
  154. package/dist/runtime/ocr.js +468 -0
  155. package/dist/runtime/otlp.d.ts +71 -2
  156. package/dist/runtime/otlp.js +398 -16
  157. package/dist/runtime/provider.d.ts +7 -0
  158. package/dist/runtime/provider.js +14 -0
  159. package/dist/runtime/proxy-errors.js +6 -2
  160. package/dist/runtime/proxy-nodemaven.d.ts +56 -0
  161. package/dist/runtime/proxy-nodemaven.js +147 -0
  162. package/dist/runtime/proxy-retry-policy.d.ts +4 -1
  163. package/dist/runtime/proxy-retry-policy.js +11 -2
  164. package/dist/runtime/proxy-telemetry.d.ts +133 -3
  165. package/dist/runtime/proxy-telemetry.js +218 -70
  166. package/dist/runtime/redirect-hop-headers.d.ts +19 -0
  167. package/dist/runtime/redirect-hop-headers.js +58 -0
  168. package/dist/runtime/redirects.d.ts +61 -0
  169. package/dist/runtime/redirects.js +149 -0
  170. package/dist/runtime/redis.d.ts +1 -1
  171. package/dist/runtime/redis.js +4 -2
  172. package/dist/runtime/request-options.d.ts +75 -1
  173. package/dist/runtime/request-options.js +588 -0
  174. package/dist/runtime/request-telemetry.d.ts +70 -0
  175. package/dist/runtime/request-telemetry.js +229 -0
  176. package/dist/runtime/resolver-config.d.ts +13 -0
  177. package/dist/runtime/resolver-config.js +14 -0
  178. package/dist/runtime/resolver-public.d.ts +3 -0
  179. package/dist/runtime/resolver-public.js +1 -0
  180. package/dist/runtime/resolver-runtime-options.d.ts +14 -0
  181. package/dist/runtime/resolver-runtime-options.js +33 -0
  182. package/dist/runtime/resolver-shared.d.ts +24 -0
  183. package/dist/runtime/resolver-shared.js +69 -0
  184. package/dist/runtime/resolver-span-attributes.d.ts +34 -0
  185. package/dist/runtime/resolver-span-attributes.js +93 -0
  186. package/dist/runtime/resolver-telemetry.d.ts +135 -0
  187. package/dist/runtime/resolver-telemetry.js +204 -0
  188. package/dist/runtime/resolver-usage.d.ts +32 -0
  189. package/dist/runtime/resolver-usage.js +54 -0
  190. package/dist/runtime/resolver-vendors/bindings.d.ts +53 -0
  191. package/dist/runtime/resolver-vendors/bindings.js +45 -0
  192. package/dist/runtime/resolver-vendors/browser.d.ts +24 -0
  193. package/dist/runtime/resolver-vendors/browser.js +423 -0
  194. package/dist/runtime/resolver-vendors/capsolver.d.ts +22 -0
  195. package/dist/runtime/resolver-vendors/capsolver.js +544 -0
  196. package/dist/runtime/resolver-vendors/hosts.d.ts +23 -0
  197. package/dist/runtime/resolver-vendors/hosts.js +68 -0
  198. package/dist/runtime/resolver-vendors/hypersolutions.d.ts +27 -0
  199. package/dist/runtime/resolver-vendors/hypersolutions.js +369 -0
  200. package/dist/runtime/resolver-vendors/twocaptcha.d.ts +24 -0
  201. package/dist/runtime/resolver-vendors/twocaptcha.js +425 -0
  202. package/dist/runtime/resolver-vendors/types.d.ts +125 -0
  203. package/dist/runtime/resolver-vendors/types.js +106 -0
  204. package/dist/runtime/resolver.d.ts +79 -0
  205. package/dist/runtime/resolver.js +1094 -0
  206. package/dist/runtime/state-telemetry.d.ts +79 -0
  207. package/dist/runtime/state-telemetry.js +194 -0
  208. package/dist/runtime/state.d.ts +10 -1
  209. package/dist/runtime/state.js +322 -82
  210. package/dist/runtime/stealth-akamai-sbsd.d.ts +54 -0
  211. package/dist/runtime/stealth-akamai-sbsd.js +142 -0
  212. package/dist/runtime/stealth-cookies.d.ts +24 -0
  213. package/dist/runtime/stealth-cookies.js +131 -0
  214. package/dist/runtime/stealth-owned-headers.d.ts +39 -0
  215. package/dist/runtime/stealth-owned-headers.js +57 -0
  216. package/dist/runtime/stealth-telemetry.d.ts +92 -0
  217. package/dist/runtime/stealth-telemetry.js +179 -0
  218. package/dist/runtime/stealth.d.ts +45 -6
  219. package/dist/runtime/stealth.js +1775 -320
  220. package/dist/runtime/stt-telemetry.d.ts +77 -0
  221. package/dist/runtime/stt-telemetry.js +176 -0
  222. package/dist/runtime/stt.js +18 -19
  223. package/dist/runtime/timeout.d.ts +5 -0
  224. package/dist/runtime/timeout.js +12 -0
  225. package/dist/runtime/trace-config.d.ts +12 -0
  226. package/dist/runtime/trace-config.js +62 -0
  227. package/dist/runtime/trace.d.ts +17 -0
  228. package/dist/runtime/trace.js +176 -25
  229. package/dist/runtime-boundary-lint.d.ts +137 -0
  230. package/dist/runtime-boundary-lint.js +590 -0
  231. package/dist/schema.d.ts +20 -0
  232. package/dist/schema.js +72 -2
  233. package/dist/serve.d.ts +1 -1
  234. package/dist/serve.js +1 -1
  235. package/dist/server/error-observability.d.ts +1 -0
  236. package/dist/server/error-observability.js +1 -0
  237. package/dist/server/error-stack-frames.d.ts +35 -0
  238. package/dist/server/error-stack-frames.js +219 -0
  239. package/dist/server/index.d.ts +21 -2
  240. package/dist/server/index.js +11 -2
  241. package/dist/server/runtime-diagnostics.d.ts +165 -0
  242. package/dist/server/runtime-diagnostics.js +360 -0
  243. package/dist/server/sdk-version.d.ts +3 -0
  244. package/dist/server/sdk-version.js +19 -0
  245. package/dist/server/self-test-input-tokens.d.ts +2 -1
  246. package/dist/server/self-test-input-tokens.js +18 -14
  247. package/dist/server/self-test-redaction.js +8 -33
  248. package/dist/server/self-test.d.ts +18 -3
  249. package/dist/server/self-test.js +139 -76
  250. package/dist/server/sensitive-values.d.ts +26 -0
  251. package/dist/server/sensitive-values.js +137 -0
  252. package/dist/server/serve-implementation.d.ts +285 -0
  253. package/dist/server/serve-implementation.js +3172 -0
  254. package/dist/server/serve.d.ts +1 -75
  255. package/dist/server/serve.js +1 -1170
  256. package/dist/server/state-backend-report.d.ts +70 -0
  257. package/dist/server/state-backend-report.js +113 -0
  258. package/dist/server/trace-output.d.ts +7 -0
  259. package/dist/server/trace-output.js +111 -0
  260. package/dist/server/types.d.ts +37 -5
  261. package/dist/server/types.js +34 -1
  262. package/dist/stateful/errors.d.ts +19 -0
  263. package/dist/stateful/errors.js +24 -0
  264. package/dist/stateful/http-provider-event-emitter.d.ts +40 -0
  265. package/dist/stateful/http-provider-event-emitter.js +237 -0
  266. package/dist/stateful/http-session-owner-registry.d.ts +44 -0
  267. package/dist/stateful/http-session-owner-registry.js +210 -0
  268. package/dist/stateful/index.d.ts +18 -0
  269. package/dist/stateful/index.js +18 -0
  270. package/dist/stateful/provider-event-delivery-failures.d.ts +32 -0
  271. package/dist/stateful/provider-event-delivery-failures.js +43 -0
  272. package/dist/stateful/provider-event-pipeline-metrics.d.ts +46 -0
  273. package/dist/stateful/provider-event-pipeline-metrics.js +48 -0
  274. package/dist/stateful/provider-event-pipeline.d.ts +50 -0
  275. package/dist/stateful/provider-event-pipeline.js +1 -0
  276. package/dist/stateful/provider-events.d.ts +101 -0
  277. package/dist/stateful/provider-events.js +289 -0
  278. package/dist/stateful/session-key.d.ts +15 -0
  279. package/dist/stateful/session-key.js +86 -0
  280. package/dist/stateful/stateful-provider-adapter-context.d.ts +5 -0
  281. package/dist/stateful/stateful-provider-adapter-context.js +42 -0
  282. package/dist/stateful/stateful-provider-adapter-metrics.d.ts +15 -0
  283. package/dist/stateful/stateful-provider-adapter-metrics.js +21 -0
  284. package/dist/stateful/stateful-provider-adapter.d.ts +98 -0
  285. package/dist/stateful/stateful-provider-adapter.js +287 -0
  286. package/dist/stateful/stateful-provider-observability.d.ts +62 -0
  287. package/dist/stateful/stateful-provider-observability.js +161 -0
  288. package/dist/stateful/stateful-provider-owner-forwarder.d.ts +41 -0
  289. package/dist/stateful/stateful-provider-owner-forwarder.js +237 -0
  290. package/dist/stateful/stateful-provider-runtime-context.d.ts +35 -0
  291. package/dist/stateful/stateful-provider-runtime-context.js +61 -0
  292. package/dist/stateful/stateful-provider-runtime-executor.d.ts +34 -0
  293. package/dist/stateful/stateful-provider-runtime-executor.js +52 -0
  294. package/dist/stateful/stateful-provider-session-routing.d.ts +67 -0
  295. package/dist/stateful/stateful-provider-session-routing.js +345 -0
  296. package/dist/stateful/stateful-provider-session-runtime.d.ts +98 -0
  297. package/dist/stateful/stateful-provider-session-runtime.js +245 -0
  298. package/dist/stateful-signing.d.ts +18 -0
  299. package/dist/stateful-signing.js +27 -0
  300. package/dist/stealth/profiles.d.ts +15 -4
  301. package/dist/stealth/profiles.js +169 -211
  302. package/dist/stream-evidence.d.ts +74 -0
  303. package/dist/stream-evidence.js +785 -0
  304. package/dist/stream.js +7 -1
  305. package/dist/testing/index.d.ts +2 -1
  306. package/dist/testing/index.js +2 -1
  307. package/dist/testing/run.d.ts +32 -2
  308. package/dist/testing/run.js +509 -25
  309. package/dist/trace-sanitization.d.ts +6 -0
  310. package/dist/trace-sanitization.js +68 -0
  311. package/dist/types.d.ts +899 -210
  312. package/dist/types.js +1 -0
  313. package/dist/typescript-module.d.ts +39 -0
  314. package/dist/typescript-module.js +72 -0
  315. package/package.json +52 -6
  316. package/src/auth-turn/index.ts +1 -1
  317. package/src/auth.ts +118 -0
  318. package/src/ceremonies/index.ts +189 -46
  319. package/src/cli/commands.ts +37 -0
  320. package/src/cli/create.ts +8 -2
  321. package/src/cli/deployment-intent.ts +364 -0
  322. package/src/cli/local-connection.ts +221 -0
  323. package/src/cli/migrate-deployment-intent.ts +1908 -0
  324. package/src/cli/migrate-operation-declaration.ts +3026 -0
  325. package/src/cli/migrate-operation-shape.ts +184 -0
  326. package/src/cli/migrate-provider-shape.ts +778 -0
  327. package/src/cli/resolver-runtime.ts +72 -0
  328. package/src/cli/templates/provider/.agents/skills/health-checks-and-fail-closed/SKILL.md.tpl +141 -23
  329. package/src/cli/templates/provider/Dockerfile.tpl +1 -1
  330. package/src/cli/templates/provider/README.md.tpl +41 -19
  331. package/src/cli/templates/provider/index.ts.tpl +6 -3
  332. package/src/cli/templates/provider/operations/ping.ts.tpl +4 -1
  333. package/src/cli/templates/provider/provider.json.tpl +6 -0
  334. package/src/config/loader.ts +774 -181
  335. package/src/contract-serialization.ts +5 -7
  336. package/src/contract-types.ts +12 -5
  337. package/src/contract.ts +24 -10
  338. package/src/declaration-validation.ts +376 -0
  339. package/src/define.ts +1105 -131
  340. package/src/dev.ts +13 -1
  341. package/src/engine.ts +390 -0
  342. package/src/error-observability.ts +64 -0
  343. package/src/error-resolution.ts +269 -0
  344. package/src/errors.ts +157 -0
  345. package/src/fixture-sanitization.ts +422 -0
  346. package/src/handle-meta.ts +143 -0
  347. package/src/handle.ts +694 -0
  348. package/src/health-scenario.ts +960 -0
  349. package/src/i18n/catalog.ts +23 -1
  350. package/src/i18n/error-messages.ts +255 -0
  351. package/src/i18n/index.ts +1 -0
  352. package/src/i18n/keys.ts +80 -6
  353. package/src/i18n/operation-locale-namespace.ts +10 -0
  354. package/src/index.ts +390 -23
  355. package/src/lint.ts +2975 -234
  356. package/src/native-address.ts +340 -0
  357. package/src/native-egress-policy.ts +358 -0
  358. package/src/observability.ts +51 -1
  359. package/src/provider.ts +207 -27
  360. package/src/runtime/auth-flow.ts +15 -0
  361. package/src/runtime/bounded-expiring-map.ts +67 -0
  362. package/src/runtime/browser-telemetry-binding.ts +92 -0
  363. package/src/runtime/browser-telemetry.ts +289 -0
  364. package/src/runtime/browser.ts +882 -99
  365. package/src/runtime/cache-telemetry.ts +247 -0
  366. package/src/runtime/cache.ts +258 -22
  367. package/src/runtime/capability-response.ts +53 -0
  368. package/src/runtime/capability-telemetry.ts +101 -0
  369. package/src/runtime/chrome149-header-order.ts +330 -0
  370. package/src/runtime/diagnostic-env-values.ts +56 -0
  371. package/src/runtime/diagnostic-env.ts +65 -0
  372. package/src/runtime/diagnostic-redactor.ts +668 -0
  373. package/src/runtime/egress-lease.ts +341 -0
  374. package/src/runtime/engine-mode.ts +233 -0
  375. package/src/runtime/env.ts +15 -1
  376. package/src/runtime/executor.ts +39 -32
  377. package/src/runtime/handle-wordlist.ts +266 -0
  378. package/src/runtime/handle.ts +1180 -0
  379. package/src/runtime/http-telemetry-binding.ts +162 -0
  380. package/src/runtime/http-telemetry-guard.ts +208 -0
  381. package/src/runtime/http-telemetry.ts +292 -0
  382. package/src/runtime/http.ts +896 -150
  383. package/src/runtime/insights.ts +2 -2
  384. package/src/runtime/instrumentation.ts +628 -28
  385. package/src/runtime/native-network-errors.ts +99 -0
  386. package/src/runtime/native-network.ts +1853 -0
  387. package/src/runtime/native-telemetry.ts +366 -0
  388. package/src/runtime/ocr-telemetry.ts +308 -0
  389. package/src/runtime/ocr.ts +564 -0
  390. package/src/runtime/otlp.ts +468 -21
  391. package/src/runtime/provider.ts +15 -0
  392. package/src/runtime/proxy-errors.ts +12 -4
  393. package/src/runtime/proxy-nodemaven.ts +222 -0
  394. package/src/runtime/proxy-retry-policy.ts +12 -1
  395. package/src/runtime/proxy-telemetry.ts +358 -103
  396. package/src/runtime/redirect-hop-headers.ts +71 -0
  397. package/src/runtime/redirects.ts +215 -0
  398. package/src/runtime/redis.ts +7 -2
  399. package/src/runtime/request-options.ts +728 -1
  400. package/src/runtime/request-telemetry.ts +375 -0
  401. package/src/runtime/resolver-config.ts +18 -0
  402. package/src/runtime/resolver-public.ts +44 -0
  403. package/src/runtime/resolver-runtime-options.ts +50 -0
  404. package/src/runtime/resolver-shared.ts +113 -0
  405. package/src/runtime/resolver-span-attributes.ts +139 -0
  406. package/src/runtime/resolver-telemetry.ts +362 -0
  407. package/src/runtime/resolver-usage.ts +99 -0
  408. package/src/runtime/resolver-vendors/bindings.ts +61 -0
  409. package/src/runtime/resolver-vendors/browser.ts +591 -0
  410. package/src/runtime/resolver-vendors/capsolver.ts +727 -0
  411. package/src/runtime/resolver-vendors/hosts.ts +81 -0
  412. package/src/runtime/resolver-vendors/hypersolutions.ts +480 -0
  413. package/src/runtime/resolver-vendors/twocaptcha.ts +555 -0
  414. package/src/runtime/resolver-vendors/types.ts +261 -0
  415. package/src/runtime/resolver.ts +1543 -0
  416. package/src/runtime/state-telemetry.ts +298 -0
  417. package/src/runtime/state.ts +435 -89
  418. package/src/runtime/stealth-akamai-sbsd.ts +207 -0
  419. package/src/runtime/stealth-cookies.ts +155 -0
  420. package/src/runtime/stealth-owned-headers.ts +67 -0
  421. package/src/runtime/stealth-telemetry.ts +309 -0
  422. package/src/runtime/stealth.ts +2275 -372
  423. package/src/runtime/stt-telemetry.ts +282 -0
  424. package/src/runtime/stt.ts +96 -91
  425. package/src/runtime/timeout.ts +18 -0
  426. package/src/runtime/trace-config.ts +78 -0
  427. package/src/runtime/trace.ts +257 -35
  428. package/src/runtime-boundary-lint.ts +707 -0
  429. package/src/schema.ts +80 -2
  430. package/src/serve.ts +6 -1
  431. package/src/server/error-observability.ts +1 -0
  432. package/src/server/error-stack-frames.ts +214 -0
  433. package/src/server/index.ts +184 -2
  434. package/src/server/runtime-diagnostics.ts +522 -0
  435. package/src/server/sdk-version.ts +22 -0
  436. package/src/server/self-test-input-tokens.ts +29 -14
  437. package/src/server/self-test-redaction.ts +10 -40
  438. package/src/server/self-test.ts +205 -89
  439. package/src/server/sensitive-values.ts +211 -0
  440. package/src/server/serve-implementation.ts +4596 -0
  441. package/src/server/serve.ts +1 -1685
  442. package/src/server/state-backend-report.ts +185 -0
  443. package/src/server/trace-output.ts +189 -0
  444. package/src/server/types.ts +34 -1
  445. package/src/stateful/README.md +146 -0
  446. package/src/stateful/errors.ts +35 -0
  447. package/src/stateful/http-provider-event-emitter.ts +314 -0
  448. package/src/stateful/http-session-owner-registry.ts +306 -0
  449. package/src/stateful/index.ts +18 -0
  450. package/src/stateful/provider-event-delivery-failures.ts +80 -0
  451. package/src/stateful/provider-event-pipeline-metrics.ts +95 -0
  452. package/src/stateful/provider-event-pipeline.ts +61 -0
  453. package/src/stateful/provider-events.ts +462 -0
  454. package/src/stateful/session-key.ts +111 -0
  455. package/src/stateful/stateful-provider-adapter-context.ts +59 -0
  456. package/src/stateful/stateful-provider-adapter-metrics.ts +48 -0
  457. package/src/stateful/stateful-provider-adapter.ts +562 -0
  458. package/src/stateful/stateful-provider-observability.ts +261 -0
  459. package/src/stateful/stateful-provider-owner-forwarder.ts +310 -0
  460. package/src/stateful/stateful-provider-runtime-context.ts +96 -0
  461. package/src/stateful/stateful-provider-runtime-executor.ts +96 -0
  462. package/src/stateful/stateful-provider-session-routing.ts +546 -0
  463. package/src/stateful/stateful-provider-session-runtime.ts +403 -0
  464. package/src/stateful-signing.ts +46 -0
  465. package/src/stealth/profiles.ts +212 -231
  466. package/src/stream-evidence.ts +988 -0
  467. package/src/stream.ts +8 -1
  468. package/src/testing/index.ts +10 -1
  469. package/src/testing/run.ts +685 -19
  470. package/src/trace-sanitization.ts +110 -0
  471. package/src/types.ts +1014 -273
  472. package/src/typescript-module.ts +100 -0
  473. package/dist/choice-token.d.ts +0 -24
  474. package/dist/choice-token.js +0 -74
  475. package/dist/runtime/choice.d.ts +0 -15
  476. package/dist/runtime/choice.js +0 -413
  477. package/src/choice-token.ts +0 -165
  478. package/src/runtime/choice.ts +0 -686
@@ -0,0 +1,422 @@
1
+ import { Buffer } from "node:buffer";
2
+
3
+ import type { JsonValue } from "./contract-json.js";
4
+
5
+ export const REDACTED_FIXTURE_VALUE = "[REDACTED]";
6
+
7
+ const OPAQUE_TOKEN = /^[A-Za-z0-9_+/=.:~-]+$/;
8
+ const OPAQUE_TOKEN_RUN = /[A-Za-z0-9_+/=.:~-]{24,}/g;
9
+ const URL_RUN = /https?:\/\/[^\s"'<>]+/gi;
10
+ const EMAIL_ADDRESS_RUN = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi;
11
+ const DIAGNOSTIC_URL_SENTINEL_DELIMITER = String.fromCodePoint(0);
12
+ const DIAGNOSTIC_URL_SENTINEL_RUN = new RegExp(
13
+ `${DIAGNOSTIC_URL_SENTINEL_DELIMITER}APIFUSE_URL(\\d+)${DIAGNOSTIC_URL_SENTINEL_DELIMITER}`,
14
+ "g",
15
+ );
16
+ const PEM_PRIVATE_KEY =
17
+ /-----BEGIN (?:[A-Z0-9 ]+ )?PRIVATE KEY-----[\s\S]*?-----END (?:[A-Z0-9 ]+ )?PRIVATE KEY-----/g;
18
+ /** RFC 9110 §11.4 `token68`, the single credential token of Bearer, Basic, Negotiate and NTLM. */
19
+ const TOKEN68 = "[A-Za-z0-9._~+/-]+=*";
20
+ /**
21
+ * A credential header's value is the whole RFC 9110 `credentials` production
22
+ * (`auth-scheme SP token68`), so the scheme and its token are one value, not two words.
23
+ * The key starts at a word boundary so a long word run is scanned once, not from every offset.
24
+ */
25
+ const CREDENTIALS_HEADER_ASSIGNMENT = new RegExp(
26
+ String.raw`(?<![\w-])((["']?)[\w-]*authorization\2\s*[:=]\s*)[A-Za-z][\w.+-]*[ \t]+${TOKEN68}(?![^\s,;&])`,
27
+ "gi",
28
+ );
29
+ /**
30
+ * Keyless echoes of the token68 schemes whose token is always a credential. Only the scheme and
31
+ * its whitespace are matched, so matches never overlap a later scheme; the token is the whole
32
+ * opaque run that follows, so no partial redaction leaves a remainder too short for the
33
+ * opaque-token scan. Negotiate and NTLM match only their registered spelling so prose such as
34
+ * "failed to negotiate TLS" is retained.
35
+ */
36
+ const TOKEN68_SCHEMES = [/\bBearer\s+/gi, /\b(?:Negotiate|NTLM)\s+/g] as const;
37
+ /** A keyless `Basic` token is a credential only when it decodes to `user-id:password`. */
38
+ const BASIC_SCHEME = /\bBasic\s+/gi;
39
+ const OPAQUE_TOKEN_CHARACTER = /[A-Za-z0-9_+/=.:~-]/;
40
+ const BASE64_CHARACTER = /[A-Za-z0-9+/]/;
41
+ const ASSIGNMENT_VALUE_DELIMITER = /[\s,;&]/;
42
+ /**
43
+ * A key and its `:`/`=`, starting at a word boundary so a word run is scanned once. Values are
44
+ * never consumed, so an assignment inside another value is still found.
45
+ */
46
+ const ASSIGNMENT_KEY = /(["']?)(?<![\w-])([\w-]+)\1\s*[:=]\s*/gi;
47
+
48
+ /** Matches credential field names without treating benign prefixes such as `author` as `auth`. */
49
+ export function isSensitiveFixtureKey(key: string): boolean {
50
+ const normalized = key.replace(/[-_\s]/g, "").toLowerCase();
51
+ const candidates = [normalized, normalized.replace(/(?:value|payload|header)$/, "")];
52
+ return candidates.some(
53
+ (candidate) =>
54
+ /^(?:authorization|authentication|auth|bearer|cookie|credential|password|passwd|privatekey|secret|session|sessionid|token)$/.test(
55
+ candidate,
56
+ ) ||
57
+ // Unanchored at the start so vendor-prefixed headers (`x-api-key`) match too.
58
+ /(?:api|client|service|access|consumer)(?:key|secret|token)$/.test(candidate) ||
59
+ /(?:authorization|credential|password|passwd|privatekey|secret|sessionid|token)$/.test(
60
+ candidate,
61
+ ),
62
+ );
63
+ }
64
+
65
+ /**
66
+ * Returns JSON fixture data with credential-bearing keys and heuristic-confirmed string secrets
67
+ * replaced. Ordinary short prose and identifiers are retained.
68
+ */
69
+ export function sanitizeFixture(value: JsonValue): JsonValue {
70
+ if (Array.isArray(value)) {
71
+ return value.map((item) => sanitizeFixture(item));
72
+ }
73
+
74
+ if (typeof value === "string") return sanitizeFixtureString(value);
75
+ if (value === null || typeof value !== "object") return value;
76
+
77
+ return Object.fromEntries(
78
+ Object.entries(value).map(([key, entryValue]) => [
79
+ key,
80
+ isSensitiveFixtureKey(key) ? REDACTED_FIXTURE_VALUE : sanitizeFixture(entryValue),
81
+ ]),
82
+ );
83
+ }
84
+
85
+ /** Applies the shared credential-key policy to ordinary JSON fixtures. */
86
+ export function sanitizeOrdinaryFixture(value: JsonValue): JsonValue {
87
+ if (Array.isArray(value)) return value.map((item) => sanitizeOrdinaryFixture(item));
88
+ if (value === null || typeof value !== "object") return value;
89
+ return Object.fromEntries(
90
+ Object.entries(value).map(([key, entryValue]) => [
91
+ key,
92
+ isSensitiveFixtureKey(key) ? REDACTED_FIXTURE_VALUE : sanitizeOrdinaryFixture(entryValue),
93
+ ]),
94
+ );
95
+ }
96
+
97
+ /** Sanitizes a primitive fixture string only when textual-secret heuristics match. */
98
+ export function sanitizeFixtureString(value: string): string {
99
+ let sanitized = value.replace(PEM_PRIVATE_KEY, REDACTED_FIXTURE_VALUE);
100
+ const retainedUrls: string[] = [];
101
+ sanitized = sanitized.replace(URL_RUN, (url) => {
102
+ const index =
103
+ retainedUrls.push(isCredentialBearingUrl(url) ? sanitizeUrlForLogs(url) : url) - 1;
104
+ return `APIFUSEURL${index}X`;
105
+ });
106
+ sanitized = redactCredentialSpans(sanitized, [credentialHeaderSpans, sensitiveAssignmentSpans]);
107
+ sanitized = sanitized.replace(OPAQUE_TOKEN_RUN, (candidate) =>
108
+ isSensitiveFixtureValue(candidate) ? REDACTED_FIXTURE_VALUE : candidate,
109
+ );
110
+ sanitized = sanitized.replace(
111
+ /APIFUSEURL(\d+)X/g,
112
+ (_match, index: string) => retainedUrls[Number(index)] ?? REDACTED_FIXTURE_VALUE,
113
+ );
114
+ return sanitized;
115
+ }
116
+
117
+ /** True for opaque values that are unsafe to retain in paths or unstructured text. */
118
+ export function isSensitiveFixtureValue(value: string): boolean {
119
+ const candidate = decodePathSegment(value);
120
+ if (/^bot(?:\d{6,}:)?[A-Za-z0-9_-]{16,}$/i.test(candidate)) return true;
121
+ if (/^\d{6,}:[A-Za-z0-9_-]{20,}$/.test(candidate)) return true;
122
+ if (/^(?:gh[opusr]_|sk[-_]|xox[baprs]-)[A-Za-z0-9_-]{16,}$/i.test(candidate)) return true;
123
+ if (!OPAQUE_TOKEN.test(candidate) || candidate.length < 24) return false;
124
+ if (/^[a-f0-9]{32,}$/i.test(candidate)) return true;
125
+ return shannonEntropy(candidate) >= 3.5;
126
+ }
127
+
128
+ /** Sanitizes every path segment and values following a credential-like segment name. */
129
+ export function sanitizePathname(pathname: string): string {
130
+ const segments = pathname.split("/");
131
+ return segments
132
+ .map((segment, index) => {
133
+ if (!segment) return segment;
134
+ const decoded = decodePathSegment(segment);
135
+ const previous = index > 0 ? decodePathSegment(segments[index - 1] as string) : "";
136
+ if (
137
+ isSensitivePathSegment(decoded) ||
138
+ isCredentialPathKey(previous) ||
139
+ isSensitiveFixtureValue(decoded)
140
+ ) {
141
+ return REDACTED_FIXTURE_VALUE;
142
+ }
143
+ return segment;
144
+ })
145
+ .join("/");
146
+ }
147
+
148
+ function isCredentialPathKey(key: string): boolean {
149
+ const finalPathPart = key.split("/").at(-1) ?? "";
150
+ const baseSegment = finalPathPart.split(";", 1)[0] ?? "";
151
+ return isSensitiveFixtureKey(baseSegment.split(/[=:]/, 1)[0] ?? "");
152
+ }
153
+
154
+ function isSensitivePathSegment(segment: string): boolean {
155
+ return segment
156
+ .split(/[;/]/)
157
+ .some((part) => isSensitiveFixtureKey(part.split(/[=:]/, 1)[0] ?? ""));
158
+ }
159
+
160
+ /** Removes userinfo, query values, fragments, and credential-like path segments from log URLs. */
161
+ export function sanitizeUrlForLogs(value: string): string {
162
+ try {
163
+ const parsed = new URL(value, "https://fixture.invalid");
164
+ const queryMarker = parsed.search ? `?${REDACTED_FIXTURE_VALUE}` : "";
165
+ const path = sanitizePathname(parsed.pathname);
166
+ if (parsed.origin === "https://fixture.invalid" && !hasExplicitOrigin(value)) {
167
+ return `${path}${queryMarker}`;
168
+ }
169
+ return `${parsed.origin}${path}${queryMarker}`;
170
+ } catch {
171
+ return sanitizePathname(value.split(/[?#]/, 1)[0]);
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Returns query-free request provenance with each path segment scrubbed for credential-like values.
177
+ * Origins, URL userinfo, query values, and fragments are never persisted in request provenance.
178
+ */
179
+ export function requestPathForFixture(value: string): string {
180
+ try {
181
+ return sanitizePathname(new URL(value, "https://fixture.invalid").pathname);
182
+ } catch {
183
+ const path = value.split(/[?#]/, 1)[0];
184
+ return sanitizePathname(path.startsWith("/") ? path : `/${path}`);
185
+ }
186
+ }
187
+
188
+ /** Scrubs secrets and terminal/log control characters before diagnostic text is emitted. */
189
+ export function sanitizeDiagnosticText(value: string): string {
190
+ const retainedUrls: string[] = [];
191
+ // Remove attacker-controlled NUL delimiters before introducing internal URL sentinels.
192
+ let sanitized = encodeDiagnosticControls(value)
193
+ .replace(URL_RUN, (url) => {
194
+ const index = retainedUrls.push(sanitizeUrlForLogs(url)) - 1;
195
+ return `${DIAGNOSTIC_URL_SENTINEL_DELIMITER}APIFUSE_URL${index}${DIAGNOSTIC_URL_SENTINEL_DELIMITER}`;
196
+ })
197
+ .replace(EMAIL_ADDRESS_RUN, REDACTED_FIXTURE_VALUE);
198
+ sanitized = redactCredentialSpans(sanitized, [
199
+ credentialHeaderSpans,
200
+ keylessCredentialSpans,
201
+ sensitiveAssignmentSpans,
202
+ ]);
203
+ sanitized = sanitized.replace(OPAQUE_TOKEN_RUN, (candidate, offset: number, source: string) => {
204
+ if (/^(?:request|trace|correlation)[-_]?id[:=]/i.test(candidate)) return candidate;
205
+ const prefix = source.slice(Math.max(0, offset - 32), offset);
206
+ if (/(?:request|trace|correlation)[-_]?id\s*[:=]\s*$/i.test(prefix)) return candidate;
207
+ return isSensitiveFixtureValue(candidate) ? REDACTED_FIXTURE_VALUE : candidate;
208
+ });
209
+ sanitized = sanitized.replace(
210
+ DIAGNOSTIC_URL_SENTINEL_RUN,
211
+ (_match, index: string) => retainedUrls[Number(index)] ?? REDACTED_FIXTURE_VALUE,
212
+ );
213
+ return encodeDiagnosticControls(sanitized);
214
+ }
215
+
216
+ type CredentialSpan = readonly [start: number, end: number];
217
+
218
+ /**
219
+ * Every credential rule scans the same unmodified text and reports spans; the output redacts
220
+ * their union. No rule's rewrite can hide a key or a scheme word from another rule, so adding a
221
+ * rule can only redact more.
222
+ */
223
+ function redactCredentialSpans(
224
+ value: string,
225
+ rules: readonly ((value: string) => Iterable<CredentialSpan>)[],
226
+ ): string {
227
+ const spans = rules
228
+ .flatMap((rule) => [...rule(value)])
229
+ .sort((a, b) => a[0] - b[0] || b[1] - a[1]);
230
+ let output = "";
231
+ let cursor = 0;
232
+ let open: [number, number] | undefined;
233
+ const close = (span: [number, number]) => {
234
+ output += value.slice(cursor, span[0]) + REDACTED_FIXTURE_VALUE;
235
+ cursor = span[1];
236
+ };
237
+ for (const [start, end] of spans) {
238
+ if (open && start <= open[1]) {
239
+ open[1] = Math.max(open[1], end);
240
+ continue;
241
+ }
242
+ if (open) close(open);
243
+ open = [start, end];
244
+ }
245
+ if (open) close(open);
246
+ return output + value.slice(cursor);
247
+ }
248
+
249
+ /** The scheme and token of a credential header value, as one span. */
250
+ function* credentialHeaderSpans(value: string): Iterable<CredentialSpan> {
251
+ for (const match of value.matchAll(CREDENTIALS_HEADER_ASSIGNMENT)) {
252
+ yield [match.index + (match[1] as string).length, match.index + match[0].length];
253
+ }
254
+ }
255
+
256
+ /** Tokens after keyless `Bearer`/`Negotiate`/`NTLM`, and `Basic` tokens that decode to `user-id:password`. */
257
+ function* keylessCredentialSpans(value: string): Iterable<CredentialSpan> {
258
+ const opaqueEnds = runEnds(value, OPAQUE_TOKEN_CHARACTER);
259
+ for (const scheme of TOKEN68_SCHEMES) {
260
+ for (const match of value.matchAll(scheme)) {
261
+ const start = match.index + match[0].length;
262
+ if (opaqueEnds[start] > start) yield [start, opaqueEnds[start]];
263
+ }
264
+ }
265
+ const base64Ends = runEnds(value, BASE64_CHARACTER);
266
+ for (const match of value.matchAll(BASIC_SCHEME)) {
267
+ const start = match.index + match[0].length;
268
+ const payloadEnd = base64Ends[start];
269
+ let end = payloadEnd;
270
+ while (end < payloadEnd + 2 && value[end] === "=") end++;
271
+ // The base64 token must be the whole opaque run, not the prefix of a longer token.
272
+ if (payloadEnd === start || end !== opaqueEnds[start]) continue;
273
+ if (Buffer.from(value.slice(start, end), "base64").includes(0x3a)) yield [start, end];
274
+ }
275
+ }
276
+
277
+ /**
278
+ * Values of credential-named keys; quotes around a quoted value are kept. Every key is visited,
279
+ * including keys inside another key's value (`auth: {"password": "x"}`), and each value's extent
280
+ * is read from precomputed tables, so the scan stays linear in the text length.
281
+ */
282
+ function* sensitiveAssignmentSpans(value: string): Iterable<CredentialSpan> {
283
+ let extents: AssignmentValueExtents | undefined;
284
+ for (const key of value.matchAll(ASSIGNMENT_KEY)) {
285
+ const name = key[2] as string;
286
+ if (!isSensitiveFixtureKey(name) && name.toLowerCase() !== "key") continue;
287
+ extents ??= assignmentValueExtents(value);
288
+ const start = key.index + key[0].length;
289
+ const quote = value[start];
290
+ const closing =
291
+ quote === '"'
292
+ ? extents.doubleQuoteClose[start + 1]
293
+ : quote === "'"
294
+ ? extents.singleQuoteClose[start + 1]
295
+ : -1;
296
+ if (closing !== -1) {
297
+ yield [start + 1, closing];
298
+ } else if (extents.unquotedEnds[start] > start) {
299
+ yield [start, extents.unquotedEnds[start]];
300
+ }
301
+ }
302
+ }
303
+
304
+ type AssignmentValueExtents = {
305
+ /** End of the unquoted value (`[^\s,;&]+`) starting at each index. */
306
+ readonly unquotedEnds: Int32Array;
307
+ /** Index of the closing `"` when a `"(?:\\.|[^"\\])*"` body is read from each index, or -1. */
308
+ readonly doubleQuoteClose: Int32Array;
309
+ readonly singleQuoteClose: Int32Array;
310
+ };
311
+
312
+ function assignmentValueExtents(value: string): AssignmentValueExtents {
313
+ const unquotedEnds = new Int32Array(value.length + 1);
314
+ unquotedEnds[value.length] = value.length;
315
+ for (let index = value.length - 1; index >= 0; index--) {
316
+ unquotedEnds[index] = ASSIGNMENT_VALUE_DELIMITER.test(value[index] as string)
317
+ ? index
318
+ : unquotedEnds[index + 1];
319
+ }
320
+ return {
321
+ unquotedEnds,
322
+ doubleQuoteClose: closingQuotes(value, '"'),
323
+ singleQuoteClose: closingQuotes(value, "'"),
324
+ };
325
+ }
326
+
327
+ /** Right-to-left: reading a quoted body from index i either closes at a quote or skips an escape pair. */
328
+ function closingQuotes(value: string, quote: string): Int32Array {
329
+ const closing = new Int32Array(value.length + 2).fill(-1);
330
+ for (let index = value.length - 1; index >= 0; index--) {
331
+ const character = value[index];
332
+ closing[index] =
333
+ character === quote
334
+ ? index
335
+ : character === "\\"
336
+ ? /[^\n\r\u2028\u2029]/.test(value[index + 1] ?? "\n")
337
+ ? (closing[index + 2] as number)
338
+ : -1
339
+ : (closing[index + 1] as number);
340
+ }
341
+ return closing;
342
+ }
343
+
344
+ /** For each index, the end of the run of `character` starting there, in one right-to-left pass. */
345
+ function runEnds(value: string, character: RegExp): Int32Array {
346
+ const ends = new Int32Array(value.length + 1);
347
+ ends[value.length] = value.length;
348
+ for (let index = value.length - 1; index >= 0; index--) {
349
+ ends[index] = character.test(value[index] as string) ? (ends[index + 1] as number) : index;
350
+ }
351
+ return ends;
352
+ }
353
+
354
+ function isCredentialBearingUrl(value: string): boolean {
355
+ try {
356
+ const parsed = new URL(value);
357
+ return (
358
+ parsed.username !== "" ||
359
+ parsed.password !== "" ||
360
+ parsed.hash !== "" ||
361
+ parsed.search !== "" ||
362
+ parsed.pathname.split("/").some((segment, index, segments) => {
363
+ const decoded = decodePathSegment(segment);
364
+ const previous = decodePathSegment(segments[index - 1] ?? "");
365
+ return (
366
+ isSensitiveFixtureKey(decoded) ||
367
+ isCredentialPathKey(previous) ||
368
+ isSensitiveFixtureValue(decoded)
369
+ );
370
+ })
371
+ );
372
+ } catch {
373
+ return false;
374
+ }
375
+ }
376
+
377
+ /** Encodes terminal/log control characters without applying value-level secret heuristics. */
378
+ export function encodeDiagnosticControls(value: string): string {
379
+ let result = "";
380
+ for (const character of value) {
381
+ const code = character.codePointAt(0) ?? 0;
382
+ if (code === 0x0a || code === 0x0d || code === 0x2028 || code === 0x2029) {
383
+ result += " ";
384
+ } else if (
385
+ (code >= 0 && code <= 0x1f) ||
386
+ (code >= 0x7f && code <= 0x9f) ||
387
+ code === 0x061c ||
388
+ code === 0x200e ||
389
+ code === 0x200f ||
390
+ (code >= 0x202a && code <= 0x202e) ||
391
+ (code >= 0x2066 && code <= 0x2069)
392
+ ) {
393
+ result += `\\u${code.toString(16).padStart(4, "0")}`;
394
+ } else {
395
+ result += character;
396
+ }
397
+ }
398
+ return result;
399
+ }
400
+
401
+ function decodePathSegment(value: string): string {
402
+ try {
403
+ return decodeURIComponent(value);
404
+ } catch {
405
+ return value;
406
+ }
407
+ }
408
+
409
+ function hasExplicitOrigin(value: string): boolean {
410
+ return /^[a-z][a-z\d+.-]*:\/\//i.test(value);
411
+ }
412
+
413
+ function shannonEntropy(value: string): number {
414
+ const counts = new Map<string, number>();
415
+ for (const character of value) counts.set(character, (counts.get(character) ?? 0) + 1);
416
+ let entropy = 0;
417
+ for (const count of counts.values()) {
418
+ const probability = count / value.length;
419
+ entropy -= probability * Math.log2(probability);
420
+ }
421
+ return entropy;
422
+ }
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Shared, dependency-free definitions for LLM-friendly handles (ADR-0012).
3
+ *
4
+ * Kept separate from the runtime so schema helpers, lint, and the runtime can
5
+ * agree on the JSON Schema meta key and the declaration shape without importing
6
+ * each other.
7
+ */
8
+
9
+ /** JSON Schema meta key placed on every schema field created by `kind.field()`. */
10
+ export const APIFUSE_HANDLE_META_KEY = "x-apifuse-handle" as const;
11
+
12
+ export type HandleKindType = "cursor" | "draft";
13
+ export type HandleAccess = "bound" | "public";
14
+
15
+ /**
16
+ * Operation key(s) that issue a handle. A single string is the common case; a
17
+ * list names every operation whose output carries the field (for example
18
+ * `search-address` and `reverse-geocode` both issuing `location_token`).
19
+ */
20
+ export type HandleIssuedBy = string | readonly string[];
21
+
22
+ /** The schema side a handle field appears on. */
23
+ export type HandleFieldDirection = "input" | "output";
24
+
25
+ /**
26
+ * Direction-specific property keys for one handle kind, for the operations
27
+ * whose caller-facing contract names the same handle differently coming in and
28
+ * going out (the platform canon's `cursor` in, `next_cursor` out). The two keys
29
+ * are two spellings of ONE handle: the value a caller reads from `output` is
30
+ * the value it must send back under `input`, unedited.
31
+ */
32
+ export interface HandleFieldNames {
33
+ readonly input: string;
34
+ readonly output: string;
35
+ }
36
+
37
+ /**
38
+ * Value stored under {@link APIFUSE_HANDLE_META_KEY} on a handle field.
39
+ *
40
+ * `fieldName` is the INPUT property key — the name a caller sends the handle
41
+ * back under, and the name every runtime error message quotes. `outputFieldName`
42
+ * is present only when the output key differs, so a kind with one name emits
43
+ * exactly the meta it always did.
44
+ */
45
+ export interface HandleFieldMeta {
46
+ readonly kind: string;
47
+ readonly type: HandleKindType;
48
+ readonly fieldName: string;
49
+ readonly outputFieldName?: string;
50
+ readonly issuedBy?: HandleIssuedBy;
51
+ }
52
+
53
+ /**
54
+ * Structural view of a handle kind as seen by the provider declaration
55
+ * (`handle: [WaitingDraft, PageCursor]`), lint, and the registry. The runtime
56
+ * kinds returned by `defineCursor` / `defineDraft` satisfy this shape.
57
+ *
58
+ * `fieldName` / `outputFieldName` follow the {@link HandleFieldMeta} rule:
59
+ * `fieldName` is the input key, `outputFieldName` is set only when the output
60
+ * key differs.
61
+ */
62
+ export interface HandleKindDeclaration {
63
+ readonly name: string;
64
+ readonly type: HandleKindType;
65
+ readonly fieldName: string;
66
+ readonly outputFieldName?: string;
67
+ readonly access: HandleAccess;
68
+ readonly issuedBy?: HandleIssuedBy;
69
+ }
70
+
71
+ /**
72
+ * The property key a handle kind claims on one side of an operation.
73
+ *
74
+ * This is the single place that resolves a direction to a key, so lint, the
75
+ * runtime, and error text cannot disagree about which name belongs where.
76
+ */
77
+ export function handleFieldNameFor(
78
+ kind: Pick<HandleKindDeclaration, "fieldName" | "outputFieldName">,
79
+ direction: HandleFieldDirection,
80
+ ): string {
81
+ if (direction === "input") return kind.fieldName;
82
+ return kind.outputFieldName ?? kind.fieldName;
83
+ }
84
+
85
+ /** True when a kind names the same handle differently on input and output. */
86
+ export function handleHasDirectionalFieldNames(
87
+ kind: Pick<HandleKindDeclaration, "fieldName" | "outputFieldName">,
88
+ ): boolean {
89
+ return kind.outputFieldName !== undefined && kind.outputFieldName !== kind.fieldName;
90
+ }
91
+
92
+ /** Kind names are lowercase letters only so they never fuse with the word body. */
93
+ export const HANDLE_KIND_NAME_PATTERN = /^[a-z]{2,12}$/;
94
+
95
+ /** True for a single operation key or a non-empty list of operation keys. */
96
+ export function isHandleIssuedBy(value: unknown): value is HandleIssuedBy {
97
+ if (typeof value === "string") return true;
98
+ return (
99
+ Array.isArray(value) && value.length > 0 && value.every((entry) => typeof entry === "string")
100
+ );
101
+ }
102
+
103
+ /** Normalizes `issuedBy` to a list of operation keys (empty when undefined). */
104
+ export function handleIssuerList(issuedBy: HandleIssuedBy | undefined): readonly string[] {
105
+ if (issuedBy === undefined) return [];
106
+ return typeof issuedBy === "string" ? [issuedBy] : issuedBy;
107
+ }
108
+
109
+ /**
110
+ * Renders the issuing operation(s) as inline code for error and description
111
+ * text: `` `a` ``, `` `a` or `b` ``, `` `a`, `b`, or `c` ``. Undefined when no
112
+ * issuer is declared.
113
+ */
114
+ export function formatHandleIssuers(issuedBy: HandleIssuedBy | undefined): string | undefined {
115
+ const issuers = handleIssuerList(issuedBy).map((operation) => `\`${operation}\``);
116
+ if (issuers.length === 0) return undefined;
117
+ if (issuers.length === 1) return issuers[0];
118
+ if (issuers.length === 2) return `${issuers[0]} or ${issuers[1]}`;
119
+ return `${issuers.slice(0, -1).join(", ")}, or ${issuers[issuers.length - 1]}`;
120
+ }
121
+
122
+ export function isHandleFieldMeta(value: unknown): value is HandleFieldMeta {
123
+ if (typeof value !== "object" || value === null) return false;
124
+ const record = value as Record<string, unknown>;
125
+ return (
126
+ typeof record.kind === "string" &&
127
+ (record.type === "cursor" || record.type === "draft") &&
128
+ typeof record.fieldName === "string" &&
129
+ (record.outputFieldName === undefined || typeof record.outputFieldName === "string") &&
130
+ (record.issuedBy === undefined || isHandleIssuedBy(record.issuedBy))
131
+ );
132
+ }
133
+
134
+ /**
135
+ * The SDK-owned description `kind.field()` emits. Lint exempts a handle field
136
+ * from `describeKey` only while its description is exactly this text; a
137
+ * provider override re-enters the localization rules.
138
+ */
139
+ export function handleFieldDescription(meta: Pick<HandleFieldMeta, "issuedBy">): string {
140
+ const issuers = formatHandleIssuers(meta.issuedBy);
141
+ const origin = issuers ? ` issued by ${issuers}` : "";
142
+ return `Opaque handle${origin}. Copy it exactly as returned; do not edit or shorten it.`;
143
+ }