@wirebench/engine 2.2.1 → 3.1.0

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 (566) hide show
  1. package/README.md +134 -1
  2. package/dist/account/schema.d.ts +34 -0
  3. package/dist/account/schema.js +29 -0
  4. package/dist/assert/callback.d.ts +76 -0
  5. package/dist/assert/callback.js +320 -0
  6. package/dist/assert/capture-source.d.ts +50 -0
  7. package/dist/assert/capture-source.js +89 -0
  8. package/dist/assert/check.d.ts +8 -0
  9. package/dist/assert/check.js +18 -0
  10. package/dist/assert/header.d.ts +5 -0
  11. package/dist/assert/header.js +48 -0
  12. package/dist/assert/index.d.ts +2 -2
  13. package/dist/assert/index.js +12 -0
  14. package/dist/assert/match.d.ts +6 -0
  15. package/dist/assert/match.js +13 -4
  16. package/dist/assert/model.d.ts +110 -3
  17. package/dist/assert/model.js +16 -1
  18. package/dist/assert/schema.d.ts +194 -1
  19. package/dist/assert/schema.js +154 -12
  20. package/dist/assert/status-names.d.ts +9 -0
  21. package/dist/assert/status-names.js +27 -0
  22. package/dist/assert/status.d.ts +4 -1
  23. package/dist/assert/status.js +27 -3
  24. package/dist/asyncapi/browser.d.ts +7 -0
  25. package/dist/asyncapi/browser.js +6 -0
  26. package/dist/asyncapi/frame-check-worker-host.d.ts +44 -0
  27. package/dist/asyncapi/frame-check-worker-host.js +175 -0
  28. package/dist/asyncapi/frame-check-worker.d.ts +12 -0
  29. package/dist/asyncapi/frame-check-worker.js +25 -0
  30. package/dist/asyncapi/frame-check.d.ts +25 -0
  31. package/dist/asyncapi/frame-check.js +78 -0
  32. package/dist/asyncapi/import.d.ts +25 -0
  33. package/dist/asyncapi/import.js +34 -0
  34. package/dist/asyncapi/map.d.ts +40 -0
  35. package/dist/asyncapi/map.js +230 -0
  36. package/dist/asyncapi/model.d.ts +84 -0
  37. package/dist/asyncapi/model.js +7 -0
  38. package/dist/asyncapi/normalise-2.d.ts +13 -0
  39. package/dist/asyncapi/normalise-2.js +113 -0
  40. package/dist/asyncapi/normalise-3.d.ts +14 -0
  41. package/dist/asyncapi/normalise-3.js +133 -0
  42. package/dist/asyncapi/parse.d.ts +31 -0
  43. package/dist/asyncapi/parse.js +85 -0
  44. package/dist/asyncapi/read.d.ts +35 -0
  45. package/dist/asyncapi/read.js +186 -0
  46. package/dist/asyncapi/security.d.ts +14 -0
  47. package/dist/asyncapi/security.js +55 -0
  48. package/dist/asyncapi/update.d.ts +53 -0
  49. package/dist/asyncapi/update.js +220 -0
  50. package/dist/errors.d.ts +16 -0
  51. package/dist/errors.js +22 -0
  52. package/dist/grpc/events.d.ts +22 -0
  53. package/dist/grpc/events.js +1 -0
  54. package/dist/grpc/expand.d.ts +1 -1
  55. package/dist/grpc/expand.js +15 -5
  56. package/dist/grpc/files.d.ts +271 -0
  57. package/dist/grpc/files.js +62 -0
  58. package/dist/grpc/model.d.ts +11 -1
  59. package/dist/grpc/module.d.ts +2 -0
  60. package/dist/grpc/module.js +13 -0
  61. package/dist/grpc/run.d.ts +73 -0
  62. package/dist/grpc/run.js +407 -0
  63. package/dist/grpc/script-types.d.ts +8 -0
  64. package/dist/grpc/script-types.js +149 -0
  65. package/dist/grpc/scripting.d.ts +34 -0
  66. package/dist/grpc/scripting.js +147 -0
  67. package/dist/grpc/send-input.d.ts +46 -0
  68. package/dist/grpc/send-input.js +42 -0
  69. package/dist/grpc/send.d.ts +2 -2
  70. package/dist/grpc/status.d.ts +9 -2
  71. package/dist/grpc/status.js +11 -24
  72. package/dist/grpc/storage.d.ts +9 -0
  73. package/dist/grpc/storage.js +167 -0
  74. package/dist/{rest/auth.d.ts → http/auth/apply-auth.d.ts} +4 -4
  75. package/dist/{rest/auth.js → http/auth/apply-auth.js} +1 -1
  76. package/dist/http/auth/apply.d.ts +1 -1
  77. package/dist/{rest → http/auth}/oauth2.d.ts +2 -2
  78. package/dist/{rest → http/auth}/oauth2.js +1 -1
  79. package/dist/http/auth/send-auth.d.ts +57 -0
  80. package/dist/http/auth/send-auth.js +7 -0
  81. package/dist/http/client.js +150 -8
  82. package/dist/http/cookies.d.ts +79 -0
  83. package/dist/http/cookies.js +81 -0
  84. package/dist/http/curl.d.ts +4 -14
  85. package/dist/http/curl.js +4 -154
  86. package/dist/http/decompress.d.ts +7 -0
  87. package/dist/http/decompress.js +23 -1
  88. package/dist/http/document-fetch.d.ts +42 -0
  89. package/dist/http/document-fetch.js +207 -0
  90. package/dist/http/entries.d.ts +27 -0
  91. package/dist/http/entries.js +16 -0
  92. package/dist/http/escape.d.ts +18 -0
  93. package/dist/http/escape.js +30 -0
  94. package/dist/http/fetch-document.d.ts +28 -0
  95. package/dist/{wsdl/fetch.js → http/fetch-document.js} +8 -16
  96. package/dist/http/media-type.d.ts +9 -0
  97. package/dist/http/media-type.js +12 -0
  98. package/dist/http/transcript-cap.d.ts +26 -0
  99. package/dist/http/transcript-cap.js +36 -0
  100. package/dist/http/types.d.ts +59 -0
  101. package/dist/http/webhook-signature.d.ts +81 -0
  102. package/dist/http/webhook-signature.js +227 -0
  103. package/dist/import-detect.d.ts +2 -2
  104. package/dist/import-detect.js +10 -1
  105. package/dist/index.d.ts +207 -55
  106. package/dist/index.js +129 -32
  107. package/dist/json/cursor.d.ts +2 -0
  108. package/dist/json/cursor.js +1 -0
  109. package/dist/json/pointer-range.d.ts +18 -0
  110. package/dist/json/pointer-range.js +157 -0
  111. package/dist/json/schema/model.d.ts +59 -0
  112. package/dist/json/schema/model.js +8 -0
  113. package/dist/json/schema/parse-text.d.ts +16 -0
  114. package/dist/json/schema/parse-text.js +37 -0
  115. package/dist/{rest/openapi → json/schema}/refs.d.ts +2 -2
  116. package/dist/{rest/openapi → json/schema}/refs.js +3 -3
  117. package/dist/{rest/openapi → json/schema}/sample.d.ts +4 -0
  118. package/dist/{rest/openapi → json/schema}/sample.js +2 -2
  119. package/dist/json/schema/source.d.ts +21 -0
  120. package/dist/json/schema/source.js +8 -0
  121. package/dist/json/schema-validate.d.ts +49 -0
  122. package/dist/json/schema-validate.js +557 -0
  123. package/dist/{wss/keystore → keystore}/index.js +1 -1
  124. package/dist/{wss/keystore → keystore}/pem.js +1 -1
  125. package/dist/{wss/keystore → keystore}/pkcs12.js +1 -1
  126. package/dist/project/attachments-cache.d.ts +3 -1
  127. package/dist/{wsdl → project}/cache-naming.js +1 -1
  128. package/dist/project/endpoints.d.ts +18 -8
  129. package/dist/project/endpoints.js +21 -6
  130. package/dist/project/environments.d.ts +7 -5
  131. package/dist/project/history.d.ts +105 -9
  132. package/dist/project/history.js +195 -23
  133. package/dist/project/inherit.d.ts +7 -0
  134. package/dist/project/inherit.js +9 -0
  135. package/dist/project/keystores.d.ts +1 -1
  136. package/dist/project/load-helpers.d.ts +89 -0
  137. package/dist/project/load-helpers.js +177 -0
  138. package/dist/project/load.d.ts +30 -4
  139. package/dist/project/load.js +168 -545
  140. package/dist/project/managed-files.d.ts +36 -0
  141. package/dist/project/managed-files.js +97 -0
  142. package/dist/project/migrate.d.ts +2 -2
  143. package/dist/project/migrate.js +2 -2
  144. package/dist/project/model.d.ts +92 -14
  145. package/dist/project/model.js +50 -4
  146. package/dist/project/paths.d.ts +10 -1
  147. package/dist/project/paths.js +10 -1
  148. package/dist/project/preferences.d.ts +11 -2
  149. package/dist/project/preferences.js +5 -1
  150. package/dist/project/properties.d.ts +31 -17
  151. package/dist/project/properties.js +89 -70
  152. package/dist/project/request-location.d.ts +20 -0
  153. package/dist/project/request-location.js +62 -0
  154. package/dist/project/save.d.ts +12 -2
  155. package/dist/project/save.js +186 -141
  156. package/dist/project/schema-parts.d.ts +337 -0
  157. package/dist/project/schema-parts.js +248 -0
  158. package/dist/project/schema.d.ts +72 -928
  159. package/dist/project/schema.js +17 -466
  160. package/dist/project/sequence-guards.d.ts +43 -0
  161. package/dist/project/sequence-guards.js +109 -0
  162. package/dist/project/serialize-helpers.d.ts +60 -0
  163. package/dist/project/serialize-helpers.js +108 -0
  164. package/dist/project/serialize.d.ts +7 -8
  165. package/dist/project/serialize.js +34 -293
  166. package/dist/protocol/features.d.ts +51 -0
  167. package/dist/protocol/features.js +64 -0
  168. package/dist/protocol/index.d.ts +7 -0
  169. package/dist/protocol/index.js +4 -0
  170. package/dist/protocol/module.d.ts +247 -0
  171. package/dist/protocol/module.js +48 -0
  172. package/dist/protocol/registry.d.ts +42 -0
  173. package/dist/protocol/registry.js +47 -0
  174. package/dist/protocols.d.ts +77 -0
  175. package/dist/protocols.js +40 -0
  176. package/dist/redact/index.d.ts +12 -1
  177. package/dist/redact/index.js +55 -7
  178. package/dist/redact/literal.d.ts +10 -0
  179. package/dist/redact/literal.js +110 -12
  180. package/dist/rest/body.d.ts +2 -10
  181. package/dist/rest/body.js +3 -25
  182. package/dist/rest/browser.d.ts +4 -0
  183. package/dist/rest/browser.js +5 -0
  184. package/dist/rest/contract-check-worker-host.d.ts +36 -0
  185. package/dist/rest/contract-check-worker-host.js +158 -0
  186. package/dist/rest/contract-check-worker.d.ts +7 -0
  187. package/dist/rest/contract-check-worker.js +28 -0
  188. package/dist/rest/contract-check.d.ts +43 -0
  189. package/dist/rest/contract-check.js +217 -0
  190. package/dist/rest/cookie-jar.d.ts +62 -0
  191. package/dist/rest/cookie-jar.js +262 -0
  192. package/dist/rest/cookies.d.ts +6 -22
  193. package/dist/rest/cookies.js +6 -42
  194. package/dist/rest/curl.js +5 -1
  195. package/dist/rest/events.d.ts +12 -0
  196. package/dist/rest/events.js +1 -0
  197. package/dist/rest/expand.d.ts +6 -1
  198. package/dist/rest/expand.js +29 -11
  199. package/dist/rest/files.d.ts +439 -0
  200. package/dist/rest/files.js +107 -0
  201. package/dist/rest/json-form.d.ts +97 -0
  202. package/dist/rest/json-form.js +396 -0
  203. package/dist/rest/model.d.ts +35 -24
  204. package/dist/rest/model.js +6 -9
  205. package/dist/rest/module.d.ts +2 -0
  206. package/dist/rest/module.js +13 -0
  207. package/dist/rest/openapi/cache.d.ts +7 -2
  208. package/dist/rest/openapi/cache.js +2 -2
  209. package/dist/rest/openapi/import.d.ts +11 -16
  210. package/dist/rest/openapi/import.js +6 -2
  211. package/dist/rest/openapi/map.d.ts +38 -1
  212. package/dist/rest/openapi/map.js +151 -41
  213. package/dist/rest/openapi/match.d.ts +20 -0
  214. package/dist/rest/openapi/match.js +117 -0
  215. package/dist/rest/openapi/model.d.ts +33 -45
  216. package/dist/rest/openapi/parse.d.ts +3 -12
  217. package/dist/rest/openapi/parse.js +138 -67
  218. package/dist/rest/openapi/responses.d.ts +23 -0
  219. package/dist/rest/openapi/responses.js +59 -0
  220. package/dist/rest/openapi/runtime-expression.d.ts +69 -0
  221. package/dist/rest/openapi/runtime-expression.js +213 -0
  222. package/dist/rest/openapi/update.d.ts +116 -0
  223. package/dist/rest/openapi/update.js +456 -0
  224. package/dist/rest/postman/map.d.ts +1 -0
  225. package/dist/rest/postman/map.js +66 -4
  226. package/dist/rest/postman/model.d.ts +9 -0
  227. package/dist/rest/postman/parse.js +33 -10
  228. package/dist/rest/response.d.ts +2 -23
  229. package/dist/rest/response.js +3 -75
  230. package/dist/rest/run.d.ts +56 -0
  231. package/dist/rest/run.js +395 -0
  232. package/dist/rest/script-types.d.ts +7 -0
  233. package/dist/rest/script-types.js +83 -0
  234. package/dist/rest/scripting.d.ts +38 -0
  235. package/dist/rest/scripting.js +282 -0
  236. package/dist/rest/send-input.d.ts +54 -0
  237. package/dist/rest/send-input.js +73 -0
  238. package/dist/rest/send.d.ts +77 -4
  239. package/dist/rest/send.js +128 -7
  240. package/dist/rest/sse-transcript.d.ts +25 -0
  241. package/dist/rest/sse-transcript.js +95 -0
  242. package/dist/rest/sse.d.ts +43 -0
  243. package/dist/rest/sse.js +195 -0
  244. package/dist/rest/storage.d.ts +26 -0
  245. package/dist/rest/storage.js +262 -0
  246. package/dist/rest/url.d.ts +10 -4
  247. package/dist/rest/url.js +38 -5
  248. package/dist/run/baseline-update.d.ts +35 -0
  249. package/dist/run/baseline-update.js +97 -0
  250. package/dist/run/baseline.d.ts +42 -0
  251. package/dist/run/baseline.js +93 -0
  252. package/dist/run/context.d.ts +86 -0
  253. package/dist/run/context.js +31 -0
  254. package/dist/run/current-values.d.ts +32 -0
  255. package/dist/run/current-values.js +43 -0
  256. package/dist/run/event-queue.d.ts +14 -0
  257. package/dist/run/event-queue.js +46 -0
  258. package/dist/run/exchange.d.ts +62 -0
  259. package/dist/run/exchange.js +65 -0
  260. package/dist/run/host.d.ts +89 -0
  261. package/dist/run/host.js +1 -0
  262. package/dist/run/index.d.ts +20 -7
  263. package/dist/run/index.js +12 -4
  264. package/dist/run/oauth2-token.d.ts +42 -0
  265. package/dist/run/oauth2-token.js +92 -0
  266. package/dist/run/open.d.ts +12 -0
  267. package/dist/run/open.js +16 -0
  268. package/dist/run/run.d.ts +124 -4
  269. package/dist/run/run.js +293 -134
  270. package/dist/run/scope.d.ts +11 -0
  271. package/dist/run/scope.js +26 -0
  272. package/dist/run/script-support.d.ts +53 -0
  273. package/dist/run/script-support.js +68 -0
  274. package/dist/run/secret-needs.d.ts +11 -4
  275. package/dist/run/secret-needs.js +48 -96
  276. package/dist/run/select.d.ts +34 -25
  277. package/dist/run/select.js +45 -54
  278. package/dist/run/send-helpers.d.ts +93 -0
  279. package/dist/run/send-helpers.js +255 -0
  280. package/dist/run/tree.d.ts +50 -0
  281. package/dist/run/tree.js +54 -0
  282. package/dist/script/api/postman.d.ts +10 -0
  283. package/dist/script/api/postman.js +264 -0
  284. package/dist/script/api/prelude.d.ts +20 -0
  285. package/dist/script/api/prelude.js +215 -0
  286. package/dist/script/apply.d.ts +44 -0
  287. package/dist/script/apply.js +120 -0
  288. package/dist/script/check/host.d.ts +41 -0
  289. package/dist/script/check/host.js +143 -0
  290. package/dist/script/check/service.d.ts +52 -0
  291. package/dist/script/check/service.js +195 -0
  292. package/dist/script/check/worker.d.ts +1 -0
  293. package/dist/script/check/worker.js +40 -0
  294. package/dist/script/index.d.ts +16 -0
  295. package/dist/script/index.js +10 -0
  296. package/dist/script/lookup.d.ts +10 -0
  297. package/dist/script/lookup.js +23 -0
  298. package/dist/script/model.d.ts +82 -0
  299. package/dist/script/model.js +18 -0
  300. package/dist/script/props.d.ts +8 -0
  301. package/dist/script/props.js +28 -0
  302. package/dist/script/request-scripts.d.ts +83 -0
  303. package/dist/script/request-scripts.js +160 -0
  304. package/dist/script/run.d.ts +47 -0
  305. package/dist/script/run.js +146 -0
  306. package/dist/script/sandbox/execute.d.ts +26 -0
  307. package/dist/script/sandbox/execute.js +212 -0
  308. package/dist/script/sandbox/host-api.d.ts +27 -0
  309. package/dist/script/sandbox/host-api.js +101 -0
  310. package/dist/script/sandbox/host.d.ts +18 -0
  311. package/dist/script/sandbox/host.js +172 -0
  312. package/dist/script/sandbox/model.d.ts +69 -0
  313. package/dist/script/sandbox/model.js +24 -0
  314. package/dist/script/sandbox/worker.d.ts +1 -0
  315. package/dist/script/sandbox/worker.js +20 -0
  316. package/dist/script/send.d.ts +27 -0
  317. package/dist/script/send.js +76 -0
  318. package/dist/script/strip.d.ts +5 -0
  319. package/dist/script/strip.js +43 -0
  320. package/dist/script/types/api.d.ts +38 -0
  321. package/dist/script/types/api.js +113 -0
  322. package/dist/script/types/json-schema.d.ts +26 -0
  323. package/dist/script/types/json-schema.js +206 -0
  324. package/dist/secrets/env-names.d.ts +9 -1
  325. package/dist/secrets/env-names.js +17 -1
  326. package/dist/secrets/resolve.d.ts +44 -4
  327. package/dist/secrets/resolve.js +58 -1
  328. package/dist/secrets/scan/apply.d.ts +36 -0
  329. package/dist/secrets/scan/apply.js +194 -0
  330. package/dist/secrets/scan/rules.d.ts +32 -0
  331. package/dist/secrets/scan/rules.js +346 -0
  332. package/dist/secrets/scan/scan.d.ts +6 -0
  333. package/dist/secrets/scan/scan.js +57 -0
  334. package/dist/secrets/scan/walk.d.ts +70 -0
  335. package/dist/secrets/scan/walk.js +156 -0
  336. package/dist/secrets/secret-refs.d.ts +8 -0
  337. package/dist/secrets/secret-refs.js +25 -0
  338. package/dist/secrets/secret-token.d.ts +17 -0
  339. package/dist/secrets/secret-token.js +25 -0
  340. package/dist/sequence/file.d.ts +169 -0
  341. package/dist/sequence/file.js +237 -0
  342. package/dist/sequence/index.d.ts +7 -0
  343. package/dist/sequence/index.js +5 -0
  344. package/dist/sequence/load.d.ts +32 -0
  345. package/dist/sequence/load.js +56 -0
  346. package/dist/sequence/model.d.ts +115 -0
  347. package/dist/sequence/model.js +53 -0
  348. package/dist/sequence/run.d.ts +131 -0
  349. package/dist/sequence/run.js +191 -0
  350. package/dist/sequence/transfer.d.ts +24 -0
  351. package/dist/sequence/transfer.js +75 -0
  352. package/dist/server-api/audit.d.ts +455 -0
  353. package/dist/server-api/audit.js +186 -0
  354. package/dist/server-api/ci-tokens.d.ts +50 -0
  355. package/dist/server-api/ci-tokens.js +35 -0
  356. package/dist/server-api/hooks.d.ts +337 -0
  357. package/dist/server-api/hooks.js +152 -0
  358. package/dist/server-api/identity.d.ts +210 -0
  359. package/dist/server-api/identity.js +115 -0
  360. package/dist/server-api/licensing.d.ts +80 -0
  361. package/dist/server-api/licensing.js +53 -0
  362. package/dist/server-api/live.d.ts +132 -0
  363. package/dist/server-api/live.js +102 -0
  364. package/dist/server-api/meta.d.ts +31 -0
  365. package/dist/server-api/meta.js +21 -0
  366. package/dist/server-api/sync.d.ts +179 -0
  367. package/dist/server-api/sync.js +124 -0
  368. package/dist/server-api/teams.d.ts +306 -0
  369. package/dist/server-api/teams.js +113 -0
  370. package/dist/snapshot/diff.d.ts +30 -0
  371. package/dist/snapshot/diff.js +75 -0
  372. package/dist/snapshot/format.d.ts +17 -0
  373. package/dist/snapshot/format.js +40 -0
  374. package/dist/snapshot/golden-file.d.ts +45 -0
  375. package/dist/snapshot/golden-file.js +173 -0
  376. package/dist/snapshot/ignore.d.ts +14 -0
  377. package/dist/snapshot/ignore.js +79 -0
  378. package/dist/snapshot/index.d.ts +9 -0
  379. package/dist/snapshot/index.js +9 -0
  380. package/dist/snapshot/json-diff.d.ts +3 -0
  381. package/dist/snapshot/json-diff.js +93 -0
  382. package/dist/snapshot/xml-diff.d.ts +10 -0
  383. package/dist/snapshot/xml-diff.js +194 -0
  384. package/dist/soap/auth.d.ts +26 -0
  385. package/dist/soap/auth.js +54 -0
  386. package/dist/soap/curl.d.ts +20 -0
  387. package/dist/soap/curl.js +161 -0
  388. package/dist/soap/expand.d.ts +24 -0
  389. package/dist/soap/expand.js +95 -0
  390. package/dist/soap/files.d.ts +384 -0
  391. package/dist/soap/files.js +114 -0
  392. package/dist/soap/form-request.d.ts +13 -0
  393. package/dist/soap/form-request.js +2 -2
  394. package/dist/soap/generate.d.ts +12 -0
  395. package/dist/{generate.js → soap/generate.js} +4 -4
  396. package/dist/{import.d.ts → soap/import.d.ts} +4 -4
  397. package/dist/{import.js → soap/import.js} +13 -13
  398. package/dist/soap/json-operation.d.ts +35 -0
  399. package/dist/soap/json-operation.js +225 -0
  400. package/dist/soap/legacy-project/env-refs.d.ts +19 -0
  401. package/dist/soap/legacy-project/env-refs.js +105 -0
  402. package/dist/soap/legacy-project/map.d.ts +2 -2
  403. package/dist/soap/legacy-project/map.js +6 -0
  404. package/dist/soap/mime/multipart.d.ts +2 -2
  405. package/dist/soap/mime/multipart.js +2 -5
  406. package/dist/soap/mime/send-pipeline.d.ts +1 -1
  407. package/dist/soap/mime/send-pipeline.js +1 -1
  408. package/dist/soap/mime/types.d.ts +1 -3
  409. package/dist/soap/module.d.ts +2 -0
  410. package/dist/soap/module.js +13 -0
  411. package/dist/soap/namespace-scope.js +1 -1
  412. package/dist/soap/operations.d.ts +12 -0
  413. package/dist/{operations.js → soap/operations.js} +4 -4
  414. package/dist/soap/run.d.ts +70 -0
  415. package/dist/soap/run.js +549 -0
  416. package/dist/soap/script-types.d.ts +65 -0
  417. package/dist/soap/script-types.js +379 -0
  418. package/dist/soap/scripting.d.ts +45 -0
  419. package/dist/soap/scripting.js +221 -0
  420. package/dist/soap/send-input.d.ts +61 -0
  421. package/dist/{send-options.js → soap/send-input.js} +5 -109
  422. package/dist/{send.d.ts → soap/send.d.ts} +1 -1
  423. package/dist/{send.js → soap/send.js} +28 -12
  424. package/dist/soap/storage.d.ts +9 -0
  425. package/dist/soap/storage.js +237 -0
  426. package/dist/soap/transforms.d.ts +3 -12
  427. package/dist/soap/transforms.js +3 -14
  428. package/dist/{types.d.ts → soap/types.d.ts} +34 -84
  429. package/dist/sync/git-cli.d.ts +202 -0
  430. package/dist/sync/git-cli.js +684 -0
  431. package/dist/sync/three-way-merge.d.ts +41 -0
  432. package/dist/sync/three-way-merge.js +85 -0
  433. package/dist/sync/tree-paths.d.ts +34 -0
  434. package/dist/sync/tree-paths.js +131 -0
  435. package/dist/team-secrets/envelope.d.ts +10 -0
  436. package/dist/team-secrets/envelope.js +121 -0
  437. package/dist/team-secrets/errors.d.ts +26 -0
  438. package/dist/team-secrets/errors.js +27 -0
  439. package/dist/team-secrets/index.d.ts +14 -0
  440. package/dist/team-secrets/index.js +8 -0
  441. package/dist/team-secrets/keys.d.ts +52 -0
  442. package/dist/team-secrets/keys.js +158 -0
  443. package/dist/team-secrets/log.d.ts +48 -0
  444. package/dist/team-secrets/log.js +175 -0
  445. package/dist/team-secrets/resolve-conflict.d.ts +7 -0
  446. package/dist/team-secrets/resolve-conflict.js +14 -0
  447. package/dist/team-secrets/schema.d.ts +97 -0
  448. package/dist/team-secrets/schema.js +157 -0
  449. package/dist/team-secrets/sign.d.ts +24 -0
  450. package/dist/team-secrets/sign.js +71 -0
  451. package/dist/team-secrets/vault.d.ts +49 -0
  452. package/dist/team-secrets/vault.js +148 -0
  453. package/dist/validate/wsi/run-message.d.ts +2 -2
  454. package/dist/validate/wsi/run-message.js +1 -1
  455. package/dist/validate/wsi/run-wsdl.d.ts +2 -2
  456. package/dist/validate/wsi/run-wsdl.js +1 -1
  457. package/dist/validate/wsi/types.d.ts +1 -1
  458. package/dist/webhooks/model.d.ts +138 -0
  459. package/dist/webhooks/model.js +150 -0
  460. package/dist/workspace/commit-message.d.ts +1 -1
  461. package/dist/workspace/commit-message.js +6 -1
  462. package/dist/workspace/environments.d.ts +11 -4
  463. package/dist/workspace/environments.js +12 -0
  464. package/dist/workspace/index.d.ts +3 -3
  465. package/dist/workspace/index.js +2 -2
  466. package/dist/workspace/paths.d.ts +5 -2
  467. package/dist/workspace/paths.js +5 -2
  468. package/dist/workspace/schema.d.ts +7 -3
  469. package/dist/workspace/schema.js +24 -3
  470. package/dist/workspace/share.d.ts +30 -11
  471. package/dist/workspace/share.js +32 -6
  472. package/dist/ws/call.d.ts +1 -1
  473. package/dist/ws/call.js +2 -2
  474. package/dist/ws/connect.d.ts +60 -0
  475. package/dist/ws/connect.js +131 -0
  476. package/dist/ws/events.d.ts +17 -0
  477. package/dist/ws/events.js +1 -0
  478. package/dist/ws/expand.d.ts +1 -1
  479. package/dist/ws/expand.js +1 -1
  480. package/dist/ws/files.d.ts +284 -0
  481. package/dist/ws/files.js +71 -0
  482. package/dist/ws/history-entry.d.ts +44 -0
  483. package/dist/ws/history-entry.js +93 -0
  484. package/dist/ws/model.d.ts +44 -3
  485. package/dist/ws/model.js +3 -0
  486. package/dist/ws/module.d.ts +2 -0
  487. package/dist/ws/module.js +11 -0
  488. package/dist/ws/run.d.ts +63 -0
  489. package/dist/ws/run.js +470 -0
  490. package/dist/ws/session.d.ts +18 -0
  491. package/dist/ws/session.js +52 -25
  492. package/dist/ws/storage.d.ts +9 -0
  493. package/dist/ws/storage.js +176 -0
  494. package/dist/ws/transcript.d.ts +3 -4
  495. package/dist/ws/transcript.js +26 -17
  496. package/dist/ws/url.d.ts +1 -1
  497. package/dist/wsdl/cache.js +1 -1
  498. package/dist/wsdl/docs-generator.d.ts +3 -3
  499. package/dist/wsdl/docs-generator.js +1 -1
  500. package/dist/wsdl/export-definition.js +1 -1
  501. package/dist/wsdl/qname.d.ts +2 -5
  502. package/dist/wsdl/resolver.d.ts +4 -11
  503. package/dist/wsdl/resolver.js +3 -3
  504. package/dist/wsdl/update-definition.d.ts +5 -5
  505. package/dist/wsdl/update-definition.js +6 -6
  506. package/dist/wss/apply.js +1 -1
  507. package/dist/{project/wss-configs.d.ts → wss/configs.d.ts} +2 -2
  508. package/dist/{project/wss-configs.js → wss/configs.js} +2 -2
  509. package/dist/wss/incoming/decrypt.d.ts +1 -1
  510. package/dist/wss/incoming/index.js +1 -1
  511. package/dist/wss/incoming/verify.d.ts +1 -1
  512. package/dist/wss/incoming/verify.js +1 -1
  513. package/dist/wss/key-identifiers.js +1 -1
  514. package/dist/wss/model.d.ts +1 -1
  515. package/dist/wss/outgoing/encryption.d.ts +1 -1
  516. package/dist/wss/outgoing/signature.d.ts +1 -1
  517. package/dist/xml/decode.d.ts +15 -0
  518. package/dist/xml/decode.js +22 -0
  519. package/dist/xml/entitize.d.ts +17 -0
  520. package/dist/xml/entitize.js +19 -0
  521. package/dist/xml/index.d.ts +1 -1
  522. package/dist/xml/index.js +1 -1
  523. package/dist/xml/locate.d.ts +39 -0
  524. package/dist/xml/locate.js +117 -0
  525. package/dist/xml/qname.d.ts +12 -0
  526. package/dist/xml/qname.js +8 -0
  527. package/dist/xpath/evaluate-async.d.ts +19 -0
  528. package/dist/xpath/evaluate-async.js +31 -11
  529. package/dist/xpath/evaluate.d.ts +1 -1
  530. package/dist/xpath/namespaces.js +1 -1
  531. package/dist/xpath/worker.d.ts +11 -5
  532. package/dist/xpath/worker.js +24 -7
  533. package/dist/xsd/form-model.d.ts +19 -2
  534. package/dist/xsd/form-model.js +14 -2
  535. package/dist/xsd/json-bridge.d.ts +152 -0
  536. package/dist/xsd/json-bridge.js +1213 -0
  537. package/dist/xsd/locate.d.ts +2 -30
  538. package/dist/xsd/locate.js +1 -109
  539. package/dist/xsd/xml-scan.d.ts +2 -0
  540. package/dist/xsd/xml-scan.js +1 -1
  541. package/dist/xsd/xml-writer.d.ts +2 -0
  542. package/dist/xsd/xml-writer.js +4 -0
  543. package/package.json +12 -1
  544. package/dist/generate.d.ts +0 -12
  545. package/dist/operations.d.ts +0 -12
  546. package/dist/run/effective-auth.d.ts +0 -10
  547. package/dist/run/effective-auth.js +0 -19
  548. package/dist/run/prepare.d.ts +0 -43
  549. package/dist/run/prepare.js +0 -280
  550. package/dist/send-options.d.ts +0 -141
  551. package/dist/wsdl/fetch.d.ts +0 -13
  552. /package/dist/{soap → http}/charset.d.ts +0 -0
  553. /package/dist/{soap → http}/charset.js +0 -0
  554. /package/dist/{wsdl → http}/ref-policy.d.ts +0 -0
  555. /package/dist/{wsdl → http}/ref-policy.js +0 -0
  556. /package/dist/{wss/keystore → keystore}/certificate.d.ts +0 -0
  557. /package/dist/{wss/keystore → keystore}/certificate.js +0 -0
  558. /package/dist/{wss/keystore → keystore}/index.d.ts +0 -0
  559. /package/dist/{wss/keystore → keystore}/model.d.ts +0 -0
  560. /package/dist/{wss/keystore → keystore}/model.js +0 -0
  561. /package/dist/{wss/keystore → keystore}/pem.d.ts +0 -0
  562. /package/dist/{wss/keystore → keystore}/pkcs12.d.ts +0 -0
  563. /package/dist/{wsdl → project}/cache-naming.d.ts +0 -0
  564. /package/dist/{types.js → soap/types.js} +0 -0
  565. /package/dist/{soap → xml}/prefixes.d.ts +0 -0
  566. /package/dist/{soap → xml}/prefixes.js +0 -0
@@ -0,0 +1,194 @@
1
+ import { SECRET_NAME_PATTERN, secretToken } from '../secret-token.js';
2
+ /** The stored text a location is in: a URL finding's query key is left out, as the URL is one text. */
3
+ function locationKey(location) {
4
+ if (location.kind === 'rest-url' || location.kind === 'ws-url') {
5
+ return JSON.stringify({ kind: location.kind, requestId: location.requestId });
6
+ }
7
+ return JSON.stringify(location);
8
+ }
9
+ /** Rewrites stored texts; records which moves applied and the raw values they replaced. */
10
+ class Rewriter {
11
+ applied = new Map();
12
+ byKey = new Map();
13
+ constructor(moves) {
14
+ for (const move of moves) {
15
+ if (!SECRET_NAME_PATTERN.test(move.name))
16
+ continue;
17
+ const key = locationKey(move.finding.location);
18
+ const list = this.byKey.get(key) ?? [];
19
+ list.push(move);
20
+ this.byKey.set(key, list);
21
+ }
22
+ }
23
+ /** `text` with every move at `location` applied right to left; the same string when none apply. */
24
+ text(location, text) {
25
+ const moves = this.byKey.get(locationKey(location));
26
+ if (moves === undefined)
27
+ return text;
28
+ const sorted = [...moves].sort((a, b) => b.finding.valueStart - a.finding.valueStart);
29
+ let out = text;
30
+ let limit = Infinity;
31
+ for (const { finding, name } of sorted) {
32
+ const { valueStart: start, valueEnd: end, value, id } = finding;
33
+ // Compare against the original `text`: ranges index into it, and moves to the right only change `out` past `limit`.
34
+ if (this.applied.has(id) || end > limit || start >= end || text.slice(start, end) !== value)
35
+ continue;
36
+ out = out.slice(0, start) + secretToken(name) + out.slice(end);
37
+ limit = start;
38
+ this.applied.set(id, value);
39
+ }
40
+ return out;
41
+ }
42
+ }
43
+ /** `list` mapped by `fn`, or `list` itself when no element changed. */
44
+ function mapShared(list, fn) {
45
+ let changed = false;
46
+ const out = list.map((item, index) => {
47
+ const next = fn(item, index);
48
+ if (next !== item)
49
+ changed = true;
50
+ return next;
51
+ });
52
+ return changed ? out : list;
53
+ }
54
+ function patch(obj, next) {
55
+ for (const key of Object.keys(next)) {
56
+ if (next[key] !== obj[key])
57
+ return { ...obj, ...next };
58
+ }
59
+ return obj;
60
+ }
61
+ function mapTree(node, fn) {
62
+ return patch(node, {
63
+ requests: mapShared(node.requests, fn),
64
+ folders: mapShared(node.folders, (folder) => mapTree(folder, fn)),
65
+ });
66
+ }
67
+ function keyedEntries(rw, kind, owner, entries) {
68
+ return mapShared(entries, (entry, index) => {
69
+ const location = { kind, ...owner, name: entry.name, index };
70
+ return patch(entry, { value: rw.text(location, entry.value) });
71
+ });
72
+ }
73
+ function properties(rw, props, location) {
74
+ let out;
75
+ for (const [name, value] of Object.entries(props)) {
76
+ const next = rw.text(location(name), value);
77
+ if (next !== value)
78
+ (out ??= { ...props })[name] = next;
79
+ }
80
+ return out ?? props;
81
+ }
82
+ function restBody(rw, requestId, body) {
83
+ if (body.kind === 'raw') {
84
+ return patch(body, { text: rw.text({ kind: 'rest-body', requestId }, body.text) });
85
+ }
86
+ // Built in the key order `walk.ts` uses: the location's JSON is the rewriter's key.
87
+ const field = (entry, index) => 'value' in entry
88
+ ? patch(entry, {
89
+ value: rw.text({ kind: 'rest-body', requestId, field: index, name: entry.name }, entry.value),
90
+ })
91
+ : entry;
92
+ if (body.kind === 'form')
93
+ return patch(body, { fields: mapShared(body.fields, field) });
94
+ if (body.kind === 'multipart')
95
+ return patch(body, { parts: mapShared(body.parts, field) });
96
+ return body;
97
+ }
98
+ function restRequest(rw, request) {
99
+ const requestId = request.id;
100
+ return patch(request, {
101
+ url: rw.text({ kind: 'rest-url', requestId }, request.url),
102
+ query: keyedEntries(rw, 'rest-query', { requestId }, request.query),
103
+ headers: keyedEntries(rw, 'rest-header', { requestId }, request.headers),
104
+ body: restBody(rw, requestId, request.body),
105
+ });
106
+ }
107
+ /**
108
+ * Replace each move's finding range with `${secret:name}`. Moves in the same text apply right to
109
+ * left, so earlier ranges stay valid; a REST or WS URL and its query table are separate texts. A move
110
+ * whose text no longer holds `finding.value` at `[valueStart, valueEnd)` is skipped and listed in
111
+ * `stale`. See {@link SecretMovesResult.values} for the value main stores.
112
+ */
113
+ export function applySecretMoves(project, moves) {
114
+ const rw = new Rewriter(moves);
115
+ const next = patch(project, {
116
+ properties: properties(rw, project.properties, (name) => ({ kind: 'project-property', name })),
117
+ environments: mapShared(project.environments, (env) => patch(env, {
118
+ properties: properties(rw, env.properties, (name) => ({
119
+ kind: 'env-property',
120
+ environmentId: env.id,
121
+ name,
122
+ })),
123
+ })),
124
+ interfaces: mapShared(project.interfaces, (iface) => patch(iface, {
125
+ operations: mapShared(iface.operations, (operation) => patch(operation, {
126
+ requests: mapShared(operation.requests, (request) => patch(request, {
127
+ headers: keyedEntries(rw, 'soap-header', { requestId: request.id }, request.headers),
128
+ envelopeXml: rw.text({ kind: 'soap-body', requestId: request.id }, request.envelopeXml),
129
+ })),
130
+ })),
131
+ })),
132
+ apis: mapShared(project.apis, (api) => mapTree(api, (request) => restRequest(rw, request))),
133
+ ...(project.webhooks === undefined
134
+ ? {}
135
+ : { webhooks: mapTree(project.webhooks, (request) => restRequest(rw, request)) }),
136
+ grpcApis: mapShared(project.grpcApis, (api) => mapTree(patch(api, { metadata: keyedEntries(rw, 'grpc-api-metadata', { apiId: api.id }, api.metadata) }), (request) => patch(request, {
137
+ metadata: keyedEntries(rw, 'grpc-metadata', { requestId: request.id }, request.metadata),
138
+ message: rw.text({ kind: 'grpc-message', requestId: request.id }, request.message),
139
+ }))),
140
+ wsApis: mapShared(project.wsApis, (api) => mapTree(patch(api, { headers: keyedEntries(rw, 'ws-api-header', { apiId: api.id }, api.headers) }), (request) => patch(request, {
141
+ url: rw.text({ kind: 'ws-url', requestId: request.id }, request.url),
142
+ query: keyedEntries(rw, 'ws-query', { requestId: request.id }, request.query),
143
+ headers: keyedEntries(rw, 'ws-header', { requestId: request.id }, request.headers),
144
+ messages: mapShared(request.messages, (message) => message.format !== 'text'
145
+ ? message
146
+ : patch(message, {
147
+ content: rw.text({ kind: 'ws-message', requestId: request.id, messageId: message.id }, message.content),
148
+ })),
149
+ }))),
150
+ });
151
+ const stale = moves.map((m) => m.finding.id).filter((id) => !rw.applied.has(id));
152
+ return { project: next, stale: [...new Set(stale)], values: Object.fromEntries(rw.applied) };
153
+ }
154
+ const RULE_NAMES = {
155
+ 'sensitive-name': 'secret',
156
+ jwt: 'jwt',
157
+ bearer: 'bearer_token',
158
+ basic: 'basic_auth',
159
+ 'url-credentials': 'url_password',
160
+ 'aws-key': 'aws_access_key',
161
+ 'private-key': 'private_key',
162
+ 'vendor-token': 'api_token',
163
+ 'high-entropy': 'secret',
164
+ };
165
+ function sanitize(raw) {
166
+ const name = raw
167
+ .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
168
+ .toLowerCase()
169
+ .replace(/[^a-z0-9_]+/g, '_')
170
+ .replace(/_+/g, '_')
171
+ .replace(/^_|_$/g, '');
172
+ if (name === '')
173
+ return '';
174
+ return /^[0-9]/.test(name) ? `_${name}` : name;
175
+ }
176
+ /**
177
+ * The name offered for `finding`: its header, query (in the table or the URL), metadata, field or
178
+ * property name, sanitised to `snake_case` (else a name from its rule: `url_password` for a URL's
179
+ * password), made unique against `taken` ignoring case with a `_2`, `_3`, … suffix. Always matches
180
+ * `SECRET_NAME_PATTERN`.
181
+ */
182
+ export function proposeSecretName(finding, taken) {
183
+ const { location } = finding;
184
+ const source = 'name' in location ? (location.name ?? '') : '';
185
+ const base = sanitize(source) || RULE_NAMES[finding.rule];
186
+ const lower = new Set([...taken].map((t) => t.toLowerCase()));
187
+ if (!lower.has(base))
188
+ return base;
189
+ for (let n = 2;; n++) {
190
+ const candidate = `${base}_${n}`;
191
+ if (!lower.has(candidate))
192
+ return candidate;
193
+ }
194
+ }
@@ -0,0 +1,32 @@
1
+ export type SecretRule = 'sensitive-name' | 'jwt' | 'bearer' | 'basic' | 'url-credentials' | 'aws-key' | 'private-key' | 'vendor-token' | 'high-entropy';
2
+ /** One detected credential: its rule and its `[start, end)` range in the scanned text. */
3
+ export interface SecretMatch {
4
+ readonly rule: SecretRule;
5
+ readonly start: number;
6
+ readonly end: number;
7
+ /** The decoded key of the form pair (a URL's query parameter) the value was found under. */
8
+ readonly name?: string;
9
+ }
10
+ export interface DetectContext {
11
+ /** The body's content type; `application/x-www-form-urlencoded` turns on form-pair detection. */
12
+ readonly contentType?: string;
13
+ /**
14
+ * The name the text is stored under (a header, query parameter, form field or property). When
15
+ * given, the text is one value: a non-empty value under a sensitive name is a finding as a whole.
16
+ */
17
+ readonly fieldName?: string;
18
+ /** Which name list {@link fieldName} is checked against; every list when absent. */
19
+ readonly nameKind?: 'header' | 'query' | 'field' | 'property';
20
+ }
21
+ /** Text beyond this many characters is not scanned (spec: bodies over 1 MiB, first 1 MiB only). */
22
+ export declare const SECRET_TEXT_SCAN_LIMIT: number;
23
+ /** Shannon entropy of `text`, in bits per character. */
24
+ export declare function shannonEntropy(text: string): number;
25
+ /**
26
+ * Every credential in `text`. With `context.fieldName` the text is one stored value (a header, a
27
+ * query parameter, a form field, a property); without it, a body, where JSON keys (a string or a
28
+ * number value), form fields and XML element local names (text content, or one CDATA section) in
29
+ * `SECRET_BODY_KEYS` count as sensitive names. Only the first
30
+ * {@link SECRET_TEXT_SCAN_LIMIT} characters are scanned.
31
+ */
32
+ export declare function detectInText(text: string, context?: DetectContext): SecretMatch[];
@@ -0,0 +1,346 @@
1
+ /**
2
+ * Detection rules for the secret scanner (docs/specs/2026-09-22-secret-scanning-design.md §7):
3
+ * a value under a sensitive name, and credential shapes that are recognisable anywhere. Every match
4
+ * is the credential part only (`Bearer eyJ…` matches `eyJ…`), so replacing `[start, end)` with a
5
+ * `${secret:name}` token leaves the rest of the text as it was. A match that touches a `${…}`
6
+ * expansion is dropped: that value is already a reference, not a secret.
7
+ *
8
+ * `url-credentials` (the password of a `scheme://user:password@host` URL) is a shape rule, like
9
+ * `basic`: the credential is recognisable by where it sits in the text, not by a name it is stored
10
+ * under, so it applies to every scanned text — a URL, a property holding a connection string, a
11
+ * body.
12
+ *
13
+ * Pure module: no I/O.
14
+ */
15
+ import { isSensitiveHeaderName, isSensitiveQueryParam, SECRET_BODY_KEYS } from '../../redact/index.js';
16
+ /** Text beyond this many characters is not scanned (spec: bodies over 1 MiB, first 1 MiB only). */
17
+ export const SECRET_TEXT_SCAN_LIMIT = 1024 * 1024;
18
+ const BODY_KEY_SET = new Set(SECRET_BODY_KEYS);
19
+ /** Name parts that make a name credential-like on their own (`dbPassword`, `client_secret`). */
20
+ const CREDENTIAL_PARTS = new Set(['secret', 'token', 'password', 'passwd', 'credential', 'credentials']);
21
+ /** Parts that make a following `key` credential-like (`apiKey`, `secret_key`); `cacheKey` is not. */
22
+ const KEY_QUALIFIERS = new Set(['api', 'secret', 'access', 'private', 'signing', 'client', 'auth', 'master']);
23
+ const HIGH_ENTROPY_MIN_LENGTH = 20;
24
+ const HIGH_ENTROPY_MIN_BITS = 3.5;
25
+ /** When two matches overlap, the one whose rule comes first here wins. */
26
+ const RULE_PRIORITY = [
27
+ 'private-key',
28
+ 'jwt',
29
+ 'vendor-token',
30
+ 'aws-key',
31
+ 'bearer',
32
+ 'basic',
33
+ 'url-credentials',
34
+ 'sensitive-name',
35
+ 'high-entropy',
36
+ ];
37
+ const PEM_RE = /-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY-----[\s\S]*?-----END (?:[A-Z0-9]+ )*PRIVATE KEY-----/g;
38
+ const JWT_RE = /(?<![A-Za-z0-9_-])eyJ[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{5,}/g;
39
+ const VENDOR_RE = /(?<![A-Za-z0-9_])(?:gh[pos]_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,}|xox[abpr]-[A-Za-z0-9-]{10,})/g;
40
+ const AWS_RE = /(?<![A-Za-z0-9])(?:AKIA|ASIA)[A-Z0-9]{16}(?![A-Za-z0-9])/g;
41
+ const BEARER_RE = /\bBearer[ \t]+([A-Za-z0-9._~+/-]+=*)/g;
42
+ const BASIC_RE = /\bBasic[ \t]+([A-Za-z0-9+/]{8,}={0,2})(?![A-Za-z0-9+/=])/g;
43
+ /**
44
+ * `scheme://user:password@`: group 1 is the password, which may be empty-user (`redis://:pw@`).
45
+ * Neither part crosses a delimiter, whitespace or a quote, so `host:8080/a@b` is a port, not a pair.
46
+ * The user may hold whole `${…}` expansions (`${env:user}`), colons inside them included. The
47
+ * scheme starts only where a run of scheme-like characters does (not after a letter, digit, `_`,
48
+ * `+`, `.` or `-`), so a long `a-a-a…` run is walked once, not once per letter.
49
+ */
50
+ const URL_CREDENTIALS_RE = /(?<![\w+.-])[A-Za-z][A-Za-z0-9+.-]*:\/\/(?:\$\{[^{}]*\}|\$(?!\{)|[^\s/?#@:"'<>$])*:([^\s/?#@"'<>]+)(?=@)/g;
51
+ /** URL passwords that stand for one rather than being one (`user:password@`, `u:****@`). */
52
+ const PLACEHOLDER_PASSWORDS = new Set(['password', 'pass', 'passwd', 'secret']);
53
+ /** A scheme prefix on a sensitive header's value, left in place when the credential is replaced. */
54
+ const SCHEME_PREFIX_RE = /^(?:Bearer|Basic|Token|Digest|Negotiate|NTLM)[ \t]+/i;
55
+ /**
56
+ * A key and its string value (group 2) or number value (group 3); `true`, `false`, `null` and the
57
+ * other kinds are not matched. The lookahead refuses a number that runs on (`12ab`).
58
+ */
59
+ const JSON_PAIR_RE = /"((?:[^"\\\n]|\\.){1,128})"\s*:\s*(?:"((?:[^"\\]|\\.)*)"|(-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)(?![\w.+-]))/g;
60
+ const XML_ELEMENT_RE = /<((?:[\w.-]+:)?([\w.-]+))((?:\s[^<>]*)?)>([^<]*)<\/\1>/g;
61
+ /** An element's start tag, matched where it starts (sticky). */
62
+ const XML_START_TAG_RE = /<((?:[\w.-]+:)?([\w.-]+))((?:\s[^<>]*)?)>/y;
63
+ const CDATA_OPEN = '<![CDATA[';
64
+ const CDATA_CLOSE = ']]>';
65
+ const FORM_PAIR_RE = /(?:^|[&?])([^=&?#]+)=([^&#]*)/g;
66
+ /** Shannon entropy of `text`, in bits per character. */
67
+ export function shannonEntropy(text) {
68
+ if (text.length === 0)
69
+ return 0;
70
+ const counts = new Map();
71
+ for (const ch of text)
72
+ counts.set(ch, (counts.get(ch) ?? 0) + 1);
73
+ let bits = 0;
74
+ for (const count of counts.values()) {
75
+ const p = count / text.length;
76
+ bits -= p * Math.log2(p);
77
+ }
78
+ return bits;
79
+ }
80
+ /** `[start, end)` of every `${…}` expansion in `text`, nested braces included. */
81
+ function expansionRanges(text) {
82
+ const ranges = [];
83
+ let from = 0;
84
+ for (;;) {
85
+ const open = text.indexOf('${', from);
86
+ if (open < 0)
87
+ return ranges;
88
+ let depth = 0;
89
+ let end = -1;
90
+ for (let i = open + 1; i < text.length; i++) {
91
+ const ch = text.charCodeAt(i);
92
+ if (ch === 123 /* { */)
93
+ depth++;
94
+ else if (ch === 125 /* } */ && --depth === 0) {
95
+ end = i + 1;
96
+ break;
97
+ }
98
+ }
99
+ if (end < 0)
100
+ return ranges;
101
+ ranges.push([open, end]);
102
+ from = end;
103
+ }
104
+ }
105
+ /** `dbPassword`, `api_key`, `X-Client-Secret` → lower-case word parts. */
106
+ function nameParts(name) {
107
+ return name
108
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
109
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
110
+ .toLowerCase()
111
+ .split(/[^a-z0-9]+/)
112
+ .filter((part) => part !== '');
113
+ }
114
+ /** True when a word part of `name` says it holds a credential. */
115
+ function isCredentialLikeName(name) {
116
+ const parts = nameParts(name);
117
+ return parts.some((part, i) => CREDENTIAL_PARTS.has(part) || (part === 'key' && i > 0 && KEY_QUALIFIERS.has(parts[i - 1])));
118
+ }
119
+ function isSensitiveName(name, kind) {
120
+ const lower = name.trim().toLowerCase();
121
+ if (kind === 'property')
122
+ return BODY_KEY_SET.has(lower) || isCredentialLikeName(name);
123
+ if (kind === 'header')
124
+ return isSensitiveHeaderName(lower) || BODY_KEY_SET.has(lower);
125
+ if (kind === 'query')
126
+ return isSensitiveQueryParam(lower) || BODY_KEY_SET.has(lower);
127
+ if (kind === 'field')
128
+ return BODY_KEY_SET.has(lower);
129
+ return isSensitiveHeaderName(lower) || isSensitiveQueryParam(lower) || BODY_KEY_SET.has(lower);
130
+ }
131
+ function basicLooksReal(encoded) {
132
+ try {
133
+ return Buffer.from(encoded, 'base64').toString('latin1').includes(':');
134
+ }
135
+ catch {
136
+ return false;
137
+ }
138
+ }
139
+ function isPlaceholderPassword(password) {
140
+ return /^(?:\*+|[xX]+)$/.test(password) || PLACEHOLDER_PASSWORDS.has(password.toLowerCase());
141
+ }
142
+ function pushAll(out, re, text, rule, group) {
143
+ re.lastIndex = 0;
144
+ for (let m = re.exec(text); m !== null; m = re.exec(text)) {
145
+ if (group === undefined) {
146
+ out.push({ rule, start: m.index, end: m.index + m[0].length });
147
+ continue;
148
+ }
149
+ const part = m[group];
150
+ const start = m.index + m[0].length - part.length;
151
+ // A token-shaped run of 16+ characters; words joined by `/` ("Bearer tokens/credentials") are prose.
152
+ if (rule === 'bearer' && (part.length < 16 || /^[A-Za-z]+(?:\/[A-Za-z]+)+$/.test(part)))
153
+ continue;
154
+ if (rule === 'basic' && !basicLooksReal(part))
155
+ continue;
156
+ if (rule === 'url-credentials' && isPlaceholderPassword(part))
157
+ continue;
158
+ out.push({ rule, start, end: start + part.length });
159
+ }
160
+ }
161
+ function shapeMatches(text) {
162
+ const out = [];
163
+ if (text.includes('-----BEGIN'))
164
+ pushAll(out, PEM_RE, text, 'private-key');
165
+ if (text.includes('eyJ'))
166
+ pushAll(out, JWT_RE, text, 'jwt');
167
+ if (text.includes('gh') || text.includes('github_pat_') || text.includes('xox'))
168
+ pushAll(out, VENDOR_RE, text, 'vendor-token');
169
+ if (text.includes('AKIA') || text.includes('ASIA'))
170
+ pushAll(out, AWS_RE, text, 'aws-key');
171
+ if (text.includes('Bearer'))
172
+ pushAll(out, BEARER_RE, text, 'bearer', 1);
173
+ if (text.includes('Basic'))
174
+ pushAll(out, BASIC_RE, text, 'basic', 1);
175
+ if (text.includes('://'))
176
+ pushAll(out, URL_CREDENTIALS_RE, text, 'url-credentials', 1);
177
+ return out;
178
+ }
179
+ /**
180
+ * The name-based match for one value stored under `name`, offset by `offset` in the scanned text:
181
+ * `sensitive-name` for a listed name (whatever the value looks like), else `high-entropy` for a
182
+ * long, random-looking value under a name that merely sounds secret.
183
+ */
184
+ function nameMatch(value, offset, name, kind) {
185
+ let start = 0;
186
+ let end = value.length;
187
+ while (start < end && /\s/.test(value[start]))
188
+ start++;
189
+ while (end > start && /\s/.test(value[end - 1]))
190
+ end--;
191
+ const scheme = SCHEME_PREFIX_RE.exec(value.slice(start, end));
192
+ if (scheme !== null && kind !== 'field' && kind !== 'property')
193
+ start += scheme[0].length;
194
+ if (start >= end)
195
+ return undefined;
196
+ if (isSensitiveName(name, kind))
197
+ return { rule: 'sensitive-name', start: offset + start, end: offset + end };
198
+ const core = value.slice(start, end);
199
+ if (isCredentialLikeName(name) &&
200
+ !/\s/.test(core) &&
201
+ !/^(?:[a-z][a-z0-9+.-]*:\/\/|[~.]?[/\\]|[A-Za-z]:[/\\])/i.test(core) &&
202
+ core.length >= HIGH_ENTROPY_MIN_LENGTH &&
203
+ shannonEntropy(core) >= HIGH_ENTROPY_MIN_BITS) {
204
+ return { rule: 'high-entropy', start: offset + start, end: offset + end };
205
+ }
206
+ return undefined;
207
+ }
208
+ function isWhitespaceCode(code) {
209
+ return code === 32 || code === 9 || code === 10 || code === 13;
210
+ }
211
+ /**
212
+ * Name matches for elements whose whole content is one CDATA section, whitespace around it
213
+ * allowed (`<Password><![CDATA[x]]></Password>`); the range is inside the section, so a Move keeps
214
+ * the wrapper. Each section is found with `indexOf`, and the start tag before it by walking back
215
+ * over whitespace to one `<…>`, so unclosed sections cannot make this quadratic.
216
+ */
217
+ function cdataMatches(text, add) {
218
+ let from = 0;
219
+ for (;;) {
220
+ const open = text.indexOf(CDATA_OPEN, from);
221
+ if (open < 0)
222
+ return;
223
+ const valueStart = open + CDATA_OPEN.length;
224
+ const close = text.indexOf(CDATA_CLOSE, valueStart);
225
+ if (close < 0)
226
+ return;
227
+ from = close + CDATA_CLOSE.length;
228
+ let gt = open - 1;
229
+ while (gt >= 0 && isWhitespaceCode(text.charCodeAt(gt)))
230
+ gt--;
231
+ if (gt < 0 || text[gt] !== '>')
232
+ continue;
233
+ const lt = text.lastIndexOf('<', gt);
234
+ if (lt < 0)
235
+ continue;
236
+ XML_START_TAG_RE.lastIndex = lt;
237
+ const tag = XML_START_TAG_RE.exec(text);
238
+ if (tag === null || lt + tag[0].length !== gt + 1)
239
+ continue;
240
+ if (/PasswordDigest/.test(tag[3]))
241
+ continue;
242
+ let after = from;
243
+ while (after < text.length && isWhitespaceCode(text.charCodeAt(after)))
244
+ after++;
245
+ if (!text.startsWith(`</${tag[1]}>`, after))
246
+ continue;
247
+ add(nameMatch(text.slice(valueStart, close), valueStart, tag[2], 'field'));
248
+ }
249
+ }
250
+ function isFormType(contentType) {
251
+ return (contentType ?? '').split(';')[0].trim().toLowerCase() === 'application/x-www-form-urlencoded';
252
+ }
253
+ function structuredMatches(text, contentType, formNames) {
254
+ const out = [];
255
+ const add = (m) => {
256
+ if (m !== undefined)
257
+ out.push(m);
258
+ };
259
+ if (text.includes('":')) {
260
+ JSON_PAIR_RE.lastIndex = 0;
261
+ for (let m = JSON_PAIR_RE.exec(text); m !== null; m = JSON_PAIR_RE.exec(text)) {
262
+ const value = m[2];
263
+ if (value !== undefined) {
264
+ add(nameMatch(value, m.index + m[0].length - 1 - value.length, m[1], 'field'));
265
+ }
266
+ else if (isSensitiveName(m[1], 'field')) {
267
+ // A number under a sensitive key is a credential too (`"password": 123456`); its range is
268
+ // the digits alone, so a Move leaves a bare token that expands back to the same number.
269
+ // Digits alone never reach the high-entropy threshold, so only the name is checked.
270
+ const number = m[3];
271
+ add({ rule: 'sensitive-name', start: m.index + m[0].length - number.length, end: m.index + m[0].length });
272
+ }
273
+ }
274
+ }
275
+ if (text.includes('</')) {
276
+ XML_ELEMENT_RE.lastIndex = 0;
277
+ for (let m = XML_ELEMENT_RE.exec(text); m !== null; m = XML_ELEMENT_RE.exec(text)) {
278
+ // A WS-Security digest is a hash of the password, not the password; redaction keeps it too.
279
+ if (/PasswordDigest/.test(m[3]))
280
+ continue;
281
+ const value = m[4];
282
+ const closeLength = m[1].length + 3;
283
+ add(nameMatch(value, m.index + m[0].length - closeLength - value.length, m[2], 'field'));
284
+ }
285
+ if (text.includes(CDATA_OPEN))
286
+ cdataMatches(text, add);
287
+ }
288
+ if (isFormType(contentType)) {
289
+ FORM_PAIR_RE.lastIndex = 0;
290
+ for (let m = FORM_PAIR_RE.exec(text); m !== null; m = FORM_PAIR_RE.exec(text)) {
291
+ let key = m[1];
292
+ try {
293
+ key = decodeURIComponent(key.replace(/\+/g, ' '));
294
+ }
295
+ catch {
296
+ // keep the raw key
297
+ }
298
+ const value = m[2];
299
+ const match = nameMatch(value, m.index + m[0].length - value.length, key, formNames);
300
+ if (match !== undefined)
301
+ out.push({ ...match, name: key });
302
+ }
303
+ }
304
+ return out;
305
+ }
306
+ /** Drops matches inside or across an expansion, then overlaps, keeping the higher-priority rule. */
307
+ function settle(matches, text) {
308
+ if (matches.length === 0)
309
+ return matches;
310
+ const expansions = text.includes('${') ? expansionRanges(text) : [];
311
+ const live = matches.filter((m) => m.end > m.start && !expansions.some(([s, e]) => m.start < e && s < m.end));
312
+ live.sort((a, b) => a.start - b.start || RULE_PRIORITY.indexOf(a.rule) - RULE_PRIORITY.indexOf(b.rule) || b.end - a.end);
313
+ const kept = [];
314
+ for (const m of live) {
315
+ const clash = kept.findIndex((k) => m.start < k.end && k.start < m.end);
316
+ if (clash < 0) {
317
+ kept.push(m);
318
+ }
319
+ else if (RULE_PRIORITY.indexOf(m.rule) < RULE_PRIORITY.indexOf(kept[clash].rule)) {
320
+ kept[clash] = m;
321
+ }
322
+ }
323
+ return kept.sort((a, b) => a.start - b.start);
324
+ }
325
+ /**
326
+ * Every credential in `text`. With `context.fieldName` the text is one stored value (a header, a
327
+ * query parameter, a form field, a property); without it, a body, where JSON keys (a string or a
328
+ * number value), form fields and XML element local names (text content, or one CDATA section) in
329
+ * `SECRET_BODY_KEYS` count as sensitive names. Only the first
330
+ * {@link SECRET_TEXT_SCAN_LIMIT} characters are scanned.
331
+ */
332
+ export function detectInText(text, context = {}) {
333
+ const scanned = text.length > SECRET_TEXT_SCAN_LIMIT ? text.slice(0, SECRET_TEXT_SCAN_LIMIT) : text;
334
+ if (scanned.length === 0)
335
+ return [];
336
+ const matches = shapeMatches(scanned);
337
+ if (context.fieldName !== undefined) {
338
+ const named = nameMatch(scanned, 0, context.fieldName, context.nameKind);
339
+ if (named !== undefined && matches.length === 0)
340
+ matches.push(named);
341
+ }
342
+ else {
343
+ matches.push(...structuredMatches(scanned, context.contentType, context.nameKind ?? 'field'));
344
+ }
345
+ return settle(matches, scanned);
346
+ }
@@ -0,0 +1,6 @@
1
+ import type { Project } from '../../project/model.js';
2
+ import type { SecretFinding } from './walk.js';
3
+ /** Every finding in `project`. The same project always yields the same findings with the same ids. */
4
+ export declare function scanProjectForSecrets(project: Project): SecretFinding[];
5
+ /** The only form of a value that leaves main: its first 3 characters, `…`, and its length. */
6
+ export declare function maskedPreview(value: string): string;
@@ -0,0 +1,57 @@
1
+ /**
2
+ * `scanProjectForSecrets`: every credential stored in plain text in a project, for the review
3
+ * dialog shown before a manual save or Sync commit (docs/specs/2026-09-22-secret-scanning-design.md).
4
+ *
5
+ * Pure module: no I/O.
6
+ */
7
+ import { createHash } from 'node:crypto';
8
+ import { detectInText } from './rules.js';
9
+ import { scanTargets } from './walk.js';
10
+ /**
11
+ * The id of the `occurrence`-th (from 0) match of `value` in the text at `location`. Counting equal
12
+ * values rather than taking the offset keeps an id when text before it is edited. The value goes
13
+ * last, after two NUL-ended fields (a location's JSON holds no raw NUL), so no value can be read as
14
+ * another value's occurrence.
15
+ */
16
+ function findingId(location, value, occurrence) {
17
+ return createHash('sha256')
18
+ .update(JSON.stringify(location))
19
+ .update('\0')
20
+ .update(String(occurrence))
21
+ .update('\0')
22
+ .update(value)
23
+ .digest('hex')
24
+ .slice(0, 16);
25
+ }
26
+ /** Every finding in `project`. The same project always yields the same findings with the same ids. */
27
+ export function scanProjectForSecrets(project) {
28
+ const findings = [];
29
+ for (const target of scanTargets(project)) {
30
+ const matches = detectInText(target.text, target.context);
31
+ const seen = new Map();
32
+ const { location } = target;
33
+ const inUrl = location.kind === 'rest-url' || location.kind === 'ws-url';
34
+ for (const match of matches) {
35
+ const value = target.text.slice(match.start, match.end);
36
+ const occurrence = seen.get(value) ?? 0;
37
+ seen.set(value, occurrence + 1);
38
+ // A URL finding under a query parameter carries its key, for the name the dialog proposes.
39
+ const at = inUrl && match.name !== undefined ? { ...location, name: match.name } : location;
40
+ findings.push({
41
+ id: findingId(at, value, occurrence),
42
+ location: at,
43
+ rule: match.rule,
44
+ label: target.label,
45
+ valueStart: match.start,
46
+ valueEnd: match.end,
47
+ value,
48
+ });
49
+ }
50
+ }
51
+ return findings;
52
+ }
53
+ /** The only form of a value that leaves main: its first 3 characters, `…`, and its length. */
54
+ export function maskedPreview(value) {
55
+ const shown = value.length > 6 ? value.slice(0, 3) : '';
56
+ return `${shown}… (${value.length} chars)`;
57
+ }