@fluentcommerce/fc-connect-sdk 0.1.54 → 0.1.56

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 (476) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +11 -0
  3. package/dist/cjs/clients/fluent-client.js +13 -6
  4. package/dist/cjs/utils/pagination-helpers.js +38 -2
  5. package/dist/cjs/versori/fluent-versori-client.js +11 -5
  6. package/dist/esm/clients/fluent-client.js +13 -6
  7. package/dist/esm/utils/pagination-helpers.js +38 -2
  8. package/dist/esm/versori/fluent-versori-client.js +11 -5
  9. package/dist/tsconfig.esm.tsbuildinfo +1 -1
  10. package/dist/tsconfig.tsbuildinfo +1 -1
  11. package/dist/tsconfig.types.tsbuildinfo +1 -1
  12. package/docs/00-START-HERE/EXPORT-VALIDATION.md +158 -158
  13. package/docs/00-START-HERE/cli-analyze-source-structure-guide.md +655 -655
  14. package/docs/00-START-HERE/cli-documentation-index.md +202 -202
  15. package/docs/00-START-HERE/cli-quick-reference.md +252 -252
  16. package/docs/00-START-HERE/decision-tree.md +552 -552
  17. package/docs/00-START-HERE/getting-started.md +1070 -1070
  18. package/docs/00-START-HERE/mapper-quick-decision-guide.md +235 -235
  19. package/docs/00-START-HERE/readme.md +237 -237
  20. package/docs/00-START-HERE/retailerid-configuration.md +404 -404
  21. package/docs/00-START-HERE/sdk-philosophy.md +794 -794
  22. package/docs/00-START-HERE/troubleshooting-quick-reference.md +1086 -1086
  23. package/docs/01-TEMPLATES/faq.md +686 -686
  24. package/docs/01-TEMPLATES/patterns/pattern-templates-guide.md +68 -68
  25. package/docs/01-TEMPLATES/patterns/patterns-csv-schema-validation-and-rejection-report.md +233 -233
  26. package/docs/01-TEMPLATES/patterns/patterns-custom-resolvers.md +407 -407
  27. package/docs/01-TEMPLATES/patterns/patterns-error-handling-retry.md +511 -511
  28. package/docs/01-TEMPLATES/patterns/patterns-field-mapping-universal.md +701 -701
  29. package/docs/01-TEMPLATES/patterns/patterns-large-file-splitting.md +1430 -1430
  30. package/docs/01-TEMPLATES/patterns/patterns-master-data-etl.md +2399 -2399
  31. package/docs/01-TEMPLATES/patterns/patterns-pagination-streaming.md +447 -447
  32. package/docs/01-TEMPLATES/patterns/patterns-state-duplicate-prevention.md +385 -385
  33. package/docs/01-TEMPLATES/readme.md +957 -957
  34. package/docs/01-TEMPLATES/standalone/standalone-asn-inbound-processing.md +1209 -1209
  35. package/docs/01-TEMPLATES/standalone/standalone-graphql-query-export.md +1140 -1140
  36. package/docs/01-TEMPLATES/standalone/standalone-graphql-to-parquet-partitioned-s3.md +432 -432
  37. package/docs/01-TEMPLATES/standalone/standalone-multi-channel-inventory-sync.md +1185 -1185
  38. package/docs/01-TEMPLATES/standalone/standalone-multi-source-aggregation.md +1462 -1462
  39. package/docs/01-TEMPLATES/standalone/standalone-s3-csv-batch-api.md +1390 -1390
  40. package/docs/01-TEMPLATES/standalone/standalone-s3-csv-inventory-to-batch.md +330 -330
  41. package/docs/01-TEMPLATES/standalone/standalone-scripts-guide.md +87 -87
  42. package/docs/01-TEMPLATES/standalone/standalone-sftp-xml-graphql.md +1444 -1444
  43. package/docs/01-TEMPLATES/standalone/standalone-webhook-payload-processing.md +688 -688
  44. package/docs/01-TEMPLATES/versori/business-examples/business-examples-dropship-order-routing.md +193 -193
  45. package/docs/01-TEMPLATES/versori/business-examples/business-examples-graphql-parquet-extraction.md +518 -518
  46. package/docs/01-TEMPLATES/versori/business-examples/business-examples-inter-location-transfers.md +2162 -2162
  47. package/docs/01-TEMPLATES/versori/business-examples/business-examples-pre-order-allocation.md +2226 -2226
  48. package/docs/01-TEMPLATES/versori/business-examples/business-scenarios-guide.md +87 -87
  49. package/docs/01-TEMPLATES/versori/patterns/versori-patterns-connection-validation-pattern.md +656 -656
  50. package/docs/01-TEMPLATES/versori/patterns/versori-patterns-dual-workflow-connector.md +835 -835
  51. package/docs/01-TEMPLATES/versori/patterns/versori-patterns-guide.md +108 -108
  52. package/docs/01-TEMPLATES/versori/patterns/versori-patterns-kv-state-management.md +1533 -1533
  53. package/docs/01-TEMPLATES/versori/patterns/versori-patterns-xml-response-patterns.md +1160 -1160
  54. package/docs/01-TEMPLATES/versori/versori-platform-guide.md +201 -201
  55. package/docs/01-TEMPLATES/versori/webhooks/template-webhook-asn-purchase-order.md +1906 -1906
  56. package/docs/01-TEMPLATES/versori/webhooks/template-webhook-dropship-routing.md +1074 -1074
  57. package/docs/01-TEMPLATES/versori/webhooks/template-webhook-flash-sale-reserve.md +1395 -1395
  58. package/docs/01-TEMPLATES/versori/webhooks/template-webhook-generic-xml-order.md +888 -888
  59. package/docs/01-TEMPLATES/versori/webhooks/template-webhook-payment-gateway-integration.md +2478 -2478
  60. package/docs/01-TEMPLATES/versori/webhooks/template-webhook-rma-returns-comprehensive.md +2240 -2240
  61. package/docs/01-TEMPLATES/versori/webhooks/template-webhook-xml-order-ingestion.md +2029 -2029
  62. package/docs/01-TEMPLATES/versori/webhooks/webhook-templates-guide.md +140 -140
  63. package/docs/01-TEMPLATES/versori/workflows/_examples/sample-data/inventory-mapping.json +20 -20
  64. package/docs/01-TEMPLATES/versori/workflows/_examples/sample-data/products_2025-01-22.csv +11 -11
  65. package/docs/01-TEMPLATES/versori/workflows/_examples/sample-data/sample-data-guide.md +34 -34
  66. package/docs/01-TEMPLATES/versori/workflows/_examples/workflow-examples-guide.md +36 -36
  67. package/docs/01-TEMPLATES/versori/workflows/extraction/extraction-modes-guide.md +1038 -1038
  68. package/docs/01-TEMPLATES/versori/workflows/extraction/extraction-workflows-guide.md +138 -138
  69. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/graphql-extraction-guide.md +63 -63
  70. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-fulfillments-to-sftp-csv.md +2062 -2062
  71. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-fulfillments-to-sftp-xml.md +2294 -2294
  72. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-positions-to-s3-csv.md +2461 -2461
  73. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-positions-to-sftp-xml.md +2529 -2529
  74. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-quantities-to-s3-csv.md +2464 -2464
  75. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-quantities-to-s3-json.md +1959 -1959
  76. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-orders-to-s3-csv.md +1953 -1953
  77. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-orders-to-sftp-xml.md +2541 -2541
  78. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-products-to-s3-json.md +2384 -2384
  79. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-products-to-sftp-xml.md +2445 -2445
  80. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-virtual-positions-to-s3-csv.md +2355 -2355
  81. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-virtual-positions-to-s3-json.md +2042 -2042
  82. package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-virtual-positions-to-sftp-xml.md +2726 -2726
  83. package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/batch-api-guide.md +206 -206
  84. package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-cycle-count-reconciliation.md +2030 -2030
  85. package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-multi-channel-inventory-sync.md +1882 -1882
  86. package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-s3-csv-inventory-batch.md +2827 -2827
  87. package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-s3-json-inventory-batch.md +1952 -1952
  88. package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-s3-xml-inventory-batch.md +3289 -3289
  89. package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-sftp-csv-inventory-batch.md +3064 -3064
  90. package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-sftp-json-inventory-batch.md +3238 -3238
  91. package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-sftp-xml-inventory-batch.md +2977 -2977
  92. package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/event-api-guide.md +321 -321
  93. package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-payload-json-order-cancel-event.md +959 -959
  94. package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-payload-xml-order-cancel-event.md +1170 -1170
  95. package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-csv-product-event.md +2312 -2312
  96. package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-json-product-event.md +2999 -2999
  97. package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-parquet-product-event.md +2836 -2836
  98. package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-xml-product-event.md +2395 -2395
  99. package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-csv-product-event.md +2295 -2295
  100. package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-json-product-event.md +2602 -2602
  101. package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-parquet-product-event.md +2589 -2589
  102. package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-xml-product-event.md +3578 -3578
  103. package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/graphql-mutations-guide.md +93 -93
  104. package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-payload-json-order-update-graphql.md +1260 -1260
  105. package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-payload-xml-order-update-graphql.md +1472 -1472
  106. package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-csv-control-graphql.md +2417 -2417
  107. package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-csv-location-graphql.md +2811 -2811
  108. package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-csv-price-graphql.md +2619 -2619
  109. package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-json-location-graphql.md +2807 -2807
  110. package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-xml-location-graphql.md +2373 -2373
  111. package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-sftp-csv-control-graphql.md +2740 -2740
  112. package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-sftp-csv-location-graphql.md +2760 -2760
  113. package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-sftp-json-location-graphql.md +1710 -1710
  114. package/docs/01-TEMPLATES/versori/workflows/ingestion/ingestion-workflows-guide.md +136 -136
  115. package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/rubix-webhooks-guide.md +520 -520
  116. package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/template-webhook-rubix-fulfilment-to-sftp-xml-inline.md +1418 -1418
  117. package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/template-webhook-rubix-fulfilment-to-sftp-xml-universal-mapper.md +1785 -1785
  118. package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/template-webhook-rubix-order-attribute-update.md +824 -824
  119. package/docs/01-TEMPLATES/versori/workflows/workflows-overview-guide.md +646 -646
  120. package/docs/02-CORE-GUIDES/advanced-services/advanced-services-batch-archival.md +724 -724
  121. package/docs/02-CORE-GUIDES/advanced-services/advanced-services-job-tracker.md +627 -627
  122. package/docs/02-CORE-GUIDES/advanced-services/advanced-services-partial-batch-recovery.md +561 -561
  123. package/docs/02-CORE-GUIDES/advanced-services/advanced-services-quick-reference.md +367 -367
  124. package/docs/02-CORE-GUIDES/advanced-services/advanced-services-readme.md +407 -407
  125. package/docs/02-CORE-GUIDES/advanced-services/readme.md +49 -49
  126. package/docs/02-CORE-GUIDES/api-reference/api-reference-quick-reference.md +548 -548
  127. package/docs/02-CORE-GUIDES/api-reference/event-api-input-output-reference.md +702 -1171
  128. package/docs/02-CORE-GUIDES/api-reference/examples/client-initialization.ts +286 -286
  129. package/docs/02-CORE-GUIDES/api-reference/graphql-error-classification.md +337 -337
  130. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-01-client-api.md +399 -520
  131. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-03-authentication.md +199 -199
  132. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-04-graphql-mapping.md +925 -925
  133. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-05-services.md +1198 -1198
  134. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-06-data-sources.md +1083 -1083
  135. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-07-parsers.md +1097 -1097
  136. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-08-pagination.md +513 -513
  137. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-08-types.md +545 -597
  138. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-09-error-handling.md +527 -527
  139. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-09-webhook-validation.md +514 -514
  140. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-10-extraction.md +557 -557
  141. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-10-utilities.md +412 -412
  142. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-11-cli-tools.md +423 -423
  143. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-11-error-handling.md +716 -716
  144. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-analyze-source-structure.md +518 -518
  145. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-partial-responses.md +212 -212
  146. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-testing.md +300 -300
  147. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-13-resolver-builder.md +322 -322
  148. package/docs/02-CORE-GUIDES/api-reference/readme.md +279 -279
  149. package/docs/02-CORE-GUIDES/auto-pagination/auto-pagination-quick-reference.md +351 -351
  150. package/docs/02-CORE-GUIDES/auto-pagination/auto-pagination-readme.md +277 -277
  151. package/docs/02-CORE-GUIDES/auto-pagination/examples/auto-pagination-readme.md +178 -178
  152. package/docs/02-CORE-GUIDES/auto-pagination/examples/common-patterns.ts +351 -351
  153. package/docs/02-CORE-GUIDES/auto-pagination/examples/paginate-products.ts +384 -384
  154. package/docs/02-CORE-GUIDES/auto-pagination/examples/paginate-virtual-positions.ts +308 -308
  155. package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-01-foundations.md +470 -470
  156. package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-02-quick-start.md +713 -713
  157. package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-03-configuration.md +754 -754
  158. package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-04-advanced-patterns.md +732 -732
  159. package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-05-sdk-integration.md +847 -847
  160. package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-06-troubleshooting.md +359 -359
  161. package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-07-api-reference.md +462 -462
  162. package/docs/02-CORE-GUIDES/auto-pagination/readme.md +54 -54
  163. package/docs/02-CORE-GUIDES/data-sources/data-sources-file-operations-error-handling.md +1487 -1487
  164. package/docs/02-CORE-GUIDES/data-sources/data-sources-quick-reference.md +836 -836
  165. package/docs/02-CORE-GUIDES/data-sources/data-sources-readme.md +276 -276
  166. package/docs/02-CORE-GUIDES/data-sources/data-sources-sftp-credential-access-security.md +553 -553
  167. package/docs/02-CORE-GUIDES/data-sources/examples/common-patterns.ts +409 -409
  168. package/docs/02-CORE-GUIDES/data-sources/examples/data-sources-readme.md +178 -178
  169. package/docs/02-CORE-GUIDES/data-sources/examples/s3-operations.ts +308 -308
  170. package/docs/02-CORE-GUIDES/data-sources/examples/sftp-operations.ts +371 -371
  171. package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-01-foundations.md +735 -735
  172. package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-02-s3-operations.md +1302 -1302
  173. package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-03-sftp-operations.md +1379 -1379
  174. package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-04-file-patterns.md +941 -941
  175. package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-05-advanced-topics.md +813 -813
  176. package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-06-integration-patterns.md +486 -486
  177. package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-07-troubleshooting.md +387 -387
  178. package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-08-api-reference.md +417 -417
  179. package/docs/02-CORE-GUIDES/data-sources/readme.md +77 -77
  180. package/docs/02-CORE-GUIDES/error-handling-guide.md +936 -936
  181. package/docs/02-CORE-GUIDES/extraction/examples/02-core-guides-extraction-readme.md +116 -116
  182. package/docs/02-CORE-GUIDES/extraction/examples/common-patterns.ts +428 -428
  183. package/docs/02-CORE-GUIDES/extraction/examples/extract-inventory-basic.ts +187 -187
  184. package/docs/02-CORE-GUIDES/extraction/extraction-quick-reference.md +596 -596
  185. package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-01-foundations.md +514 -514
  186. package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-02-basic-extraction.md +823 -823
  187. package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-03-parquet-processing.md +507 -507
  188. package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-04-data-enrichment.md +546 -546
  189. package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-05-transformation.md +494 -494
  190. package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-06-export-formats.md +458 -458
  191. package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-06-performance.md +138 -138
  192. package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-07-api-reference.md +148 -148
  193. package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-07-optimization.md +692 -692
  194. package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-08-extraction-orchestrator.md +1008 -1008
  195. package/docs/02-CORE-GUIDES/extraction/readme.md +151 -151
  196. package/docs/02-CORE-GUIDES/ingestion/examples/_simple-kv-store.ts +40 -40
  197. package/docs/02-CORE-GUIDES/ingestion/examples/error-recovery.ts +728 -728
  198. package/docs/02-CORE-GUIDES/ingestion/examples/event-driven.ts +501 -501
  199. package/docs/02-CORE-GUIDES/ingestion/examples/local-file-ingestion.ts +88 -88
  200. package/docs/02-CORE-GUIDES/ingestion/examples/parquet-ingestion.ts +117 -117
  201. package/docs/02-CORE-GUIDES/ingestion/examples/performance-optimized.ts +647 -647
  202. package/docs/02-CORE-GUIDES/ingestion/examples/s3-csv-ingestion.ts +169 -169
  203. package/docs/02-CORE-GUIDES/ingestion/examples/sftp-csv-ingestion.ts +134 -134
  204. package/docs/02-CORE-GUIDES/ingestion/ingestion-quick-reference.md +546 -546
  205. package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-01-introduction.md +626 -626
  206. package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-02-quick-start.md +658 -658
  207. package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-03-data-sources.md +1052 -1052
  208. package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-04-field-mapping.md +763 -763
  209. package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-05-advanced-parsers.md +676 -676
  210. package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-06-batch-api.md +1295 -1295
  211. package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-07-api-reference.md +138 -138
  212. package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-07-state-management.md +1037 -1037
  213. package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-08-performance-optimization.md +1349 -1349
  214. package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-09-best-practices.md +1893 -1893
  215. package/docs/02-CORE-GUIDES/ingestion/readme.md +160 -160
  216. package/docs/02-CORE-GUIDES/logging-guide.md +585 -585
  217. package/docs/02-CORE-GUIDES/mapping/error-handling-patterns.md +401 -401
  218. package/docs/02-CORE-GUIDES/mapping/examples/02-core-guides-mapping-readme.md +128 -128
  219. package/docs/02-CORE-GUIDES/mapping/examples/common-patterns.ts +273 -273
  220. package/docs/02-CORE-GUIDES/mapping/examples/csv-location-ingestion.json +36 -36
  221. package/docs/02-CORE-GUIDES/mapping/examples/csv-mapping.ts +242 -242
  222. package/docs/02-CORE-GUIDES/mapping/examples/graphql-to-parquet-extraction.json +36 -36
  223. package/docs/02-CORE-GUIDES/mapping/examples/json-mapping.ts +213 -213
  224. package/docs/02-CORE-GUIDES/mapping/examples/json-product-to-mutation.json +48 -48
  225. package/docs/02-CORE-GUIDES/mapping/examples/xml-mapping.ts +291 -291
  226. package/docs/02-CORE-GUIDES/mapping/examples/xml-order-to-mutation.json +45 -45
  227. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/graphql-mutation-mapping-quick-reference.md +463 -463
  228. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/graphql-mutation-mapping-readme.md +227 -227
  229. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-01-introduction.md +222 -222
  230. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-02-quick-start.md +351 -351
  231. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-03-schema-validation.md +569 -569
  232. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-04-mapping-patterns.md +471 -471
  233. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-05-configuration-reference.md +611 -611
  234. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-06-advanced-xpath.md +148 -148
  235. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-06-path-syntax.md +464 -464
  236. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-07-api-reference.md +94 -94
  237. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-07-array-handling.md +307 -307
  238. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-08-custom-resolvers.md +544 -544
  239. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-09-advanced-patterns.md +427 -427
  240. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-10-hooks-and-variables.md +336 -336
  241. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-11-error-handling.md +488 -488
  242. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-12-arguments-vs-nodes.md +383 -383
  243. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-13-best-practices.md +477 -477
  244. package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/readme.md +62 -62
  245. package/docs/02-CORE-GUIDES/mapping/mapping-format-decision-tree.md +480 -480
  246. package/docs/02-CORE-GUIDES/mapping/mapping-graphql-alias-batching-guide.md +820 -820
  247. package/docs/02-CORE-GUIDES/mapping/mapping-javascript-objects.md +2369 -2369
  248. package/docs/02-CORE-GUIDES/mapping/mapping-mapper-comparison-guide.md +682 -682
  249. package/docs/02-CORE-GUIDES/mapping/modules/02-core-guides-mapping-07-api-reference.md +1327 -1327
  250. package/docs/02-CORE-GUIDES/mapping/modules/02-core-guides-mapping-08-error-handling.md +1142 -1142
  251. package/docs/02-CORE-GUIDES/mapping/modules/mapping-04-use-cases.md +891 -891
  252. package/docs/02-CORE-GUIDES/mapping/modules/mapping-06-helpers-resolvers.md +1126 -1126
  253. package/docs/02-CORE-GUIDES/mapping/modules/mapping-06-sdk-resolvers.md +199 -199
  254. package/docs/02-CORE-GUIDES/mapping/modules/mapping-07-api-reference.md +1319 -1319
  255. package/docs/02-CORE-GUIDES/mapping/readme.md +178 -178
  256. package/docs/02-CORE-GUIDES/mapping/resolver-registration.md +410 -410
  257. package/docs/02-CORE-GUIDES/mapping/resolvers/examples/common-patterns.ts +226 -226
  258. package/docs/02-CORE-GUIDES/mapping/resolvers/examples/custom-resolvers.ts +227 -227
  259. package/docs/02-CORE-GUIDES/mapping/resolvers/examples/sdk-resolvers-usage.ts +203 -203
  260. package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-readme.md +274 -274
  261. package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-api-reference.md +679 -679
  262. package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-cookbook.md +826 -826
  263. package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-guide.md +1330 -1330
  264. package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-helpers-reference.md +1437 -1437
  265. package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-parameters-reference.md +553 -553
  266. package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-troubleshooting.md +854 -854
  267. package/docs/02-CORE-GUIDES/mapping/resolvers/readme.md +75 -75
  268. package/docs/02-CORE-GUIDES/parsers/examples/02-core-guides-parsers-readme.md +161 -161
  269. package/docs/02-CORE-GUIDES/parsers/examples/csv-parser-examples.ts +110 -110
  270. package/docs/02-CORE-GUIDES/parsers/examples/json-parser-examples.ts +33 -33
  271. package/docs/02-CORE-GUIDES/parsers/examples/parquet-parser-examples.ts +47 -47
  272. package/docs/02-CORE-GUIDES/parsers/examples/xml-parser-examples.ts +38 -38
  273. package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-01-foundations.md +355 -355
  274. package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-02-csv-parser.md +772 -772
  275. package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-03-json-parser.md +789 -789
  276. package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-04-xml-parser.md +857 -857
  277. package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-05-parquet-parser.md +603 -603
  278. package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-06-integration-patterns.md +702 -702
  279. package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-06-streaming.md +121 -121
  280. package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-07-api-reference.md +89 -89
  281. package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-07-troubleshooting.md +727 -727
  282. package/docs/02-CORE-GUIDES/parsers/parsers-quick-reference.md +482 -482
  283. package/docs/02-CORE-GUIDES/parsers/parsers-readme.md +258 -258
  284. package/docs/02-CORE-GUIDES/parsers/readme.md +65 -65
  285. package/docs/02-CORE-GUIDES/readme.md +194 -194
  286. package/docs/02-CORE-GUIDES/webhook-validation/examples/basic-validation.ts +108 -108
  287. package/docs/02-CORE-GUIDES/webhook-validation/examples/common-patterns.ts +316 -316
  288. package/docs/02-CORE-GUIDES/webhook-validation/examples/webhook-validation-readme.md +61 -61
  289. package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-01-foundations.md +440 -440
  290. package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-02-quick-start.md +525 -525
  291. package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-03-versori-integration.md +741 -741
  292. package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-04-platform-integration.md +629 -629
  293. package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-05-configuration.md +535 -535
  294. package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-06-error-handling.md +611 -611
  295. package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-06-troubleshooting.md +124 -124
  296. package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-07-api-reference.md +511 -511
  297. package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-08-rubix-webhooks.md +590 -590
  298. package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-09-rubix-event-vs-http-call.md +432 -432
  299. package/docs/02-CORE-GUIDES/webhook-validation/readme.md +239 -239
  300. package/docs/02-CORE-GUIDES/webhook-validation/webhook-validation-quick-reference.md +392 -392
  301. package/docs/03-PATTERN-GUIDES/connector-scenarios/connector-scenarios-quick-reference.md +498 -498
  302. package/docs/03-PATTERN-GUIDES/connector-scenarios/connector-scenarios-readme.md +313 -313
  303. package/docs/03-PATTERN-GUIDES/connector-scenarios/examples/common-patterns.ts +612 -612
  304. package/docs/03-PATTERN-GUIDES/connector-scenarios/examples/connector-scenarios-readme.md +253 -253
  305. package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-01-foundations.md +452 -452
  306. package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-02-simple-scenarios.md +681 -681
  307. package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-03-intermediate-scenarios.md +637 -637
  308. package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-04-advanced-scenarios.md +650 -650
  309. package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-05-bidirectional-sync.md +233 -233
  310. package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-06-production-patterns.md +442 -442
  311. package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-07-reference.md +445 -445
  312. package/docs/03-PATTERN-GUIDES/connector-scenarios/readme.md +31 -31
  313. package/docs/03-PATTERN-GUIDES/enterprise-integration-patterns.md +1528 -1528
  314. package/docs/03-PATTERN-GUIDES/error-handling/comprehensive-error-handling-guide.md +1437 -1437
  315. package/docs/03-PATTERN-GUIDES/error-handling/error-handling-quick-reference.md +390 -390
  316. package/docs/03-PATTERN-GUIDES/error-handling/examples/common-patterns.ts +438 -438
  317. package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-01-foundations.md +362 -362
  318. package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-02-error-types.md +850 -850
  319. package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-03-utf8-handling.md +456 -456
  320. package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-04-error-scenarios.md +658 -658
  321. package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-05-calling-patterns.md +671 -671
  322. package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-06-retry-strategies.md +1034 -1034
  323. package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-07-monitoring.md +653 -653
  324. package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-08-api-reference.md +847 -847
  325. package/docs/03-PATTERN-GUIDES/error-handling/readme.md +36 -36
  326. package/docs/03-PATTERN-GUIDES/examples/__tests__/readme.md +40 -40
  327. package/docs/03-PATTERN-GUIDES/examples/__tests__/resolver-examples.test.js +282 -282
  328. package/docs/03-PATTERN-GUIDES/examples/test-data/03-pattern-guides-readme.md +110 -110
  329. package/docs/03-PATTERN-GUIDES/examples/test-data/canonical-inventory.json +123 -123
  330. package/docs/03-PATTERN-GUIDES/examples/test-data/canonical-order.json +171 -171
  331. package/docs/03-PATTERN-GUIDES/examples/test-data/readme.md +28 -28
  332. package/docs/03-PATTERN-GUIDES/extraction/extraction-readme.md +15 -15
  333. package/docs/03-PATTERN-GUIDES/extraction/readme.md +25 -25
  334. package/docs/03-PATTERN-GUIDES/file-operations/examples/common-patterns.ts +407 -407
  335. package/docs/03-PATTERN-GUIDES/file-operations/examples/file-operations-readme.md +142 -142
  336. package/docs/03-PATTERN-GUIDES/file-operations/file-operations-quick-reference.md +462 -462
  337. package/docs/03-PATTERN-GUIDES/file-operations/file-operations-readme.md +379 -379
  338. package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-01-foundations.md +430 -430
  339. package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-02-quick-start.md +484 -484
  340. package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-03-s3-operations.md +507 -507
  341. package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-04-sftp-operations.md +963 -963
  342. package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-05-streaming-performance.md +503 -503
  343. package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-06-archive-patterns.md +386 -386
  344. package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-06-error-handling.md +117 -117
  345. package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-07-api-reference.md +78 -78
  346. package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-07-testing-troubleshooting.md +567 -567
  347. package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-08-api-reference.md +1055 -1055
  348. package/docs/03-PATTERN-GUIDES/file-operations/readme.md +32 -32
  349. package/docs/03-PATTERN-GUIDES/ingestion/ingestion-readme.md +15 -15
  350. package/docs/03-PATTERN-GUIDES/ingestion/readme.md +25 -25
  351. package/docs/03-PATTERN-GUIDES/integration-patterns/examples/batch-processing.ts +130 -130
  352. package/docs/03-PATTERN-GUIDES/integration-patterns/examples/common-patterns.ts +360 -360
  353. package/docs/03-PATTERN-GUIDES/integration-patterns/examples/delta-sync.ts +130 -130
  354. package/docs/03-PATTERN-GUIDES/integration-patterns/examples/integration-patterns-readme.md +100 -100
  355. package/docs/03-PATTERN-GUIDES/integration-patterns/examples/real-time-webhook.ts +398 -398
  356. package/docs/03-PATTERN-GUIDES/integration-patterns/integration-patterns-quick-reference.md +962 -962
  357. package/docs/03-PATTERN-GUIDES/integration-patterns/integration-patterns-readme.md +134 -134
  358. package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-01-real-time-processing.md +991 -991
  359. package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-02-batch-processing.md +1547 -1547
  360. package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-03-delta-sync.md +1108 -1108
  361. package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-04-webhook-patterns.md +1181 -1181
  362. package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-05-error-handling.md +1061 -1061
  363. package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-06-advanced-integration-services.md +1547 -1547
  364. package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-06-performance.md +109 -109
  365. package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-07-api-reference.md +34 -34
  366. package/docs/03-PATTERN-GUIDES/integration-patterns/readme.md +30 -30
  367. package/docs/03-PATTERN-GUIDES/logging-minimal-mode.md +128 -128
  368. package/docs/03-PATTERN-GUIDES/multiple-connections/examples/common-patterns.ts +380 -380
  369. package/docs/03-PATTERN-GUIDES/multiple-connections/examples/multiple-connections-readme.md +139 -139
  370. package/docs/03-PATTERN-GUIDES/multiple-connections/examples/parallel-root-connections.ts +149 -149
  371. package/docs/03-PATTERN-GUIDES/multiple-connections/examples/real-world-scenarios.ts +405 -405
  372. package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-01-foundations.md +378 -378
  373. package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-02-quick-start.md +566 -566
  374. package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-03-targeting-connections.md +659 -659
  375. package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-04-parallel-queries.md +656 -656
  376. package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-05-best-practices.md +624 -624
  377. package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-06-api-reference.md +824 -824
  378. package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-06-versori.md +119 -119
  379. package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-07-api-reference.md +87 -87
  380. package/docs/03-PATTERN-GUIDES/multiple-connections/multiple-connections-quick-reference.md +353 -353
  381. package/docs/03-PATTERN-GUIDES/multiple-connections/multiple-connections-readme.md +270 -270
  382. package/docs/03-PATTERN-GUIDES/multiple-connections/readme.md +30 -30
  383. package/docs/03-PATTERN-GUIDES/pagination/pagination-readme.md +14 -14
  384. package/docs/03-PATTERN-GUIDES/pagination/readme.md +24 -24
  385. package/docs/03-PATTERN-GUIDES/parquet/examples/common-patterns.ts +180 -180
  386. package/docs/03-PATTERN-GUIDES/parquet/examples/read-parquet.ts +48 -48
  387. package/docs/03-PATTERN-GUIDES/parquet/examples/write-parquet.ts +65 -65
  388. package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-01-introduction.md +393 -393
  389. package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-02-quick-start.md +572 -572
  390. package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-03-reading-parquet.md +525 -525
  391. package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-04-writing-parquet.md +554 -554
  392. package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-05-graphql-extraction.md +405 -405
  393. package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-06-performance.md +104 -104
  394. package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-06-s3-integration.md +511 -511
  395. package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-07-api-reference.md +90 -90
  396. package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-07-performance-optimization.md +525 -525
  397. package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-08-best-practices.md +712 -712
  398. package/docs/03-PATTERN-GUIDES/parquet/parquet-quick-reference.md +683 -683
  399. package/docs/03-PATTERN-GUIDES/parquet/parquet-readme.md +248 -248
  400. package/docs/03-PATTERN-GUIDES/parquet/readme.md +32 -32
  401. package/docs/03-PATTERN-GUIDES/parsers/parsers-readme.md +12 -12
  402. package/docs/03-PATTERN-GUIDES/parsers/readme.md +24 -24
  403. package/docs/03-PATTERN-GUIDES/readme.md +159 -159
  404. package/docs/03-PATTERN-GUIDES/webhooks/readme.md +24 -24
  405. package/docs/03-PATTERN-GUIDES/webhooks/webhooks-readme.md +8 -8
  406. package/docs/04-REFERENCE/architecture/architecture-01-overview.md +427 -427
  407. package/docs/04-REFERENCE/architecture/architecture-02-client-architecture.md +424 -424
  408. package/docs/04-REFERENCE/architecture/architecture-03-data-flow.md +690 -690
  409. package/docs/04-REFERENCE/architecture/architecture-04-service-layer.md +834 -834
  410. package/docs/04-REFERENCE/architecture/architecture-05-integration-architecture.md +655 -655
  411. package/docs/04-REFERENCE/architecture/architecture-06-state-management.md +653 -653
  412. package/docs/04-REFERENCE/architecture/architecture-adding-new-data-sources.md +686 -686
  413. package/docs/04-REFERENCE/architecture/readme.md +279 -279
  414. package/docs/04-REFERENCE/platforms/deno/readme.md +117 -117
  415. package/docs/04-REFERENCE/platforms/nodejs/readme.md +146 -146
  416. package/docs/04-REFERENCE/platforms/readme.md +135 -135
  417. package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-01-introduction.md +398 -398
  418. package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-02-quick-start.md +560 -560
  419. package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-03-authentication.md +757 -757
  420. package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-04-workflows.md +2476 -2476
  421. package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-05-connections.md +1167 -1167
  422. package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-06-kv-storage.md +990 -990
  423. package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-06-state-management.md +121 -121
  424. package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-07-api-reference.md +68 -68
  425. package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-07-deployment.md +731 -731
  426. package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-08-best-practices.md +1111 -1111
  427. package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-09-signature-reference.md +766 -766
  428. package/docs/04-REFERENCE/platforms/versori/platforms-versori-readme.md +299 -299
  429. package/docs/04-REFERENCE/platforms/versori/platforms-versori-s3-sftp-configuration-guide.md +1425 -1425
  430. package/docs/04-REFERENCE/platforms/versori/platforms-versori-webhook-api-key-security.md +816 -816
  431. package/docs/04-REFERENCE/platforms/versori/platforms-versori-webhook-connection-security.md +681 -681
  432. package/docs/04-REFERENCE/platforms/versori/platforms-versori-workflow-task-types.md +708 -708
  433. package/docs/04-REFERENCE/platforms/versori/readme.md +108 -108
  434. package/docs/04-REFERENCE/readme.md +148 -148
  435. package/docs/04-REFERENCE/resolver-signature/examples/advanced-resolvers.ts +482 -482
  436. package/docs/04-REFERENCE/resolver-signature/examples/async-resolvers.ts +496 -496
  437. package/docs/04-REFERENCE/resolver-signature/examples/basic-resolvers.ts +343 -343
  438. package/docs/04-REFERENCE/resolver-signature/examples/resolver-signature-readme.md +188 -188
  439. package/docs/04-REFERENCE/resolver-signature/examples/testing-resolvers.ts +463 -463
  440. package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-01-foundations.md +286 -286
  441. package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-02-parameter-reference.md +643 -643
  442. package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-03-basic-examples.md +521 -521
  443. package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-04-advanced-patterns.md +739 -739
  444. package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-05-sdk-resolvers.md +531 -531
  445. package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-06-migration-guide.md +650 -650
  446. package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-06-testing.md +125 -125
  447. package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-07-api-reference.md +794 -794
  448. package/docs/04-REFERENCE/resolver-signature/readme.md +64 -64
  449. package/docs/04-REFERENCE/resolver-signature/resolver-signature-quick-reference.md +270 -270
  450. package/docs/04-REFERENCE/resolver-signature/resolver-signature-readme.md +351 -351
  451. package/docs/04-REFERENCE/schema/fluent-commerce-schema.json +764 -764
  452. package/docs/04-REFERENCE/schema/readme.md +141 -141
  453. package/docs/04-REFERENCE/testing/examples/04-reference-testing-readme.md +158 -158
  454. package/docs/04-REFERENCE/testing/examples/fluent-testing.ts +62 -62
  455. package/docs/04-REFERENCE/testing/examples/health-check.ts +155 -155
  456. package/docs/04-REFERENCE/testing/examples/integration-test.ts +119 -119
  457. package/docs/04-REFERENCE/testing/examples/performance-test.ts +183 -183
  458. package/docs/04-REFERENCE/testing/examples/s3-testing.ts +127 -127
  459. package/docs/04-REFERENCE/testing/modules/04-reference-testing-01-foundations.md +267 -267
  460. package/docs/04-REFERENCE/testing/modules/04-reference-testing-02-s3-testing.md +599 -599
  461. package/docs/04-REFERENCE/testing/modules/04-reference-testing-03-fluent-testing.md +589 -589
  462. package/docs/04-REFERENCE/testing/modules/04-reference-testing-04-integration-testing.md +699 -699
  463. package/docs/04-REFERENCE/testing/modules/04-reference-testing-05-debugging.md +478 -478
  464. package/docs/04-REFERENCE/testing/modules/04-reference-testing-06-cicd-integration.md +463 -463
  465. package/docs/04-REFERENCE/testing/modules/04-reference-testing-06-preflight-validation.md +131 -131
  466. package/docs/04-REFERENCE/testing/modules/04-reference-testing-07-best-practices.md +499 -499
  467. package/docs/04-REFERENCE/testing/modules/04-reference-testing-07-coverage-ci.md +165 -165
  468. package/docs/04-REFERENCE/testing/modules/04-reference-testing-08-api-reference.md +634 -634
  469. package/docs/04-REFERENCE/testing/readme.md +86 -86
  470. package/docs/04-REFERENCE/testing/testing-quick-reference.md +667 -667
  471. package/docs/04-REFERENCE/testing/testing-readme.md +286 -286
  472. package/docs/04-REFERENCE/troubleshooting/readme.md +144 -144
  473. package/docs/04-REFERENCE/troubleshooting/troubleshooting-deno-sftp-compatibility.md +392 -392
  474. package/docs/template-loading-matrix.md +242 -242
  475. package/package.json +5 -3
  476. package/docs/02-CORE-GUIDES/api-reference/cli-profile-integration.md +0 -377
@@ -1,963 +1,963 @@
1
- # Module 4: SFTP Operations
2
-
3
- **Level:** Intermediate
4
- **Estimated Time:** 25 minutes
5
-
6
- ## Overview
7
-
8
- Complete guide to SFTP-specific file operations, including connection management, directory operations, and SFTP-specific features.
9
-
10
- ## Learning Objectives
11
-
12
- By the end of this module, you will:
13
-
14
- - ✅ Master all SFTP file operations
15
- - ✅ Understand connection pooling and lifecycle
16
- - ✅ Learn directory creation and path handling
17
- - ✅ Implement secure authentication (password and key-based)
18
- - ✅ Handle SFTP-specific errors
19
-
20
- ## SFTP Concepts
21
-
22
- ### Connection Lifecycle
23
-
24
- ```typescript
25
- import {
26
- SftpDataSource,
27
- createConsoleLogger,
28
- toStructuredLogger
29
- } from '@fluentcommerce/fc-connect-sdk';
30
-
31
- const logger = createConsoleLogger();
32
-
33
- // Create data source (connection established on first operation)
34
- const sftp = new SftpDataSource(
35
- {
36
- settings: {
37
- remotePath: '/data/incoming',
38
- filePattern: '*.csv',
39
- host: process.env.SFTP_HOST!,
40
- port: 22,
41
- username: process.env.SFTP_USERNAME!,
42
- password: process.env.SFTP_PASSWORD!,
43
- },
44
- },
45
- logger
46
- );
47
-
48
- try {
49
- // Use the connection
50
- const files = await sftp.listFiles();
51
- } finally {
52
- // CRITICAL: Always dispose to close connection
53
- await sftp.dispose();
54
- }
55
- ```
56
-
57
- ### Path Conventions
58
-
59
- ```typescript
60
- // ✅ CORRECT: Absolute paths with leading slash
61
- const file = await sftp.downloadFile('/data/incoming/orders.csv');
62
-
63
- // ❌ WRONG: Relative paths without leading slash
64
- const file = await sftp.downloadFile('data/incoming/orders.csv'); // May fail
65
-
66
- // remotePath in config provides base path
67
- settings: {
68
- remotePath: '/data/incoming', // Base path
69
- filePattern: '*.csv'
70
- }
71
- ```
72
-
73
- ## All SFTP Operations
74
-
75
- ### List Files
76
-
77
- ```typescript
78
- const sftp = new SftpDataSource(
79
- {
80
- settings: {
81
- remotePath: '/data/incoming',
82
- filePattern: '*.csv', // Pattern specified in config
83
- host: process.env.SFTP_HOST!,
84
- username: process.env.SFTP_USERNAME!,
85
- password: process.env.SFTP_PASSWORD!,
86
- },
87
- },
88
- logger
89
- );
90
-
91
- try {
92
- // List files matching pattern from remotePath
93
- const files = await sftp.listFiles();
94
-
95
- files.forEach(file => {
96
- console.log(`Name: ${file.name}`);
97
- console.log(`Path: ${file.path}`);
98
- console.log(`Size: ${file.size} bytes`);
99
- });
100
- } finally {
101
- await sftp.dispose();
102
- }
103
- ```
104
-
105
- ### Download Files
106
-
107
- ```typescript
108
- try {
109
- const content = await sftp.downloadFile('/data/incoming/orders.csv');
110
- console.log(`Downloaded ${content.length} bytes`);
111
-
112
- // Process content
113
- const lines = content.split('\n');
114
- console.log(`File has ${lines.length} lines`);
115
- } catch (error: any) {
116
- if (error.message.includes('No such file')) {
117
- console.error('File does not exist');
118
- } else {
119
- console.error('Download failed:', error);
120
- }
121
- } finally {
122
- await sftp.dispose();
123
- }
124
- ```
125
-
126
- ### Upload Files
127
-
128
- ```typescript
129
- try {
130
- const data = JSON.stringify({ status: 'processed' });
131
-
132
- await sftp.uploadFile('/data/outgoing/results.json', data);
133
- console.log('Upload successful');
134
-
135
- // Upload buffer
136
- const buffer = Buffer.from('Hello, SFTP!');
137
- await sftp.uploadFile('/data/messages/greeting.txt', buffer);
138
- } finally {
139
- await sftp.dispose();
140
- }
141
- ```
142
-
143
- **Options parameter** (optional):
144
- ```typescript
145
- await sftp.uploadFile('/data/outgoing/results.json', data, {
146
- overwrite: true, // Overwrite existing file (default: false)
147
- createDirectories: true, // Create parent directories (default: false)
148
- permissions: '0644', // File permissions (default: server default)
149
- encoding: 'utf8' // Content encoding (default: 'utf8')
150
- });
151
- ```
152
-
153
- ### Move Files
154
-
155
- ```typescript
156
- try {
157
- // Move/rename file (default: overwrite = false)
158
- await sftp.moveFile('/data/incoming/orders.csv', '/data/processed/orders.csv');
159
-
160
- // Move with overwrite flag
161
- await sftp.moveFile(
162
- '/data/incoming/inventory.csv',
163
- '/archive/2025/01/15/inventory.csv',
164
- true // Overwrite if destination exists
165
- );
166
-
167
- console.log('Files moved successfully');
168
- } finally {
169
- await sftp.dispose();
170
- }
171
- ````
172
-
173
- ### Copy Files
174
-
175
- ```typescript
176
- try {
177
- // Copy file (default: overwrite = false)
178
- await sftp.copyFile('/data/important/config.json', '/backup/config.json');
179
-
180
- // Copy with overwrite
181
- await sftp.copyFile('/data/source.json', '/backup/target.json', true);
182
-
183
- console.log('File copied successfully');
184
- } finally {
185
- await sftp.dispose();
186
- }
187
- ```
188
-
189
- ### Delete Files
190
-
191
- ```typescript
192
- try {
193
- // Delete file
194
- await sftp.deleteFile('/data/temp/old-file.csv');
195
-
196
- // Delete with existence check
197
- if (await sftp.fileExists('/data/archive/old-data.csv')) {
198
- await sftp.deleteFile('/data/archive/old-data.csv');
199
- console.log('File deleted');
200
- }
201
- } finally {
202
- await sftp.dispose();
203
- }
204
- ```
205
-
206
- ### Check File Existence
207
-
208
- ```typescript
209
- try {
210
- const exists = await sftp.fileExists('/data/incoming/orders.csv');
211
- console.log(`File exists: ${exists}`);
212
-
213
- // Conditional processing
214
- if (exists) {
215
- const content = await sftp.downloadFile('/data/incoming/orders.csv');
216
- // Process file
217
- }
218
- } finally {
219
- await sftp.dispose();
220
- }
221
- ```
222
-
223
- ## Authentication Methods
224
-
225
- SFTP supports multiple authentication methods. How you access credentials depends on your deployment context.
226
-
227
- ### Credential Access by Runtime
228
-
229
- | Runtime | Method | Location |
230
- |---------|--------|----------|
231
- | **Versori** | `activation.connections` (recommended) | Connection UI |
232
- | **Versori** | `credentials().get()` (alternative) | Connection UI |
233
- | **Standalone** | Environment variables | `.env` file |
234
-
235
- ### Versori Platform: Connection-Based Auth (Recommended)
236
-
237
- **Best practice for Versori platform** - credentials are already decoded and type-safe:
238
-
239
- ```typescript
240
- import { schedule, http } from '@versori/run';
241
- import { SftpDataSource } from '@fluentcommerce/fc-connect-sdk';
242
-
243
- export const sftpWorkflow = schedule("sftp-sync", "0 * * * *")
244
- .then(http("process", { connection: "fluent_commerce" }, async (ctx) => {
245
- const { activation, log } = ctx;
246
-
247
- // Access ALL connections from activation
248
- const allConnections = activation.connections || [];
249
-
250
- // Find SFTP connection by name
251
- const sftpConnection = allConnections.find(c => c.name === 'versori_ftp_server');
252
-
253
- if (!sftpConnection) {
254
- throw new Error(
255
- `SFTP connection not found. Available: ${allConnections.map(c => c.name).join(', ')}`
256
- );
257
- }
258
-
259
- // Extract credentials (already decoded - no Buffer needed!)
260
- const sftpCred = sftpConnection.credentials[0]?.credential;
261
-
262
- if (!sftpCred?.data?.basicAuth) {
263
- throw new Error('SFTP connection missing Basic Authentication configuration');
264
- }
265
-
266
- const sftpUsername = sftpCred.data.basicAuth.username;
267
- const sftpPassword = sftpCred.data.basicAuth.password;
268
- const sftpHost = sftpConnection.baseUrl || 'sftp.example.com';
269
-
270
- // Create SFTP data source
271
- const sftp = new SftpDataSource(
272
- {
273
- settings: {
274
- host: sftpHost,
275
- port: 22,
276
- username: sftpUsername,
277
- password: sftpPassword,
278
- remotePath: '/data',
279
- },
280
- },
281
- log
282
- );
283
-
284
- // Use SFTP
285
- const files = await sftp.listFiles();
286
- return { success: true, fileCount: files.length };
287
- }));
288
- ```
289
-
290
- **Why this is recommended:**
291
- - ✅ Credentials already decoded (no Buffer.from needed)
292
- - ✅ Works in any task type (fn, http, webhook)
293
- - ✅ Type-safe access
294
- - ✅ Better error messages
295
- - ✅ Supports multiple connections
296
-
297
- ### Versori Platform: credentials().get() (Alternative)
298
-
299
- **Use when working in fn() tasks** where connectionVariables aren't available:
300
-
301
- ```typescript
302
- import { schedule, fn } from '@versori/run';
303
- import { Buffer } from 'node:buffer'; // Required for Deno runtime!
304
- import { SftpDataSource } from '@fluentcommerce/fc-connect-sdk';
305
-
306
- export const sftpWorkflow = schedule("sftp-sync", "0 * * * *")
307
- .then(fn("get-files", async (ctx) => {
308
- const { log } = ctx;
309
-
310
- // Retrieve credentials from connection configuration
311
- const sftpCred = await ctx.credentials().getAccessToken('SFTP');
312
-
313
- if (!sftpCred?.accessToken) {
314
- throw new Error('No SFTP credentials found in connection configuration');
315
- }
316
-
317
- // Decode base64 accessToken to get "username:password"
318
- const rawBasicAuth = Buffer.from(sftpCred.accessToken, 'base64').toString('utf-8');
319
-
320
- // Split on ':' to extract username and password
321
- const [sftpUsername, sftpPassword] = rawBasicAuth.split(':');
322
-
323
- if (!sftpUsername || !sftpPassword) {
324
- throw new Error('Invalid SFTP credential format - expected username:password');
325
- }
326
-
327
- // Create SFTP data source
328
- const sftp = new SftpDataSource(
329
- {
330
- settings: {
331
- host: 'sftp.example.com',
332
- port: 22,
333
- username: sftpUsername,
334
- password: sftpPassword,
335
- remotePath: '/data',
336
- },
337
- },
338
- log
339
- );
340
-
341
- const files = await sftp.listFiles();
342
- return { success: true, fileCount: files.length };
343
- }));
344
- ```
345
-
346
- **Limitations:**
347
- - ⚠️ Requires manual base64 decoding
348
- - ⚠️ Only works in fn() tasks
349
- - ⚠️ More error-prone (string splitting)
350
-
351
- ### Standalone: Password Authentication
352
-
353
- **For Node.js/Deno standalone scripts:**
354
-
355
- ```typescript
356
- import { SftpDataSource, createConsoleLogger } from '@fluentcommerce/fc-connect-sdk';
357
-
358
- const logger = createConsoleLogger();
359
-
360
- const sftp = new SftpDataSource(
361
- {
362
- settings: {
363
- host: process.env.SFTP_HOST!,
364
- port: 22,
365
- username: process.env.SFTP_USERNAME!,
366
- password: process.env.SFTP_PASSWORD!,
367
- remotePath: '/data',
368
- },
369
- },
370
- logger
371
- );
372
-
373
- try {
374
- const files = await sftp.listFiles();
375
- console.log(`Found ${files.length} files`);
376
- } finally {
377
- await sftp.dispose();
378
- }
379
- ```
380
-
381
- **Environment variables (.env):**
382
- ```bash
383
- SFTP_HOST=sftp.example.com
384
- SFTP_USERNAME=myuser
385
- SFTP_PASSWORD=mypassword
386
- ```
387
-
388
- ### Standalone: Private Key Authentication
389
-
390
- **For key-based authentication in standalone scripts:**
391
-
392
- ```typescript
393
- import * as fs from 'fs';
394
- import { SftpDataSource, createConsoleLogger } from '@fluentcommerce/fc-connect-sdk';
395
-
396
- const logger = createConsoleLogger();
397
- const privateKey = fs.readFileSync('/path/to/private-key.pem', 'utf-8');
398
-
399
- const sftp = new SftpDataSource(
400
- {
401
- settings: {
402
- host: 'sftp.example.com',
403
- port: 22,
404
- username: 'myuser',
405
- privateKey: privateKey,
406
- passphrase: process.env.KEY_PASSPHRASE, // Optional if key is encrypted
407
- remotePath: '/data',
408
- },
409
- },
410
- logger
411
- );
412
- ```
413
-
414
- ### Security Best Practices
415
-
416
- **For all authentication methods:**
417
-
418
- ```typescript
419
- // ✅ GOOD: Never log credentials
420
- log.info('SFTP configuration', {
421
- host: sftpHost,
422
- username: sftpUsername,
423
- hasPassword: !!sftpPassword,
424
- passwordLength: sftpPassword.length // Log length, not value
425
- });
426
-
427
- // ❌ BAD: Logging credentials
428
- log.info('SFTP config', { username, password }); // Don't do this!
429
-
430
- // ✅ GOOD: Validate credentials before use
431
- if (!sftpUsername || !sftpPassword) {
432
- throw new Error('Missing SFTP credentials');
433
- }
434
-
435
- if (sftpPassword.length < 8) {
436
- log.warn('SFTP password seems short', { length: sftpPassword.length });
437
- }
438
-
439
- // ✅ GOOD: Centralized credential management
440
- // Store credentials in Versori Connections (platform) or .env files (standalone)
441
- // Never hardcode credentials in code
442
-
443
- // ❌ BAD: Hardcoded credentials
444
- const username = 'hardcoded-user'; // Don't do this!
445
- const password = 'hardcoded-pass'; // Don't do this!
446
- ```
447
-
448
- **See also:** [SFTP Credential Access & Security Guide](../../../02-CORE-GUIDES/data-sources/data-sources-sftp-credential-access-security.md) for complete credential retrieval patterns.
449
-
450
- ## SFTP-Specific Patterns
451
-
452
- ### Multi-Folder Processing
453
-
454
- ```typescript
455
- const folders = ['orders', 'inventory', 'products'];
456
-
457
- for (const folder of folders) {
458
- const sftp = new SftpDataSource(
459
- {
460
- settings: {
461
- remotePath: `/data/${folder}`,
462
- filePattern: '*.csv',
463
- host: process.env.SFTP_HOST!,
464
- username: process.env.SFTP_USERNAME!,
465
- password: process.env.SFTP_PASSWORD!,
466
- },
467
- },
468
- logger
469
- );
470
-
471
- try {
472
- const files = await sftp.listFiles();
473
- console.log(`Processing ${files.length} files from ${folder}`);
474
-
475
- for (const file of files) {
476
- const content = await sftp.downloadFile(file.path);
477
- await processByType(folder, content);
478
-
479
- const archivePath = `/archive/${folder}/${file.name}`;
480
- await sftp.moveFile(file.path, archivePath);
481
- }
482
- } finally {
483
- await sftp.dispose();
484
- }
485
- }
486
- ```
487
-
488
- ### Date-Based Archive
489
-
490
- ```typescript
491
- function getDateBasedPath(filename: string, basePath: string): string {
492
- const now = new Date();
493
- const year = now.getFullYear();
494
- const month = String(now.getMonth() + 1).padStart(2, '0');
495
- const day = String(now.getDate()).padStart(2, '0');
496
-
497
- return `${basePath}/${year}/${month}/${day}/${filename}`;
498
- }
499
-
500
- try {
501
- const files = await sftp.listFiles();
502
-
503
- for (const file of files) {
504
- const content = await sftp.downloadFile(file.path);
505
- await processData(content);
506
-
507
- const archivePath = getDateBasedPath(file.name, '/archive');
508
- await sftp.moveFile(file.path, archivePath);
509
- // Result: /archive/2025/01/15/orders.csv
510
- }
511
- } finally {
512
- await sftp.dispose();
513
- }
514
- ```
515
-
516
- ### Batch Processing with Connection Reuse
517
-
518
- ```typescript
519
- const sftp = new SftpDataSource(
520
- {
521
- settings: {
522
- remotePath: '/data/incoming',
523
- filePattern: '*.csv',
524
- host: process.env.SFTP_HOST!,
525
- username: process.env.SFTP_USERNAME!,
526
- password: process.env.SFTP_PASSWORD!,
527
- },
528
- },
529
- logger
530
- );
531
-
532
- try {
533
- const files = await sftp.listFiles();
534
-
535
- // Process all files using same connection
536
- for (const file of files) {
537
- try {
538
- const content = await sftp.downloadFile(file.path);
539
- const result = await processData(content);
540
-
541
- await sftp.uploadFile(`/data/outgoing/${file.name.replace('.csv', '.json', JSON.stringify(result))}`
542
- );
543
-
544
- const today = new Date().toISOString().split('T')[0];
545
- await sftp.moveFile(file.path, `/archive/${today}/${file.name}`);
546
-
547
- console.log(`Processed ${file.name}`);
548
- } catch (error) {
549
- console.error(`Failed to process ${file.name}:`, error);
550
- // Continue with next file
551
- }
552
- }
553
- } finally {
554
- // Connection closed once after processing all files
555
- await sftp.dispose();
556
- }
557
- ```
558
-
559
- ## Error Handling
560
-
561
- ### Common SFTP Errors
562
-
563
- ```typescript
564
- try {
565
- await sftp.downloadFile('/data/missing-file.csv');
566
- } catch (error: any) {
567
- const message = error.message.toLowerCase();
568
-
569
- if (message.includes('no such file')) {
570
- console.error('File does not exist');
571
- } else if (message.includes('permission denied')) {
572
- console.error('Permission denied - check SFTP permissions');
573
- } else if (message.includes('connection')) {
574
- console.error('Connection error - check host/port/credentials');
575
- } else if (message.includes('authentication')) {
576
- console.error('Authentication failed - check username/password');
577
- } else if (message.includes('timeout')) {
578
- console.error('Operation timed out - check network connectivity');
579
- } else {
580
- console.error('SFTP error:', error.message);
581
- }
582
- }
583
- ```
584
-
585
- ### Connection Retry Logic
586
-
587
- ```typescript
588
- async function createSftpWithRetry(
589
- config: any,
590
- logger: Logger,
591
- maxRetries = 3
592
- ): Promise<SftpDataSource> {
593
- for (let attempt = 1; attempt <= maxRetries; attempt++) {
594
- try {
595
- const sftp = new SftpDataSource(config, logger);
596
-
597
- // Test connection
598
- await sftp.fileExists('/'); // Simple operation to verify connection
599
-
600
- return sftp;
601
- } catch (error: any) {
602
- if (attempt === maxRetries) throw error;
603
-
604
- const delay = Math.pow(2, attempt) * 1000;
605
- console.log(`Connection retry ${attempt}/${maxRetries} after ${delay}ms`);
606
- await new Promise(resolve => setTimeout(resolve, delay));
607
- }
608
- }
609
- throw new Error('Should not reach here');
610
- }
611
- ```
612
-
613
- ## Performance Tips
614
-
615
- ### Connection Pooling
616
-
617
- The SDK handles connection pooling automatically, but you should:
618
-
619
- ```typescript
620
- // ✅ GOOD: Reuse connection for multiple operations
621
- const sftp = new SftpDataSource(config, logger);
622
- try {
623
- await sftp.downloadFile('/file1.csv');
624
- await sftp.downloadFile('/file2.csv');
625
- await sftp.downloadFile('/file3.csv');
626
- } finally {
627
- await sftp.dispose(); // Close once
628
- }
629
-
630
- // ❌ BAD: Create/dispose for each operation
631
- for (const file of files) {
632
- const sftp = new SftpDataSource(config, logger);
633
- await sftp.downloadFile(file.path);
634
- await sftp.dispose(); // Too many connections!
635
- }
636
- ```
637
-
638
- ### Parallel Operations
639
-
640
- ```typescript
641
- // Process files in parallel (with connection limit)
642
- const sftp = new SftpDataSource(config, logger);
643
-
644
- try {
645
- const files = await sftp.listFiles();
646
-
647
- // Limit concurrency to avoid overwhelming server
648
- const concurrencyLimit = 5;
649
-
650
- for (let i = 0; i < files.length; i += concurrencyLimit) {
651
- const batch = files.slice(i, i + concurrencyLimit);
652
-
653
- await Promise.all(
654
- batch.map(async file => {
655
- const content = await sftp.downloadFile(file.path);
656
- return processData(content);
657
- })
658
- );
659
- }
660
- } finally {
661
- await sftp.dispose();
662
- }
663
- ```
664
-
665
- ## Directory Operations
666
-
667
- ### Create Directory
668
-
669
- ```typescript
670
- try {
671
- // Create directory (non-recursive)
672
- await sftp.createDirectory('/data/incoming');
673
-
674
- // Create directory recursively (creates parent directories if needed)
675
- await sftp.createDirectory('/data/archive/2025/01/15', true);
676
-
677
- // Create with specific permissions
678
- await sftp.createDirectory('/data/public', true, '0755');
679
- } finally {
680
- await sftp.dispose();
681
- }
682
- ```
683
-
684
- ### Check Directory Existence
685
-
686
- ```typescript
687
- try {
688
- const exists = await sftp.directoryExists('/data/incoming');
689
-
690
- if (!exists) {
691
- await sftp.createDirectory('/data/incoming', true);
692
- console.log('Directory created');
693
- } else {
694
- console.log('Directory already exists');
695
- }
696
- } finally {
697
- await sftp.dispose();
698
- }
699
- ```
700
-
701
- ### Directory Operations Pattern
702
-
703
- ```typescript
704
- async function ensureDirectoryExists(sftp: SftpDataSource, path: string) {
705
- if (!(await sftp.directoryExists(path))) {
706
- await sftp.createDirectory(path, true); // Recursive
707
- console.log(`Created directory: ${path}`);
708
- }
709
- }
710
-
711
- try {
712
- // Ensure archive directory exists before moving files
713
- await ensureDirectoryExists(sftp, '/archive/2025/01/15');
714
-
715
- const files = await sftp.listFiles();
716
- for (const file of files) {
717
- const archivePath = `/archive/2025/01/15/${file.name}`;
718
- await sftp.moveFile(file.path, archivePath);
719
- }
720
- } finally {
721
- await sftp.dispose();
722
- }
723
- ```
724
-
725
- ## Built-in Content Parsing
726
-
727
- SFTP data source provides built-in parsing methods for CSV and JSON content, eliminating the need for separate parser services.
728
-
729
- ### Parse CSV Content
730
-
731
- ```typescript
732
- try {
733
- // Download file
734
- const csvContent = await sftp.downloadFile('/data/incoming/orders.csv');
735
-
736
- // Parse CSV content directly (no need for CSVParserService)
737
- const fileMetadata = {
738
- path: '/data/incoming/orders.csv',
739
- name: 'orders.csv',
740
- size: csvContent.length,
741
- source: 'SFTP'
742
- };
743
-
744
- const records = sftp.parseCsvContent(csvContent as string, fileMetadata);
745
-
746
- console.log(`Parsed ${records.length} CSV records`);
747
- records.forEach(record => {
748
- console.log(`Order: ${record.orderId}, Total: ${record.total}`);
749
- });
750
- } finally {
751
- await sftp.dispose();
752
- }
753
- ```
754
-
755
- ### Parse JSON Content
756
-
757
- ```typescript
758
- try {
759
- // Download JSON file
760
- const jsonContent = await sftp.downloadFile('/data/incoming/orders.json');
761
-
762
- // Parse JSON content (supports both JSON and JSONL formats)
763
- const fileMetadata = {
764
- path: '/data/incoming/orders.json',
765
- name: 'orders.json',
766
- size: jsonContent.length,
767
- source: 'SFTP'
768
- };
769
-
770
- // Auto-detect format (JSON array or JSONL)
771
- const records = sftp.parseJsonContent(jsonContent as string, fileMetadata);
772
-
773
- // Or specify format explicitly
774
- const jsonRecords = sftp.parseJsonContent(jsonContent as string, fileMetadata, {
775
- format: 'json', // or 'jsonl'
776
- validate: true, // Throw on invalid JSON
777
- maxDepth: 10 // Limit nesting depth
778
- });
779
-
780
- console.log(`Parsed ${records.length} JSON records`);
781
- } finally {
782
- await sftp.dispose();
783
- }
784
- ```
785
-
786
- ### Write CSV Content
787
-
788
- ```typescript
789
- try {
790
- // Prepare data
791
- const records = [
792
- { sku: 'SKU-001', quantity: 10, location: 'WAREHOUSE-A' },
793
- { sku: 'SKU-002', quantity: 25, location: 'WAREHOUSE-B' }
794
- ];
795
-
796
- // Generate CSV content
797
- const csvContent = sftp.writeCsvContent(records, {
798
- headers: ['sku', 'quantity', 'location'],
799
- delimiter: ',',
800
- includeHeaders: true
801
- });
802
-
803
- // Upload CSV file
804
- await sftp.uploadFile('/data/outgoing/inventory.csv', csvContent);
805
- } finally {
806
- await sftp.dispose();
807
- }
808
- ```
809
-
810
- ### Write JSON Content
811
-
812
- ```typescript
813
- try {
814
- // Prepare data
815
- const orders = [
816
- { id: 'ORD-001', total: 99.99, status: 'pending' },
817
- { id: 'ORD-002', total: 149.99, status: 'shipped' }
818
- ];
819
-
820
- // Generate JSON content (compact or pretty)
821
- const jsonContent = sftp.writeJsonContent(orders, {
822
- format: 'json', // or 'jsonl' for JSON Lines
823
- pretty: true // Pretty-print JSON
824
- });
825
-
826
- // Upload JSON file
827
- await sftp.uploadFile('/data/outgoing/orders.json', jsonContent);
828
- } finally {
829
- await sftp.dispose();
830
- }
831
- ```
832
-
833
- ### Complete Parsing Workflow
834
-
835
- ```typescript
836
- try {
837
- // List CSV files
838
- const csvFiles = await sftp.listFiles({ filePattern: '*.csv' });
839
-
840
- for (const file of csvFiles) {
841
- // Download and parse in one step
842
- const content = await sftp.downloadFile(file.path);
843
- const records = sftp.parseCsvContent(content as string, file);
844
-
845
- // Transform data
846
- const transformed = records.map(record => ({
847
- ref: record.sku,
848
- qty: parseInt(record.quantity),
849
- locationRef: record.location
850
- }));
851
-
852
- // Write transformed data as JSON
853
- const jsonContent = sftp.writeJsonContent(transformed, { pretty: true });
854
- const outputPath = file.path.replace('.csv', '.json');
855
- await sftp.uploadFile(outputPath, jsonContent);
856
-
857
- // Archive original
858
- await sftp.moveFile(file.path, `/archive/${file.name}`);
859
- }
860
- } finally {
861
- await sftp.dispose();
862
- }
863
- ```
864
-
865
- ## Parquet Support
866
-
867
- SFTP data source supports writing Parquet files for efficient data storage and transfer.
868
-
869
- ### Write Parquet Content
870
-
871
- ```typescript
872
- try {
873
- // Prepare data records
874
- const records = [
875
- { sku: 'SKU-001', quantity: 10, price: 99.99, available: true },
876
- { sku: 'SKU-002', quantity: 25, price: 149.99, available: true }
877
- ];
878
-
879
- // Generate Parquet content as Buffer
880
- const parquetBuffer = await sftp.writeParquetContent(records, {
881
- compression: 'GZIP', // Options: 'UNCOMPRESSED', 'GZIP', 'SNAPPY', 'LZO', 'BROTLI'
882
- rowGroupSize: 5000, // Number of rows per row group
883
- schema: { // Optional: explicit schema (auto-detected if not provided)
884
- sku: { type: 'UTF8' },
885
- quantity: { type: 'INT64' },
886
- price: { type: 'DOUBLE' },
887
- available: { type: 'BOOLEAN' }
888
- }
889
- });
890
-
891
- // Upload Parquet file
892
- await sftp.uploadFile('/data/outgoing/inventory.parquet', parquetBuffer);
893
-
894
- console.log(`Generated Parquet file: ${parquetBuffer.length} bytes`);
895
- } finally {
896
- await sftp.dispose();
897
- }
898
- ```
899
-
900
- ### Parquet with Compression
901
-
902
- ```typescript
903
- try {
904
- const records = /* ... large dataset ... */;
905
-
906
- // Use SNAPPY compression for faster processing
907
- const compressed = await sftp.writeParquetContent(records, {
908
- compression: 'SNAPPY',
909
- rowGroupSize: 10000
910
- });
911
-
912
- // Use GZIP for smaller file size
913
- const gzipped = await sftp.writeParquetContent(records, {
914
- compression: 'GZIP',
915
- rowGroupSize: 10000
916
- });
917
-
918
- console.log(`SNAPPY: ${compressed.length} bytes`);
919
- console.log(`GZIP: ${gzipped.length} bytes`);
920
-
921
- // Upload smaller file
922
- await sftp.uploadFile('/data/outgoing/data.parquet', gzipped);
923
- } finally {
924
- await sftp.dispose();
925
- }
926
- ```
927
-
928
- **Note:** For reading Parquet files, use `ParquetParserService` from the SDK:
929
-
930
- ```typescript
931
- import { ParquetParserService } from '@fluentcommerce/fc-connect-sdk';
932
-
933
- const parser = new ParquetParserService();
934
- const content = await sftp.downloadFile('/data/incoming/inventory.parquet', {
935
- asBuffer: true
936
- });
937
- const records = await parser.parse(content as Buffer);
938
- ```
939
-
940
- ## Key Takeaways
941
-
942
- - 🎯 Always call `dispose()` in `finally` blocks to close connections
943
- - 🎯 Use absolute paths with leading slashes (`/data/file.csv`)
944
- - 🎯 SFTP has native move operation (faster than S3's copy + delete)
945
- - 🎯 Use `createDirectory()` with `recursive: true` to create parent directories
946
- - 🎯 Use `directoryExists()` to check before creating directories
947
- - 🎯 Built-in `parseCsvContent()` and `parseJsonContent()` eliminate need for separate parsers
948
- - 🎯 Built-in `writeCsvContent()` and `writeJsonContent()` for generating content
949
- - 🎯 `writeParquetContent()` generates compressed Parquet files efficiently
950
- - 🎯 Reuse connections for batch operations to improve performance
951
- - 🎯 Handle authentication errors, connection timeouts, and permission issues
952
-
953
- ## Next Steps
954
-
955
- Continue to [Module 5: Streaming & Performance](./file-operations-05-streaming-performance.md) for handling large files.
956
-
957
- ---
958
-
959
- **Related Resources:**
960
-
961
- - [Quick Reference](../../../02-CORE-GUIDES/advanced-services/advanced-services-quick-reference.md) - SFTP operations cheat sheet
962
- - [Common Patterns](../examples/common-patterns.ts) - Copy-paste ready snippets
963
- - [Testing Guide](./file-operations-07-testing-troubleshooting.md) - SFTP test coverage
1
+ # Module 4: SFTP Operations
2
+
3
+ **Level:** Intermediate
4
+ **Estimated Time:** 25 minutes
5
+
6
+ ## Overview
7
+
8
+ Complete guide to SFTP-specific file operations, including connection management, directory operations, and SFTP-specific features.
9
+
10
+ ## Learning Objectives
11
+
12
+ By the end of this module, you will:
13
+
14
+ - ✅ Master all SFTP file operations
15
+ - ✅ Understand connection pooling and lifecycle
16
+ - ✅ Learn directory creation and path handling
17
+ - ✅ Implement secure authentication (password and key-based)
18
+ - ✅ Handle SFTP-specific errors
19
+
20
+ ## SFTP Concepts
21
+
22
+ ### Connection Lifecycle
23
+
24
+ ```typescript
25
+ import {
26
+ SftpDataSource,
27
+ createConsoleLogger,
28
+ toStructuredLogger
29
+ } from '@fluentcommerce/fc-connect-sdk';
30
+
31
+ const logger = createConsoleLogger();
32
+
33
+ // Create data source (connection established on first operation)
34
+ const sftp = new SftpDataSource(
35
+ {
36
+ settings: {
37
+ remotePath: '/data/incoming',
38
+ filePattern: '*.csv',
39
+ host: process.env.SFTP_HOST!,
40
+ port: 22,
41
+ username: process.env.SFTP_USERNAME!,
42
+ password: process.env.SFTP_PASSWORD!,
43
+ },
44
+ },
45
+ logger
46
+ );
47
+
48
+ try {
49
+ // Use the connection
50
+ const files = await sftp.listFiles();
51
+ } finally {
52
+ // CRITICAL: Always dispose to close connection
53
+ await sftp.dispose();
54
+ }
55
+ ```
56
+
57
+ ### Path Conventions
58
+
59
+ ```typescript
60
+ // ✅ CORRECT: Absolute paths with leading slash
61
+ const file = await sftp.downloadFile('/data/incoming/orders.csv');
62
+
63
+ // ❌ WRONG: Relative paths without leading slash
64
+ const file = await sftp.downloadFile('data/incoming/orders.csv'); // May fail
65
+
66
+ // remotePath in config provides base path
67
+ settings: {
68
+ remotePath: '/data/incoming', // Base path
69
+ filePattern: '*.csv'
70
+ }
71
+ ```
72
+
73
+ ## All SFTP Operations
74
+
75
+ ### List Files
76
+
77
+ ```typescript
78
+ const sftp = new SftpDataSource(
79
+ {
80
+ settings: {
81
+ remotePath: '/data/incoming',
82
+ filePattern: '*.csv', // Pattern specified in config
83
+ host: process.env.SFTP_HOST!,
84
+ username: process.env.SFTP_USERNAME!,
85
+ password: process.env.SFTP_PASSWORD!,
86
+ },
87
+ },
88
+ logger
89
+ );
90
+
91
+ try {
92
+ // List files matching pattern from remotePath
93
+ const files = await sftp.listFiles();
94
+
95
+ files.forEach(file => {
96
+ console.log(`Name: ${file.name}`);
97
+ console.log(`Path: ${file.path}`);
98
+ console.log(`Size: ${file.size} bytes`);
99
+ });
100
+ } finally {
101
+ await sftp.dispose();
102
+ }
103
+ ```
104
+
105
+ ### Download Files
106
+
107
+ ```typescript
108
+ try {
109
+ const content = await sftp.downloadFile('/data/incoming/orders.csv');
110
+ console.log(`Downloaded ${content.length} bytes`);
111
+
112
+ // Process content
113
+ const lines = content.split('\n');
114
+ console.log(`File has ${lines.length} lines`);
115
+ } catch (error: any) {
116
+ if (error.message.includes('No such file')) {
117
+ console.error('File does not exist');
118
+ } else {
119
+ console.error('Download failed:', error);
120
+ }
121
+ } finally {
122
+ await sftp.dispose();
123
+ }
124
+ ```
125
+
126
+ ### Upload Files
127
+
128
+ ```typescript
129
+ try {
130
+ const data = JSON.stringify({ status: 'processed' });
131
+
132
+ await sftp.uploadFile('/data/outgoing/results.json', data);
133
+ console.log('Upload successful');
134
+
135
+ // Upload buffer
136
+ const buffer = Buffer.from('Hello, SFTP!');
137
+ await sftp.uploadFile('/data/messages/greeting.txt', buffer);
138
+ } finally {
139
+ await sftp.dispose();
140
+ }
141
+ ```
142
+
143
+ **Options parameter** (optional):
144
+ ```typescript
145
+ await sftp.uploadFile('/data/outgoing/results.json', data, {
146
+ overwrite: true, // Overwrite existing file (default: false)
147
+ createDirectories: true, // Create parent directories (default: false)
148
+ permissions: '0644', // File permissions (default: server default)
149
+ encoding: 'utf8' // Content encoding (default: 'utf8')
150
+ });
151
+ ```
152
+
153
+ ### Move Files
154
+
155
+ ```typescript
156
+ try {
157
+ // Move/rename file (default: overwrite = false)
158
+ await sftp.moveFile('/data/incoming/orders.csv', '/data/processed/orders.csv');
159
+
160
+ // Move with overwrite flag
161
+ await sftp.moveFile(
162
+ '/data/incoming/inventory.csv',
163
+ '/archive/2025/01/15/inventory.csv',
164
+ true // Overwrite if destination exists
165
+ );
166
+
167
+ console.log('Files moved successfully');
168
+ } finally {
169
+ await sftp.dispose();
170
+ }
171
+ ````
172
+
173
+ ### Copy Files
174
+
175
+ ```typescript
176
+ try {
177
+ // Copy file (default: overwrite = false)
178
+ await sftp.copyFile('/data/important/config.json', '/backup/config.json');
179
+
180
+ // Copy with overwrite
181
+ await sftp.copyFile('/data/source.json', '/backup/target.json', true);
182
+
183
+ console.log('File copied successfully');
184
+ } finally {
185
+ await sftp.dispose();
186
+ }
187
+ ```
188
+
189
+ ### Delete Files
190
+
191
+ ```typescript
192
+ try {
193
+ // Delete file
194
+ await sftp.deleteFile('/data/temp/old-file.csv');
195
+
196
+ // Delete with existence check
197
+ if (await sftp.fileExists('/data/archive/old-data.csv')) {
198
+ await sftp.deleteFile('/data/archive/old-data.csv');
199
+ console.log('File deleted');
200
+ }
201
+ } finally {
202
+ await sftp.dispose();
203
+ }
204
+ ```
205
+
206
+ ### Check File Existence
207
+
208
+ ```typescript
209
+ try {
210
+ const exists = await sftp.fileExists('/data/incoming/orders.csv');
211
+ console.log(`File exists: ${exists}`);
212
+
213
+ // Conditional processing
214
+ if (exists) {
215
+ const content = await sftp.downloadFile('/data/incoming/orders.csv');
216
+ // Process file
217
+ }
218
+ } finally {
219
+ await sftp.dispose();
220
+ }
221
+ ```
222
+
223
+ ## Authentication Methods
224
+
225
+ SFTP supports multiple authentication methods. How you access credentials depends on your deployment context.
226
+
227
+ ### Credential Access by Runtime
228
+
229
+ | Runtime | Method | Location |
230
+ |---------|--------|----------|
231
+ | **Versori** | `activation.connections` (recommended) | Connection UI |
232
+ | **Versori** | `credentials().get()` (alternative) | Connection UI |
233
+ | **Standalone** | Environment variables | `.env` file |
234
+
235
+ ### Versori Platform: Connection-Based Auth (Recommended)
236
+
237
+ **Best practice for Versori platform** - credentials are already decoded and type-safe:
238
+
239
+ ```typescript
240
+ import { schedule, http } from '@versori/run';
241
+ import { SftpDataSource } from '@fluentcommerce/fc-connect-sdk';
242
+
243
+ export const sftpWorkflow = schedule("sftp-sync", "0 * * * *")
244
+ .then(http("process", { connection: "fluent_commerce" }, async (ctx) => {
245
+ const { activation, log } = ctx;
246
+
247
+ // Access ALL connections from activation
248
+ const allConnections = activation.connections || [];
249
+
250
+ // Find SFTP connection by name
251
+ const sftpConnection = allConnections.find(c => c.name === 'versori_ftp_server');
252
+
253
+ if (!sftpConnection) {
254
+ throw new Error(
255
+ `SFTP connection not found. Available: ${allConnections.map(c => c.name).join(', ')}`
256
+ );
257
+ }
258
+
259
+ // Extract credentials (already decoded - no Buffer needed!)
260
+ const sftpCred = sftpConnection.credentials[0]?.credential;
261
+
262
+ if (!sftpCred?.data?.basicAuth) {
263
+ throw new Error('SFTP connection missing Basic Authentication configuration');
264
+ }
265
+
266
+ const sftpUsername = sftpCred.data.basicAuth.username;
267
+ const sftpPassword = sftpCred.data.basicAuth.password;
268
+ const sftpHost = sftpConnection.baseUrl || 'sftp.example.com';
269
+
270
+ // Create SFTP data source
271
+ const sftp = new SftpDataSource(
272
+ {
273
+ settings: {
274
+ host: sftpHost,
275
+ port: 22,
276
+ username: sftpUsername,
277
+ password: sftpPassword,
278
+ remotePath: '/data',
279
+ },
280
+ },
281
+ log
282
+ );
283
+
284
+ // Use SFTP
285
+ const files = await sftp.listFiles();
286
+ return { success: true, fileCount: files.length };
287
+ }));
288
+ ```
289
+
290
+ **Why this is recommended:**
291
+ - ✅ Credentials already decoded (no Buffer.from needed)
292
+ - ✅ Works in any task type (fn, http, webhook)
293
+ - ✅ Type-safe access
294
+ - ✅ Better error messages
295
+ - ✅ Supports multiple connections
296
+
297
+ ### Versori Platform: credentials().get() (Alternative)
298
+
299
+ **Use when working in fn() tasks** where connectionVariables aren't available:
300
+
301
+ ```typescript
302
+ import { schedule, fn } from '@versori/run';
303
+ import { Buffer } from 'node:buffer'; // Required for Deno runtime!
304
+ import { SftpDataSource } from '@fluentcommerce/fc-connect-sdk';
305
+
306
+ export const sftpWorkflow = schedule("sftp-sync", "0 * * * *")
307
+ .then(fn("get-files", async (ctx) => {
308
+ const { log } = ctx;
309
+
310
+ // Retrieve credentials from connection configuration
311
+ const sftpCred = await ctx.credentials().getAccessToken('SFTP');
312
+
313
+ if (!sftpCred?.accessToken) {
314
+ throw new Error('No SFTP credentials found in connection configuration');
315
+ }
316
+
317
+ // Decode base64 accessToken to get "username:password"
318
+ const rawBasicAuth = Buffer.from(sftpCred.accessToken, 'base64').toString('utf-8');
319
+
320
+ // Split on ':' to extract username and password
321
+ const [sftpUsername, sftpPassword] = rawBasicAuth.split(':');
322
+
323
+ if (!sftpUsername || !sftpPassword) {
324
+ throw new Error('Invalid SFTP credential format - expected username:password');
325
+ }
326
+
327
+ // Create SFTP data source
328
+ const sftp = new SftpDataSource(
329
+ {
330
+ settings: {
331
+ host: 'sftp.example.com',
332
+ port: 22,
333
+ username: sftpUsername,
334
+ password: sftpPassword,
335
+ remotePath: '/data',
336
+ },
337
+ },
338
+ log
339
+ );
340
+
341
+ const files = await sftp.listFiles();
342
+ return { success: true, fileCount: files.length };
343
+ }));
344
+ ```
345
+
346
+ **Limitations:**
347
+ - ⚠️ Requires manual base64 decoding
348
+ - ⚠️ Only works in fn() tasks
349
+ - ⚠️ More error-prone (string splitting)
350
+
351
+ ### Standalone: Password Authentication
352
+
353
+ **For Node.js/Deno standalone scripts:**
354
+
355
+ ```typescript
356
+ import { SftpDataSource, createConsoleLogger } from '@fluentcommerce/fc-connect-sdk';
357
+
358
+ const logger = createConsoleLogger();
359
+
360
+ const sftp = new SftpDataSource(
361
+ {
362
+ settings: {
363
+ host: process.env.SFTP_HOST!,
364
+ port: 22,
365
+ username: process.env.SFTP_USERNAME!,
366
+ password: process.env.SFTP_PASSWORD!,
367
+ remotePath: '/data',
368
+ },
369
+ },
370
+ logger
371
+ );
372
+
373
+ try {
374
+ const files = await sftp.listFiles();
375
+ console.log(`Found ${files.length} files`);
376
+ } finally {
377
+ await sftp.dispose();
378
+ }
379
+ ```
380
+
381
+ **Environment variables (.env):**
382
+ ```bash
383
+ SFTP_HOST=sftp.example.com
384
+ SFTP_USERNAME=myuser
385
+ SFTP_PASSWORD=mypassword
386
+ ```
387
+
388
+ ### Standalone: Private Key Authentication
389
+
390
+ **For key-based authentication in standalone scripts:**
391
+
392
+ ```typescript
393
+ import * as fs from 'fs';
394
+ import { SftpDataSource, createConsoleLogger } from '@fluentcommerce/fc-connect-sdk';
395
+
396
+ const logger = createConsoleLogger();
397
+ const privateKey = fs.readFileSync('/path/to/private-key.pem', 'utf-8');
398
+
399
+ const sftp = new SftpDataSource(
400
+ {
401
+ settings: {
402
+ host: 'sftp.example.com',
403
+ port: 22,
404
+ username: 'myuser',
405
+ privateKey: privateKey,
406
+ passphrase: process.env.KEY_PASSPHRASE, // Optional if key is encrypted
407
+ remotePath: '/data',
408
+ },
409
+ },
410
+ logger
411
+ );
412
+ ```
413
+
414
+ ### Security Best Practices
415
+
416
+ **For all authentication methods:**
417
+
418
+ ```typescript
419
+ // ✅ GOOD: Never log credentials
420
+ log.info('SFTP configuration', {
421
+ host: sftpHost,
422
+ username: sftpUsername,
423
+ hasPassword: !!sftpPassword,
424
+ passwordLength: sftpPassword.length // Log length, not value
425
+ });
426
+
427
+ // ❌ BAD: Logging credentials
428
+ log.info('SFTP config', { username, password }); // Don't do this!
429
+
430
+ // ✅ GOOD: Validate credentials before use
431
+ if (!sftpUsername || !sftpPassword) {
432
+ throw new Error('Missing SFTP credentials');
433
+ }
434
+
435
+ if (sftpPassword.length < 8) {
436
+ log.warn('SFTP password seems short', { length: sftpPassword.length });
437
+ }
438
+
439
+ // ✅ GOOD: Centralized credential management
440
+ // Store credentials in Versori Connections (platform) or .env files (standalone)
441
+ // Never hardcode credentials in code
442
+
443
+ // ❌ BAD: Hardcoded credentials
444
+ const username = 'hardcoded-user'; // Don't do this!
445
+ const password = 'hardcoded-pass'; // Don't do this!
446
+ ```
447
+
448
+ **See also:** [SFTP Credential Access & Security Guide](../../../02-CORE-GUIDES/data-sources/data-sources-sftp-credential-access-security.md) for complete credential retrieval patterns.
449
+
450
+ ## SFTP-Specific Patterns
451
+
452
+ ### Multi-Folder Processing
453
+
454
+ ```typescript
455
+ const folders = ['orders', 'inventory', 'products'];
456
+
457
+ for (const folder of folders) {
458
+ const sftp = new SftpDataSource(
459
+ {
460
+ settings: {
461
+ remotePath: `/data/${folder}`,
462
+ filePattern: '*.csv',
463
+ host: process.env.SFTP_HOST!,
464
+ username: process.env.SFTP_USERNAME!,
465
+ password: process.env.SFTP_PASSWORD!,
466
+ },
467
+ },
468
+ logger
469
+ );
470
+
471
+ try {
472
+ const files = await sftp.listFiles();
473
+ console.log(`Processing ${files.length} files from ${folder}`);
474
+
475
+ for (const file of files) {
476
+ const content = await sftp.downloadFile(file.path);
477
+ await processByType(folder, content);
478
+
479
+ const archivePath = `/archive/${folder}/${file.name}`;
480
+ await sftp.moveFile(file.path, archivePath);
481
+ }
482
+ } finally {
483
+ await sftp.dispose();
484
+ }
485
+ }
486
+ ```
487
+
488
+ ### Date-Based Archive
489
+
490
+ ```typescript
491
+ function getDateBasedPath(filename: string, basePath: string): string {
492
+ const now = new Date();
493
+ const year = now.getFullYear();
494
+ const month = String(now.getMonth() + 1).padStart(2, '0');
495
+ const day = String(now.getDate()).padStart(2, '0');
496
+
497
+ return `${basePath}/${year}/${month}/${day}/${filename}`;
498
+ }
499
+
500
+ try {
501
+ const files = await sftp.listFiles();
502
+
503
+ for (const file of files) {
504
+ const content = await sftp.downloadFile(file.path);
505
+ await processData(content);
506
+
507
+ const archivePath = getDateBasedPath(file.name, '/archive');
508
+ await sftp.moveFile(file.path, archivePath);
509
+ // Result: /archive/2025/01/15/orders.csv
510
+ }
511
+ } finally {
512
+ await sftp.dispose();
513
+ }
514
+ ```
515
+
516
+ ### Batch Processing with Connection Reuse
517
+
518
+ ```typescript
519
+ const sftp = new SftpDataSource(
520
+ {
521
+ settings: {
522
+ remotePath: '/data/incoming',
523
+ filePattern: '*.csv',
524
+ host: process.env.SFTP_HOST!,
525
+ username: process.env.SFTP_USERNAME!,
526
+ password: process.env.SFTP_PASSWORD!,
527
+ },
528
+ },
529
+ logger
530
+ );
531
+
532
+ try {
533
+ const files = await sftp.listFiles();
534
+
535
+ // Process all files using same connection
536
+ for (const file of files) {
537
+ try {
538
+ const content = await sftp.downloadFile(file.path);
539
+ const result = await processData(content);
540
+
541
+ await sftp.uploadFile(`/data/outgoing/${file.name.replace('.csv', '.json', JSON.stringify(result))}`
542
+ );
543
+
544
+ const today = new Date().toISOString().split('T')[0];
545
+ await sftp.moveFile(file.path, `/archive/${today}/${file.name}`);
546
+
547
+ console.log(`Processed ${file.name}`);
548
+ } catch (error) {
549
+ console.error(`Failed to process ${file.name}:`, error);
550
+ // Continue with next file
551
+ }
552
+ }
553
+ } finally {
554
+ // Connection closed once after processing all files
555
+ await sftp.dispose();
556
+ }
557
+ ```
558
+
559
+ ## Error Handling
560
+
561
+ ### Common SFTP Errors
562
+
563
+ ```typescript
564
+ try {
565
+ await sftp.downloadFile('/data/missing-file.csv');
566
+ } catch (error: any) {
567
+ const message = error.message.toLowerCase();
568
+
569
+ if (message.includes('no such file')) {
570
+ console.error('File does not exist');
571
+ } else if (message.includes('permission denied')) {
572
+ console.error('Permission denied - check SFTP permissions');
573
+ } else if (message.includes('connection')) {
574
+ console.error('Connection error - check host/port/credentials');
575
+ } else if (message.includes('authentication')) {
576
+ console.error('Authentication failed - check username/password');
577
+ } else if (message.includes('timeout')) {
578
+ console.error('Operation timed out - check network connectivity');
579
+ } else {
580
+ console.error('SFTP error:', error.message);
581
+ }
582
+ }
583
+ ```
584
+
585
+ ### Connection Retry Logic
586
+
587
+ ```typescript
588
+ async function createSftpWithRetry(
589
+ config: any,
590
+ logger: Logger,
591
+ maxRetries = 3
592
+ ): Promise<SftpDataSource> {
593
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
594
+ try {
595
+ const sftp = new SftpDataSource(config, logger);
596
+
597
+ // Test connection
598
+ await sftp.fileExists('/'); // Simple operation to verify connection
599
+
600
+ return sftp;
601
+ } catch (error: any) {
602
+ if (attempt === maxRetries) throw error;
603
+
604
+ const delay = Math.pow(2, attempt) * 1000;
605
+ console.log(`Connection retry ${attempt}/${maxRetries} after ${delay}ms`);
606
+ await new Promise(resolve => setTimeout(resolve, delay));
607
+ }
608
+ }
609
+ throw new Error('Should not reach here');
610
+ }
611
+ ```
612
+
613
+ ## Performance Tips
614
+
615
+ ### Connection Pooling
616
+
617
+ The SDK handles connection pooling automatically, but you should:
618
+
619
+ ```typescript
620
+ // ✅ GOOD: Reuse connection for multiple operations
621
+ const sftp = new SftpDataSource(config, logger);
622
+ try {
623
+ await sftp.downloadFile('/file1.csv');
624
+ await sftp.downloadFile('/file2.csv');
625
+ await sftp.downloadFile('/file3.csv');
626
+ } finally {
627
+ await sftp.dispose(); // Close once
628
+ }
629
+
630
+ // ❌ BAD: Create/dispose for each operation
631
+ for (const file of files) {
632
+ const sftp = new SftpDataSource(config, logger);
633
+ await sftp.downloadFile(file.path);
634
+ await sftp.dispose(); // Too many connections!
635
+ }
636
+ ```
637
+
638
+ ### Parallel Operations
639
+
640
+ ```typescript
641
+ // Process files in parallel (with connection limit)
642
+ const sftp = new SftpDataSource(config, logger);
643
+
644
+ try {
645
+ const files = await sftp.listFiles();
646
+
647
+ // Limit concurrency to avoid overwhelming server
648
+ const concurrencyLimit = 5;
649
+
650
+ for (let i = 0; i < files.length; i += concurrencyLimit) {
651
+ const batch = files.slice(i, i + concurrencyLimit);
652
+
653
+ await Promise.all(
654
+ batch.map(async file => {
655
+ const content = await sftp.downloadFile(file.path);
656
+ return processData(content);
657
+ })
658
+ );
659
+ }
660
+ } finally {
661
+ await sftp.dispose();
662
+ }
663
+ ```
664
+
665
+ ## Directory Operations
666
+
667
+ ### Create Directory
668
+
669
+ ```typescript
670
+ try {
671
+ // Create directory (non-recursive)
672
+ await sftp.createDirectory('/data/incoming');
673
+
674
+ // Create directory recursively (creates parent directories if needed)
675
+ await sftp.createDirectory('/data/archive/2025/01/15', true);
676
+
677
+ // Create with specific permissions
678
+ await sftp.createDirectory('/data/public', true, '0755');
679
+ } finally {
680
+ await sftp.dispose();
681
+ }
682
+ ```
683
+
684
+ ### Check Directory Existence
685
+
686
+ ```typescript
687
+ try {
688
+ const exists = await sftp.directoryExists('/data/incoming');
689
+
690
+ if (!exists) {
691
+ await sftp.createDirectory('/data/incoming', true);
692
+ console.log('Directory created');
693
+ } else {
694
+ console.log('Directory already exists');
695
+ }
696
+ } finally {
697
+ await sftp.dispose();
698
+ }
699
+ ```
700
+
701
+ ### Directory Operations Pattern
702
+
703
+ ```typescript
704
+ async function ensureDirectoryExists(sftp: SftpDataSource, path: string) {
705
+ if (!(await sftp.directoryExists(path))) {
706
+ await sftp.createDirectory(path, true); // Recursive
707
+ console.log(`Created directory: ${path}`);
708
+ }
709
+ }
710
+
711
+ try {
712
+ // Ensure archive directory exists before moving files
713
+ await ensureDirectoryExists(sftp, '/archive/2025/01/15');
714
+
715
+ const files = await sftp.listFiles();
716
+ for (const file of files) {
717
+ const archivePath = `/archive/2025/01/15/${file.name}`;
718
+ await sftp.moveFile(file.path, archivePath);
719
+ }
720
+ } finally {
721
+ await sftp.dispose();
722
+ }
723
+ ```
724
+
725
+ ## Built-in Content Parsing
726
+
727
+ SFTP data source provides built-in parsing methods for CSV and JSON content, eliminating the need for separate parser services.
728
+
729
+ ### Parse CSV Content
730
+
731
+ ```typescript
732
+ try {
733
+ // Download file
734
+ const csvContent = await sftp.downloadFile('/data/incoming/orders.csv');
735
+
736
+ // Parse CSV content directly (no need for CSVParserService)
737
+ const fileMetadata = {
738
+ path: '/data/incoming/orders.csv',
739
+ name: 'orders.csv',
740
+ size: csvContent.length,
741
+ source: 'SFTP'
742
+ };
743
+
744
+ const records = sftp.parseCsvContent(csvContent as string, fileMetadata);
745
+
746
+ console.log(`Parsed ${records.length} CSV records`);
747
+ records.forEach(record => {
748
+ console.log(`Order: ${record.orderId}, Total: ${record.total}`);
749
+ });
750
+ } finally {
751
+ await sftp.dispose();
752
+ }
753
+ ```
754
+
755
+ ### Parse JSON Content
756
+
757
+ ```typescript
758
+ try {
759
+ // Download JSON file
760
+ const jsonContent = await sftp.downloadFile('/data/incoming/orders.json');
761
+
762
+ // Parse JSON content (supports both JSON and JSONL formats)
763
+ const fileMetadata = {
764
+ path: '/data/incoming/orders.json',
765
+ name: 'orders.json',
766
+ size: jsonContent.length,
767
+ source: 'SFTP'
768
+ };
769
+
770
+ // Auto-detect format (JSON array or JSONL)
771
+ const records = sftp.parseJsonContent(jsonContent as string, fileMetadata);
772
+
773
+ // Or specify format explicitly
774
+ const jsonRecords = sftp.parseJsonContent(jsonContent as string, fileMetadata, {
775
+ format: 'json', // or 'jsonl'
776
+ validate: true, // Throw on invalid JSON
777
+ maxDepth: 10 // Limit nesting depth
778
+ });
779
+
780
+ console.log(`Parsed ${records.length} JSON records`);
781
+ } finally {
782
+ await sftp.dispose();
783
+ }
784
+ ```
785
+
786
+ ### Write CSV Content
787
+
788
+ ```typescript
789
+ try {
790
+ // Prepare data
791
+ const records = [
792
+ { sku: 'SKU-001', quantity: 10, location: 'WAREHOUSE-A' },
793
+ { sku: 'SKU-002', quantity: 25, location: 'WAREHOUSE-B' }
794
+ ];
795
+
796
+ // Generate CSV content
797
+ const csvContent = sftp.writeCsvContent(records, {
798
+ headers: ['sku', 'quantity', 'location'],
799
+ delimiter: ',',
800
+ includeHeaders: true
801
+ });
802
+
803
+ // Upload CSV file
804
+ await sftp.uploadFile('/data/outgoing/inventory.csv', csvContent);
805
+ } finally {
806
+ await sftp.dispose();
807
+ }
808
+ ```
809
+
810
+ ### Write JSON Content
811
+
812
+ ```typescript
813
+ try {
814
+ // Prepare data
815
+ const orders = [
816
+ { id: 'ORD-001', total: 99.99, status: 'pending' },
817
+ { id: 'ORD-002', total: 149.99, status: 'shipped' }
818
+ ];
819
+
820
+ // Generate JSON content (compact or pretty)
821
+ const jsonContent = sftp.writeJsonContent(orders, {
822
+ format: 'json', // or 'jsonl' for JSON Lines
823
+ pretty: true // Pretty-print JSON
824
+ });
825
+
826
+ // Upload JSON file
827
+ await sftp.uploadFile('/data/outgoing/orders.json', jsonContent);
828
+ } finally {
829
+ await sftp.dispose();
830
+ }
831
+ ```
832
+
833
+ ### Complete Parsing Workflow
834
+
835
+ ```typescript
836
+ try {
837
+ // List CSV files
838
+ const csvFiles = await sftp.listFiles({ filePattern: '*.csv' });
839
+
840
+ for (const file of csvFiles) {
841
+ // Download and parse in one step
842
+ const content = await sftp.downloadFile(file.path);
843
+ const records = sftp.parseCsvContent(content as string, file);
844
+
845
+ // Transform data
846
+ const transformed = records.map(record => ({
847
+ ref: record.sku,
848
+ qty: parseInt(record.quantity),
849
+ locationRef: record.location
850
+ }));
851
+
852
+ // Write transformed data as JSON
853
+ const jsonContent = sftp.writeJsonContent(transformed, { pretty: true });
854
+ const outputPath = file.path.replace('.csv', '.json');
855
+ await sftp.uploadFile(outputPath, jsonContent);
856
+
857
+ // Archive original
858
+ await sftp.moveFile(file.path, `/archive/${file.name}`);
859
+ }
860
+ } finally {
861
+ await sftp.dispose();
862
+ }
863
+ ```
864
+
865
+ ## Parquet Support
866
+
867
+ SFTP data source supports writing Parquet files for efficient data storage and transfer.
868
+
869
+ ### Write Parquet Content
870
+
871
+ ```typescript
872
+ try {
873
+ // Prepare data records
874
+ const records = [
875
+ { sku: 'SKU-001', quantity: 10, price: 99.99, available: true },
876
+ { sku: 'SKU-002', quantity: 25, price: 149.99, available: true }
877
+ ];
878
+
879
+ // Generate Parquet content as Buffer
880
+ const parquetBuffer = await sftp.writeParquetContent(records, {
881
+ compression: 'GZIP', // Options: 'UNCOMPRESSED', 'GZIP', 'SNAPPY', 'LZO', 'BROTLI'
882
+ rowGroupSize: 5000, // Number of rows per row group
883
+ schema: { // Optional: explicit schema (auto-detected if not provided)
884
+ sku: { type: 'UTF8' },
885
+ quantity: { type: 'INT64' },
886
+ price: { type: 'DOUBLE' },
887
+ available: { type: 'BOOLEAN' }
888
+ }
889
+ });
890
+
891
+ // Upload Parquet file
892
+ await sftp.uploadFile('/data/outgoing/inventory.parquet', parquetBuffer);
893
+
894
+ console.log(`Generated Parquet file: ${parquetBuffer.length} bytes`);
895
+ } finally {
896
+ await sftp.dispose();
897
+ }
898
+ ```
899
+
900
+ ### Parquet with Compression
901
+
902
+ ```typescript
903
+ try {
904
+ const records = /* ... large dataset ... */;
905
+
906
+ // Use SNAPPY compression for faster processing
907
+ const compressed = await sftp.writeParquetContent(records, {
908
+ compression: 'SNAPPY',
909
+ rowGroupSize: 10000
910
+ });
911
+
912
+ // Use GZIP for smaller file size
913
+ const gzipped = await sftp.writeParquetContent(records, {
914
+ compression: 'GZIP',
915
+ rowGroupSize: 10000
916
+ });
917
+
918
+ console.log(`SNAPPY: ${compressed.length} bytes`);
919
+ console.log(`GZIP: ${gzipped.length} bytes`);
920
+
921
+ // Upload smaller file
922
+ await sftp.uploadFile('/data/outgoing/data.parquet', gzipped);
923
+ } finally {
924
+ await sftp.dispose();
925
+ }
926
+ ```
927
+
928
+ **Note:** For reading Parquet files, use `ParquetParserService` from the SDK:
929
+
930
+ ```typescript
931
+ import { ParquetParserService } from '@fluentcommerce/fc-connect-sdk';
932
+
933
+ const parser = new ParquetParserService();
934
+ const content = await sftp.downloadFile('/data/incoming/inventory.parquet', {
935
+ asBuffer: true
936
+ });
937
+ const records = await parser.parse(content as Buffer);
938
+ ```
939
+
940
+ ## Key Takeaways
941
+
942
+ - 🎯 Always call `dispose()` in `finally` blocks to close connections
943
+ - 🎯 Use absolute paths with leading slashes (`/data/file.csv`)
944
+ - 🎯 SFTP has native move operation (faster than S3's copy + delete)
945
+ - 🎯 Use `createDirectory()` with `recursive: true` to create parent directories
946
+ - 🎯 Use `directoryExists()` to check before creating directories
947
+ - 🎯 Built-in `parseCsvContent()` and `parseJsonContent()` eliminate need for separate parsers
948
+ - 🎯 Built-in `writeCsvContent()` and `writeJsonContent()` for generating content
949
+ - 🎯 `writeParquetContent()` generates compressed Parquet files efficiently
950
+ - 🎯 Reuse connections for batch operations to improve performance
951
+ - 🎯 Handle authentication errors, connection timeouts, and permission issues
952
+
953
+ ## Next Steps
954
+
955
+ Continue to [Module 5: Streaming & Performance](./file-operations-05-streaming-performance.md) for handling large files.
956
+
957
+ ---
958
+
959
+ **Related Resources:**
960
+
961
+ - [Quick Reference](../../../02-CORE-GUIDES/advanced-services/advanced-services-quick-reference.md) - SFTP operations cheat sheet
962
+ - [Common Patterns](../examples/common-patterns.ts) - Copy-paste ready snippets
963
+ - [Testing Guide](./file-operations-07-testing-troubleshooting.md) - SFTP test coverage