@yolk-sdk/emulators 0.1.0-canary.96

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 (333) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2135 -0
  3. package/dist/anthropic.d.mts +269 -0
  4. package/dist/anthropic.d.mts.map +1 -0
  5. package/dist/anthropic.mjs +177 -0
  6. package/dist/anthropic.mjs.map +1 -0
  7. package/dist/chat-completions.d.mts +288 -0
  8. package/dist/chat-completions.d.mts.map +1 -0
  9. package/dist/chat-completions.mjs +451 -0
  10. package/dist/chat-completions.mjs.map +1 -0
  11. package/dist/codex.d.mts +265 -0
  12. package/dist/codex.d.mts.map +1 -0
  13. package/dist/codex.mjs +199 -0
  14. package/dist/codex.mjs.map +1 -0
  15. package/dist/dropbox/api.d.mts +63 -0
  16. package/dist/dropbox/api.d.mts.map +1 -0
  17. package/dist/dropbox/api.mjs +565 -0
  18. package/dist/dropbox/api.mjs.map +1 -0
  19. package/dist/dropbox/state.d.mts +136 -0
  20. package/dist/dropbox/state.d.mts.map +1 -0
  21. package/dist/dropbox/state.mjs +209 -0
  22. package/dist/dropbox/state.mjs.map +1 -0
  23. package/dist/dropbox.d.mts +79 -0
  24. package/dist/dropbox.d.mts.map +1 -0
  25. package/dist/dropbox.mjs +124 -0
  26. package/dist/dropbox.mjs.map +1 -0
  27. package/dist/email-fixtures.d.mts +36 -0
  28. package/dist/email-fixtures.d.mts.map +1 -0
  29. package/dist/email-fixtures.mjs +1080 -0
  30. package/dist/email-fixtures.mjs.map +1 -0
  31. package/dist/email.d.mts +160 -0
  32. package/dist/email.d.mts.map +1 -0
  33. package/dist/email.mjs +608 -0
  34. package/dist/email.mjs.map +1 -0
  35. package/dist/emulator-compose.d.mts +40 -0
  36. package/dist/emulator-compose.d.mts.map +1 -0
  37. package/dist/emulator-compose.mjs +91 -0
  38. package/dist/emulator-compose.mjs.map +1 -0
  39. package/dist/emulator-http.d.mts +53 -0
  40. package/dist/emulator-http.d.mts.map +1 -0
  41. package/dist/emulator-http.mjs +117 -0
  42. package/dist/emulator-http.mjs.map +1 -0
  43. package/dist/emulator-kernel.d.mts +176 -0
  44. package/dist/emulator-kernel.d.mts.map +1 -0
  45. package/dist/emulator-kernel.mjs +413 -0
  46. package/dist/emulator-kernel.mjs.map +1 -0
  47. package/dist/fixture-route.d.mts +129 -0
  48. package/dist/fixture-route.d.mts.map +1 -0
  49. package/dist/fixture-route.mjs +257 -0
  50. package/dist/fixture-route.mjs.map +1 -0
  51. package/dist/fortnox/api.d.mts +92 -0
  52. package/dist/fortnox/api.d.mts.map +1 -0
  53. package/dist/fortnox/api.mjs +750 -0
  54. package/dist/fortnox/api.mjs.map +1 -0
  55. package/dist/fortnox/state.d.mts +533 -0
  56. package/dist/fortnox/state.d.mts.map +1 -0
  57. package/dist/fortnox/state.mjs +612 -0
  58. package/dist/fortnox/state.mjs.map +1 -0
  59. package/dist/fortnox.d.mts +128 -0
  60. package/dist/fortnox.d.mts.map +1 -0
  61. package/dist/fortnox.mjs +403 -0
  62. package/dist/fortnox.mjs.map +1 -0
  63. package/dist/gateway-evaluate-recordings.d.mts +12 -0
  64. package/dist/gateway-evaluate-recordings.d.mts.map +1 -0
  65. package/dist/gateway-evaluate-recordings.mjs +128 -0
  66. package/dist/gateway-evaluate-recordings.mjs.map +1 -0
  67. package/dist/gateway.d.mts +272 -0
  68. package/dist/gateway.d.mts.map +1 -0
  69. package/dist/gateway.mjs +400 -0
  70. package/dist/gateway.mjs.map +1 -0
  71. package/dist/github/api.d.mts +63 -0
  72. package/dist/github/api.d.mts.map +1 -0
  73. package/dist/github/api.mjs +527 -0
  74. package/dist/github/api.mjs.map +1 -0
  75. package/dist/github/state.d.mts +272 -0
  76. package/dist/github/state.d.mts.map +1 -0
  77. package/dist/github/state.mjs +303 -0
  78. package/dist/github/state.mjs.map +1 -0
  79. package/dist/github.d.mts +77 -0
  80. package/dist/github.d.mts.map +1 -0
  81. package/dist/github.mjs +180 -0
  82. package/dist/github.mjs.map +1 -0
  83. package/dist/google/calendar.d.mts +10 -0
  84. package/dist/google/calendar.d.mts.map +1 -0
  85. package/dist/google/calendar.mjs +252 -0
  86. package/dist/google/calendar.mjs.map +1 -0
  87. package/dist/google/drive.d.mts +8 -0
  88. package/dist/google/drive.d.mts.map +1 -0
  89. package/dist/google/drive.mjs +252 -0
  90. package/dist/google/drive.mjs.map +1 -0
  91. package/dist/google/gmail.d.mts +10 -0
  92. package/dist/google/gmail.d.mts.map +1 -0
  93. package/dist/google/gmail.mjs +591 -0
  94. package/dist/google/gmail.mjs.map +1 -0
  95. package/dist/google/shared.d.mts +107 -0
  96. package/dist/google/shared.d.mts.map +1 -0
  97. package/dist/google/shared.mjs +146 -0
  98. package/dist/google/shared.mjs.map +1 -0
  99. package/dist/google/state.d.mts +385 -0
  100. package/dist/google/state.d.mts.map +1 -0
  101. package/dist/google/state.mjs +496 -0
  102. package/dist/google/state.mjs.map +1 -0
  103. package/dist/google.d.mts +75 -0
  104. package/dist/google.d.mts.map +1 -0
  105. package/dist/google.mjs +210 -0
  106. package/dist/google.mjs.map +1 -0
  107. package/dist/linkedin-search/api.d.mts +45 -0
  108. package/dist/linkedin-search/api.d.mts.map +1 -0
  109. package/dist/linkedin-search/api.mjs +181 -0
  110. package/dist/linkedin-search/api.mjs.map +1 -0
  111. package/dist/linkedin-search/state.d.mts +123 -0
  112. package/dist/linkedin-search/state.d.mts.map +1 -0
  113. package/dist/linkedin-search/state.mjs +261 -0
  114. package/dist/linkedin-search/state.mjs.map +1 -0
  115. package/dist/linkedin-search.d.mts +69 -0
  116. package/dist/linkedin-search.d.mts.map +1 -0
  117. package/dist/linkedin-search.mjs +158 -0
  118. package/dist/linkedin-search.mjs.map +1 -0
  119. package/dist/mcp/api.d.mts +44 -0
  120. package/dist/mcp/api.d.mts.map +1 -0
  121. package/dist/mcp/api.mjs +557 -0
  122. package/dist/mcp/api.mjs.map +1 -0
  123. package/dist/mcp/recordings.d.mts +40 -0
  124. package/dist/mcp/recordings.d.mts.map +1 -0
  125. package/dist/mcp/recordings.mjs +2390 -0
  126. package/dist/mcp/recordings.mjs.map +1 -0
  127. package/dist/mcp/state.d.mts +71 -0
  128. package/dist/mcp/state.d.mts.map +1 -0
  129. package/dist/mcp/state.mjs +83 -0
  130. package/dist/mcp/state.mjs.map +1 -0
  131. package/dist/mcp.d.mts +84 -0
  132. package/dist/mcp.d.mts.map +1 -0
  133. package/dist/mcp.mjs +241 -0
  134. package/dist/mcp.mjs.map +1 -0
  135. package/dist/messages.d.mts +183 -0
  136. package/dist/messages.d.mts.map +1 -0
  137. package/dist/messages.mjs +532 -0
  138. package/dist/messages.mjs.map +1 -0
  139. package/dist/microsoft/api.d.mts +47 -0
  140. package/dist/microsoft/api.d.mts.map +1 -0
  141. package/dist/microsoft/api.mjs +178 -0
  142. package/dist/microsoft/api.mjs.map +1 -0
  143. package/dist/microsoft/calendar.d.mts +30 -0
  144. package/dist/microsoft/calendar.d.mts.map +1 -0
  145. package/dist/microsoft/calendar.mjs +271 -0
  146. package/dist/microsoft/calendar.mjs.map +1 -0
  147. package/dist/microsoft/drive.d.mts +36 -0
  148. package/dist/microsoft/drive.d.mts.map +1 -0
  149. package/dist/microsoft/drive.mjs +298 -0
  150. package/dist/microsoft/drive.mjs.map +1 -0
  151. package/dist/microsoft/graph.d.mts +198 -0
  152. package/dist/microsoft/graph.d.mts.map +1 -0
  153. package/dist/microsoft/graph.mjs +264 -0
  154. package/dist/microsoft/graph.mjs.map +1 -0
  155. package/dist/microsoft/mail.d.mts +48 -0
  156. package/dist/microsoft/mail.d.mts.map +1 -0
  157. package/dist/microsoft/mail.mjs +488 -0
  158. package/dist/microsoft/mail.mjs.map +1 -0
  159. package/dist/microsoft/state.d.mts +480 -0
  160. package/dist/microsoft/state.d.mts.map +1 -0
  161. package/dist/microsoft/state.mjs +625 -0
  162. package/dist/microsoft/state.mjs.map +1 -0
  163. package/dist/microsoft.d.mts +168 -0
  164. package/dist/microsoft.d.mts.map +1 -0
  165. package/dist/microsoft.mjs +483 -0
  166. package/dist/microsoft.mjs.map +1 -0
  167. package/dist/node.d.mts +47 -0
  168. package/dist/node.d.mts.map +1 -0
  169. package/dist/node.mjs +178 -0
  170. package/dist/node.mjs.map +1 -0
  171. package/dist/notion/api.d.mts +51 -0
  172. package/dist/notion/api.d.mts.map +1 -0
  173. package/dist/notion/api.mjs +507 -0
  174. package/dist/notion/api.mjs.map +1 -0
  175. package/dist/notion/state.d.mts +329 -0
  176. package/dist/notion/state.d.mts.map +1 -0
  177. package/dist/notion/state.mjs +432 -0
  178. package/dist/notion/state.mjs.map +1 -0
  179. package/dist/notion.d.mts +72 -0
  180. package/dist/notion.d.mts.map +1 -0
  181. package/dist/notion.mjs +127 -0
  182. package/dist/notion.mjs.map +1 -0
  183. package/dist/openai.d.mts +165 -0
  184. package/dist/openai.d.mts.map +1 -0
  185. package/dist/openai.mjs +141 -0
  186. package/dist/openai.mjs.map +1 -0
  187. package/dist/opencode-recordings.d.mts +7 -0
  188. package/dist/opencode-recordings.d.mts.map +1 -0
  189. package/dist/opencode-recordings.mjs +222 -0
  190. package/dist/opencode-recordings.mjs.map +1 -0
  191. package/dist/opencode.d.mts +70 -0
  192. package/dist/opencode.d.mts.map +1 -0
  193. package/dist/opencode.mjs +217 -0
  194. package/dist/opencode.mjs.map +1 -0
  195. package/dist/r2-fixtures.d.mts +36 -0
  196. package/dist/r2-fixtures.d.mts.map +1 -0
  197. package/dist/r2-fixtures.mjs +245 -0
  198. package/dist/r2-fixtures.mjs.map +1 -0
  199. package/dist/r2-guard.d.mts +43 -0
  200. package/dist/r2-guard.d.mts.map +1 -0
  201. package/dist/r2-guard.mjs +215 -0
  202. package/dist/r2-guard.mjs.map +1 -0
  203. package/dist/r2.d.mts +166 -0
  204. package/dist/r2.d.mts.map +1 -0
  205. package/dist/r2.mjs +517 -0
  206. package/dist/r2.mjs.map +1 -0
  207. package/dist/responses.d.mts +231 -0
  208. package/dist/responses.d.mts.map +1 -0
  209. package/dist/responses.mjs +556 -0
  210. package/dist/responses.mjs.map +1 -0
  211. package/dist/route-evidence.d.mts +45 -0
  212. package/dist/route-evidence.d.mts.map +1 -0
  213. package/dist/route-evidence.mjs +56 -0
  214. package/dist/route-evidence.mjs.map +1 -0
  215. package/dist/router.d.mts +93 -0
  216. package/dist/router.d.mts.map +1 -0
  217. package/dist/router.mjs +259 -0
  218. package/dist/router.mjs.map +1 -0
  219. package/dist/stateful-core.d.mts +17 -0
  220. package/dist/stateful-core.d.mts.map +1 -0
  221. package/dist/stateful-core.mjs +41 -0
  222. package/dist/stateful-core.mjs.map +1 -0
  223. package/dist/stateful-emulator.d.mts +565 -0
  224. package/dist/stateful-emulator.d.mts.map +1 -0
  225. package/dist/stateful-emulator.mjs +1228 -0
  226. package/dist/stateful-emulator.mjs.map +1 -0
  227. package/dist/stateful-secrets.d.mts +84 -0
  228. package/dist/stateful-secrets.d.mts.map +1 -0
  229. package/dist/stateful-secrets.mjs +216 -0
  230. package/dist/stateful-secrets.mjs.map +1 -0
  231. package/dist/subscription-usage-recordings.d.mts +9 -0
  232. package/dist/subscription-usage-recordings.d.mts.map +1 -0
  233. package/dist/subscription-usage-recordings.mjs +53 -0
  234. package/dist/subscription-usage-recordings.mjs.map +1 -0
  235. package/dist/subscription-usage.d.mts +53 -0
  236. package/dist/subscription-usage.d.mts.map +1 -0
  237. package/dist/subscription-usage.mjs +21 -0
  238. package/dist/subscription-usage.mjs.map +1 -0
  239. package/dist/telegram/api.d.mts +34 -0
  240. package/dist/telegram/api.d.mts.map +1 -0
  241. package/dist/telegram/api.mjs +257 -0
  242. package/dist/telegram/api.mjs.map +1 -0
  243. package/dist/telegram/state.d.mts +128 -0
  244. package/dist/telegram/state.d.mts.map +1 -0
  245. package/dist/telegram/state.mjs +154 -0
  246. package/dist/telegram/state.mjs.map +1 -0
  247. package/dist/telegram.d.mts +62 -0
  248. package/dist/telegram.d.mts.map +1 -0
  249. package/dist/telegram.mjs +127 -0
  250. package/dist/telegram.mjs.map +1 -0
  251. package/dist/todoist/api.d.mts +55 -0
  252. package/dist/todoist/api.d.mts.map +1 -0
  253. package/dist/todoist/api.mjs +415 -0
  254. package/dist/todoist/api.mjs.map +1 -0
  255. package/dist/todoist/state.d.mts +275 -0
  256. package/dist/todoist/state.d.mts.map +1 -0
  257. package/dist/todoist/state.mjs +345 -0
  258. package/dist/todoist/state.mjs.map +1 -0
  259. package/dist/todoist.d.mts +68 -0
  260. package/dist/todoist.d.mts.map +1 -0
  261. package/dist/todoist.mjs +181 -0
  262. package/dist/todoist.mjs.map +1 -0
  263. package/dist/xai.d.mts +267 -0
  264. package/dist/xai.d.mts.map +1 -0
  265. package/dist/xai.mjs +252 -0
  266. package/dist/xai.mjs.map +1 -0
  267. package/package.json +157 -0
  268. package/src/anthropic.ts +289 -0
  269. package/src/chat-completions.ts +924 -0
  270. package/src/codex.ts +296 -0
  271. package/src/dropbox/api.ts +1084 -0
  272. package/src/dropbox/state.ts +364 -0
  273. package/src/dropbox.ts +203 -0
  274. package/src/email-fixtures.ts +1297 -0
  275. package/src/email.ts +1108 -0
  276. package/src/emulator-compose.ts +147 -0
  277. package/src/emulator-http.ts +184 -0
  278. package/src/emulator-kernel.ts +844 -0
  279. package/src/fixture-route.ts +561 -0
  280. package/src/fortnox/api.ts +1352 -0
  281. package/src/fortnox/state.ts +798 -0
  282. package/src/fortnox.ts +801 -0
  283. package/src/gateway-evaluate-recordings.ts +153 -0
  284. package/src/gateway.ts +530 -0
  285. package/src/github/api.ts +986 -0
  286. package/src/github/state.ts +439 -0
  287. package/src/github.ts +271 -0
  288. package/src/google/calendar.ts +486 -0
  289. package/src/google/drive.ts +471 -0
  290. package/src/google/gmail.ts +1011 -0
  291. package/src/google/shared.ts +321 -0
  292. package/src/google/state.ts +686 -0
  293. package/src/google.ts +298 -0
  294. package/src/linkedin-search/api.ts +363 -0
  295. package/src/linkedin-search/state.ts +373 -0
  296. package/src/linkedin-search.ts +241 -0
  297. package/src/mcp/api.ts +995 -0
  298. package/src/mcp/recordings.ts +2665 -0
  299. package/src/mcp/state.ts +125 -0
  300. package/src/mcp.ts +319 -0
  301. package/src/messages.ts +844 -0
  302. package/src/microsoft/api.ts +426 -0
  303. package/src/microsoft/calendar.ts +491 -0
  304. package/src/microsoft/drive.ts +541 -0
  305. package/src/microsoft/graph.ts +550 -0
  306. package/src/microsoft/mail.ts +833 -0
  307. package/src/microsoft/state.ts +822 -0
  308. package/src/microsoft.ts +982 -0
  309. package/src/node.ts +293 -0
  310. package/src/notion/api.ts +976 -0
  311. package/src/notion/state.ts +512 -0
  312. package/src/notion.ts +210 -0
  313. package/src/openai.ts +198 -0
  314. package/src/opencode-recordings.ts +257 -0
  315. package/src/opencode.ts +271 -0
  316. package/src/r2-fixtures.ts +295 -0
  317. package/src/r2-guard.ts +323 -0
  318. package/src/r2.ts +890 -0
  319. package/src/responses.ts +901 -0
  320. package/src/route-evidence.ts +90 -0
  321. package/src/router.ts +490 -0
  322. package/src/stateful-core.ts +70 -0
  323. package/src/stateful-emulator.ts +2571 -0
  324. package/src/stateful-secrets.ts +299 -0
  325. package/src/subscription-usage-recordings.ts +77 -0
  326. package/src/subscription-usage.ts +68 -0
  327. package/src/telegram/api.ts +437 -0
  328. package/src/telegram/state.ts +229 -0
  329. package/src/telegram.ts +227 -0
  330. package/src/todoist/api.ts +736 -0
  331. package/src/todoist/state.ts +456 -0
  332. package/src/todoist.ts +316 -0
  333. package/src/xai.ts +341 -0
@@ -0,0 +1,36 @@
1
+ import * as Schema from "effect/Schema";
2
+
3
+ //#region src/r2-fixtures.d.ts
4
+ /** A port failure as a fixture records it (mirrors `PortFailure` in `@yolk-sdk/conformance`). */
5
+ type R2EmulatorFailure = {
6
+ readonly kind: 'expected' | 'error';
7
+ readonly code: string;
8
+ readonly message: string;
9
+ readonly status?: number;
10
+ };
11
+ type R2EmulatorFixtureFields = {
12
+ readonly id: string;
13
+ readonly port: string;
14
+ readonly method: string;
15
+ readonly request: Schema.Json;
16
+ readonly observed?: {
17
+ readonly account: string;
18
+ readonly date: string;
19
+ };
20
+ readonly note?: string;
21
+ };
22
+ /**
23
+ * One recorded `R2Presigner` or `R2ObjectClient` call (the plain-JSON shape of a `PortFixture`):
24
+ * exactly one of `response` or `failure`.
25
+ */
26
+ type R2EmulatorFixture = (R2EmulatorFixtureFields & {
27
+ readonly response: Schema.Json;
28
+ readonly failure?: never;
29
+ }) | (R2EmulatorFixtureFields & {
30
+ readonly failure: R2EmulatorFailure;
31
+ readonly response?: never;
32
+ });
33
+ declare const r2EmulatorFixtures: ReadonlyArray<R2EmulatorFixture>;
34
+ //#endregion
35
+ export { R2EmulatorFailure, R2EmulatorFixture, r2EmulatorFixtures };
36
+ //# sourceMappingURL=r2-fixtures.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"r2-fixtures.d.mts","names":[],"sources":["../src/r2-fixtures.ts"],"mappings":";;;;KAYY,iBAAA;EAAA,SACD,IAAA;EAAA,SACA,IAAA;EAAA,SACA,OAAA;EAAA,SACA,MAAA;AAAA;AAAA,KAGN,uBAAA;EAAA,SACM,EAAA;EAAA,SACA,IAAA;EAAA,SACA,MAAA;EAAA,SACA,OAAA,EAAS,MAAA,CAAO,IAAI;EAAA,SACpB,QAAA;IAAA,SAAsB,OAAA;IAAA,SAA0B,IAAA;EAAA;EAAA,SAChD,IAAA;AAAA;AAAI;AAOf;;;AAPe,KAOH,iBAAA,IACP,uBAAA;EAAA,SAAqC,QAAA,EAAU,MAAA,CAAO,IAAA;EAAA,SAAe,OAAA;AAAA,MACrE,uBAAA;EAAA,SACU,OAAA,EAAS,iBAAA;EAAA,SACT,QAAA;AAAA;AAAA,cAGF,kBAAA,EAAoB,aAAa,CAAC,iBAAA"}
@@ -0,0 +1,245 @@
1
+ //#region src/r2-fixtures.ts
2
+ const r2EmulatorFixtures = [
3
+ {
4
+ id: "r2.presign.put-upload-url.presign.synthetic",
5
+ port: "R2Presigner",
6
+ method: "presignPutObject",
7
+ request: {
8
+ endpoint: "https://synthetic-account.r2.example.test",
9
+ bucket: "yolk-synthetic-bucket",
10
+ key: "yolk-conformance/run-synthetic/presign.txt",
11
+ contentType: "text/plain"
12
+ },
13
+ response: { uploadUrl: "https://synthetic-account.r2.example.test/yolk-synthetic-bucket/yolk-conformance/run-synthetic/presign.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=yolk-synthetic-access-key-id%2F20260930%2Fauto%2Fs3%2Faws4_request&X-Amz-Date=20260930T120000Z&X-Amz-Expires=900&X-Amz-Signature=yolk-synthetic-signature&X-Amz-SignedHeaders=content-type%3Bhost&x-id=PutObject" },
14
+ note: "Local SigV4 query signing (15-minute expiry, content-type signed); nothing sent to R2. Synthetic."
15
+ },
16
+ {
17
+ id: "r2.objects.get-max-bytes.within-budget.synthetic",
18
+ port: "R2ObjectClient",
19
+ method: "get",
20
+ request: {
21
+ bucket: "yolk-synthetic-bucket",
22
+ key: "fixtures/synthetic-object.txt",
23
+ maxBytes: 1048576
24
+ },
25
+ response: {
26
+ etag: "\"a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5\"",
27
+ size: 44,
28
+ bodyBase64: "U3ludGhldGljIGNvbmZvcm1hbmNlIG9iamVjdDogc2FmZSB0byByZWFkLgo="
29
+ },
30
+ note: "The seeded object within the budget. Synthetic."
31
+ },
32
+ {
33
+ id: "r2.objects.get-max-bytes.over-budget.synthetic",
34
+ port: "R2ObjectClient",
35
+ method: "get",
36
+ request: {
37
+ bucket: "yolk-synthetic-bucket",
38
+ key: "fixtures/synthetic-object.txt",
39
+ maxBytes: 43
40
+ },
41
+ failure: {
42
+ kind: "error",
43
+ code: "response_too_large",
44
+ message: "The object is larger than maxBytes; no bytes were returned."
45
+ },
46
+ note: "The host stops at maxBytes (Content-Length or streamed count). Synthetic."
47
+ },
48
+ {
49
+ id: "r2.objects.get-expected-etag.plain.synthetic",
50
+ port: "R2ObjectClient",
51
+ method: "get",
52
+ request: {
53
+ bucket: "yolk-synthetic-bucket",
54
+ key: "fixtures/synthetic-object.txt",
55
+ maxBytes: 1048576
56
+ },
57
+ response: {
58
+ etag: "\"a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5\"",
59
+ size: 44,
60
+ bodyBase64: "U3ludGhldGljIGNvbmZvcm1hbmNlIG9iamVjdDogc2FmZSB0byByZWFkLgo="
61
+ },
62
+ note: "A plain read that learns the etag. Synthetic."
63
+ },
64
+ {
65
+ id: "r2.objects.get-expected-etag.current.synthetic",
66
+ port: "R2ObjectClient",
67
+ method: "get",
68
+ request: {
69
+ bucket: "yolk-synthetic-bucket",
70
+ key: "fixtures/synthetic-object.txt",
71
+ expectedEtag: "\"a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5\"",
72
+ maxBytes: 1048576
73
+ },
74
+ response: {
75
+ etag: "\"a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5\"",
76
+ size: 44,
77
+ bodyBase64: "U3ludGhldGljIGNvbmZvcm1hbmNlIG9iamVjdDogc2FmZSB0byByZWFkLgo="
78
+ },
79
+ note: "If-Match with the current etag. Synthetic."
80
+ },
81
+ {
82
+ id: "r2.objects.get-expected-etag.stale.synthetic",
83
+ port: "R2ObjectClient",
84
+ method: "get",
85
+ request: {
86
+ bucket: "yolk-synthetic-bucket",
87
+ key: "fixtures/synthetic-object.txt",
88
+ expectedEtag: "\"00000000000000000000000000000000\"",
89
+ maxBytes: 1048576
90
+ },
91
+ failure: {
92
+ kind: "error",
93
+ code: "conflict",
94
+ message: "Precondition failed (If-Match)."
95
+ },
96
+ note: "If-Match with a stale etag: HTTP 412 or a binding onlyIf without body. Synthetic."
97
+ },
98
+ {
99
+ id: "r2.objects.get-missing-not-found.get.synthetic",
100
+ port: "R2ObjectClient",
101
+ method: "get",
102
+ request: {
103
+ bucket: "yolk-synthetic-bucket",
104
+ key: "yolk-conformance/run-synthetic/absent.txt",
105
+ maxBytes: 1048576
106
+ },
107
+ failure: {
108
+ kind: "error",
109
+ code: "not_found",
110
+ message: "No object with that key."
111
+ },
112
+ note: "HTTP 404 NoSuchKey, or a binding get answering null. Synthetic."
113
+ },
114
+ {
115
+ id: "r2.objects.create-if-absent.create.synthetic",
116
+ port: "R2ObjectClient",
117
+ method: "put",
118
+ request: {
119
+ bucket: "yolk-synthetic-bucket",
120
+ key: "yolk-conformance/run-synthetic/create-if-absent.txt",
121
+ condition: { kind: "absent" },
122
+ bodyBase64: "eW9say1jb25mb3JtYW5jZSBydW4tc3ludGhldGljOiBmaXJzdCBzeW50aGV0aWMgYm9keSwgc2FmZSB0byBkZWxldGU=",
123
+ maxUploadBytes: 1048576
124
+ },
125
+ response: {
126
+ etag: "\"c0ffee00c0ffee00c0ffee00c0ffee01\"",
127
+ size: 68
128
+ },
129
+ note: "PUT with If-None-Match: * on a fresh key. Synthetic."
130
+ },
131
+ {
132
+ id: "r2.objects.create-if-absent.create-again.synthetic",
133
+ port: "R2ObjectClient",
134
+ method: "put",
135
+ request: {
136
+ bucket: "yolk-synthetic-bucket",
137
+ key: "yolk-conformance/run-synthetic/create-if-absent.txt",
138
+ condition: { kind: "absent" },
139
+ bodyBase64: "eW9say1jb25mb3JtYW5jZSBydW4tc3ludGhldGljOiBzZWNvbmQgc3ludGhldGljIGJvZHksIG5ldmVyIHN0b3JlZA==",
140
+ maxUploadBytes: 1048576
141
+ },
142
+ failure: {
143
+ kind: "error",
144
+ code: "conflict",
145
+ message: "Precondition failed (If-None-Match)."
146
+ },
147
+ note: "The same key again: HTTP 412, or a binding put answering null. Synthetic."
148
+ },
149
+ {
150
+ id: "r2.objects.create-if-absent.read.synthetic",
151
+ port: "R2ObjectClient",
152
+ method: "get",
153
+ request: {
154
+ bucket: "yolk-synthetic-bucket",
155
+ key: "yolk-conformance/run-synthetic/create-if-absent.txt",
156
+ expectedEtag: "\"c0ffee00c0ffee00c0ffee00c0ffee01\"",
157
+ maxBytes: 1048576
158
+ },
159
+ response: {
160
+ etag: "\"c0ffee00c0ffee00c0ffee00c0ffee01\"",
161
+ size: 68,
162
+ bodyBase64: "eW9say1jb25mb3JtYW5jZSBydW4tc3ludGhldGljOiBmaXJzdCBzeW50aGV0aWMgYm9keSwgc2FmZSB0byBkZWxldGU="
163
+ },
164
+ note: "The first bytes, unchanged. Synthetic."
165
+ },
166
+ {
167
+ id: "r2.objects.update-if-match.create.synthetic",
168
+ port: "R2ObjectClient",
169
+ method: "put",
170
+ request: {
171
+ bucket: "yolk-synthetic-bucket",
172
+ key: "yolk-conformance/run-synthetic/update-if-match.txt",
173
+ condition: { kind: "absent" },
174
+ bodyBase64: "eW9say1jb25mb3JtYW5jZSBydW4tc3ludGhldGljOiBvcmlnaW5hbCBzeW50aGV0aWMgYm9keSwgc2FmZSB0byBkZWxldGU=",
175
+ maxUploadBytes: 1048576
176
+ },
177
+ response: {
178
+ etag: "\"c0ffee00c0ffee00c0ffee00c0ffee02\"",
179
+ size: 71
180
+ },
181
+ note: "PUT with If-None-Match: * on a fresh key. Synthetic."
182
+ },
183
+ {
184
+ id: "r2.objects.update-if-match.stale.synthetic",
185
+ port: "R2ObjectClient",
186
+ method: "put",
187
+ request: {
188
+ bucket: "yolk-synthetic-bucket",
189
+ key: "yolk-conformance/run-synthetic/update-if-match.txt",
190
+ condition: {
191
+ kind: "etag",
192
+ etag: "\"00000000000000000000000000000000\""
193
+ },
194
+ bodyBase64: "eW9say1jb25mb3JtYW5jZSBydW4tc3ludGhldGljOiByZXBsYWNlbWVudCBzeW50aGV0aWMgYm9keSwgc2FmZSB0byBkZWxldGU=",
195
+ maxUploadBytes: 1048576
196
+ },
197
+ failure: {
198
+ kind: "error",
199
+ code: "conflict",
200
+ message: "Precondition failed (If-Match)."
201
+ },
202
+ note: "PUT with a stale If-Match: HTTP 412, or a binding put answering null. Synthetic."
203
+ },
204
+ {
205
+ id: "r2.objects.update-if-match.current.synthetic",
206
+ port: "R2ObjectClient",
207
+ method: "put",
208
+ request: {
209
+ bucket: "yolk-synthetic-bucket",
210
+ key: "yolk-conformance/run-synthetic/update-if-match.txt",
211
+ condition: {
212
+ kind: "etag",
213
+ etag: "\"c0ffee00c0ffee00c0ffee00c0ffee02\""
214
+ },
215
+ bodyBase64: "eW9say1jb25mb3JtYW5jZSBydW4tc3ludGhldGljOiByZXBsYWNlbWVudCBzeW50aGV0aWMgYm9keSwgc2FmZSB0byBkZWxldGU=",
216
+ maxUploadBytes: 1048576
217
+ },
218
+ response: {
219
+ etag: "\"c0ffee00c0ffee00c0ffee00c0ffee03\"",
220
+ size: 74
221
+ },
222
+ note: "PUT with the current If-Match: a new MD5 etag. Synthetic."
223
+ },
224
+ {
225
+ id: "r2.objects.update-if-match.read.synthetic",
226
+ port: "R2ObjectClient",
227
+ method: "get",
228
+ request: {
229
+ bucket: "yolk-synthetic-bucket",
230
+ key: "yolk-conformance/run-synthetic/update-if-match.txt",
231
+ expectedEtag: "\"c0ffee00c0ffee00c0ffee00c0ffee03\"",
232
+ maxBytes: 1048576
233
+ },
234
+ response: {
235
+ etag: "\"c0ffee00c0ffee00c0ffee00c0ffee03\"",
236
+ size: 74,
237
+ bodyBase64: "eW9say1jb25mb3JtYW5jZSBydW4tc3ludGhldGljOiByZXBsYWNlbWVudCBzeW50aGV0aWMgYm9keSwgc2FmZSB0byBkZWxldGU="
238
+ },
239
+ note: "The replacement bytes under the new etag. Synthetic."
240
+ }
241
+ ];
242
+ //#endregion
243
+ export { r2EmulatorFixtures };
244
+
245
+ //# sourceMappingURL=r2-fixtures.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"r2-fixtures.mjs","names":[],"sources":["../src/r2-fixtures.ts"],"sourcesContent":["/**\n * The R2 conformance fixtures, copied as data (internal; re-exported by `r2.ts`).\n *\n * Emulators never import SDK code: this is a verbatim copy of `r2ConformanceFixtures` from\n * `@yolk-sdk/connectors/r2-storage/conformance` (synthetic `PortFixture`s; none observed live).\n * The presigned URL carries only the canonical synthetic SigV4 placeholders\n * (`syntheticPortCredentialParams` of `@yolk-sdk/conformance`). `test/r2-conformance.test.ts`\n * fails when the two drift apart; update both together.\n */\nimport type * as Schema from 'effect/Schema'\n\n/** A port failure as a fixture records it (mirrors `PortFailure` in `@yolk-sdk/conformance`). */\nexport type R2EmulatorFailure = {\n readonly kind: 'expected' | 'error'\n readonly code: string\n readonly message: string\n readonly status?: number\n}\n\ntype R2EmulatorFixtureFields = {\n readonly id: string\n readonly port: string\n readonly method: string\n readonly request: Schema.Json\n readonly observed?: { readonly account: string; readonly date: string }\n readonly note?: string\n}\n\n/**\n * One recorded `R2Presigner` or `R2ObjectClient` call (the plain-JSON shape of a `PortFixture`):\n * exactly one of `response` or `failure`.\n */\nexport type R2EmulatorFixture =\n | (R2EmulatorFixtureFields & { readonly response: Schema.Json; readonly failure?: never })\n | (R2EmulatorFixtureFields & {\n readonly failure: R2EmulatorFailure\n readonly response?: never\n })\n\nexport const r2EmulatorFixtures: ReadonlyArray<R2EmulatorFixture> = [\n {\n id: 'r2.presign.put-upload-url.presign.synthetic',\n port: 'R2Presigner',\n method: 'presignPutObject',\n request: {\n endpoint: 'https://synthetic-account.r2.example.test',\n bucket: 'yolk-synthetic-bucket',\n key: 'yolk-conformance/run-synthetic/presign.txt',\n contentType: 'text/plain'\n },\n response: {\n uploadUrl:\n 'https://synthetic-account.r2.example.test/yolk-synthetic-bucket/yolk-conformance/run-synthetic/presign.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=yolk-synthetic-access-key-id%2F20260930%2Fauto%2Fs3%2Faws4_request&X-Amz-Date=20260930T120000Z&X-Amz-Expires=900&X-Amz-Signature=yolk-synthetic-signature&X-Amz-SignedHeaders=content-type%3Bhost&x-id=PutObject'\n },\n note: 'Local SigV4 query signing (15-minute expiry, content-type signed); nothing sent to R2. Synthetic.'\n },\n {\n id: 'r2.objects.get-max-bytes.within-budget.synthetic',\n port: 'R2ObjectClient',\n method: 'get',\n request: {\n bucket: 'yolk-synthetic-bucket',\n key: 'fixtures/synthetic-object.txt',\n maxBytes: 1048576\n },\n response: {\n etag: '\"a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5\"',\n size: 44,\n bodyBase64: 'U3ludGhldGljIGNvbmZvcm1hbmNlIG9iamVjdDogc2FmZSB0byByZWFkLgo='\n },\n note: 'The seeded object within the budget. Synthetic.'\n },\n {\n id: 'r2.objects.get-max-bytes.over-budget.synthetic',\n port: 'R2ObjectClient',\n method: 'get',\n request: {\n bucket: 'yolk-synthetic-bucket',\n key: 'fixtures/synthetic-object.txt',\n maxBytes: 43\n },\n failure: {\n kind: 'error',\n code: 'response_too_large',\n message: 'The object is larger than maxBytes; no bytes were returned.'\n },\n note: 'The host stops at maxBytes (Content-Length or streamed count). Synthetic.'\n },\n {\n id: 'r2.objects.get-expected-etag.plain.synthetic',\n port: 'R2ObjectClient',\n method: 'get',\n request: {\n bucket: 'yolk-synthetic-bucket',\n key: 'fixtures/synthetic-object.txt',\n maxBytes: 1048576\n },\n response: {\n etag: '\"a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5\"',\n size: 44,\n bodyBase64: 'U3ludGhldGljIGNvbmZvcm1hbmNlIG9iamVjdDogc2FmZSB0byByZWFkLgo='\n },\n note: 'A plain read that learns the etag. Synthetic.'\n },\n {\n id: 'r2.objects.get-expected-etag.current.synthetic',\n port: 'R2ObjectClient',\n method: 'get',\n request: {\n bucket: 'yolk-synthetic-bucket',\n key: 'fixtures/synthetic-object.txt',\n expectedEtag: '\"a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5\"',\n maxBytes: 1048576\n },\n response: {\n etag: '\"a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5\"',\n size: 44,\n bodyBase64: 'U3ludGhldGljIGNvbmZvcm1hbmNlIG9iamVjdDogc2FmZSB0byByZWFkLgo='\n },\n note: 'If-Match with the current etag. Synthetic.'\n },\n {\n id: 'r2.objects.get-expected-etag.stale.synthetic',\n port: 'R2ObjectClient',\n method: 'get',\n request: {\n bucket: 'yolk-synthetic-bucket',\n key: 'fixtures/synthetic-object.txt',\n expectedEtag: '\"00000000000000000000000000000000\"',\n maxBytes: 1048576\n },\n failure: {\n kind: 'error',\n code: 'conflict',\n message: 'Precondition failed (If-Match).'\n },\n note: 'If-Match with a stale etag: HTTP 412 or a binding onlyIf without body. Synthetic.'\n },\n {\n id: 'r2.objects.get-missing-not-found.get.synthetic',\n port: 'R2ObjectClient',\n method: 'get',\n request: {\n bucket: 'yolk-synthetic-bucket',\n key: 'yolk-conformance/run-synthetic/absent.txt',\n maxBytes: 1048576\n },\n failure: {\n kind: 'error',\n code: 'not_found',\n message: 'No object with that key.'\n },\n note: 'HTTP 404 NoSuchKey, or a binding get answering null. Synthetic.'\n },\n {\n id: 'r2.objects.create-if-absent.create.synthetic',\n port: 'R2ObjectClient',\n method: 'put',\n request: {\n bucket: 'yolk-synthetic-bucket',\n key: 'yolk-conformance/run-synthetic/create-if-absent.txt',\n condition: {\n kind: 'absent'\n },\n bodyBase64:\n 'eW9say1jb25mb3JtYW5jZSBydW4tc3ludGhldGljOiBmaXJzdCBzeW50aGV0aWMgYm9keSwgc2FmZSB0byBkZWxldGU=',\n maxUploadBytes: 1048576\n },\n response: {\n etag: '\"c0ffee00c0ffee00c0ffee00c0ffee01\"',\n size: 68\n },\n note: 'PUT with If-None-Match: * on a fresh key. Synthetic.'\n },\n {\n id: 'r2.objects.create-if-absent.create-again.synthetic',\n port: 'R2ObjectClient',\n method: 'put',\n request: {\n bucket: 'yolk-synthetic-bucket',\n key: 'yolk-conformance/run-synthetic/create-if-absent.txt',\n condition: {\n kind: 'absent'\n },\n bodyBase64:\n 'eW9say1jb25mb3JtYW5jZSBydW4tc3ludGhldGljOiBzZWNvbmQgc3ludGhldGljIGJvZHksIG5ldmVyIHN0b3JlZA==',\n maxUploadBytes: 1048576\n },\n failure: {\n kind: 'error',\n code: 'conflict',\n message: 'Precondition failed (If-None-Match).'\n },\n note: 'The same key again: HTTP 412, or a binding put answering null. Synthetic.'\n },\n {\n id: 'r2.objects.create-if-absent.read.synthetic',\n port: 'R2ObjectClient',\n method: 'get',\n request: {\n bucket: 'yolk-synthetic-bucket',\n key: 'yolk-conformance/run-synthetic/create-if-absent.txt',\n expectedEtag: '\"c0ffee00c0ffee00c0ffee00c0ffee01\"',\n maxBytes: 1048576\n },\n response: {\n etag: '\"c0ffee00c0ffee00c0ffee00c0ffee01\"',\n size: 68,\n bodyBase64:\n 'eW9say1jb25mb3JtYW5jZSBydW4tc3ludGhldGljOiBmaXJzdCBzeW50aGV0aWMgYm9keSwgc2FmZSB0byBkZWxldGU='\n },\n note: 'The first bytes, unchanged. Synthetic.'\n },\n {\n id: 'r2.objects.update-if-match.create.synthetic',\n port: 'R2ObjectClient',\n method: 'put',\n request: {\n bucket: 'yolk-synthetic-bucket',\n key: 'yolk-conformance/run-synthetic/update-if-match.txt',\n condition: {\n kind: 'absent'\n },\n bodyBase64:\n 'eW9say1jb25mb3JtYW5jZSBydW4tc3ludGhldGljOiBvcmlnaW5hbCBzeW50aGV0aWMgYm9keSwgc2FmZSB0byBkZWxldGU=',\n maxUploadBytes: 1048576\n },\n response: {\n etag: '\"c0ffee00c0ffee00c0ffee00c0ffee02\"',\n size: 71\n },\n note: 'PUT with If-None-Match: * on a fresh key. Synthetic.'\n },\n {\n id: 'r2.objects.update-if-match.stale.synthetic',\n port: 'R2ObjectClient',\n method: 'put',\n request: {\n bucket: 'yolk-synthetic-bucket',\n key: 'yolk-conformance/run-synthetic/update-if-match.txt',\n condition: {\n kind: 'etag',\n etag: '\"00000000000000000000000000000000\"'\n },\n bodyBase64:\n 'eW9say1jb25mb3JtYW5jZSBydW4tc3ludGhldGljOiByZXBsYWNlbWVudCBzeW50aGV0aWMgYm9keSwgc2FmZSB0byBkZWxldGU=',\n maxUploadBytes: 1048576\n },\n failure: {\n kind: 'error',\n code: 'conflict',\n message: 'Precondition failed (If-Match).'\n },\n note: 'PUT with a stale If-Match: HTTP 412, or a binding put answering null. Synthetic.'\n },\n {\n id: 'r2.objects.update-if-match.current.synthetic',\n port: 'R2ObjectClient',\n method: 'put',\n request: {\n bucket: 'yolk-synthetic-bucket',\n key: 'yolk-conformance/run-synthetic/update-if-match.txt',\n condition: {\n kind: 'etag',\n etag: '\"c0ffee00c0ffee00c0ffee00c0ffee02\"'\n },\n bodyBase64:\n 'eW9say1jb25mb3JtYW5jZSBydW4tc3ludGhldGljOiByZXBsYWNlbWVudCBzeW50aGV0aWMgYm9keSwgc2FmZSB0byBkZWxldGU=',\n maxUploadBytes: 1048576\n },\n response: {\n etag: '\"c0ffee00c0ffee00c0ffee00c0ffee03\"',\n size: 74\n },\n note: 'PUT with the current If-Match: a new MD5 etag. Synthetic.'\n },\n {\n id: 'r2.objects.update-if-match.read.synthetic',\n port: 'R2ObjectClient',\n method: 'get',\n request: {\n bucket: 'yolk-synthetic-bucket',\n key: 'yolk-conformance/run-synthetic/update-if-match.txt',\n expectedEtag: '\"c0ffee00c0ffee00c0ffee00c0ffee03\"',\n maxBytes: 1048576\n },\n response: {\n etag: '\"c0ffee00c0ffee00c0ffee00c0ffee03\"',\n size: 74,\n bodyBase64:\n 'eW9say1jb25mb3JtYW5jZSBydW4tc3ludGhldGljOiByZXBsYWNlbWVudCBzeW50aGV0aWMgYm9keSwgc2FmZSB0byBkZWxldGU='\n },\n note: 'The replacement bytes under the new etag. Synthetic.'\n }\n]\n"],"mappings":";AAuCA,MAAa,qBAAuD;CAClE;EACE,IAAI;EACJ,MAAM;EACN,QAAQ;EACR,SAAS;GACP,UAAU;GACV,QAAQ;GACR,KAAK;GACL,aAAa;EACf;EACA,UAAU,EACR,WACE,sZACJ;EACA,MAAM;CACR;CACA;EACE,IAAI;EACJ,MAAM;EACN,QAAQ;EACR,SAAS;GACP,QAAQ;GACR,KAAK;GACL,UAAU;EACZ;EACA,UAAU;GACR,MAAM;GACN,MAAM;GACN,YAAY;EACd;EACA,MAAM;CACR;CACA;EACE,IAAI;EACJ,MAAM;EACN,QAAQ;EACR,SAAS;GACP,QAAQ;GACR,KAAK;GACL,UAAU;EACZ;EACA,SAAS;GACP,MAAM;GACN,MAAM;GACN,SAAS;EACX;EACA,MAAM;CACR;CACA;EACE,IAAI;EACJ,MAAM;EACN,QAAQ;EACR,SAAS;GACP,QAAQ;GACR,KAAK;GACL,UAAU;EACZ;EACA,UAAU;GACR,MAAM;GACN,MAAM;GACN,YAAY;EACd;EACA,MAAM;CACR;CACA;EACE,IAAI;EACJ,MAAM;EACN,QAAQ;EACR,SAAS;GACP,QAAQ;GACR,KAAK;GACL,cAAc;GACd,UAAU;EACZ;EACA,UAAU;GACR,MAAM;GACN,MAAM;GACN,YAAY;EACd;EACA,MAAM;CACR;CACA;EACE,IAAI;EACJ,MAAM;EACN,QAAQ;EACR,SAAS;GACP,QAAQ;GACR,KAAK;GACL,cAAc;GACd,UAAU;EACZ;EACA,SAAS;GACP,MAAM;GACN,MAAM;GACN,SAAS;EACX;EACA,MAAM;CACR;CACA;EACE,IAAI;EACJ,MAAM;EACN,QAAQ;EACR,SAAS;GACP,QAAQ;GACR,KAAK;GACL,UAAU;EACZ;EACA,SAAS;GACP,MAAM;GACN,MAAM;GACN,SAAS;EACX;EACA,MAAM;CACR;CACA;EACE,IAAI;EACJ,MAAM;EACN,QAAQ;EACR,SAAS;GACP,QAAQ;GACR,KAAK;GACL,WAAW,EACT,MAAM,SACR;GACA,YACE;GACF,gBAAgB;EAClB;EACA,UAAU;GACR,MAAM;GACN,MAAM;EACR;EACA,MAAM;CACR;CACA;EACE,IAAI;EACJ,MAAM;EACN,QAAQ;EACR,SAAS;GACP,QAAQ;GACR,KAAK;GACL,WAAW,EACT,MAAM,SACR;GACA,YACE;GACF,gBAAgB;EAClB;EACA,SAAS;GACP,MAAM;GACN,MAAM;GACN,SAAS;EACX;EACA,MAAM;CACR;CACA;EACE,IAAI;EACJ,MAAM;EACN,QAAQ;EACR,SAAS;GACP,QAAQ;GACR,KAAK;GACL,cAAc;GACd,UAAU;EACZ;EACA,UAAU;GACR,MAAM;GACN,MAAM;GACN,YACE;EACJ;EACA,MAAM;CACR;CACA;EACE,IAAI;EACJ,MAAM;EACN,QAAQ;EACR,SAAS;GACP,QAAQ;GACR,KAAK;GACL,WAAW,EACT,MAAM,SACR;GACA,YACE;GACF,gBAAgB;EAClB;EACA,UAAU;GACR,MAAM;GACN,MAAM;EACR;EACA,MAAM;CACR;CACA;EACE,IAAI;EACJ,MAAM;EACN,QAAQ;EACR,SAAS;GACP,QAAQ;GACR,KAAK;GACL,WAAW;IACT,MAAM;IACN,MAAM;GACR;GACA,YACE;GACF,gBAAgB;EAClB;EACA,SAAS;GACP,MAAM;GACN,MAAM;GACN,SAAS;EACX;EACA,MAAM;CACR;CACA;EACE,IAAI;EACJ,MAAM;EACN,QAAQ;EACR,SAAS;GACP,QAAQ;GACR,KAAK;GACL,WAAW;IACT,MAAM;IACN,MAAM;GACR;GACA,YACE;GACF,gBAAgB;EAClB;EACA,UAAU;GACR,MAAM;GACN,MAAM;EACR;EACA,MAAM;CACR;CACA;EACE,IAAI;EACJ,MAAM;EACN,QAAQ;EACR,SAAS;GACP,QAAQ;GACR,KAAK;GACL,cAAc;GACd,UAAU;EACZ;EACA,UAAU;GACR,MAAM;GACN,MAAM;GACN,YACE;EACJ;EACA,MAAM;CACR;AACF"}
@@ -0,0 +1,43 @@
1
+ import * as Schema from "effect/Schema";
2
+
3
+ //#region src/r2-guard.d.ts
4
+ type JsonObject = Schema.JsonObject;
5
+ /**
6
+ * The credential field names of the shared port scan (`isPortCredentialKey`): `credential(s)`
7
+ * plus the credential field names, AWS-style `accessKeyId` / `secretAccessKey` / `sessionToken`
8
+ * included. One regular-expression source per name; matched whole and case-insensitively.
9
+ */
10
+ declare const r2CredentialKeyNames: ReadonlyArray<string>;
11
+ /** True for a key whose whole value is a credential (dropped, never compared or recorded). */
12
+ declare const isR2CredentialKey: (key: string) => boolean;
13
+ /**
14
+ * The credential query or form parameter names of the shared port scan (`hasLiveCredentialParam`).
15
+ * One regular-expression source per name.
16
+ */
17
+ declare const r2CredentialParamNames: ReadonlyArray<string>;
18
+ /**
19
+ * The shared scan's token patterns (`bearerPattern` and `apiKeyPatterns`): a bearer token, common
20
+ * API-key prefixes, JSON Web Tokens, and PEM private keys.
21
+ */
22
+ declare const r2TokenPatterns: ReadonlyArray<RegExp>;
23
+ /** True when `text` carries a credential or repeats a guarded value. */
24
+ declare const textCarriesCredential: (text: string, guarded: ReadonlyArray<string>) => boolean;
25
+ /**
26
+ * The texts a number is matched by: as JavaScript prints it (`1e+21`, `12345678`) and, for an
27
+ * integer, its full digit string (`1000000000000000000000`).
28
+ */
29
+ declare const numberTexts: (value: number) => ReadonlyArray<string>;
30
+ /** The guarded values of every credential field of `value`, at any depth, deduplicated. */
31
+ declare const guardedValues: (value: Schema.Json) => ReadonlyArray<string>;
32
+ /** True when `value` holds an own `__proto__` key at any depth (an admission bypass otherwise). */
33
+ declare const hasOwnProtoKey: (value: Schema.Json) => boolean;
34
+ /**
35
+ * A copy without credential fields, at any depth (`redactPortPayload` of the shared scan), built
36
+ * with own data properties only (`Object.fromEntries`).
37
+ */
38
+ declare const withoutCredentials: (value: JsonObject) => JsonObject;
39
+ /** Every object key, string value, and number (`numberTexts`) of `value`. */
40
+ declare const textsOf: (value: Schema.Json) => ReadonlyArray<string>;
41
+ //#endregion
42
+ export { guardedValues, hasOwnProtoKey, isR2CredentialKey, numberTexts, r2CredentialKeyNames, r2CredentialParamNames, r2TokenPatterns, textCarriesCredential, textsOf, withoutCredentials };
43
+ //# sourceMappingURL=r2-guard.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"r2-guard.d.mts","names":[],"sources":["../src/r2-guard.ts"],"mappings":";;;KAiBK,UAAA,GAAa,MAAA,CAAO,UAAU;;AAHS;;;;cAa/B,oBAAA,EAAsB,aAAa;AAAhD;AAAA,cA0Ba,iBAAA,GAAqB,GAAW;;;AA1BG;AA0BhD;cAMa,sBAAA,EAAwB,aAAa;;;AANL;AAM7C;cAwBa,eAAA,EAAiB,aAAa,CAAC,MAAA;;cAkJ/B,qBAAA,GAAyB,IAAA,UAAc,OAAA,EAAS,aAAa;AA1KxB;AAwBlD;;;AAxBkD,cAmLrC,WAAA,GAAe,KAAA,aAAgB,aAAa;AA3JP;AAAA,cA4LrC,aAAA,GAAiB,KAAA,EAAO,MAAA,CAAO,IAAA,KAAO,aAAa;;cAenD,cAAA,GAAkB,KAAA,EAAO,MAAA,CAAO,IAAI;;;;;cAkBpC,kBAAA,GAAsB,KAAA,EAAO,UAAA,KAAa,UAKpD;AAhF+E;AAAA,cAmFrE,OAAA,GAAW,KAAA,EAAO,MAAA,CAAO,IAAA,KAAO,aAAa"}
@@ -0,0 +1,215 @@
1
+ import { textClosureOutcome } from "./stateful-secrets.mjs";
2
+ import { Predicate } from "effect";
3
+ //#region src/r2-guard.ts
4
+ /**
5
+ * The credential guard of the R2 port emulator (internal; used by `r2.ts`, and imported by
6
+ * `test/r2.test.ts` for the exhaustive parity tests).
7
+ *
8
+ * The copied lists here mirror `@yolk-sdk/conformance` (`isPortCredentialKey`, the credential query
9
+ * parameter names of `hasLiveCredentialParam`, `bearerPattern`, and `apiKeyPatterns`) and the R2
10
+ * conformance guard `findR2PortFixtureSecrets` (canonical placeholders and decodings). Emulators
11
+ * never import SDK code, so `test/r2.test.ts` holds one sample per entry of every list here,
12
+ * asserts the sample count equals the list length, and checks each sample against the shared
13
+ * scan; a new entry without a sample fails.
14
+ *
15
+ * @experimental
16
+ */
17
+ const isJsonObject = (value) => value !== null && Predicate.isObject(value) && !Array.isArray(value);
18
+ /**
19
+ * The credential field names of the shared port scan (`isPortCredentialKey`): `credential(s)`
20
+ * plus the credential field names, AWS-style `accessKeyId` / `secretAccessKey` / `sessionToken`
21
+ * included. One regular-expression source per name; matched whole and case-insensitively.
22
+ */
23
+ const r2CredentialKeyNames = [
24
+ "credentials?",
25
+ "access[_-]?token",
26
+ "refresh[_-]?token",
27
+ "id[_-]?token",
28
+ "auth[_-]?token",
29
+ "api[_-]?token",
30
+ "session[_-]?token",
31
+ "private[_-]?token",
32
+ "bearer[_-]?token",
33
+ "oauth[_-]?token",
34
+ "token",
35
+ "client[_-]?secret",
36
+ "secret(?:[_-]?key)?",
37
+ "private[_-]?key",
38
+ "password",
39
+ "passwd",
40
+ "api[_-]?key",
41
+ "access[_-]?key[_-]?id",
42
+ "secret[_-]?access[_-]?key",
43
+ "authorization"
44
+ ];
45
+ const credentialKeyPattern = new RegExp(`^(?:${r2CredentialKeyNames.join("|")})$`, "i");
46
+ /** True for a key whose whole value is a credential (dropped, never compared or recorded). */
47
+ const isR2CredentialKey = (key) => credentialKeyPattern.test(key);
48
+ /**
49
+ * The credential query or form parameter names of the shared port scan (`hasLiveCredentialParam`).
50
+ * One regular-expression source per name.
51
+ */
52
+ const r2CredentialParamNames = [
53
+ "api[_-]?key",
54
+ "key",
55
+ "token",
56
+ "access[_-]?token",
57
+ "refresh[_-]?token",
58
+ "id[_-]?token",
59
+ "auth",
60
+ "secret",
61
+ "password",
62
+ "client[_-]?secret",
63
+ "x-amz-signature",
64
+ "x-amz-credential",
65
+ "x-amz-security-token"
66
+ ];
67
+ const credentialParamAt = new RegExp(`(?:^|[?&])(?:${r2CredentialParamNames.join("|")})=[^&#]`, "i");
68
+ /**
69
+ * The shared scan's token patterns (`bearerPattern` and `apiKeyPatterns`): a bearer token, common
70
+ * API-key prefixes, JSON Web Tokens, and PEM private keys.
71
+ */
72
+ const r2TokenPatterns = [
73
+ /\bbearer\s+[A-Za-z0-9._~+/=-]{8,}/i,
74
+ /\bsk-[A-Za-z0-9_-]{16,}/,
75
+ /\b[sr]k_(live|test)_[A-Za-z0-9]{16,}/,
76
+ /\bxai-[A-Za-z0-9]{20,}/,
77
+ /\bvck_[A-Za-z0-9]{16,}/,
78
+ /\bgh[pousr]_[A-Za-z0-9]{20,}/,
79
+ /\bgithub_pat_[A-Za-z0-9_]{20,}/,
80
+ /\bAKIA[0-9A-Z]{16}\b/,
81
+ /\bAIza[0-9A-Za-z_-]{35}/,
82
+ /\bxox[abprs]-[A-Za-z0-9-]{10,}/,
83
+ /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/,
84
+ /-----BEGIN [A-Z ]*PRIVATE KEY-----/
85
+ ];
86
+ /** The canonical placeholders (`syntheticPortCredentialParams`), as the fixtures write them. */
87
+ const canonicalPlaceholders = new Map([["x-amz-signature", "yolk-synthetic-signature"], ["x-amz-credential", encodeURIComponent("yolk-synthetic-access-key-id/20260930/auto/s3/aws4_request")]]);
88
+ const canonicalNamesAt = /(^|[?&])(x-amz-signature|x-amz-credential)=/gi;
89
+ const canonicalValueEnd = /^(?:[&#\s"<>]|$)/;
90
+ const withoutCanonicalPlaceholders = (text) => {
91
+ let masked = text;
92
+ for (const match of text.matchAll(canonicalNamesAt)) {
93
+ const nameStart = match.index + (match[1] ?? "").length;
94
+ const valueStart = match.index + match[0].length;
95
+ const placeholder = canonicalPlaceholders.get((match[2] ?? "").toLowerCase()) ?? "";
96
+ const valueEnd = valueStart + placeholder.length;
97
+ if (placeholder.length > 0 && text.startsWith(placeholder, valueStart) && canonicalValueEnd.test(text.slice(valueEnd))) {
98
+ const blank = " ".repeat(valueEnd - nameStart);
99
+ masked = `${masked.slice(0, nameStart)}${blank}${masked.slice(valueEnd)}`;
100
+ }
101
+ }
102
+ return masked;
103
+ };
104
+ const percentDecodedOnce = (text) => text.replace(/%([0-9A-Fa-f]{2})/g, (_match, hex) => String.fromCharCode(Number.parseInt(hex, 16)));
105
+ const codePoint = (value) => String.fromCodePoint(Math.min(value, 1114111));
106
+ const escapesDecodedOnce = (text) => text.replace(/\\u([0-9A-Fa-f]{4})|\\x([0-9A-Fa-f]{2})|\\\\|&#[xX]([0-9A-Fa-f]{1,6});?|&#([0-9]{1,7});?/g, (match, unicode, hex, entityHex, entity) => {
107
+ const hexDigits = unicode ?? hex ?? entityHex;
108
+ if (hexDigits !== void 0) return codePoint(Number.parseInt(hexDigits, 16));
109
+ if (entity !== void 0) return codePoint(Number.parseInt(entity, 10));
110
+ return match === "\\\\" ? "\\" : match;
111
+ });
112
+ const maxRoundsPerDecoding = 3;
113
+ /** `text` and every variant within three percent rounds and three escape rounds, in any order. */
114
+ const decodedVariants = (text) => {
115
+ const seen = new Set([text]);
116
+ let frontier = [{
117
+ text,
118
+ percent: 0,
119
+ escapes: 0
120
+ }];
121
+ while (frontier.length > 0) frontier = frontier.flatMap((variant) => [...variant.percent < maxRoundsPerDecoding ? [{
122
+ ...variant,
123
+ text: percentDecodedOnce(variant.text),
124
+ percent: variant.percent + 1
125
+ }] : [], ...variant.escapes < maxRoundsPerDecoding ? [{
126
+ ...variant,
127
+ text: escapesDecodedOnce(variant.text),
128
+ escapes: variant.escapes + 1
129
+ }] : []].filter((candidate) => {
130
+ if (seen.has(candidate.text)) return false;
131
+ seen.add(candidate.text);
132
+ return true;
133
+ }));
134
+ return [...seen];
135
+ };
136
+ const sigV4CredentialNames = /x-amz-credential|x-amz-signature|x-amz-security-token/i;
137
+ /** True when `text` holds a SigV4 credential name, a credential parameter, or a token. */
138
+ const credentialText = (text) => sigV4CredentialNames.test(text) || credentialParamAt.test(text) || r2TokenPatterns.some((pattern) => pattern.test(text));
139
+ /**
140
+ * True when `text`, once its exact canonical placeholder occurrences are blanked out, holds a
141
+ * credential (`credentialText`) raw, in any variant within three percent and three escape rounds
142
+ * (the R2 guard's decodings), or anywhere in the fail-closed closure of `textClosureOutcome`
143
+ * (any depth of percent-encoding and JSON escaping; a work cap counts as a credential).
144
+ */
145
+ const carriesCredential = (text) => {
146
+ const masked = withoutCanonicalPlaceholders(text);
147
+ return decodedVariants(masked).some(credentialText) || textClosureOutcome(masked, credentialText) !== "clear";
148
+ };
149
+ /**
150
+ * True when `text` repeats a guarded value: raw, in any of R2's own decodings (`decodedVariants`:
151
+ * percent, `\uXXXX` of any code point, `\xXX`, numeric HTML references, in any order), or
152
+ * anywhere in the shared closure (a work cap counts as a repeat). Every non-empty value is
153
+ * guarded: unlike the shared `textRepeatsSecret`, there is no minimum length.
154
+ */
155
+ const repeatsGuardedValue = (text, guarded) => {
156
+ if (guarded.length === 0) return false;
157
+ const holds = (candidate) => guarded.some((value) => candidate.includes(value));
158
+ return decodedVariants(text).some(holds) || textClosureOutcome(text, holds) !== "clear";
159
+ };
160
+ /** True when `text` carries a credential or repeats a guarded value. */
161
+ const textCarriesCredential = (text, guarded) => carriesCredential(text) || repeatsGuardedValue(text, guarded);
162
+ const numericText = /^[+-]?(?:[0-9]+\.?[0-9]*|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$/;
163
+ /**
164
+ * The texts a number is matched by: as JavaScript prints it (`1e+21`, `12345678`) and, for an
165
+ * integer, its full digit string (`1000000000000000000000`).
166
+ */
167
+ const numberTexts = (value) => Number.isSafeInteger(value) || !Number.isInteger(value) ? [String(value)] : [String(value), BigInt(value).toString()];
168
+ /** A guarded value and, when it is numeric text (`0012345678`), the forms of its number. */
169
+ const guardedForms = (value) => {
170
+ if (value.length === 0) return [];
171
+ const number = Number(value);
172
+ return numericText.test(value) && Number.isFinite(number) ? [value, ...numberTexts(number)] : [value];
173
+ };
174
+ /** Every key, string, and number under a credential field, at any depth (the values to guard). */
175
+ const credentialSubtree = (value) => {
176
+ if (Predicate.isString(value)) return guardedForms(value);
177
+ if (Predicate.isNumber(value)) return numberTexts(value);
178
+ if (Array.isArray(value)) return value.flatMap(credentialSubtree);
179
+ return isJsonObject(value) ? Object.entries(value).flatMap(([key, item]) => [...guardedForms(key), ...credentialSubtree(item)]) : [];
180
+ };
181
+ /** The guarded values of every credential field of `value`, at any depth, deduplicated. */
182
+ const guardedValues = (value) => {
183
+ const collect = (item) => {
184
+ if (Array.isArray(item)) return item.flatMap(collect);
185
+ if (!isJsonObject(item)) return [];
186
+ return Object.entries(item).flatMap(([key, child]) => isR2CredentialKey(key) ? credentialSubtree(child) : collect(child));
187
+ };
188
+ return [...new Set(collect(value))];
189
+ };
190
+ /** True when `value` holds an own `__proto__` key at any depth (an admission bypass otherwise). */
191
+ const hasOwnProtoKey = (value) => {
192
+ if (Array.isArray(value)) return value.some(hasOwnProtoKey);
193
+ if (!isJsonObject(value)) return false;
194
+ return Object.hasOwn(value, "__proto__") || Object.values(value).some(hasOwnProtoKey);
195
+ };
196
+ const strippedOf = (value) => {
197
+ if (Array.isArray(value)) return value.map(strippedOf);
198
+ return isJsonObject(value) ? withoutCredentials(value) : value;
199
+ };
200
+ /**
201
+ * A copy without credential fields, at any depth (`redactPortPayload` of the shared scan), built
202
+ * with own data properties only (`Object.fromEntries`).
203
+ */
204
+ const withoutCredentials = (value) => Object.fromEntries(Object.entries(value).flatMap(([key, item]) => isR2CredentialKey(key) ? [] : [[key, strippedOf(item)]]));
205
+ /** Every object key, string value, and number (`numberTexts`) of `value`. */
206
+ const textsOf = (value) => {
207
+ if (Predicate.isString(value)) return [value];
208
+ if (Predicate.isNumber(value)) return numberTexts(value);
209
+ if (Array.isArray(value)) return value.flatMap(textsOf);
210
+ return isJsonObject(value) ? Object.entries(value).flatMap(([key, item]) => [key, ...textsOf(item)]) : [];
211
+ };
212
+ //#endregion
213
+ export { guardedValues, hasOwnProtoKey, isR2CredentialKey, numberTexts, r2CredentialKeyNames, r2CredentialParamNames, r2TokenPatterns, textCarriesCredential, textsOf, withoutCredentials };
214
+
215
+ //# sourceMappingURL=r2-guard.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"r2-guard.mjs","names":[],"sources":["../src/r2-guard.ts"],"sourcesContent":["/**\n * The credential guard of the R2 port emulator (internal; used by `r2.ts`, and imported by\n * `test/r2.test.ts` for the exhaustive parity tests).\n *\n * The copied lists here mirror `@yolk-sdk/conformance` (`isPortCredentialKey`, the credential query\n * parameter names of `hasLiveCredentialParam`, `bearerPattern`, and `apiKeyPatterns`) and the R2\n * conformance guard `findR2PortFixtureSecrets` (canonical placeholders and decodings). Emulators\n * never import SDK code, so `test/r2.test.ts` holds one sample per entry of every list here,\n * asserts the sample count equals the list length, and checks each sample against the shared\n * scan; a new entry without a sample fails.\n *\n * @experimental\n */\nimport { Predicate } from 'effect'\nimport type * as Schema from 'effect/Schema'\nimport { textClosureOutcome } from './stateful-secrets.ts'\n\ntype JsonObject = Schema.JsonObject\n\nconst isJsonObject = (value: Schema.Json): value is JsonObject =>\n value !== null && Predicate.isObject(value) && !Array.isArray(value)\n\n/**\n * The credential field names of the shared port scan (`isPortCredentialKey`): `credential(s)`\n * plus the credential field names, AWS-style `accessKeyId` / `secretAccessKey` / `sessionToken`\n * included. One regular-expression source per name; matched whole and case-insensitively.\n */\nexport const r2CredentialKeyNames: ReadonlyArray<string> = [\n 'credentials?',\n 'access[_-]?token',\n 'refresh[_-]?token',\n 'id[_-]?token',\n 'auth[_-]?token',\n 'api[_-]?token',\n 'session[_-]?token',\n 'private[_-]?token',\n 'bearer[_-]?token',\n 'oauth[_-]?token',\n 'token',\n 'client[_-]?secret',\n 'secret(?:[_-]?key)?',\n 'private[_-]?key',\n 'password',\n 'passwd',\n 'api[_-]?key',\n 'access[_-]?key[_-]?id',\n 'secret[_-]?access[_-]?key',\n 'authorization'\n]\n\nconst credentialKeyPattern = new RegExp(`^(?:${r2CredentialKeyNames.join('|')})$`, 'i')\n\n/** True for a key whose whole value is a credential (dropped, never compared or recorded). */\nexport const isR2CredentialKey = (key: string): boolean => credentialKeyPattern.test(key)\n\n/**\n * The credential query or form parameter names of the shared port scan (`hasLiveCredentialParam`).\n * One regular-expression source per name.\n */\nexport const r2CredentialParamNames: ReadonlyArray<string> = [\n 'api[_-]?key',\n 'key',\n 'token',\n 'access[_-]?token',\n 'refresh[_-]?token',\n 'id[_-]?token',\n 'auth',\n 'secret',\n 'password',\n 'client[_-]?secret',\n 'x-amz-signature',\n 'x-amz-credential',\n 'x-amz-security-token'\n]\n\n// A credential parameter: a name at the start or after `?` / `&`, `=`, and a value (any character\n// but `&` or `#`).\nconst credentialParamAt = new RegExp(`(?:^|[?&])(?:${r2CredentialParamNames.join('|')})=[^&#]`, 'i')\n\n/**\n * The shared scan's token patterns (`bearerPattern` and `apiKeyPatterns`): a bearer token, common\n * API-key prefixes, JSON Web Tokens, and PEM private keys.\n */\nexport const r2TokenPatterns: ReadonlyArray<RegExp> = [\n /\\bbearer\\s+[A-Za-z0-9._~+/=-]{8,}/i,\n /\\bsk-[A-Za-z0-9_-]{16,}/,\n /\\b[sr]k_(live|test)_[A-Za-z0-9]{16,}/,\n /\\bxai-[A-Za-z0-9]{20,}/,\n /\\bvck_[A-Za-z0-9]{16,}/,\n /\\bgh[pousr]_[A-Za-z0-9]{20,}/,\n /\\bgithub_pat_[A-Za-z0-9_]{20,}/,\n /\\bAKIA[0-9A-Z]{16}\\b/,\n /\\bAIza[0-9A-Za-z_-]{35}/,\n /\\bxox[abprs]-[A-Za-z0-9-]{10,}/,\n /\\beyJ[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}/,\n /-----BEGIN [A-Z ]*PRIVATE KEY-----/\n]\n\n// The SigV4 rules of the R2 conformance guard (`findR2PortFixtureSecrets` in\n// `@yolk-sdk/connectors/r2-storage/conformance`), copied: the exact canonical placeholder\n// occurrences are blanked out, then any credential name left in any decoding layer is refused.\n\n/** The canonical placeholders (`syntheticPortCredentialParams`), as the fixtures write them. */\nconst canonicalPlaceholders = new Map<string, string>([\n ['x-amz-signature', 'yolk-synthetic-signature'],\n [\n 'x-amz-credential',\n encodeURIComponent('yolk-synthetic-access-key-id/20260930/auto/s3/aws4_request')\n ]\n])\n\nconst canonicalNamesAt = /(^|[?&])(x-amz-signature|x-amz-credential)=/gi\n\nconst canonicalValueEnd = /^(?:[&#\\s\"<>]|$)/\n\nconst withoutCanonicalPlaceholders = (text: string): string => {\n let masked = text\n\n for (const match of text.matchAll(canonicalNamesAt)) {\n const nameStart = match.index + (match[1] ?? '').length\n const valueStart = match.index + match[0].length\n const placeholder = canonicalPlaceholders.get((match[2] ?? '').toLowerCase()) ?? ''\n const valueEnd = valueStart + placeholder.length\n\n if (\n placeholder.length > 0 &&\n text.startsWith(placeholder, valueStart) &&\n canonicalValueEnd.test(text.slice(valueEnd))\n ) {\n const blank = ' '.repeat(valueEnd - nameStart)\n\n masked = `${masked.slice(0, nameStart)}${blank}${masked.slice(valueEnd)}`\n }\n }\n\n return masked\n}\n\nconst percentDecodedOnce = (text: string): string =>\n text.replace(/%([0-9A-Fa-f]{2})/g, (_match, hex: string) =>\n String.fromCharCode(Number.parseInt(hex, 16))\n )\n\nconst codePoint = (value: number): string => String.fromCodePoint(Math.min(value, 0x10ffff))\n\nconst escapesDecodedOnce = (text: string): string =>\n text.replace(\n /\\\\u([0-9A-Fa-f]{4})|\\\\x([0-9A-Fa-f]{2})|\\\\\\\\|&#[xX]([0-9A-Fa-f]{1,6});?|&#([0-9]{1,7});?/g,\n (match, unicode?: string, hex?: string, entityHex?: string, entity?: string) => {\n const hexDigits = unicode ?? hex ?? entityHex\n\n if (hexDigits !== undefined) return codePoint(Number.parseInt(hexDigits, 16))\n\n if (entity !== undefined) return codePoint(Number.parseInt(entity, 10))\n\n return match === '\\\\\\\\' ? '\\\\' : match\n }\n )\n\nconst maxRoundsPerDecoding = 3\n\n/** `text` and every variant within three percent rounds and three escape rounds, in any order. */\nconst decodedVariants = (text: string): ReadonlyArray<string> => {\n const seen = new Set<string>([text])\n\n let frontier: ReadonlyArray<{ text: string; percent: number; escapes: number }> = [\n { text, percent: 0, escapes: 0 }\n ]\n\n while (frontier.length > 0) {\n frontier = frontier.flatMap(variant =>\n [\n ...(variant.percent < maxRoundsPerDecoding\n ? [{ ...variant, text: percentDecodedOnce(variant.text), percent: variant.percent + 1 }]\n : []),\n ...(variant.escapes < maxRoundsPerDecoding\n ? [{ ...variant, text: escapesDecodedOnce(variant.text), escapes: variant.escapes + 1 }]\n : [])\n ].filter(candidate => {\n if (seen.has(candidate.text)) return false\n\n seen.add(candidate.text)\n\n return true\n })\n )\n }\n\n return [...seen]\n}\n\nconst sigV4CredentialNames = /x-amz-credential|x-amz-signature|x-amz-security-token/i\n\n/** True when `text` holds a SigV4 credential name, a credential parameter, or a token. */\nconst credentialText = (text: string): boolean =>\n sigV4CredentialNames.test(text) ||\n credentialParamAt.test(text) ||\n r2TokenPatterns.some(pattern => pattern.test(text))\n\n/**\n * True when `text`, once its exact canonical placeholder occurrences are blanked out, holds a\n * credential (`credentialText`) raw, in any variant within three percent and three escape rounds\n * (the R2 guard's decodings), or anywhere in the fail-closed closure of `textClosureOutcome`\n * (any depth of percent-encoding and JSON escaping; a work cap counts as a credential).\n */\nconst carriesCredential = (text: string): boolean => {\n const masked = withoutCanonicalPlaceholders(text)\n\n return (\n decodedVariants(masked).some(credentialText) ||\n textClosureOutcome(masked, credentialText) !== 'clear'\n )\n}\n\n/**\n * True when `text` repeats a guarded value: raw, in any of R2's own decodings (`decodedVariants`:\n * percent, `\\uXXXX` of any code point, `\\xXX`, numeric HTML references, in any order), or\n * anywhere in the shared closure (a work cap counts as a repeat). Every non-empty value is\n * guarded: unlike the shared `textRepeatsSecret`, there is no minimum length.\n */\nconst repeatsGuardedValue = (text: string, guarded: ReadonlyArray<string>): boolean => {\n if (guarded.length === 0) return false\n\n const holds = (candidate: string) => guarded.some(value => candidate.includes(value))\n\n return decodedVariants(text).some(holds) || textClosureOutcome(text, holds) !== 'clear'\n}\n\n/** True when `text` carries a credential or repeats a guarded value. */\nexport const textCarriesCredential = (text: string, guarded: ReadonlyArray<string>): boolean =>\n carriesCredential(text) || repeatsGuardedValue(text, guarded)\n\nconst numericText = /^[+-]?(?:[0-9]+\\.?[0-9]*|\\.[0-9]+)(?:[eE][+-]?[0-9]+)?$/\n\n/**\n * The texts a number is matched by: as JavaScript prints it (`1e+21`, `12345678`) and, for an\n * integer, its full digit string (`1000000000000000000000`).\n */\nexport const numberTexts = (value: number): ReadonlyArray<string> =>\n Number.isSafeInteger(value) || !Number.isInteger(value)\n ? [String(value)]\n : [String(value), BigInt(value).toString()]\n\n/** A guarded value and, when it is numeric text (`0012345678`), the forms of its number. */\nconst guardedForms = (value: string): ReadonlyArray<string> => {\n if (value.length === 0) return []\n\n const number = Number(value)\n\n return numericText.test(value) && Number.isFinite(number)\n ? [value, ...numberTexts(number)]\n : [value]\n}\n\n/** Every key, string, and number under a credential field, at any depth (the values to guard). */\nconst credentialSubtree = (value: Schema.Json): ReadonlyArray<string> => {\n if (Predicate.isString(value)) return guardedForms(value)\n\n if (Predicate.isNumber(value)) return numberTexts(value)\n\n if (Array.isArray(value)) return value.flatMap(credentialSubtree)\n\n return isJsonObject(value)\n ? Object.entries(value).flatMap(([key, item]) => [\n ...guardedForms(key),\n ...credentialSubtree(item)\n ])\n : []\n}\n\n/** The guarded values of every credential field of `value`, at any depth, deduplicated. */\nexport const guardedValues = (value: Schema.Json): ReadonlyArray<string> => {\n const collect = (item: Schema.Json): ReadonlyArray<string> => {\n if (Array.isArray(item)) return item.flatMap(collect)\n\n if (!isJsonObject(item)) return []\n\n return Object.entries(item).flatMap(([key, child]) =>\n isR2CredentialKey(key) ? credentialSubtree(child) : collect(child)\n )\n }\n\n return [...new Set(collect(value))]\n}\n\n/** True when `value` holds an own `__proto__` key at any depth (an admission bypass otherwise). */\nexport const hasOwnProtoKey = (value: Schema.Json): boolean => {\n if (Array.isArray(value)) return value.some(hasOwnProtoKey)\n\n if (!isJsonObject(value)) return false\n\n return Object.hasOwn(value, '__proto__') || Object.values(value).some(hasOwnProtoKey)\n}\n\nconst strippedOf = (value: Schema.Json): Schema.Json => {\n if (Array.isArray(value)) return value.map(strippedOf)\n\n return isJsonObject(value) ? withoutCredentials(value) : value\n}\n\n/**\n * A copy without credential fields, at any depth (`redactPortPayload` of the shared scan), built\n * with own data properties only (`Object.fromEntries`).\n */\nexport const withoutCredentials = (value: JsonObject): JsonObject =>\n Object.fromEntries(\n Object.entries(value).flatMap(([key, item]) =>\n isR2CredentialKey(key) ? [] : [[key, strippedOf(item)]]\n )\n )\n\n/** Every object key, string value, and number (`numberTexts`) of `value`. */\nexport const textsOf = (value: Schema.Json): ReadonlyArray<string> => {\n if (Predicate.isString(value)) return [value]\n\n if (Predicate.isNumber(value)) return numberTexts(value)\n\n if (Array.isArray(value)) return value.flatMap(textsOf)\n\n return isJsonObject(value)\n ? Object.entries(value).flatMap(([key, item]) => [key, ...textsOf(item)])\n : []\n}\n"],"mappings":"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"}