@aws-blocks/core 0.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 (394) hide show
  1. package/LICENSE +174 -0
  2. package/dist/api.d.ts +147 -0
  3. package/dist/api.d.ts.map +1 -0
  4. package/dist/api.js +10 -0
  5. package/dist/api.test.d.ts +2 -0
  6. package/dist/api.test.d.ts.map +1 -0
  7. package/dist/api.test.js +19 -0
  8. package/dist/bb-utils.d.ts +11 -0
  9. package/dist/bb-utils.d.ts.map +1 -0
  10. package/dist/bb-utils.js +12 -0
  11. package/dist/builtin-routes.d.ts +6 -0
  12. package/dist/builtin-routes.d.ts.map +1 -0
  13. package/dist/builtin-routes.js +57 -0
  14. package/dist/cdk/__fixtures__/factory-backend.d.ts +3 -0
  15. package/dist/cdk/__fixtures__/factory-backend.d.ts.map +1 -0
  16. package/dist/cdk/__fixtures__/factory-backend.js +6 -0
  17. package/dist/cdk/__fixtures__/fullid-construct-backend.d.ts +2 -0
  18. package/dist/cdk/__fixtures__/fullid-construct-backend.d.ts.map +1 -0
  19. package/dist/cdk/__fixtures__/fullid-construct-backend.js +37 -0
  20. package/dist/cdk/__fixtures__/handler.d.ts +5 -0
  21. package/dist/cdk/__fixtures__/handler.d.ts.map +1 -0
  22. package/dist/cdk/__fixtures__/handler.js +3 -0
  23. package/dist/cdk/__fixtures__/side-effect-backend.d.ts +2 -0
  24. package/dist/cdk/__fixtures__/side-effect-backend.d.ts.map +1 -0
  25. package/dist/cdk/__fixtures__/side-effect-backend.js +5 -0
  26. package/dist/cdk/blocks-backend.d.ts +68 -0
  27. package/dist/cdk/blocks-backend.d.ts.map +1 -0
  28. package/dist/cdk/blocks-backend.js +214 -0
  29. package/dist/cdk/blocks-backend.test.d.ts +2 -0
  30. package/dist/cdk/blocks-backend.test.d.ts.map +1 -0
  31. package/dist/cdk/blocks-backend.test.js +151 -0
  32. package/dist/cdk/blocks-stack.test.d.ts +2 -0
  33. package/dist/cdk/blocks-stack.test.d.ts.map +1 -0
  34. package/dist/cdk/blocks-stack.test.js +78 -0
  35. package/dist/cdk/config-registry.d.ts +26 -0
  36. package/dist/cdk/config-registry.d.ts.map +1 -0
  37. package/dist/cdk/config-registry.js +80 -0
  38. package/dist/cdk/index.d.ts +35 -0
  39. package/dist/cdk/index.d.ts.map +1 -0
  40. package/dist/cdk/index.js +90 -0
  41. package/dist/cdk/mixins.d.ts +28 -0
  42. package/dist/cdk/mixins.d.ts.map +1 -0
  43. package/dist/cdk/mixins.js +37 -0
  44. package/dist/cdk/mixins.test.d.ts +2 -0
  45. package/dist/cdk/mixins.test.d.ts.map +1 -0
  46. package/dist/cdk/mixins.test.js +108 -0
  47. package/dist/cdk/node-version.d.ts +14 -0
  48. package/dist/cdk/node-version.d.ts.map +1 -0
  49. package/dist/cdk/node-version.js +15 -0
  50. package/dist/cdk/stack-metadata.d.ts +12 -0
  51. package/dist/cdk/stack-metadata.d.ts.map +1 -0
  52. package/dist/cdk/stack-metadata.js +22 -0
  53. package/dist/cdk/stack-metadata.test.d.ts +2 -0
  54. package/dist/cdk/stack-metadata.test.d.ts.map +1 -0
  55. package/dist/cdk/stack-metadata.test.js +56 -0
  56. package/dist/cdk/synth-guard.d.ts +14 -0
  57. package/dist/cdk/synth-guard.d.ts.map +1 -0
  58. package/dist/cdk/synth-guard.js +20 -0
  59. package/dist/cdk/synth-guard.test.d.ts +2 -0
  60. package/dist/cdk/synth-guard.test.d.ts.map +1 -0
  61. package/dist/cdk/synth-guard.test.js +15 -0
  62. package/dist/client/index.d.ts +97 -0
  63. package/dist/client/index.d.ts.map +1 -0
  64. package/dist/client/index.js +238 -0
  65. package/dist/client/index.test.d.ts +2 -0
  66. package/dist/client/index.test.d.ts.map +1 -0
  67. package/dist/client/index.test.js +113 -0
  68. package/dist/common/config.d.ts +45 -0
  69. package/dist/common/config.d.ts.map +1 -0
  70. package/dist/common/config.js +154 -0
  71. package/dist/common/config.test.d.ts +2 -0
  72. package/dist/common/config.test.d.ts.map +1 -0
  73. package/dist/common/config.test.js +173 -0
  74. package/dist/common/constants.d.ts +11 -0
  75. package/dist/common/constants.d.ts.map +1 -0
  76. package/dist/common/constants.js +12 -0
  77. package/dist/common/crypto.d.ts +6 -0
  78. package/dist/common/crypto.d.ts.map +1 -0
  79. package/dist/common/crypto.js +14 -0
  80. package/dist/common/index.d.ts +220 -0
  81. package/dist/common/index.d.ts.map +1 -0
  82. package/dist/common/index.js +180 -0
  83. package/dist/common/index.test.d.ts +2 -0
  84. package/dist/common/index.test.d.ts.map +1 -0
  85. package/dist/common/index.test.js +195 -0
  86. package/dist/common/mock-data.d.ts +40 -0
  87. package/dist/common/mock-data.d.ts.map +1 -0
  88. package/dist/common/mock-data.js +39 -0
  89. package/dist/common/official-bb-names.generated.d.ts +7 -0
  90. package/dist/common/official-bb-names.generated.d.ts.map +1 -0
  91. package/dist/common/official-bb-names.generated.js +28 -0
  92. package/dist/common/sdk-registry.d.ts +136 -0
  93. package/dist/common/sdk-registry.d.ts.map +1 -0
  94. package/dist/common/sdk-registry.js +125 -0
  95. package/dist/constants.d.ts +27 -0
  96. package/dist/constants.d.ts.map +1 -0
  97. package/dist/constants.js +28 -0
  98. package/dist/cors.d.ts +39 -0
  99. package/dist/cors.d.ts.map +1 -0
  100. package/dist/cors.js +85 -0
  101. package/dist/cors.test.d.ts +2 -0
  102. package/dist/cors.test.d.ts.map +1 -0
  103. package/dist/cors.test.js +241 -0
  104. package/dist/db-naming.d.ts +21 -0
  105. package/dist/db-naming.d.ts.map +1 -0
  106. package/dist/db-naming.js +38 -0
  107. package/dist/db-naming.test.d.ts +2 -0
  108. package/dist/db-naming.test.d.ts.map +1 -0
  109. package/dist/db-naming.test.js +29 -0
  110. package/dist/errors.d.ts +67 -0
  111. package/dist/errors.d.ts.map +1 -0
  112. package/dist/errors.js +69 -0
  113. package/dist/hosting.d.ts +258 -0
  114. package/dist/hosting.d.ts.map +1 -0
  115. package/dist/hosting.js +340 -0
  116. package/dist/hosting.test.d.ts +2 -0
  117. package/dist/hosting.test.d.ts.map +1 -0
  118. package/dist/hosting.test.js +1077 -0
  119. package/dist/index.cdk.d.ts +13 -0
  120. package/dist/index.cdk.d.ts.map +1 -0
  121. package/dist/index.cdk.js +13 -0
  122. package/dist/index.d.ts +9 -0
  123. package/dist/index.d.ts.map +1 -0
  124. package/dist/index.js +10 -0
  125. package/dist/lambda-handler.d.ts +131 -0
  126. package/dist/lambda-handler.d.ts.map +1 -0
  127. package/dist/lambda-handler.js +610 -0
  128. package/dist/lambda-handler.test.d.ts +2 -0
  129. package/dist/lambda-handler.test.d.ts.map +1 -0
  130. package/dist/lambda-handler.test.js +835 -0
  131. package/dist/pipeline/index.d.ts +4 -0
  132. package/dist/pipeline/index.d.ts.map +1 -0
  133. package/dist/pipeline/index.js +3 -0
  134. package/dist/pipeline/pipeline-construct.d.ts +130 -0
  135. package/dist/pipeline/pipeline-construct.d.ts.map +1 -0
  136. package/dist/pipeline/pipeline-construct.js +484 -0
  137. package/dist/pipeline/pipeline.test.d.ts +2 -0
  138. package/dist/pipeline/pipeline.test.d.ts.map +1 -0
  139. package/dist/pipeline/pipeline.test.js +1231 -0
  140. package/dist/pipeline/types.d.ts +351 -0
  141. package/dist/pipeline/types.d.ts.map +1 -0
  142. package/dist/pipeline/types.js +3 -0
  143. package/dist/raw-route.cdk.d.ts +47 -0
  144. package/dist/raw-route.cdk.d.ts.map +1 -0
  145. package/dist/raw-route.cdk.js +51 -0
  146. package/dist/raw-route.d.ts +119 -0
  147. package/dist/raw-route.d.ts.map +1 -0
  148. package/dist/raw-route.js +239 -0
  149. package/dist/raw-route.mock.d.ts +47 -0
  150. package/dist/raw-route.mock.d.ts.map +1 -0
  151. package/dist/raw-route.mock.js +51 -0
  152. package/dist/raw-route.test.d.ts +2 -0
  153. package/dist/raw-route.test.d.ts.map +1 -0
  154. package/dist/raw-route.test.js +597 -0
  155. package/dist/redact.d.ts +51 -0
  156. package/dist/redact.d.ts.map +1 -0
  157. package/dist/redact.js +134 -0
  158. package/dist/redact.test.d.ts +2 -0
  159. package/dist/redact.test.d.ts.map +1 -0
  160. package/dist/redact.test.js +199 -0
  161. package/dist/rpc.d.ts +54 -0
  162. package/dist/rpc.d.ts.map +1 -0
  163. package/dist/rpc.js +127 -0
  164. package/dist/rpc.test.d.ts +2 -0
  165. package/dist/rpc.test.d.ts.map +1 -0
  166. package/dist/rpc.test.js +60 -0
  167. package/dist/scripts/cdk-telemetry-env.d.ts +13 -0
  168. package/dist/scripts/cdk-telemetry-env.d.ts.map +1 -0
  169. package/dist/scripts/cdk-telemetry-env.js +21 -0
  170. package/dist/scripts/cdk-telemetry-env.test.d.ts +2 -0
  171. package/dist/scripts/cdk-telemetry-env.test.d.ts.map +1 -0
  172. package/dist/scripts/cdk-telemetry-env.test.js +56 -0
  173. package/dist/scripts/cleanup.d.ts +3 -0
  174. package/dist/scripts/cleanup.d.ts.map +1 -0
  175. package/dist/scripts/cleanup.js +35 -0
  176. package/dist/scripts/console.d.ts +6 -0
  177. package/dist/scripts/console.d.ts.map +1 -0
  178. package/dist/scripts/console.js +25 -0
  179. package/dist/scripts/deploy.d.ts +6 -0
  180. package/dist/scripts/deploy.d.ts.map +1 -0
  181. package/dist/scripts/deploy.js +82 -0
  182. package/dist/scripts/destroy.d.ts +6 -0
  183. package/dist/scripts/destroy.d.ts.map +1 -0
  184. package/dist/scripts/destroy.js +30 -0
  185. package/dist/scripts/dev-server-cors.test.d.ts +2 -0
  186. package/dist/scripts/dev-server-cors.test.d.ts.map +1 -0
  187. package/dist/scripts/dev-server-cors.test.js +23 -0
  188. package/dist/scripts/dev-server-telemetry.test.d.ts +2 -0
  189. package/dist/scripts/dev-server-telemetry.test.d.ts.map +1 -0
  190. package/dist/scripts/dev-server-telemetry.test.js +113 -0
  191. package/dist/scripts/dev-server.d.ts +21 -0
  192. package/dist/scripts/dev-server.d.ts.map +1 -0
  193. package/dist/scripts/dev-server.js +456 -0
  194. package/dist/scripts/ensure-secrets.d.ts +31 -0
  195. package/dist/scripts/ensure-secrets.d.ts.map +1 -0
  196. package/dist/scripts/ensure-secrets.js +100 -0
  197. package/dist/scripts/ensure-secrets.test.d.ts +2 -0
  198. package/dist/scripts/ensure-secrets.test.d.ts.map +1 -0
  199. package/dist/scripts/ensure-secrets.test.js +44 -0
  200. package/dist/scripts/external-migrations-step.d.ts +62 -0
  201. package/dist/scripts/external-migrations-step.d.ts.map +1 -0
  202. package/dist/scripts/external-migrations-step.js +243 -0
  203. package/dist/scripts/external-migrations-step.test.d.ts +2 -0
  204. package/dist/scripts/external-migrations-step.test.d.ts.map +1 -0
  205. package/dist/scripts/external-migrations-step.test.js +47 -0
  206. package/dist/scripts/extract-ts-types.d.ts +65 -0
  207. package/dist/scripts/extract-ts-types.d.ts.map +1 -0
  208. package/dist/scripts/extract-ts-types.js +664 -0
  209. package/dist/scripts/extract-ts-types.test.d.ts +2 -0
  210. package/dist/scripts/extract-ts-types.test.d.ts.map +1 -0
  211. package/dist/scripts/extract-ts-types.test.js +497 -0
  212. package/dist/scripts/generate-client-worker.d.ts +2 -0
  213. package/dist/scripts/generate-client-worker.d.ts.map +1 -0
  214. package/dist/scripts/generate-client-worker.js +13 -0
  215. package/dist/scripts/generate-client.d.ts +14 -0
  216. package/dist/scripts/generate-client.d.ts.map +1 -0
  217. package/dist/scripts/generate-client.js +73 -0
  218. package/dist/scripts/generate-client.test.d.ts +2 -0
  219. package/dist/scripts/generate-client.test.d.ts.map +1 -0
  220. package/dist/scripts/generate-client.test.js +48 -0
  221. package/dist/scripts/generate-spec-cli.d.ts +3 -0
  222. package/dist/scripts/generate-spec-cli.d.ts.map +1 -0
  223. package/dist/scripts/generate-spec-cli.js +77 -0
  224. package/dist/scripts/generate-spec-cli.test.d.ts +14 -0
  225. package/dist/scripts/generate-spec-cli.test.d.ts.map +1 -0
  226. package/dist/scripts/generate-spec-cli.test.js +90 -0
  227. package/dist/scripts/generate-spec.d.ts +61 -0
  228. package/dist/scripts/generate-spec.d.ts.map +1 -0
  229. package/dist/scripts/generate-spec.js +746 -0
  230. package/dist/scripts/generate-spec.test.d.ts +12 -0
  231. package/dist/scripts/generate-spec.test.d.ts.map +1 -0
  232. package/dist/scripts/generate-spec.test.js +88 -0
  233. package/dist/scripts/index.d.ts +12 -0
  234. package/dist/scripts/index.d.ts.map +1 -0
  235. package/dist/scripts/index.js +13 -0
  236. package/dist/scripts/sandbox.d.ts +11 -0
  237. package/dist/scripts/sandbox.d.ts.map +1 -0
  238. package/dist/scripts/sandbox.js +200 -0
  239. package/dist/scripts/telemetry-cli.d.ts +3 -0
  240. package/dist/scripts/telemetry-cli.d.ts.map +1 -0
  241. package/dist/scripts/telemetry-cli.js +8 -0
  242. package/dist/scripts/telemetry.d.ts +17 -0
  243. package/dist/scripts/telemetry.d.ts.map +1 -0
  244. package/dist/scripts/telemetry.js +150 -0
  245. package/dist/scripts/telemetry.test.d.ts +2 -0
  246. package/dist/scripts/telemetry.test.d.ts.map +1 -0
  247. package/dist/scripts/telemetry.test.js +203 -0
  248. package/dist/scripts/validate-spec.d.ts +22 -0
  249. package/dist/scripts/validate-spec.d.ts.map +1 -0
  250. package/dist/scripts/validate-spec.js +159 -0
  251. package/dist/server/index.d.ts +3 -0
  252. package/dist/server/index.d.ts.map +1 -0
  253. package/dist/server/index.js +3 -0
  254. package/dist/server/withAuth.d.ts +142 -0
  255. package/dist/server/withAuth.d.ts.map +1 -0
  256. package/dist/server/withAuth.js +261 -0
  257. package/dist/server/withAuth.test.d.ts +2 -0
  258. package/dist/server/withAuth.test.d.ts.map +1 -0
  259. package/dist/server/withAuth.test.js +278 -0
  260. package/dist/telemetry/client.d.ts +59 -0
  261. package/dist/telemetry/client.d.ts.map +1 -0
  262. package/dist/telemetry/client.js +215 -0
  263. package/dist/telemetry/config-writer.d.ts +16 -0
  264. package/dist/telemetry/config-writer.d.ts.map +1 -0
  265. package/dist/telemetry/config-writer.js +36 -0
  266. package/dist/telemetry/consent-commands.test.d.ts +2 -0
  267. package/dist/telemetry/consent-commands.test.d.ts.map +1 -0
  268. package/dist/telemetry/consent-commands.test.js +161 -0
  269. package/dist/telemetry/consent.d.ts +60 -0
  270. package/dist/telemetry/consent.d.ts.map +1 -0
  271. package/dist/telemetry/consent.js +122 -0
  272. package/dist/telemetry/environment.d.ts +33 -0
  273. package/dist/telemetry/environment.d.ts.map +1 -0
  274. package/dist/telemetry/environment.js +87 -0
  275. package/dist/telemetry/identifiers.d.ts +20 -0
  276. package/dist/telemetry/identifiers.d.ts.map +1 -0
  277. package/dist/telemetry/identifiers.js +86 -0
  278. package/dist/telemetry/index.d.ts +6 -0
  279. package/dist/telemetry/index.d.ts.map +1 -0
  280. package/dist/telemetry/index.js +4 -0
  281. package/dist/telemetry/telemetry.test.d.ts +2 -0
  282. package/dist/telemetry/telemetry.test.d.ts.map +1 -0
  283. package/dist/telemetry/telemetry.test.js +1131 -0
  284. package/dist/telemetry/trackCommand.d.ts +32 -0
  285. package/dist/telemetry/trackCommand.d.ts.map +1 -0
  286. package/dist/telemetry/trackCommand.js +102 -0
  287. package/dist/telemetry/types.d.ts +92 -0
  288. package/dist/telemetry/types.d.ts.map +1 -0
  289. package/dist/telemetry/types.js +1 -0
  290. package/dist/types.d.ts +24 -0
  291. package/dist/types.d.ts.map +1 -0
  292. package/dist/types.js +3 -0
  293. package/dist/version.d.ts +2 -0
  294. package/dist/version.d.ts.map +1 -0
  295. package/dist/version.js +2 -0
  296. package/package.json +83 -0
  297. package/src/api.test.ts +25 -0
  298. package/src/api.ts +163 -0
  299. package/src/bb-utils.ts +13 -0
  300. package/src/builtin-routes.ts +62 -0
  301. package/src/cdk/__fixtures__/factory-backend.ts +8 -0
  302. package/src/cdk/__fixtures__/fullid-construct-backend.ts +42 -0
  303. package/src/cdk/__fixtures__/handler.ts +4 -0
  304. package/src/cdk/__fixtures__/side-effect-backend.ts +7 -0
  305. package/src/cdk/blocks-backend.test.ts +207 -0
  306. package/src/cdk/blocks-backend.ts +249 -0
  307. package/src/cdk/blocks-stack.test.ts +106 -0
  308. package/src/cdk/config-registry.ts +106 -0
  309. package/src/cdk/index.ts +115 -0
  310. package/src/cdk/mixins.test.ts +129 -0
  311. package/src/cdk/mixins.ts +41 -0
  312. package/src/cdk/node-version.ts +17 -0
  313. package/src/cdk/stack-metadata.test.ts +71 -0
  314. package/src/cdk/stack-metadata.ts +29 -0
  315. package/src/cdk/synth-guard.test.ts +21 -0
  316. package/src/cdk/synth-guard.ts +23 -0
  317. package/src/client/index.test.ts +118 -0
  318. package/src/client/index.ts +320 -0
  319. package/src/common/config.test.ts +236 -0
  320. package/src/common/config.ts +169 -0
  321. package/src/common/constants.ts +15 -0
  322. package/src/common/crypto.ts +15 -0
  323. package/src/common/index.test.ts +242 -0
  324. package/src/common/index.ts +335 -0
  325. package/src/common/mock-data.ts +52 -0
  326. package/src/common/official-bb-names.generated.ts +29 -0
  327. package/src/common/sdk-registry.ts +150 -0
  328. package/src/constants.ts +31 -0
  329. package/src/cors.test.ts +279 -0
  330. package/src/cors.ts +91 -0
  331. package/src/db-naming.test.ts +48 -0
  332. package/src/db-naming.ts +41 -0
  333. package/src/errors.ts +72 -0
  334. package/src/hosting.test.ts +1431 -0
  335. package/src/hosting.ts +626 -0
  336. package/src/index.cdk.ts +45 -0
  337. package/src/index.ts +22 -0
  338. package/src/lambda-handler.test.ts +1005 -0
  339. package/src/lambda-handler.ts +706 -0
  340. package/src/pipeline/index.ts +12 -0
  341. package/src/pipeline/pipeline-construct.ts +662 -0
  342. package/src/pipeline/pipeline.test.ts +1565 -0
  343. package/src/pipeline/types.ts +384 -0
  344. package/src/raw-route.cdk.ts +56 -0
  345. package/src/raw-route.mock.ts +56 -0
  346. package/src/raw-route.test.ts +819 -0
  347. package/src/raw-route.ts +316 -0
  348. package/src/redact.test.ts +222 -0
  349. package/src/redact.ts +143 -0
  350. package/src/rpc.test.ts +73 -0
  351. package/src/rpc.ts +174 -0
  352. package/src/scripts/cdk-telemetry-env.test.ts +75 -0
  353. package/src/scripts/cdk-telemetry-env.ts +23 -0
  354. package/src/scripts/cleanup.ts +39 -0
  355. package/src/scripts/console.ts +34 -0
  356. package/src/scripts/deploy.ts +106 -0
  357. package/src/scripts/destroy.ts +42 -0
  358. package/src/scripts/dev-server-cors.test.ts +28 -0
  359. package/src/scripts/dev-server-telemetry.test.ts +127 -0
  360. package/src/scripts/dev-server.ts +515 -0
  361. package/src/scripts/ensure-secrets.test.ts +50 -0
  362. package/src/scripts/ensure-secrets.ts +113 -0
  363. package/src/scripts/external-migrations-step.test.ts +64 -0
  364. package/src/scripts/external-migrations-step.ts +269 -0
  365. package/src/scripts/extract-ts-types.test.ts +516 -0
  366. package/src/scripts/extract-ts-types.ts +773 -0
  367. package/src/scripts/generate-client-worker.ts +15 -0
  368. package/src/scripts/generate-client.test.ts +69 -0
  369. package/src/scripts/generate-client.ts +86 -0
  370. package/src/scripts/generate-spec-cli.test.ts +106 -0
  371. package/src/scripts/generate-spec-cli.ts +92 -0
  372. package/src/scripts/generate-spec.test.ts +99 -0
  373. package/src/scripts/generate-spec.ts +833 -0
  374. package/src/scripts/index.ts +21 -0
  375. package/src/scripts/sandbox.ts +230 -0
  376. package/src/scripts/telemetry-cli.ts +10 -0
  377. package/src/scripts/telemetry.test.ts +239 -0
  378. package/src/scripts/telemetry.ts +173 -0
  379. package/src/scripts/validate-spec.ts +199 -0
  380. package/src/server/index.ts +5 -0
  381. package/src/server/withAuth.test.ts +348 -0
  382. package/src/server/withAuth.ts +323 -0
  383. package/src/telemetry/client.ts +226 -0
  384. package/src/telemetry/config-writer.ts +52 -0
  385. package/src/telemetry/consent-commands.test.ts +196 -0
  386. package/src/telemetry/consent.ts +154 -0
  387. package/src/telemetry/environment.ts +100 -0
  388. package/src/telemetry/identifiers.ts +94 -0
  389. package/src/telemetry/index.ts +11 -0
  390. package/src/telemetry/telemetry.test.ts +1343 -0
  391. package/src/telemetry/trackCommand.ts +110 -0
  392. package/src/telemetry/types.ts +92 -0
  393. package/src/types.ts +33 -0
  394. package/src/version.ts +2 -0
@@ -0,0 +1,316 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import type { BlocksContext } from './api.js';
5
+ import type { ScopeParent } from './common/index.js';
6
+ import { BLOCKS_NAMESPACE, BLOCKS_RPC_PREFIX } from './constants.js';
7
+
8
+ // ── Public types ────────────────────────────────────────────────────────────
9
+
10
+ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';
11
+
12
+ export interface RawRouteOptions {
13
+ method: HttpMethod;
14
+ /**
15
+ * URL path pattern. When provided, used exactly as given.
16
+ * When omitted, derived from the scope-chain IDs.
17
+ *
18
+ * - `/health` — exact match
19
+ * - `/users/{id}` — named path parameter (captures one segment)
20
+ * - `/v1/*` — wildcard (captures everything after prefix)
21
+ *
22
+ * **Scope-chain derivation:** If omitted, the path is built by collecting
23
+ * each ancestor scope's `id` (excluding the root BlocksStack) and joining
24
+ * with `/`. For example, `BlocksStack('app') → Scope('v1') → RawRoute('health')`
25
+ * yields `/v1/health`.
26
+ */
27
+ path?: string;
28
+ handler: (context: BlocksContext) => Promise<void>;
29
+ }
30
+
31
+ /**
32
+ * Typed error constants for RawRoute. Use with `isBlocksError()` in catch blocks.
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * try {
37
+ * new RawRoute(scope, 'dup', { method: 'GET', path: '/health', handler });
38
+ * } catch (e: unknown) {
39
+ * if (isBlocksError(e, RawRouteErrors.DuplicateRoute)) {
40
+ * // same method+path already registered
41
+ * }
42
+ * }
43
+ * ```
44
+ */
45
+ export const RawRouteErrors = {
46
+ DuplicateRoute: 'DuplicateRouteException',
47
+ } as const;
48
+
49
+ // ── Scope-chain path derivation ─────────────────────────────────────────────
50
+
51
+ /**
52
+ * Derive a URL path from the scope chain.
53
+ *
54
+ * Walks the parent chain from `scope` up to (but NOT including) the root
55
+ * BlocksStack or the top-level user Scope, collects each intermediate scope's
56
+ * `id`, URL-encodes segments, and joins with `/`. The RawRoute's own `id`
57
+ * is appended as the final segment.
58
+ *
59
+ * A scope is "root-level" if its parent has no `parent` property (i.e., it's
60
+ * the BlocksStack or the fallback `{ id: ... }` sentinel). Root-level scopes
61
+ * are excluded from the path — they represent the application boundary, not
62
+ * a URL segment.
63
+ *
64
+ * @example
65
+ * ```
66
+ * Scope('app') → RawRoute('health') → /health
67
+ * Scope('app') → Scope('v1') → RawRoute('users') → /v1/users
68
+ * Scope('app') → Scope('api') → Scope('v2') → RawRoute('items') → /api/v2/items
69
+ * ```
70
+ */
71
+ export function derivePathFromScope(scope: ScopeParent, id: string): string {
72
+ const segments: string[] = [];
73
+
74
+ let current: ScopeParent | undefined = scope;
75
+ while (current && 'parent' in current && current.parent) {
76
+ const parentNode: ScopeParent = (current as { parent: ScopeParent }).parent;
77
+ const parentIsRoot = !('parent' in parentNode) || !(parentNode as any).parent;
78
+ if (parentIsRoot) break;
79
+ segments.unshift(encodeURIComponent(current.id));
80
+ current = parentNode;
81
+ }
82
+
83
+ segments.push(encodeURIComponent(id));
84
+ return '/' + segments.join('/');
85
+ }
86
+
87
+ /**
88
+ * Resolve the effective path for a RawRoute — uses the explicit `path` if
89
+ * provided, otherwise derives it from the scope chain.
90
+ */
91
+ export function resolveRoutePath(scope: ScopeParent, id: string, options: RawRouteOptions): string {
92
+ return options.path ?? derivePathFromScope(scope, id);
93
+ }
94
+
95
+ // ── Route registry ──────────────────────────────────────────────────────────
96
+
97
+ export interface RegisteredRoute {
98
+ method: string;
99
+ /** Normalized path (trailing slashes removed, double slashes collapsed). */
100
+ path: string;
101
+ /** Compiled regex for matching incoming request paths. */
102
+ pattern: RegExp;
103
+ /** Extracted parameter names in capture-group order. */
104
+ paramNames: string[];
105
+ handler: (context: BlocksContext) => Promise<void>;
106
+ }
107
+
108
+ const routeRegistry: RegisteredRoute[] = [];
109
+ let registrationLocked = false;
110
+
111
+ const VALID_METHODS = new Set<string>(['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS']);
112
+
113
+ /** Prevent further route registration. Call after initialization is complete. */
114
+ export function lockRouteRegistry(): void {
115
+ registrationLocked = true;
116
+ }
117
+
118
+ /** Re-allow route registration (useful in tests after lock). */
119
+ export function unlockRouteRegistry(): void {
120
+ registrationLocked = false;
121
+ }
122
+
123
+ /**
124
+ * Register a raw HTTP route for runtime dispatch.
125
+ *
126
+ * **Internal.** The public API is `new RawRoute(scope, id, options)`.
127
+ * Path must be resolved before calling this function.
128
+ *
129
+ * @throws If registration is locked (after handler creation).
130
+ * @throws If the HTTP method is invalid.
131
+ * @throws If the path is under the reserved namespace (`/aws-blocks` or `/aws-blocks/api/*`).
132
+ * @throws {RawRouteErrors.DuplicateRoute} If the same method+path is registered twice.
133
+ */
134
+ export function registerRoute(options: RawRouteOptions & { path: string }): void {
135
+ if (registrationLocked) {
136
+ throw new Error('Routes must be registered during initialization. Cannot register routes after handler creation.');
137
+ }
138
+
139
+ if (!VALID_METHODS.has(options.method)) {
140
+ throw new Error(`Invalid HTTP method: ${options.method}. Must be one of: ${Array.from(VALID_METHODS).join(', ')}`);
141
+ }
142
+
143
+ const normalizedPath = options.path.replace(/\/+/g, '/').replace(/\/$/, '') || '/';
144
+ if (normalizedPath === '/') {
145
+ throw new Error("Cannot register RawRoute at '/' — root path is not supported (API Gateway proxy resource doesn't handle root). Use a sub-path like '/health'.");
146
+ }
147
+
148
+ const lowerPath = normalizedPath.toLowerCase();
149
+ if (lowerPath === BLOCKS_NAMESPACE || lowerPath === BLOCKS_RPC_PREFIX || lowerPath.startsWith(`${BLOCKS_RPC_PREFIX}/`)) {
150
+ throw new Error(`Cannot register RawRoute at ${BLOCKS_NAMESPACE} or ${BLOCKS_RPC_PREFIX}/* — these paths are reserved for RPC dispatch`);
151
+ }
152
+
153
+ const existing = routeRegistry.find(
154
+ (r) => r.method === options.method && r.path === normalizedPath,
155
+ );
156
+ if (existing) {
157
+ const err = new Error(
158
+ `${RawRouteErrors.DuplicateRoute}: ${options.method} ${normalizedPath} is already registered`,
159
+ );
160
+ err.name = RawRouteErrors.DuplicateRoute;
161
+ throw err;
162
+ }
163
+
164
+ const { pattern, paramNames } = compilePath(normalizedPath);
165
+
166
+ routeRegistry.push({
167
+ method: options.method,
168
+ path: normalizedPath,
169
+ pattern,
170
+ paramNames,
171
+ handler: options.handler,
172
+ });
173
+ }
174
+
175
+ /** Return a read-only snapshot of all registered routes. */
176
+ export function getRegisteredRoutes(): readonly RegisteredRoute[] {
177
+ return routeRegistry;
178
+ }
179
+
180
+ /** Remove all registered routes and unlock registration (used by tests). */
181
+ export function clearRouteRegistry(): void {
182
+ routeRegistry.length = 0;
183
+ registrationLocked = false;
184
+ }
185
+
186
+ /**
187
+ * Match an incoming HTTP request against registered routes.
188
+ *
189
+ * @returns The matched route and extracted path parameters, or `null` if no match.
190
+ */
191
+ export function matchRoute(
192
+ method: string,
193
+ path: string,
194
+ ): { route: RegisteredRoute; params: Record<string, string> } | null {
195
+ // Normalize incoming path: collapse double slashes
196
+ path = path.replace(/\/+/g, '/');
197
+
198
+ for (const route of routeRegistry) {
199
+ if (route.method !== method) continue;
200
+ const match = route.pattern.exec(path);
201
+ if (match) {
202
+ return { route, params: extractParams(route, match) };
203
+ }
204
+ // Retry without trailing slash for non-root paths (e.g. /health/ → /health)
205
+ if (path.length > 1 && path.endsWith('/')) {
206
+ const trimmed = path.slice(0, -1);
207
+ const retryMatch = route.pattern.exec(trimmed);
208
+ if (retryMatch) {
209
+ return { route, params: extractParams(route, retryMatch) };
210
+ }
211
+ }
212
+ }
213
+ return null;
214
+ }
215
+
216
+ function extractParams(route: RegisteredRoute, match: RegExpExecArray): Record<string, string> {
217
+ const params: Record<string, string> = Object.create(null);
218
+ route.paramNames.forEach((name, i) => {
219
+ const raw = match[i + 1];
220
+
221
+ if (name === '*') {
222
+ // Wildcards capture raw paths — no decoding (prevents path traversal)
223
+ params[name] = raw;
224
+ return;
225
+ }
226
+
227
+ // Named params get decoded
228
+ try {
229
+ params[name] = decodeURIComponent(raw);
230
+ } catch {
231
+ // Invalid percent encoding — use raw value
232
+ params[name] = raw;
233
+ }
234
+ });
235
+ return params;
236
+ }
237
+
238
+ // ── Path compilation ────────────────────────────────────────────────────────
239
+
240
+ const REGEX_SPECIAL = new Set(['.', '+', '?', '^', '$', '|', '(', ')', '[', ']', '\\']);
241
+ const BLOCKED_PARAM_NAMES = new Set(['__proto__', 'constructor', 'prototype']);
242
+
243
+ /**
244
+ * Compile a path pattern into a RegExp and extract parameter names.
245
+ *
246
+ * Supported syntax:
247
+ * - `/health` → exact match
248
+ * - `/users/{id}` → captures one path segment as `id`
249
+ * - `/v1/*` → captures everything after `/v1/` as `*`
250
+ * - `/a/{x}/b/{y}` → multiple named parameters
251
+ */
252
+ export function compilePath(path: string): { pattern: RegExp; paramNames: string[] } {
253
+ if (!path.startsWith('/')) {
254
+ throw new Error('Path must start with /');
255
+ }
256
+
257
+ // Normalize: collapse double slashes
258
+ path = path.replace(/\/+/g, '/');
259
+
260
+ // Remove trailing slash (except root '/')
261
+ if (path.length > 1 && path.endsWith('/')) {
262
+ path = path.slice(0, -1);
263
+ }
264
+
265
+ const paramNames: string[] = [];
266
+ let regexStr = '';
267
+ let i = 0;
268
+ let wildcardCount = 0;
269
+
270
+ while (i < path.length) {
271
+ if (path[i] === '{') {
272
+ const end = path.indexOf('}', i);
273
+ if (end === -1) {
274
+ throw new Error(`Unclosed '{' in path pattern: ${path}`);
275
+ }
276
+ const name = path.substring(i + 1, end);
277
+ if (!name || !/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
278
+ throw new Error(
279
+ `Invalid parameter name '${name}' in path '${path}'. Parameter names must be valid JavaScript identifiers (start with letter or underscore, contain only letters, digits, underscores).`,
280
+ );
281
+ }
282
+ if (BLOCKED_PARAM_NAMES.has(name)) {
283
+ throw new Error(`Parameter name '${name}' is reserved and cannot be used in path '${path}'.`);
284
+ }
285
+ paramNames.push(name);
286
+ regexStr += '([^/]+)';
287
+ i = end + 1;
288
+ } else if (path[i] === '}') {
289
+ throw new Error(`Unexpected '}' without matching '{' in path pattern: ${path}`);
290
+ } else if (path[i] === '*') {
291
+ wildcardCount++;
292
+ if (wildcardCount > 1) {
293
+ throw new Error(
294
+ `Path '${path}' contains multiple wildcards. Only one wildcard (*) is allowed per route, and it must be the last segment.`,
295
+ );
296
+ }
297
+ if (i !== path.length - 1) {
298
+ throw new Error(
299
+ `Path '${path}' contains multiple wildcards. Only one wildcard (*) is allowed per route, and it must be the last segment.`,
300
+ );
301
+ }
302
+ paramNames.push('*');
303
+ regexStr += '(.*)';
304
+ i++;
305
+ } else if (REGEX_SPECIAL.has(path[i])) {
306
+ regexStr += '\\' + path[i];
307
+ i++;
308
+ } else {
309
+ regexStr += path[i];
310
+ i++;
311
+ }
312
+ }
313
+
314
+ const fullPattern = '^' + regexStr + '$';
315
+ return { pattern: new RegExp(fullPattern), paramNames };
316
+ }
@@ -0,0 +1,222 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { describe, it } from 'node:test';
5
+ import assert from 'node:assert';
6
+ import { redactForLogging, redactToJson, REDACTED } from './redact.js';
7
+
8
+ describe('redactForLogging', () => {
9
+ it('redacts a top-level password field', () => {
10
+ const out = redactForLogging({ username: 'alice', password: 'hunter2' });
11
+ assert.deepEqual(out, { username: 'alice', password: REDACTED });
12
+ });
13
+
14
+ it('redacts every sensitive credential/token field name', () => {
15
+ const out = redactForLogging({
16
+ password: 'p',
17
+ newPassword: 'np',
18
+ session: 's',
19
+ accessToken: 'at',
20
+ refreshToken: 'rt',
21
+ idToken: 'it',
22
+ sharedSecret: 'ss',
23
+ clientSecret: 'cs',
24
+ apiKey: 'ak',
25
+ authorization: 'Bearer x',
26
+ code: '123456',
27
+ credentials: 'c',
28
+ });
29
+ for (const v of Object.values(out as Record<string, unknown>)) {
30
+ assert.equal(v, REDACTED);
31
+ }
32
+ });
33
+
34
+ it('matches field names case-insensitively', () => {
35
+ const out = redactForLogging({ PassWord: 'x', SESSION: 'y' }) as Record<string, unknown>;
36
+ assert.equal(out.PassWord, REDACTED);
37
+ assert.equal(out.SESSION, REDACTED);
38
+ });
39
+
40
+ it('leaves non-sensitive fields untouched', () => {
41
+ const input = { username: 'bob', email: 'b@example.com', count: 3, ok: true };
42
+ assert.deepEqual(redactForLogging(input), input);
43
+ });
44
+
45
+ it('walks nested objects', () => {
46
+ const out = redactForLogging({
47
+ action: 'confirmSignIn',
48
+ payload: { session: 'tok', code: '999' },
49
+ });
50
+ assert.deepEqual(out, {
51
+ action: 'confirmSignIn',
52
+ payload: { session: REDACTED, code: REDACTED },
53
+ });
54
+ });
55
+
56
+ it('walks arrays (e.g. positional RPC args)', () => {
57
+ const out = redactForLogging([
58
+ { action: 'signIn', username: 'a', password: 'secret' },
59
+ { action: 'confirmSignIn', session: 'tok', code: '111' },
60
+ ]);
61
+ assert.deepEqual(out, [
62
+ { action: 'signIn', username: 'a', password: REDACTED },
63
+ { action: 'confirmSignIn', session: REDACTED, code: REDACTED },
64
+ ]);
65
+ });
66
+
67
+ it('preserves null/undefined sensitive fields without masking', () => {
68
+ const out = redactForLogging({ password: null, session: undefined }) as Record<string, unknown>;
69
+ assert.equal(out.password, null);
70
+ assert.equal(out.session, undefined);
71
+ });
72
+
73
+ it('returns primitives unchanged', () => {
74
+ assert.equal(redactForLogging('just a string'), 'just a string');
75
+ assert.equal(redactForLogging(42), 42);
76
+ assert.equal(redactForLogging(null), null);
77
+ });
78
+
79
+ it('does not mutate the input object', () => {
80
+ const input = { password: 'keep-me', username: 'alice' };
81
+ redactForLogging(input);
82
+ assert.equal(input.password, 'keep-me');
83
+ });
84
+
85
+ // Regression: the auth state machine ships secrets as form-field
86
+ // descriptors `{ name: 'session', defaultValue: '<token>' }`. The secret
87
+ // sits under `defaultValue`, NOT under a key literally named `session`, so
88
+ // plain key-name redaction walks past it. These guard the descriptor branch.
89
+ it('redacts defaultValue of a sensitive AuthField descriptor', () => {
90
+ const out = redactForLogging({
91
+ name: 'session',
92
+ label: 'Session',
93
+ type: 'hidden',
94
+ defaultValue: 'COGNITO_SESSION_TOKEN',
95
+ }) as Record<string, unknown>;
96
+ assert.equal(out.name, 'session');
97
+ assert.equal(out.defaultValue, REDACTED);
98
+ });
99
+
100
+ it('redacts session + sharedSecret in a full confirmingSignIn AuthState', () => {
101
+ const authState = {
102
+ state: 'confirmingSignIn',
103
+ actions: [{
104
+ name: 'confirmSignIn',
105
+ label: 'Set Up Authenticator',
106
+ fields: [
107
+ { name: 'session', label: 'Session', type: 'hidden', required: true, defaultValue: 'SESSION_TOKEN_abc' },
108
+ { name: 'sharedSecret', label: 'Shared Secret', type: 'hidden', required: true, defaultValue: 'JBSWY3DPEHPK3PXP' },
109
+ { name: 'code', label: 'Code', type: 'text', required: true },
110
+ ],
111
+ }],
112
+ };
113
+ const json = JSON.stringify(redactForLogging(authState));
114
+ assert.ok(!json.includes('SESSION_TOKEN_abc'), 'session token must not leak');
115
+ assert.ok(!json.includes('JBSWY3DPEHPK3PXP'), 'TOTP shared secret must not leak');
116
+ // Non-secret descriptor fields are preserved for debuggability.
117
+ assert.ok(json.includes('confirmSignIn'));
118
+ assert.ok(json.includes('Set Up Authenticator'));
119
+ });
120
+
121
+ it('leaves defaultValue untouched for a non-sensitive field (e.g. echoed username)', () => {
122
+ const out = redactForLogging({
123
+ name: 'username',
124
+ type: 'hidden',
125
+ defaultValue: 'alice',
126
+ }) as Record<string, unknown>;
127
+ assert.equal(out.defaultValue, 'alice');
128
+ });
129
+
130
+ it('handles circular references', () => {
131
+ const a: Record<string, unknown> = { username: 'alice' };
132
+ a.self = a;
133
+ const out = redactForLogging(a) as Record<string, unknown>;
134
+ assert.equal(out.username, 'alice');
135
+ assert.equal(out.self, '[Circular]');
136
+ });
137
+
138
+ it('detects cycles nested deeper than the root', () => {
139
+ const root: Record<string, unknown> = { level: 1 };
140
+ const child: Record<string, unknown> = { level: 2, parent: root };
141
+ root.child = child;
142
+ const out = redactForLogging(root) as any;
143
+ assert.equal(out.child.level, 2);
144
+ assert.equal(out.child.parent, '[Circular]');
145
+ });
146
+
147
+ // Regression: `seen` must track the ancestor PATH, not every node ever
148
+ // visited. A shared (non-cyclic) reference reached twice via sibling
149
+ // branches must be emitted in full both times — a visited-set would wrongly
150
+ // flag the second one as '[Circular]' and drop real data from the log.
151
+ it('does not flag shared (non-circular) references as circular', () => {
152
+ const shared = { region: 'us-east-1', count: 2 };
153
+ const out = redactForLogging({ a: shared, b: shared }) as Record<string, unknown>;
154
+ assert.deepEqual(out.a, shared);
155
+ assert.deepEqual(out.b, shared);
156
+ });
157
+
158
+ it('emits a shared reference repeated in an array, not [Circular]', () => {
159
+ const shared = { id: 'x' };
160
+ const out = redactForLogging([shared, shared, shared]) as unknown[];
161
+ assert.deepEqual(out, [shared, shared, shared]);
162
+ });
163
+
164
+ it('still redacts secrets inside a shared reference each time', () => {
165
+ const shared = { username: 'u', password: 'secret' };
166
+ const out = redactForLogging({ first: shared, second: shared }) as any;
167
+ assert.equal(out.first.password, REDACTED);
168
+ assert.equal(out.second.password, REDACTED);
169
+ assert.equal(out.second.username, 'u');
170
+ });
171
+
172
+ // Regression: `code` is exact-match only, so substring siblings like
173
+ // statusCode/errorCode stay visible — they carry no secret and masking them
174
+ // defeats the debugging purpose of the logs.
175
+ it('redacts an exact `code` field but not `code`-substring fields', () => {
176
+ const out = redactForLogging({
177
+ code: '123456',
178
+ statusCode: 200,
179
+ errorCode: 'E_BAD',
180
+ countryCode: 'US',
181
+ zipCode: '98101',
182
+ qrCode: 'data:image/png',
183
+ }) as Record<string, unknown>;
184
+ assert.equal(out.code, REDACTED);
185
+ assert.equal(out.statusCode, 200);
186
+ assert.equal(out.errorCode, 'E_BAD');
187
+ assert.equal(out.countryCode, 'US');
188
+ assert.equal(out.zipCode, '98101');
189
+ assert.equal(out.qrCode, 'data:image/png');
190
+ });
191
+ });
192
+
193
+ describe('redactToJson', () => {
194
+ it('serializes a redacted copy', () => {
195
+ const json = redactToJson({ username: 'alice', password: 'hunter2' });
196
+ assert.equal(json, JSON.stringify({ username: 'alice', password: REDACTED }));
197
+ });
198
+
199
+ it('returns a safe placeholder on serialization failure', () => {
200
+ // BigInt is not JSON-serializable and survives redaction (not a sensitive key).
201
+ assert.equal(redactToJson({ big: 10n }), '[unserializable]');
202
+ });
203
+
204
+ it('does not leak secrets even when truncation would apply downstream', () => {
205
+ const json = redactToJson([{ action: 'signIn', username: 'u', password: 'p'.repeat(50) }]);
206
+ assert.ok(!json.includes('pppp'));
207
+ assert.ok(json.includes(REDACTED));
208
+ });
209
+
210
+ it('redacts secrets in a real JSON-RPC request body shape', () => {
211
+ const body = {
212
+ jsonrpc: '2.0',
213
+ method: 'authApi.setAuthState',
214
+ params: [{ action: 'signIn', username: 'a', password: 'hunter2' }],
215
+ id: 1,
216
+ };
217
+ const json = redactToJson(body);
218
+ assert.ok(!json.includes('hunter2'));
219
+ assert.ok(json.includes(REDACTED));
220
+ assert.ok(json.includes('setAuthState'), 'method name preserved for debugging');
221
+ });
222
+ });
package/src/redact.ts ADDED
@@ -0,0 +1,143 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /**
5
+ * Sensitive-field redaction for logging.
6
+ *
7
+ * The dev server logs RPC request/response bodies to help debug cross-stack
8
+ * wire-format mismatches. Those bodies carry credentials (sign-in passwords,
9
+ * new-password challenge responses) and bearer-style secrets (Cognito challenge
10
+ * `session` tokens, MFA shared secrets, OTP codes). Logging them verbatim puts
11
+ * plaintext passwords and reusable tokens in the log stream. This module masks
12
+ * those fields before anything is logged, without mutating the value that
13
+ * actually flows over the wire.
14
+ *
15
+ * Two complementary rules, because auth payloads hide secrets two ways:
16
+ *
17
+ * 1. **Direct keys** — `{ password: '...', session: '...' }`. Redacted by
18
+ * field NAME, matched case-insensitively (substring).
19
+ *
20
+ * 2. **Form-field descriptors** — the auth state machine ships secrets as
21
+ * `{ name: 'session', defaultValue: '<token>' }` (see
22
+ * `@aws-blocks/auth-common` AuthField). Here the secret sits under the
23
+ * innocuous key `defaultValue`; the sensitive word is the *value* of a
24
+ * sibling `name`. Rule 1 alone would walk straight past it. So when an
25
+ * object carries a sensitive `name`, its `defaultValue` is redacted too.
26
+ *
27
+ * The wire payload is never altered — only the logged copy is scrubbed.
28
+ */
29
+
30
+ /** The string substituted for any sensitive value. */
31
+ export const REDACTED = '[REDACTED]' as const;
32
+
33
+ /**
34
+ * Field-name fragments whose values must never be logged. Matched
35
+ * case-insensitively as a SUBSTRING, so prefixed/suffixed variants
36
+ * (`accessToken`, `refreshToken`, `idToken`, `apiKey`, `clientSecret`) all
37
+ * match without enumerating every spelling. Keep aligned with the auth action
38
+ * payloads in `@aws-blocks/auth-common` plus the generic credential vocabulary.
39
+ */
40
+ const SENSITIVE_KEY_PARTS: readonly string[] = [
41
+ 'password',
42
+ 'session',
43
+ 'token',
44
+ 'secret',
45
+ 'credential',
46
+ 'apikey',
47
+ 'authorization',
48
+ ];
49
+
50
+ /**
51
+ * Field names that are sensitive only as an EXACT match. The OTP / verification
52
+ * code the auth state machine sends is literally named `code` (see
53
+ * `@aws-blocks/auth-common` confirmSignIn payloads). Matching it as a substring
54
+ * would mask innocuous fields like `statusCode`, `errorCode`, `countryCode`,
55
+ * `zipCode`, `qrCode` — log noise that defeats the debugging purpose of these
56
+ * logs without protecting anything. So `code` is exact-match only.
57
+ */
58
+ const SENSITIVE_KEY_EXACT: ReadonlySet<string> = new Set(['code']);
59
+
60
+ /**
61
+ * Whether a field name should be redacted: an exact-match against
62
+ * {@link SENSITIVE_KEY_EXACT}, or a case-insensitive substring match against
63
+ * {@link SENSITIVE_KEY_PARTS}.
64
+ */
65
+ function isSensitiveKey(key: string): boolean {
66
+ const lower = key.toLowerCase();
67
+ if (SENSITIVE_KEY_EXACT.has(lower)) return true;
68
+ return SENSITIVE_KEY_PARTS.some((part) => lower.includes(part));
69
+ }
70
+
71
+ /**
72
+ * Return a deep copy of `value` with the value of every sensitive field
73
+ * replaced by {@link REDACTED}. Arrays and nested objects are walked; the
74
+ * input is never mutated. Circular references are emitted as `'[Circular]'`.
75
+ *
76
+ * Primitives are returned unchanged — only object/array *fields* are matched
77
+ * against the sensitive-key list, since a bare string has no field name to
78
+ * key off.
79
+ *
80
+ * `seen` tracks the current ancestor PATH, not every node ever visited: each
81
+ * node is added before its children are walked and removed afterwards. A
82
+ * plain visited-set would flag the second appearance of a *shared* (non-cyclic)
83
+ * reference as `'[Circular]'` — e.g. `{ a: shared, b: shared }` — silently
84
+ * dropping real data from the log. Only a reference still open on the stack is
85
+ * a genuine cycle.
86
+ */
87
+ export function redactForLogging(value: unknown, seen: WeakSet<object> = new WeakSet()): unknown {
88
+ if (value === null || typeof value !== 'object') {
89
+ return value;
90
+ }
91
+
92
+ if (seen.has(value)) {
93
+ return '[Circular]';
94
+ }
95
+ seen.add(value);
96
+
97
+ let out: unknown;
98
+ if (Array.isArray(value)) {
99
+ out = value.map((item) => redactForLogging(item, seen));
100
+ } else {
101
+ const obj = value as Record<string, unknown>;
102
+
103
+ // Rule 2: form-field descriptor `{ name: <sensitive>, defaultValue: <secret> }`.
104
+ // The secret hides under `defaultValue` while the sensitive word is the value
105
+ // of `name` — so the direct-key pass below never sees it. Detect the shape and
106
+ // redact `defaultValue` explicitly.
107
+ const masksDefaultValue =
108
+ typeof obj.name === 'string'
109
+ && isSensitiveKey(obj.name)
110
+ && 'defaultValue' in obj;
111
+
112
+ const result: Record<string, unknown> = {};
113
+ for (const [key, val] of Object.entries(obj)) {
114
+ if (isSensitiveKey(key) || (key === 'defaultValue' && masksDefaultValue)) {
115
+ // Preserve presence/absence so logs still show the field was sent,
116
+ // without revealing it. Skip masking explicit null/undefined so an
117
+ // absent secret doesn't look like a populated one.
118
+ result[key] = val === null || val === undefined ? val : REDACTED;
119
+ } else {
120
+ result[key] = redactForLogging(val, seen);
121
+ }
122
+ }
123
+ out = result;
124
+ }
125
+
126
+ // Pop this node off the ancestor path now that its subtree is fully walked,
127
+ // so sibling branches can reference the same object without false `[Circular]`.
128
+ seen.delete(value);
129
+ return out;
130
+ }
131
+
132
+ /**
133
+ * Convenience for log call sites: redact `value` and serialize it to a JSON
134
+ * string. Returns a safe placeholder instead of throwing if serialization
135
+ * fails (e.g. a BigInt slips through), so logging can never crash a request.
136
+ */
137
+ export function redactToJson(value: unknown): string {
138
+ try {
139
+ return JSON.stringify(redactForLogging(value));
140
+ } catch {
141
+ return '[unserializable]';
142
+ }
143
+ }