@adtrackify/at-service-common 3.18.11 → 3.18.12

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 (559) hide show
  1. package/dist/cjs/__tests__/clients/cross-platform-compression.spec.d.ts +1 -0
  2. package/dist/cjs/__tests__/clients/cross-platform-compression.spec.js +355 -0
  3. package/dist/cjs/__tests__/clients/cross-platform-compression.spec.js.map +1 -0
  4. package/dist/cjs/__tests__/clients/sqs-bundled-client.spec.d.ts +1 -1
  5. package/dist/cjs/__tests__/clients/sqs-bundled-client.spec.js +921 -921
  6. package/dist/cjs/__tests__/clients/sqs-bundling-contracts.spec.d.ts +1 -0
  7. package/dist/cjs/__tests__/clients/sqs-bundling-contracts.spec.js +576 -0
  8. package/dist/cjs/__tests__/clients/sqs-bundling-contracts.spec.js.map +1 -0
  9. package/dist/cjs/__tests__/clients/sqs-client.spec.d.ts +1 -1
  10. package/dist/cjs/__tests__/clients/sqs-client.spec.js +191 -191
  11. package/dist/cjs/__tests__/clients/sqs-unbundle.spec.d.ts +1 -1
  12. package/dist/cjs/__tests__/clients/sqs-unbundle.spec.js +1357 -1228
  13. package/dist/cjs/__tests__/clients/sqs-unbundle.spec.js.map +1 -1
  14. package/dist/cjs/__tests__/db/shared-read-db-services.spec.d.ts +1 -1
  15. package/dist/cjs/__tests__/db/shared-read-db-services.spec.js +89 -89
  16. package/dist/cjs/__tests__/helpers/account-users-helper.spec.d.ts +1 -1
  17. package/dist/cjs/__tests__/helpers/account-users-helper.spec.js +220 -220
  18. package/dist/cjs/__tests__/helpers/api-key-auth-helper.spec.d.ts +1 -1
  19. package/dist/cjs/__tests__/helpers/api-key-auth-helper.spec.js +82 -82
  20. package/dist/cjs/__tests__/identity-cache/identity-cache-db-service.spec.d.ts +1 -1
  21. package/dist/cjs/__tests__/identity-cache/identity-cache-db-service.spec.js +674 -674
  22. package/dist/cjs/__tests__/identity-cache/trait-merging-and-staleness.spec.d.ts +1 -1
  23. package/dist/cjs/__tests__/identity-cache/trait-merging-and-staleness.spec.js +588 -588
  24. package/dist/cjs/__tests__/integration/sqs-bundling-roundtrip.spec.d.ts +1 -1
  25. package/dist/cjs/__tests__/integration/sqs-bundling-roundtrip.spec.js +584 -584
  26. package/dist/cjs/__tests__/libs/compress-decompress.spec.d.ts +1 -1
  27. package/dist/cjs/__tests__/libs/compress-decompress.spec.js +16 -16
  28. package/dist/cjs/__tests__/libs/currency.spec.d.ts +1 -1
  29. package/dist/cjs/__tests__/libs/currency.spec.js +220 -220
  30. package/dist/cjs/__tests__/libs/dates.spec.d.ts +1 -1
  31. package/dist/cjs/__tests__/libs/dates.spec.js +130 -130
  32. package/dist/cjs/__tests__/libs/domain.spec.d.ts +1 -1
  33. package/dist/cjs/__tests__/libs/domain.spec.js +107 -107
  34. package/dist/cjs/__tests__/libs/numbers.spec.d.ts +1 -1
  35. package/dist/cjs/__tests__/libs/numbers.spec.js +261 -261
  36. package/dist/cjs/__tests__/s3-client/s3-client.spec.d.ts +1 -1
  37. package/dist/cjs/__tests__/s3-client/s3-client.spec.js +33 -33
  38. package/dist/cjs/__tests__/shopify/shopify-graphql-transformer.spec.d.ts +1 -1
  39. package/dist/cjs/__tests__/shopify/shopify-graphql-transformer.spec.js +35 -35
  40. package/dist/cjs/__tests__/unit/libs/api-router/public-api-router.spec.d.ts +1 -1
  41. package/dist/cjs/__tests__/unit/libs/api-router/public-api-router.spec.js +181 -181
  42. package/dist/cjs/__tests__/unit/libs/api-router/route-matcher.spec.d.ts +1 -1
  43. package/dist/cjs/__tests__/unit/libs/api-router/route-matcher.spec.js +69 -69
  44. package/dist/cjs/clients/generic/cognito-client.d.ts +23 -23
  45. package/dist/cjs/clients/generic/cognito-client.js +209 -209
  46. package/dist/cjs/clients/generic/dynamodb-client.d.ts +18 -18
  47. package/dist/cjs/clients/generic/dynamodb-client.js +172 -172
  48. package/dist/cjs/clients/generic/eventbridge-client.d.ts +14 -14
  49. package/dist/cjs/clients/generic/eventbridge-client.js +51 -51
  50. package/dist/cjs/clients/generic/http-client.d.ts +14 -14
  51. package/dist/cjs/clients/generic/http-client.js +61 -61
  52. package/dist/cjs/clients/generic/index.d.ts +13 -13
  53. package/dist/cjs/clients/generic/index.js +29 -29
  54. package/dist/cjs/clients/generic/lambda-invoke-client.d.ts +10 -10
  55. package/dist/cjs/clients/generic/lambda-invoke-client.js +39 -39
  56. package/dist/cjs/clients/generic/location-client.d.ts +8 -8
  57. package/dist/cjs/clients/generic/location-client.js +31 -31
  58. package/dist/cjs/clients/generic/redis-client.d.ts +33 -33
  59. package/dist/cjs/clients/generic/redis-client.js +191 -191
  60. package/dist/cjs/clients/generic/s3-client.d.ts +23 -23
  61. package/dist/cjs/clients/generic/s3-client.js +216 -216
  62. package/dist/cjs/clients/generic/singlestore-db-client.d.ts +14 -14
  63. package/dist/cjs/clients/generic/singlestore-db-client.js +67 -67
  64. package/dist/cjs/clients/generic/sqs-bundled-client.d.ts +15 -15
  65. package/dist/cjs/clients/generic/sqs-bundled-client.js +311 -311
  66. package/dist/cjs/clients/generic/sqs-bundled-client.types.d.ts +53 -53
  67. package/dist/cjs/clients/generic/sqs-bundled-client.types.js +17 -17
  68. package/dist/cjs/clients/generic/sqs-client.d.ts +53 -53
  69. package/dist/cjs/clients/generic/sqs-client.js +285 -285
  70. package/dist/cjs/clients/generic/sqs-unbundle.d.ts +32 -32
  71. package/dist/cjs/clients/generic/sqs-unbundle.js +144 -144
  72. package/dist/cjs/clients/index.d.ts +3 -3
  73. package/dist/cjs/clients/index.js +19 -19
  74. package/dist/cjs/clients/internal-api/accounts-client.d.ts +91 -91
  75. package/dist/cjs/clients/internal-api/accounts-client.js +129 -129
  76. package/dist/cjs/clients/internal-api/cache-lambda-client.d.ts +26 -26
  77. package/dist/cjs/clients/internal-api/cache-lambda-client.js +89 -89
  78. package/dist/cjs/clients/internal-api/db-management-client.d.ts +18 -18
  79. package/dist/cjs/clients/internal-api/db-management-client.js +36 -36
  80. package/dist/cjs/clients/internal-api/destinations-client.d.ts +34 -34
  81. package/dist/cjs/clients/internal-api/destinations-client.js +79 -79
  82. package/dist/cjs/clients/internal-api/event-collector-client.d.ts +20 -20
  83. package/dist/cjs/clients/internal-api/event-collector-client.js +36 -36
  84. package/dist/cjs/clients/internal-api/identity-client.d.ts +31 -31
  85. package/dist/cjs/clients/internal-api/identity-client.js +91 -91
  86. package/dist/cjs/clients/internal-api/index.d.ts +9 -9
  87. package/dist/cjs/clients/internal-api/index.js +25 -25
  88. package/dist/cjs/clients/internal-api/shopify-app-install-client.d.ts +37 -37
  89. package/dist/cjs/clients/internal-api/shopify-app-install-client.js +81 -81
  90. package/dist/cjs/clients/internal-api/subscriptions-client.d.ts +26 -26
  91. package/dist/cjs/clients/internal-api/subscriptions-client.js +77 -77
  92. package/dist/cjs/clients/internal-api/users-auth-client.d.ts +35 -35
  93. package/dist/cjs/clients/internal-api/users-auth-client.js +110 -110
  94. package/dist/cjs/clients/third-party/audience-acuity/acuity-api-service.d.ts +8 -0
  95. package/dist/cjs/clients/third-party/audience-acuity/acuity-api-service.js +55 -0
  96. package/dist/cjs/clients/third-party/audience-acuity/acuity-api-service.js.map +1 -0
  97. package/dist/cjs/clients/third-party/audience-acuity/acuity-client.d.ts +9 -0
  98. package/dist/cjs/clients/third-party/audience-acuity/acuity-client.js +35 -0
  99. package/dist/cjs/clients/third-party/audience-acuity/acuity-client.js.map +1 -0
  100. package/dist/cjs/clients/third-party/audience-acuity/acuity-helpers.d.ts +2 -0
  101. package/dist/cjs/clients/third-party/audience-acuity/acuity-helpers.js +23 -0
  102. package/dist/cjs/clients/third-party/audience-acuity/acuity-helpers.js.map +1 -0
  103. package/dist/cjs/clients/third-party/audience-acuity/acuity-types.d.ts +74 -0
  104. package/dist/cjs/clients/third-party/audience-acuity/acuity-types.js +3 -0
  105. package/dist/cjs/clients/third-party/audience-acuity/acuity-types.js.map +1 -0
  106. package/dist/cjs/clients/third-party/audience-acuity/index.d.ts +4 -0
  107. package/dist/cjs/clients/third-party/audience-acuity/index.js +21 -0
  108. package/dist/cjs/clients/third-party/audience-acuity/index.js.map +1 -0
  109. package/dist/cjs/clients/third-party/emailable-client.d.ts +7 -7
  110. package/dist/cjs/clients/third-party/emailable-client.js +25 -25
  111. package/dist/cjs/clients/third-party/exchange-rate-api-client.d.ts +17 -17
  112. package/dist/cjs/clients/third-party/exchange-rate-api-client.js +19 -19
  113. package/dist/cjs/clients/third-party/index.d.ts +5 -4
  114. package/dist/cjs/clients/third-party/index.js +21 -20
  115. package/dist/cjs/clients/third-party/index.js.map +1 -1
  116. package/dist/cjs/clients/third-party/loops-client.d.ts +10 -10
  117. package/dist/cjs/clients/third-party/loops-client.js +30 -30
  118. package/dist/cjs/clients/third-party/shopify/graphql-order-queries.d.ts +25 -25
  119. package/dist/cjs/clients/third-party/shopify/graphql-order-queries.js +4 -4
  120. package/dist/cjs/clients/third-party/shopify/graphql-product-queries.d.ts +2 -2
  121. package/dist/cjs/clients/third-party/shopify/graphql-product-queries.js +5 -5
  122. package/dist/cjs/clients/third-party/shopify/shopify-graphql-client.d.ts +10 -10
  123. package/dist/cjs/clients/third-party/shopify/shopify-graphql-client.js +161 -161
  124. package/dist/cjs/clients/third-party/shopify-client.d.ts +29 -29
  125. package/dist/cjs/clients/third-party/shopify-client.js +146 -146
  126. package/dist/cjs/constants/index.d.ts +1 -1
  127. package/dist/cjs/constants/index.js +17 -17
  128. package/dist/cjs/constants/sqs.d.ts +20 -20
  129. package/dist/cjs/constants/sqs.js +26 -26
  130. package/dist/cjs/helpers/account-users-helper.d.ts +2 -2
  131. package/dist/cjs/helpers/account-users-helper.js +22 -22
  132. package/dist/cjs/helpers/api-key-auth-helper.d.ts +9 -9
  133. package/dist/cjs/helpers/api-key-auth-helper.js +40 -40
  134. package/dist/cjs/helpers/api-key-authorizer-helper.d.ts +36 -36
  135. package/dist/cjs/helpers/api-key-authorizer-helper.js +87 -87
  136. package/dist/cjs/helpers/identity-cache-helper.d.ts +21 -21
  137. package/dist/cjs/helpers/identity-cache-helper.js +156 -156
  138. package/dist/cjs/helpers/index.d.ts +9 -9
  139. package/dist/cjs/helpers/index.js +25 -25
  140. package/dist/cjs/helpers/input-validation-helper.d.ts +3 -3
  141. package/dist/cjs/helpers/input-validation-helper.js +22 -22
  142. package/dist/cjs/helpers/logging-helper.d.ts +16 -16
  143. package/dist/cjs/helpers/logging-helper.js +84 -84
  144. package/dist/cjs/helpers/response-helper.d.ts +18 -18
  145. package/dist/cjs/helpers/response-helper.js +43 -43
  146. package/dist/cjs/helpers/shopify-helper.d.ts +9 -9
  147. package/dist/cjs/helpers/shopify-helper.js +26 -26
  148. package/dist/cjs/helpers/sqs-utils.d.ts +6 -6
  149. package/dist/cjs/helpers/sqs-utils.js +14 -14
  150. package/dist/cjs/index.d.ts +7 -7
  151. package/dist/cjs/index.js +23 -23
  152. package/dist/cjs/libs/api-router/index.d.ts +2 -2
  153. package/dist/cjs/libs/api-router/index.js +18 -18
  154. package/dist/cjs/libs/api-router/public-api-router.d.ts +3 -3
  155. package/dist/cjs/libs/api-router/public-api-router.js +36 -36
  156. package/dist/cjs/libs/api-router/route-matcher.d.ts +21 -21
  157. package/dist/cjs/libs/api-router/route-matcher.js +36 -36
  158. package/dist/cjs/libs/click-id-parser.d.ts +23 -23
  159. package/dist/cjs/libs/click-id-parser.js +49 -49
  160. package/dist/cjs/libs/compression.d.ts +2 -2
  161. package/dist/cjs/libs/compression.js +33 -33
  162. package/dist/cjs/libs/cookie.d.ts +17 -17
  163. package/dist/cjs/libs/cookie.js +76 -76
  164. package/dist/cjs/libs/crypto.d.ts +4 -4
  165. package/dist/cjs/libs/crypto.js +25 -25
  166. package/dist/cjs/libs/csv.d.ts +2 -2
  167. package/dist/cjs/libs/csv.js +35 -35
  168. package/dist/cjs/libs/currency.d.ts +1 -1
  169. package/dist/cjs/libs/currency.js +29 -29
  170. package/dist/cjs/libs/dates.d.ts +12 -12
  171. package/dist/cjs/libs/dates.js +96 -96
  172. package/dist/cjs/libs/domain.d.ts +2 -2
  173. package/dist/cjs/libs/domain.js +38 -38
  174. package/dist/cjs/libs/emails.d.ts +6 -6
  175. package/dist/cjs/libs/emails.js +122 -122
  176. package/dist/cjs/libs/http-error.d.ts +21 -21
  177. package/dist/cjs/libs/http-error.js +63 -63
  178. package/dist/cjs/libs/http-status-codes.d.ts +58 -58
  179. package/dist/cjs/libs/http-status-codes.js +62 -62
  180. package/dist/cjs/libs/index.d.ts +18 -18
  181. package/dist/cjs/libs/index.js +34 -34
  182. package/dist/cjs/libs/numbers.d.ts +1 -1
  183. package/dist/cjs/libs/numbers.js +15 -15
  184. package/dist/cjs/libs/referrer-parser/index.d.ts +2 -2
  185. package/dist/cjs/libs/referrer-parser/index.js +18 -18
  186. package/dist/cjs/libs/referrer-parser/referrer-data.d.ts +9 -9
  187. package/dist/cjs/libs/referrer-parser/referrer-data.js +3307 -3307
  188. package/dist/cjs/libs/referrer-parser/referrer-parser-util.d.ts +20 -20
  189. package/dist/cjs/libs/referrer-parser/referrer-parser-util.js +131 -131
  190. package/dist/cjs/libs/strings.d.ts +3 -3
  191. package/dist/cjs/libs/strings.js +46 -46
  192. package/dist/cjs/libs/traits.d.ts +6 -6
  193. package/dist/cjs/libs/traits.js +65 -65
  194. package/dist/cjs/libs/url.d.ts +1 -1
  195. package/dist/cjs/libs/url.js +13 -13
  196. package/dist/cjs/services/cache/generic-cached-object.d.ts +5 -5
  197. package/dist/cjs/services/cache/generic-cached-object.js +2 -2
  198. package/dist/cjs/services/cache/index.d.ts +1 -1
  199. package/dist/cjs/services/cache/index.js +17 -17
  200. package/dist/cjs/services/cache/product-cache-service.d.ts +21 -21
  201. package/dist/cjs/services/cache/product-cache-service.js +76 -76
  202. package/dist/cjs/services/currency-exchange-rate-lookup-service.d.ts +11 -11
  203. package/dist/cjs/services/currency-exchange-rate-lookup-service.js +66 -66
  204. package/dist/cjs/services/db/accounts-db-service.d.ts +9 -9
  205. package/dist/cjs/services/db/accounts-db-service.js +33 -33
  206. package/dist/cjs/services/db/api-keys-db-service.d.ts +10 -10
  207. package/dist/cjs/services/db/api-keys-db-service.js +36 -36
  208. package/dist/cjs/services/db/currency-exchange-rates-db-service.d.ts +21 -21
  209. package/dist/cjs/services/db/currency-exchange-rates-db-service.js +39 -39
  210. package/dist/cjs/services/db/destinations-db-service.d.ts +12 -12
  211. package/dist/cjs/services/db/destinations-db-service.js +76 -76
  212. package/dist/cjs/services/db/identity-cache-db-service.d.ts +28 -28
  213. package/dist/cjs/services/db/identity-cache-db-service.js +320 -320
  214. package/dist/cjs/services/db/index.d.ts +13 -13
  215. package/dist/cjs/services/db/index.js +29 -29
  216. package/dist/cjs/services/db/log-events-db-service.d.ts +11 -11
  217. package/dist/cjs/services/db/log-events-db-service.js +181 -181
  218. package/dist/cjs/services/db/pixels-db-service.d.ts +8 -8
  219. package/dist/cjs/services/db/pixels-db-service.js +35 -35
  220. package/dist/cjs/services/db/purchasable-contacts-db-service.d.ts +9 -9
  221. package/dist/cjs/services/db/purchasable-contacts-db-service.js +43 -43
  222. package/dist/cjs/services/db/purchased-contacts-db-service.d.ts +17 -17
  223. package/dist/cjs/services/db/purchased-contacts-db-service.js +143 -143
  224. package/dist/cjs/services/db/shopify-app-installs-db-service.d.ts +8 -8
  225. package/dist/cjs/services/db/shopify-app-installs-db-service.js +51 -51
  226. package/dist/cjs/services/db/shopify-products-cache-db-service.d.ts +16 -16
  227. package/dist/cjs/services/db/shopify-products-cache-db-service.js +73 -73
  228. package/dist/cjs/services/db/subscriptions-db-service.d.ts +10 -10
  229. package/dist/cjs/services/db/subscriptions-db-service.js +34 -34
  230. package/dist/cjs/services/db/tracking-events-db-service.d.ts +20 -20
  231. package/dist/cjs/services/db/tracking-events-db-service.js +165 -165
  232. package/dist/cjs/services/eventbridge-integration-service.d.ts +9 -9
  233. package/dist/cjs/services/eventbridge-integration-service.js +28 -28
  234. package/dist/cjs/services/events/index.d.ts +3 -3
  235. package/dist/cjs/services/events/index.js +19 -19
  236. package/dist/cjs/services/events/log-event-service.d.ts +19 -19
  237. package/dist/cjs/services/events/log-event-service.js +77 -77
  238. package/dist/cjs/services/events/metric-event-service.d.ts +9 -9
  239. package/dist/cjs/services/events/metric-event-service.js +49 -49
  240. package/dist/cjs/services/events/tracking-event-sqs-service.d.ts +8 -8
  241. package/dist/cjs/services/events/tracking-event-sqs-service.js +34 -34
  242. package/dist/cjs/services/generic-cache-service.d.ts +7 -7
  243. package/dist/cjs/services/generic-cache-service.js +33 -33
  244. package/dist/cjs/services/index.d.ts +8 -8
  245. package/dist/cjs/services/index.js +24 -24
  246. package/dist/cjs/services/ipdata-lookup-service.d.ts +20 -20
  247. package/dist/cjs/services/ipdata-lookup-service.js +112 -112
  248. package/dist/cjs/services/shopify/index.d.ts +2 -2
  249. package/dist/cjs/services/shopify/index.js +18 -18
  250. package/dist/cjs/services/shopify/products/index.d.ts +1 -1
  251. package/dist/cjs/services/shopify/products/index.js +17 -17
  252. package/dist/cjs/services/shopify/products/shopify-products-serviceV2.d.ts +17 -17
  253. package/dist/cjs/services/shopify/products/shopify-products-serviceV2.js +112 -112
  254. package/dist/cjs/services/shopify/shopify-graphql-transformer.d.ts +8 -8
  255. package/dist/cjs/services/shopify/shopify-graphql-transformer.js +141 -141
  256. package/dist/cjs/types/api-response.d.ts +6 -6
  257. package/dist/cjs/types/api-response.js +2 -2
  258. package/dist/cjs/types/index.d.ts +3 -3
  259. package/dist/cjs/types/index.js +32 -32
  260. package/dist/cjs/types/internal-events/event-detail-types.d.ts +20 -20
  261. package/dist/cjs/types/internal-events/event-detail-types.js +27 -27
  262. package/dist/cjs/types/internal-events/index.d.ts +1 -1
  263. package/dist/cjs/types/internal-events/index.js +17 -17
  264. package/dist/cjs/types/shopify-graphql-types/admin.generated.d.ts +123 -123
  265. package/dist/cjs/types/shopify-graphql-types/admin.generated.js +2 -2
  266. package/dist/cjs/types/shopify-graphql-types/admin.types.d.ts +26289 -26289
  267. package/dist/cjs/types/shopify-graphql-types/admin.types.js +5311 -5311
  268. package/dist/cjs/types/shopify-graphql-types/index.d.ts +2 -2
  269. package/dist/cjs/types/shopify-graphql-types/index.js +18 -18
  270. package/dist/cjs/types/shopify-rest-types.d.ts +767 -767
  271. package/dist/cjs/types/shopify-rest-types.js +2 -2
  272. package/dist/cjs/utils/compression.d.ts +36 -36
  273. package/dist/cjs/utils/compression.js +198 -198
  274. package/dist/cjs/utils/index.d.ts +3 -3
  275. package/dist/cjs/utils/index.js +19 -19
  276. package/dist/cjs/utils/retry-envelope.d.ts +12 -12
  277. package/dist/cjs/utils/retry-envelope.js +28 -28
  278. package/dist/cjs/utils/size.d.ts +2 -2
  279. package/dist/cjs/utils/size.js +49 -49
  280. package/dist/esm/__tests__/clients/cross-platform-compression.spec.d.ts +1 -0
  281. package/dist/esm/__tests__/clients/cross-platform-compression.spec.js +330 -0
  282. package/dist/esm/__tests__/clients/cross-platform-compression.spec.js.map +1 -0
  283. package/dist/esm/__tests__/clients/sqs-bundled-client.spec.d.ts +1 -1
  284. package/dist/esm/__tests__/clients/sqs-bundled-client.spec.js +896 -896
  285. package/dist/esm/__tests__/clients/sqs-bundling-contracts.spec.d.ts +1 -0
  286. package/dist/esm/__tests__/clients/sqs-bundling-contracts.spec.js +551 -0
  287. package/dist/esm/__tests__/clients/sqs-bundling-contracts.spec.js.map +1 -0
  288. package/dist/esm/__tests__/clients/sqs-client.spec.d.ts +1 -1
  289. package/dist/esm/__tests__/clients/sqs-client.spec.js +189 -189
  290. package/dist/esm/__tests__/clients/sqs-unbundle.spec.d.ts +1 -1
  291. package/dist/esm/__tests__/clients/sqs-unbundle.spec.js +1355 -1226
  292. package/dist/esm/__tests__/clients/sqs-unbundle.spec.js.map +1 -1
  293. package/dist/esm/__tests__/db/shared-read-db-services.spec.d.ts +1 -1
  294. package/dist/esm/__tests__/db/shared-read-db-services.spec.js +87 -87
  295. package/dist/esm/__tests__/helpers/account-users-helper.spec.d.ts +1 -1
  296. package/dist/esm/__tests__/helpers/account-users-helper.spec.js +218 -218
  297. package/dist/esm/__tests__/helpers/api-key-auth-helper.spec.d.ts +1 -1
  298. package/dist/esm/__tests__/helpers/api-key-auth-helper.spec.js +80 -80
  299. package/dist/esm/__tests__/identity-cache/identity-cache-db-service.spec.d.ts +1 -1
  300. package/dist/esm/__tests__/identity-cache/identity-cache-db-service.spec.js +672 -672
  301. package/dist/esm/__tests__/identity-cache/trait-merging-and-staleness.spec.d.ts +1 -1
  302. package/dist/esm/__tests__/identity-cache/trait-merging-and-staleness.spec.js +586 -586
  303. package/dist/esm/__tests__/integration/sqs-bundling-roundtrip.spec.d.ts +1 -1
  304. package/dist/esm/__tests__/integration/sqs-bundling-roundtrip.spec.js +582 -582
  305. package/dist/esm/__tests__/libs/compress-decompress.spec.d.ts +1 -1
  306. package/dist/esm/__tests__/libs/compress-decompress.spec.js +14 -14
  307. package/dist/esm/__tests__/libs/currency.spec.d.ts +1 -1
  308. package/dist/esm/__tests__/libs/currency.spec.js +218 -218
  309. package/dist/esm/__tests__/libs/dates.spec.d.ts +1 -1
  310. package/dist/esm/__tests__/libs/dates.spec.js +128 -128
  311. package/dist/esm/__tests__/libs/domain.spec.d.ts +1 -1
  312. package/dist/esm/__tests__/libs/domain.spec.js +105 -105
  313. package/dist/esm/__tests__/libs/numbers.spec.d.ts +1 -1
  314. package/dist/esm/__tests__/libs/numbers.spec.js +259 -259
  315. package/dist/esm/__tests__/s3-client/s3-client.spec.d.ts +1 -1
  316. package/dist/esm/__tests__/s3-client/s3-client.spec.js +31 -31
  317. package/dist/esm/__tests__/shopify/shopify-graphql-transformer.spec.d.ts +1 -1
  318. package/dist/esm/__tests__/shopify/shopify-graphql-transformer.spec.js +33 -33
  319. package/dist/esm/__tests__/unit/libs/api-router/public-api-router.spec.d.ts +1 -1
  320. package/dist/esm/__tests__/unit/libs/api-router/public-api-router.spec.js +156 -156
  321. package/dist/esm/__tests__/unit/libs/api-router/route-matcher.spec.d.ts +1 -1
  322. package/dist/esm/__tests__/unit/libs/api-router/route-matcher.spec.js +67 -67
  323. package/dist/esm/clients/generic/cognito-client.d.ts +23 -23
  324. package/dist/esm/clients/generic/cognito-client.js +204 -204
  325. package/dist/esm/clients/generic/dynamodb-client.d.ts +18 -18
  326. package/dist/esm/clients/generic/dynamodb-client.js +168 -168
  327. package/dist/esm/clients/generic/eventbridge-client.d.ts +14 -14
  328. package/dist/esm/clients/generic/eventbridge-client.js +47 -47
  329. package/dist/esm/clients/generic/http-client.d.ts +14 -14
  330. package/dist/esm/clients/generic/http-client.js +53 -53
  331. package/dist/esm/clients/generic/index.d.ts +13 -13
  332. package/dist/esm/clients/generic/index.js +13 -13
  333. package/dist/esm/clients/generic/lambda-invoke-client.d.ts +10 -10
  334. package/dist/esm/clients/generic/lambda-invoke-client.js +35 -35
  335. package/dist/esm/clients/generic/location-client.d.ts +8 -8
  336. package/dist/esm/clients/generic/location-client.js +27 -27
  337. package/dist/esm/clients/generic/redis-client.d.ts +33 -33
  338. package/dist/esm/clients/generic/redis-client.js +184 -184
  339. package/dist/esm/clients/generic/s3-client.d.ts +23 -23
  340. package/dist/esm/clients/generic/s3-client.js +209 -209
  341. package/dist/esm/clients/generic/singlestore-db-client.d.ts +14 -14
  342. package/dist/esm/clients/generic/singlestore-db-client.js +40 -40
  343. package/dist/esm/clients/generic/sqs-bundled-client.d.ts +15 -15
  344. package/dist/esm/clients/generic/sqs-bundled-client.js +307 -307
  345. package/dist/esm/clients/generic/sqs-bundled-client.types.d.ts +53 -53
  346. package/dist/esm/clients/generic/sqs-bundled-client.types.js +14 -14
  347. package/dist/esm/clients/generic/sqs-client.d.ts +53 -53
  348. package/dist/esm/clients/generic/sqs-client.js +281 -281
  349. package/dist/esm/clients/generic/sqs-unbundle.d.ts +32 -32
  350. package/dist/esm/clients/generic/sqs-unbundle.js +137 -137
  351. package/dist/esm/clients/index.d.ts +3 -3
  352. package/dist/esm/clients/index.js +3 -3
  353. package/dist/esm/clients/internal-api/accounts-client.d.ts +91 -91
  354. package/dist/esm/clients/internal-api/accounts-client.js +125 -125
  355. package/dist/esm/clients/internal-api/cache-lambda-client.d.ts +26 -26
  356. package/dist/esm/clients/internal-api/cache-lambda-client.js +85 -85
  357. package/dist/esm/clients/internal-api/db-management-client.d.ts +18 -18
  358. package/dist/esm/clients/internal-api/db-management-client.js +32 -32
  359. package/dist/esm/clients/internal-api/destinations-client.d.ts +34 -34
  360. package/dist/esm/clients/internal-api/destinations-client.js +75 -75
  361. package/dist/esm/clients/internal-api/event-collector-client.d.ts +20 -20
  362. package/dist/esm/clients/internal-api/event-collector-client.js +32 -32
  363. package/dist/esm/clients/internal-api/identity-client.d.ts +31 -31
  364. package/dist/esm/clients/internal-api/identity-client.js +87 -87
  365. package/dist/esm/clients/internal-api/index.d.ts +9 -9
  366. package/dist/esm/clients/internal-api/index.js +9 -9
  367. package/dist/esm/clients/internal-api/shopify-app-install-client.d.ts +37 -37
  368. package/dist/esm/clients/internal-api/shopify-app-install-client.js +77 -77
  369. package/dist/esm/clients/internal-api/subscriptions-client.d.ts +26 -26
  370. package/dist/esm/clients/internal-api/subscriptions-client.js +73 -73
  371. package/dist/esm/clients/internal-api/users-auth-client.d.ts +35 -35
  372. package/dist/esm/clients/internal-api/users-auth-client.js +106 -106
  373. package/dist/esm/clients/third-party/audience-acuity/acuity-api-service.d.ts +8 -0
  374. package/dist/esm/clients/third-party/audience-acuity/acuity-api-service.js +51 -0
  375. package/dist/esm/clients/third-party/audience-acuity/acuity-api-service.js.map +1 -0
  376. package/dist/esm/clients/third-party/audience-acuity/acuity-client.d.ts +9 -0
  377. package/dist/esm/clients/third-party/audience-acuity/acuity-client.js +31 -0
  378. package/dist/esm/clients/third-party/audience-acuity/acuity-client.js.map +1 -0
  379. package/dist/esm/clients/third-party/audience-acuity/acuity-helpers.d.ts +2 -0
  380. package/dist/esm/clients/third-party/audience-acuity/acuity-helpers.js +19 -0
  381. package/dist/esm/clients/third-party/audience-acuity/acuity-helpers.js.map +1 -0
  382. package/dist/esm/clients/third-party/audience-acuity/acuity-types.d.ts +74 -0
  383. package/dist/esm/clients/third-party/audience-acuity/acuity-types.js +2 -0
  384. package/dist/esm/clients/third-party/audience-acuity/acuity-types.js.map +1 -0
  385. package/dist/esm/clients/third-party/audience-acuity/index.d.ts +4 -0
  386. package/dist/esm/clients/third-party/audience-acuity/index.js +5 -0
  387. package/dist/esm/clients/third-party/audience-acuity/index.js.map +1 -0
  388. package/dist/esm/clients/third-party/emailable-client.d.ts +7 -7
  389. package/dist/esm/clients/third-party/emailable-client.js +21 -21
  390. package/dist/esm/clients/third-party/exchange-rate-api-client.d.ts +17 -17
  391. package/dist/esm/clients/third-party/exchange-rate-api-client.js +15 -15
  392. package/dist/esm/clients/third-party/index.d.ts +5 -4
  393. package/dist/esm/clients/third-party/index.js +5 -4
  394. package/dist/esm/clients/third-party/index.js.map +1 -1
  395. package/dist/esm/clients/third-party/loops-client.d.ts +10 -10
  396. package/dist/esm/clients/third-party/loops-client.js +26 -26
  397. package/dist/esm/clients/third-party/shopify/graphql-order-queries.d.ts +25 -25
  398. package/dist/esm/clients/third-party/shopify/graphql-order-queries.js +1 -1
  399. package/dist/esm/clients/third-party/shopify/graphql-product-queries.d.ts +2 -2
  400. package/dist/esm/clients/third-party/shopify/graphql-product-queries.js +2 -2
  401. package/dist/esm/clients/third-party/shopify/shopify-graphql-client.d.ts +10 -10
  402. package/dist/esm/clients/third-party/shopify/shopify-graphql-client.js +157 -157
  403. package/dist/esm/clients/third-party/shopify-client.d.ts +29 -29
  404. package/dist/esm/clients/third-party/shopify-client.js +142 -142
  405. package/dist/esm/constants/index.d.ts +1 -1
  406. package/dist/esm/constants/index.js +1 -1
  407. package/dist/esm/constants/sqs.d.ts +20 -20
  408. package/dist/esm/constants/sqs.js +22 -22
  409. package/dist/esm/helpers/account-users-helper.d.ts +2 -2
  410. package/dist/esm/helpers/account-users-helper.js +18 -18
  411. package/dist/esm/helpers/api-key-auth-helper.d.ts +9 -9
  412. package/dist/esm/helpers/api-key-auth-helper.js +35 -35
  413. package/dist/esm/helpers/api-key-authorizer-helper.d.ts +36 -36
  414. package/dist/esm/helpers/api-key-authorizer-helper.js +83 -83
  415. package/dist/esm/helpers/identity-cache-helper.d.ts +21 -21
  416. package/dist/esm/helpers/identity-cache-helper.js +151 -151
  417. package/dist/esm/helpers/index.d.ts +9 -9
  418. package/dist/esm/helpers/index.js +9 -9
  419. package/dist/esm/helpers/input-validation-helper.d.ts +3 -3
  420. package/dist/esm/helpers/input-validation-helper.js +18 -18
  421. package/dist/esm/helpers/logging-helper.d.ts +16 -16
  422. package/dist/esm/helpers/logging-helper.js +56 -56
  423. package/dist/esm/helpers/response-helper.d.ts +18 -18
  424. package/dist/esm/helpers/response-helper.js +37 -37
  425. package/dist/esm/helpers/shopify-helper.d.ts +9 -9
  426. package/dist/esm/helpers/shopify-helper.js +21 -21
  427. package/dist/esm/helpers/sqs-utils.d.ts +6 -6
  428. package/dist/esm/helpers/sqs-utils.js +9 -9
  429. package/dist/esm/index.d.ts +7 -7
  430. package/dist/esm/index.js +7 -7
  431. package/dist/esm/libs/api-router/index.d.ts +2 -2
  432. package/dist/esm/libs/api-router/index.js +2 -2
  433. package/dist/esm/libs/api-router/public-api-router.d.ts +3 -3
  434. package/dist/esm/libs/api-router/public-api-router.js +32 -32
  435. package/dist/esm/libs/api-router/route-matcher.d.ts +21 -21
  436. package/dist/esm/libs/api-router/route-matcher.js +30 -30
  437. package/dist/esm/libs/click-id-parser.d.ts +23 -23
  438. package/dist/esm/libs/click-id-parser.js +45 -45
  439. package/dist/esm/libs/compression.d.ts +2 -2
  440. package/dist/esm/libs/compression.js +25 -25
  441. package/dist/esm/libs/cookie.d.ts +17 -17
  442. package/dist/esm/libs/cookie.js +70 -70
  443. package/dist/esm/libs/crypto.d.ts +4 -4
  444. package/dist/esm/libs/crypto.js +15 -15
  445. package/dist/esm/libs/csv.d.ts +2 -2
  446. package/dist/esm/libs/csv.js +30 -30
  447. package/dist/esm/libs/currency.d.ts +1 -1
  448. package/dist/esm/libs/currency.js +22 -22
  449. package/dist/esm/libs/dates.d.ts +12 -12
  450. package/dist/esm/libs/dates.js +83 -83
  451. package/dist/esm/libs/domain.d.ts +2 -2
  452. package/dist/esm/libs/domain.js +33 -33
  453. package/dist/esm/libs/emails.d.ts +6 -6
  454. package/dist/esm/libs/emails.js +115 -115
  455. package/dist/esm/libs/http-error.d.ts +21 -21
  456. package/dist/esm/libs/http-error.js +59 -59
  457. package/dist/esm/libs/http-status-codes.d.ts +58 -58
  458. package/dist/esm/libs/http-status-codes.js +59 -59
  459. package/dist/esm/libs/index.d.ts +18 -18
  460. package/dist/esm/libs/index.js +18 -18
  461. package/dist/esm/libs/numbers.d.ts +1 -1
  462. package/dist/esm/libs/numbers.js +11 -11
  463. package/dist/esm/libs/referrer-parser/index.d.ts +2 -2
  464. package/dist/esm/libs/referrer-parser/index.js +2 -2
  465. package/dist/esm/libs/referrer-parser/referrer-data.d.ts +9 -9
  466. package/dist/esm/libs/referrer-parser/referrer-data.js +3304 -3304
  467. package/dist/esm/libs/referrer-parser/referrer-parser-util.d.ts +20 -20
  468. package/dist/esm/libs/referrer-parser/referrer-parser-util.js +124 -124
  469. package/dist/esm/libs/strings.d.ts +3 -3
  470. package/dist/esm/libs/strings.js +40 -40
  471. package/dist/esm/libs/traits.d.ts +6 -6
  472. package/dist/esm/libs/traits.js +54 -54
  473. package/dist/esm/libs/url.d.ts +1 -1
  474. package/dist/esm/libs/url.js +9 -9
  475. package/dist/esm/services/cache/generic-cached-object.d.ts +5 -5
  476. package/dist/esm/services/cache/generic-cached-object.js +1 -1
  477. package/dist/esm/services/cache/index.d.ts +1 -1
  478. package/dist/esm/services/cache/index.js +1 -1
  479. package/dist/esm/services/cache/product-cache-service.d.ts +21 -21
  480. package/dist/esm/services/cache/product-cache-service.js +68 -68
  481. package/dist/esm/services/currency-exchange-rate-lookup-service.d.ts +11 -11
  482. package/dist/esm/services/currency-exchange-rate-lookup-service.js +62 -62
  483. package/dist/esm/services/db/accounts-db-service.d.ts +9 -9
  484. package/dist/esm/services/db/accounts-db-service.js +29 -29
  485. package/dist/esm/services/db/api-keys-db-service.d.ts +10 -10
  486. package/dist/esm/services/db/api-keys-db-service.js +32 -32
  487. package/dist/esm/services/db/currency-exchange-rates-db-service.d.ts +21 -21
  488. package/dist/esm/services/db/currency-exchange-rates-db-service.js +35 -35
  489. package/dist/esm/services/db/destinations-db-service.d.ts +12 -12
  490. package/dist/esm/services/db/destinations-db-service.js +72 -72
  491. package/dist/esm/services/db/identity-cache-db-service.d.ts +28 -28
  492. package/dist/esm/services/db/identity-cache-db-service.js +313 -313
  493. package/dist/esm/services/db/index.d.ts +13 -13
  494. package/dist/esm/services/db/index.js +13 -13
  495. package/dist/esm/services/db/log-events-db-service.d.ts +11 -11
  496. package/dist/esm/services/db/log-events-db-service.js +177 -177
  497. package/dist/esm/services/db/pixels-db-service.d.ts +8 -8
  498. package/dist/esm/services/db/pixels-db-service.js +31 -31
  499. package/dist/esm/services/db/purchasable-contacts-db-service.d.ts +9 -9
  500. package/dist/esm/services/db/purchasable-contacts-db-service.js +39 -39
  501. package/dist/esm/services/db/purchased-contacts-db-service.d.ts +17 -17
  502. package/dist/esm/services/db/purchased-contacts-db-service.js +139 -139
  503. package/dist/esm/services/db/shopify-app-installs-db-service.d.ts +8 -8
  504. package/dist/esm/services/db/shopify-app-installs-db-service.js +47 -47
  505. package/dist/esm/services/db/shopify-products-cache-db-service.d.ts +16 -16
  506. package/dist/esm/services/db/shopify-products-cache-db-service.js +66 -66
  507. package/dist/esm/services/db/subscriptions-db-service.d.ts +10 -10
  508. package/dist/esm/services/db/subscriptions-db-service.js +30 -30
  509. package/dist/esm/services/db/tracking-events-db-service.d.ts +20 -20
  510. package/dist/esm/services/db/tracking-events-db-service.js +161 -161
  511. package/dist/esm/services/eventbridge-integration-service.d.ts +9 -9
  512. package/dist/esm/services/eventbridge-integration-service.js +24 -24
  513. package/dist/esm/services/events/index.d.ts +3 -3
  514. package/dist/esm/services/events/index.js +3 -3
  515. package/dist/esm/services/events/log-event-service.d.ts +19 -19
  516. package/dist/esm/services/events/log-event-service.js +73 -73
  517. package/dist/esm/services/events/metric-event-service.d.ts +9 -9
  518. package/dist/esm/services/events/metric-event-service.js +45 -45
  519. package/dist/esm/services/events/tracking-event-sqs-service.d.ts +8 -8
  520. package/dist/esm/services/events/tracking-event-sqs-service.js +30 -30
  521. package/dist/esm/services/generic-cache-service.d.ts +7 -7
  522. package/dist/esm/services/generic-cache-service.js +29 -29
  523. package/dist/esm/services/index.d.ts +8 -8
  524. package/dist/esm/services/index.js +8 -8
  525. package/dist/esm/services/ipdata-lookup-service.d.ts +20 -20
  526. package/dist/esm/services/ipdata-lookup-service.js +108 -108
  527. package/dist/esm/services/shopify/index.d.ts +2 -2
  528. package/dist/esm/services/shopify/index.js +2 -2
  529. package/dist/esm/services/shopify/products/index.d.ts +1 -1
  530. package/dist/esm/services/shopify/products/index.js +1 -1
  531. package/dist/esm/services/shopify/products/shopify-products-serviceV2.d.ts +17 -17
  532. package/dist/esm/services/shopify/products/shopify-products-serviceV2.js +108 -108
  533. package/dist/esm/services/shopify/shopify-graphql-transformer.d.ts +8 -8
  534. package/dist/esm/services/shopify/shopify-graphql-transformer.js +138 -138
  535. package/dist/esm/types/api-response.d.ts +6 -6
  536. package/dist/esm/types/api-response.js +1 -1
  537. package/dist/esm/types/index.d.ts +3 -3
  538. package/dist/esm/types/index.js +3 -3
  539. package/dist/esm/types/internal-events/event-detail-types.d.ts +20 -20
  540. package/dist/esm/types/internal-events/event-detail-types.js +24 -24
  541. package/dist/esm/types/internal-events/index.d.ts +1 -1
  542. package/dist/esm/types/internal-events/index.js +1 -1
  543. package/dist/esm/types/shopify-graphql-types/admin.generated.d.ts +123 -123
  544. package/dist/esm/types/shopify-graphql-types/admin.generated.js +1 -1
  545. package/dist/esm/types/shopify-graphql-types/admin.types.d.ts +26289 -26289
  546. package/dist/esm/types/shopify-graphql-types/admin.types.js +5299 -5299
  547. package/dist/esm/types/shopify-graphql-types/index.d.ts +2 -2
  548. package/dist/esm/types/shopify-graphql-types/index.js +2 -2
  549. package/dist/esm/types/shopify-rest-types.d.ts +767 -767
  550. package/dist/esm/types/shopify-rest-types.js +1 -1
  551. package/dist/esm/utils/compression.d.ts +36 -36
  552. package/dist/esm/utils/compression.js +187 -187
  553. package/dist/esm/utils/index.d.ts +3 -3
  554. package/dist/esm/utils/index.js +3 -3
  555. package/dist/esm/utils/retry-envelope.d.ts +12 -12
  556. package/dist/esm/utils/retry-envelope.js +22 -22
  557. package/dist/esm/utils/size.d.ts +2 -2
  558. package/dist/esm/utils/size.js +44 -44
  559. package/package.json +134 -134
@@ -1,583 +1,583 @@
1
- import { BundledSQSClient } from '../../clients/generic/sqs-bundled-client';
2
- import { CompressionAlgorithm } from '../../clients/generic/sqs-bundled-client.types';
3
- import { unbundleRecords } from '../../clients/generic/sqs-unbundle';
4
- import { compress } from '../../utils/compression';
5
- import { createSqsLimits, SQS_1MB } from '../../constants/sqs';
6
- jest.mock('../../helpers/logging-helper', () => ({
7
- Logger: {
8
- debug: jest.fn(),
9
- info: jest.fn(),
10
- warn: jest.fn(),
11
- error: jest.fn(),
12
- },
13
- }));
14
- function makeTestEvent(id, name = 'test', value, metadata) {
15
- return { id, name, value, metadata };
16
- }
17
- function makeRealisticTrackingEvent(index) {
18
- return {
19
- anonymousId: `anon-${index}-${Math.random().toString(36).substring(7)}`,
20
- accountId: `acc-${index % 10}`,
21
- pixelId: `pixel-${index % 5}`,
22
- eventType: ['track', 'identify', 'page'][index % 3] ?? 'track',
23
- eventName: ['purchase', 'add_to_cart', 'page_view', 'begin_checkout'][index % 4] ?? 'track',
24
- timestamp: new Date(Date.now() - index * 1000).toISOString(),
25
- context: {
26
- page: {
27
- url: `https://example.com/product/${index}`,
28
- path: `/product/${index}`,
29
- title: `Product ${index} - Example Store`,
30
- referrer: index % 3 === 0 ? 'https://google.com' : '',
31
- },
32
- userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
33
- ip: `192.168.${index % 256}.${(index * 7) % 256}`,
34
- locale: ['en-US', 'en-GB', 'de-DE', 'fr-FR'][index % 4] ?? 'en-US',
35
- timezone: 'America/New_York',
36
- },
37
- properties: {
38
- productId: `prod-${index}`,
39
- productName: `Amazing Product ${index}`,
40
- price: 29.99 + (index % 100),
41
- currency: 'USD',
42
- quantity: (index % 5) + 1,
43
- category: ['Electronics', 'Clothing', 'Home', 'Sports'][index % 4],
44
- customFields: {
45
- brand: `Brand${index % 20}`,
46
- sku: `SKU-${index}-${Date.now()}`,
47
- inStock: index % 2 === 0,
48
- },
49
- },
50
- shopifyData: index % 3 === 0
51
- ? {
52
- checkoutToken: `checkout-${index}`,
53
- orderId: index % 5 === 0 ? `order-${index}` : undefined,
54
- customerEmail: `customer${index}@example.com`,
55
- lineItems: Array.from({ length: (index % 3) + 1 }, (_, j) => ({
56
- variantId: `variant-${index}-${j}`,
57
- title: `Line Item ${j}`,
58
- price: 19.99 + j * 10,
59
- quantity: j + 1,
60
- })),
61
- }
62
- : undefined,
63
- };
64
- }
65
- function simulateSqsDelivery(envelopes) {
66
- return envelopes.map((env, i) => ({
67
- messageId: `msg-${i}-${Date.now()}`,
68
- body: JSON.stringify({ messageBody: env }),
69
- }));
70
- }
71
- describe('Full Round-Trip: Producer → Consumer', () => {
72
- let mockSqsClient;
73
- let capturedEnvelopes;
74
- beforeEach(() => {
75
- capturedEnvelopes = [];
76
- mockSqsClient = {
77
- buildAndSendMessagesV2: jest.fn(async (_type, envelopes) => {
78
- capturedEnvelopes.push(...envelopes);
79
- return {
80
- successCount: envelopes.length,
81
- failedCount: 0,
82
- batchCount: 1,
83
- failedMessages: [],
84
- };
85
- }),
86
- queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/test-queue',
87
- limits: createSqsLimits(SQS_1MB),
88
- };
89
- });
90
- describe('1. Basic round-trip test (100 items)', () => {
91
- it('100 items survive complete round-trip with deep equality', async () => {
92
- const bundledClient = new BundledSQSClient(mockSqsClient, {
93
- compression: CompressionAlgorithm.ZSTD,
94
- compressionLevel: 3,
95
- });
96
- const originalItems = Array.from({ length: 100 }, (_, i) => ({
97
- id: `event-${i}`,
98
- name: 'purchase',
99
- value: i * 99.99,
100
- metadata: { source: 'test', index: i },
101
- }));
102
- await bundledClient.sendBundled('test', originalItems);
103
- expect(capturedEnvelopes.length).toBeGreaterThanOrEqual(1);
104
- const sqsRecords = simulateSqsDelivery(capturedEnvelopes);
105
- const { items: recoveredItems, stats } = await unbundleRecords(sqsRecords);
106
- expect(recoveredItems).toHaveLength(100);
107
- expect(recoveredItems).toEqual(originalItems);
108
- expect(stats.bundledSqsRecords).toBe(capturedEnvelopes.length);
109
- expect(stats.failedRecords).toBe(0);
110
- });
111
- it('preserves all primitive types correctly', async () => {
112
- const bundledClient = new BundledSQSClient(mockSqsClient, {
113
- compression: CompressionAlgorithm.ZSTD,
114
- });
115
- const originalItems = [
116
- { id: 'str', name: 'string-test', value: undefined },
117
- { id: 'num', name: 'number-test', value: 123.456 },
118
- { id: 'zero', name: 'zero-test', value: 0 },
119
- { id: 'neg', name: 'negative-test', value: -999.99 },
120
- { id: 'big', name: 'big-number', value: Number.MAX_SAFE_INTEGER },
121
- ];
122
- await bundledClient.sendBundled('test', originalItems);
123
- const sqsRecords = simulateSqsDelivery(capturedEnvelopes);
124
- const { items: recoveredItems } = await unbundleRecords(sqsRecords);
125
- expect(recoveredItems).toEqual(originalItems);
126
- });
127
- });
128
- describe('2. Large scale round-trip (1000 items)', () => {
129
- it('1000 items survive round-trip with no data loss', async () => {
130
- const bundledClient = new BundledSQSClient(mockSqsClient, {
131
- compression: CompressionAlgorithm.ZSTD,
132
- compressionLevel: 3,
133
- });
134
- const originalItems = Array.from({ length: 1000 }, (_, i) => ({
135
- id: `event-${i.toString().padStart(4, '0')}`,
136
- name: `type-${i % 10}`,
137
- value: i * 1.5,
138
- metadata: {
139
- batch: Math.floor(i / 100),
140
- position: i % 100,
141
- timestamp: Date.now() + i,
142
- },
143
- }));
144
- await bundledClient.sendBundled('test', originalItems);
145
- const sqsRecords = simulateSqsDelivery(capturedEnvelopes);
146
- const { items: recoveredItems, stats } = await unbundleRecords(sqsRecords);
147
- expect(recoveredItems).toHaveLength(1000);
148
- expect(stats.totalItems).toBe(1000);
149
- expect(stats.failedRecords).toBe(0);
150
- const recoveredIds = recoveredItems.map((item) => item.id);
151
- const originalIds = originalItems.map((item) => item.id);
152
- expect(new Set(recoveredIds)).toEqual(new Set(originalIds));
153
- expect(recoveredItems).toEqual(originalItems);
154
- });
155
- it('preserves order within each bundle', async () => {
156
- const bundledClient = new BundledSQSClient(mockSqsClient, {
157
- compression: CompressionAlgorithm.ZSTD,
158
- maxItemsPerBundle: 100,
159
- });
160
- const originalItems = Array.from({ length: 500 }, (_, i) => makeTestEvent(`seq-${i}`, 'ordered', i));
161
- await bundledClient.sendBundled('test', originalItems);
162
- const sqsRecords = simulateSqsDelivery(capturedEnvelopes);
163
- const { items: recoveredItems } = await unbundleRecords(sqsRecords);
164
- let currentBundleStart = 0;
165
- for (const envelope of capturedEnvelopes) {
166
- const bundleSize = envelope.n;
167
- const bundleItems = recoveredItems.slice(currentBundleStart, currentBundleStart + bundleSize);
168
- for (let j = 1; j < bundleItems.length; j++) {
169
- const prevValue = bundleItems[j - 1]?.value ?? 0;
170
- const currValue = bundleItems[j]?.value ?? 0;
171
- expect(currValue).toBeGreaterThan(prevValue);
172
- }
173
- currentBundleStart += bundleSize;
174
- }
175
- });
176
- });
177
- describe('3. Compression round-trip verification', () => {
178
- it('ZSTD compressed data decompresses to byte-exact JSON', async () => {
179
- const bundledClient = new BundledSQSClient(mockSqsClient, {
180
- compression: CompressionAlgorithm.ZSTD,
181
- compressionLevel: 5,
182
- });
183
- const originalItems = Array.from({ length: 50 }, (_, i) => ({
184
- id: `compress-test-${i}`,
185
- name: 'compression-verification',
186
- value: i * 123.456789,
187
- metadata: {
188
- unicode: '日本語テスト',
189
- emoji: '🚀✨🎉',
190
- special: 'line\nbreak\ttab\r\nwindows',
191
- quotes: '"double" and \'single\'',
192
- },
193
- }));
194
- const originalJson = JSON.stringify(originalItems);
195
- await bundledClient.sendBundled('test', originalItems);
196
- const sqsRecords = simulateSqsDelivery(capturedEnvelopes);
197
- const { items: recoveredItems } = await unbundleRecords(sqsRecords);
198
- const recoveredJson = JSON.stringify(recoveredItems);
199
- expect(recoveredJson).toBe(originalJson);
200
- expect(recoveredItems).toEqual(originalItems);
201
- });
202
- it('verifies actual compression occurred', async () => {
203
- const bundledClient = new BundledSQSClient(mockSqsClient, {
204
- compression: CompressionAlgorithm.ZSTD,
205
- compressionLevel: 3,
206
- });
207
- const originalItems = Array.from({ length: 200 }, (_, i) => ({
208
- id: `item-${i}`,
209
- name: 'repetitive-data-for-compression',
210
- value: i,
211
- metadata: {
212
- commonField1: 'this is repeated many times',
213
- commonField2: 'another repeated value',
214
- },
215
- }));
216
- const originalSizeBytes = Buffer.byteLength(JSON.stringify(originalItems), 'utf8');
217
- await bundledClient.sendBundled('test', originalItems);
218
- for (const envelope of capturedEnvelopes) {
219
- expect(envelope.c).toBe(CompressionAlgorithm.ZSTD);
220
- expect(typeof envelope.p).toBe('string');
221
- const compressedSizeBytes = Buffer.byteLength(envelope.p, 'utf8');
222
- expect(compressedSizeBytes).toBeLessThan(originalSizeBytes);
223
- }
224
- });
225
- });
226
- describe('4. Mixed message types round-trip', () => {
227
- it('handles mix of NONE and ZSTD compression in same batch', async () => {
228
- const zstdClient = new BundledSQSClient(mockSqsClient, {
229
- compression: CompressionAlgorithm.ZSTD,
230
- });
231
- const noneClient = new BundledSQSClient(mockSqsClient, {
232
- compression: CompressionAlgorithm.NONE,
233
- });
234
- const zstdItems = [
235
- makeTestEvent('zstd-1', 'compressed', 100),
236
- makeTestEvent('zstd-2', 'compressed', 200),
237
- ];
238
- const noneItems = [
239
- makeTestEvent('none-1', 'uncompressed', 300),
240
- makeTestEvent('none-2', 'uncompressed', 400),
241
- ];
242
- await zstdClient.sendBundled('test', zstdItems);
243
- const zstdEnvelopes = [...capturedEnvelopes];
244
- capturedEnvelopes = [];
245
- await noneClient.sendBundled('test', noneItems);
246
- const noneEnvelopes = [...capturedEnvelopes];
247
- const allEnvelopes = [...zstdEnvelopes, ...noneEnvelopes];
248
- const sqsRecords = simulateSqsDelivery(allEnvelopes);
249
- const { items: recoveredItems, stats } = await unbundleRecords(sqsRecords);
250
- expect(recoveredItems).toHaveLength(4);
251
- expect(stats.bundledSqsRecords).toBe(2);
252
- expect(stats.failedRecords).toBe(0);
253
- const zstdRecovered = recoveredItems.filter((item) => item.name === 'compressed');
254
- const noneRecovered = recoveredItems.filter((item) => item.name === 'uncompressed');
255
- expect(zstdRecovered).toHaveLength(2);
256
- expect(noneRecovered).toHaveLength(2);
257
- expect(zstdRecovered).toEqual(zstdItems);
258
- expect(noneRecovered).toEqual(noneItems);
259
- });
260
- it('handles mixed bundled and legacy messages', async () => {
261
- const bundledClient = new BundledSQSClient(mockSqsClient, {
262
- compression: CompressionAlgorithm.ZSTD,
263
- });
264
- const bundledItems = [
265
- makeTestEvent('bundled-1', 'bundled', 100),
266
- makeTestEvent('bundled-2', 'bundled', 200),
267
- ];
268
- await bundledClient.sendBundled('test', bundledItems);
269
- const legacyRecord = {
270
- messageId: 'legacy-msg-1',
271
- body: JSON.stringify(makeTestEvent('legacy-1', 'legacy', 999)),
272
- };
273
- const wrappedLegacyRecord = {
274
- messageId: 'legacy-msg-2',
275
- body: JSON.stringify({ messageBody: makeTestEvent('legacy-2', 'wrapped-legacy', 888) }),
276
- };
277
- const bundledRecords = simulateSqsDelivery(capturedEnvelopes);
278
- const allRecords = [...bundledRecords, legacyRecord, wrappedLegacyRecord];
279
- const { items: recoveredItems, stats } = await unbundleRecords(allRecords);
280
- expect(recoveredItems).toHaveLength(4);
281
- expect(stats.bundledSqsRecords).toBe(1);
282
- expect(stats.legacySqsRecords).toBe(2);
283
- expect(stats.failedRecords).toBe(0);
284
- });
285
- });
286
- describe('5. Error injection round-trip', () => {
287
- it('recovers valid bundles while reporting corrupted bundle in failedMessageIds', async () => {
288
- const bundledClient = new BundledSQSClient(mockSqsClient, {
289
- compression: CompressionAlgorithm.ZSTD,
290
- });
291
- const validItems1 = [makeTestEvent('valid-1', 'first-batch', 100)];
292
- const validItems2 = [makeTestEvent('valid-2', 'second-batch', 200)];
293
- await bundledClient.sendBundled('test', validItems1);
294
- const envelope1 = { ...capturedEnvelopes[0] };
295
- capturedEnvelopes = [];
296
- await bundledClient.sendBundled('test', validItems2);
297
- const envelope2 = { ...capturedEnvelopes[0] };
298
- const corruptedEnvelope = {
299
- v: 1,
300
- c: CompressionAlgorithm.ZSTD,
301
- n: 10,
302
- s: 1000,
303
- p: 'dGhpcyBpcyBub3QgdmFsaWQgenN0ZA==',
304
- };
305
- const records = [
306
- { messageId: 'valid-1', body: JSON.stringify({ messageBody: envelope1 }) },
307
- { messageId: 'corrupted', body: JSON.stringify({ messageBody: corruptedEnvelope }) },
308
- { messageId: 'valid-2', body: JSON.stringify({ messageBody: envelope2 }) },
309
- ];
310
- const { items: recoveredItems, failedMessageIds, stats } = await unbundleRecords(records);
311
- expect(recoveredItems).toHaveLength(2);
312
- expect(recoveredItems[0]?.id).toBe('valid-1');
313
- expect(recoveredItems[1]?.id).toBe('valid-2');
314
- expect(failedMessageIds).toContain('corrupted');
315
- expect(failedMessageIds).toHaveLength(1);
316
- expect(stats.bundledSqsRecords).toBe(2);
317
- expect(stats.failedRecords).toBe(1);
318
- expect(stats.totalItems).toBe(2);
319
- });
320
- it('handles invalid JSON gracefully', async () => {
321
- const bundledClient = new BundledSQSClient(mockSqsClient, {
322
- compression: CompressionAlgorithm.ZSTD,
323
- });
324
- const validItems = [makeTestEvent('valid', 'test', 100)];
325
- await bundledClient.sendBundled('test', validItems);
326
- const records = [
327
- ...simulateSqsDelivery(capturedEnvelopes),
328
- { messageId: 'invalid-json-1', body: 'not-valid-json{{{' },
329
- { messageId: 'invalid-json-2', body: '{"incomplete":' },
330
- { messageId: 'empty', body: '' },
331
- ];
332
- const { items: recoveredItems, failedMessageIds, stats } = await unbundleRecords(records);
333
- expect(recoveredItems).toHaveLength(1);
334
- expect(recoveredItems[0]?.id).toBe('valid');
335
- expect(failedMessageIds).toContain('invalid-json-1');
336
- expect(failedMessageIds).toContain('invalid-json-2');
337
- expect(failedMessageIds).toContain('empty');
338
- expect(failedMessageIds).toHaveLength(3);
339
- expect(stats.failedRecords).toBe(3);
340
- });
341
- it('handles decompression bomb protection', async () => {
342
- const largeItems = Array.from({ length: 1000 }, (_, i) => ({
343
- id: `item-${i}`,
344
- data: 'x'.repeat(1000),
345
- }));
346
- const json = JSON.stringify(largeItems);
347
- const compressed = await compress(Buffer.from(json, 'utf8'), CompressionAlgorithm.ZSTD, 3);
348
- const base64 = compressed.toString('base64');
349
- const bigEnvelope = {
350
- v: 1,
351
- c: CompressionAlgorithm.ZSTD,
352
- n: 1000,
353
- s: json.length,
354
- p: base64,
355
- };
356
- const records = [{ messageId: 'too-large', body: JSON.stringify({ messageBody: bigEnvelope }) }];
357
- const { failedMessageIds, stats } = await unbundleRecords(records, {
358
- maxDecompressedSizeBytes: 100 * 1024,
359
- });
360
- expect(failedMessageIds).toContain('too-large');
361
- expect(stats.failedRecords).toBe(1);
362
- });
363
- });
364
- describe('6. Realistic event types round-trip', () => {
365
- it('TrackingEvent-like structures survive round-trip exactly', async () => {
366
- const realisticMockSqs = {
367
- buildAndSendMessagesV2: jest.fn(async (_type, envelopes) => {
368
- capturedEnvelopes = [];
369
- capturedEnvelopes.push(...envelopes);
370
- return {
371
- successCount: envelopes.length,
372
- failedCount: 0,
373
- batchCount: 1,
374
- failedMessages: [],
375
- };
376
- }),
377
- queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/test-queue',
378
- limits: createSqsLimits(SQS_1MB),
379
- };
380
- const bundledClient = new BundledSQSClient(realisticMockSqs, {
381
- compression: CompressionAlgorithm.ZSTD,
382
- compressionLevel: 3,
383
- });
384
- const originalEvents = Array.from({ length: 100 }, (_, i) => makeRealisticTrackingEvent(i));
385
- await bundledClient.sendBundled('tracking-events', originalEvents);
386
- const sqsRecords = simulateSqsDelivery(capturedEnvelopes);
387
- const { items: recoveredEvents, stats } = await unbundleRecords(sqsRecords);
388
- expect(recoveredEvents).toHaveLength(100);
389
- expect(stats.failedRecords).toBe(0);
390
- expect(recoveredEvents).toEqual(originalEvents);
391
- for (let i = 0; i < originalEvents.length; i++) {
392
- const original = originalEvents[i];
393
- const recovered = recoveredEvents[i];
394
- expect(recovered?.anonymousId).toBe(original?.anonymousId);
395
- expect(recovered?.context.page.url).toBe(original?.context.page.url);
396
- expect(recovered?.properties.price).toBe(original?.properties.price);
397
- if (original?.shopifyData) {
398
- expect(recovered?.shopifyData).toEqual(original.shopifyData);
399
- }
400
- }
401
- });
402
- it('preserves nested object structures exactly', async () => {
403
- const bundledClient = new BundledSQSClient(mockSqsClient, {
404
- compression: CompressionAlgorithm.ZSTD,
405
- });
406
- const deeplyNestedItems = [
407
- {
408
- id: 'nested-1',
409
- name: 'deep-nesting',
410
- nested: {
411
- level1: {
412
- level2: {
413
- deep: 'very deep value',
414
- },
415
- },
416
- },
417
- },
418
- {
419
- id: 'nested-2',
420
- name: 'with-arrays',
421
- tags: ['tag1', 'tag2', 'tag3'],
422
- metadata: {
423
- array: [1, 2, 3],
424
- object: { a: 1, b: 2 },
425
- mixed: [{ key: 'value' }, [1, 2, 3]],
426
- },
427
- },
428
- ];
429
- await bundledClient.sendBundled('test', deeplyNestedItems);
430
- const sqsRecords = simulateSqsDelivery(capturedEnvelopes);
431
- const { items: recoveredItems } = await unbundleRecords(sqsRecords);
432
- expect(recoveredItems).toEqual(deeplyNestedItems);
433
- expect(recoveredItems[0]?.nested?.level1.level2.deep).toBe('very deep value');
434
- expect(recoveredItems[1]?.tags).toEqual(['tag1', 'tag2', 'tag3']);
435
- });
436
- });
437
- describe('7. High compression ratio round-trip', () => {
438
- it('highly repetitive data compresses and decompresses correctly', async () => {
439
- const bundledClient = new BundledSQSClient(mockSqsClient, {
440
- compression: CompressionAlgorithm.ZSTD,
441
- compressionLevel: 3,
442
- });
443
- const repetitiveString = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
444
- const originalItems = Array.from({ length: 200 }, (_, i) => ({
445
- id: `repetitive-${i}`,
446
- name: repetitiveString,
447
- value: 42,
448
- metadata: {
449
- field1: repetitiveString,
450
- field2: repetitiveString,
451
- field3: repetitiveString,
452
- field4: repetitiveString,
453
- field5: repetitiveString,
454
- },
455
- }));
456
- const originalSizeBytes = Buffer.byteLength(JSON.stringify(originalItems), 'utf8');
457
- await bundledClient.sendBundled('test', originalItems);
458
- let totalCompressedSize = 0;
459
- for (const envelope of capturedEnvelopes) {
460
- expect(typeof envelope.p).toBe('string');
461
- totalCompressedSize += Buffer.byteLength(envelope.p, 'utf8');
462
- }
463
- const compressionRatio = originalSizeBytes / totalCompressedSize;
464
- expect(compressionRatio).toBeGreaterThan(5);
465
- const sqsRecords = simulateSqsDelivery(capturedEnvelopes);
466
- const { items: recoveredItems, stats } = await unbundleRecords(sqsRecords);
467
- expect(recoveredItems).toHaveLength(200);
468
- expect(recoveredItems).toEqual(originalItems);
469
- expect(stats.failedRecords).toBe(0);
470
- for (const item of recoveredItems) {
471
- expect(item.name).toBe(repetitiveString);
472
- expect(item.value).toBe(42);
473
- expect(item.metadata?.field1).toBe(repetitiveString);
474
- }
475
- });
476
- it('varying compression levels all round-trip correctly', async () => {
477
- const items = Array.from({ length: 50 }, (_, i) => makeTestEvent(`level-test-${i}`, 'compression-level', i));
478
- for (const level of [1, 3, 9, 15]) {
479
- capturedEnvelopes = [];
480
- const bundledClient = new BundledSQSClient(mockSqsClient, {
481
- compression: CompressionAlgorithm.ZSTD,
482
- compressionLevel: level,
483
- });
484
- await bundledClient.sendBundled('test', items);
485
- const sqsRecords = simulateSqsDelivery(capturedEnvelopes);
486
- const { items: recoveredItems, stats } = await unbundleRecords(sqsRecords);
487
- expect(recoveredItems).toEqual(items);
488
- expect(stats.failedRecords).toBe(0);
489
- }
490
- });
491
- });
492
- describe('8. Edge cases', () => {
493
- it('empty arrays survive round-trip', async () => {
494
- const bundledClient = new BundledSQSClient(mockSqsClient, {
495
- compression: CompressionAlgorithm.ZSTD,
496
- });
497
- const result = await bundledClient.sendBundled('test', []);
498
- expect(result.totalItems).toBe(0);
499
- expect(capturedEnvelopes).toHaveLength(0);
500
- });
501
- it('single item survives round-trip', async () => {
502
- const bundledClient = new BundledSQSClient(mockSqsClient, {
503
- compression: CompressionAlgorithm.ZSTD,
504
- });
505
- const singleItem = [makeTestEvent('single', 'lone-wolf', 999)];
506
- await bundledClient.sendBundled('test', singleItem);
507
- const sqsRecords = simulateSqsDelivery(capturedEnvelopes);
508
- const { items: recoveredItems } = await unbundleRecords(sqsRecords);
509
- expect(recoveredItems).toHaveLength(1);
510
- expect(recoveredItems).toEqual(singleItem);
511
- });
512
- it('special characters survive round-trip', async () => {
513
- const bundledClient = new BundledSQSClient(mockSqsClient, {
514
- compression: CompressionAlgorithm.ZSTD,
515
- });
516
- const specialCharsItems = [
517
- {
518
- id: 'unicode-日本語',
519
- name: '中文测试',
520
- metadata: {
521
- emoji: '🎉🚀✨💯',
522
- rtl: 'مرحبا بالعالم',
523
- escape: '\\n\\t\\r',
524
- quotes: '"double" \'single\' `backtick`',
525
- angle: '<script>alert("xss")</script>',
526
- null_char: 'before\x00after',
527
- },
528
- },
529
- ];
530
- await bundledClient.sendBundled('test', specialCharsItems);
531
- const sqsRecords = simulateSqsDelivery(capturedEnvelopes);
532
- const { items: recoveredItems } = await unbundleRecords(sqsRecords);
533
- expect(recoveredItems).toEqual(specialCharsItems);
534
- });
535
- it('null and undefined values are handled correctly', async () => {
536
- const bundledClient = new BundledSQSClient(mockSqsClient, {
537
- compression: CompressionAlgorithm.ZSTD,
538
- });
539
- const itemsWithNulls = [
540
- { id: 'null-test', name: 'test', value: undefined },
541
- {
542
- id: 'explicit-null',
543
- name: 'test',
544
- metadata: { key: null },
545
- },
546
- ];
547
- await bundledClient.sendBundled('test', itemsWithNulls);
548
- const sqsRecords = simulateSqsDelivery(capturedEnvelopes);
549
- const { items: recoveredItems } = await unbundleRecords(sqsRecords);
550
- expect(recoveredItems[0]?.value).toBeUndefined();
551
- expect(recoveredItems[1]?.metadata?.key).toBeNull();
552
- });
553
- it('recordMap correctly tracks items back to SQS messages', async () => {
554
- const bundledClient = new BundledSQSClient(mockSqsClient, {
555
- compression: CompressionAlgorithm.ZSTD,
556
- maxItemsPerBundle: 3,
557
- });
558
- const items = Array.from({ length: 10 }, (_, i) => makeTestEvent(`item-${i}`, 'tracking', i));
559
- await bundledClient.sendBundled('test', items);
560
- const sqsRecords = capturedEnvelopes.map((env, i) => ({
561
- messageId: `bundle-${i}`,
562
- body: JSON.stringify({ messageBody: env }),
563
- }));
564
- const { items: recoveredItems, recordMap, stats } = await unbundleRecords(sqsRecords);
565
- expect(recoveredItems).toHaveLength(10);
566
- const messageIdCounts = new Map();
567
- for (const item of recoveredItems) {
568
- const messageId = recordMap.get(item);
569
- expect(messageId).toBeDefined();
570
- if (messageId) {
571
- messageIdCounts.set(messageId, (messageIdCounts.get(messageId) ?? 0) + 1);
572
- }
573
- }
574
- expect(messageIdCounts.size).toBe(capturedEnvelopes.length);
575
- for (const [messageId, count] of messageIdCounts) {
576
- expect(count).toBeLessThanOrEqual(3);
577
- expect(messageId).toMatch(/^bundle-\d+$/);
578
- }
579
- expect(stats.bundledSqsRecords).toBe(capturedEnvelopes.length);
580
- });
581
- });
582
- });
1
+ import { BundledSQSClient } from '../../clients/generic/sqs-bundled-client';
2
+ import { CompressionAlgorithm } from '../../clients/generic/sqs-bundled-client.types';
3
+ import { unbundleRecords } from '../../clients/generic/sqs-unbundle';
4
+ import { compress } from '../../utils/compression';
5
+ import { createSqsLimits, SQS_1MB } from '../../constants/sqs';
6
+ jest.mock('../../helpers/logging-helper', () => ({
7
+ Logger: {
8
+ debug: jest.fn(),
9
+ info: jest.fn(),
10
+ warn: jest.fn(),
11
+ error: jest.fn(),
12
+ },
13
+ }));
14
+ function makeTestEvent(id, name = 'test', value, metadata) {
15
+ return { id, name, value, metadata };
16
+ }
17
+ function makeRealisticTrackingEvent(index) {
18
+ return {
19
+ anonymousId: `anon-${index}-${Math.random().toString(36).substring(7)}`,
20
+ accountId: `acc-${index % 10}`,
21
+ pixelId: `pixel-${index % 5}`,
22
+ eventType: ['track', 'identify', 'page'][index % 3] ?? 'track',
23
+ eventName: ['purchase', 'add_to_cart', 'page_view', 'begin_checkout'][index % 4] ?? 'track',
24
+ timestamp: new Date(Date.now() - index * 1000).toISOString(),
25
+ context: {
26
+ page: {
27
+ url: `https://example.com/product/${index}`,
28
+ path: `/product/${index}`,
29
+ title: `Product ${index} - Example Store`,
30
+ referrer: index % 3 === 0 ? 'https://google.com' : '',
31
+ },
32
+ userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
33
+ ip: `192.168.${index % 256}.${(index * 7) % 256}`,
34
+ locale: ['en-US', 'en-GB', 'de-DE', 'fr-FR'][index % 4] ?? 'en-US',
35
+ timezone: 'America/New_York',
36
+ },
37
+ properties: {
38
+ productId: `prod-${index}`,
39
+ productName: `Amazing Product ${index}`,
40
+ price: 29.99 + (index % 100),
41
+ currency: 'USD',
42
+ quantity: (index % 5) + 1,
43
+ category: ['Electronics', 'Clothing', 'Home', 'Sports'][index % 4],
44
+ customFields: {
45
+ brand: `Brand${index % 20}`,
46
+ sku: `SKU-${index}-${Date.now()}`,
47
+ inStock: index % 2 === 0,
48
+ },
49
+ },
50
+ shopifyData: index % 3 === 0
51
+ ? {
52
+ checkoutToken: `checkout-${index}`,
53
+ orderId: index % 5 === 0 ? `order-${index}` : undefined,
54
+ customerEmail: `customer${index}@example.com`,
55
+ lineItems: Array.from({ length: (index % 3) + 1 }, (_, j) => ({
56
+ variantId: `variant-${index}-${j}`,
57
+ title: `Line Item ${j}`,
58
+ price: 19.99 + j * 10,
59
+ quantity: j + 1,
60
+ })),
61
+ }
62
+ : undefined,
63
+ };
64
+ }
65
+ function simulateSqsDelivery(envelopes) {
66
+ return envelopes.map((env, i) => ({
67
+ messageId: `msg-${i}-${Date.now()}`,
68
+ body: JSON.stringify({ messageBody: env }),
69
+ }));
70
+ }
71
+ describe('Full Round-Trip: Producer → Consumer', () => {
72
+ let mockSqsClient;
73
+ let capturedEnvelopes;
74
+ beforeEach(() => {
75
+ capturedEnvelopes = [];
76
+ mockSqsClient = {
77
+ buildAndSendMessagesV2: jest.fn(async (_type, envelopes) => {
78
+ capturedEnvelopes.push(...envelopes);
79
+ return {
80
+ successCount: envelopes.length,
81
+ failedCount: 0,
82
+ batchCount: 1,
83
+ failedMessages: [],
84
+ };
85
+ }),
86
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/test-queue',
87
+ limits: createSqsLimits(SQS_1MB),
88
+ };
89
+ });
90
+ describe('1. Basic round-trip test (100 items)', () => {
91
+ it('100 items survive complete round-trip with deep equality', async () => {
92
+ const bundledClient = new BundledSQSClient(mockSqsClient, {
93
+ compression: CompressionAlgorithm.ZSTD,
94
+ compressionLevel: 3,
95
+ });
96
+ const originalItems = Array.from({ length: 100 }, (_, i) => ({
97
+ id: `event-${i}`,
98
+ name: 'purchase',
99
+ value: i * 99.99,
100
+ metadata: { source: 'test', index: i },
101
+ }));
102
+ await bundledClient.sendBundled('test', originalItems);
103
+ expect(capturedEnvelopes.length).toBeGreaterThanOrEqual(1);
104
+ const sqsRecords = simulateSqsDelivery(capturedEnvelopes);
105
+ const { items: recoveredItems, stats } = await unbundleRecords(sqsRecords);
106
+ expect(recoveredItems).toHaveLength(100);
107
+ expect(recoveredItems).toEqual(originalItems);
108
+ expect(stats.bundledSqsRecords).toBe(capturedEnvelopes.length);
109
+ expect(stats.failedRecords).toBe(0);
110
+ });
111
+ it('preserves all primitive types correctly', async () => {
112
+ const bundledClient = new BundledSQSClient(mockSqsClient, {
113
+ compression: CompressionAlgorithm.ZSTD,
114
+ });
115
+ const originalItems = [
116
+ { id: 'str', name: 'string-test', value: undefined },
117
+ { id: 'num', name: 'number-test', value: 123.456 },
118
+ { id: 'zero', name: 'zero-test', value: 0 },
119
+ { id: 'neg', name: 'negative-test', value: -999.99 },
120
+ { id: 'big', name: 'big-number', value: Number.MAX_SAFE_INTEGER },
121
+ ];
122
+ await bundledClient.sendBundled('test', originalItems);
123
+ const sqsRecords = simulateSqsDelivery(capturedEnvelopes);
124
+ const { items: recoveredItems } = await unbundleRecords(sqsRecords);
125
+ expect(recoveredItems).toEqual(originalItems);
126
+ });
127
+ });
128
+ describe('2. Large scale round-trip (1000 items)', () => {
129
+ it('1000 items survive round-trip with no data loss', async () => {
130
+ const bundledClient = new BundledSQSClient(mockSqsClient, {
131
+ compression: CompressionAlgorithm.ZSTD,
132
+ compressionLevel: 3,
133
+ });
134
+ const originalItems = Array.from({ length: 1000 }, (_, i) => ({
135
+ id: `event-${i.toString().padStart(4, '0')}`,
136
+ name: `type-${i % 10}`,
137
+ value: i * 1.5,
138
+ metadata: {
139
+ batch: Math.floor(i / 100),
140
+ position: i % 100,
141
+ timestamp: Date.now() + i,
142
+ },
143
+ }));
144
+ await bundledClient.sendBundled('test', originalItems);
145
+ const sqsRecords = simulateSqsDelivery(capturedEnvelopes);
146
+ const { items: recoveredItems, stats } = await unbundleRecords(sqsRecords);
147
+ expect(recoveredItems).toHaveLength(1000);
148
+ expect(stats.totalItems).toBe(1000);
149
+ expect(stats.failedRecords).toBe(0);
150
+ const recoveredIds = recoveredItems.map((item) => item.id);
151
+ const originalIds = originalItems.map((item) => item.id);
152
+ expect(new Set(recoveredIds)).toEqual(new Set(originalIds));
153
+ expect(recoveredItems).toEqual(originalItems);
154
+ });
155
+ it('preserves order within each bundle', async () => {
156
+ const bundledClient = new BundledSQSClient(mockSqsClient, {
157
+ compression: CompressionAlgorithm.ZSTD,
158
+ maxItemsPerBundle: 100,
159
+ });
160
+ const originalItems = Array.from({ length: 500 }, (_, i) => makeTestEvent(`seq-${i}`, 'ordered', i));
161
+ await bundledClient.sendBundled('test', originalItems);
162
+ const sqsRecords = simulateSqsDelivery(capturedEnvelopes);
163
+ const { items: recoveredItems } = await unbundleRecords(sqsRecords);
164
+ let currentBundleStart = 0;
165
+ for (const envelope of capturedEnvelopes) {
166
+ const bundleSize = envelope.n;
167
+ const bundleItems = recoveredItems.slice(currentBundleStart, currentBundleStart + bundleSize);
168
+ for (let j = 1; j < bundleItems.length; j++) {
169
+ const prevValue = bundleItems[j - 1]?.value ?? 0;
170
+ const currValue = bundleItems[j]?.value ?? 0;
171
+ expect(currValue).toBeGreaterThan(prevValue);
172
+ }
173
+ currentBundleStart += bundleSize;
174
+ }
175
+ });
176
+ });
177
+ describe('3. Compression round-trip verification', () => {
178
+ it('ZSTD compressed data decompresses to byte-exact JSON', async () => {
179
+ const bundledClient = new BundledSQSClient(mockSqsClient, {
180
+ compression: CompressionAlgorithm.ZSTD,
181
+ compressionLevel: 5,
182
+ });
183
+ const originalItems = Array.from({ length: 50 }, (_, i) => ({
184
+ id: `compress-test-${i}`,
185
+ name: 'compression-verification',
186
+ value: i * 123.456789,
187
+ metadata: {
188
+ unicode: '日本語テスト',
189
+ emoji: '🚀✨🎉',
190
+ special: 'line\nbreak\ttab\r\nwindows',
191
+ quotes: '"double" and \'single\'',
192
+ },
193
+ }));
194
+ const originalJson = JSON.stringify(originalItems);
195
+ await bundledClient.sendBundled('test', originalItems);
196
+ const sqsRecords = simulateSqsDelivery(capturedEnvelopes);
197
+ const { items: recoveredItems } = await unbundleRecords(sqsRecords);
198
+ const recoveredJson = JSON.stringify(recoveredItems);
199
+ expect(recoveredJson).toBe(originalJson);
200
+ expect(recoveredItems).toEqual(originalItems);
201
+ });
202
+ it('verifies actual compression occurred', async () => {
203
+ const bundledClient = new BundledSQSClient(mockSqsClient, {
204
+ compression: CompressionAlgorithm.ZSTD,
205
+ compressionLevel: 3,
206
+ });
207
+ const originalItems = Array.from({ length: 200 }, (_, i) => ({
208
+ id: `item-${i}`,
209
+ name: 'repetitive-data-for-compression',
210
+ value: i,
211
+ metadata: {
212
+ commonField1: 'this is repeated many times',
213
+ commonField2: 'another repeated value',
214
+ },
215
+ }));
216
+ const originalSizeBytes = Buffer.byteLength(JSON.stringify(originalItems), 'utf8');
217
+ await bundledClient.sendBundled('test', originalItems);
218
+ for (const envelope of capturedEnvelopes) {
219
+ expect(envelope.c).toBe(CompressionAlgorithm.ZSTD);
220
+ expect(typeof envelope.p).toBe('string');
221
+ const compressedSizeBytes = Buffer.byteLength(envelope.p, 'utf8');
222
+ expect(compressedSizeBytes).toBeLessThan(originalSizeBytes);
223
+ }
224
+ });
225
+ });
226
+ describe('4. Mixed message types round-trip', () => {
227
+ it('handles mix of NONE and ZSTD compression in same batch', async () => {
228
+ const zstdClient = new BundledSQSClient(mockSqsClient, {
229
+ compression: CompressionAlgorithm.ZSTD,
230
+ });
231
+ const noneClient = new BundledSQSClient(mockSqsClient, {
232
+ compression: CompressionAlgorithm.NONE,
233
+ });
234
+ const zstdItems = [
235
+ makeTestEvent('zstd-1', 'compressed', 100),
236
+ makeTestEvent('zstd-2', 'compressed', 200),
237
+ ];
238
+ const noneItems = [
239
+ makeTestEvent('none-1', 'uncompressed', 300),
240
+ makeTestEvent('none-2', 'uncompressed', 400),
241
+ ];
242
+ await zstdClient.sendBundled('test', zstdItems);
243
+ const zstdEnvelopes = [...capturedEnvelopes];
244
+ capturedEnvelopes = [];
245
+ await noneClient.sendBundled('test', noneItems);
246
+ const noneEnvelopes = [...capturedEnvelopes];
247
+ const allEnvelopes = [...zstdEnvelopes, ...noneEnvelopes];
248
+ const sqsRecords = simulateSqsDelivery(allEnvelopes);
249
+ const { items: recoveredItems, stats } = await unbundleRecords(sqsRecords);
250
+ expect(recoveredItems).toHaveLength(4);
251
+ expect(stats.bundledSqsRecords).toBe(2);
252
+ expect(stats.failedRecords).toBe(0);
253
+ const zstdRecovered = recoveredItems.filter((item) => item.name === 'compressed');
254
+ const noneRecovered = recoveredItems.filter((item) => item.name === 'uncompressed');
255
+ expect(zstdRecovered).toHaveLength(2);
256
+ expect(noneRecovered).toHaveLength(2);
257
+ expect(zstdRecovered).toEqual(zstdItems);
258
+ expect(noneRecovered).toEqual(noneItems);
259
+ });
260
+ it('handles mixed bundled and legacy messages', async () => {
261
+ const bundledClient = new BundledSQSClient(mockSqsClient, {
262
+ compression: CompressionAlgorithm.ZSTD,
263
+ });
264
+ const bundledItems = [
265
+ makeTestEvent('bundled-1', 'bundled', 100),
266
+ makeTestEvent('bundled-2', 'bundled', 200),
267
+ ];
268
+ await bundledClient.sendBundled('test', bundledItems);
269
+ const legacyRecord = {
270
+ messageId: 'legacy-msg-1',
271
+ body: JSON.stringify(makeTestEvent('legacy-1', 'legacy', 999)),
272
+ };
273
+ const wrappedLegacyRecord = {
274
+ messageId: 'legacy-msg-2',
275
+ body: JSON.stringify({ messageBody: makeTestEvent('legacy-2', 'wrapped-legacy', 888) }),
276
+ };
277
+ const bundledRecords = simulateSqsDelivery(capturedEnvelopes);
278
+ const allRecords = [...bundledRecords, legacyRecord, wrappedLegacyRecord];
279
+ const { items: recoveredItems, stats } = await unbundleRecords(allRecords);
280
+ expect(recoveredItems).toHaveLength(4);
281
+ expect(stats.bundledSqsRecords).toBe(1);
282
+ expect(stats.legacySqsRecords).toBe(2);
283
+ expect(stats.failedRecords).toBe(0);
284
+ });
285
+ });
286
+ describe('5. Error injection round-trip', () => {
287
+ it('recovers valid bundles while reporting corrupted bundle in failedMessageIds', async () => {
288
+ const bundledClient = new BundledSQSClient(mockSqsClient, {
289
+ compression: CompressionAlgorithm.ZSTD,
290
+ });
291
+ const validItems1 = [makeTestEvent('valid-1', 'first-batch', 100)];
292
+ const validItems2 = [makeTestEvent('valid-2', 'second-batch', 200)];
293
+ await bundledClient.sendBundled('test', validItems1);
294
+ const envelope1 = { ...capturedEnvelopes[0] };
295
+ capturedEnvelopes = [];
296
+ await bundledClient.sendBundled('test', validItems2);
297
+ const envelope2 = { ...capturedEnvelopes[0] };
298
+ const corruptedEnvelope = {
299
+ v: 1,
300
+ c: CompressionAlgorithm.ZSTD,
301
+ n: 10,
302
+ s: 1000,
303
+ p: 'dGhpcyBpcyBub3QgdmFsaWQgenN0ZA==',
304
+ };
305
+ const records = [
306
+ { messageId: 'valid-1', body: JSON.stringify({ messageBody: envelope1 }) },
307
+ { messageId: 'corrupted', body: JSON.stringify({ messageBody: corruptedEnvelope }) },
308
+ { messageId: 'valid-2', body: JSON.stringify({ messageBody: envelope2 }) },
309
+ ];
310
+ const { items: recoveredItems, failedMessageIds, stats } = await unbundleRecords(records);
311
+ expect(recoveredItems).toHaveLength(2);
312
+ expect(recoveredItems[0]?.id).toBe('valid-1');
313
+ expect(recoveredItems[1]?.id).toBe('valid-2');
314
+ expect(failedMessageIds).toContain('corrupted');
315
+ expect(failedMessageIds).toHaveLength(1);
316
+ expect(stats.bundledSqsRecords).toBe(2);
317
+ expect(stats.failedRecords).toBe(1);
318
+ expect(stats.totalItems).toBe(2);
319
+ });
320
+ it('handles invalid JSON gracefully', async () => {
321
+ const bundledClient = new BundledSQSClient(mockSqsClient, {
322
+ compression: CompressionAlgorithm.ZSTD,
323
+ });
324
+ const validItems = [makeTestEvent('valid', 'test', 100)];
325
+ await bundledClient.sendBundled('test', validItems);
326
+ const records = [
327
+ ...simulateSqsDelivery(capturedEnvelopes),
328
+ { messageId: 'invalid-json-1', body: 'not-valid-json{{{' },
329
+ { messageId: 'invalid-json-2', body: '{"incomplete":' },
330
+ { messageId: 'empty', body: '' },
331
+ ];
332
+ const { items: recoveredItems, failedMessageIds, stats } = await unbundleRecords(records);
333
+ expect(recoveredItems).toHaveLength(1);
334
+ expect(recoveredItems[0]?.id).toBe('valid');
335
+ expect(failedMessageIds).toContain('invalid-json-1');
336
+ expect(failedMessageIds).toContain('invalid-json-2');
337
+ expect(failedMessageIds).toContain('empty');
338
+ expect(failedMessageIds).toHaveLength(3);
339
+ expect(stats.failedRecords).toBe(3);
340
+ });
341
+ it('handles decompression bomb protection', async () => {
342
+ const largeItems = Array.from({ length: 1000 }, (_, i) => ({
343
+ id: `item-${i}`,
344
+ data: 'x'.repeat(1000),
345
+ }));
346
+ const json = JSON.stringify(largeItems);
347
+ const compressed = await compress(Buffer.from(json, 'utf8'), CompressionAlgorithm.ZSTD, 3);
348
+ const base64 = compressed.toString('base64');
349
+ const bigEnvelope = {
350
+ v: 1,
351
+ c: CompressionAlgorithm.ZSTD,
352
+ n: 1000,
353
+ s: json.length,
354
+ p: base64,
355
+ };
356
+ const records = [{ messageId: 'too-large', body: JSON.stringify({ messageBody: bigEnvelope }) }];
357
+ const { failedMessageIds, stats } = await unbundleRecords(records, {
358
+ maxDecompressedSizeBytes: 100 * 1024,
359
+ });
360
+ expect(failedMessageIds).toContain('too-large');
361
+ expect(stats.failedRecords).toBe(1);
362
+ });
363
+ });
364
+ describe('6. Realistic event types round-trip', () => {
365
+ it('TrackingEvent-like structures survive round-trip exactly', async () => {
366
+ const realisticMockSqs = {
367
+ buildAndSendMessagesV2: jest.fn(async (_type, envelopes) => {
368
+ capturedEnvelopes = [];
369
+ capturedEnvelopes.push(...envelopes);
370
+ return {
371
+ successCount: envelopes.length,
372
+ failedCount: 0,
373
+ batchCount: 1,
374
+ failedMessages: [],
375
+ };
376
+ }),
377
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/test-queue',
378
+ limits: createSqsLimits(SQS_1MB),
379
+ };
380
+ const bundledClient = new BundledSQSClient(realisticMockSqs, {
381
+ compression: CompressionAlgorithm.ZSTD,
382
+ compressionLevel: 3,
383
+ });
384
+ const originalEvents = Array.from({ length: 100 }, (_, i) => makeRealisticTrackingEvent(i));
385
+ await bundledClient.sendBundled('tracking-events', originalEvents);
386
+ const sqsRecords = simulateSqsDelivery(capturedEnvelopes);
387
+ const { items: recoveredEvents, stats } = await unbundleRecords(sqsRecords);
388
+ expect(recoveredEvents).toHaveLength(100);
389
+ expect(stats.failedRecords).toBe(0);
390
+ expect(recoveredEvents).toEqual(originalEvents);
391
+ for (let i = 0; i < originalEvents.length; i++) {
392
+ const original = originalEvents[i];
393
+ const recovered = recoveredEvents[i];
394
+ expect(recovered?.anonymousId).toBe(original?.anonymousId);
395
+ expect(recovered?.context.page.url).toBe(original?.context.page.url);
396
+ expect(recovered?.properties.price).toBe(original?.properties.price);
397
+ if (original?.shopifyData) {
398
+ expect(recovered?.shopifyData).toEqual(original.shopifyData);
399
+ }
400
+ }
401
+ });
402
+ it('preserves nested object structures exactly', async () => {
403
+ const bundledClient = new BundledSQSClient(mockSqsClient, {
404
+ compression: CompressionAlgorithm.ZSTD,
405
+ });
406
+ const deeplyNestedItems = [
407
+ {
408
+ id: 'nested-1',
409
+ name: 'deep-nesting',
410
+ nested: {
411
+ level1: {
412
+ level2: {
413
+ deep: 'very deep value',
414
+ },
415
+ },
416
+ },
417
+ },
418
+ {
419
+ id: 'nested-2',
420
+ name: 'with-arrays',
421
+ tags: ['tag1', 'tag2', 'tag3'],
422
+ metadata: {
423
+ array: [1, 2, 3],
424
+ object: { a: 1, b: 2 },
425
+ mixed: [{ key: 'value' }, [1, 2, 3]],
426
+ },
427
+ },
428
+ ];
429
+ await bundledClient.sendBundled('test', deeplyNestedItems);
430
+ const sqsRecords = simulateSqsDelivery(capturedEnvelopes);
431
+ const { items: recoveredItems } = await unbundleRecords(sqsRecords);
432
+ expect(recoveredItems).toEqual(deeplyNestedItems);
433
+ expect(recoveredItems[0]?.nested?.level1.level2.deep).toBe('very deep value');
434
+ expect(recoveredItems[1]?.tags).toEqual(['tag1', 'tag2', 'tag3']);
435
+ });
436
+ });
437
+ describe('7. High compression ratio round-trip', () => {
438
+ it('highly repetitive data compresses and decompresses correctly', async () => {
439
+ const bundledClient = new BundledSQSClient(mockSqsClient, {
440
+ compression: CompressionAlgorithm.ZSTD,
441
+ compressionLevel: 3,
442
+ });
443
+ const repetitiveString = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
444
+ const originalItems = Array.from({ length: 200 }, (_, i) => ({
445
+ id: `repetitive-${i}`,
446
+ name: repetitiveString,
447
+ value: 42,
448
+ metadata: {
449
+ field1: repetitiveString,
450
+ field2: repetitiveString,
451
+ field3: repetitiveString,
452
+ field4: repetitiveString,
453
+ field5: repetitiveString,
454
+ },
455
+ }));
456
+ const originalSizeBytes = Buffer.byteLength(JSON.stringify(originalItems), 'utf8');
457
+ await bundledClient.sendBundled('test', originalItems);
458
+ let totalCompressedSize = 0;
459
+ for (const envelope of capturedEnvelopes) {
460
+ expect(typeof envelope.p).toBe('string');
461
+ totalCompressedSize += Buffer.byteLength(envelope.p, 'utf8');
462
+ }
463
+ const compressionRatio = originalSizeBytes / totalCompressedSize;
464
+ expect(compressionRatio).toBeGreaterThan(5);
465
+ const sqsRecords = simulateSqsDelivery(capturedEnvelopes);
466
+ const { items: recoveredItems, stats } = await unbundleRecords(sqsRecords);
467
+ expect(recoveredItems).toHaveLength(200);
468
+ expect(recoveredItems).toEqual(originalItems);
469
+ expect(stats.failedRecords).toBe(0);
470
+ for (const item of recoveredItems) {
471
+ expect(item.name).toBe(repetitiveString);
472
+ expect(item.value).toBe(42);
473
+ expect(item.metadata?.field1).toBe(repetitiveString);
474
+ }
475
+ });
476
+ it('varying compression levels all round-trip correctly', async () => {
477
+ const items = Array.from({ length: 50 }, (_, i) => makeTestEvent(`level-test-${i}`, 'compression-level', i));
478
+ for (const level of [1, 3, 9, 15]) {
479
+ capturedEnvelopes = [];
480
+ const bundledClient = new BundledSQSClient(mockSqsClient, {
481
+ compression: CompressionAlgorithm.ZSTD,
482
+ compressionLevel: level,
483
+ });
484
+ await bundledClient.sendBundled('test', items);
485
+ const sqsRecords = simulateSqsDelivery(capturedEnvelopes);
486
+ const { items: recoveredItems, stats } = await unbundleRecords(sqsRecords);
487
+ expect(recoveredItems).toEqual(items);
488
+ expect(stats.failedRecords).toBe(0);
489
+ }
490
+ });
491
+ });
492
+ describe('8. Edge cases', () => {
493
+ it('empty arrays survive round-trip', async () => {
494
+ const bundledClient = new BundledSQSClient(mockSqsClient, {
495
+ compression: CompressionAlgorithm.ZSTD,
496
+ });
497
+ const result = await bundledClient.sendBundled('test', []);
498
+ expect(result.totalItems).toBe(0);
499
+ expect(capturedEnvelopes).toHaveLength(0);
500
+ });
501
+ it('single item survives round-trip', async () => {
502
+ const bundledClient = new BundledSQSClient(mockSqsClient, {
503
+ compression: CompressionAlgorithm.ZSTD,
504
+ });
505
+ const singleItem = [makeTestEvent('single', 'lone-wolf', 999)];
506
+ await bundledClient.sendBundled('test', singleItem);
507
+ const sqsRecords = simulateSqsDelivery(capturedEnvelopes);
508
+ const { items: recoveredItems } = await unbundleRecords(sqsRecords);
509
+ expect(recoveredItems).toHaveLength(1);
510
+ expect(recoveredItems).toEqual(singleItem);
511
+ });
512
+ it('special characters survive round-trip', async () => {
513
+ const bundledClient = new BundledSQSClient(mockSqsClient, {
514
+ compression: CompressionAlgorithm.ZSTD,
515
+ });
516
+ const specialCharsItems = [
517
+ {
518
+ id: 'unicode-日本語',
519
+ name: '中文测试',
520
+ metadata: {
521
+ emoji: '🎉🚀✨💯',
522
+ rtl: 'مرحبا بالعالم',
523
+ escape: '\\n\\t\\r',
524
+ quotes: '"double" \'single\' `backtick`',
525
+ angle: '<script>alert("xss")</script>',
526
+ null_char: 'before\x00after',
527
+ },
528
+ },
529
+ ];
530
+ await bundledClient.sendBundled('test', specialCharsItems);
531
+ const sqsRecords = simulateSqsDelivery(capturedEnvelopes);
532
+ const { items: recoveredItems } = await unbundleRecords(sqsRecords);
533
+ expect(recoveredItems).toEqual(specialCharsItems);
534
+ });
535
+ it('null and undefined values are handled correctly', async () => {
536
+ const bundledClient = new BundledSQSClient(mockSqsClient, {
537
+ compression: CompressionAlgorithm.ZSTD,
538
+ });
539
+ const itemsWithNulls = [
540
+ { id: 'null-test', name: 'test', value: undefined },
541
+ {
542
+ id: 'explicit-null',
543
+ name: 'test',
544
+ metadata: { key: null },
545
+ },
546
+ ];
547
+ await bundledClient.sendBundled('test', itemsWithNulls);
548
+ const sqsRecords = simulateSqsDelivery(capturedEnvelopes);
549
+ const { items: recoveredItems } = await unbundleRecords(sqsRecords);
550
+ expect(recoveredItems[0]?.value).toBeUndefined();
551
+ expect(recoveredItems[1]?.metadata?.key).toBeNull();
552
+ });
553
+ it('recordMap correctly tracks items back to SQS messages', async () => {
554
+ const bundledClient = new BundledSQSClient(mockSqsClient, {
555
+ compression: CompressionAlgorithm.ZSTD,
556
+ maxItemsPerBundle: 3,
557
+ });
558
+ const items = Array.from({ length: 10 }, (_, i) => makeTestEvent(`item-${i}`, 'tracking', i));
559
+ await bundledClient.sendBundled('test', items);
560
+ const sqsRecords = capturedEnvelopes.map((env, i) => ({
561
+ messageId: `bundle-${i}`,
562
+ body: JSON.stringify({ messageBody: env }),
563
+ }));
564
+ const { items: recoveredItems, recordMap, stats } = await unbundleRecords(sqsRecords);
565
+ expect(recoveredItems).toHaveLength(10);
566
+ const messageIdCounts = new Map();
567
+ for (const item of recoveredItems) {
568
+ const messageId = recordMap.get(item);
569
+ expect(messageId).toBeDefined();
570
+ if (messageId) {
571
+ messageIdCounts.set(messageId, (messageIdCounts.get(messageId) ?? 0) + 1);
572
+ }
573
+ }
574
+ expect(messageIdCounts.size).toBe(capturedEnvelopes.length);
575
+ for (const [messageId, count] of messageIdCounts) {
576
+ expect(count).toBeLessThanOrEqual(3);
577
+ expect(messageId).toMatch(/^bundle-\d+$/);
578
+ }
579
+ expect(stats.bundledSqsRecords).toBe(capturedEnvelopes.length);
580
+ });
581
+ });
582
+ });
583
583
  //# sourceMappingURL=sqs-bundling-roundtrip.spec.js.map