@fluentcommerce/fc-connect-sdk 0.1.54 → 0.1.55

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 (475) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cjs/clients/fluent-client.js +13 -6
  3. package/dist/cjs/utils/pagination-helpers.js +38 -2
  4. package/dist/cjs/versori/fluent-versori-client.js +11 -5
  5. package/dist/esm/clients/fluent-client.js +13 -6
  6. package/dist/esm/utils/pagination-helpers.js +38 -2
  7. package/dist/esm/versori/fluent-versori-client.js +11 -5
  8. package/dist/tsconfig.esm.tsbuildinfo +1 -1
  9. package/dist/tsconfig.tsbuildinfo +1 -1
  10. package/dist/tsconfig.types.tsbuildinfo +1 -1
  11. package/docs/00-START-HERE/EXPORT-VALIDATION.md +158 -158
  12. package/docs/00-START-HERE/cli-analyze-source-structure-guide.md +655 -655
  13. package/docs/00-START-HERE/cli-documentation-index.md +202 -202
  14. package/docs/00-START-HERE/cli-quick-reference.md +252 -252
  15. package/docs/00-START-HERE/decision-tree.md +552 -552
  16. package/docs/00-START-HERE/getting-started.md +1070 -1070
  17. package/docs/00-START-HERE/mapper-quick-decision-guide.md +235 -235
  18. package/docs/00-START-HERE/readme.md +237 -237
  19. package/docs/00-START-HERE/retailerid-configuration.md +404 -404
  20. package/docs/00-START-HERE/sdk-philosophy.md +794 -794
  21. package/docs/00-START-HERE/troubleshooting-quick-reference.md +1086 -1086
  22. package/docs/01-TEMPLATES/faq.md +686 -686
  23. package/docs/01-TEMPLATES/patterns/pattern-templates-guide.md +68 -68
  24. package/docs/01-TEMPLATES/patterns/patterns-csv-schema-validation-and-rejection-report.md +233 -233
  25. package/docs/01-TEMPLATES/patterns/patterns-custom-resolvers.md +407 -407
  26. package/docs/01-TEMPLATES/patterns/patterns-error-handling-retry.md +511 -511
  27. package/docs/01-TEMPLATES/patterns/patterns-field-mapping-universal.md +701 -701
  28. package/docs/01-TEMPLATES/patterns/patterns-large-file-splitting.md +1430 -1430
  29. package/docs/01-TEMPLATES/patterns/patterns-master-data-etl.md +2399 -2399
  30. package/docs/01-TEMPLATES/patterns/patterns-pagination-streaming.md +447 -447
  31. package/docs/01-TEMPLATES/patterns/patterns-state-duplicate-prevention.md +385 -385
  32. package/docs/01-TEMPLATES/readme.md +957 -957
  33. package/docs/01-TEMPLATES/standalone/standalone-asn-inbound-processing.md +1209 -1209
  34. package/docs/01-TEMPLATES/standalone/standalone-graphql-query-export.md +1140 -1140
  35. package/docs/01-TEMPLATES/standalone/standalone-graphql-to-parquet-partitioned-s3.md +432 -432
  36. package/docs/01-TEMPLATES/standalone/standalone-multi-channel-inventory-sync.md +1185 -1185
  37. package/docs/01-TEMPLATES/standalone/standalone-multi-source-aggregation.md +1462 -1462
  38. package/docs/01-TEMPLATES/standalone/standalone-s3-csv-batch-api.md +1390 -1390
  39. package/docs/01-TEMPLATES/standalone/standalone-s3-csv-inventory-to-batch.md +330 -330
  40. package/docs/01-TEMPLATES/standalone/standalone-scripts-guide.md +87 -87
  41. package/docs/01-TEMPLATES/standalone/standalone-sftp-xml-graphql.md +1444 -1444
  42. package/docs/01-TEMPLATES/standalone/standalone-webhook-payload-processing.md +688 -688
  43. package/docs/01-TEMPLATES/versori/business-examples/business-examples-dropship-order-routing.md +193 -193
  44. package/docs/01-TEMPLATES/versori/business-examples/business-examples-graphql-parquet-extraction.md +518 -518
  45. package/docs/01-TEMPLATES/versori/business-examples/business-examples-inter-location-transfers.md +2162 -2162
  46. package/docs/01-TEMPLATES/versori/business-examples/business-examples-pre-order-allocation.md +2226 -2226
  47. package/docs/01-TEMPLATES/versori/business-examples/business-scenarios-guide.md +87 -87
  48. package/docs/01-TEMPLATES/versori/patterns/versori-patterns-connection-validation-pattern.md +656 -656
  49. package/docs/01-TEMPLATES/versori/patterns/versori-patterns-dual-workflow-connector.md +835 -835
  50. package/docs/01-TEMPLATES/versori/patterns/versori-patterns-guide.md +108 -108
  51. package/docs/01-TEMPLATES/versori/patterns/versori-patterns-kv-state-management.md +1533 -1533
  52. package/docs/01-TEMPLATES/versori/patterns/versori-patterns-xml-response-patterns.md +1160 -1160
  53. package/docs/01-TEMPLATES/versori/versori-platform-guide.md +201 -201
  54. package/docs/01-TEMPLATES/versori/webhooks/template-webhook-asn-purchase-order.md +1906 -1906
  55. package/docs/01-TEMPLATES/versori/webhooks/template-webhook-dropship-routing.md +1074 -1074
  56. package/docs/01-TEMPLATES/versori/webhooks/template-webhook-flash-sale-reserve.md +1395 -1395
  57. package/docs/01-TEMPLATES/versori/webhooks/template-webhook-generic-xml-order.md +888 -888
  58. package/docs/01-TEMPLATES/versori/webhooks/template-webhook-payment-gateway-integration.md +2478 -2478
  59. package/docs/01-TEMPLATES/versori/webhooks/template-webhook-rma-returns-comprehensive.md +2240 -2240
  60. package/docs/01-TEMPLATES/versori/webhooks/template-webhook-xml-order-ingestion.md +2029 -2029
  61. package/docs/01-TEMPLATES/versori/webhooks/webhook-templates-guide.md +140 -140
  62. package/docs/01-TEMPLATES/versori/workflows/_examples/sample-data/inventory-mapping.json +20 -20
  63. package/docs/01-TEMPLATES/versori/workflows/_examples/sample-data/products_2025-01-22.csv +11 -11
  64. package/docs/01-TEMPLATES/versori/workflows/_examples/sample-data/sample-data-guide.md +34 -34
  65. package/docs/01-TEMPLATES/versori/workflows/_examples/workflow-examples-guide.md +36 -36
  66. package/docs/01-TEMPLATES/versori/workflows/extraction/extraction-modes-guide.md +1038 -1038
  67. package/docs/01-TEMPLATES/versori/workflows/extraction/extraction-workflows-guide.md +138 -138
  68. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/graphql-extraction-guide.md +63 -63
  69. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-fulfillments-to-sftp-csv.md +2062 -2062
  70. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-fulfillments-to-sftp-xml.md +2294 -2294
  71. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-positions-to-s3-csv.md +2461 -2461
  72. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-positions-to-sftp-xml.md +2529 -2529
  73. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-quantities-to-s3-csv.md +2464 -2464
  74. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-quantities-to-s3-json.md +1959 -1959
  75. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-orders-to-s3-csv.md +1953 -1953
  76. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-orders-to-sftp-xml.md +2541 -2541
  77. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-products-to-s3-json.md +2384 -2384
  78. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-products-to-sftp-xml.md +2445 -2445
  79. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-virtual-positions-to-s3-csv.md +2355 -2355
  80. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-virtual-positions-to-s3-json.md +2042 -2042
  81. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-virtual-positions-to-sftp-xml.md +2726 -2726
  82. package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/batch-api-guide.md +206 -206
  83. package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-cycle-count-reconciliation.md +2030 -2030
  84. package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-multi-channel-inventory-sync.md +1882 -1882
  85. package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-s3-csv-inventory-batch.md +2827 -2827
  86. package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-s3-json-inventory-batch.md +1952 -1952
  87. package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-s3-xml-inventory-batch.md +3289 -3289
  88. package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-sftp-csv-inventory-batch.md +3064 -3064
  89. package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-sftp-json-inventory-batch.md +3238 -3238
  90. package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-sftp-xml-inventory-batch.md +2977 -2977
  91. package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/event-api-guide.md +321 -321
  92. package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-payload-json-order-cancel-event.md +959 -959
  93. package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-payload-xml-order-cancel-event.md +1170 -1170
  94. package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-csv-product-event.md +2312 -2312
  95. package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-json-product-event.md +2999 -2999
  96. package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-parquet-product-event.md +2836 -2836
  97. package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-xml-product-event.md +2395 -2395
  98. package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-csv-product-event.md +2295 -2295
  99. package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-json-product-event.md +2602 -2602
  100. package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-parquet-product-event.md +2589 -2589
  101. package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-xml-product-event.md +3578 -3578
  102. package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/graphql-mutations-guide.md +93 -93
  103. package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-payload-json-order-update-graphql.md +1260 -1260
  104. package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-payload-xml-order-update-graphql.md +1472 -1472
  105. package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-csv-control-graphql.md +2417 -2417
  106. package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-csv-location-graphql.md +2811 -2811
  107. package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-csv-price-graphql.md +2619 -2619
  108. package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-json-location-graphql.md +2807 -2807
  109. package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-xml-location-graphql.md +2373 -2373
  110. package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-sftp-csv-control-graphql.md +2740 -2740
  111. package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-sftp-csv-location-graphql.md +2760 -2760
  112. package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-sftp-json-location-graphql.md +1710 -1710
  113. package/docs/01-TEMPLATES/versori/workflows/ingestion/ingestion-workflows-guide.md +136 -136
  114. package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/rubix-webhooks-guide.md +520 -520
  115. package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/template-webhook-rubix-fulfilment-to-sftp-xml-inline.md +1418 -1418
  116. package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/template-webhook-rubix-fulfilment-to-sftp-xml-universal-mapper.md +1785 -1785
  117. package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/template-webhook-rubix-order-attribute-update.md +824 -824
  118. package/docs/01-TEMPLATES/versori/workflows/workflows-overview-guide.md +646 -646
  119. package/docs/02-CORE-GUIDES/advanced-services/advanced-services-batch-archival.md +724 -724
  120. package/docs/02-CORE-GUIDES/advanced-services/advanced-services-job-tracker.md +627 -627
  121. package/docs/02-CORE-GUIDES/advanced-services/advanced-services-partial-batch-recovery.md +561 -561
  122. package/docs/02-CORE-GUIDES/advanced-services/advanced-services-quick-reference.md +367 -367
  123. package/docs/02-CORE-GUIDES/advanced-services/advanced-services-readme.md +407 -407
  124. package/docs/02-CORE-GUIDES/advanced-services/readme.md +49 -49
  125. package/docs/02-CORE-GUIDES/api-reference/api-reference-quick-reference.md +548 -548
  126. package/docs/02-CORE-GUIDES/api-reference/event-api-input-output-reference.md +702 -1171
  127. package/docs/02-CORE-GUIDES/api-reference/examples/client-initialization.ts +286 -286
  128. package/docs/02-CORE-GUIDES/api-reference/graphql-error-classification.md +337 -337
  129. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-01-client-api.md +399 -520
  130. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-03-authentication.md +199 -199
  131. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-04-graphql-mapping.md +925 -925
  132. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-05-services.md +1198 -1198
  133. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-06-data-sources.md +1083 -1083
  134. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-07-parsers.md +1097 -1097
  135. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-08-pagination.md +513 -513
  136. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-08-types.md +545 -597
  137. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-09-error-handling.md +527 -527
  138. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-09-webhook-validation.md +514 -514
  139. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-10-extraction.md +557 -557
  140. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-10-utilities.md +412 -412
  141. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-11-cli-tools.md +423 -423
  142. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-11-error-handling.md +716 -716
  143. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-analyze-source-structure.md +518 -518
  144. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-partial-responses.md +212 -212
  145. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-testing.md +300 -300
  146. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-13-resolver-builder.md +322 -322
  147. package/docs/02-CORE-GUIDES/api-reference/readme.md +279 -279
  148. package/docs/02-CORE-GUIDES/auto-pagination/auto-pagination-quick-reference.md +351 -351
  149. package/docs/02-CORE-GUIDES/auto-pagination/auto-pagination-readme.md +277 -277
  150. package/docs/02-CORE-GUIDES/auto-pagination/examples/auto-pagination-readme.md +178 -178
  151. package/docs/02-CORE-GUIDES/auto-pagination/examples/common-patterns.ts +351 -351
  152. package/docs/02-CORE-GUIDES/auto-pagination/examples/paginate-products.ts +384 -384
  153. package/docs/02-CORE-GUIDES/auto-pagination/examples/paginate-virtual-positions.ts +308 -308
  154. package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-01-foundations.md +470 -470
  155. package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-02-quick-start.md +713 -713
  156. package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-03-configuration.md +754 -754
  157. package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-04-advanced-patterns.md +732 -732
  158. package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-05-sdk-integration.md +847 -847
  159. package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-06-troubleshooting.md +359 -359
  160. package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-07-api-reference.md +462 -462
  161. package/docs/02-CORE-GUIDES/auto-pagination/readme.md +54 -54
  162. package/docs/02-CORE-GUIDES/data-sources/data-sources-file-operations-error-handling.md +1487 -1487
  163. package/docs/02-CORE-GUIDES/data-sources/data-sources-quick-reference.md +836 -836
  164. package/docs/02-CORE-GUIDES/data-sources/data-sources-readme.md +276 -276
  165. package/docs/02-CORE-GUIDES/data-sources/data-sources-sftp-credential-access-security.md +553 -553
  166. package/docs/02-CORE-GUIDES/data-sources/examples/common-patterns.ts +409 -409
  167. package/docs/02-CORE-GUIDES/data-sources/examples/data-sources-readme.md +178 -178
  168. package/docs/02-CORE-GUIDES/data-sources/examples/s3-operations.ts +308 -308
  169. package/docs/02-CORE-GUIDES/data-sources/examples/sftp-operations.ts +371 -371
  170. package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-01-foundations.md +735 -735
  171. package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-02-s3-operations.md +1302 -1302
  172. package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-03-sftp-operations.md +1379 -1379
  173. package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-04-file-patterns.md +941 -941
  174. package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-05-advanced-topics.md +813 -813
  175. package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-06-integration-patterns.md +486 -486
  176. package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-07-troubleshooting.md +387 -387
  177. package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-08-api-reference.md +417 -417
  178. package/docs/02-CORE-GUIDES/data-sources/readme.md +77 -77
  179. package/docs/02-CORE-GUIDES/error-handling-guide.md +936 -936
  180. package/docs/02-CORE-GUIDES/extraction/examples/02-core-guides-extraction-readme.md +116 -116
  181. package/docs/02-CORE-GUIDES/extraction/examples/common-patterns.ts +428 -428
  182. package/docs/02-CORE-GUIDES/extraction/examples/extract-inventory-basic.ts +187 -187
  183. package/docs/02-CORE-GUIDES/extraction/extraction-quick-reference.md +596 -596
  184. package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-01-foundations.md +514 -514
  185. package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-02-basic-extraction.md +823 -823
  186. package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-03-parquet-processing.md +507 -507
  187. package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-04-data-enrichment.md +546 -546
  188. package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-05-transformation.md +494 -494
  189. package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-06-export-formats.md +458 -458
  190. package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-06-performance.md +138 -138
  191. package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-07-api-reference.md +148 -148
  192. package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-07-optimization.md +692 -692
  193. package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-08-extraction-orchestrator.md +1008 -1008
  194. package/docs/02-CORE-GUIDES/extraction/readme.md +151 -151
  195. package/docs/02-CORE-GUIDES/ingestion/examples/_simple-kv-store.ts +40 -40
  196. package/docs/02-CORE-GUIDES/ingestion/examples/error-recovery.ts +728 -728
  197. package/docs/02-CORE-GUIDES/ingestion/examples/event-driven.ts +501 -501
  198. package/docs/02-CORE-GUIDES/ingestion/examples/local-file-ingestion.ts +88 -88
  199. package/docs/02-CORE-GUIDES/ingestion/examples/parquet-ingestion.ts +117 -117
  200. package/docs/02-CORE-GUIDES/ingestion/examples/performance-optimized.ts +647 -647
  201. package/docs/02-CORE-GUIDES/ingestion/examples/s3-csv-ingestion.ts +169 -169
  202. package/docs/02-CORE-GUIDES/ingestion/examples/sftp-csv-ingestion.ts +134 -134
  203. package/docs/02-CORE-GUIDES/ingestion/ingestion-quick-reference.md +546 -546
  204. package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-01-introduction.md +626 -626
  205. package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-02-quick-start.md +658 -658
  206. package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-03-data-sources.md +1052 -1052
  207. package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-04-field-mapping.md +763 -763
  208. package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-05-advanced-parsers.md +676 -676
  209. package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-06-batch-api.md +1295 -1295
  210. package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-07-api-reference.md +138 -138
  211. package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-07-state-management.md +1037 -1037
  212. package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-08-performance-optimization.md +1349 -1349
  213. package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-09-best-practices.md +1893 -1893
  214. package/docs/02-CORE-GUIDES/ingestion/readme.md +160 -160
  215. package/docs/02-CORE-GUIDES/logging-guide.md +585 -585
  216. package/docs/02-CORE-GUIDES/mapping/error-handling-patterns.md +401 -401
  217. package/docs/02-CORE-GUIDES/mapping/examples/02-core-guides-mapping-readme.md +128 -128
  218. package/docs/02-CORE-GUIDES/mapping/examples/common-patterns.ts +273 -273
  219. package/docs/02-CORE-GUIDES/mapping/examples/csv-location-ingestion.json +36 -36
  220. package/docs/02-CORE-GUIDES/mapping/examples/csv-mapping.ts +242 -242
  221. package/docs/02-CORE-GUIDES/mapping/examples/graphql-to-parquet-extraction.json +36 -36
  222. package/docs/02-CORE-GUIDES/mapping/examples/json-mapping.ts +213 -213
  223. package/docs/02-CORE-GUIDES/mapping/examples/json-product-to-mutation.json +48 -48
  224. package/docs/02-CORE-GUIDES/mapping/examples/xml-mapping.ts +291 -291
  225. package/docs/02-CORE-GUIDES/mapping/examples/xml-order-to-mutation.json +45 -45
  226. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/graphql-mutation-mapping-quick-reference.md +463 -463
  227. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/graphql-mutation-mapping-readme.md +227 -227
  228. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-01-introduction.md +222 -222
  229. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-02-quick-start.md +351 -351
  230. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-03-schema-validation.md +569 -569
  231. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-04-mapping-patterns.md +471 -471
  232. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-05-configuration-reference.md +611 -611
  233. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-06-advanced-xpath.md +148 -148
  234. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-06-path-syntax.md +464 -464
  235. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-07-api-reference.md +94 -94
  236. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-07-array-handling.md +307 -307
  237. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-08-custom-resolvers.md +544 -544
  238. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-09-advanced-patterns.md +427 -427
  239. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-10-hooks-and-variables.md +336 -336
  240. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-11-error-handling.md +488 -488
  241. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-12-arguments-vs-nodes.md +383 -383
  242. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-13-best-practices.md +477 -477
  243. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/readme.md +62 -62
  244. package/docs/02-CORE-GUIDES/mapping/mapping-format-decision-tree.md +480 -480
  245. package/docs/02-CORE-GUIDES/mapping/mapping-graphql-alias-batching-guide.md +820 -820
  246. package/docs/02-CORE-GUIDES/mapping/mapping-javascript-objects.md +2369 -2369
  247. package/docs/02-CORE-GUIDES/mapping/mapping-mapper-comparison-guide.md +682 -682
  248. package/docs/02-CORE-GUIDES/mapping/modules/02-core-guides-mapping-07-api-reference.md +1327 -1327
  249. package/docs/02-CORE-GUIDES/mapping/modules/02-core-guides-mapping-08-error-handling.md +1142 -1142
  250. package/docs/02-CORE-GUIDES/mapping/modules/mapping-04-use-cases.md +891 -891
  251. package/docs/02-CORE-GUIDES/mapping/modules/mapping-06-helpers-resolvers.md +1126 -1126
  252. package/docs/02-CORE-GUIDES/mapping/modules/mapping-06-sdk-resolvers.md +199 -199
  253. package/docs/02-CORE-GUIDES/mapping/modules/mapping-07-api-reference.md +1319 -1319
  254. package/docs/02-CORE-GUIDES/mapping/readme.md +178 -178
  255. package/docs/02-CORE-GUIDES/mapping/resolver-registration.md +410 -410
  256. package/docs/02-CORE-GUIDES/mapping/resolvers/examples/common-patterns.ts +226 -226
  257. package/docs/02-CORE-GUIDES/mapping/resolvers/examples/custom-resolvers.ts +227 -227
  258. package/docs/02-CORE-GUIDES/mapping/resolvers/examples/sdk-resolvers-usage.ts +203 -203
  259. package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-readme.md +274 -274
  260. package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-api-reference.md +679 -679
  261. package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-cookbook.md +826 -826
  262. package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-guide.md +1330 -1330
  263. package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-helpers-reference.md +1437 -1437
  264. package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-parameters-reference.md +553 -553
  265. package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-troubleshooting.md +854 -854
  266. package/docs/02-CORE-GUIDES/mapping/resolvers/readme.md +75 -75
  267. package/docs/02-CORE-GUIDES/parsers/examples/02-core-guides-parsers-readme.md +161 -161
  268. package/docs/02-CORE-GUIDES/parsers/examples/csv-parser-examples.ts +110 -110
  269. package/docs/02-CORE-GUIDES/parsers/examples/json-parser-examples.ts +33 -33
  270. package/docs/02-CORE-GUIDES/parsers/examples/parquet-parser-examples.ts +47 -47
  271. package/docs/02-CORE-GUIDES/parsers/examples/xml-parser-examples.ts +38 -38
  272. package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-01-foundations.md +355 -355
  273. package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-02-csv-parser.md +772 -772
  274. package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-03-json-parser.md +789 -789
  275. package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-04-xml-parser.md +857 -857
  276. package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-05-parquet-parser.md +603 -603
  277. package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-06-integration-patterns.md +702 -702
  278. package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-06-streaming.md +121 -121
  279. package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-07-api-reference.md +89 -89
  280. package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-07-troubleshooting.md +727 -727
  281. package/docs/02-CORE-GUIDES/parsers/parsers-quick-reference.md +482 -482
  282. package/docs/02-CORE-GUIDES/parsers/parsers-readme.md +258 -258
  283. package/docs/02-CORE-GUIDES/parsers/readme.md +65 -65
  284. package/docs/02-CORE-GUIDES/readme.md +194 -194
  285. package/docs/02-CORE-GUIDES/webhook-validation/examples/basic-validation.ts +108 -108
  286. package/docs/02-CORE-GUIDES/webhook-validation/examples/common-patterns.ts +316 -316
  287. package/docs/02-CORE-GUIDES/webhook-validation/examples/webhook-validation-readme.md +61 -61
  288. package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-01-foundations.md +440 -440
  289. package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-02-quick-start.md +525 -525
  290. package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-03-versori-integration.md +741 -741
  291. package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-04-platform-integration.md +629 -629
  292. package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-05-configuration.md +535 -535
  293. package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-06-error-handling.md +611 -611
  294. package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-06-troubleshooting.md +124 -124
  295. package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-07-api-reference.md +511 -511
  296. package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-08-rubix-webhooks.md +590 -590
  297. package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-09-rubix-event-vs-http-call.md +432 -432
  298. package/docs/02-CORE-GUIDES/webhook-validation/readme.md +239 -239
  299. package/docs/02-CORE-GUIDES/webhook-validation/webhook-validation-quick-reference.md +392 -392
  300. package/docs/03-PATTERN-GUIDES/connector-scenarios/connector-scenarios-quick-reference.md +498 -498
  301. package/docs/03-PATTERN-GUIDES/connector-scenarios/connector-scenarios-readme.md +313 -313
  302. package/docs/03-PATTERN-GUIDES/connector-scenarios/examples/common-patterns.ts +612 -612
  303. package/docs/03-PATTERN-GUIDES/connector-scenarios/examples/connector-scenarios-readme.md +253 -253
  304. package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-01-foundations.md +452 -452
  305. package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-02-simple-scenarios.md +681 -681
  306. package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-03-intermediate-scenarios.md +637 -637
  307. package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-04-advanced-scenarios.md +650 -650
  308. package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-05-bidirectional-sync.md +233 -233
  309. package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-06-production-patterns.md +442 -442
  310. package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-07-reference.md +445 -445
  311. package/docs/03-PATTERN-GUIDES/connector-scenarios/readme.md +31 -31
  312. package/docs/03-PATTERN-GUIDES/enterprise-integration-patterns.md +1528 -1528
  313. package/docs/03-PATTERN-GUIDES/error-handling/comprehensive-error-handling-guide.md +1437 -1437
  314. package/docs/03-PATTERN-GUIDES/error-handling/error-handling-quick-reference.md +390 -390
  315. package/docs/03-PATTERN-GUIDES/error-handling/examples/common-patterns.ts +438 -438
  316. package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-01-foundations.md +362 -362
  317. package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-02-error-types.md +850 -850
  318. package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-03-utf8-handling.md +456 -456
  319. package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-04-error-scenarios.md +658 -658
  320. package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-05-calling-patterns.md +671 -671
  321. package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-06-retry-strategies.md +1034 -1034
  322. package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-07-monitoring.md +653 -653
  323. package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-08-api-reference.md +847 -847
  324. package/docs/03-PATTERN-GUIDES/error-handling/readme.md +36 -36
  325. package/docs/03-PATTERN-GUIDES/examples/__tests__/readme.md +40 -40
  326. package/docs/03-PATTERN-GUIDES/examples/__tests__/resolver-examples.test.js +282 -282
  327. package/docs/03-PATTERN-GUIDES/examples/test-data/03-pattern-guides-readme.md +110 -110
  328. package/docs/03-PATTERN-GUIDES/examples/test-data/canonical-inventory.json +123 -123
  329. package/docs/03-PATTERN-GUIDES/examples/test-data/canonical-order.json +171 -171
  330. package/docs/03-PATTERN-GUIDES/examples/test-data/readme.md +28 -28
  331. package/docs/03-PATTERN-GUIDES/extraction/extraction-readme.md +15 -15
  332. package/docs/03-PATTERN-GUIDES/extraction/readme.md +25 -25
  333. package/docs/03-PATTERN-GUIDES/file-operations/examples/common-patterns.ts +407 -407
  334. package/docs/03-PATTERN-GUIDES/file-operations/examples/file-operations-readme.md +142 -142
  335. package/docs/03-PATTERN-GUIDES/file-operations/file-operations-quick-reference.md +462 -462
  336. package/docs/03-PATTERN-GUIDES/file-operations/file-operations-readme.md +379 -379
  337. package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-01-foundations.md +430 -430
  338. package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-02-quick-start.md +484 -484
  339. package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-03-s3-operations.md +507 -507
  340. package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-04-sftp-operations.md +963 -963
  341. package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-05-streaming-performance.md +503 -503
  342. package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-06-archive-patterns.md +386 -386
  343. package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-06-error-handling.md +117 -117
  344. package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-07-api-reference.md +78 -78
  345. package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-07-testing-troubleshooting.md +567 -567
  346. package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-08-api-reference.md +1055 -1055
  347. package/docs/03-PATTERN-GUIDES/file-operations/readme.md +32 -32
  348. package/docs/03-PATTERN-GUIDES/ingestion/ingestion-readme.md +15 -15
  349. package/docs/03-PATTERN-GUIDES/ingestion/readme.md +25 -25
  350. package/docs/03-PATTERN-GUIDES/integration-patterns/examples/batch-processing.ts +130 -130
  351. package/docs/03-PATTERN-GUIDES/integration-patterns/examples/common-patterns.ts +360 -360
  352. package/docs/03-PATTERN-GUIDES/integration-patterns/examples/delta-sync.ts +130 -130
  353. package/docs/03-PATTERN-GUIDES/integration-patterns/examples/integration-patterns-readme.md +100 -100
  354. package/docs/03-PATTERN-GUIDES/integration-patterns/examples/real-time-webhook.ts +398 -398
  355. package/docs/03-PATTERN-GUIDES/integration-patterns/integration-patterns-quick-reference.md +962 -962
  356. package/docs/03-PATTERN-GUIDES/integration-patterns/integration-patterns-readme.md +134 -134
  357. package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-01-real-time-processing.md +991 -991
  358. package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-02-batch-processing.md +1547 -1547
  359. package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-03-delta-sync.md +1108 -1108
  360. package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-04-webhook-patterns.md +1181 -1181
  361. package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-05-error-handling.md +1061 -1061
  362. package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-06-advanced-integration-services.md +1547 -1547
  363. package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-06-performance.md +109 -109
  364. package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-07-api-reference.md +34 -34
  365. package/docs/03-PATTERN-GUIDES/integration-patterns/readme.md +30 -30
  366. package/docs/03-PATTERN-GUIDES/logging-minimal-mode.md +128 -128
  367. package/docs/03-PATTERN-GUIDES/multiple-connections/examples/common-patterns.ts +380 -380
  368. package/docs/03-PATTERN-GUIDES/multiple-connections/examples/multiple-connections-readme.md +139 -139
  369. package/docs/03-PATTERN-GUIDES/multiple-connections/examples/parallel-root-connections.ts +149 -149
  370. package/docs/03-PATTERN-GUIDES/multiple-connections/examples/real-world-scenarios.ts +405 -405
  371. package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-01-foundations.md +378 -378
  372. package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-02-quick-start.md +566 -566
  373. package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-03-targeting-connections.md +659 -659
  374. package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-04-parallel-queries.md +656 -656
  375. package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-05-best-practices.md +624 -624
  376. package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-06-api-reference.md +824 -824
  377. package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-06-versori.md +119 -119
  378. package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-07-api-reference.md +87 -87
  379. package/docs/03-PATTERN-GUIDES/multiple-connections/multiple-connections-quick-reference.md +353 -353
  380. package/docs/03-PATTERN-GUIDES/multiple-connections/multiple-connections-readme.md +270 -270
  381. package/docs/03-PATTERN-GUIDES/multiple-connections/readme.md +30 -30
  382. package/docs/03-PATTERN-GUIDES/pagination/pagination-readme.md +14 -14
  383. package/docs/03-PATTERN-GUIDES/pagination/readme.md +24 -24
  384. package/docs/03-PATTERN-GUIDES/parquet/examples/common-patterns.ts +180 -180
  385. package/docs/03-PATTERN-GUIDES/parquet/examples/read-parquet.ts +48 -48
  386. package/docs/03-PATTERN-GUIDES/parquet/examples/write-parquet.ts +65 -65
  387. package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-01-introduction.md +393 -393
  388. package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-02-quick-start.md +572 -572
  389. package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-03-reading-parquet.md +525 -525
  390. package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-04-writing-parquet.md +554 -554
  391. package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-05-graphql-extraction.md +405 -405
  392. package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-06-performance.md +104 -104
  393. package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-06-s3-integration.md +511 -511
  394. package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-07-api-reference.md +90 -90
  395. package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-07-performance-optimization.md +525 -525
  396. package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-08-best-practices.md +712 -712
  397. package/docs/03-PATTERN-GUIDES/parquet/parquet-quick-reference.md +683 -683
  398. package/docs/03-PATTERN-GUIDES/parquet/parquet-readme.md +248 -248
  399. package/docs/03-PATTERN-GUIDES/parquet/readme.md +32 -32
  400. package/docs/03-PATTERN-GUIDES/parsers/parsers-readme.md +12 -12
  401. package/docs/03-PATTERN-GUIDES/parsers/readme.md +24 -24
  402. package/docs/03-PATTERN-GUIDES/readme.md +159 -159
  403. package/docs/03-PATTERN-GUIDES/webhooks/readme.md +24 -24
  404. package/docs/03-PATTERN-GUIDES/webhooks/webhooks-readme.md +8 -8
  405. package/docs/04-REFERENCE/architecture/architecture-01-overview.md +427 -427
  406. package/docs/04-REFERENCE/architecture/architecture-02-client-architecture.md +424 -424
  407. package/docs/04-REFERENCE/architecture/architecture-03-data-flow.md +690 -690
  408. package/docs/04-REFERENCE/architecture/architecture-04-service-layer.md +834 -834
  409. package/docs/04-REFERENCE/architecture/architecture-05-integration-architecture.md +655 -655
  410. package/docs/04-REFERENCE/architecture/architecture-06-state-management.md +653 -653
  411. package/docs/04-REFERENCE/architecture/architecture-adding-new-data-sources.md +686 -686
  412. package/docs/04-REFERENCE/architecture/readme.md +279 -279
  413. package/docs/04-REFERENCE/platforms/deno/readme.md +117 -117
  414. package/docs/04-REFERENCE/platforms/nodejs/readme.md +146 -146
  415. package/docs/04-REFERENCE/platforms/readme.md +135 -135
  416. package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-01-introduction.md +398 -398
  417. package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-02-quick-start.md +560 -560
  418. package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-03-authentication.md +757 -757
  419. package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-04-workflows.md +2476 -2476
  420. package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-05-connections.md +1167 -1167
  421. package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-06-kv-storage.md +990 -990
  422. package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-06-state-management.md +121 -121
  423. package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-07-api-reference.md +68 -68
  424. package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-07-deployment.md +731 -731
  425. package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-08-best-practices.md +1111 -1111
  426. package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-09-signature-reference.md +766 -766
  427. package/docs/04-REFERENCE/platforms/versori/platforms-versori-readme.md +299 -299
  428. package/docs/04-REFERENCE/platforms/versori/platforms-versori-s3-sftp-configuration-guide.md +1425 -1425
  429. package/docs/04-REFERENCE/platforms/versori/platforms-versori-webhook-api-key-security.md +816 -816
  430. package/docs/04-REFERENCE/platforms/versori/platforms-versori-webhook-connection-security.md +681 -681
  431. package/docs/04-REFERENCE/platforms/versori/platforms-versori-workflow-task-types.md +708 -708
  432. package/docs/04-REFERENCE/platforms/versori/readme.md +108 -108
  433. package/docs/04-REFERENCE/readme.md +148 -148
  434. package/docs/04-REFERENCE/resolver-signature/examples/advanced-resolvers.ts +482 -482
  435. package/docs/04-REFERENCE/resolver-signature/examples/async-resolvers.ts +496 -496
  436. package/docs/04-REFERENCE/resolver-signature/examples/basic-resolvers.ts +343 -343
  437. package/docs/04-REFERENCE/resolver-signature/examples/resolver-signature-readme.md +188 -188
  438. package/docs/04-REFERENCE/resolver-signature/examples/testing-resolvers.ts +463 -463
  439. package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-01-foundations.md +286 -286
  440. package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-02-parameter-reference.md +643 -643
  441. package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-03-basic-examples.md +521 -521
  442. package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-04-advanced-patterns.md +739 -739
  443. package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-05-sdk-resolvers.md +531 -531
  444. package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-06-migration-guide.md +650 -650
  445. package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-06-testing.md +125 -125
  446. package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-07-api-reference.md +794 -794
  447. package/docs/04-REFERENCE/resolver-signature/readme.md +64 -64
  448. package/docs/04-REFERENCE/resolver-signature/resolver-signature-quick-reference.md +270 -270
  449. package/docs/04-REFERENCE/resolver-signature/resolver-signature-readme.md +351 -351
  450. package/docs/04-REFERENCE/schema/fluent-commerce-schema.json +764 -764
  451. package/docs/04-REFERENCE/schema/readme.md +141 -141
  452. package/docs/04-REFERENCE/testing/examples/04-reference-testing-readme.md +158 -158
  453. package/docs/04-REFERENCE/testing/examples/fluent-testing.ts +62 -62
  454. package/docs/04-REFERENCE/testing/examples/health-check.ts +155 -155
  455. package/docs/04-REFERENCE/testing/examples/integration-test.ts +119 -119
  456. package/docs/04-REFERENCE/testing/examples/performance-test.ts +183 -183
  457. package/docs/04-REFERENCE/testing/examples/s3-testing.ts +127 -127
  458. package/docs/04-REFERENCE/testing/modules/04-reference-testing-01-foundations.md +267 -267
  459. package/docs/04-REFERENCE/testing/modules/04-reference-testing-02-s3-testing.md +599 -599
  460. package/docs/04-REFERENCE/testing/modules/04-reference-testing-03-fluent-testing.md +589 -589
  461. package/docs/04-REFERENCE/testing/modules/04-reference-testing-04-integration-testing.md +699 -699
  462. package/docs/04-REFERENCE/testing/modules/04-reference-testing-05-debugging.md +478 -478
  463. package/docs/04-REFERENCE/testing/modules/04-reference-testing-06-cicd-integration.md +463 -463
  464. package/docs/04-REFERENCE/testing/modules/04-reference-testing-06-preflight-validation.md +131 -131
  465. package/docs/04-REFERENCE/testing/modules/04-reference-testing-07-best-practices.md +499 -499
  466. package/docs/04-REFERENCE/testing/modules/04-reference-testing-07-coverage-ci.md +165 -165
  467. package/docs/04-REFERENCE/testing/modules/04-reference-testing-08-api-reference.md +634 -634
  468. package/docs/04-REFERENCE/testing/readme.md +86 -86
  469. package/docs/04-REFERENCE/testing/testing-quick-reference.md +667 -667
  470. package/docs/04-REFERENCE/testing/testing-readme.md +286 -286
  471. package/docs/04-REFERENCE/troubleshooting/readme.md +144 -144
  472. package/docs/04-REFERENCE/troubleshooting/troubleshooting-deno-sftp-compatibility.md +392 -392
  473. package/docs/template-loading-matrix.md +242 -242
  474. package/package.json +5 -3
  475. package/docs/02-CORE-GUIDES/api-reference/cli-profile-integration.md +0 -377
@@ -1,1034 +1,1034 @@
1
- # Module 6: Retry Strategies
2
-
3
- **Level:** Advanced
4
- **Estimated Time:** 25 minutes
5
-
6
- ## Overview
7
-
8
- This module teaches you how to implement effective retry strategies, including how to distinguish retryable from non-retryable errors and how to use exponential backoff to avoid overwhelming systems.
9
-
10
- ## Learning Objectives
11
-
12
- By the end of this module, you will:
13
- - ✅ Know which errors are retryable and which are permanent
14
- - ✅ Implement exponential backoff with jitter
15
- - ✅ Use the SDK's `isRetryable()` method effectively
16
- - ✅ Handle rate limiting and timeouts correctly
17
- - ✅ Build production-ready retry logic
18
-
19
- ## Retryable vs Non-Retryable Errors
20
-
21
- ### Non-Retryable Errors (Permanent Failures)
22
-
23
- These errors indicate problems with data or configuration that won't be fixed by retrying:
24
-
25
- ```typescript
26
- const NON_RETRYABLE_CODES = [
27
- IngestionErrorCode.PARSE_ERROR, // Invalid XML/JSON/CSV
28
- IngestionErrorCode.VALIDATION_ERROR, // Data validation failed
29
- IngestionErrorCode.REQUIRED_FIELD_MISSING, // Missing required field
30
- IngestionErrorCode.CONFIGURATION_ERROR, // Invalid config
31
- IngestionErrorCode.FIELD_MAPPING_ERROR, // Mapping config wrong
32
- IngestionErrorCode.INVALID_FILE_FORMAT, // Unsupported file type
33
- ];
34
- ```
35
-
36
- **Why not retryable:**
37
- - Source data is malformed
38
- - Configuration is incorrect
39
- - Business logic validation failed
40
- - Schema mismatch
41
-
42
- **What to do:**
43
- 1. Log the error with full context
44
- 2. Alert developers/operators
45
- 3. Fix the underlying issue (data or config)
46
- 4. Re-submit after fix
47
-
48
- **Example:**
49
-
50
- ```typescript
51
- if (error instanceof FileParsingError) {
52
- logger.error('PERMANENT ERROR - Fix source file:', {
53
- fileName: error.fileName,
54
- lineNumber: error.lineNumber,
55
- message: error.message
56
- });
57
-
58
- // Don't retry - save to dead letter queue for manual review
59
- await saveToDeadLetterQueue(fileName, content, error);
60
-
61
- throw error; // Fail immediately
62
- }
63
- ```
64
-
65
- ### Retryable Errors (Transient Failures)
66
-
67
- These errors are likely temporary and may succeed if retried:
68
-
69
- ```typescript
70
- const RETRYABLE_CODES = [
71
- IngestionErrorCode.NETWORK_ERROR, // Network connectivity issue
72
- IngestionErrorCode.TIMEOUT_ERROR, // Request timed out
73
- IngestionErrorCode.RATE_LIMIT_ERROR, // API rate limit hit
74
- IngestionErrorCode.LOCK_ACQUISITION_FAILED, // Distributed lock conflict
75
- ];
76
-
77
- // Also retryable:
78
- // - FluentAPIError with statusCode >= 500 (Server Error)
79
- // - FluentAPIError with statusCode === 429 (Rate Limit)
80
- // - GraphQLExecutionError with code 'TIMEOUT' or 'NETWORK_ERROR'
81
- ```
82
-
83
- **Why retryable:**
84
- - Network issues are often temporary
85
- - Timeouts may be due to temporary load
86
- - Rate limits reset after time period
87
- - Lock conflicts resolve when other process completes
88
-
89
- **What to do:**
90
- 1. Check `error.isRetryable()` returns `true`
91
- 2. Implement exponential backoff
92
- 3. Respect rate limit headers (`retryAfter`)
93
- 4. Set maximum retry limit
94
- 5. Log retry attempts
95
-
96
- **Example:**
97
-
98
- ```typescript
99
- if (error instanceof IngestionError && error.isRetryable()) {
100
- const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
101
- logger.info(`Retryable error - will retry in ${delay}ms`, {
102
- code: error.code,
103
- attempt,
104
- maxRetries
105
- });
106
-
107
- await new Promise(resolve => setTimeout(resolve, delay));
108
- return retry();
109
- }
110
- ```
111
-
112
- ## Using isRetryable()
113
-
114
- The SDK's `IngestionError` class provides an `isRetryable()` method:
115
-
116
- ```typescript
117
- abstract class IngestionError extends Error {
118
- isRetryable(): boolean {
119
- const retryableCodes = [
120
- IngestionErrorCode.NETWORK_ERROR,
121
- IngestionErrorCode.TIMEOUT_ERROR,
122
- IngestionErrorCode.RATE_LIMIT_ERROR,
123
- IngestionErrorCode.LOCK_ACQUISITION_FAILED,
124
- ];
125
-
126
- return retryableCodes.includes(this.code);
127
- }
128
- }
129
- ```
130
-
131
- ### Basic Usage
132
-
133
- ```typescript
134
- import { IngestionError } from '@fluentcommerce/fc-connect-sdk';
135
-
136
- try {
137
- await processFile(fileName);
138
- } catch (error) {
139
- if (error instanceof IngestionError) {
140
- if (error.isRetryable()) {
141
- // Retry logic
142
- return scheduleRetry();
143
- } else {
144
- // Permanent failure
145
- logger.error('Permanent error - manual intervention required');
146
- throw error;
147
- }
148
- }
149
- throw error;
150
- }
151
- ```
152
-
153
- ### Handling Non-SDK Errors
154
-
155
- Not all errors extend `IngestionError`:
156
-
157
- ```typescript
158
- try {
159
- await operation();
160
- } catch (error) {
161
- // Check SDK errors first
162
- if (error instanceof IngestionError) {
163
- if (!error.isRetryable()) {
164
- throw error; // Don't retry
165
- }
166
- }
167
-
168
- // File parsing errors are never retryable
169
- if (error instanceof FileParsingError) {
170
- throw error; // Don't retry
171
- }
172
-
173
- // Path resolution errors are config issues
174
- if (error instanceof PathResolutionError) {
175
- throw error; // Don't retry
176
- }
177
-
178
- // Mapping errors are data/config issues
179
- if (error instanceof MappingError) {
180
- throw error; // Don't retry
181
- }
182
-
183
- // Unknown errors - retry with caution
184
- if (attempt < maxRetries) {
185
- logger.warn('Unknown error - attempting retry', { error });
186
- await delay(1000 * attempt);
187
- return retry();
188
- }
189
-
190
- throw error;
191
- }
192
- ```
193
-
194
- ## Exponential Backoff
195
-
196
- ### Why Exponential Backoff?
197
-
198
- **Linear backoff** (same delay each time):
199
- ```
200
- Attempt 1: Wait 1s
201
- Attempt 2: Wait 1s
202
- Attempt 3: Wait 1s
203
- ```
204
- ❌ Doesn't give system time to recover from overload
205
-
206
- **Exponential backoff** (increasing delay):
207
- ```
208
- Attempt 1: Wait 1s
209
- Attempt 2: Wait 2s
210
- Attempt 3: Wait 4s
211
- Attempt 4: Wait 8s
212
- ```
213
- ✅ Reduces load on overloaded systems
214
- ✅ Increases chance of success on each retry
215
-
216
- ### Basic Implementation
217
-
218
- ```typescript
219
- async function retryWithBackoff<T>(
220
- operation: () => Promise<T>,
221
- maxRetries: number = 3,
222
- initialDelay: number = 1000
223
- ): Promise<T> {
224
- let lastError: Error;
225
-
226
- for (let attempt = 0; attempt < maxRetries; attempt++) {
227
- try {
228
- return await operation();
229
- } catch (error) {
230
- lastError = error;
231
-
232
- // Check if error is retryable
233
- if (error instanceof IngestionError) {
234
- if (!error.isRetryable()) {
235
- throw error; // Don't retry permanent errors
236
- }
237
- } else if (error instanceof MappingError ||
238
- error instanceof PathResolutionError ||
239
- error instanceof FileParsingError) {
240
- throw error; // These are permanent errors
241
- }
242
-
243
- // Last attempt - throw error
244
- if (attempt === maxRetries - 1) {
245
- throw lastError;
246
- }
247
-
248
- // Calculate delay with exponential backoff
249
- const delay = initialDelay * Math.pow(2, attempt);
250
-
251
- console.log(`Retry ${attempt + 1}/${maxRetries} after ${delay}ms`);
252
- await new Promise(resolve => setTimeout(resolve, delay));
253
- }
254
- }
255
-
256
- throw lastError!;
257
- }
258
-
259
- // Usage
260
- const result = await retryWithBackoff(
261
- () => processOrder(xmlContent),
262
- 3, // max retries
263
- 2000 // initial delay (2 seconds)
264
- );
265
- ```
266
-
267
- ### With Jitter
268
-
269
- Adding random jitter prevents thundering herd problem:
270
-
271
- ```typescript
272
- async function retryWithBackoffAndJitter<T>(
273
- operation: () => Promise<T>,
274
- maxRetries: number = 3,
275
- initialDelay: number = 1000,
276
- maxDelay: number = 60000
277
- ): Promise<T> {
278
- let lastError: Error;
279
-
280
- for (let attempt = 0; attempt < maxRetries; attempt++) {
281
- try {
282
- return await operation();
283
- } catch (error) {
284
- lastError = error;
285
-
286
- // Check if retryable
287
- if (error instanceof IngestionError && !error.isRetryable()) {
288
- throw error;
289
- }
290
- if (error instanceof MappingError ||
291
- error instanceof PathResolutionError ||
292
- error instanceof FileParsingError) {
293
- throw error;
294
- }
295
-
296
- if (attempt === maxRetries - 1) {
297
- throw lastError;
298
- }
299
-
300
- // Exponential backoff with jitter
301
- const exponentialDelay = initialDelay * Math.pow(2, attempt);
302
- const jitter = Math.random() * 100; // 0-100ms random jitter
303
- const delay = Math.min(exponentialDelay + jitter, maxDelay);
304
-
305
- console.log(`Retry ${attempt + 1}/${maxRetries} after ${delay}ms`);
306
- await new Promise(resolve => setTimeout(resolve, delay));
307
- }
308
- }
309
-
310
- throw lastError!;
311
- }
312
- ```
313
-
314
- **Benefits of jitter:**
315
- - ✅ Prevents multiple clients retrying at exact same time
316
- - ✅ Spreads load more evenly
317
- - ✅ Reduces likelihood of cascading failures
318
-
319
- ### Full-Jitter Strategy
320
-
321
- AWS recommended approach - more aggressive randomization:
322
-
323
- ```typescript
324
- function calculateBackoffWithFullJitter(
325
- attempt: number,
326
- baseDelay: number = 1000,
327
- maxDelay: number = 60000
328
- ): number {
329
- const exponentialDelay = baseDelay * Math.pow(2, attempt);
330
- const cappedDelay = Math.min(exponentialDelay, maxDelay);
331
-
332
- // Full jitter: random value between 0 and cappedDelay
333
- return Math.random() * cappedDelay;
334
- }
335
-
336
- // Usage
337
- const delay = calculateBackoffWithFullJitter(attempt);
338
- await new Promise(resolve => setTimeout(resolve, delay));
339
- ```
340
-
341
- ## Rate Limiting
342
-
343
- ### Handling Rate Limit Errors
344
-
345
- Respect the `retryAfter` value from rate limit errors:
346
-
347
- ```typescript
348
- import { IngestionError, IngestionErrorCode } from '@fluentcommerce/fc-connect-sdk';
349
-
350
- async function handleRateLimit(
351
- operation: () => Promise<any>,
352
- maxRetries: number = 3
353
- ): Promise<any> {
354
- for (let attempt = 0; attempt < maxRetries; attempt++) {
355
- try {
356
- return await operation();
357
- } catch (error) {
358
- if (error instanceof IngestionError &&
359
- error.code === IngestionErrorCode.RATE_LIMIT_ERROR) {
360
-
361
- // Get retry-after value from error context
362
- const retryAfter = error.context?.retryAfter || 60;
363
-
364
- if (attempt < maxRetries - 1) {
365
- logger.warn(`Rate limited - waiting ${retryAfter} seconds`, {
366
- attempt,
367
- maxRetries
368
- });
369
-
370
- // Wait for rate limit to reset
371
- await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
372
- continue;
373
- }
374
- }
375
-
376
- // Non-rate-limit error or max retries exceeded
377
- throw error;
378
- }
379
- }
380
-
381
- throw new Error('Max retries exceeded');
382
- }
383
- ```
384
-
385
- ### Rate Limit with Exponential Backoff Fallback
386
-
387
- If no `retryAfter` provided, use exponential backoff:
388
-
389
- ```typescript
390
- async function handleRateLimitSmart(
391
- operation: () => Promise<any>,
392
- maxRetries: number = 5
393
- ): Promise<any> {
394
- for (let attempt = 0; attempt < maxRetries; attempt++) {
395
- try {
396
- return await operation();
397
- } catch (error) {
398
- if (error instanceof IngestionError &&
399
- error.code === IngestionErrorCode.RATE_LIMIT_ERROR) {
400
-
401
- if (attempt >= maxRetries - 1) {
402
- throw error; // Max retries exceeded
403
- }
404
-
405
- // Use retryAfter if provided, else exponential backoff
406
- const retryAfter = error.context?.retryAfter;
407
- let delay: number;
408
-
409
- if (retryAfter) {
410
- delay = retryAfter * 1000; // Convert to ms
411
- logger.info(`Rate limited - retry after ${retryAfter}s`);
412
- } else {
413
- delay = Math.pow(2, attempt) * 1000; // Exponential backoff
414
- logger.info(`Rate limited - retry after ${delay}ms (no retryAfter)`);
415
- }
416
-
417
- await new Promise(resolve => setTimeout(resolve, delay));
418
- continue;
419
- }
420
-
421
- // Not a rate limit error
422
- throw error;
423
- }
424
- }
425
-
426
- throw new Error('Max retries exceeded');
427
- }
428
- ```
429
-
430
- ## Timeout Handling
431
-
432
- ### Network Timeouts
433
-
434
- Handle network timeouts with retry:
435
-
436
- ```typescript
437
- async function executeWithTimeout<T>(
438
- operation: () => Promise<T>,
439
- timeoutMs: number = 30000,
440
- maxRetries: number = 3
441
- ): Promise<T> {
442
- for (let attempt = 0; attempt < maxRetries; attempt++) {
443
- try {
444
- // Race between operation and timeout
445
- const result = await Promise.race([
446
- operation(),
447
- new Promise<never>((_, reject) =>
448
- setTimeout(() => reject(new Error('Operation timeout')), timeoutMs)
449
- )
450
- ]);
451
-
452
- return result;
453
-
454
- } catch (error) {
455
- if (error.message === 'Operation timeout') {
456
- if (attempt < maxRetries - 1) {
457
- const delay = Math.pow(2, attempt) * 1000;
458
- logger.warn(`Timeout - retrying in ${delay}ms`, { attempt });
459
- await new Promise(resolve => setTimeout(resolve, delay));
460
- continue;
461
- }
462
- }
463
-
464
- // Not a timeout or max retries exceeded
465
- throw error;
466
- }
467
- }
468
-
469
- throw new Error('Max retries exceeded');
470
- }
471
- ```
472
-
473
- ## SDK Built-In Retry Logic
474
-
475
- ### S3DataSource Retry
476
-
477
- The SDK's S3DataSource uses the shared `RetryHelper` utility for consistent retry behavior:
478
-
479
- ```typescript
480
- import { S3DataSource } from '@fluentcommerce/fc-connect-sdk';
481
-
482
- const s3Config = {
483
- type: 'S3_CSV' as const,
484
- s3Config: {
485
- bucket: 'my-bucket',
486
- region: 'us-east-1',
487
- accessKeyId: process.env.AWS_ACCESS_KEY_ID,
488
- secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
489
- maxAttempts: 3 // Default: 3 retries
490
- }
491
- };
492
-
493
- const s3 = new S3DataSource(s3Config, logger);
494
-
495
- // Automatic retry on:
496
- // - HTTP 429 (rate limit)
497
- // - HTTP 500, 502, 503, 504 (server errors)
498
- // - Network errors (ECONNRESET, ETIMEDOUT, etc.)
499
- const files = await s3.listFiles(); // Retries automatically
500
- const content = await s3.downloadFile(key); // Retries automatically
501
- ```
502
-
503
- **Retry Configuration:**
504
- - **Max attempts**: Configurable via `maxAttempts` (default: 3)
505
- - **Base delay**: 1000ms
506
- - **Backoff factor**: 2 (exponential)
507
- - **Max delay**: 8000ms (8 seconds)
508
- - **Jitter**: Full jitter (AWS recommended)
509
- - **Retryable status codes**: 429, 500, 502, 503, 504
510
- - **Retryable network errors**: Connection resets, timeouts, unreachable hosts
511
-
512
- **Implementation Note:** S3DataSource uses `RetryHelper.executeWithRetry()` from `src/utils/retry-helper.ts` for consistency across all SDK HTTP operations.
513
-
514
- **SFTP Note:** SftpDataSource uses custom retry logic due to connection pooling requirements. See lines 536-645 for SFTP-specific retry patterns.
515
-
516
- ### SftpDataSource Retry
517
-
518
- The SDK's SftpDataSource includes configurable retry logic with connection pooling:
519
-
520
- ```typescript
521
- import { SftpDataSource, SftpDataSourceConfig } from '@fluentcommerce/fc-connect-sdk';
522
-
523
- const sftpConfig: SftpDataSourceConfig = {
524
- type: 'SFTP_CSV',
525
- settings: {
526
- host: 'sftp.example.com',
527
- port: 22,
528
- username: 'user',
529
- password: 'pass',
530
- remotePath: '/data',
531
- filePattern: '*.csv',
532
-
533
- // Enhanced retry configuration
534
- retry: {
535
- maxAttempts: 3, // Default: 3
536
- baseDelayMs: 1000, // Default: 1000ms (1 second)
537
- backoffFactor: 2, // Default: 2 (exponential)
538
- maxDelayMs: 8000, // Default: 8000ms (8 seconds)
539
- jitter: 'full', // Default: 'full' jitter
540
- reconnectOnFailure: true, // Default: true - reconnect on error
541
- isRetryable: (error) => {
542
- // Custom retry logic (optional)
543
- return error.message.includes('timeout');
544
- }
545
- }
546
- }
547
- };
548
-
549
- const sftp = new SftpDataSource(sftpConfig, logger);
550
-
551
- // Automatic retry with exponential backoff and connection pooling
552
- const files = await sftp.listFiles(); // Retries automatically!
553
- const content = await sftp.downloadFile('file.csv'); // Retries automatically!
554
- ```
555
-
556
- **Retry behavior:**
557
- - **Default strategy**: Exponential backoff with full jitter
558
- - **Connection pooling**: Reuses connections, creates new ones when needed
559
- - **Wait queue**: Queues requests when pool is full (prevents connection spam)
560
- - **Reconnect on failure**: Automatically reconnects on transient errors
561
-
562
- **Retryable errors:**
563
- - Connection resets (`ECONNRESET`)
564
- - Timeouts (`ETIMEDOUT`)
565
- - Host unreachable (`EHOSTUNREACH`)
566
- - Connection refused (`ECONNREFUSED`)
567
- - Broken pipe (`EPIPE`)
568
- - Message patterns: "connection lost", "timeout", "network error"
569
-
570
- **Non-retryable errors:**
571
- - Authentication failures
572
- - Permission denied
573
- - File not found
574
- - Algorithm/cipher errors
575
- - Key exchange failures
576
-
577
- **Implementation details:**
578
- ```typescript
579
- private async executeWithRetry<T>(
580
- operation: () => Promise<T>,
581
- operationName: string,
582
- retryOverride?: Partial<SftpRetryConfig>
583
- ): Promise<T> {
584
- const config = { ...this.retryConfig, ...retryOverride };
585
- let lastError: Error;
586
-
587
- for (let attempt = 1; attempt <= config.maxAttempts; attempt++) {
588
- try {
589
- return await operation();
590
- } catch (error) {
591
- lastError = error as Error;
592
- const isRetryable = config.isRetryable(lastError);
593
- const isLastAttempt = attempt === config.maxAttempts;
594
-
595
- if (!isRetryable || isLastAttempt) {
596
- throw error;
597
- }
598
-
599
- // Reconnect on failure if enabled
600
- if (config.reconnectOnFailure) {
601
- await this.closeConnection(connectionId);
602
- }
603
-
604
- // Calculate delay with jitter
605
- const delay = this.calculateDelay(attempt);
606
- await new Promise(resolve => setTimeout(resolve, delay));
607
- }
608
- }
609
-
610
- throw lastError!;
611
- }
612
-
613
- private calculateDelay(attempt: number): number {
614
- const exponentialDelay =
615
- this.retryConfig.baseDelayMs * Math.pow(this.retryConfig.backoffFactor, attempt - 1);
616
- const cappedDelay = Math.min(exponentialDelay, this.retryConfig.maxDelayMs);
617
-
618
- if (this.retryConfig.jitter === 'none') {
619
- return cappedDelay;
620
- }
621
-
622
- // Full jitter: random between 0 and cappedDelay
623
- return Math.floor(Math.random() * cappedDelay);
624
- }
625
- ```
626
-
627
- ### Connection Pooling (SFTP only)
628
-
629
- ```typescript
630
- // Connection pool configuration
631
- const sftpConfig: SftpDataSourceConfig = {
632
- type: 'SFTP_CSV',
633
- settings: {
634
- // ... other settings
635
- maxConnections: 5 // Default: 5 concurrent connections
636
- }
637
- };
638
-
639
- // Connection pooling features:
640
- // - Reuses idle connections
641
- // - Creates new connections up to maxConnections
642
- // - Queues requests when pool is full
643
- // - Automatically closes idle connections after timeout
644
- // - Handles connection failures gracefully
645
- ```
646
-
647
- ### When to Use Built-In Retry vs Custom Retry
648
-
649
- **Use SDK built-in retry when:**
650
- - ✅ Working with S3DataSource or SftpDataSource
651
- - ✅ Default retry behavior is acceptable
652
- - ✅ You want zero configuration
653
- - ✅ You need connection pooling (SFTP)
654
-
655
- **Use custom retry when:**
656
- - ✅ Working with FluentClient GraphQL operations
657
- - ✅ Need custom retry logic beyond data source operations
658
- - ✅ Need to coordinate retries across multiple services
659
- - ✅ Want different retry strategies for different operations
660
-
661
- **Example: Combining both**
662
-
663
- ```typescript
664
- // S3 operations use built-in retry
665
- const files = await s3.listFiles(); // Automatic retry
666
-
667
- // Custom retry for high-level workflow
668
- await retryOperation(
669
- async () => {
670
- // Download (automatic retry)
671
- const content = await s3.downloadFile(files[0].path);
672
-
673
- // Parse
674
- const parsed = await parser.parse(content);
675
-
676
- // Map
677
- const payload = await mapper.map(parsed);
678
-
679
- // Submit to Fluent (custom retry)
680
- return await client.graphql(payload);
681
- },
682
- {
683
- maxRetries: 5,
684
- initialDelay: 2000
685
- }
686
- );
687
- ```
688
-
689
- ## Partial Batch Recovery
690
-
691
- ### PartialBatchRecovery Service
692
-
693
- The SDK's `PartialBatchRecovery` service handles partial batch failures gracefully by retrying only failed records instead of the entire batch.
694
-
695
- **When to use:**
696
- - Large batch imports where some records may fail
697
- - Network errors affecting subset of records
698
- - Validation failures on specific records
699
- - Want to minimize reprocessing overhead
700
-
701
- ```typescript
702
- import {
703
- PartialBatchRecovery,
704
- createClient,
705
- createConsoleLogger,
706
- toStructuredLogger
707
- } from '@fluentcommerce/fc-connect-sdk';
708
-
709
- // Standalone
710
- const logger = toStructuredLogger(createConsoleLogger(), { service: 'BatchRecovery' });
711
-
712
- // Versori Platform
713
- const { log: logger } = ctx;
714
-
715
- const recovery = new PartialBatchRecovery(logger);
716
- const client = await createClient({ config });
717
-
718
- // Process batch with automatic retry of failed records
719
- const result = await recovery.processBatchWithRecovery(
720
- inventoryRecords,
721
- async (batch) => {
722
- // Your batch processing logic
723
- const job = await client.createJob({
724
- name: 'Inventory Sync with Recovery',
725
- retailerId: 'my-retailer'
726
- });
727
-
728
- const batchResult = await client.sendBatch(job.id, {
729
- action: 'UPSERT',
730
- entityType: 'INVENTORY',
731
- entities: batch
732
- });
733
-
734
- return batchResult;
735
- },
736
- {
737
- maxRetries: 3, // Retry each failed record up to 3 times
738
- retryOnlyFailed: true, // Only retry failed records (default: true)
739
- batchSize: 100, // Process in batches of 100
740
- retryDelay: 1000, // Wait 1 second between retries
741
- exponentialBackoff: true // Use exponential backoff (default: true)
742
- }
743
- );
744
-
745
- // Results
746
- console.log(`Total records: ${inventoryRecords.length}`);
747
- console.log(`Successful: ${result.successCount}`);
748
- console.log(`Failed: ${result.failedCount}`);
749
-
750
- if (result.failedCount > 0) {
751
- console.log('Failed records:');
752
- result.failedRecords.forEach(({ record, error, attemptCount }) => {
753
- console.error(`Record ${record.ref} failed after ${attemptCount} attempts:`, error.message);
754
- });
755
- }
756
- ```
757
-
758
- ### Recovery Result Structure
759
-
760
- ```typescript
761
- interface RecoveryResult {
762
- successCount: number; // Total successful records
763
- failedCount: number; // Total failed after all retries
764
- failedRecords: Array<{
765
- record: any; // The record that failed
766
- error: Error; // Last error that occurred
767
- attemptCount: number; // Total retry attempts for this record
768
- }>;
769
- }
770
- ```
771
-
772
- ### Advanced Recovery Options
773
-
774
- ```typescript
775
- const result = await recovery.processBatchWithRecovery(
776
- records,
777
- processFn,
778
- {
779
- maxRetries: 5,
780
- retryOnlyFailed: true,
781
- batchSize: 50,
782
- retryDelay: 2000,
783
- exponentialBackoff: true,
784
-
785
- // Custom retry decision logic
786
- shouldRetry: (error, attemptCount) => {
787
- // Don't retry validation errors
788
- if (error instanceof ValidationError) {
789
- return false;
790
- }
791
- // Retry network/timeout errors
792
- if (error instanceof IngestionError) {
793
- return error.isRetryable() && attemptCount < 5;
794
- }
795
- return attemptCount < 3;
796
- },
797
-
798
- // Custom backoff calculation
799
- calculateDelay: (attempt, baseDelay) => {
800
- return Math.min(baseDelay * Math.pow(2, attempt), 30000);
801
- },
802
-
803
- // Progress callback
804
- onProgress: (current, total) => {
805
- logger.info(`Processing: ${current}/${total} records`);
806
- },
807
-
808
- // Retry callback
809
- onRetry: (record, attempt, error) => {
810
- logger.warn(`Retrying record ${record.ref}, attempt ${attempt}`, {
811
- error: error.message
812
- });
813
- }
814
- }
815
- );
816
- ```
817
-
818
- ### Combining with Built-In Retry
819
-
820
- PartialBatchRecovery works well with SDK's built-in retry logic:
821
-
822
- ```typescript
823
- // S3 operations have automatic retry
824
- const files = await s3.listFiles(); // Automatic retry on network errors
825
-
826
- // Parse files
827
- const records = [];
828
- for (const file of files) {
829
- const content = await s3.downloadFile(file.path); // Automatic retry
830
- const parsed = await parser.parse(content);
831
- records.push(...parsed);
832
- }
833
-
834
- // Use PartialBatchRecovery for batch submission
835
- const result = await recovery.processBatchWithRecovery(
836
- records,
837
- async (batch) => {
838
- const job = await client.createJob({ name: 'Sync', retailerId });
839
- return await client.sendBatch(job.id, {
840
- action: 'UPSERT',
841
- entityType: 'INVENTORY',
842
- entities: batch
843
- });
844
- },
845
- {
846
- maxRetries: 3,
847
- retryOnlyFailed: true
848
- }
849
- );
850
- ```
851
-
852
- ### Benefits of PartialBatchRecovery
853
-
854
- **Efficiency:**
855
- - ✅ Only retries failed records (not entire batch)
856
- - ✅ Reduces API calls and processing time
857
- - ✅ Lower resource consumption
858
-
859
- **Reliability:**
860
- - ✅ Tracks each record individually
861
- - ✅ Configurable retry logic per record
862
- - ✅ Detailed failure reporting
863
-
864
- **Observability:**
865
- - ✅ Know exactly which records failed
866
- - ✅ Understand failure patterns
867
- - ✅ Better error reporting for monitoring
868
-
869
- **Example: 1000 records, 10 fail**
870
- - **Without PartialBatchRecovery:** Retry all 1000 records
871
- - **With PartialBatchRecovery:** Retry only 10 failed records
872
-
873
- ## Production-Ready Retry Function
874
-
875
- Complete retry implementation with all best practices:
876
-
877
- ```typescript
878
- import {
879
- IngestionError,
880
- IngestionErrorCode,
881
- FileParsingError,
882
- PathResolutionError,
883
- MappingError,
884
- FluentAPIError,
885
- GraphQLExecutionError
886
- } from '@fluentcommerce/fc-connect-sdk';
887
-
888
- interface RetryOptions {
889
- maxRetries?: number;
890
- initialDelay?: number;
891
- maxDelay?: number;
892
- useJitter?: boolean;
893
- onRetry?: (attempt: number, error: Error) => void;
894
- }
895
-
896
- async function retryOperation<T>(
897
- operation: () => Promise<T>,
898
- options: RetryOptions = {}
899
- ): Promise<T> {
900
- const {
901
- maxRetries = 3,
902
- initialDelay = 1000,
903
- maxDelay = 60000,
904
- useJitter = true,
905
- onRetry
906
- } = options;
907
-
908
- let lastError: Error;
909
-
910
- for (let attempt = 0; attempt < maxRetries; attempt++) {
911
- try {
912
- return await operation();
913
-
914
- } catch (error) {
915
- lastError = error;
916
-
917
- // 1. Don't retry permanent errors
918
- if (error instanceof FileParsingError ||
919
- error instanceof PathResolutionError ||
920
- error instanceof MappingError) {
921
- throw error;
922
- }
923
-
924
- // 2. Check FluentAPIError (HTTP 5xx / 429)
925
- let isRetryable = false;
926
- let retryDelay = 0;
927
-
928
- if (error instanceof FluentAPIError) {
929
- if (error.statusCode >= 500 || error.statusCode === 429) {
930
- isRetryable = true;
931
- } else {
932
- throw error; // 4xx are permanent
933
- }
934
- }
935
-
936
- // 3. Check IngestionError
937
- else if (error instanceof IngestionError) {
938
- if (error.isRetryable()) {
939
- isRetryable = true;
940
- // Special handling for rate limit error context
941
- if (error.code === IngestionErrorCode.RATE_LIMIT_ERROR) {
942
- retryDelay = (error.context?.retryAfter || 60) * 1000;
943
- }
944
- } else {
945
- throw error;
946
- }
947
- }
948
-
949
- // 4. Check GraphQLExecutionError
950
- else if (error instanceof GraphQLExecutionError) {
951
- const firstError = error.graphqlErrors[0];
952
- if (firstError?.extensions?.code === 'TIMEOUT' ||
953
- firstError?.extensions?.code === 'NETWORK_ERROR') {
954
- isRetryable = true;
955
- } else {
956
- throw error; // Validation errors are permanent
957
- }
958
- }
959
-
960
- // Last attempt - throw
961
- if (attempt >= maxRetries - 1) {
962
- throw error;
963
- }
964
-
965
- // Calculate delay if not already set
966
- if (!retryDelay) {
967
- let delay = initialDelay * Math.pow(2, attempt);
968
- if (useJitter) {
969
- delay = Math.random() * delay;
970
- }
971
- retryDelay = Math.min(delay, maxDelay);
972
- }
973
-
974
- // Callback
975
- if (onRetry) {
976
- onRetry(attempt + 1, error);
977
- }
978
-
979
- await new Promise(resolve => setTimeout(resolve, retryDelay));
980
- }
981
- }
982
-
983
- throw lastError!;
984
- }
985
-
986
- // Usage
987
- const result = await retryOperation(
988
- () => processOrder(xmlContent),
989
- {
990
- maxRetries: 5,
991
- initialDelay: 2000,
992
- maxDelay: 60000,
993
- useJitter: true,
994
- onRetry: (attempt, error) => {
995
- logger.warn('Retrying operation', { attempt, error: error.message });
996
- }
997
- }
998
- );
999
- ```
1000
-
1001
- ## Key Takeaways
1002
-
1003
- - 🎯 **SDK built-in retry** - S3DataSource and SftpDataSource have automatic retry
1004
- - 🎯 **Use isRetryable()** - Let SDK determine if error is transient
1005
- - 🎯 **Exponential backoff** - Increases delay between retries (with jitter)
1006
- - 🎯 **Connection pooling** - SftpDataSource reuses connections efficiently
1007
- - 🎯 **Add jitter** - Prevents thundering herd problem (full jitter recommended)
1008
- - 🎯 **Respect rate limits** - Use `retryAfter` when provided
1009
- - 🎯 **Set max retries** - Prevent infinite retry loops (default: 3)
1010
- - 🎯 **Don't retry permanent errors** - Parse, mapping, validation errors won't succeed
1011
-
1012
- ## Practice Exercise
1013
-
1014
- Implement a retry function that:
1015
- 1. Retries only transient errors
1016
- 2. Uses exponential backoff with jitter
1017
- 3. Respects rate limit headers
1018
- 4. Has a maximum of 5 retries
1019
-
1020
- <details>
1021
- <summary>Click to see solution</summary>
1022
-
1023
- See the "Production-Ready Retry Function" section above for a complete implementation.
1024
-
1025
- </details>
1026
-
1027
- ## Next Steps
1028
-
1029
- Continue to [Module 7: Monitoring →](./error-handling-07-monitoring.md) to learn how to effectively log and monitor errors.
1030
-
1031
- ---
1032
-
1033
- **Previous:** [← Module 5: Calling Patterns](./error-handling-05-calling-patterns.md)
1034
- **Next:** [Module 7: Monitoring →](./error-handling-07-monitoring.md)
1
+ # Module 6: Retry Strategies
2
+
3
+ **Level:** Advanced
4
+ **Estimated Time:** 25 minutes
5
+
6
+ ## Overview
7
+
8
+ This module teaches you how to implement effective retry strategies, including how to distinguish retryable from non-retryable errors and how to use exponential backoff to avoid overwhelming systems.
9
+
10
+ ## Learning Objectives
11
+
12
+ By the end of this module, you will:
13
+ - ✅ Know which errors are retryable and which are permanent
14
+ - ✅ Implement exponential backoff with jitter
15
+ - ✅ Use the SDK's `isRetryable()` method effectively
16
+ - ✅ Handle rate limiting and timeouts correctly
17
+ - ✅ Build production-ready retry logic
18
+
19
+ ## Retryable vs Non-Retryable Errors
20
+
21
+ ### Non-Retryable Errors (Permanent Failures)
22
+
23
+ These errors indicate problems with data or configuration that won't be fixed by retrying:
24
+
25
+ ```typescript
26
+ const NON_RETRYABLE_CODES = [
27
+ IngestionErrorCode.PARSE_ERROR, // Invalid XML/JSON/CSV
28
+ IngestionErrorCode.VALIDATION_ERROR, // Data validation failed
29
+ IngestionErrorCode.REQUIRED_FIELD_MISSING, // Missing required field
30
+ IngestionErrorCode.CONFIGURATION_ERROR, // Invalid config
31
+ IngestionErrorCode.FIELD_MAPPING_ERROR, // Mapping config wrong
32
+ IngestionErrorCode.INVALID_FILE_FORMAT, // Unsupported file type
33
+ ];
34
+ ```
35
+
36
+ **Why not retryable:**
37
+ - Source data is malformed
38
+ - Configuration is incorrect
39
+ - Business logic validation failed
40
+ - Schema mismatch
41
+
42
+ **What to do:**
43
+ 1. Log the error with full context
44
+ 2. Alert developers/operators
45
+ 3. Fix the underlying issue (data or config)
46
+ 4. Re-submit after fix
47
+
48
+ **Example:**
49
+
50
+ ```typescript
51
+ if (error instanceof FileParsingError) {
52
+ logger.error('PERMANENT ERROR - Fix source file:', {
53
+ fileName: error.fileName,
54
+ lineNumber: error.lineNumber,
55
+ message: error.message
56
+ });
57
+
58
+ // Don't retry - save to dead letter queue for manual review
59
+ await saveToDeadLetterQueue(fileName, content, error);
60
+
61
+ throw error; // Fail immediately
62
+ }
63
+ ```
64
+
65
+ ### Retryable Errors (Transient Failures)
66
+
67
+ These errors are likely temporary and may succeed if retried:
68
+
69
+ ```typescript
70
+ const RETRYABLE_CODES = [
71
+ IngestionErrorCode.NETWORK_ERROR, // Network connectivity issue
72
+ IngestionErrorCode.TIMEOUT_ERROR, // Request timed out
73
+ IngestionErrorCode.RATE_LIMIT_ERROR, // API rate limit hit
74
+ IngestionErrorCode.LOCK_ACQUISITION_FAILED, // Distributed lock conflict
75
+ ];
76
+
77
+ // Also retryable:
78
+ // - FluentAPIError with statusCode >= 500 (Server Error)
79
+ // - FluentAPIError with statusCode === 429 (Rate Limit)
80
+ // - GraphQLExecutionError with code 'TIMEOUT' or 'NETWORK_ERROR'
81
+ ```
82
+
83
+ **Why retryable:**
84
+ - Network issues are often temporary
85
+ - Timeouts may be due to temporary load
86
+ - Rate limits reset after time period
87
+ - Lock conflicts resolve when other process completes
88
+
89
+ **What to do:**
90
+ 1. Check `error.isRetryable()` returns `true`
91
+ 2. Implement exponential backoff
92
+ 3. Respect rate limit headers (`retryAfter`)
93
+ 4. Set maximum retry limit
94
+ 5. Log retry attempts
95
+
96
+ **Example:**
97
+
98
+ ```typescript
99
+ if (error instanceof IngestionError && error.isRetryable()) {
100
+ const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
101
+ logger.info(`Retryable error - will retry in ${delay}ms`, {
102
+ code: error.code,
103
+ attempt,
104
+ maxRetries
105
+ });
106
+
107
+ await new Promise(resolve => setTimeout(resolve, delay));
108
+ return retry();
109
+ }
110
+ ```
111
+
112
+ ## Using isRetryable()
113
+
114
+ The SDK's `IngestionError` class provides an `isRetryable()` method:
115
+
116
+ ```typescript
117
+ abstract class IngestionError extends Error {
118
+ isRetryable(): boolean {
119
+ const retryableCodes = [
120
+ IngestionErrorCode.NETWORK_ERROR,
121
+ IngestionErrorCode.TIMEOUT_ERROR,
122
+ IngestionErrorCode.RATE_LIMIT_ERROR,
123
+ IngestionErrorCode.LOCK_ACQUISITION_FAILED,
124
+ ];
125
+
126
+ return retryableCodes.includes(this.code);
127
+ }
128
+ }
129
+ ```
130
+
131
+ ### Basic Usage
132
+
133
+ ```typescript
134
+ import { IngestionError } from '@fluentcommerce/fc-connect-sdk';
135
+
136
+ try {
137
+ await processFile(fileName);
138
+ } catch (error) {
139
+ if (error instanceof IngestionError) {
140
+ if (error.isRetryable()) {
141
+ // Retry logic
142
+ return scheduleRetry();
143
+ } else {
144
+ // Permanent failure
145
+ logger.error('Permanent error - manual intervention required');
146
+ throw error;
147
+ }
148
+ }
149
+ throw error;
150
+ }
151
+ ```
152
+
153
+ ### Handling Non-SDK Errors
154
+
155
+ Not all errors extend `IngestionError`:
156
+
157
+ ```typescript
158
+ try {
159
+ await operation();
160
+ } catch (error) {
161
+ // Check SDK errors first
162
+ if (error instanceof IngestionError) {
163
+ if (!error.isRetryable()) {
164
+ throw error; // Don't retry
165
+ }
166
+ }
167
+
168
+ // File parsing errors are never retryable
169
+ if (error instanceof FileParsingError) {
170
+ throw error; // Don't retry
171
+ }
172
+
173
+ // Path resolution errors are config issues
174
+ if (error instanceof PathResolutionError) {
175
+ throw error; // Don't retry
176
+ }
177
+
178
+ // Mapping errors are data/config issues
179
+ if (error instanceof MappingError) {
180
+ throw error; // Don't retry
181
+ }
182
+
183
+ // Unknown errors - retry with caution
184
+ if (attempt < maxRetries) {
185
+ logger.warn('Unknown error - attempting retry', { error });
186
+ await delay(1000 * attempt);
187
+ return retry();
188
+ }
189
+
190
+ throw error;
191
+ }
192
+ ```
193
+
194
+ ## Exponential Backoff
195
+
196
+ ### Why Exponential Backoff?
197
+
198
+ **Linear backoff** (same delay each time):
199
+ ```
200
+ Attempt 1: Wait 1s
201
+ Attempt 2: Wait 1s
202
+ Attempt 3: Wait 1s
203
+ ```
204
+ ❌ Doesn't give system time to recover from overload
205
+
206
+ **Exponential backoff** (increasing delay):
207
+ ```
208
+ Attempt 1: Wait 1s
209
+ Attempt 2: Wait 2s
210
+ Attempt 3: Wait 4s
211
+ Attempt 4: Wait 8s
212
+ ```
213
+ ✅ Reduces load on overloaded systems
214
+ ✅ Increases chance of success on each retry
215
+
216
+ ### Basic Implementation
217
+
218
+ ```typescript
219
+ async function retryWithBackoff<T>(
220
+ operation: () => Promise<T>,
221
+ maxRetries: number = 3,
222
+ initialDelay: number = 1000
223
+ ): Promise<T> {
224
+ let lastError: Error;
225
+
226
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
227
+ try {
228
+ return await operation();
229
+ } catch (error) {
230
+ lastError = error;
231
+
232
+ // Check if error is retryable
233
+ if (error instanceof IngestionError) {
234
+ if (!error.isRetryable()) {
235
+ throw error; // Don't retry permanent errors
236
+ }
237
+ } else if (error instanceof MappingError ||
238
+ error instanceof PathResolutionError ||
239
+ error instanceof FileParsingError) {
240
+ throw error; // These are permanent errors
241
+ }
242
+
243
+ // Last attempt - throw error
244
+ if (attempt === maxRetries - 1) {
245
+ throw lastError;
246
+ }
247
+
248
+ // Calculate delay with exponential backoff
249
+ const delay = initialDelay * Math.pow(2, attempt);
250
+
251
+ console.log(`Retry ${attempt + 1}/${maxRetries} after ${delay}ms`);
252
+ await new Promise(resolve => setTimeout(resolve, delay));
253
+ }
254
+ }
255
+
256
+ throw lastError!;
257
+ }
258
+
259
+ // Usage
260
+ const result = await retryWithBackoff(
261
+ () => processOrder(xmlContent),
262
+ 3, // max retries
263
+ 2000 // initial delay (2 seconds)
264
+ );
265
+ ```
266
+
267
+ ### With Jitter
268
+
269
+ Adding random jitter prevents thundering herd problem:
270
+
271
+ ```typescript
272
+ async function retryWithBackoffAndJitter<T>(
273
+ operation: () => Promise<T>,
274
+ maxRetries: number = 3,
275
+ initialDelay: number = 1000,
276
+ maxDelay: number = 60000
277
+ ): Promise<T> {
278
+ let lastError: Error;
279
+
280
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
281
+ try {
282
+ return await operation();
283
+ } catch (error) {
284
+ lastError = error;
285
+
286
+ // Check if retryable
287
+ if (error instanceof IngestionError && !error.isRetryable()) {
288
+ throw error;
289
+ }
290
+ if (error instanceof MappingError ||
291
+ error instanceof PathResolutionError ||
292
+ error instanceof FileParsingError) {
293
+ throw error;
294
+ }
295
+
296
+ if (attempt === maxRetries - 1) {
297
+ throw lastError;
298
+ }
299
+
300
+ // Exponential backoff with jitter
301
+ const exponentialDelay = initialDelay * Math.pow(2, attempt);
302
+ const jitter = Math.random() * 100; // 0-100ms random jitter
303
+ const delay = Math.min(exponentialDelay + jitter, maxDelay);
304
+
305
+ console.log(`Retry ${attempt + 1}/${maxRetries} after ${delay}ms`);
306
+ await new Promise(resolve => setTimeout(resolve, delay));
307
+ }
308
+ }
309
+
310
+ throw lastError!;
311
+ }
312
+ ```
313
+
314
+ **Benefits of jitter:**
315
+ - ✅ Prevents multiple clients retrying at exact same time
316
+ - ✅ Spreads load more evenly
317
+ - ✅ Reduces likelihood of cascading failures
318
+
319
+ ### Full-Jitter Strategy
320
+
321
+ AWS recommended approach - more aggressive randomization:
322
+
323
+ ```typescript
324
+ function calculateBackoffWithFullJitter(
325
+ attempt: number,
326
+ baseDelay: number = 1000,
327
+ maxDelay: number = 60000
328
+ ): number {
329
+ const exponentialDelay = baseDelay * Math.pow(2, attempt);
330
+ const cappedDelay = Math.min(exponentialDelay, maxDelay);
331
+
332
+ // Full jitter: random value between 0 and cappedDelay
333
+ return Math.random() * cappedDelay;
334
+ }
335
+
336
+ // Usage
337
+ const delay = calculateBackoffWithFullJitter(attempt);
338
+ await new Promise(resolve => setTimeout(resolve, delay));
339
+ ```
340
+
341
+ ## Rate Limiting
342
+
343
+ ### Handling Rate Limit Errors
344
+
345
+ Respect the `retryAfter` value from rate limit errors:
346
+
347
+ ```typescript
348
+ import { IngestionError, IngestionErrorCode } from '@fluentcommerce/fc-connect-sdk';
349
+
350
+ async function handleRateLimit(
351
+ operation: () => Promise<any>,
352
+ maxRetries: number = 3
353
+ ): Promise<any> {
354
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
355
+ try {
356
+ return await operation();
357
+ } catch (error) {
358
+ if (error instanceof IngestionError &&
359
+ error.code === IngestionErrorCode.RATE_LIMIT_ERROR) {
360
+
361
+ // Get retry-after value from error context
362
+ const retryAfter = error.context?.retryAfter || 60;
363
+
364
+ if (attempt < maxRetries - 1) {
365
+ logger.warn(`Rate limited - waiting ${retryAfter} seconds`, {
366
+ attempt,
367
+ maxRetries
368
+ });
369
+
370
+ // Wait for rate limit to reset
371
+ await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
372
+ continue;
373
+ }
374
+ }
375
+
376
+ // Non-rate-limit error or max retries exceeded
377
+ throw error;
378
+ }
379
+ }
380
+
381
+ throw new Error('Max retries exceeded');
382
+ }
383
+ ```
384
+
385
+ ### Rate Limit with Exponential Backoff Fallback
386
+
387
+ If no `retryAfter` provided, use exponential backoff:
388
+
389
+ ```typescript
390
+ async function handleRateLimitSmart(
391
+ operation: () => Promise<any>,
392
+ maxRetries: number = 5
393
+ ): Promise<any> {
394
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
395
+ try {
396
+ return await operation();
397
+ } catch (error) {
398
+ if (error instanceof IngestionError &&
399
+ error.code === IngestionErrorCode.RATE_LIMIT_ERROR) {
400
+
401
+ if (attempt >= maxRetries - 1) {
402
+ throw error; // Max retries exceeded
403
+ }
404
+
405
+ // Use retryAfter if provided, else exponential backoff
406
+ const retryAfter = error.context?.retryAfter;
407
+ let delay: number;
408
+
409
+ if (retryAfter) {
410
+ delay = retryAfter * 1000; // Convert to ms
411
+ logger.info(`Rate limited - retry after ${retryAfter}s`);
412
+ } else {
413
+ delay = Math.pow(2, attempt) * 1000; // Exponential backoff
414
+ logger.info(`Rate limited - retry after ${delay}ms (no retryAfter)`);
415
+ }
416
+
417
+ await new Promise(resolve => setTimeout(resolve, delay));
418
+ continue;
419
+ }
420
+
421
+ // Not a rate limit error
422
+ throw error;
423
+ }
424
+ }
425
+
426
+ throw new Error('Max retries exceeded');
427
+ }
428
+ ```
429
+
430
+ ## Timeout Handling
431
+
432
+ ### Network Timeouts
433
+
434
+ Handle network timeouts with retry:
435
+
436
+ ```typescript
437
+ async function executeWithTimeout<T>(
438
+ operation: () => Promise<T>,
439
+ timeoutMs: number = 30000,
440
+ maxRetries: number = 3
441
+ ): Promise<T> {
442
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
443
+ try {
444
+ // Race between operation and timeout
445
+ const result = await Promise.race([
446
+ operation(),
447
+ new Promise<never>((_, reject) =>
448
+ setTimeout(() => reject(new Error('Operation timeout')), timeoutMs)
449
+ )
450
+ ]);
451
+
452
+ return result;
453
+
454
+ } catch (error) {
455
+ if (error.message === 'Operation timeout') {
456
+ if (attempt < maxRetries - 1) {
457
+ const delay = Math.pow(2, attempt) * 1000;
458
+ logger.warn(`Timeout - retrying in ${delay}ms`, { attempt });
459
+ await new Promise(resolve => setTimeout(resolve, delay));
460
+ continue;
461
+ }
462
+ }
463
+
464
+ // Not a timeout or max retries exceeded
465
+ throw error;
466
+ }
467
+ }
468
+
469
+ throw new Error('Max retries exceeded');
470
+ }
471
+ ```
472
+
473
+ ## SDK Built-In Retry Logic
474
+
475
+ ### S3DataSource Retry
476
+
477
+ The SDK's S3DataSource uses the shared `RetryHelper` utility for consistent retry behavior:
478
+
479
+ ```typescript
480
+ import { S3DataSource } from '@fluentcommerce/fc-connect-sdk';
481
+
482
+ const s3Config = {
483
+ type: 'S3_CSV' as const,
484
+ s3Config: {
485
+ bucket: 'my-bucket',
486
+ region: 'us-east-1',
487
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID,
488
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
489
+ maxAttempts: 3 // Default: 3 retries
490
+ }
491
+ };
492
+
493
+ const s3 = new S3DataSource(s3Config, logger);
494
+
495
+ // Automatic retry on:
496
+ // - HTTP 429 (rate limit)
497
+ // - HTTP 500, 502, 503, 504 (server errors)
498
+ // - Network errors (ECONNRESET, ETIMEDOUT, etc.)
499
+ const files = await s3.listFiles(); // Retries automatically
500
+ const content = await s3.downloadFile(key); // Retries automatically
501
+ ```
502
+
503
+ **Retry Configuration:**
504
+ - **Max attempts**: Configurable via `maxAttempts` (default: 3)
505
+ - **Base delay**: 1000ms
506
+ - **Backoff factor**: 2 (exponential)
507
+ - **Max delay**: 8000ms (8 seconds)
508
+ - **Jitter**: Full jitter (AWS recommended)
509
+ - **Retryable status codes**: 429, 500, 502, 503, 504
510
+ - **Retryable network errors**: Connection resets, timeouts, unreachable hosts
511
+
512
+ **Implementation Note:** S3DataSource uses `RetryHelper.executeWithRetry()` from `src/utils/retry-helper.ts` for consistency across all SDK HTTP operations.
513
+
514
+ **SFTP Note:** SftpDataSource uses custom retry logic due to connection pooling requirements. See lines 536-645 for SFTP-specific retry patterns.
515
+
516
+ ### SftpDataSource Retry
517
+
518
+ The SDK's SftpDataSource includes configurable retry logic with connection pooling:
519
+
520
+ ```typescript
521
+ import { SftpDataSource, SftpDataSourceConfig } from '@fluentcommerce/fc-connect-sdk';
522
+
523
+ const sftpConfig: SftpDataSourceConfig = {
524
+ type: 'SFTP_CSV',
525
+ settings: {
526
+ host: 'sftp.example.com',
527
+ port: 22,
528
+ username: 'user',
529
+ password: 'pass',
530
+ remotePath: '/data',
531
+ filePattern: '*.csv',
532
+
533
+ // Enhanced retry configuration
534
+ retry: {
535
+ maxAttempts: 3, // Default: 3
536
+ baseDelayMs: 1000, // Default: 1000ms (1 second)
537
+ backoffFactor: 2, // Default: 2 (exponential)
538
+ maxDelayMs: 8000, // Default: 8000ms (8 seconds)
539
+ jitter: 'full', // Default: 'full' jitter
540
+ reconnectOnFailure: true, // Default: true - reconnect on error
541
+ isRetryable: (error) => {
542
+ // Custom retry logic (optional)
543
+ return error.message.includes('timeout');
544
+ }
545
+ }
546
+ }
547
+ };
548
+
549
+ const sftp = new SftpDataSource(sftpConfig, logger);
550
+
551
+ // Automatic retry with exponential backoff and connection pooling
552
+ const files = await sftp.listFiles(); // Retries automatically!
553
+ const content = await sftp.downloadFile('file.csv'); // Retries automatically!
554
+ ```
555
+
556
+ **Retry behavior:**
557
+ - **Default strategy**: Exponential backoff with full jitter
558
+ - **Connection pooling**: Reuses connections, creates new ones when needed
559
+ - **Wait queue**: Queues requests when pool is full (prevents connection spam)
560
+ - **Reconnect on failure**: Automatically reconnects on transient errors
561
+
562
+ **Retryable errors:**
563
+ - Connection resets (`ECONNRESET`)
564
+ - Timeouts (`ETIMEDOUT`)
565
+ - Host unreachable (`EHOSTUNREACH`)
566
+ - Connection refused (`ECONNREFUSED`)
567
+ - Broken pipe (`EPIPE`)
568
+ - Message patterns: "connection lost", "timeout", "network error"
569
+
570
+ **Non-retryable errors:**
571
+ - Authentication failures
572
+ - Permission denied
573
+ - File not found
574
+ - Algorithm/cipher errors
575
+ - Key exchange failures
576
+
577
+ **Implementation details:**
578
+ ```typescript
579
+ private async executeWithRetry<T>(
580
+ operation: () => Promise<T>,
581
+ operationName: string,
582
+ retryOverride?: Partial<SftpRetryConfig>
583
+ ): Promise<T> {
584
+ const config = { ...this.retryConfig, ...retryOverride };
585
+ let lastError: Error;
586
+
587
+ for (let attempt = 1; attempt <= config.maxAttempts; attempt++) {
588
+ try {
589
+ return await operation();
590
+ } catch (error) {
591
+ lastError = error as Error;
592
+ const isRetryable = config.isRetryable(lastError);
593
+ const isLastAttempt = attempt === config.maxAttempts;
594
+
595
+ if (!isRetryable || isLastAttempt) {
596
+ throw error;
597
+ }
598
+
599
+ // Reconnect on failure if enabled
600
+ if (config.reconnectOnFailure) {
601
+ await this.closeConnection(connectionId);
602
+ }
603
+
604
+ // Calculate delay with jitter
605
+ const delay = this.calculateDelay(attempt);
606
+ await new Promise(resolve => setTimeout(resolve, delay));
607
+ }
608
+ }
609
+
610
+ throw lastError!;
611
+ }
612
+
613
+ private calculateDelay(attempt: number): number {
614
+ const exponentialDelay =
615
+ this.retryConfig.baseDelayMs * Math.pow(this.retryConfig.backoffFactor, attempt - 1);
616
+ const cappedDelay = Math.min(exponentialDelay, this.retryConfig.maxDelayMs);
617
+
618
+ if (this.retryConfig.jitter === 'none') {
619
+ return cappedDelay;
620
+ }
621
+
622
+ // Full jitter: random between 0 and cappedDelay
623
+ return Math.floor(Math.random() * cappedDelay);
624
+ }
625
+ ```
626
+
627
+ ### Connection Pooling (SFTP only)
628
+
629
+ ```typescript
630
+ // Connection pool configuration
631
+ const sftpConfig: SftpDataSourceConfig = {
632
+ type: 'SFTP_CSV',
633
+ settings: {
634
+ // ... other settings
635
+ maxConnections: 5 // Default: 5 concurrent connections
636
+ }
637
+ };
638
+
639
+ // Connection pooling features:
640
+ // - Reuses idle connections
641
+ // - Creates new connections up to maxConnections
642
+ // - Queues requests when pool is full
643
+ // - Automatically closes idle connections after timeout
644
+ // - Handles connection failures gracefully
645
+ ```
646
+
647
+ ### When to Use Built-In Retry vs Custom Retry
648
+
649
+ **Use SDK built-in retry when:**
650
+ - ✅ Working with S3DataSource or SftpDataSource
651
+ - ✅ Default retry behavior is acceptable
652
+ - ✅ You want zero configuration
653
+ - ✅ You need connection pooling (SFTP)
654
+
655
+ **Use custom retry when:**
656
+ - ✅ Working with FluentClient GraphQL operations
657
+ - ✅ Need custom retry logic beyond data source operations
658
+ - ✅ Need to coordinate retries across multiple services
659
+ - ✅ Want different retry strategies for different operations
660
+
661
+ **Example: Combining both**
662
+
663
+ ```typescript
664
+ // S3 operations use built-in retry
665
+ const files = await s3.listFiles(); // Automatic retry
666
+
667
+ // Custom retry for high-level workflow
668
+ await retryOperation(
669
+ async () => {
670
+ // Download (automatic retry)
671
+ const content = await s3.downloadFile(files[0].path);
672
+
673
+ // Parse
674
+ const parsed = await parser.parse(content);
675
+
676
+ // Map
677
+ const payload = await mapper.map(parsed);
678
+
679
+ // Submit to Fluent (custom retry)
680
+ return await client.graphql(payload);
681
+ },
682
+ {
683
+ maxRetries: 5,
684
+ initialDelay: 2000
685
+ }
686
+ );
687
+ ```
688
+
689
+ ## Partial Batch Recovery
690
+
691
+ ### PartialBatchRecovery Service
692
+
693
+ The SDK's `PartialBatchRecovery` service handles partial batch failures gracefully by retrying only failed records instead of the entire batch.
694
+
695
+ **When to use:**
696
+ - Large batch imports where some records may fail
697
+ - Network errors affecting subset of records
698
+ - Validation failures on specific records
699
+ - Want to minimize reprocessing overhead
700
+
701
+ ```typescript
702
+ import {
703
+ PartialBatchRecovery,
704
+ createClient,
705
+ createConsoleLogger,
706
+ toStructuredLogger
707
+ } from '@fluentcommerce/fc-connect-sdk';
708
+
709
+ // Standalone
710
+ const logger = toStructuredLogger(createConsoleLogger(), { service: 'BatchRecovery' });
711
+
712
+ // Versori Platform
713
+ const { log: logger } = ctx;
714
+
715
+ const recovery = new PartialBatchRecovery(logger);
716
+ const client = await createClient({ config });
717
+
718
+ // Process batch with automatic retry of failed records
719
+ const result = await recovery.processBatchWithRecovery(
720
+ inventoryRecords,
721
+ async (batch) => {
722
+ // Your batch processing logic
723
+ const job = await client.createJob({
724
+ name: 'Inventory Sync with Recovery',
725
+ retailerId: 'my-retailer'
726
+ });
727
+
728
+ const batchResult = await client.sendBatch(job.id, {
729
+ action: 'UPSERT',
730
+ entityType: 'INVENTORY',
731
+ entities: batch
732
+ });
733
+
734
+ return batchResult;
735
+ },
736
+ {
737
+ maxRetries: 3, // Retry each failed record up to 3 times
738
+ retryOnlyFailed: true, // Only retry failed records (default: true)
739
+ batchSize: 100, // Process in batches of 100
740
+ retryDelay: 1000, // Wait 1 second between retries
741
+ exponentialBackoff: true // Use exponential backoff (default: true)
742
+ }
743
+ );
744
+
745
+ // Results
746
+ console.log(`Total records: ${inventoryRecords.length}`);
747
+ console.log(`Successful: ${result.successCount}`);
748
+ console.log(`Failed: ${result.failedCount}`);
749
+
750
+ if (result.failedCount > 0) {
751
+ console.log('Failed records:');
752
+ result.failedRecords.forEach(({ record, error, attemptCount }) => {
753
+ console.error(`Record ${record.ref} failed after ${attemptCount} attempts:`, error.message);
754
+ });
755
+ }
756
+ ```
757
+
758
+ ### Recovery Result Structure
759
+
760
+ ```typescript
761
+ interface RecoveryResult {
762
+ successCount: number; // Total successful records
763
+ failedCount: number; // Total failed after all retries
764
+ failedRecords: Array<{
765
+ record: any; // The record that failed
766
+ error: Error; // Last error that occurred
767
+ attemptCount: number; // Total retry attempts for this record
768
+ }>;
769
+ }
770
+ ```
771
+
772
+ ### Advanced Recovery Options
773
+
774
+ ```typescript
775
+ const result = await recovery.processBatchWithRecovery(
776
+ records,
777
+ processFn,
778
+ {
779
+ maxRetries: 5,
780
+ retryOnlyFailed: true,
781
+ batchSize: 50,
782
+ retryDelay: 2000,
783
+ exponentialBackoff: true,
784
+
785
+ // Custom retry decision logic
786
+ shouldRetry: (error, attemptCount) => {
787
+ // Don't retry validation errors
788
+ if (error instanceof ValidationError) {
789
+ return false;
790
+ }
791
+ // Retry network/timeout errors
792
+ if (error instanceof IngestionError) {
793
+ return error.isRetryable() && attemptCount < 5;
794
+ }
795
+ return attemptCount < 3;
796
+ },
797
+
798
+ // Custom backoff calculation
799
+ calculateDelay: (attempt, baseDelay) => {
800
+ return Math.min(baseDelay * Math.pow(2, attempt), 30000);
801
+ },
802
+
803
+ // Progress callback
804
+ onProgress: (current, total) => {
805
+ logger.info(`Processing: ${current}/${total} records`);
806
+ },
807
+
808
+ // Retry callback
809
+ onRetry: (record, attempt, error) => {
810
+ logger.warn(`Retrying record ${record.ref}, attempt ${attempt}`, {
811
+ error: error.message
812
+ });
813
+ }
814
+ }
815
+ );
816
+ ```
817
+
818
+ ### Combining with Built-In Retry
819
+
820
+ PartialBatchRecovery works well with SDK's built-in retry logic:
821
+
822
+ ```typescript
823
+ // S3 operations have automatic retry
824
+ const files = await s3.listFiles(); // Automatic retry on network errors
825
+
826
+ // Parse files
827
+ const records = [];
828
+ for (const file of files) {
829
+ const content = await s3.downloadFile(file.path); // Automatic retry
830
+ const parsed = await parser.parse(content);
831
+ records.push(...parsed);
832
+ }
833
+
834
+ // Use PartialBatchRecovery for batch submission
835
+ const result = await recovery.processBatchWithRecovery(
836
+ records,
837
+ async (batch) => {
838
+ const job = await client.createJob({ name: 'Sync', retailerId });
839
+ return await client.sendBatch(job.id, {
840
+ action: 'UPSERT',
841
+ entityType: 'INVENTORY',
842
+ entities: batch
843
+ });
844
+ },
845
+ {
846
+ maxRetries: 3,
847
+ retryOnlyFailed: true
848
+ }
849
+ );
850
+ ```
851
+
852
+ ### Benefits of PartialBatchRecovery
853
+
854
+ **Efficiency:**
855
+ - ✅ Only retries failed records (not entire batch)
856
+ - ✅ Reduces API calls and processing time
857
+ - ✅ Lower resource consumption
858
+
859
+ **Reliability:**
860
+ - ✅ Tracks each record individually
861
+ - ✅ Configurable retry logic per record
862
+ - ✅ Detailed failure reporting
863
+
864
+ **Observability:**
865
+ - ✅ Know exactly which records failed
866
+ - ✅ Understand failure patterns
867
+ - ✅ Better error reporting for monitoring
868
+
869
+ **Example: 1000 records, 10 fail**
870
+ - **Without PartialBatchRecovery:** Retry all 1000 records
871
+ - **With PartialBatchRecovery:** Retry only 10 failed records
872
+
873
+ ## Production-Ready Retry Function
874
+
875
+ Complete retry implementation with all best practices:
876
+
877
+ ```typescript
878
+ import {
879
+ IngestionError,
880
+ IngestionErrorCode,
881
+ FileParsingError,
882
+ PathResolutionError,
883
+ MappingError,
884
+ FluentAPIError,
885
+ GraphQLExecutionError
886
+ } from '@fluentcommerce/fc-connect-sdk';
887
+
888
+ interface RetryOptions {
889
+ maxRetries?: number;
890
+ initialDelay?: number;
891
+ maxDelay?: number;
892
+ useJitter?: boolean;
893
+ onRetry?: (attempt: number, error: Error) => void;
894
+ }
895
+
896
+ async function retryOperation<T>(
897
+ operation: () => Promise<T>,
898
+ options: RetryOptions = {}
899
+ ): Promise<T> {
900
+ const {
901
+ maxRetries = 3,
902
+ initialDelay = 1000,
903
+ maxDelay = 60000,
904
+ useJitter = true,
905
+ onRetry
906
+ } = options;
907
+
908
+ let lastError: Error;
909
+
910
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
911
+ try {
912
+ return await operation();
913
+
914
+ } catch (error) {
915
+ lastError = error;
916
+
917
+ // 1. Don't retry permanent errors
918
+ if (error instanceof FileParsingError ||
919
+ error instanceof PathResolutionError ||
920
+ error instanceof MappingError) {
921
+ throw error;
922
+ }
923
+
924
+ // 2. Check FluentAPIError (HTTP 5xx / 429)
925
+ let isRetryable = false;
926
+ let retryDelay = 0;
927
+
928
+ if (error instanceof FluentAPIError) {
929
+ if (error.statusCode >= 500 || error.statusCode === 429) {
930
+ isRetryable = true;
931
+ } else {
932
+ throw error; // 4xx are permanent
933
+ }
934
+ }
935
+
936
+ // 3. Check IngestionError
937
+ else if (error instanceof IngestionError) {
938
+ if (error.isRetryable()) {
939
+ isRetryable = true;
940
+ // Special handling for rate limit error context
941
+ if (error.code === IngestionErrorCode.RATE_LIMIT_ERROR) {
942
+ retryDelay = (error.context?.retryAfter || 60) * 1000;
943
+ }
944
+ } else {
945
+ throw error;
946
+ }
947
+ }
948
+
949
+ // 4. Check GraphQLExecutionError
950
+ else if (error instanceof GraphQLExecutionError) {
951
+ const firstError = error.graphqlErrors[0];
952
+ if (firstError?.extensions?.code === 'TIMEOUT' ||
953
+ firstError?.extensions?.code === 'NETWORK_ERROR') {
954
+ isRetryable = true;
955
+ } else {
956
+ throw error; // Validation errors are permanent
957
+ }
958
+ }
959
+
960
+ // Last attempt - throw
961
+ if (attempt >= maxRetries - 1) {
962
+ throw error;
963
+ }
964
+
965
+ // Calculate delay if not already set
966
+ if (!retryDelay) {
967
+ let delay = initialDelay * Math.pow(2, attempt);
968
+ if (useJitter) {
969
+ delay = Math.random() * delay;
970
+ }
971
+ retryDelay = Math.min(delay, maxDelay);
972
+ }
973
+
974
+ // Callback
975
+ if (onRetry) {
976
+ onRetry(attempt + 1, error);
977
+ }
978
+
979
+ await new Promise(resolve => setTimeout(resolve, retryDelay));
980
+ }
981
+ }
982
+
983
+ throw lastError!;
984
+ }
985
+
986
+ // Usage
987
+ const result = await retryOperation(
988
+ () => processOrder(xmlContent),
989
+ {
990
+ maxRetries: 5,
991
+ initialDelay: 2000,
992
+ maxDelay: 60000,
993
+ useJitter: true,
994
+ onRetry: (attempt, error) => {
995
+ logger.warn('Retrying operation', { attempt, error: error.message });
996
+ }
997
+ }
998
+ );
999
+ ```
1000
+
1001
+ ## Key Takeaways
1002
+
1003
+ - 🎯 **SDK built-in retry** - S3DataSource and SftpDataSource have automatic retry
1004
+ - 🎯 **Use isRetryable()** - Let SDK determine if error is transient
1005
+ - 🎯 **Exponential backoff** - Increases delay between retries (with jitter)
1006
+ - 🎯 **Connection pooling** - SftpDataSource reuses connections efficiently
1007
+ - 🎯 **Add jitter** - Prevents thundering herd problem (full jitter recommended)
1008
+ - 🎯 **Respect rate limits** - Use `retryAfter` when provided
1009
+ - 🎯 **Set max retries** - Prevent infinite retry loops (default: 3)
1010
+ - 🎯 **Don't retry permanent errors** - Parse, mapping, validation errors won't succeed
1011
+
1012
+ ## Practice Exercise
1013
+
1014
+ Implement a retry function that:
1015
+ 1. Retries only transient errors
1016
+ 2. Uses exponential backoff with jitter
1017
+ 3. Respects rate limit headers
1018
+ 4. Has a maximum of 5 retries
1019
+
1020
+ <details>
1021
+ <summary>Click to see solution</summary>
1022
+
1023
+ See the "Production-Ready Retry Function" section above for a complete implementation.
1024
+
1025
+ </details>
1026
+
1027
+ ## Next Steps
1028
+
1029
+ Continue to [Module 7: Monitoring →](./error-handling-07-monitoring.md) to learn how to effectively log and monitor errors.
1030
+
1031
+ ---
1032
+
1033
+ **Previous:** [← Module 5: Calling Patterns](./error-handling-05-calling-patterns.md)
1034
+ **Next:** [Module 7: Monitoring →](./error-handling-07-monitoring.md)