@fluentcommerce/fc-connect-sdk 0.1.53 → 0.1.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -2
- package/README.md +39 -0
- package/dist/cjs/auth/index.d.ts +3 -0
- package/dist/cjs/auth/index.js +13 -0
- package/dist/cjs/auth/profile-loader.d.ts +18 -0
- package/dist/cjs/auth/profile-loader.js +208 -0
- package/dist/cjs/client-factory.d.ts +4 -0
- package/dist/cjs/client-factory.js +10 -0
- package/dist/cjs/clients/fluent-client.js +13 -6
- package/dist/cjs/index.d.ts +3 -1
- package/dist/cjs/index.js +8 -2
- package/dist/cjs/utils/pagination-helpers.js +38 -2
- package/dist/cjs/versori/fluent-versori-client.js +11 -5
- package/dist/esm/auth/index.d.ts +3 -0
- package/dist/esm/auth/index.js +2 -0
- package/dist/esm/auth/profile-loader.d.ts +18 -0
- package/dist/esm/auth/profile-loader.js +169 -0
- package/dist/esm/client-factory.d.ts +4 -0
- package/dist/esm/client-factory.js +9 -0
- package/dist/esm/clients/fluent-client.js +13 -6
- package/dist/esm/index.d.ts +3 -1
- package/dist/esm/index.js +2 -1
- package/dist/esm/utils/pagination-helpers.js +38 -2
- package/dist/esm/versori/fluent-versori-client.js +11 -5
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/tsconfig.types.tsbuildinfo +1 -1
- package/dist/types/auth/index.d.ts +3 -0
- package/dist/types/auth/profile-loader.d.ts +18 -0
- package/dist/types/client-factory.d.ts +4 -0
- package/dist/types/index.d.ts +3 -1
- package/docs/00-START-HERE/EXPORT-VALIDATION.md +158 -158
- package/docs/00-START-HERE/cli-analyze-source-structure-guide.md +655 -655
- package/docs/00-START-HERE/cli-documentation-index.md +202 -202
- package/docs/00-START-HERE/cli-quick-reference.md +252 -252
- package/docs/00-START-HERE/decision-tree.md +552 -552
- package/docs/00-START-HERE/getting-started.md +1070 -1070
- package/docs/00-START-HERE/mapper-quick-decision-guide.md +235 -235
- package/docs/00-START-HERE/readme.md +237 -237
- package/docs/00-START-HERE/retailerid-configuration.md +404 -404
- package/docs/00-START-HERE/sdk-philosophy.md +794 -794
- package/docs/00-START-HERE/troubleshooting-quick-reference.md +1086 -1086
- package/docs/01-TEMPLATES/faq.md +686 -686
- package/docs/01-TEMPLATES/patterns/pattern-templates-guide.md +68 -68
- package/docs/01-TEMPLATES/patterns/patterns-csv-schema-validation-and-rejection-report.md +233 -233
- package/docs/01-TEMPLATES/patterns/patterns-custom-resolvers.md +407 -407
- package/docs/01-TEMPLATES/patterns/patterns-error-handling-retry.md +511 -511
- package/docs/01-TEMPLATES/patterns/patterns-field-mapping-universal.md +701 -701
- package/docs/01-TEMPLATES/patterns/patterns-large-file-splitting.md +1430 -1430
- package/docs/01-TEMPLATES/patterns/patterns-master-data-etl.md +2399 -2399
- package/docs/01-TEMPLATES/patterns/patterns-pagination-streaming.md +447 -447
- package/docs/01-TEMPLATES/patterns/patterns-state-duplicate-prevention.md +385 -385
- package/docs/01-TEMPLATES/readme.md +957 -957
- package/docs/01-TEMPLATES/standalone/standalone-asn-inbound-processing.md +1209 -1209
- package/docs/01-TEMPLATES/standalone/standalone-graphql-query-export.md +1140 -1140
- package/docs/01-TEMPLATES/standalone/standalone-graphql-to-parquet-partitioned-s3.md +432 -432
- package/docs/01-TEMPLATES/standalone/standalone-multi-channel-inventory-sync.md +1185 -1185
- package/docs/01-TEMPLATES/standalone/standalone-multi-source-aggregation.md +1462 -1462
- package/docs/01-TEMPLATES/standalone/standalone-s3-csv-batch-api.md +1390 -1390
- package/docs/01-TEMPLATES/standalone/standalone-s3-csv-inventory-to-batch.md +330 -330
- package/docs/01-TEMPLATES/standalone/standalone-scripts-guide.md +87 -87
- package/docs/01-TEMPLATES/standalone/standalone-sftp-xml-graphql.md +1444 -1444
- package/docs/01-TEMPLATES/standalone/standalone-webhook-payload-processing.md +688 -688
- package/docs/01-TEMPLATES/versori/business-examples/business-examples-dropship-order-routing.md +193 -193
- package/docs/01-TEMPLATES/versori/business-examples/business-examples-graphql-parquet-extraction.md +518 -518
- package/docs/01-TEMPLATES/versori/business-examples/business-examples-inter-location-transfers.md +2162 -2162
- package/docs/01-TEMPLATES/versori/business-examples/business-examples-pre-order-allocation.md +2226 -2226
- package/docs/01-TEMPLATES/versori/business-examples/business-scenarios-guide.md +87 -87
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-connection-validation-pattern.md +656 -656
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-dual-workflow-connector.md +835 -835
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-guide.md +108 -108
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-kv-state-management.md +1533 -1533
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-xml-response-patterns.md +1160 -1160
- package/docs/01-TEMPLATES/versori/versori-platform-guide.md +201 -201
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-asn-purchase-order.md +1906 -1906
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-dropship-routing.md +1074 -1074
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-flash-sale-reserve.md +1395 -1395
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-generic-xml-order.md +888 -888
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-payment-gateway-integration.md +2478 -2478
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-rma-returns-comprehensive.md +2240 -2240
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-xml-order-ingestion.md +2029 -2029
- package/docs/01-TEMPLATES/versori/webhooks/webhook-templates-guide.md +140 -140
- package/docs/01-TEMPLATES/versori/workflows/_examples/sample-data/inventory-mapping.json +20 -20
- package/docs/01-TEMPLATES/versori/workflows/_examples/sample-data/products_2025-01-22.csv +11 -11
- package/docs/01-TEMPLATES/versori/workflows/_examples/sample-data/sample-data-guide.md +34 -34
- package/docs/01-TEMPLATES/versori/workflows/_examples/workflow-examples-guide.md +36 -36
- package/docs/01-TEMPLATES/versori/workflows/extraction/extraction-modes-guide.md +1038 -1038
- package/docs/01-TEMPLATES/versori/workflows/extraction/extraction-workflows-guide.md +138 -138
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/graphql-extraction-guide.md +63 -63
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-fulfillments-to-sftp-csv.md +2062 -2062
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-fulfillments-to-sftp-xml.md +2294 -2294
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-positions-to-s3-csv.md +2461 -2461
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-positions-to-sftp-xml.md +2529 -2529
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-quantities-to-s3-csv.md +2464 -2464
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-quantities-to-s3-json.md +1959 -1959
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-orders-to-s3-csv.md +1953 -1953
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-orders-to-sftp-xml.md +2541 -2541
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-products-to-s3-json.md +2384 -2384
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-products-to-sftp-xml.md +2445 -2445
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-virtual-positions-to-s3-csv.md +2355 -2355
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-virtual-positions-to-s3-json.md +2042 -2042
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-virtual-positions-to-sftp-xml.md +2726 -2726
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/batch-api-guide.md +206 -206
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-cycle-count-reconciliation.md +2030 -2030
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-multi-channel-inventory-sync.md +1882 -1882
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-s3-csv-inventory-batch.md +2827 -2827
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-s3-json-inventory-batch.md +1952 -1952
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-s3-xml-inventory-batch.md +3289 -3289
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-sftp-csv-inventory-batch.md +3064 -3064
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-sftp-json-inventory-batch.md +3238 -3238
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-sftp-xml-inventory-batch.md +2977 -2977
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/event-api-guide.md +321 -321
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-payload-json-order-cancel-event.md +959 -959
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-payload-xml-order-cancel-event.md +1170 -1170
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-csv-product-event.md +2312 -2312
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-json-product-event.md +2999 -2999
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-parquet-product-event.md +2836 -2836
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-xml-product-event.md +2395 -2395
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-csv-product-event.md +2295 -2295
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-json-product-event.md +2602 -2602
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-parquet-product-event.md +2589 -2589
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-xml-product-event.md +3578 -3578
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/graphql-mutations-guide.md +93 -93
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-payload-json-order-update-graphql.md +1260 -1260
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-payload-xml-order-update-graphql.md +1472 -1472
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-csv-control-graphql.md +2417 -2417
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-csv-location-graphql.md +2811 -2811
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-csv-price-graphql.md +2619 -2619
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-json-location-graphql.md +2807 -2807
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-xml-location-graphql.md +2373 -2373
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-sftp-csv-control-graphql.md +2740 -2740
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-sftp-csv-location-graphql.md +2760 -2760
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-sftp-json-location-graphql.md +1710 -1710
- package/docs/01-TEMPLATES/versori/workflows/ingestion/ingestion-workflows-guide.md +136 -136
- package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/rubix-webhooks-guide.md +520 -520
- package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/template-webhook-rubix-fulfilment-to-sftp-xml-inline.md +1418 -1418
- package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/template-webhook-rubix-fulfilment-to-sftp-xml-universal-mapper.md +1785 -1785
- package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/template-webhook-rubix-order-attribute-update.md +824 -824
- package/docs/01-TEMPLATES/versori/workflows/workflows-overview-guide.md +646 -646
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-batch-archival.md +724 -724
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-job-tracker.md +627 -627
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-partial-batch-recovery.md +561 -561
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-quick-reference.md +367 -367
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-readme.md +407 -407
- package/docs/02-CORE-GUIDES/advanced-services/readme.md +49 -49
- package/docs/02-CORE-GUIDES/api-reference/api-reference-quick-reference.md +548 -548
- package/docs/02-CORE-GUIDES/api-reference/event-api-input-output-reference.md +702 -1171
- package/docs/02-CORE-GUIDES/api-reference/examples/client-initialization.ts +286 -286
- package/docs/02-CORE-GUIDES/api-reference/graphql-error-classification.md +337 -337
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-01-client-api.md +399 -482
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-03-authentication.md +199 -199
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-04-graphql-mapping.md +925 -925
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-05-services.md +1198 -1198
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-06-data-sources.md +1083 -1083
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-07-parsers.md +1097 -1097
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-08-pagination.md +513 -513
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-08-types.md +545 -597
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-09-error-handling.md +527 -527
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-09-webhook-validation.md +514 -514
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-10-extraction.md +557 -557
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-10-utilities.md +412 -412
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-11-cli-tools.md +423 -423
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-11-error-handling.md +716 -716
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-analyze-source-structure.md +518 -518
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-partial-responses.md +212 -212
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-testing.md +300 -300
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-13-resolver-builder.md +322 -322
- package/docs/02-CORE-GUIDES/api-reference/readme.md +279 -279
- package/docs/02-CORE-GUIDES/auto-pagination/auto-pagination-quick-reference.md +351 -351
- package/docs/02-CORE-GUIDES/auto-pagination/auto-pagination-readme.md +277 -277
- package/docs/02-CORE-GUIDES/auto-pagination/examples/auto-pagination-readme.md +178 -178
- package/docs/02-CORE-GUIDES/auto-pagination/examples/common-patterns.ts +351 -351
- package/docs/02-CORE-GUIDES/auto-pagination/examples/paginate-products.ts +384 -384
- package/docs/02-CORE-GUIDES/auto-pagination/examples/paginate-virtual-positions.ts +308 -308
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-01-foundations.md +470 -470
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-02-quick-start.md +713 -713
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-03-configuration.md +754 -754
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-04-advanced-patterns.md +732 -732
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-05-sdk-integration.md +847 -847
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-06-troubleshooting.md +359 -359
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-07-api-reference.md +462 -462
- package/docs/02-CORE-GUIDES/auto-pagination/readme.md +54 -54
- package/docs/02-CORE-GUIDES/data-sources/data-sources-file-operations-error-handling.md +1487 -1487
- package/docs/02-CORE-GUIDES/data-sources/data-sources-quick-reference.md +836 -836
- package/docs/02-CORE-GUIDES/data-sources/data-sources-readme.md +276 -276
- package/docs/02-CORE-GUIDES/data-sources/data-sources-sftp-credential-access-security.md +553 -553
- package/docs/02-CORE-GUIDES/data-sources/examples/common-patterns.ts +409 -409
- package/docs/02-CORE-GUIDES/data-sources/examples/data-sources-readme.md +178 -178
- package/docs/02-CORE-GUIDES/data-sources/examples/s3-operations.ts +308 -308
- package/docs/02-CORE-GUIDES/data-sources/examples/sftp-operations.ts +371 -371
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-01-foundations.md +735 -735
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-02-s3-operations.md +1302 -1302
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-03-sftp-operations.md +1379 -1379
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-04-file-patterns.md +941 -941
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-05-advanced-topics.md +813 -813
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-06-integration-patterns.md +486 -486
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-07-troubleshooting.md +387 -387
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-08-api-reference.md +417 -417
- package/docs/02-CORE-GUIDES/data-sources/readme.md +77 -77
- package/docs/02-CORE-GUIDES/error-handling-guide.md +936 -936
- package/docs/02-CORE-GUIDES/extraction/examples/02-core-guides-extraction-readme.md +116 -116
- package/docs/02-CORE-GUIDES/extraction/examples/common-patterns.ts +428 -428
- package/docs/02-CORE-GUIDES/extraction/examples/extract-inventory-basic.ts +187 -187
- package/docs/02-CORE-GUIDES/extraction/extraction-quick-reference.md +596 -596
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-01-foundations.md +514 -514
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-02-basic-extraction.md +823 -823
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-03-parquet-processing.md +507 -507
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-04-data-enrichment.md +546 -546
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-05-transformation.md +494 -494
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-06-export-formats.md +458 -458
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-06-performance.md +138 -138
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-07-api-reference.md +148 -148
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-07-optimization.md +692 -692
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-08-extraction-orchestrator.md +1008 -1008
- package/docs/02-CORE-GUIDES/extraction/readme.md +151 -151
- package/docs/02-CORE-GUIDES/ingestion/examples/_simple-kv-store.ts +40 -40
- package/docs/02-CORE-GUIDES/ingestion/examples/error-recovery.ts +728 -728
- package/docs/02-CORE-GUIDES/ingestion/examples/event-driven.ts +501 -501
- package/docs/02-CORE-GUIDES/ingestion/examples/local-file-ingestion.ts +88 -88
- package/docs/02-CORE-GUIDES/ingestion/examples/parquet-ingestion.ts +117 -117
- package/docs/02-CORE-GUIDES/ingestion/examples/performance-optimized.ts +647 -647
- package/docs/02-CORE-GUIDES/ingestion/examples/s3-csv-ingestion.ts +169 -169
- package/docs/02-CORE-GUIDES/ingestion/examples/sftp-csv-ingestion.ts +134 -134
- package/docs/02-CORE-GUIDES/ingestion/ingestion-quick-reference.md +546 -546
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-01-introduction.md +626 -626
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-02-quick-start.md +658 -658
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-03-data-sources.md +1052 -1052
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-04-field-mapping.md +763 -763
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-05-advanced-parsers.md +676 -676
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-06-batch-api.md +1295 -1295
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-07-api-reference.md +138 -138
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-07-state-management.md +1037 -1037
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-08-performance-optimization.md +1349 -1349
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-09-best-practices.md +1893 -1893
- package/docs/02-CORE-GUIDES/ingestion/readme.md +160 -160
- package/docs/02-CORE-GUIDES/logging-guide.md +585 -585
- package/docs/02-CORE-GUIDES/mapping/error-handling-patterns.md +401 -401
- package/docs/02-CORE-GUIDES/mapping/examples/02-core-guides-mapping-readme.md +128 -128
- package/docs/02-CORE-GUIDES/mapping/examples/common-patterns.ts +273 -273
- package/docs/02-CORE-GUIDES/mapping/examples/csv-location-ingestion.json +36 -36
- package/docs/02-CORE-GUIDES/mapping/examples/csv-mapping.ts +242 -242
- package/docs/02-CORE-GUIDES/mapping/examples/graphql-to-parquet-extraction.json +36 -36
- package/docs/02-CORE-GUIDES/mapping/examples/json-mapping.ts +213 -213
- package/docs/02-CORE-GUIDES/mapping/examples/json-product-to-mutation.json +48 -48
- package/docs/02-CORE-GUIDES/mapping/examples/xml-mapping.ts +291 -291
- package/docs/02-CORE-GUIDES/mapping/examples/xml-order-to-mutation.json +45 -45
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/graphql-mutation-mapping-quick-reference.md +463 -463
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/graphql-mutation-mapping-readme.md +227 -227
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-01-introduction.md +222 -222
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-02-quick-start.md +351 -351
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-03-schema-validation.md +569 -569
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-04-mapping-patterns.md +471 -471
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-05-configuration-reference.md +611 -611
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-06-advanced-xpath.md +148 -148
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-06-path-syntax.md +464 -464
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-07-api-reference.md +94 -94
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-07-array-handling.md +307 -307
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-08-custom-resolvers.md +544 -544
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-09-advanced-patterns.md +427 -427
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-10-hooks-and-variables.md +336 -336
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-11-error-handling.md +488 -488
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-12-arguments-vs-nodes.md +383 -383
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-13-best-practices.md +477 -477
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/readme.md +62 -62
- package/docs/02-CORE-GUIDES/mapping/mapping-format-decision-tree.md +480 -480
- package/docs/02-CORE-GUIDES/mapping/mapping-graphql-alias-batching-guide.md +820 -820
- package/docs/02-CORE-GUIDES/mapping/mapping-javascript-objects.md +2369 -2369
- package/docs/02-CORE-GUIDES/mapping/mapping-mapper-comparison-guide.md +682 -682
- package/docs/02-CORE-GUIDES/mapping/modules/02-core-guides-mapping-07-api-reference.md +1327 -1327
- package/docs/02-CORE-GUIDES/mapping/modules/02-core-guides-mapping-08-error-handling.md +1142 -1142
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-04-use-cases.md +891 -891
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-06-helpers-resolvers.md +1126 -1126
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-06-sdk-resolvers.md +199 -199
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-07-api-reference.md +1319 -1319
- package/docs/02-CORE-GUIDES/mapping/readme.md +178 -178
- package/docs/02-CORE-GUIDES/mapping/resolver-registration.md +410 -410
- package/docs/02-CORE-GUIDES/mapping/resolvers/examples/common-patterns.ts +226 -226
- package/docs/02-CORE-GUIDES/mapping/resolvers/examples/custom-resolvers.ts +227 -227
- package/docs/02-CORE-GUIDES/mapping/resolvers/examples/sdk-resolvers-usage.ts +203 -203
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-readme.md +274 -274
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-api-reference.md +679 -679
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-cookbook.md +826 -826
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-guide.md +1330 -1330
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-helpers-reference.md +1437 -1437
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-parameters-reference.md +553 -553
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-troubleshooting.md +854 -854
- package/docs/02-CORE-GUIDES/mapping/resolvers/readme.md +75 -75
- package/docs/02-CORE-GUIDES/parsers/examples/02-core-guides-parsers-readme.md +161 -161
- package/docs/02-CORE-GUIDES/parsers/examples/csv-parser-examples.ts +110 -110
- package/docs/02-CORE-GUIDES/parsers/examples/json-parser-examples.ts +33 -33
- package/docs/02-CORE-GUIDES/parsers/examples/parquet-parser-examples.ts +47 -47
- package/docs/02-CORE-GUIDES/parsers/examples/xml-parser-examples.ts +38 -38
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-01-foundations.md +355 -355
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-02-csv-parser.md +772 -772
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-03-json-parser.md +789 -789
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-04-xml-parser.md +857 -857
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-05-parquet-parser.md +603 -603
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-06-integration-patterns.md +702 -702
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-06-streaming.md +121 -121
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-07-api-reference.md +89 -89
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-07-troubleshooting.md +727 -727
- package/docs/02-CORE-GUIDES/parsers/parsers-quick-reference.md +482 -482
- package/docs/02-CORE-GUIDES/parsers/parsers-readme.md +258 -258
- package/docs/02-CORE-GUIDES/parsers/readme.md +65 -65
- package/docs/02-CORE-GUIDES/readme.md +194 -194
- package/docs/02-CORE-GUIDES/webhook-validation/examples/basic-validation.ts +108 -108
- package/docs/02-CORE-GUIDES/webhook-validation/examples/common-patterns.ts +316 -316
- package/docs/02-CORE-GUIDES/webhook-validation/examples/webhook-validation-readme.md +61 -61
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-01-foundations.md +440 -440
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-02-quick-start.md +525 -525
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-03-versori-integration.md +741 -741
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-04-platform-integration.md +629 -629
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-05-configuration.md +535 -535
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-06-error-handling.md +611 -611
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-06-troubleshooting.md +124 -124
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-07-api-reference.md +511 -511
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-08-rubix-webhooks.md +590 -590
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-09-rubix-event-vs-http-call.md +432 -432
- package/docs/02-CORE-GUIDES/webhook-validation/readme.md +239 -239
- package/docs/02-CORE-GUIDES/webhook-validation/webhook-validation-quick-reference.md +392 -392
- package/docs/03-PATTERN-GUIDES/connector-scenarios/connector-scenarios-quick-reference.md +498 -498
- package/docs/03-PATTERN-GUIDES/connector-scenarios/connector-scenarios-readme.md +313 -313
- package/docs/03-PATTERN-GUIDES/connector-scenarios/examples/common-patterns.ts +612 -612
- package/docs/03-PATTERN-GUIDES/connector-scenarios/examples/connector-scenarios-readme.md +253 -253
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-01-foundations.md +452 -452
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-02-simple-scenarios.md +681 -681
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-03-intermediate-scenarios.md +637 -637
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-04-advanced-scenarios.md +650 -650
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-05-bidirectional-sync.md +233 -233
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-06-production-patterns.md +442 -442
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-07-reference.md +445 -445
- package/docs/03-PATTERN-GUIDES/connector-scenarios/readme.md +31 -31
- package/docs/03-PATTERN-GUIDES/enterprise-integration-patterns.md +1528 -1528
- package/docs/03-PATTERN-GUIDES/error-handling/comprehensive-error-handling-guide.md +1437 -1437
- package/docs/03-PATTERN-GUIDES/error-handling/error-handling-quick-reference.md +390 -390
- package/docs/03-PATTERN-GUIDES/error-handling/examples/common-patterns.ts +438 -438
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-01-foundations.md +362 -362
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-02-error-types.md +850 -850
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-03-utf8-handling.md +456 -456
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-04-error-scenarios.md +658 -658
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-05-calling-patterns.md +671 -671
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-06-retry-strategies.md +1034 -1034
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-07-monitoring.md +653 -653
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-08-api-reference.md +847 -847
- package/docs/03-PATTERN-GUIDES/error-handling/readme.md +36 -36
- package/docs/03-PATTERN-GUIDES/examples/__tests__/readme.md +40 -40
- package/docs/03-PATTERN-GUIDES/examples/__tests__/resolver-examples.test.js +282 -282
- package/docs/03-PATTERN-GUIDES/examples/test-data/03-pattern-guides-readme.md +110 -110
- package/docs/03-PATTERN-GUIDES/examples/test-data/canonical-inventory.json +123 -123
- package/docs/03-PATTERN-GUIDES/examples/test-data/canonical-order.json +171 -171
- package/docs/03-PATTERN-GUIDES/examples/test-data/readme.md +28 -28
- package/docs/03-PATTERN-GUIDES/extraction/extraction-readme.md +15 -15
- package/docs/03-PATTERN-GUIDES/extraction/readme.md +25 -25
- package/docs/03-PATTERN-GUIDES/file-operations/examples/common-patterns.ts +407 -407
- package/docs/03-PATTERN-GUIDES/file-operations/examples/file-operations-readme.md +142 -142
- package/docs/03-PATTERN-GUIDES/file-operations/file-operations-quick-reference.md +462 -462
- package/docs/03-PATTERN-GUIDES/file-operations/file-operations-readme.md +379 -379
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-01-foundations.md +430 -430
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-02-quick-start.md +484 -484
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-03-s3-operations.md +507 -507
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-04-sftp-operations.md +963 -963
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-05-streaming-performance.md +503 -503
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-06-archive-patterns.md +386 -386
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-06-error-handling.md +117 -117
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-07-api-reference.md +78 -78
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-07-testing-troubleshooting.md +567 -567
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-08-api-reference.md +1055 -1055
- package/docs/03-PATTERN-GUIDES/file-operations/readme.md +32 -32
- package/docs/03-PATTERN-GUIDES/ingestion/ingestion-readme.md +15 -15
- package/docs/03-PATTERN-GUIDES/ingestion/readme.md +25 -25
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/batch-processing.ts +130 -130
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/common-patterns.ts +360 -360
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/delta-sync.ts +130 -130
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/integration-patterns-readme.md +100 -100
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/real-time-webhook.ts +398 -398
- package/docs/03-PATTERN-GUIDES/integration-patterns/integration-patterns-quick-reference.md +962 -962
- package/docs/03-PATTERN-GUIDES/integration-patterns/integration-patterns-readme.md +134 -134
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-01-real-time-processing.md +991 -991
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-02-batch-processing.md +1547 -1547
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-03-delta-sync.md +1108 -1108
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-04-webhook-patterns.md +1181 -1181
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-05-error-handling.md +1061 -1061
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-06-advanced-integration-services.md +1547 -1547
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-06-performance.md +109 -109
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-07-api-reference.md +34 -34
- package/docs/03-PATTERN-GUIDES/integration-patterns/readme.md +30 -30
- package/docs/03-PATTERN-GUIDES/logging-minimal-mode.md +128 -128
- package/docs/03-PATTERN-GUIDES/multiple-connections/examples/common-patterns.ts +380 -380
- package/docs/03-PATTERN-GUIDES/multiple-connections/examples/multiple-connections-readme.md +139 -139
- package/docs/03-PATTERN-GUIDES/multiple-connections/examples/parallel-root-connections.ts +149 -149
- package/docs/03-PATTERN-GUIDES/multiple-connections/examples/real-world-scenarios.ts +405 -405
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-01-foundations.md +378 -378
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-02-quick-start.md +566 -566
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-03-targeting-connections.md +659 -659
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-04-parallel-queries.md +656 -656
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-05-best-practices.md +624 -624
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-06-api-reference.md +824 -824
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-06-versori.md +119 -119
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-07-api-reference.md +87 -87
- package/docs/03-PATTERN-GUIDES/multiple-connections/multiple-connections-quick-reference.md +353 -353
- package/docs/03-PATTERN-GUIDES/multiple-connections/multiple-connections-readme.md +270 -270
- package/docs/03-PATTERN-GUIDES/multiple-connections/readme.md +30 -30
- package/docs/03-PATTERN-GUIDES/pagination/pagination-readme.md +14 -14
- package/docs/03-PATTERN-GUIDES/pagination/readme.md +24 -24
- package/docs/03-PATTERN-GUIDES/parquet/examples/common-patterns.ts +180 -180
- package/docs/03-PATTERN-GUIDES/parquet/examples/read-parquet.ts +48 -48
- package/docs/03-PATTERN-GUIDES/parquet/examples/write-parquet.ts +65 -65
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-01-introduction.md +393 -393
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-02-quick-start.md +572 -572
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-03-reading-parquet.md +525 -525
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-04-writing-parquet.md +554 -554
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-05-graphql-extraction.md +405 -405
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-06-performance.md +104 -104
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-06-s3-integration.md +511 -511
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-07-api-reference.md +90 -90
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-07-performance-optimization.md +525 -525
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-08-best-practices.md +712 -712
- package/docs/03-PATTERN-GUIDES/parquet/parquet-quick-reference.md +683 -683
- package/docs/03-PATTERN-GUIDES/parquet/parquet-readme.md +248 -248
- package/docs/03-PATTERN-GUIDES/parquet/readme.md +32 -32
- package/docs/03-PATTERN-GUIDES/parsers/parsers-readme.md +12 -12
- package/docs/03-PATTERN-GUIDES/parsers/readme.md +24 -24
- package/docs/03-PATTERN-GUIDES/readme.md +159 -159
- package/docs/03-PATTERN-GUIDES/webhooks/readme.md +24 -24
- package/docs/03-PATTERN-GUIDES/webhooks/webhooks-readme.md +8 -8
- package/docs/04-REFERENCE/architecture/architecture-01-overview.md +427 -427
- package/docs/04-REFERENCE/architecture/architecture-02-client-architecture.md +424 -424
- package/docs/04-REFERENCE/architecture/architecture-03-data-flow.md +690 -690
- package/docs/04-REFERENCE/architecture/architecture-04-service-layer.md +834 -834
- package/docs/04-REFERENCE/architecture/architecture-05-integration-architecture.md +655 -655
- package/docs/04-REFERENCE/architecture/architecture-06-state-management.md +653 -653
- package/docs/04-REFERENCE/architecture/architecture-adding-new-data-sources.md +686 -686
- package/docs/04-REFERENCE/architecture/readme.md +279 -279
- package/docs/04-REFERENCE/platforms/deno/readme.md +117 -117
- package/docs/04-REFERENCE/platforms/nodejs/readme.md +146 -146
- package/docs/04-REFERENCE/platforms/readme.md +135 -135
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-01-introduction.md +398 -398
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-02-quick-start.md +560 -560
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-03-authentication.md +757 -757
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-04-workflows.md +2476 -2476
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-05-connections.md +1167 -1167
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-06-kv-storage.md +990 -990
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-06-state-management.md +121 -121
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-07-api-reference.md +68 -68
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-07-deployment.md +731 -731
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-08-best-practices.md +1111 -1111
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-09-signature-reference.md +766 -766
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-readme.md +299 -299
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-s3-sftp-configuration-guide.md +1425 -1425
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-webhook-api-key-security.md +816 -816
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-webhook-connection-security.md +681 -681
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-workflow-task-types.md +708 -708
- package/docs/04-REFERENCE/platforms/versori/readme.md +108 -108
- package/docs/04-REFERENCE/readme.md +148 -148
- package/docs/04-REFERENCE/resolver-signature/examples/advanced-resolvers.ts +482 -482
- package/docs/04-REFERENCE/resolver-signature/examples/async-resolvers.ts +496 -496
- package/docs/04-REFERENCE/resolver-signature/examples/basic-resolvers.ts +343 -343
- package/docs/04-REFERENCE/resolver-signature/examples/resolver-signature-readme.md +188 -188
- package/docs/04-REFERENCE/resolver-signature/examples/testing-resolvers.ts +463 -463
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-01-foundations.md +286 -286
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-02-parameter-reference.md +643 -643
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-03-basic-examples.md +521 -521
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-04-advanced-patterns.md +739 -739
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-05-sdk-resolvers.md +531 -531
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-06-migration-guide.md +650 -650
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-06-testing.md +125 -125
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-07-api-reference.md +794 -794
- package/docs/04-REFERENCE/resolver-signature/readme.md +64 -64
- package/docs/04-REFERENCE/resolver-signature/resolver-signature-quick-reference.md +270 -270
- package/docs/04-REFERENCE/resolver-signature/resolver-signature-readme.md +351 -351
- package/docs/04-REFERENCE/schema/fluent-commerce-schema.json +764 -764
- package/docs/04-REFERENCE/schema/readme.md +141 -141
- package/docs/04-REFERENCE/testing/examples/04-reference-testing-readme.md +158 -158
- package/docs/04-REFERENCE/testing/examples/fluent-testing.ts +62 -62
- package/docs/04-REFERENCE/testing/examples/health-check.ts +155 -155
- package/docs/04-REFERENCE/testing/examples/integration-test.ts +119 -119
- package/docs/04-REFERENCE/testing/examples/performance-test.ts +183 -183
- package/docs/04-REFERENCE/testing/examples/s3-testing.ts +127 -127
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-01-foundations.md +267 -267
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-02-s3-testing.md +599 -599
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-03-fluent-testing.md +589 -589
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-04-integration-testing.md +699 -699
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-05-debugging.md +478 -478
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-06-cicd-integration.md +463 -463
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-06-preflight-validation.md +131 -131
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-07-best-practices.md +499 -499
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-07-coverage-ci.md +165 -165
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-08-api-reference.md +634 -634
- package/docs/04-REFERENCE/testing/readme.md +86 -86
- package/docs/04-REFERENCE/testing/testing-quick-reference.md +667 -667
- package/docs/04-REFERENCE/testing/testing-readme.md +286 -286
- package/docs/04-REFERENCE/troubleshooting/readme.md +144 -144
- package/docs/04-REFERENCE/troubleshooting/troubleshooting-deno-sftp-compatibility.md +392 -392
- package/docs/template-loading-matrix.md +242 -242
- package/package.json +5 -3
|
@@ -1,1052 +1,1052 @@
|
|
|
1
|
-
# Module 3: Data Sources & Parsers
|
|
2
|
-
|
|
3
|
-
**Level:** Intermediate
|
|
4
|
-
**Estimated Time:** 30 minutes
|
|
5
|
-
|
|
6
|
-
## Overview
|
|
7
|
-
|
|
8
|
-
This module teaches you how to read inventory data from different sources (S3, SFTP) and parse various file formats (CSV, Parquet, XML, JSON) using the SDK's data source services.
|
|
9
|
-
|
|
10
|
-
**IMPORTANT:** Data sources in this SDK have integrated parsing capabilities. You don't need separate parser services - each data source handles its own file formats.
|
|
11
|
-
|
|
12
|
-
## Learning Objectives
|
|
13
|
-
|
|
14
|
-
By the end of this module, you will:
|
|
15
|
-
- ✅ Understand the data source abstraction pattern
|
|
16
|
-
- ✅ Configure S3DataSource for AWS S3 buckets with retry logic
|
|
17
|
-
- ✅ Configure SftpDataSource with connection pooling and retry
|
|
18
|
-
- ✅ Parse CSV, JSON, and Parquet files directly from data sources
|
|
19
|
-
- ✅ Handle large files with streaming and batching
|
|
20
|
-
- ✅ Choose the right data source configuration for your use case
|
|
21
|
-
|
|
22
|
-
## Data Source Abstraction
|
|
23
|
-
|
|
24
|
-
The SDK provides data sources that handle both file operations AND parsing:
|
|
25
|
-
|
|
26
|
-
```typescript
|
|
27
|
-
import { S3DataSource, SftpDataSource } from '@fluentcommerce/fc-connect-sdk';
|
|
28
|
-
import { createConsoleLogger } from '@fluentcommerce/fc-connect-sdk';
|
|
29
|
-
|
|
30
|
-
// Each data source is a complete file + parsing solution
|
|
31
|
-
// No need for separate parser services
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
### Benefits
|
|
35
|
-
|
|
36
|
-
| Benefit | Description |
|
|
37
|
-
|---------|-------------|
|
|
38
|
-
| **Integrated** | File operations and parsing in one service |
|
|
39
|
-
| **Retry Logic** | Automatic exponential backoff for transient failures |
|
|
40
|
-
| **Connection Pooling** | (SFTP only) Reuse connections for performance |
|
|
41
|
-
| **Format Support** | CSV, JSON, XML, Parquet, JSONL |
|
|
42
|
-
| **Testability** | Mock data sources for unit testing |
|
|
43
|
-
|
|
44
|
-
## S3 Data Source
|
|
45
|
-
|
|
46
|
-
### Configuration
|
|
47
|
-
|
|
48
|
-
```typescript
|
|
49
|
-
import { S3DataSource } from '@fluentcommerce/fc-connect-sdk';
|
|
50
|
-
import { createConsoleLogger } from '@fluentcommerce/fc-connect-sdk';
|
|
51
|
-
|
|
52
|
-
const logger = createConsoleLogger();
|
|
53
|
-
|
|
54
|
-
const s3 = new S3DataSource(
|
|
55
|
-
{
|
|
56
|
-
type: 'S3_CSV', // or 'S3_JSON', 'S3_XML', 'S3_PARQUET'
|
|
57
|
-
name: 'S3 Inventory Source',
|
|
58
|
-
connectionId: 's3-prod',
|
|
59
|
-
s3Config: {
|
|
60
|
-
bucket: 'inventory-bucket',
|
|
61
|
-
region: 'us-east-1',
|
|
62
|
-
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
63
|
-
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
64
|
-
|
|
65
|
-
// Optional: Session token for temporary credentials
|
|
66
|
-
sessionToken: process.env.AWS_SESSION_TOKEN,
|
|
67
|
-
|
|
68
|
-
// Optional: Retry configuration
|
|
69
|
-
maxAttempts: 3 // Default: 3 attempts with exponential backoff
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
logger
|
|
73
|
-
);
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### Basic Operations
|
|
77
|
-
|
|
78
|
-
```typescript
|
|
79
|
-
// List files with filtering
|
|
80
|
-
const files = await s3.listFiles({
|
|
81
|
-
prefix: 'data/', // Filter by prefix
|
|
82
|
-
delimiter: '/', // Group by "directory"
|
|
83
|
-
maxKeys: 1000, // Max results per page
|
|
84
|
-
continuationToken: token // For pagination
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
console.log(`Found ${files.length} files`);
|
|
88
|
-
files.forEach(file => {
|
|
89
|
-
console.log(` ${file.path} (${file.size} bytes, ${file.lastModified})`);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
// Download file
|
|
93
|
-
const content = await s3.downloadFile('data/inventory-2024-01-15.csv', {
|
|
94
|
-
encoding: 'utf8' // or 'binary' for Buffer
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// Download binary files (images, Parquet, etc.)
|
|
98
|
-
const binaryData = await s3.downloadFile('data/inventory.parquet', {
|
|
99
|
-
encoding: 'binary'
|
|
100
|
-
});
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### Retry Configuration
|
|
104
|
-
|
|
105
|
-
S3DataSource includes automatic retry with exponential backoff for transient failures:
|
|
106
|
-
|
|
107
|
-
```typescript
|
|
108
|
-
const s3 = new S3DataSource(
|
|
109
|
-
{
|
|
110
|
-
type: 'S3_CSV',
|
|
111
|
-
name: 'S3 with Custom Retry',
|
|
112
|
-
s3Config: {
|
|
113
|
-
bucket: 'inventory-bucket',
|
|
114
|
-
region: 'us-east-1',
|
|
115
|
-
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
116
|
-
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
117
|
-
|
|
118
|
-
// Retry configuration
|
|
119
|
-
maxAttempts: 5 // Try up to 5 times (default: 3)
|
|
120
|
-
}
|
|
121
|
-
},
|
|
122
|
-
logger
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
// Retry behavior:
|
|
126
|
-
// - Attempt 1: Immediate
|
|
127
|
-
// - Attempt 2: 500ms delay
|
|
128
|
-
// - Attempt 3: 1000ms delay
|
|
129
|
-
// - Attempt 4: 2000ms delay
|
|
130
|
-
// - Attempt 5: 4000ms delay
|
|
131
|
-
// - Max delay: 8000ms
|
|
132
|
-
|
|
133
|
-
// Retries on: 429, 500, 502, 503, 504 status codes
|
|
134
|
-
// Retries on: Network errors (ECONNRESET, ETIMEDOUT, etc.)
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### Upload Operations
|
|
138
|
-
|
|
139
|
-
```typescript
|
|
140
|
-
// Upload text file
|
|
141
|
-
await s3.uploadFile(
|
|
142
|
-
'processed/inventory-2024-01-15.csv',
|
|
143
|
-
csvContent,
|
|
144
|
-
{
|
|
145
|
-
contentType: 'text/csv',
|
|
146
|
-
metadata: {
|
|
147
|
-
processedAt: new Date().toISOString(),
|
|
148
|
-
source: 'inventory-sync'
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
);
|
|
152
|
-
|
|
153
|
-
// Upload binary file
|
|
154
|
-
await s3.uploadFile(
|
|
155
|
-
'data/inventory.parquet',
|
|
156
|
-
parquetBuffer,
|
|
157
|
-
{
|
|
158
|
-
contentType: 'application/octet-stream'
|
|
159
|
-
}
|
|
160
|
-
);
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
### File Management
|
|
164
|
-
|
|
165
|
-
```typescript
|
|
166
|
-
// Copy file
|
|
167
|
-
await s3.copyFile(
|
|
168
|
-
'data/inventory.csv',
|
|
169
|
-
'backup/inventory.csv',
|
|
170
|
-
{
|
|
171
|
-
sourceBucket: 'source-bucket', // Optional: cross-bucket copy
|
|
172
|
-
metadata: { backupDate: new Date().toISOString() }
|
|
173
|
-
}
|
|
174
|
-
);
|
|
175
|
-
|
|
176
|
-
// Move file within same bucket (copy + delete)
|
|
177
|
-
await s3.moveFile(
|
|
178
|
-
'temp/inventory.csv',
|
|
179
|
-
'archive/inventory.csv'
|
|
180
|
-
// Optional: pass false to copy without deleting source
|
|
181
|
-
// await s3.moveFile('temp/inventory.csv', 'archive/inventory.csv', false);
|
|
182
|
-
);
|
|
183
|
-
|
|
184
|
-
// Archive file with automatic timestamping
|
|
185
|
-
const archivePath = await s3.archiveFile(
|
|
186
|
-
'data/inventory.csv',
|
|
187
|
-
'archive/2024-01/' // Archive prefix (timestamp + filename added automatically)
|
|
188
|
-
);
|
|
189
|
-
console.log(`File archived to: ${archivePath}`);
|
|
190
|
-
// Returns: 'archive/2024-01/2025-10-29T12-30-45-123Z_inventory.csv'
|
|
191
|
-
|
|
192
|
-
// For cross-bucket moves, use copyFile() + deleteFile() directly
|
|
193
|
-
await s3.copyFile('inventory.csv', 'backup/inventory.csv', {
|
|
194
|
-
sourceBucket: 'source-bucket',
|
|
195
|
-
metadata: { archivedAt: new Date().toISOString() }
|
|
196
|
-
});
|
|
197
|
-
await s3.deleteFile('inventory.csv');
|
|
198
|
-
|
|
199
|
-
// Delete file
|
|
200
|
-
await s3.deleteFile('temp/old-file.csv');
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
### Parquet Support
|
|
204
|
-
|
|
205
|
-
S3DataSource has built-in Parquet writing support:
|
|
206
|
-
|
|
207
|
-
```typescript
|
|
208
|
-
// Write records as Parquet file
|
|
209
|
-
const records = [
|
|
210
|
-
{ sku: 'ABC123', warehouse: 'WH1', quantity: 100 },
|
|
211
|
-
{ sku: 'DEF456', warehouse: 'WH2', quantity: 50 }
|
|
212
|
-
];
|
|
213
|
-
|
|
214
|
-
const parquetBuffer = await s3.writeParquetContent(records, {
|
|
215
|
-
compression: 'GZIP', // or 'SNAPPY', 'BROTLI', 'UNCOMPRESSED'
|
|
216
|
-
rowGroupSize: 5000, // Records per row group
|
|
217
|
-
schema: { // Optional: explicit schema
|
|
218
|
-
sku: { type: 'UTF8' },
|
|
219
|
-
warehouse: { type: 'UTF8' },
|
|
220
|
-
quantity: { type: 'INT64' }
|
|
221
|
-
}
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
// Upload Parquet file
|
|
225
|
-
await s3.uploadFile(
|
|
226
|
-
'data/inventory.parquet',
|
|
227
|
-
parquetBuffer,
|
|
228
|
-
{ contentType: 'application/octet-stream' }
|
|
229
|
-
);
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
### Connection Validation
|
|
233
|
-
|
|
234
|
-
```typescript
|
|
235
|
-
// Validate S3 connection before processing
|
|
236
|
-
const isValid = await s3.validateConnection();
|
|
237
|
-
if (!isValid) {
|
|
238
|
-
throw new Error('Failed to connect to S3');
|
|
239
|
-
}
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
### Complete S3 Example
|
|
243
|
-
|
|
244
|
-
```typescript
|
|
245
|
-
import { S3DataSource } from '@fluentcommerce/fc-connect-sdk';
|
|
246
|
-
import { createConsoleLogger } from '@fluentcommerce/fc-connect-sdk';
|
|
247
|
-
|
|
248
|
-
async function processS3Files() {
|
|
249
|
-
const logger = createConsoleLogger();
|
|
250
|
-
|
|
251
|
-
const s3 = new S3DataSource(
|
|
252
|
-
{
|
|
253
|
-
type: 'S3_CSV',
|
|
254
|
-
name: 'Inventory Source',
|
|
255
|
-
s3Config: {
|
|
256
|
-
bucket: 'inventory-bucket',
|
|
257
|
-
region: 'us-east-1',
|
|
258
|
-
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
259
|
-
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
260
|
-
maxAttempts: 3
|
|
261
|
-
}
|
|
262
|
-
},
|
|
263
|
-
logger
|
|
264
|
-
);
|
|
265
|
-
|
|
266
|
-
try {
|
|
267
|
-
// Validate connection
|
|
268
|
-
await s3.validateConnection();
|
|
269
|
-
|
|
270
|
-
// List files
|
|
271
|
-
const files = await s3.listFiles({ prefix: 'incoming/' });
|
|
272
|
-
|
|
273
|
-
for (const file of files) {
|
|
274
|
-
logger.info(`Processing: ${file.path} (${file.size} bytes)`);
|
|
275
|
-
|
|
276
|
-
// Download file
|
|
277
|
-
const content = await s3.downloadFile(file.path);
|
|
278
|
-
|
|
279
|
-
// Process content...
|
|
280
|
-
// (Use CSVParserService or other parsers as needed)
|
|
281
|
-
|
|
282
|
-
// Archive processed file (timestamp added automatically)
|
|
283
|
-
const archivedPath = await s3.archiveFile(
|
|
284
|
-
file.path,
|
|
285
|
-
`archive/${new Date().toISOString().split('T')[0]}/` // Just the prefix
|
|
286
|
-
);
|
|
287
|
-
logger.info(`Archived to: ${archivedPath}`);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
logger.info('Processing complete');
|
|
291
|
-
} catch (error) {
|
|
292
|
-
logger.error('Processing failed', error);
|
|
293
|
-
throw error;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
## SFTP Data Source
|
|
299
|
-
|
|
300
|
-
### Configuration
|
|
301
|
-
|
|
302
|
-
```typescript
|
|
303
|
-
import { SftpDataSource } from '@fluentcommerce/fc-connect-sdk';
|
|
304
|
-
import { createConsoleLogger } from '@fluentcommerce/fc-connect-sdk';
|
|
305
|
-
import * as fs from 'fs';
|
|
306
|
-
|
|
307
|
-
const logger = createConsoleLogger();
|
|
308
|
-
|
|
309
|
-
const sftp = new SftpDataSource(
|
|
310
|
-
{
|
|
311
|
-
type: 'SFTP_CSV', // or 'SFTP_JSON', 'SFTP_XML', 'SFTP_JSONL', 'SFTP_PARQUET'
|
|
312
|
-
name: 'SFTP Inventory Source',
|
|
313
|
-
connectionId: 'sftp-prod',
|
|
314
|
-
settings: {
|
|
315
|
-
host: 'sftp.example.com',
|
|
316
|
-
port: 22,
|
|
317
|
-
remotePath: '/data/inventory',
|
|
318
|
-
filePattern: '*.csv',
|
|
319
|
-
|
|
320
|
-
// Authentication: password OR private key
|
|
321
|
-
username: process.env.SFTP_USERNAME!,
|
|
322
|
-
password: process.env.SFTP_PASSWORD,
|
|
323
|
-
|
|
324
|
-
// OR use private key
|
|
325
|
-
privateKey: fs.readFileSync('/path/to/key.pem', 'utf8'),
|
|
326
|
-
passphrase: process.env.SFTP_KEY_PASSPHRASE,
|
|
327
|
-
|
|
328
|
-
// Connection pooling (NEW in v0.1.11+)
|
|
329
|
-
maxConnections: 5, // Default: 5
|
|
330
|
-
|
|
331
|
-
// Retry configuration (NEW in v0.1.11+)
|
|
332
|
-
retry: {
|
|
333
|
-
maxAttempts: 3, // Default: 3
|
|
334
|
-
baseDelayMs: 1000, // Default: 1000ms
|
|
335
|
-
backoffFactor: 2, // Default: 2 (exponential)
|
|
336
|
-
maxDelayMs: 8000, // Default: 8000ms
|
|
337
|
-
jitter: 'full', // 'full' or 'none'
|
|
338
|
-
reconnectOnFailure: true // Reconnect on failure
|
|
339
|
-
},
|
|
340
|
-
|
|
341
|
-
// CSV parsing settings
|
|
342
|
-
csvDelimiter: ',',
|
|
343
|
-
csvHeaders: ['sku', 'warehouse', 'quantity'],
|
|
344
|
-
requiredCsvHeaders: ['sku', 'quantity'],
|
|
345
|
-
csvSkipEmptyLines: true,
|
|
346
|
-
csvTrimValues: true,
|
|
347
|
-
|
|
348
|
-
// JSON parsing settings
|
|
349
|
-
jsonFormat: 'json', // or 'jsonl'
|
|
350
|
-
jsonValidate: true,
|
|
351
|
-
jsonMaxDepth: 10,
|
|
352
|
-
|
|
353
|
-
// Parquet settings
|
|
354
|
-
parquetCompression: 'gzip'
|
|
355
|
-
}
|
|
356
|
-
},
|
|
357
|
-
logger
|
|
358
|
-
);
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
### Connection Pooling (NEW in v0.1.11+)
|
|
362
|
-
|
|
363
|
-
SftpDataSource automatically manages a connection pool for performance:
|
|
364
|
-
|
|
365
|
-
```typescript
|
|
366
|
-
// Connection pool behavior:
|
|
367
|
-
// 1. Reuses existing connections when available
|
|
368
|
-
// 2. Creates new connections up to maxConnections limit
|
|
369
|
-
// 3. Waits in queue when pool is full (30s timeout)
|
|
370
|
-
// 4. Validates connection health before reuse
|
|
371
|
-
// 5. Auto-reconnects on transient failures
|
|
372
|
-
|
|
373
|
-
const sftp = new SftpDataSource(
|
|
374
|
-
{
|
|
375
|
-
type: 'SFTP_CSV',
|
|
376
|
-
name: 'SFTP with Pool',
|
|
377
|
-
settings: {
|
|
378
|
-
host: 'sftp.example.com',
|
|
379
|
-
username: process.env.SFTP_USERNAME!,
|
|
380
|
-
password: process.env.SFTP_PASSWORD!,
|
|
381
|
-
remotePath: '/data',
|
|
382
|
-
filePattern: '*.csv',
|
|
383
|
-
|
|
384
|
-
// Pool configuration
|
|
385
|
-
maxConnections: 10, // Allow up to 10 concurrent connections
|
|
386
|
-
connectionTimeout: 10000, // 10s connection timeout
|
|
387
|
-
keepAliveInterval: 10000 // Keep-alive every 10s
|
|
388
|
-
}
|
|
389
|
-
},
|
|
390
|
-
logger
|
|
391
|
-
);
|
|
392
|
-
|
|
393
|
-
// Connection pool is transparent - all operations use it automatically
|
|
394
|
-
const files = await sftp.listFiles(); // Uses pool
|
|
395
|
-
const content = await sftp.downloadFile(files[0].name); // Uses pool
|
|
396
|
-
|
|
397
|
-
// Get pool stats for monitoring
|
|
398
|
-
const info = sftp.getConnectionInfo();
|
|
399
|
-
console.log(`Pool size: ${info.connectionPoolSize}/${info.maxPoolSize}`);
|
|
400
|
-
```
|
|
401
|
-
|
|
402
|
-
### Enhanced Retry Logic (NEW in v0.1.11+)
|
|
403
|
-
|
|
404
|
-
SFTP operations automatically retry with exponential backoff:
|
|
405
|
-
|
|
406
|
-
```typescript
|
|
407
|
-
const sftp = new SftpDataSource(
|
|
408
|
-
{
|
|
409
|
-
type: 'SFTP_CSV',
|
|
410
|
-
name: 'SFTP with Custom Retry',
|
|
411
|
-
settings: {
|
|
412
|
-
host: 'sftp.example.com',
|
|
413
|
-
username: process.env.SFTP_USERNAME!,
|
|
414
|
-
password: process.env.SFTP_PASSWORD!,
|
|
415
|
-
remotePath: '/data',
|
|
416
|
-
filePattern: '*.csv',
|
|
417
|
-
|
|
418
|
-
retry: {
|
|
419
|
-
maxAttempts: 5, // Try up to 5 times
|
|
420
|
-
baseDelayMs: 2000, // Start with 2s delay
|
|
421
|
-
backoffFactor: 2, // Double delay each attempt
|
|
422
|
-
maxDelayMs: 16000, // Cap at 16s
|
|
423
|
-
jitter: 'full', // Random jitter (0 to maxDelay)
|
|
424
|
-
reconnectOnFailure: true, // Clear pool and reconnect
|
|
425
|
-
|
|
426
|
-
// Custom retry predicate
|
|
427
|
-
isRetryable: (error: Error) => {
|
|
428
|
-
// Add custom logic for retryable errors
|
|
429
|
-
return error.message.includes('timeout') ||
|
|
430
|
-
error.message.includes('connection');
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
},
|
|
435
|
-
logger
|
|
436
|
-
);
|
|
437
|
-
|
|
438
|
-
// Retry timeline with above config:
|
|
439
|
-
// - Attempt 1: Immediate
|
|
440
|
-
// - Attempt 2: Random(0-2000ms) delay
|
|
441
|
-
// - Attempt 3: Random(0-4000ms) delay
|
|
442
|
-
// - Attempt 4: Random(0-8000ms) delay
|
|
443
|
-
// - Attempt 5: Random(0-16000ms) delay
|
|
444
|
-
|
|
445
|
-
// Automatically retries on:
|
|
446
|
-
// - ECONNRESET, ETIMEDOUT, ENOTFOUND, EHOSTUNREACH, ECONNREFUSED
|
|
447
|
-
// - "connection lost", "handshake failed", "timeout", etc.
|
|
448
|
-
//
|
|
449
|
-
// Does NOT retry on:
|
|
450
|
-
// - Authentication failures
|
|
451
|
-
// - Permission denied
|
|
452
|
-
// - File not found
|
|
453
|
-
// - Algorithm/cipher/key exchange errors
|
|
454
|
-
```
|
|
455
|
-
|
|
456
|
-
### Basic Operations
|
|
457
|
-
|
|
458
|
-
```typescript
|
|
459
|
-
// List files with filtering
|
|
460
|
-
const files = await sftp.listFiles({
|
|
461
|
-
remotePath: '/data/inventory', // Optional: override config
|
|
462
|
-
filePattern: '*.csv', // Optional: override config
|
|
463
|
-
lastProcessedTimestamp: '2024-01-15T00:00:00Z', // Incremental sync
|
|
464
|
-
fileName: 'inventory' // Filter by name substring
|
|
465
|
-
});
|
|
466
|
-
|
|
467
|
-
console.log(`Found ${files.length} files`);
|
|
468
|
-
|
|
469
|
-
// Download file
|
|
470
|
-
const content = await sftp.downloadFile('/data/inventory.csv', {
|
|
471
|
-
encoding: 'utf8', // Default encoding
|
|
472
|
-
asBuffer: false, // Return string
|
|
473
|
-
maxSize: 100 * 1024 * 1024 // 100MB limit
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
// Download as buffer
|
|
477
|
-
const buffer = await sftp.downloadFile('/data/inventory.parquet', {
|
|
478
|
-
asBuffer: true
|
|
479
|
-
});
|
|
480
|
-
```
|
|
481
|
-
|
|
482
|
-
### Integrated CSV Parsing
|
|
483
|
-
|
|
484
|
-
SFTP data source has built-in CSV parsing:
|
|
485
|
-
|
|
486
|
-
```typescript
|
|
487
|
-
// Download and parse CSV in one step
|
|
488
|
-
const csvContent = await sftp.downloadFile('/data/inventory.csv');
|
|
489
|
-
|
|
490
|
-
const records = sftp.parseCsvContent(csvContent, {
|
|
491
|
-
name: 'inventory.csv',
|
|
492
|
-
path: '/data/inventory.csv',
|
|
493
|
-
size: csvContent.length,
|
|
494
|
-
source: 'sftp'
|
|
495
|
-
});
|
|
496
|
-
|
|
497
|
-
console.log(`Parsed ${records.length} records`);
|
|
498
|
-
records.forEach(record => {
|
|
499
|
-
console.log(`SKU: ${record.sku}, Qty: ${record.quantity}`);
|
|
500
|
-
});
|
|
501
|
-
```
|
|
502
|
-
|
|
503
|
-
### Integrated JSON Parsing
|
|
504
|
-
|
|
505
|
-
```typescript
|
|
506
|
-
// Download and parse JSON
|
|
507
|
-
const jsonContent = await sftp.downloadFile('/data/inventory.json');
|
|
508
|
-
|
|
509
|
-
const records = sftp.parseJsonContent(
|
|
510
|
-
jsonContent,
|
|
511
|
-
{ name: 'inventory.json', path: '/data/inventory.json', source: 'sftp' },
|
|
512
|
-
{
|
|
513
|
-
format: 'json', // or 'jsonl' for JSON Lines
|
|
514
|
-
validate: true, // Validate structure
|
|
515
|
-
maxDepth: 10 // Limit nesting depth
|
|
516
|
-
}
|
|
517
|
-
);
|
|
518
|
-
|
|
519
|
-
console.log(`Parsed ${records.length} JSON records`);
|
|
520
|
-
```
|
|
521
|
-
|
|
522
|
-
### Writing Files
|
|
523
|
-
|
|
524
|
-
```typescript
|
|
525
|
-
// Write CSV file
|
|
526
|
-
const records = [
|
|
527
|
-
{ sku: 'ABC123', warehouse: 'WH1', quantity: 100 },
|
|
528
|
-
{ sku: 'DEF456', warehouse: 'WH2', quantity: 50 }
|
|
529
|
-
];
|
|
530
|
-
|
|
531
|
-
const csvContent = sftp.writeCsvContent(records, {
|
|
532
|
-
delimiter: ',',
|
|
533
|
-
includeHeader: true,
|
|
534
|
-
quote: '"',
|
|
535
|
-
escape: '"'
|
|
536
|
-
});
|
|
537
|
-
|
|
538
|
-
await sftp.uploadFile('/data/output.csv', {
|
|
539
|
-
overwrite: true,
|
|
540
|
-
createDirectories: true
|
|
541
|
-
}, csvContent);
|
|
542
|
-
|
|
543
|
-
// Write JSON file
|
|
544
|
-
const jsonContent = sftp.writeJsonContent(records, {
|
|
545
|
-
format: 'json', // or 'jsonl'
|
|
546
|
-
indent: 2,
|
|
547
|
-
sortKeys: true
|
|
548
|
-
});
|
|
549
|
-
|
|
550
|
-
await sftp.uploadFile('/data/output.json', {
|
|
551
|
-
overwrite: true
|
|
552
|
-
}, jsonContent);
|
|
553
|
-
|
|
554
|
-
// Write Parquet file
|
|
555
|
-
const parquetBuffer = await sftp.writeParquetContent(records, {
|
|
556
|
-
compression: 'GZIP',
|
|
557
|
-
rowGroupSize: 5000
|
|
558
|
-
});
|
|
559
|
-
|
|
560
|
-
await sftp.uploadFile('/data/output.parquet', {
|
|
561
|
-
overwrite: true
|
|
562
|
-
}, parquetBuffer);
|
|
563
|
-
```
|
|
564
|
-
|
|
565
|
-
### File Management
|
|
566
|
-
|
|
567
|
-
```typescript
|
|
568
|
-
// Upload file from local path
|
|
569
|
-
await sftp.uploadFile(
|
|
570
|
-
'/local/path/inventory.csv',
|
|
571
|
-
'/remote/path/inventory.csv',
|
|
572
|
-
{
|
|
573
|
-
overwrite: false,
|
|
574
|
-
createDirectories: true,
|
|
575
|
-
permissions: '0644'
|
|
576
|
-
}
|
|
577
|
-
);
|
|
578
|
-
|
|
579
|
-
// Create directory
|
|
580
|
-
await sftp.createDirectory('/data/archive', true); // recursive
|
|
581
|
-
|
|
582
|
-
// Move file
|
|
583
|
-
await sftp.moveFile(
|
|
584
|
-
'/data/inventory.csv',
|
|
585
|
-
'/data/processed/inventory.csv'
|
|
586
|
-
);
|
|
587
|
-
|
|
588
|
-
// Delete file
|
|
589
|
-
await sftp.deleteFile('/data/old-file.csv');
|
|
590
|
-
|
|
591
|
-
// Check if file exists
|
|
592
|
-
const exists = await sftp.fileExists('/data/inventory.csv');
|
|
593
|
-
if (exists) {
|
|
594
|
-
console.log('File exists');
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
// Check if directory exists
|
|
598
|
-
const dirExists = await sftp.directoryExists('/data/archive');
|
|
599
|
-
```
|
|
600
|
-
|
|
601
|
-
### Connection Monitoring
|
|
602
|
-
|
|
603
|
-
```typescript
|
|
604
|
-
// Validate connection
|
|
605
|
-
const isValid = await sftp.validateConnection();
|
|
606
|
-
if (!isValid) {
|
|
607
|
-
throw new Error('SFTP connection failed');
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
// Get connection pool info
|
|
611
|
-
const info = sftp.getConnectionInfo();
|
|
612
|
-
console.log(`Connection Info:
|
|
613
|
-
Type: ${info.type}
|
|
614
|
-
Host: ${info.host}:${info.port}
|
|
615
|
-
Remote Path: ${info.remotePath}
|
|
616
|
-
File Pattern: ${info.filePattern}
|
|
617
|
-
Pool: ${info.connectionPoolSize}/${info.maxPoolSize} connections
|
|
618
|
-
`);
|
|
619
|
-
```
|
|
620
|
-
|
|
621
|
-
### Complete SFTP Example
|
|
622
|
-
|
|
623
|
-
```typescript
|
|
624
|
-
import { SftpDataSource } from '@fluentcommerce/fc-connect-sdk';
|
|
625
|
-
import { createConsoleLogger } from '@fluentcommerce/fc-connect-sdk';
|
|
626
|
-
|
|
627
|
-
async function processSftpFiles() {
|
|
628
|
-
const logger = createConsoleLogger();
|
|
629
|
-
|
|
630
|
-
const sftp = new SftpDataSource(
|
|
631
|
-
{
|
|
632
|
-
type: 'SFTP_CSV',
|
|
633
|
-
name: 'SFTP Inventory',
|
|
634
|
-
settings: {
|
|
635
|
-
host: process.env.SFTP_HOST!,
|
|
636
|
-
username: process.env.SFTP_USERNAME!,
|
|
637
|
-
password: process.env.SFTP_PASSWORD!,
|
|
638
|
-
remotePath: '/data/inventory',
|
|
639
|
-
filePattern: '*.csv',
|
|
640
|
-
|
|
641
|
-
// Enable connection pooling and retry
|
|
642
|
-
maxConnections: 5,
|
|
643
|
-
retry: {
|
|
644
|
-
maxAttempts: 3,
|
|
645
|
-
reconnectOnFailure: true
|
|
646
|
-
},
|
|
647
|
-
|
|
648
|
-
// CSV parsing settings
|
|
649
|
-
csvDelimiter: ',',
|
|
650
|
-
requiredCsvHeaders: ['sku', 'quantity'],
|
|
651
|
-
csvSkipEmptyLines: true
|
|
652
|
-
}
|
|
653
|
-
},
|
|
654
|
-
logger
|
|
655
|
-
);
|
|
656
|
-
|
|
657
|
-
try {
|
|
658
|
-
// Validate connection
|
|
659
|
-
await sftp.validateConnection();
|
|
660
|
-
|
|
661
|
-
// List files
|
|
662
|
-
const files = await sftp.listFiles();
|
|
663
|
-
logger.info(`Found ${files.length} files`);
|
|
664
|
-
|
|
665
|
-
for (const file of files) {
|
|
666
|
-
logger.info(`Processing: ${file.name}`);
|
|
667
|
-
|
|
668
|
-
// Download file
|
|
669
|
-
const csvContent = await sftp.downloadFile(file.path);
|
|
670
|
-
|
|
671
|
-
// Parse CSV (integrated parsing)
|
|
672
|
-
const records = sftp.parseCsvContent(csvContent, file);
|
|
673
|
-
logger.info(`Parsed ${records.length} records`);
|
|
674
|
-
|
|
675
|
-
// Process records...
|
|
676
|
-
// (Send to Fluent Commerce, etc.)
|
|
677
|
-
|
|
678
|
-
// Move to processed folder
|
|
679
|
-
await sftp.moveFile(
|
|
680
|
-
file.path,
|
|
681
|
-
`/data/processed/${file.name}`
|
|
682
|
-
);
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
// Get final pool stats
|
|
686
|
-
const info = sftp.getConnectionInfo();
|
|
687
|
-
logger.info(`Pool: ${info.connectionPoolSize}/${info.maxPoolSize}`);
|
|
688
|
-
|
|
689
|
-
} catch (error) {
|
|
690
|
-
logger.error('Processing failed', error);
|
|
691
|
-
throw error;
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
```
|
|
695
|
-
|
|
696
|
-
## Inventory Data Source (Base Interface)
|
|
697
|
-
|
|
698
|
-
**Note:** `InventoryDataSource` is an abstract base interface for inventory-specific operations. Use the concrete implementations `S3DataSource` or `SftpDataSource` directly.
|
|
699
|
-
|
|
700
|
-
```typescript
|
|
701
|
-
import { InventoryDataSource, BaseInventoryDataSource } from '@fluentcommerce/fc-connect-sdk';
|
|
702
|
-
|
|
703
|
-
// This is an interface, not a standalone data source
|
|
704
|
-
interface InventoryDataSource {
|
|
705
|
-
getBucketName(): string;
|
|
706
|
-
getCsvFilePrefix(): string;
|
|
707
|
-
parseS3ListResponse(xmlResponse: string, prefix: string, lastModifiedAfter?: string): FileMetadata[];
|
|
708
|
-
getConfig(): DataSourceConfig;
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
// Base implementation
|
|
712
|
-
class BaseInventoryDataSource implements InventoryDataSource {
|
|
713
|
-
constructor(protected config: DataSourceConfig) {}
|
|
714
|
-
|
|
715
|
-
getBucketName(): string {
|
|
716
|
-
return 'default-bucket';
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
getCsvFilePrefix(): string {
|
|
720
|
-
return 'inventory/';
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
parseS3ListResponse(xmlResponse: string, prefix: string, lastModifiedAfter?: string): FileMetadata[] {
|
|
724
|
-
// Simple XML parsing for S3 list response
|
|
725
|
-
// ...
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
getConfig(): DataSourceConfig {
|
|
729
|
-
return this.config;
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
```
|
|
733
|
-
|
|
734
|
-
**Recommendation:** Use `S3DataSource` or `SftpDataSource` instead of implementing this interface.
|
|
735
|
-
|
|
736
|
-
## Choosing the Right Data Source
|
|
737
|
-
|
|
738
|
-
| Feature | S3DataSource | SftpDataSource |
|
|
739
|
-
|---------|-------------|---------------|
|
|
740
|
-
| **Connection Pooling** | ❌ Not needed (HTTP) | ✅ Yes (v0.1.11+) |
|
|
741
|
-
| **Retry Logic** | ✅ Exponential backoff | ✅ Enhanced with reconnect |
|
|
742
|
-
| **Formats** | CSV, JSON, XML, Parquet | CSV, JSON, XML, JSONL, Parquet |
|
|
743
|
-
| **Integrated Parsing** | ❌ Use separate parsers | ✅ Built-in parsers |
|
|
744
|
-
| **Streaming** | ✅ Download streams | ❌ Download then parse |
|
|
745
|
-
| **File Operations** | List, Download, Upload, Copy, Move, Delete, Archive | List, Download, Upload, Move, Delete |
|
|
746
|
-
| **Best For** | Cloud storage, large files, archiving | FTP servers, real-time feeds |
|
|
747
|
-
| **Performance** | Excellent (parallel downloads) | Good (connection pool) |
|
|
748
|
-
|
|
749
|
-
### Decision Tree
|
|
750
|
-
|
|
751
|
-
```typescript
|
|
752
|
-
function chooseDataSource(requirements: {
|
|
753
|
-
source: 'cloud' | 'sftp';
|
|
754
|
-
fileSize: 'small' | 'large';
|
|
755
|
-
frequency: 'realtime' | 'batch';
|
|
756
|
-
format: string;
|
|
757
|
-
}): 'S3' | 'SFTP' {
|
|
758
|
-
if (requirements.source === 'cloud') {
|
|
759
|
-
return 'S3'; // S3 for cloud storage
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
if (requirements.source === 'sftp') {
|
|
763
|
-
if (requirements.frequency === 'realtime') {
|
|
764
|
-
// SFTP with connection pooling for frequent operations
|
|
765
|
-
return 'SFTP';
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
if (requirements.fileSize === 'large') {
|
|
769
|
-
// S3 better for very large files (streaming)
|
|
770
|
-
return 'S3';
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
return 'SFTP'; // SFTP for FTP servers
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
return 'S3'; // Default to S3
|
|
777
|
-
}
|
|
778
|
-
```
|
|
779
|
-
|
|
780
|
-
## Performance Considerations
|
|
781
|
-
|
|
782
|
-
### S3 Best Practices
|
|
783
|
-
|
|
784
|
-
```typescript
|
|
785
|
-
// 1. Use pagination for large lists
|
|
786
|
-
let continuationToken: string | undefined;
|
|
787
|
-
do {
|
|
788
|
-
const files = await s3.listFiles({
|
|
789
|
-
prefix: 'data/',
|
|
790
|
-
maxKeys: 1000,
|
|
791
|
-
continuationToken
|
|
792
|
-
});
|
|
793
|
-
|
|
794
|
-
// Process files...
|
|
795
|
-
|
|
796
|
-
// Get next page token (if available)
|
|
797
|
-
continuationToken = files.length === 1000 ? files[files.length - 1].path : undefined;
|
|
798
|
-
} while (continuationToken);
|
|
799
|
-
|
|
800
|
-
// 2. Use binary encoding for non-text files
|
|
801
|
-
const parquetData = await s3.downloadFile('data/large.parquet', {
|
|
802
|
-
encoding: 'binary'
|
|
803
|
-
});
|
|
804
|
-
|
|
805
|
-
// 3. Enable retry for production
|
|
806
|
-
const s3 = new S3DataSource(
|
|
807
|
-
{
|
|
808
|
-
type: 'S3_PARQUET',
|
|
809
|
-
s3Config: {
|
|
810
|
-
bucket: 'prod-bucket',
|
|
811
|
-
region: 'us-east-1',
|
|
812
|
-
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
813
|
-
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
814
|
-
maxAttempts: 5 // Production: 5 attempts
|
|
815
|
-
}
|
|
816
|
-
},
|
|
817
|
-
logger
|
|
818
|
-
);
|
|
819
|
-
```
|
|
820
|
-
|
|
821
|
-
### SFTP Best Practices
|
|
822
|
-
|
|
823
|
-
```typescript
|
|
824
|
-
// 1. Configure connection pool for concurrent operations
|
|
825
|
-
const sftp = new SftpDataSource(
|
|
826
|
-
{
|
|
827
|
-
type: 'SFTP_CSV',
|
|
828
|
-
settings: {
|
|
829
|
-
host: 'sftp.example.com',
|
|
830
|
-
username: process.env.SFTP_USERNAME!,
|
|
831
|
-
password: process.env.SFTP_PASSWORD!,
|
|
832
|
-
remotePath: '/data',
|
|
833
|
-
filePattern: '*.csv',
|
|
834
|
-
|
|
835
|
-
// Tune pool size based on workload
|
|
836
|
-
maxConnections: 10, // Higher for concurrent operations
|
|
837
|
-
|
|
838
|
-
retry: {
|
|
839
|
-
maxAttempts: 5, // Production: more retries
|
|
840
|
-
reconnectOnFailure: true
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
},
|
|
844
|
-
logger
|
|
845
|
-
);
|
|
846
|
-
|
|
847
|
-
// 2. Monitor pool usage
|
|
848
|
-
const info = sftp.getConnectionInfo();
|
|
849
|
-
if (info.connectionPoolSize === info.maxPoolSize) {
|
|
850
|
-
logger.warn('Connection pool at capacity - consider increasing maxConnections');
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
// 3. Use incremental sync for large datasets
|
|
854
|
-
const lastSync = await getLastSyncTimestamp();
|
|
855
|
-
const newFiles = await sftp.listFiles({
|
|
856
|
-
lastProcessedTimestamp: lastSync
|
|
857
|
-
});
|
|
858
|
-
logger.info(`Found ${newFiles.length} new files since ${lastSync}`);
|
|
859
|
-
```
|
|
860
|
-
|
|
861
|
-
## Error Handling
|
|
862
|
-
|
|
863
|
-
### S3 Error Handling
|
|
864
|
-
|
|
865
|
-
```typescript
|
|
866
|
-
import { DataSourceError } from '@fluentcommerce/fc-connect-sdk';
|
|
867
|
-
|
|
868
|
-
try {
|
|
869
|
-
const files = await s3.listFiles({ prefix: 'data/' });
|
|
870
|
-
} catch (error) {
|
|
871
|
-
if (error instanceof DataSourceError) {
|
|
872
|
-
console.error(`Data source error: ${error.message}`);
|
|
873
|
-
console.error(`Type: ${error.sourceType}`);
|
|
874
|
-
console.error(`Operation: ${error.operation}`);
|
|
875
|
-
console.error(`Context:`, error.context);
|
|
876
|
-
|
|
877
|
-
// Handle specific errors
|
|
878
|
-
if (error.context?.statusCode === 403) {
|
|
879
|
-
throw new Error('Access denied - check AWS credentials');
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
throw error;
|
|
883
|
-
}
|
|
884
|
-
```
|
|
885
|
-
|
|
886
|
-
### SFTP Error Handling
|
|
887
|
-
|
|
888
|
-
```typescript
|
|
889
|
-
try {
|
|
890
|
-
const files = await sftp.listFiles();
|
|
891
|
-
} catch (error) {
|
|
892
|
-
if (error instanceof DataSourceError) {
|
|
893
|
-
console.error(`SFTP error: ${error.message}`);
|
|
894
|
-
|
|
895
|
-
// Retry logic already handled automatically
|
|
896
|
-
// This error means all retries exhausted
|
|
897
|
-
|
|
898
|
-
if (error.message.includes('authentication')) {
|
|
899
|
-
throw new Error('Authentication failed - check credentials');
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
if (error.message.includes('permission')) {
|
|
903
|
-
throw new Error('Permission denied - check file permissions');
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
throw error;
|
|
907
|
-
}
|
|
908
|
-
```
|
|
909
|
-
|
|
910
|
-
## Practice Exercise
|
|
911
|
-
|
|
912
|
-
Create an ingestion workflow that:
|
|
913
|
-
1. Reads CSV files from SFTP with connection pooling
|
|
914
|
-
2. Validates required columns
|
|
915
|
-
3. Writes Parquet to S3 for archiving
|
|
916
|
-
4. Moves processed files on SFTP
|
|
917
|
-
|
|
918
|
-
<details>
|
|
919
|
-
<summary>Click to see solution</summary>
|
|
920
|
-
|
|
921
|
-
```typescript
|
|
922
|
-
import { S3DataSource, SftpDataSource } from '@fluentcommerce/fc-connect-sdk';
|
|
923
|
-
import { createConsoleLogger } from '@fluentcommerce/fc-connect-sdk';
|
|
924
|
-
|
|
925
|
-
async function exerciseSolution() {
|
|
926
|
-
const logger = createConsoleLogger();
|
|
927
|
-
|
|
928
|
-
// Configure SFTP with connection pooling
|
|
929
|
-
const sftp = new SftpDataSource(
|
|
930
|
-
{
|
|
931
|
-
type: 'SFTP_CSV',
|
|
932
|
-
name: 'SFTP Source',
|
|
933
|
-
settings: {
|
|
934
|
-
host: process.env.SFTP_HOST!,
|
|
935
|
-
username: process.env.SFTP_USERNAME!,
|
|
936
|
-
password: process.env.SFTP_PASSWORD!,
|
|
937
|
-
remotePath: '/incoming',
|
|
938
|
-
filePattern: '*.csv',
|
|
939
|
-
|
|
940
|
-
maxConnections: 5,
|
|
941
|
-
retry: {
|
|
942
|
-
maxAttempts: 3,
|
|
943
|
-
reconnectOnFailure: true
|
|
944
|
-
},
|
|
945
|
-
|
|
946
|
-
requiredCsvHeaders: ['sku', 'warehouse', 'quantity']
|
|
947
|
-
}
|
|
948
|
-
},
|
|
949
|
-
logger
|
|
950
|
-
);
|
|
951
|
-
|
|
952
|
-
// Configure S3 for archiving
|
|
953
|
-
const s3 = new S3DataSource(
|
|
954
|
-
{
|
|
955
|
-
type: 'S3_PARQUET',
|
|
956
|
-
name: 'S3 Archive',
|
|
957
|
-
s3Config: {
|
|
958
|
-
bucket: 'archive-bucket',
|
|
959
|
-
region: 'us-east-1',
|
|
960
|
-
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
961
|
-
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
962
|
-
maxAttempts: 3
|
|
963
|
-
}
|
|
964
|
-
},
|
|
965
|
-
logger
|
|
966
|
-
);
|
|
967
|
-
|
|
968
|
-
try {
|
|
969
|
-
// Validate connections
|
|
970
|
-
await sftp.validateConnection();
|
|
971
|
-
await s3.validateConnection();
|
|
972
|
-
|
|
973
|
-
// List files from SFTP
|
|
974
|
-
const files = await sftp.listFiles();
|
|
975
|
-
logger.info(`Found ${files.length} files to process`);
|
|
976
|
-
|
|
977
|
-
for (const file of files) {
|
|
978
|
-
try {
|
|
979
|
-
logger.info(`Processing: ${file.name}`);
|
|
980
|
-
|
|
981
|
-
// Download CSV from SFTP
|
|
982
|
-
const csvContent = await sftp.downloadFile(file.path);
|
|
983
|
-
|
|
984
|
-
// Parse CSV (validates required headers)
|
|
985
|
-
const records = sftp.parseCsvContent(csvContent, file);
|
|
986
|
-
logger.info(`Parsed ${records.length} records`);
|
|
987
|
-
|
|
988
|
-
// Convert to Parquet and write to S3
|
|
989
|
-
const parquetBuffer = await s3.writeParquetContent(records, {
|
|
990
|
-
compression: 'GZIP'
|
|
991
|
-
});
|
|
992
|
-
|
|
993
|
-
const archiveKey = `archive/${new Date().toISOString()}/${file.name}.parquet`;
|
|
994
|
-
await s3.uploadFile(archiveKey, parquetBuffer, {
|
|
995
|
-
contentType: 'application/octet-stream',
|
|
996
|
-
metadata: {
|
|
997
|
-
originalFile: file.name,
|
|
998
|
-
recordCount: records.length.toString()
|
|
999
|
-
}
|
|
1000
|
-
});
|
|
1001
|
-
|
|
1002
|
-
logger.info(`Archived to S3: ${archiveKey}`);
|
|
1003
|
-
|
|
1004
|
-
// Move processed file on SFTP
|
|
1005
|
-
await sftp.moveFile(
|
|
1006
|
-
file.path,
|
|
1007
|
-
`/processed/${file.name}`
|
|
1008
|
-
);
|
|
1009
|
-
|
|
1010
|
-
logger.info(`Moved on SFTP: ${file.path} → /processed/${file.name}`);
|
|
1011
|
-
|
|
1012
|
-
} catch (fileError) {
|
|
1013
|
-
logger.error(`Error processing ${file.name}:`, fileError);
|
|
1014
|
-
// Continue with next file
|
|
1015
|
-
}
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
// Log final stats
|
|
1019
|
-
const sftpInfo = sftp.getConnectionInfo();
|
|
1020
|
-
logger.info(`SFTP pool: ${sftpInfo.connectionPoolSize}/${sftpInfo.maxPoolSize}`);
|
|
1021
|
-
|
|
1022
|
-
} catch (error) {
|
|
1023
|
-
logger.error('Workflow failed:', error);
|
|
1024
|
-
throw error;
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
```
|
|
1028
|
-
|
|
1029
|
-
</details>
|
|
1030
|
-
|
|
1031
|
-
## Key Takeaways
|
|
1032
|
-
|
|
1033
|
-
- 🎯 **S3DataSource** handles S3 operations with automatic retry and exponential backoff
|
|
1034
|
-
- 🎯 **SftpDataSource** includes connection pooling (v0.1.11+) and enhanced retry logic
|
|
1035
|
-
- 🎯 **Connection pooling** (SFTP only) improves performance for concurrent operations
|
|
1036
|
-
- 🎯 **Retry logic** handles transient failures automatically (both S3 and SFTP)
|
|
1037
|
-
- 🎯 **Integrated parsing** (SFTP only) - CSV, JSON, Parquet parsing built-in
|
|
1038
|
-
- 🎯 **File operations** include upload, download, move, delete, archive
|
|
1039
|
-
- 🎯 **Parquet support** - Both S3 and SFTP can write Parquet files
|
|
1040
|
-
- 🎯 **Error handling** - Use `DataSourceError` for detailed error context
|
|
1041
|
-
|
|
1042
|
-
## Next Steps
|
|
1043
|
-
|
|
1044
|
-
Continue to [Module 4: Field Mapping →](./02-core-guides-ingestion-04-field-mapping.md) to learn how to transform parsed data for Fluent Commerce.
|
|
1045
|
-
|
|
1046
|
-
---
|
|
1047
|
-
|
|
1048
|
-
**Related Documentation:**
|
|
1049
|
-
- [S3DataSource API Reference](../../api-reference/modules/api-reference-06-data-sources.md#s3-data-source)
|
|
1050
|
-
- [SftpDataSource API Reference](../../api-reference/modules/api-reference-06-data-sources.md#sftp-data-source)
|
|
1051
|
-
- [SFTP Data Source Guide](../../data-sources/modules/data-sources-03-sftp-operations.md#connection-pooling)
|
|
1052
|
-
- [Error Handling Patterns](../../../03-PATTERN-GUIDES/error-handling/error-handling-readme.md#retry-patterns)
|
|
1
|
+
# Module 3: Data Sources & Parsers
|
|
2
|
+
|
|
3
|
+
**Level:** Intermediate
|
|
4
|
+
**Estimated Time:** 30 minutes
|
|
5
|
+
|
|
6
|
+
## Overview
|
|
7
|
+
|
|
8
|
+
This module teaches you how to read inventory data from different sources (S3, SFTP) and parse various file formats (CSV, Parquet, XML, JSON) using the SDK's data source services.
|
|
9
|
+
|
|
10
|
+
**IMPORTANT:** Data sources in this SDK have integrated parsing capabilities. You don't need separate parser services - each data source handles its own file formats.
|
|
11
|
+
|
|
12
|
+
## Learning Objectives
|
|
13
|
+
|
|
14
|
+
By the end of this module, you will:
|
|
15
|
+
- ✅ Understand the data source abstraction pattern
|
|
16
|
+
- ✅ Configure S3DataSource for AWS S3 buckets with retry logic
|
|
17
|
+
- ✅ Configure SftpDataSource with connection pooling and retry
|
|
18
|
+
- ✅ Parse CSV, JSON, and Parquet files directly from data sources
|
|
19
|
+
- ✅ Handle large files with streaming and batching
|
|
20
|
+
- ✅ Choose the right data source configuration for your use case
|
|
21
|
+
|
|
22
|
+
## Data Source Abstraction
|
|
23
|
+
|
|
24
|
+
The SDK provides data sources that handle both file operations AND parsing:
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { S3DataSource, SftpDataSource } from '@fluentcommerce/fc-connect-sdk';
|
|
28
|
+
import { createConsoleLogger } from '@fluentcommerce/fc-connect-sdk';
|
|
29
|
+
|
|
30
|
+
// Each data source is a complete file + parsing solution
|
|
31
|
+
// No need for separate parser services
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Benefits
|
|
35
|
+
|
|
36
|
+
| Benefit | Description |
|
|
37
|
+
|---------|-------------|
|
|
38
|
+
| **Integrated** | File operations and parsing in one service |
|
|
39
|
+
| **Retry Logic** | Automatic exponential backoff for transient failures |
|
|
40
|
+
| **Connection Pooling** | (SFTP only) Reuse connections for performance |
|
|
41
|
+
| **Format Support** | CSV, JSON, XML, Parquet, JSONL |
|
|
42
|
+
| **Testability** | Mock data sources for unit testing |
|
|
43
|
+
|
|
44
|
+
## S3 Data Source
|
|
45
|
+
|
|
46
|
+
### Configuration
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { S3DataSource } from '@fluentcommerce/fc-connect-sdk';
|
|
50
|
+
import { createConsoleLogger } from '@fluentcommerce/fc-connect-sdk';
|
|
51
|
+
|
|
52
|
+
const logger = createConsoleLogger();
|
|
53
|
+
|
|
54
|
+
const s3 = new S3DataSource(
|
|
55
|
+
{
|
|
56
|
+
type: 'S3_CSV', // or 'S3_JSON', 'S3_XML', 'S3_PARQUET'
|
|
57
|
+
name: 'S3 Inventory Source',
|
|
58
|
+
connectionId: 's3-prod',
|
|
59
|
+
s3Config: {
|
|
60
|
+
bucket: 'inventory-bucket',
|
|
61
|
+
region: 'us-east-1',
|
|
62
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
63
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
64
|
+
|
|
65
|
+
// Optional: Session token for temporary credentials
|
|
66
|
+
sessionToken: process.env.AWS_SESSION_TOKEN,
|
|
67
|
+
|
|
68
|
+
// Optional: Retry configuration
|
|
69
|
+
maxAttempts: 3 // Default: 3 attempts with exponential backoff
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
logger
|
|
73
|
+
);
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Basic Operations
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
// List files with filtering
|
|
80
|
+
const files = await s3.listFiles({
|
|
81
|
+
prefix: 'data/', // Filter by prefix
|
|
82
|
+
delimiter: '/', // Group by "directory"
|
|
83
|
+
maxKeys: 1000, // Max results per page
|
|
84
|
+
continuationToken: token // For pagination
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
console.log(`Found ${files.length} files`);
|
|
88
|
+
files.forEach(file => {
|
|
89
|
+
console.log(` ${file.path} (${file.size} bytes, ${file.lastModified})`);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Download file
|
|
93
|
+
const content = await s3.downloadFile('data/inventory-2024-01-15.csv', {
|
|
94
|
+
encoding: 'utf8' // or 'binary' for Buffer
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Download binary files (images, Parquet, etc.)
|
|
98
|
+
const binaryData = await s3.downloadFile('data/inventory.parquet', {
|
|
99
|
+
encoding: 'binary'
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Retry Configuration
|
|
104
|
+
|
|
105
|
+
S3DataSource includes automatic retry with exponential backoff for transient failures:
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
const s3 = new S3DataSource(
|
|
109
|
+
{
|
|
110
|
+
type: 'S3_CSV',
|
|
111
|
+
name: 'S3 with Custom Retry',
|
|
112
|
+
s3Config: {
|
|
113
|
+
bucket: 'inventory-bucket',
|
|
114
|
+
region: 'us-east-1',
|
|
115
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
116
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
117
|
+
|
|
118
|
+
// Retry configuration
|
|
119
|
+
maxAttempts: 5 // Try up to 5 times (default: 3)
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
logger
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
// Retry behavior:
|
|
126
|
+
// - Attempt 1: Immediate
|
|
127
|
+
// - Attempt 2: 500ms delay
|
|
128
|
+
// - Attempt 3: 1000ms delay
|
|
129
|
+
// - Attempt 4: 2000ms delay
|
|
130
|
+
// - Attempt 5: 4000ms delay
|
|
131
|
+
// - Max delay: 8000ms
|
|
132
|
+
|
|
133
|
+
// Retries on: 429, 500, 502, 503, 504 status codes
|
|
134
|
+
// Retries on: Network errors (ECONNRESET, ETIMEDOUT, etc.)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Upload Operations
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
// Upload text file
|
|
141
|
+
await s3.uploadFile(
|
|
142
|
+
'processed/inventory-2024-01-15.csv',
|
|
143
|
+
csvContent,
|
|
144
|
+
{
|
|
145
|
+
contentType: 'text/csv',
|
|
146
|
+
metadata: {
|
|
147
|
+
processedAt: new Date().toISOString(),
|
|
148
|
+
source: 'inventory-sync'
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
// Upload binary file
|
|
154
|
+
await s3.uploadFile(
|
|
155
|
+
'data/inventory.parquet',
|
|
156
|
+
parquetBuffer,
|
|
157
|
+
{
|
|
158
|
+
contentType: 'application/octet-stream'
|
|
159
|
+
}
|
|
160
|
+
);
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### File Management
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
// Copy file
|
|
167
|
+
await s3.copyFile(
|
|
168
|
+
'data/inventory.csv',
|
|
169
|
+
'backup/inventory.csv',
|
|
170
|
+
{
|
|
171
|
+
sourceBucket: 'source-bucket', // Optional: cross-bucket copy
|
|
172
|
+
metadata: { backupDate: new Date().toISOString() }
|
|
173
|
+
}
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
// Move file within same bucket (copy + delete)
|
|
177
|
+
await s3.moveFile(
|
|
178
|
+
'temp/inventory.csv',
|
|
179
|
+
'archive/inventory.csv'
|
|
180
|
+
// Optional: pass false to copy without deleting source
|
|
181
|
+
// await s3.moveFile('temp/inventory.csv', 'archive/inventory.csv', false);
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
// Archive file with automatic timestamping
|
|
185
|
+
const archivePath = await s3.archiveFile(
|
|
186
|
+
'data/inventory.csv',
|
|
187
|
+
'archive/2024-01/' // Archive prefix (timestamp + filename added automatically)
|
|
188
|
+
);
|
|
189
|
+
console.log(`File archived to: ${archivePath}`);
|
|
190
|
+
// Returns: 'archive/2024-01/2025-10-29T12-30-45-123Z_inventory.csv'
|
|
191
|
+
|
|
192
|
+
// For cross-bucket moves, use copyFile() + deleteFile() directly
|
|
193
|
+
await s3.copyFile('inventory.csv', 'backup/inventory.csv', {
|
|
194
|
+
sourceBucket: 'source-bucket',
|
|
195
|
+
metadata: { archivedAt: new Date().toISOString() }
|
|
196
|
+
});
|
|
197
|
+
await s3.deleteFile('inventory.csv');
|
|
198
|
+
|
|
199
|
+
// Delete file
|
|
200
|
+
await s3.deleteFile('temp/old-file.csv');
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Parquet Support
|
|
204
|
+
|
|
205
|
+
S3DataSource has built-in Parquet writing support:
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
// Write records as Parquet file
|
|
209
|
+
const records = [
|
|
210
|
+
{ sku: 'ABC123', warehouse: 'WH1', quantity: 100 },
|
|
211
|
+
{ sku: 'DEF456', warehouse: 'WH2', quantity: 50 }
|
|
212
|
+
];
|
|
213
|
+
|
|
214
|
+
const parquetBuffer = await s3.writeParquetContent(records, {
|
|
215
|
+
compression: 'GZIP', // or 'SNAPPY', 'BROTLI', 'UNCOMPRESSED'
|
|
216
|
+
rowGroupSize: 5000, // Records per row group
|
|
217
|
+
schema: { // Optional: explicit schema
|
|
218
|
+
sku: { type: 'UTF8' },
|
|
219
|
+
warehouse: { type: 'UTF8' },
|
|
220
|
+
quantity: { type: 'INT64' }
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// Upload Parquet file
|
|
225
|
+
await s3.uploadFile(
|
|
226
|
+
'data/inventory.parquet',
|
|
227
|
+
parquetBuffer,
|
|
228
|
+
{ contentType: 'application/octet-stream' }
|
|
229
|
+
);
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Connection Validation
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
// Validate S3 connection before processing
|
|
236
|
+
const isValid = await s3.validateConnection();
|
|
237
|
+
if (!isValid) {
|
|
238
|
+
throw new Error('Failed to connect to S3');
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Complete S3 Example
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
import { S3DataSource } from '@fluentcommerce/fc-connect-sdk';
|
|
246
|
+
import { createConsoleLogger } from '@fluentcommerce/fc-connect-sdk';
|
|
247
|
+
|
|
248
|
+
async function processS3Files() {
|
|
249
|
+
const logger = createConsoleLogger();
|
|
250
|
+
|
|
251
|
+
const s3 = new S3DataSource(
|
|
252
|
+
{
|
|
253
|
+
type: 'S3_CSV',
|
|
254
|
+
name: 'Inventory Source',
|
|
255
|
+
s3Config: {
|
|
256
|
+
bucket: 'inventory-bucket',
|
|
257
|
+
region: 'us-east-1',
|
|
258
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
259
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
260
|
+
maxAttempts: 3
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
logger
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
try {
|
|
267
|
+
// Validate connection
|
|
268
|
+
await s3.validateConnection();
|
|
269
|
+
|
|
270
|
+
// List files
|
|
271
|
+
const files = await s3.listFiles({ prefix: 'incoming/' });
|
|
272
|
+
|
|
273
|
+
for (const file of files) {
|
|
274
|
+
logger.info(`Processing: ${file.path} (${file.size} bytes)`);
|
|
275
|
+
|
|
276
|
+
// Download file
|
|
277
|
+
const content = await s3.downloadFile(file.path);
|
|
278
|
+
|
|
279
|
+
// Process content...
|
|
280
|
+
// (Use CSVParserService or other parsers as needed)
|
|
281
|
+
|
|
282
|
+
// Archive processed file (timestamp added automatically)
|
|
283
|
+
const archivedPath = await s3.archiveFile(
|
|
284
|
+
file.path,
|
|
285
|
+
`archive/${new Date().toISOString().split('T')[0]}/` // Just the prefix
|
|
286
|
+
);
|
|
287
|
+
logger.info(`Archived to: ${archivedPath}`);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
logger.info('Processing complete');
|
|
291
|
+
} catch (error) {
|
|
292
|
+
logger.error('Processing failed', error);
|
|
293
|
+
throw error;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## SFTP Data Source
|
|
299
|
+
|
|
300
|
+
### Configuration
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
import { SftpDataSource } from '@fluentcommerce/fc-connect-sdk';
|
|
304
|
+
import { createConsoleLogger } from '@fluentcommerce/fc-connect-sdk';
|
|
305
|
+
import * as fs from 'fs';
|
|
306
|
+
|
|
307
|
+
const logger = createConsoleLogger();
|
|
308
|
+
|
|
309
|
+
const sftp = new SftpDataSource(
|
|
310
|
+
{
|
|
311
|
+
type: 'SFTP_CSV', // or 'SFTP_JSON', 'SFTP_XML', 'SFTP_JSONL', 'SFTP_PARQUET'
|
|
312
|
+
name: 'SFTP Inventory Source',
|
|
313
|
+
connectionId: 'sftp-prod',
|
|
314
|
+
settings: {
|
|
315
|
+
host: 'sftp.example.com',
|
|
316
|
+
port: 22,
|
|
317
|
+
remotePath: '/data/inventory',
|
|
318
|
+
filePattern: '*.csv',
|
|
319
|
+
|
|
320
|
+
// Authentication: password OR private key
|
|
321
|
+
username: process.env.SFTP_USERNAME!,
|
|
322
|
+
password: process.env.SFTP_PASSWORD,
|
|
323
|
+
|
|
324
|
+
// OR use private key
|
|
325
|
+
privateKey: fs.readFileSync('/path/to/key.pem', 'utf8'),
|
|
326
|
+
passphrase: process.env.SFTP_KEY_PASSPHRASE,
|
|
327
|
+
|
|
328
|
+
// Connection pooling (NEW in v0.1.11+)
|
|
329
|
+
maxConnections: 5, // Default: 5
|
|
330
|
+
|
|
331
|
+
// Retry configuration (NEW in v0.1.11+)
|
|
332
|
+
retry: {
|
|
333
|
+
maxAttempts: 3, // Default: 3
|
|
334
|
+
baseDelayMs: 1000, // Default: 1000ms
|
|
335
|
+
backoffFactor: 2, // Default: 2 (exponential)
|
|
336
|
+
maxDelayMs: 8000, // Default: 8000ms
|
|
337
|
+
jitter: 'full', // 'full' or 'none'
|
|
338
|
+
reconnectOnFailure: true // Reconnect on failure
|
|
339
|
+
},
|
|
340
|
+
|
|
341
|
+
// CSV parsing settings
|
|
342
|
+
csvDelimiter: ',',
|
|
343
|
+
csvHeaders: ['sku', 'warehouse', 'quantity'],
|
|
344
|
+
requiredCsvHeaders: ['sku', 'quantity'],
|
|
345
|
+
csvSkipEmptyLines: true,
|
|
346
|
+
csvTrimValues: true,
|
|
347
|
+
|
|
348
|
+
// JSON parsing settings
|
|
349
|
+
jsonFormat: 'json', // or 'jsonl'
|
|
350
|
+
jsonValidate: true,
|
|
351
|
+
jsonMaxDepth: 10,
|
|
352
|
+
|
|
353
|
+
// Parquet settings
|
|
354
|
+
parquetCompression: 'gzip'
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
logger
|
|
358
|
+
);
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Connection Pooling (NEW in v0.1.11+)
|
|
362
|
+
|
|
363
|
+
SftpDataSource automatically manages a connection pool for performance:
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
// Connection pool behavior:
|
|
367
|
+
// 1. Reuses existing connections when available
|
|
368
|
+
// 2. Creates new connections up to maxConnections limit
|
|
369
|
+
// 3. Waits in queue when pool is full (30s timeout)
|
|
370
|
+
// 4. Validates connection health before reuse
|
|
371
|
+
// 5. Auto-reconnects on transient failures
|
|
372
|
+
|
|
373
|
+
const sftp = new SftpDataSource(
|
|
374
|
+
{
|
|
375
|
+
type: 'SFTP_CSV',
|
|
376
|
+
name: 'SFTP with Pool',
|
|
377
|
+
settings: {
|
|
378
|
+
host: 'sftp.example.com',
|
|
379
|
+
username: process.env.SFTP_USERNAME!,
|
|
380
|
+
password: process.env.SFTP_PASSWORD!,
|
|
381
|
+
remotePath: '/data',
|
|
382
|
+
filePattern: '*.csv',
|
|
383
|
+
|
|
384
|
+
// Pool configuration
|
|
385
|
+
maxConnections: 10, // Allow up to 10 concurrent connections
|
|
386
|
+
connectionTimeout: 10000, // 10s connection timeout
|
|
387
|
+
keepAliveInterval: 10000 // Keep-alive every 10s
|
|
388
|
+
}
|
|
389
|
+
},
|
|
390
|
+
logger
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
// Connection pool is transparent - all operations use it automatically
|
|
394
|
+
const files = await sftp.listFiles(); // Uses pool
|
|
395
|
+
const content = await sftp.downloadFile(files[0].name); // Uses pool
|
|
396
|
+
|
|
397
|
+
// Get pool stats for monitoring
|
|
398
|
+
const info = sftp.getConnectionInfo();
|
|
399
|
+
console.log(`Pool size: ${info.connectionPoolSize}/${info.maxPoolSize}`);
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### Enhanced Retry Logic (NEW in v0.1.11+)
|
|
403
|
+
|
|
404
|
+
SFTP operations automatically retry with exponential backoff:
|
|
405
|
+
|
|
406
|
+
```typescript
|
|
407
|
+
const sftp = new SftpDataSource(
|
|
408
|
+
{
|
|
409
|
+
type: 'SFTP_CSV',
|
|
410
|
+
name: 'SFTP with Custom Retry',
|
|
411
|
+
settings: {
|
|
412
|
+
host: 'sftp.example.com',
|
|
413
|
+
username: process.env.SFTP_USERNAME!,
|
|
414
|
+
password: process.env.SFTP_PASSWORD!,
|
|
415
|
+
remotePath: '/data',
|
|
416
|
+
filePattern: '*.csv',
|
|
417
|
+
|
|
418
|
+
retry: {
|
|
419
|
+
maxAttempts: 5, // Try up to 5 times
|
|
420
|
+
baseDelayMs: 2000, // Start with 2s delay
|
|
421
|
+
backoffFactor: 2, // Double delay each attempt
|
|
422
|
+
maxDelayMs: 16000, // Cap at 16s
|
|
423
|
+
jitter: 'full', // Random jitter (0 to maxDelay)
|
|
424
|
+
reconnectOnFailure: true, // Clear pool and reconnect
|
|
425
|
+
|
|
426
|
+
// Custom retry predicate
|
|
427
|
+
isRetryable: (error: Error) => {
|
|
428
|
+
// Add custom logic for retryable errors
|
|
429
|
+
return error.message.includes('timeout') ||
|
|
430
|
+
error.message.includes('connection');
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
},
|
|
435
|
+
logger
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
// Retry timeline with above config:
|
|
439
|
+
// - Attempt 1: Immediate
|
|
440
|
+
// - Attempt 2: Random(0-2000ms) delay
|
|
441
|
+
// - Attempt 3: Random(0-4000ms) delay
|
|
442
|
+
// - Attempt 4: Random(0-8000ms) delay
|
|
443
|
+
// - Attempt 5: Random(0-16000ms) delay
|
|
444
|
+
|
|
445
|
+
// Automatically retries on:
|
|
446
|
+
// - ECONNRESET, ETIMEDOUT, ENOTFOUND, EHOSTUNREACH, ECONNREFUSED
|
|
447
|
+
// - "connection lost", "handshake failed", "timeout", etc.
|
|
448
|
+
//
|
|
449
|
+
// Does NOT retry on:
|
|
450
|
+
// - Authentication failures
|
|
451
|
+
// - Permission denied
|
|
452
|
+
// - File not found
|
|
453
|
+
// - Algorithm/cipher/key exchange errors
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### Basic Operations
|
|
457
|
+
|
|
458
|
+
```typescript
|
|
459
|
+
// List files with filtering
|
|
460
|
+
const files = await sftp.listFiles({
|
|
461
|
+
remotePath: '/data/inventory', // Optional: override config
|
|
462
|
+
filePattern: '*.csv', // Optional: override config
|
|
463
|
+
lastProcessedTimestamp: '2024-01-15T00:00:00Z', // Incremental sync
|
|
464
|
+
fileName: 'inventory' // Filter by name substring
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
console.log(`Found ${files.length} files`);
|
|
468
|
+
|
|
469
|
+
// Download file
|
|
470
|
+
const content = await sftp.downloadFile('/data/inventory.csv', {
|
|
471
|
+
encoding: 'utf8', // Default encoding
|
|
472
|
+
asBuffer: false, // Return string
|
|
473
|
+
maxSize: 100 * 1024 * 1024 // 100MB limit
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
// Download as buffer
|
|
477
|
+
const buffer = await sftp.downloadFile('/data/inventory.parquet', {
|
|
478
|
+
asBuffer: true
|
|
479
|
+
});
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
### Integrated CSV Parsing
|
|
483
|
+
|
|
484
|
+
SFTP data source has built-in CSV parsing:
|
|
485
|
+
|
|
486
|
+
```typescript
|
|
487
|
+
// Download and parse CSV in one step
|
|
488
|
+
const csvContent = await sftp.downloadFile('/data/inventory.csv');
|
|
489
|
+
|
|
490
|
+
const records = sftp.parseCsvContent(csvContent, {
|
|
491
|
+
name: 'inventory.csv',
|
|
492
|
+
path: '/data/inventory.csv',
|
|
493
|
+
size: csvContent.length,
|
|
494
|
+
source: 'sftp'
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
console.log(`Parsed ${records.length} records`);
|
|
498
|
+
records.forEach(record => {
|
|
499
|
+
console.log(`SKU: ${record.sku}, Qty: ${record.quantity}`);
|
|
500
|
+
});
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### Integrated JSON Parsing
|
|
504
|
+
|
|
505
|
+
```typescript
|
|
506
|
+
// Download and parse JSON
|
|
507
|
+
const jsonContent = await sftp.downloadFile('/data/inventory.json');
|
|
508
|
+
|
|
509
|
+
const records = sftp.parseJsonContent(
|
|
510
|
+
jsonContent,
|
|
511
|
+
{ name: 'inventory.json', path: '/data/inventory.json', source: 'sftp' },
|
|
512
|
+
{
|
|
513
|
+
format: 'json', // or 'jsonl' for JSON Lines
|
|
514
|
+
validate: true, // Validate structure
|
|
515
|
+
maxDepth: 10 // Limit nesting depth
|
|
516
|
+
}
|
|
517
|
+
);
|
|
518
|
+
|
|
519
|
+
console.log(`Parsed ${records.length} JSON records`);
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
### Writing Files
|
|
523
|
+
|
|
524
|
+
```typescript
|
|
525
|
+
// Write CSV file
|
|
526
|
+
const records = [
|
|
527
|
+
{ sku: 'ABC123', warehouse: 'WH1', quantity: 100 },
|
|
528
|
+
{ sku: 'DEF456', warehouse: 'WH2', quantity: 50 }
|
|
529
|
+
];
|
|
530
|
+
|
|
531
|
+
const csvContent = sftp.writeCsvContent(records, {
|
|
532
|
+
delimiter: ',',
|
|
533
|
+
includeHeader: true,
|
|
534
|
+
quote: '"',
|
|
535
|
+
escape: '"'
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
await sftp.uploadFile('/data/output.csv', {
|
|
539
|
+
overwrite: true,
|
|
540
|
+
createDirectories: true
|
|
541
|
+
}, csvContent);
|
|
542
|
+
|
|
543
|
+
// Write JSON file
|
|
544
|
+
const jsonContent = sftp.writeJsonContent(records, {
|
|
545
|
+
format: 'json', // or 'jsonl'
|
|
546
|
+
indent: 2,
|
|
547
|
+
sortKeys: true
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
await sftp.uploadFile('/data/output.json', {
|
|
551
|
+
overwrite: true
|
|
552
|
+
}, jsonContent);
|
|
553
|
+
|
|
554
|
+
// Write Parquet file
|
|
555
|
+
const parquetBuffer = await sftp.writeParquetContent(records, {
|
|
556
|
+
compression: 'GZIP',
|
|
557
|
+
rowGroupSize: 5000
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
await sftp.uploadFile('/data/output.parquet', {
|
|
561
|
+
overwrite: true
|
|
562
|
+
}, parquetBuffer);
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
### File Management
|
|
566
|
+
|
|
567
|
+
```typescript
|
|
568
|
+
// Upload file from local path
|
|
569
|
+
await sftp.uploadFile(
|
|
570
|
+
'/local/path/inventory.csv',
|
|
571
|
+
'/remote/path/inventory.csv',
|
|
572
|
+
{
|
|
573
|
+
overwrite: false,
|
|
574
|
+
createDirectories: true,
|
|
575
|
+
permissions: '0644'
|
|
576
|
+
}
|
|
577
|
+
);
|
|
578
|
+
|
|
579
|
+
// Create directory
|
|
580
|
+
await sftp.createDirectory('/data/archive', true); // recursive
|
|
581
|
+
|
|
582
|
+
// Move file
|
|
583
|
+
await sftp.moveFile(
|
|
584
|
+
'/data/inventory.csv',
|
|
585
|
+
'/data/processed/inventory.csv'
|
|
586
|
+
);
|
|
587
|
+
|
|
588
|
+
// Delete file
|
|
589
|
+
await sftp.deleteFile('/data/old-file.csv');
|
|
590
|
+
|
|
591
|
+
// Check if file exists
|
|
592
|
+
const exists = await sftp.fileExists('/data/inventory.csv');
|
|
593
|
+
if (exists) {
|
|
594
|
+
console.log('File exists');
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Check if directory exists
|
|
598
|
+
const dirExists = await sftp.directoryExists('/data/archive');
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
### Connection Monitoring
|
|
602
|
+
|
|
603
|
+
```typescript
|
|
604
|
+
// Validate connection
|
|
605
|
+
const isValid = await sftp.validateConnection();
|
|
606
|
+
if (!isValid) {
|
|
607
|
+
throw new Error('SFTP connection failed');
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// Get connection pool info
|
|
611
|
+
const info = sftp.getConnectionInfo();
|
|
612
|
+
console.log(`Connection Info:
|
|
613
|
+
Type: ${info.type}
|
|
614
|
+
Host: ${info.host}:${info.port}
|
|
615
|
+
Remote Path: ${info.remotePath}
|
|
616
|
+
File Pattern: ${info.filePattern}
|
|
617
|
+
Pool: ${info.connectionPoolSize}/${info.maxPoolSize} connections
|
|
618
|
+
`);
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
### Complete SFTP Example
|
|
622
|
+
|
|
623
|
+
```typescript
|
|
624
|
+
import { SftpDataSource } from '@fluentcommerce/fc-connect-sdk';
|
|
625
|
+
import { createConsoleLogger } from '@fluentcommerce/fc-connect-sdk';
|
|
626
|
+
|
|
627
|
+
async function processSftpFiles() {
|
|
628
|
+
const logger = createConsoleLogger();
|
|
629
|
+
|
|
630
|
+
const sftp = new SftpDataSource(
|
|
631
|
+
{
|
|
632
|
+
type: 'SFTP_CSV',
|
|
633
|
+
name: 'SFTP Inventory',
|
|
634
|
+
settings: {
|
|
635
|
+
host: process.env.SFTP_HOST!,
|
|
636
|
+
username: process.env.SFTP_USERNAME!,
|
|
637
|
+
password: process.env.SFTP_PASSWORD!,
|
|
638
|
+
remotePath: '/data/inventory',
|
|
639
|
+
filePattern: '*.csv',
|
|
640
|
+
|
|
641
|
+
// Enable connection pooling and retry
|
|
642
|
+
maxConnections: 5,
|
|
643
|
+
retry: {
|
|
644
|
+
maxAttempts: 3,
|
|
645
|
+
reconnectOnFailure: true
|
|
646
|
+
},
|
|
647
|
+
|
|
648
|
+
// CSV parsing settings
|
|
649
|
+
csvDelimiter: ',',
|
|
650
|
+
requiredCsvHeaders: ['sku', 'quantity'],
|
|
651
|
+
csvSkipEmptyLines: true
|
|
652
|
+
}
|
|
653
|
+
},
|
|
654
|
+
logger
|
|
655
|
+
);
|
|
656
|
+
|
|
657
|
+
try {
|
|
658
|
+
// Validate connection
|
|
659
|
+
await sftp.validateConnection();
|
|
660
|
+
|
|
661
|
+
// List files
|
|
662
|
+
const files = await sftp.listFiles();
|
|
663
|
+
logger.info(`Found ${files.length} files`);
|
|
664
|
+
|
|
665
|
+
for (const file of files) {
|
|
666
|
+
logger.info(`Processing: ${file.name}`);
|
|
667
|
+
|
|
668
|
+
// Download file
|
|
669
|
+
const csvContent = await sftp.downloadFile(file.path);
|
|
670
|
+
|
|
671
|
+
// Parse CSV (integrated parsing)
|
|
672
|
+
const records = sftp.parseCsvContent(csvContent, file);
|
|
673
|
+
logger.info(`Parsed ${records.length} records`);
|
|
674
|
+
|
|
675
|
+
// Process records...
|
|
676
|
+
// (Send to Fluent Commerce, etc.)
|
|
677
|
+
|
|
678
|
+
// Move to processed folder
|
|
679
|
+
await sftp.moveFile(
|
|
680
|
+
file.path,
|
|
681
|
+
`/data/processed/${file.name}`
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// Get final pool stats
|
|
686
|
+
const info = sftp.getConnectionInfo();
|
|
687
|
+
logger.info(`Pool: ${info.connectionPoolSize}/${info.maxPoolSize}`);
|
|
688
|
+
|
|
689
|
+
} catch (error) {
|
|
690
|
+
logger.error('Processing failed', error);
|
|
691
|
+
throw error;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
## Inventory Data Source (Base Interface)
|
|
697
|
+
|
|
698
|
+
**Note:** `InventoryDataSource` is an abstract base interface for inventory-specific operations. Use the concrete implementations `S3DataSource` or `SftpDataSource` directly.
|
|
699
|
+
|
|
700
|
+
```typescript
|
|
701
|
+
import { InventoryDataSource, BaseInventoryDataSource } from '@fluentcommerce/fc-connect-sdk';
|
|
702
|
+
|
|
703
|
+
// This is an interface, not a standalone data source
|
|
704
|
+
interface InventoryDataSource {
|
|
705
|
+
getBucketName(): string;
|
|
706
|
+
getCsvFilePrefix(): string;
|
|
707
|
+
parseS3ListResponse(xmlResponse: string, prefix: string, lastModifiedAfter?: string): FileMetadata[];
|
|
708
|
+
getConfig(): DataSourceConfig;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// Base implementation
|
|
712
|
+
class BaseInventoryDataSource implements InventoryDataSource {
|
|
713
|
+
constructor(protected config: DataSourceConfig) {}
|
|
714
|
+
|
|
715
|
+
getBucketName(): string {
|
|
716
|
+
return 'default-bucket';
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
getCsvFilePrefix(): string {
|
|
720
|
+
return 'inventory/';
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
parseS3ListResponse(xmlResponse: string, prefix: string, lastModifiedAfter?: string): FileMetadata[] {
|
|
724
|
+
// Simple XML parsing for S3 list response
|
|
725
|
+
// ...
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
getConfig(): DataSourceConfig {
|
|
729
|
+
return this.config;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
**Recommendation:** Use `S3DataSource` or `SftpDataSource` instead of implementing this interface.
|
|
735
|
+
|
|
736
|
+
## Choosing the Right Data Source
|
|
737
|
+
|
|
738
|
+
| Feature | S3DataSource | SftpDataSource |
|
|
739
|
+
|---------|-------------|---------------|
|
|
740
|
+
| **Connection Pooling** | ❌ Not needed (HTTP) | ✅ Yes (v0.1.11+) |
|
|
741
|
+
| **Retry Logic** | ✅ Exponential backoff | ✅ Enhanced with reconnect |
|
|
742
|
+
| **Formats** | CSV, JSON, XML, Parquet | CSV, JSON, XML, JSONL, Parquet |
|
|
743
|
+
| **Integrated Parsing** | ❌ Use separate parsers | ✅ Built-in parsers |
|
|
744
|
+
| **Streaming** | ✅ Download streams | ❌ Download then parse |
|
|
745
|
+
| **File Operations** | List, Download, Upload, Copy, Move, Delete, Archive | List, Download, Upload, Move, Delete |
|
|
746
|
+
| **Best For** | Cloud storage, large files, archiving | FTP servers, real-time feeds |
|
|
747
|
+
| **Performance** | Excellent (parallel downloads) | Good (connection pool) |
|
|
748
|
+
|
|
749
|
+
### Decision Tree
|
|
750
|
+
|
|
751
|
+
```typescript
|
|
752
|
+
function chooseDataSource(requirements: {
|
|
753
|
+
source: 'cloud' | 'sftp';
|
|
754
|
+
fileSize: 'small' | 'large';
|
|
755
|
+
frequency: 'realtime' | 'batch';
|
|
756
|
+
format: string;
|
|
757
|
+
}): 'S3' | 'SFTP' {
|
|
758
|
+
if (requirements.source === 'cloud') {
|
|
759
|
+
return 'S3'; // S3 for cloud storage
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
if (requirements.source === 'sftp') {
|
|
763
|
+
if (requirements.frequency === 'realtime') {
|
|
764
|
+
// SFTP with connection pooling for frequent operations
|
|
765
|
+
return 'SFTP';
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
if (requirements.fileSize === 'large') {
|
|
769
|
+
// S3 better for very large files (streaming)
|
|
770
|
+
return 'S3';
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
return 'SFTP'; // SFTP for FTP servers
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
return 'S3'; // Default to S3
|
|
777
|
+
}
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
## Performance Considerations
|
|
781
|
+
|
|
782
|
+
### S3 Best Practices
|
|
783
|
+
|
|
784
|
+
```typescript
|
|
785
|
+
// 1. Use pagination for large lists
|
|
786
|
+
let continuationToken: string | undefined;
|
|
787
|
+
do {
|
|
788
|
+
const files = await s3.listFiles({
|
|
789
|
+
prefix: 'data/',
|
|
790
|
+
maxKeys: 1000,
|
|
791
|
+
continuationToken
|
|
792
|
+
});
|
|
793
|
+
|
|
794
|
+
// Process files...
|
|
795
|
+
|
|
796
|
+
// Get next page token (if available)
|
|
797
|
+
continuationToken = files.length === 1000 ? files[files.length - 1].path : undefined;
|
|
798
|
+
} while (continuationToken);
|
|
799
|
+
|
|
800
|
+
// 2. Use binary encoding for non-text files
|
|
801
|
+
const parquetData = await s3.downloadFile('data/large.parquet', {
|
|
802
|
+
encoding: 'binary'
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
// 3. Enable retry for production
|
|
806
|
+
const s3 = new S3DataSource(
|
|
807
|
+
{
|
|
808
|
+
type: 'S3_PARQUET',
|
|
809
|
+
s3Config: {
|
|
810
|
+
bucket: 'prod-bucket',
|
|
811
|
+
region: 'us-east-1',
|
|
812
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
813
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
814
|
+
maxAttempts: 5 // Production: 5 attempts
|
|
815
|
+
}
|
|
816
|
+
},
|
|
817
|
+
logger
|
|
818
|
+
);
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
### SFTP Best Practices
|
|
822
|
+
|
|
823
|
+
```typescript
|
|
824
|
+
// 1. Configure connection pool for concurrent operations
|
|
825
|
+
const sftp = new SftpDataSource(
|
|
826
|
+
{
|
|
827
|
+
type: 'SFTP_CSV',
|
|
828
|
+
settings: {
|
|
829
|
+
host: 'sftp.example.com',
|
|
830
|
+
username: process.env.SFTP_USERNAME!,
|
|
831
|
+
password: process.env.SFTP_PASSWORD!,
|
|
832
|
+
remotePath: '/data',
|
|
833
|
+
filePattern: '*.csv',
|
|
834
|
+
|
|
835
|
+
// Tune pool size based on workload
|
|
836
|
+
maxConnections: 10, // Higher for concurrent operations
|
|
837
|
+
|
|
838
|
+
retry: {
|
|
839
|
+
maxAttempts: 5, // Production: more retries
|
|
840
|
+
reconnectOnFailure: true
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
},
|
|
844
|
+
logger
|
|
845
|
+
);
|
|
846
|
+
|
|
847
|
+
// 2. Monitor pool usage
|
|
848
|
+
const info = sftp.getConnectionInfo();
|
|
849
|
+
if (info.connectionPoolSize === info.maxPoolSize) {
|
|
850
|
+
logger.warn('Connection pool at capacity - consider increasing maxConnections');
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
// 3. Use incremental sync for large datasets
|
|
854
|
+
const lastSync = await getLastSyncTimestamp();
|
|
855
|
+
const newFiles = await sftp.listFiles({
|
|
856
|
+
lastProcessedTimestamp: lastSync
|
|
857
|
+
});
|
|
858
|
+
logger.info(`Found ${newFiles.length} new files since ${lastSync}`);
|
|
859
|
+
```
|
|
860
|
+
|
|
861
|
+
## Error Handling
|
|
862
|
+
|
|
863
|
+
### S3 Error Handling
|
|
864
|
+
|
|
865
|
+
```typescript
|
|
866
|
+
import { DataSourceError } from '@fluentcommerce/fc-connect-sdk';
|
|
867
|
+
|
|
868
|
+
try {
|
|
869
|
+
const files = await s3.listFiles({ prefix: 'data/' });
|
|
870
|
+
} catch (error) {
|
|
871
|
+
if (error instanceof DataSourceError) {
|
|
872
|
+
console.error(`Data source error: ${error.message}`);
|
|
873
|
+
console.error(`Type: ${error.sourceType}`);
|
|
874
|
+
console.error(`Operation: ${error.operation}`);
|
|
875
|
+
console.error(`Context:`, error.context);
|
|
876
|
+
|
|
877
|
+
// Handle specific errors
|
|
878
|
+
if (error.context?.statusCode === 403) {
|
|
879
|
+
throw new Error('Access denied - check AWS credentials');
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
throw error;
|
|
883
|
+
}
|
|
884
|
+
```
|
|
885
|
+
|
|
886
|
+
### SFTP Error Handling
|
|
887
|
+
|
|
888
|
+
```typescript
|
|
889
|
+
try {
|
|
890
|
+
const files = await sftp.listFiles();
|
|
891
|
+
} catch (error) {
|
|
892
|
+
if (error instanceof DataSourceError) {
|
|
893
|
+
console.error(`SFTP error: ${error.message}`);
|
|
894
|
+
|
|
895
|
+
// Retry logic already handled automatically
|
|
896
|
+
// This error means all retries exhausted
|
|
897
|
+
|
|
898
|
+
if (error.message.includes('authentication')) {
|
|
899
|
+
throw new Error('Authentication failed - check credentials');
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
if (error.message.includes('permission')) {
|
|
903
|
+
throw new Error('Permission denied - check file permissions');
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
throw error;
|
|
907
|
+
}
|
|
908
|
+
```
|
|
909
|
+
|
|
910
|
+
## Practice Exercise
|
|
911
|
+
|
|
912
|
+
Create an ingestion workflow that:
|
|
913
|
+
1. Reads CSV files from SFTP with connection pooling
|
|
914
|
+
2. Validates required columns
|
|
915
|
+
3. Writes Parquet to S3 for archiving
|
|
916
|
+
4. Moves processed files on SFTP
|
|
917
|
+
|
|
918
|
+
<details>
|
|
919
|
+
<summary>Click to see solution</summary>
|
|
920
|
+
|
|
921
|
+
```typescript
|
|
922
|
+
import { S3DataSource, SftpDataSource } from '@fluentcommerce/fc-connect-sdk';
|
|
923
|
+
import { createConsoleLogger } from '@fluentcommerce/fc-connect-sdk';
|
|
924
|
+
|
|
925
|
+
async function exerciseSolution() {
|
|
926
|
+
const logger = createConsoleLogger();
|
|
927
|
+
|
|
928
|
+
// Configure SFTP with connection pooling
|
|
929
|
+
const sftp = new SftpDataSource(
|
|
930
|
+
{
|
|
931
|
+
type: 'SFTP_CSV',
|
|
932
|
+
name: 'SFTP Source',
|
|
933
|
+
settings: {
|
|
934
|
+
host: process.env.SFTP_HOST!,
|
|
935
|
+
username: process.env.SFTP_USERNAME!,
|
|
936
|
+
password: process.env.SFTP_PASSWORD!,
|
|
937
|
+
remotePath: '/incoming',
|
|
938
|
+
filePattern: '*.csv',
|
|
939
|
+
|
|
940
|
+
maxConnections: 5,
|
|
941
|
+
retry: {
|
|
942
|
+
maxAttempts: 3,
|
|
943
|
+
reconnectOnFailure: true
|
|
944
|
+
},
|
|
945
|
+
|
|
946
|
+
requiredCsvHeaders: ['sku', 'warehouse', 'quantity']
|
|
947
|
+
}
|
|
948
|
+
},
|
|
949
|
+
logger
|
|
950
|
+
);
|
|
951
|
+
|
|
952
|
+
// Configure S3 for archiving
|
|
953
|
+
const s3 = new S3DataSource(
|
|
954
|
+
{
|
|
955
|
+
type: 'S3_PARQUET',
|
|
956
|
+
name: 'S3 Archive',
|
|
957
|
+
s3Config: {
|
|
958
|
+
bucket: 'archive-bucket',
|
|
959
|
+
region: 'us-east-1',
|
|
960
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
961
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
962
|
+
maxAttempts: 3
|
|
963
|
+
}
|
|
964
|
+
},
|
|
965
|
+
logger
|
|
966
|
+
);
|
|
967
|
+
|
|
968
|
+
try {
|
|
969
|
+
// Validate connections
|
|
970
|
+
await sftp.validateConnection();
|
|
971
|
+
await s3.validateConnection();
|
|
972
|
+
|
|
973
|
+
// List files from SFTP
|
|
974
|
+
const files = await sftp.listFiles();
|
|
975
|
+
logger.info(`Found ${files.length} files to process`);
|
|
976
|
+
|
|
977
|
+
for (const file of files) {
|
|
978
|
+
try {
|
|
979
|
+
logger.info(`Processing: ${file.name}`);
|
|
980
|
+
|
|
981
|
+
// Download CSV from SFTP
|
|
982
|
+
const csvContent = await sftp.downloadFile(file.path);
|
|
983
|
+
|
|
984
|
+
// Parse CSV (validates required headers)
|
|
985
|
+
const records = sftp.parseCsvContent(csvContent, file);
|
|
986
|
+
logger.info(`Parsed ${records.length} records`);
|
|
987
|
+
|
|
988
|
+
// Convert to Parquet and write to S3
|
|
989
|
+
const parquetBuffer = await s3.writeParquetContent(records, {
|
|
990
|
+
compression: 'GZIP'
|
|
991
|
+
});
|
|
992
|
+
|
|
993
|
+
const archiveKey = `archive/${new Date().toISOString()}/${file.name}.parquet`;
|
|
994
|
+
await s3.uploadFile(archiveKey, parquetBuffer, {
|
|
995
|
+
contentType: 'application/octet-stream',
|
|
996
|
+
metadata: {
|
|
997
|
+
originalFile: file.name,
|
|
998
|
+
recordCount: records.length.toString()
|
|
999
|
+
}
|
|
1000
|
+
});
|
|
1001
|
+
|
|
1002
|
+
logger.info(`Archived to S3: ${archiveKey}`);
|
|
1003
|
+
|
|
1004
|
+
// Move processed file on SFTP
|
|
1005
|
+
await sftp.moveFile(
|
|
1006
|
+
file.path,
|
|
1007
|
+
`/processed/${file.name}`
|
|
1008
|
+
);
|
|
1009
|
+
|
|
1010
|
+
logger.info(`Moved on SFTP: ${file.path} → /processed/${file.name}`);
|
|
1011
|
+
|
|
1012
|
+
} catch (fileError) {
|
|
1013
|
+
logger.error(`Error processing ${file.name}:`, fileError);
|
|
1014
|
+
// Continue with next file
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
// Log final stats
|
|
1019
|
+
const sftpInfo = sftp.getConnectionInfo();
|
|
1020
|
+
logger.info(`SFTP pool: ${sftpInfo.connectionPoolSize}/${sftpInfo.maxPoolSize}`);
|
|
1021
|
+
|
|
1022
|
+
} catch (error) {
|
|
1023
|
+
logger.error('Workflow failed:', error);
|
|
1024
|
+
throw error;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
```
|
|
1028
|
+
|
|
1029
|
+
</details>
|
|
1030
|
+
|
|
1031
|
+
## Key Takeaways
|
|
1032
|
+
|
|
1033
|
+
- 🎯 **S3DataSource** handles S3 operations with automatic retry and exponential backoff
|
|
1034
|
+
- 🎯 **SftpDataSource** includes connection pooling (v0.1.11+) and enhanced retry logic
|
|
1035
|
+
- 🎯 **Connection pooling** (SFTP only) improves performance for concurrent operations
|
|
1036
|
+
- 🎯 **Retry logic** handles transient failures automatically (both S3 and SFTP)
|
|
1037
|
+
- 🎯 **Integrated parsing** (SFTP only) - CSV, JSON, Parquet parsing built-in
|
|
1038
|
+
- 🎯 **File operations** include upload, download, move, delete, archive
|
|
1039
|
+
- 🎯 **Parquet support** - Both S3 and SFTP can write Parquet files
|
|
1040
|
+
- 🎯 **Error handling** - Use `DataSourceError` for detailed error context
|
|
1041
|
+
|
|
1042
|
+
## Next Steps
|
|
1043
|
+
|
|
1044
|
+
Continue to [Module 4: Field Mapping →](./02-core-guides-ingestion-04-field-mapping.md) to learn how to transform parsed data for Fluent Commerce.
|
|
1045
|
+
|
|
1046
|
+
---
|
|
1047
|
+
|
|
1048
|
+
**Related Documentation:**
|
|
1049
|
+
- [S3DataSource API Reference](../../api-reference/modules/api-reference-06-data-sources.md#s3-data-source)
|
|
1050
|
+
- [SftpDataSource API Reference](../../api-reference/modules/api-reference-06-data-sources.md#sftp-data-source)
|
|
1051
|
+
- [SFTP Data Source Guide](../../data-sources/modules/data-sources-03-sftp-operations.md#connection-pooling)
|
|
1052
|
+
- [Error Handling Patterns](../../../03-PATTERN-GUIDES/error-handling/error-handling-readme.md#retry-patterns)
|