@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,836 +1,836 @@
|
|
|
1
|
-
# Data Sources - Quick Reference
|
|
2
|
-
|
|
3
|
-
> One-page cheat sheet for S3DataSource and SftpDataSource
|
|
4
|
-
|
|
5
|
-
## Quick Decision: S3 vs SFTP
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
Need to choose between S3 and SFTP?
|
|
9
|
-
|
|
10
|
-
Do you control the infrastructure?
|
|
11
|
-
├─ YES → Is this cloud-native?
|
|
12
|
-
│ ├─ YES → Use S3
|
|
13
|
-
│ └─ NO → Is volume high (>10K/hour)?
|
|
14
|
-
│ ├─ YES → Use S3
|
|
15
|
-
│ └─ NO → Either (your preference)
|
|
16
|
-
└─ NO → Use SFTP (vendor requirement)
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## S3DataSource - Quick Start
|
|
22
|
-
|
|
23
|
-
### Basic Configuration
|
|
24
|
-
|
|
25
|
-
```typescript
|
|
26
|
-
import {
|
|
27
|
-
S3DataSource,
|
|
28
|
-
createConsoleLogger,
|
|
29
|
-
toStructuredLogger
|
|
30
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
31
|
-
|
|
32
|
-
const logger = toStructuredLogger(createConsoleLogger(), {
|
|
33
|
-
logLevel: 'info'
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
const s3Source = new S3DataSource(
|
|
37
|
-
{
|
|
38
|
-
type: 'S3_CSV', // S3_JSON, S3_XML, S3_PARQUET
|
|
39
|
-
connectionId: 's3-quick-ref',
|
|
40
|
-
name: 'S3 Quick Ref',
|
|
41
|
-
s3Config: {
|
|
42
|
-
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
43
|
-
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
44
|
-
region: 'us-east-1',
|
|
45
|
-
bucket: 'my-bucket', // Required
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
logger
|
|
49
|
-
);
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### Common Operations
|
|
53
|
-
|
|
54
|
-
```typescript
|
|
55
|
-
// List files
|
|
56
|
-
const files = await s3Source.listFiles({ prefix: 'inventory/' });
|
|
57
|
-
|
|
58
|
-
// Read file
|
|
59
|
-
const content = await s3Source.downloadFile('file.csv');
|
|
60
|
-
|
|
61
|
-
// Write file
|
|
62
|
-
await s3Source.uploadFile('output.csv', csvData, {
|
|
63
|
-
contentType: 'text/csv',
|
|
64
|
-
metadata: { 'processed-date': new Date().toISOString() },
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// Delete file
|
|
68
|
-
await s3Source.deleteFile('old-file.csv');
|
|
69
|
-
|
|
70
|
-
// Copy file
|
|
71
|
-
await s3Source.copyFile('source.csv', 'archive/source.csv');
|
|
72
|
-
|
|
73
|
-
// Move file (copy + delete)
|
|
74
|
-
await s3Source.moveFile('incoming/file.csv', 'processed/file.csv');
|
|
75
|
-
|
|
76
|
-
// Check existence
|
|
77
|
-
// For S3 existence, list by prefix or handle 404 from download
|
|
78
|
-
|
|
79
|
-
// Validate connection
|
|
80
|
-
const isValid = await s3Source.validateConnection();
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
### Parquet Generation
|
|
84
|
-
|
|
85
|
-
```typescript
|
|
86
|
-
// Generate Parquet buffer
|
|
87
|
-
const records = [
|
|
88
|
-
{ id: 1, sku: 'PROD-001', quantity: 100, price: 29.99 },
|
|
89
|
-
{ id: 2, sku: 'PROD-002', quantity: 200, price: 39.99 },
|
|
90
|
-
];
|
|
91
|
-
|
|
92
|
-
const parquetBuffer = await s3Source.writeParquetContent(records, {
|
|
93
|
-
compression: 'GZIP', // UNCOMPRESSED, GZIP, SNAPPY, BROTLI
|
|
94
|
-
rowGroupSize: 50000,
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// Upload to S3
|
|
98
|
-
await s3Source.writeFile('s3://bucket/data.parquet', parquetBuffer, {
|
|
99
|
-
contentType: 'application/octet-stream',
|
|
100
|
-
});
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
---
|
|
104
|
-
|
|
105
|
-
## SftpDataSource - Quick Start
|
|
106
|
-
|
|
107
|
-
### Basic Configuration
|
|
108
|
-
|
|
109
|
-
```typescript
|
|
110
|
-
import {
|
|
111
|
-
SftpDataSource,
|
|
112
|
-
createConsoleLogger,
|
|
113
|
-
toStructuredLogger
|
|
114
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
115
|
-
|
|
116
|
-
const logger = toStructuredLogger(createConsoleLogger(), {
|
|
117
|
-
logLevel: 'info'
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
const sftpSource = new SftpDataSource(
|
|
121
|
-
{
|
|
122
|
-
type: 'SFTP_CSV', // SFTP_JSON, SFTP_JSONL, SFTP_PARQUET
|
|
123
|
-
connectionId: 'vendor-sftp',
|
|
124
|
-
name: 'Vendor SFTP Connection',
|
|
125
|
-
settings: {
|
|
126
|
-
host: 'sftp.vendor.com',
|
|
127
|
-
port: 22,
|
|
128
|
-
username: 'integration',
|
|
129
|
-
privateKey: process.env.SFTP_PRIVATE_KEY, // or password
|
|
130
|
-
remotePath: '/data/inventory',
|
|
131
|
-
filePattern: '*.csv',
|
|
132
|
-
},
|
|
133
|
-
},
|
|
134
|
-
logger
|
|
135
|
-
);
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
### Common Operations
|
|
139
|
-
|
|
140
|
-
```typescript
|
|
141
|
-
// List files
|
|
142
|
-
const files = await sftpSource.listFiles({
|
|
143
|
-
remotePath: '/data/current',
|
|
144
|
-
filePattern: '*.csv',
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// Read file
|
|
148
|
-
const content = await sftpSource.downloadFile('/data/inventory.csv');
|
|
149
|
-
|
|
150
|
-
// Write file
|
|
151
|
-
await sftpSource.writeFile('/exports/output.csv', csvData, {
|
|
152
|
-
createDirectories: true,
|
|
153
|
-
overwrite: false,
|
|
154
|
-
permissions: '0644',
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
// Delete file
|
|
158
|
-
await sftpSource.deleteFile('/temp/old-file.csv');
|
|
159
|
-
|
|
160
|
-
// Copy file (download + upload)
|
|
161
|
-
await sftpSource.copyFile(
|
|
162
|
-
'/source/file.csv',
|
|
163
|
-
'/backup/file.csv',
|
|
164
|
-
true // overwrite
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
// Move file (atomic rename)
|
|
168
|
-
await sftpSource.moveFile(
|
|
169
|
-
'/incoming/file.csv',
|
|
170
|
-
'/processed/file.csv',
|
|
171
|
-
false // don't overwrite
|
|
172
|
-
);
|
|
173
|
-
|
|
174
|
-
// Create directory
|
|
175
|
-
await sftpSource.createDirectory('/exports/daily', true, '0755');
|
|
176
|
-
|
|
177
|
-
// Check existence
|
|
178
|
-
const fileExists = await sftpSource.fileExists('/data/file.csv');
|
|
179
|
-
const dirExists = await sftpSource.directoryExists('/data/exports');
|
|
180
|
-
|
|
181
|
-
// Validate connection
|
|
182
|
-
const isValid = await sftpSource.validateConnection();
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
---
|
|
186
|
-
|
|
187
|
-
## Authentication
|
|
188
|
-
|
|
189
|
-
### S3 (AWS Credentials)
|
|
190
|
-
|
|
191
|
-
```bash
|
|
192
|
-
# Environment variables
|
|
193
|
-
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
|
|
194
|
-
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
|
|
195
|
-
AWS_REGION=us-east-1
|
|
196
|
-
AWS_SESSION_TOKEN=optional-session-token # For temporary credentials
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
### SFTP (SSH)
|
|
200
|
-
|
|
201
|
-
**Standalone - Password Auth**:
|
|
202
|
-
|
|
203
|
-
```typescript
|
|
204
|
-
settings: {
|
|
205
|
-
host: 'sftp.vendor.com',
|
|
206
|
-
username: 'integration',
|
|
207
|
-
password: process.env.SFTP_PASSWORD
|
|
208
|
-
}
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
**Standalone - Private Key Auth (Recommended)**:
|
|
212
|
-
|
|
213
|
-
```typescript
|
|
214
|
-
settings: {
|
|
215
|
-
host: 'sftp.vendor.com',
|
|
216
|
-
username: 'integration',
|
|
217
|
-
privateKey: process.env.SFTP_PRIVATE_KEY,
|
|
218
|
-
passphrase: process.env.SFTP_PASSPHRASE // If key is encrypted
|
|
219
|
-
}
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
```bash
|
|
223
|
-
# Environment variable (PEM format, NOT file path)
|
|
224
|
-
SFTP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
|
|
225
|
-
MIIEpAIBAAKCAQEA...
|
|
226
|
-
-----END RSA PRIVATE KEY-----"
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
---
|
|
230
|
-
|
|
231
|
-
## SFTP Credentials (Versori Platform)
|
|
232
|
-
|
|
233
|
-
**For secure credential access in Versori connectors**, use one of these methods:
|
|
234
|
-
|
|
235
|
-
### Method 1: activation.connections (Recommended)
|
|
236
|
-
|
|
237
|
-
```typescript
|
|
238
|
-
// Already decoded - no Buffer needed!
|
|
239
|
-
const allConns = ctx.activation.connections || [];
|
|
240
|
-
const sftpConn = allConns.find(c => c.name === 'versori_ftp_server');
|
|
241
|
-
const { username, password } = sftpConn.credentials[0].credential.data.basicAuth;
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
### Method 2: credentials().get()
|
|
245
|
-
|
|
246
|
-
```typescript
|
|
247
|
-
import { Buffer } from 'node:buffer';
|
|
248
|
-
|
|
249
|
-
// Requires base64 decoding
|
|
250
|
-
const sftpCred = await ctx.credentials().getAccessToken('SFTP');
|
|
251
|
-
const rawBasicAuth = Buffer.from(sftpCred.accessToken, 'base64').toString('utf-8');
|
|
252
|
-
const [username, password] = rawBasicAuth.split(':');
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
**See:** [sftp-credential-access-security.md](./data-sources-sftp-credential-access-security.md) for complete guide with error handling, validation, and security best practices.
|
|
256
|
-
|
|
257
|
-
---
|
|
258
|
-
|
|
259
|
-
## Dual AWS Credentials (Source + Target)
|
|
260
|
-
|
|
261
|
-
```typescript
|
|
262
|
-
// Source S3 (Vendor - READ)
|
|
263
|
-
const sourceS3 = new S3DataSource(
|
|
264
|
-
{
|
|
265
|
-
type: 'S3_CSV',
|
|
266
|
-
s3Config: {
|
|
267
|
-
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
268
|
-
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
269
|
-
region: 'us-east-1',
|
|
270
|
-
bucket: process.env.SOURCE_BUCKET,
|
|
271
|
-
},
|
|
272
|
-
},
|
|
273
|
-
logger
|
|
274
|
-
);
|
|
275
|
-
|
|
276
|
-
// Target S3 (Your Data Lake - WRITE)
|
|
277
|
-
const targetS3 = new S3DataSource(
|
|
278
|
-
{
|
|
279
|
-
type: 'S3_PARQUET',
|
|
280
|
-
s3Config: {
|
|
281
|
-
accessKeyId: process.env.TARGET_AWS_ACCESS_KEY_ID!,
|
|
282
|
-
secretAccessKey: process.env.TARGET_AWS_SECRET_ACCESS_KEY!,
|
|
283
|
-
region: 'us-west-2',
|
|
284
|
-
bucket: process.env.TARGET_BUCKET,
|
|
285
|
-
},
|
|
286
|
-
},
|
|
287
|
-
logger
|
|
288
|
-
);
|
|
289
|
-
|
|
290
|
-
// Read from vendor, write to your data lake
|
|
291
|
-
const content = await sourceS3.downloadFile('inventory.csv');
|
|
292
|
-
const processed = await processData(content);
|
|
293
|
-
await targetS3.writeFile('s3://your-bucket/processed.parquet', processed);
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
---
|
|
297
|
-
|
|
298
|
-
## File Processing Patterns
|
|
299
|
-
|
|
300
|
-
### Buffering (Small Files < 10MB)
|
|
301
|
-
|
|
302
|
-
```typescript
|
|
303
|
-
// Load entire file into memory
|
|
304
|
-
const content = await dataSource.downloadFile(file.path);
|
|
305
|
-
const records = await csvParser.parse(content as string);
|
|
306
|
-
await sendToFluent(records);
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
### Chunked Processing (Large Files > 10MB)
|
|
310
|
-
|
|
311
|
-
```typescript
|
|
312
|
-
const content = await dataSource.downloadFile(file, { encoding: 'binary' });
|
|
313
|
-
|
|
314
|
-
const chunkSize = 1024 * 1024; // 1MB chunks
|
|
315
|
-
for (let offset = 0; offset < (content as Buffer).length; offset += chunkSize) {
|
|
316
|
-
const chunk = (content as Buffer).slice(offset, offset + chunkSize);
|
|
317
|
-
await processChunk(chunk);
|
|
318
|
-
}
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
### Batch Processing
|
|
322
|
-
|
|
323
|
-
```typescript
|
|
324
|
-
const batchSize = 100;
|
|
325
|
-
for (let i = 0; i < records.length; i += batchSize) {
|
|
326
|
-
const batch = records.slice(i, i + batchSize);
|
|
327
|
-
await client.sendBatch(jobId, { entities: batch });
|
|
328
|
-
}
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
---
|
|
332
|
-
|
|
333
|
-
## State Management (Prevent Duplicates)
|
|
334
|
-
|
|
335
|
-
```typescript
|
|
336
|
-
import { StateService, VersoriKVAdapter } from '@fluentcommerce/fc-connect-sdk';
|
|
337
|
-
|
|
338
|
-
const stateService = new StateService(new VersoriKVAdapter(openKv()));
|
|
339
|
-
|
|
340
|
-
for (const file of files) {
|
|
341
|
-
// Create unique state key
|
|
342
|
-
const stateKey = `processed:${file.path}:${file.lastModified.toISOString()}`;
|
|
343
|
-
|
|
344
|
-
// Check if already processed
|
|
345
|
-
if (await stateService.isFileProcessed(stateKey)) {
|
|
346
|
-
continue;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// Process file
|
|
350
|
-
const content = await dataSource.downloadFile(file.path);
|
|
351
|
-
await processContent(content);
|
|
352
|
-
|
|
353
|
-
// Mark as processed
|
|
354
|
-
await stateService.markFileProcessed(stateKey, {
|
|
355
|
-
fileName: file.name,
|
|
356
|
-
processedAt: new Date().toISOString(),
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
---
|
|
362
|
-
|
|
363
|
-
## Format-Specific Processing
|
|
364
|
-
|
|
365
|
-
### CSV
|
|
366
|
-
|
|
367
|
-
```typescript
|
|
368
|
-
import { CSVParserService } from '@fluentcommerce/fc-connect-sdk';
|
|
369
|
-
|
|
370
|
-
const csvParser = new CSVParserService();
|
|
371
|
-
const content = await dataSource.downloadFile(file);
|
|
372
|
-
const records = await csvParser.parse(content as string, {
|
|
373
|
-
columns: true,
|
|
374
|
-
skip_empty_lines: true,
|
|
375
|
-
trim: true,
|
|
376
|
-
});
|
|
377
|
-
```
|
|
378
|
-
|
|
379
|
-
### JSON
|
|
380
|
-
|
|
381
|
-
```typescript
|
|
382
|
-
const content = await dataSource.downloadFile(file);
|
|
383
|
-
const data = JSON.parse(content);
|
|
384
|
-
const records = Array.isArray(data) ? data : [data];
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
### JSON Lines (JSONL)
|
|
388
|
-
|
|
389
|
-
```typescript
|
|
390
|
-
const content = await dataSource.downloadFile(file);
|
|
391
|
-
const lines = content.split('\n').filter(line => line.trim());
|
|
392
|
-
const records = lines.map(line => JSON.parse(line));
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
### Parquet
|
|
396
|
-
|
|
397
|
-
```typescript
|
|
398
|
-
import { ParquetParserService } from '@fluentcommerce/fc-connect-sdk';
|
|
399
|
-
|
|
400
|
-
const parquetParser = new ParquetParserService(logger);
|
|
401
|
-
const buffer = await dataSource.downloadFile(file, { encoding: 'binary' });
|
|
402
|
-
const records = await parquetParser.parse(buffer);
|
|
403
|
-
```
|
|
404
|
-
|
|
405
|
-
---
|
|
406
|
-
|
|
407
|
-
## Error Handling
|
|
408
|
-
|
|
409
|
-
### Retry with Exponential Backoff
|
|
410
|
-
|
|
411
|
-
```typescript
|
|
412
|
-
async function processWithRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {
|
|
413
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
414
|
-
try {
|
|
415
|
-
return await fn();
|
|
416
|
-
} catch (error: any) {
|
|
417
|
-
// Don't retry client errors (4xx)
|
|
418
|
-
if (error.statusCode && error.statusCode >= 400 && error.statusCode < 500) {
|
|
419
|
-
throw error;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
if (attempt < maxRetries) {
|
|
423
|
-
const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
|
|
424
|
-
await sleep(delay);
|
|
425
|
-
} else {
|
|
426
|
-
throw error;
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
function sleep(ms: number): Promise<void> {
|
|
433
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
434
|
-
}
|
|
435
|
-
```
|
|
436
|
-
|
|
437
|
-
### Partial Failure Handling
|
|
438
|
-
|
|
439
|
-
```typescript
|
|
440
|
-
const results = { successful: 0, failed: 0, errors: [] };
|
|
441
|
-
|
|
442
|
-
for (let i = 0; i < records.length; i += batchSize) {
|
|
443
|
-
const batch = records.slice(i, i + batchSize);
|
|
444
|
-
|
|
445
|
-
try {
|
|
446
|
-
await client.sendBatch(jobId, { entities: batch });
|
|
447
|
-
results.successful += batch.length;
|
|
448
|
-
} catch (error) {
|
|
449
|
-
results.failed += batch.length;
|
|
450
|
-
results.errors.push({ batch: i / batchSize, error: error.message });
|
|
451
|
-
// Continue processing remaining batches
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
```
|
|
455
|
-
|
|
456
|
-
---
|
|
457
|
-
|
|
458
|
-
## Monitoring
|
|
459
|
-
|
|
460
|
-
### Structured Logging
|
|
461
|
-
|
|
462
|
-
```typescript
|
|
463
|
-
import {
|
|
464
|
-
createConsoleLogger,
|
|
465
|
-
toStructuredLogger
|
|
466
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
467
|
-
|
|
468
|
-
// Versori Platform - Use native log
|
|
469
|
-
const { log } = ctx; // Native Versori log
|
|
470
|
-
log.info('Processing file started', {
|
|
471
|
-
fileName: file.name,
|
|
472
|
-
fileSize: file.size,
|
|
473
|
-
});
|
|
474
|
-
|
|
475
|
-
// Standalone - Use function-based utilities
|
|
476
|
-
const logger = toStructuredLogger(createConsoleLogger(), {
|
|
477
|
-
service: 'inventory-sync',
|
|
478
|
-
version: '1.0',
|
|
479
|
-
logLevel: 'info'
|
|
480
|
-
});
|
|
481
|
-
|
|
482
|
-
logger.info('Processing file started', {
|
|
483
|
-
fileName: file.name,
|
|
484
|
-
fileSize: file.size,
|
|
485
|
-
});
|
|
486
|
-
|
|
487
|
-
logger.error('Processing failed', error, {
|
|
488
|
-
fileName: file.name,
|
|
489
|
-
duration: Date.now() - startTime,
|
|
490
|
-
});
|
|
491
|
-
```
|
|
492
|
-
|
|
493
|
-
### Health Checks
|
|
494
|
-
|
|
495
|
-
```typescript
|
|
496
|
-
async function healthCheck() {
|
|
497
|
-
const s3Valid = await s3Source.validateConnection();
|
|
498
|
-
const sftpValid = await sftpSource.validateConnection();
|
|
499
|
-
|
|
500
|
-
if (!s3Valid || !sftpValid) {
|
|
501
|
-
throw new Error('Data source connectivity check failed');
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
```
|
|
505
|
-
|
|
506
|
-
---
|
|
507
|
-
|
|
508
|
-
## Common Patterns
|
|
509
|
-
|
|
510
|
-
### Pattern 1: S3 Inventory Ingestion
|
|
511
|
-
|
|
512
|
-
```typescript
|
|
513
|
-
import {
|
|
514
|
-
S3DataSource,
|
|
515
|
-
CSVParserService,
|
|
516
|
-
UniversalMapper,
|
|
517
|
-
StateService,
|
|
518
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
519
|
-
|
|
520
|
-
async function ingestInventory() {
|
|
521
|
-
const s3 = new S3DataSource(
|
|
522
|
-
{
|
|
523
|
-
/* config */
|
|
524
|
-
},
|
|
525
|
-
logger
|
|
526
|
-
);
|
|
527
|
-
const csvParser = new CSVParserService();
|
|
528
|
-
const mapper = new UniversalMapper(mappingConfig);
|
|
529
|
-
const stateService = new StateService(kvAdapter);
|
|
530
|
-
|
|
531
|
-
const files = await s3.listFiles({ prefix: 'inventory/' });
|
|
532
|
-
|
|
533
|
-
for (const file of files) {
|
|
534
|
-
const stateKey = `processed:${file.etag}`;
|
|
535
|
-
if (await stateService.isFileProcessed(stateKey)) continue;
|
|
536
|
-
|
|
537
|
-
const content = await s3.downloadFile(file.path);
|
|
538
|
-
const records = await csvParser.parse(content);
|
|
539
|
-
const result = await mapper.map(records);
|
|
540
|
-
|
|
541
|
-
if (result.success) {
|
|
542
|
-
await sendToFluent(result.data);
|
|
543
|
-
await stateService.markFileProcessed(stateKey, {
|
|
544
|
-
fileName: file.name,
|
|
545
|
-
recordCount: records.length,
|
|
546
|
-
});
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
```
|
|
551
|
-
|
|
552
|
-
### Pattern 2: SFTP Vendor Sync
|
|
553
|
-
|
|
554
|
-
```typescript
|
|
555
|
-
import { SftpDataSource, CSVParserService, StateService } from '@fluentcommerce/fc-connect-sdk';
|
|
556
|
-
|
|
557
|
-
async function syncVendorInventory() {
|
|
558
|
-
const sftp = new SftpDataSource(
|
|
559
|
-
{
|
|
560
|
-
/* config */
|
|
561
|
-
},
|
|
562
|
-
logger
|
|
563
|
-
);
|
|
564
|
-
const stateService = new StateService(kvAdapter);
|
|
565
|
-
|
|
566
|
-
const files = await sftp.listFiles({
|
|
567
|
-
remotePath: '/outbound/inventory',
|
|
568
|
-
filePattern: 'inventory_*.csv',
|
|
569
|
-
});
|
|
570
|
-
|
|
571
|
-
for (const file of files) {
|
|
572
|
-
const stateKey = `processed:${file.name}:${file.lastModified.toISOString()}`;
|
|
573
|
-
if (await stateService.isFileProcessed(stateKey)) continue;
|
|
574
|
-
|
|
575
|
-
const content = await sftp.downloadFile(file.path);
|
|
576
|
-
const records = await csvParser.parse(content);
|
|
577
|
-
await sendToFluent(records);
|
|
578
|
-
|
|
579
|
-
await stateService.markFileProcessed(stateKey);
|
|
580
|
-
|
|
581
|
-
// Move to processed folder
|
|
582
|
-
await sftp.moveFile(file.path, file.path.replace('/outbound/', '/processed/'));
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
```
|
|
586
|
-
|
|
587
|
-
### Pattern 3: Dual AWS Accounts (Read + Write)
|
|
588
|
-
|
|
589
|
-
```typescript
|
|
590
|
-
const sourceS3 = new S3DataSource(
|
|
591
|
-
{
|
|
592
|
-
type: 'S3_CSV',
|
|
593
|
-
s3Config: {
|
|
594
|
-
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
595
|
-
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
596
|
-
region: 'us-east-1',
|
|
597
|
-
},
|
|
598
|
-
},
|
|
599
|
-
logger
|
|
600
|
-
);
|
|
601
|
-
|
|
602
|
-
const targetS3 = new S3DataSource(
|
|
603
|
-
{
|
|
604
|
-
type: 'S3_PARQUET',
|
|
605
|
-
s3Config: {
|
|
606
|
-
accessKeyId: process.env.TARGET_AWS_ACCESS_KEY_ID!,
|
|
607
|
-
secretAccessKey: process.env.TARGET_AWS_SECRET_ACCESS_KEY!,
|
|
608
|
-
region: 'us-west-2',
|
|
609
|
-
},
|
|
610
|
-
},
|
|
611
|
-
logger
|
|
612
|
-
);
|
|
613
|
-
|
|
614
|
-
// Read from vendor, write to data lake
|
|
615
|
-
const content = await sourceS3.downloadFile('inventory.csv');
|
|
616
|
-
const processed = await processData(content);
|
|
617
|
-
await targetS3.writeFile('s3://datalake/processed.parquet', processed);
|
|
618
|
-
```
|
|
619
|
-
|
|
620
|
-
---
|
|
621
|
-
|
|
622
|
-
## Configuration Reference
|
|
623
|
-
|
|
624
|
-
### S3DataSource Config
|
|
625
|
-
|
|
626
|
-
```typescript
|
|
627
|
-
interface S3DataSourceConfig {
|
|
628
|
-
type: 'S3_CSV' | 'S3_JSON' | 'S3_XML' | 'S3_PARQUET';
|
|
629
|
-
s3Config: {
|
|
630
|
-
accessKeyId: string;
|
|
631
|
-
secretAccessKey: string;
|
|
632
|
-
region: string;
|
|
633
|
-
bucket: string; // Required
|
|
634
|
-
sessionToken?: string;
|
|
635
|
-
endpoint?: string; // For LocalStack
|
|
636
|
-
maxAttempts?: number; // Default: 3
|
|
637
|
-
};
|
|
638
|
-
}
|
|
639
|
-
```
|
|
640
|
-
|
|
641
|
-
### SftpDataSource Config
|
|
642
|
-
|
|
643
|
-
```typescript
|
|
644
|
-
interface SftpDataSourceConfig {
|
|
645
|
-
type: 'SFTP_CSV' | 'SFTP_JSON' | 'SFTP_JSONL' | 'SFTP_PARQUET';
|
|
646
|
-
connectionId: string;
|
|
647
|
-
name?: string;
|
|
648
|
-
settings: {
|
|
649
|
-
host: string;
|
|
650
|
-
port?: number; // Default: 22
|
|
651
|
-
username: string;
|
|
652
|
-
password?: string;
|
|
653
|
-
privateKey?: string;
|
|
654
|
-
passphrase?: string;
|
|
655
|
-
remotePath?: string;
|
|
656
|
-
filePattern?: string;
|
|
657
|
-
maxConnections?: number; // Default: 5
|
|
658
|
-
// Note: connectionWaitTimeout is fixed at 30000ms (30 seconds), not user-configurable
|
|
659
|
-
connectionTimeout?: number; // Default: 30000ms
|
|
660
|
-
keepaliveInterval?: number; // Default: 5000ms
|
|
661
|
-
retry?: SftpRetryConfig; // Retry configuration
|
|
662
|
-
writeCreateDirectories?: boolean; // Default: true
|
|
663
|
-
writeOverwrite?: boolean; // Default: false
|
|
664
|
-
writePermissions?: string; // Default: '0644'
|
|
665
|
-
};
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
interface SftpRetryConfig {
|
|
669
|
-
maxAttempts?: number; // Default: 3
|
|
670
|
-
baseDelayMs?: number; // Default: 1000
|
|
671
|
-
backoffFactor?: number; // Default: 2 (exponential)
|
|
672
|
-
maxDelayMs?: number; // Default: 8000
|
|
673
|
-
jitter?: 'none' | 'full'; // Default: 'full'
|
|
674
|
-
reconnectOnFailure?: boolean; // Default: true
|
|
675
|
-
isRetryable?: (error: Error) => boolean; // Custom retry predicate
|
|
676
|
-
}
|
|
677
|
-
```
|
|
678
|
-
|
|
679
|
-
---
|
|
680
|
-
|
|
681
|
-
## Built-in Features
|
|
682
|
-
|
|
683
|
-
### S3 Automatic Retry
|
|
684
|
-
|
|
685
|
-
**S3DataSource includes built-in retry logic** - no additional code needed!
|
|
686
|
-
|
|
687
|
-
```typescript
|
|
688
|
-
const s3Source = new S3DataSource({
|
|
689
|
-
type: 'S3_CSV',
|
|
690
|
-
s3Config: {
|
|
691
|
-
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
692
|
-
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
693
|
-
region: 'us-east-1',
|
|
694
|
-
bucket: 'my-bucket',
|
|
695
|
-
maxAttempts: 5 // Default: 3
|
|
696
|
-
}
|
|
697
|
-
}, logger);
|
|
698
|
-
|
|
699
|
-
// All operations retry automatically:
|
|
700
|
-
await s3Source.downloadFile('file.csv'); // Retries on 429, 500, 502, 503, 504, network errors
|
|
701
|
-
await s3Source.uploadFile('output.json', data); // Retries automatically
|
|
702
|
-
```
|
|
703
|
-
|
|
704
|
-
**Retry Configuration:**
|
|
705
|
-
- **Default attempts**: 3
|
|
706
|
-
- **Retryable errors**: 429, 500, 502, 503, 504, network errors
|
|
707
|
-
- **Backoff**: Exponential (500ms × 2^attempt, max 8s)
|
|
708
|
-
- **Total wait time**: Up to 7.5s (with 5 attempts)
|
|
709
|
-
|
|
710
|
-
### SFTP Enhanced Retry
|
|
711
|
-
|
|
712
|
-
**SftpDataSource includes enhanced retry logic** - configurable with exponential backoff, jitter, and custom predicates!
|
|
713
|
-
|
|
714
|
-
```typescript
|
|
715
|
-
const sftpSource = new SftpDataSource({
|
|
716
|
-
type: 'SFTP_CSV',
|
|
717
|
-
connectionId: 'vendor-sftp',
|
|
718
|
-
settings: {
|
|
719
|
-
host: 'sftp.vendor.com',
|
|
720
|
-
username: 'integration',
|
|
721
|
-
privateKey: process.env.SFTP_PRIVATE_KEY,
|
|
722
|
-
|
|
723
|
-
// Enhanced retry configuration (optional)
|
|
724
|
-
retry: {
|
|
725
|
-
maxAttempts: 5, // Default: 3
|
|
726
|
-
baseDelayMs: 1000, // Default: 1000
|
|
727
|
-
backoffFactor: 2, // Default: 2 (exponential)
|
|
728
|
-
maxDelayMs: 8000, // Default: 8000
|
|
729
|
-
jitter: 'full', // Default: 'full' (randomization)
|
|
730
|
-
reconnectOnFailure: true, // Default: true
|
|
731
|
-
|
|
732
|
-
// Optional: Custom retry predicate
|
|
733
|
-
isRetryable: (error: Error) => {
|
|
734
|
-
// Return true to retry, false to fail immediately
|
|
735
|
-
return error.message.includes('ECONNRESET');
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
}, logger);
|
|
740
|
-
|
|
741
|
-
// All operations retry automatically with your configuration:
|
|
742
|
-
await sftpSource.downloadFile('/data/file.csv');
|
|
743
|
-
await sftpSource.uploadFile('/output/file.csv', data);
|
|
744
|
-
```
|
|
745
|
-
|
|
746
|
-
**Retry Configuration:**
|
|
747
|
-
- **Default attempts**: 3
|
|
748
|
-
- **Retryable errors**: Network errors (ECONNRESET, ETIMEDOUT, etc.), transient messages (connection lost, timeout, etc.)
|
|
749
|
-
- **Non-retryable**: Authentication failures, file not found, configuration errors
|
|
750
|
-
- **Backoff**: Exponential (baseDelayMs × 2^attempt, capped at maxDelayMs)
|
|
751
|
-
- **Jitter**: Full jitter (randomization between 0 and calculated delay)
|
|
752
|
-
- **Reconnection**: Automatic reconnection between retry attempts (if enabled)
|
|
753
|
-
|
|
754
|
-
### SFTP Connection Pool Wait Queue
|
|
755
|
-
|
|
756
|
-
**SftpDataSource includes connection wait queue** - handles burst concurrency automatically!
|
|
757
|
-
|
|
758
|
-
```typescript
|
|
759
|
-
// Scenario: 10 simultaneous operations
|
|
760
|
-
const promises = files.map(file => sftpSource.downloadFile(file));
|
|
761
|
-
await Promise.all(promises);
|
|
762
|
-
// First 5: Get connections immediately
|
|
763
|
-
// Remaining 5: Wait in queue (up to 30s timeout)
|
|
764
|
-
// All complete successfully!
|
|
765
|
-
```
|
|
766
|
-
|
|
767
|
-
**Wait Queue Configuration:**
|
|
768
|
-
- **Pool size**: 5 connections (fixed)
|
|
769
|
-
- **Wait timeout**: 30 seconds (fixed)
|
|
770
|
-
- **Queue**: FIFO (First-In-First-Out)
|
|
771
|
-
- **Race protection**: Prevents over-allocation
|
|
772
|
-
|
|
773
|
-
**Production Pattern:**
|
|
774
|
-
|
|
775
|
-
```typescript
|
|
776
|
-
// Use p-limit to stay below pool limit
|
|
777
|
-
import pLimit from 'p-limit';
|
|
778
|
-
|
|
779
|
-
const limit = pLimit(3); // Lower than pool max (5)
|
|
780
|
-
const promises = files.map(file =>
|
|
781
|
-
limit(() => sftpSource.downloadFile(file))
|
|
782
|
-
);
|
|
783
|
-
await Promise.all(promises);
|
|
784
|
-
// Never hits wait queue, optimal performance
|
|
785
|
-
```
|
|
786
|
-
|
|
787
|
-
---
|
|
788
|
-
|
|
789
|
-
## Troubleshooting
|
|
790
|
-
|
|
791
|
-
| Issue | Solution |
|
|
792
|
-
| -------------------------- | ----------------------------------------------- |
|
|
793
|
-
| S3 Access Denied (403) | Check IAM policy, bucket policy, credentials |
|
|
794
|
-
| SFTP Connection Timeout | Check firewall (port 22), verify host reachable |
|
|
795
|
-
| File Already Exists (SFTP) | Use `overwrite: true` or check existence first |
|
|
796
|
-
| Presigned URL Expired | Complete operations within 1 hour |
|
|
797
|
-
| Out of Memory | Use chunked processing for large files |
|
|
798
|
-
|
|
799
|
-
---
|
|
800
|
-
|
|
801
|
-
## Performance Guidelines
|
|
802
|
-
|
|
803
|
-
| File Size | Strategy | Concurrency (S3) | Concurrency (SFTP) |
|
|
804
|
-
| --------- | ------------------ | ---------------- | ------------------ |
|
|
805
|
-
| < 10MB | Buffer entire file | 10-20 | 1-3 |
|
|
806
|
-
| 10-100MB | Buffer or chunked | 5-10 | 1-2 |
|
|
807
|
-
| > 100MB | Chunked processing | 3-5 | 1 |
|
|
808
|
-
|
|
809
|
-
---
|
|
810
|
-
|
|
811
|
-
## Best Practices
|
|
812
|
-
|
|
813
|
-
1. ✅ Use SSH keys for SFTP (not passwords)
|
|
814
|
-
2. ✅ Use dual AWS credentials for source/target separation
|
|
815
|
-
3. ✅ Implement state management to prevent duplicates
|
|
816
|
-
4. ✅ Use retry with exponential backoff for transient errors
|
|
817
|
-
5. ✅ Use structured logging with rich context
|
|
818
|
-
6. ✅ Validate files before processing (size, name pattern, age)
|
|
819
|
-
7. ✅ Use appropriate batch sizes (100-250 records)
|
|
820
|
-
8. ✅ Use chunked processing for large files (> 10MB)
|
|
821
|
-
9. ✅ Set restrictive SFTP file permissions (0600) for sensitive data
|
|
822
|
-
10. ✅ Monitor connection health with regular health checks
|
|
823
|
-
|
|
824
|
-
---
|
|
825
|
-
|
|
826
|
-
## Additional Resources
|
|
827
|
-
|
|
828
|
-
- [Module 1: Foundations](../auto-pagination/modules/auto-pagination-01-foundations.md) - S3 vs SFTP decision guide
|
|
829
|
-
- [Module 2: S3 Operations](./modules/data-sources-02-s3-operations.md) - Complete S3DataSource reference
|
|
830
|
-
- [Module 3: SFTP Operations](./modules/data-sources-03-sftp-operations.md) - Complete SftpDataSource reference
|
|
831
|
-
- [Module 4: File Processing Patterns](./modules/data-sources-04-file-patterns.md) - Streaming, batching, validation
|
|
832
|
-
- [Module 5: Advanced Topics](./modules/data-sources-05-advanced-topics.md) - Security, monitoring, testing
|
|
833
|
-
|
|
834
|
-
---
|
|
835
|
-
|
|
836
|
-
[← Back to Main Guide](./data-sources-readme.md)
|
|
1
|
+
# Data Sources - Quick Reference
|
|
2
|
+
|
|
3
|
+
> One-page cheat sheet for S3DataSource and SftpDataSource
|
|
4
|
+
|
|
5
|
+
## Quick Decision: S3 vs SFTP
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Need to choose between S3 and SFTP?
|
|
9
|
+
|
|
10
|
+
Do you control the infrastructure?
|
|
11
|
+
├─ YES → Is this cloud-native?
|
|
12
|
+
│ ├─ YES → Use S3
|
|
13
|
+
│ └─ NO → Is volume high (>10K/hour)?
|
|
14
|
+
│ ├─ YES → Use S3
|
|
15
|
+
│ └─ NO → Either (your preference)
|
|
16
|
+
└─ NO → Use SFTP (vendor requirement)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## S3DataSource - Quick Start
|
|
22
|
+
|
|
23
|
+
### Basic Configuration
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import {
|
|
27
|
+
S3DataSource,
|
|
28
|
+
createConsoleLogger,
|
|
29
|
+
toStructuredLogger
|
|
30
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
31
|
+
|
|
32
|
+
const logger = toStructuredLogger(createConsoleLogger(), {
|
|
33
|
+
logLevel: 'info'
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const s3Source = new S3DataSource(
|
|
37
|
+
{
|
|
38
|
+
type: 'S3_CSV', // S3_JSON, S3_XML, S3_PARQUET
|
|
39
|
+
connectionId: 's3-quick-ref',
|
|
40
|
+
name: 'S3 Quick Ref',
|
|
41
|
+
s3Config: {
|
|
42
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
43
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
44
|
+
region: 'us-east-1',
|
|
45
|
+
bucket: 'my-bucket', // Required
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
logger
|
|
49
|
+
);
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Common Operations
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
// List files
|
|
56
|
+
const files = await s3Source.listFiles({ prefix: 'inventory/' });
|
|
57
|
+
|
|
58
|
+
// Read file
|
|
59
|
+
const content = await s3Source.downloadFile('file.csv');
|
|
60
|
+
|
|
61
|
+
// Write file
|
|
62
|
+
await s3Source.uploadFile('output.csv', csvData, {
|
|
63
|
+
contentType: 'text/csv',
|
|
64
|
+
metadata: { 'processed-date': new Date().toISOString() },
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Delete file
|
|
68
|
+
await s3Source.deleteFile('old-file.csv');
|
|
69
|
+
|
|
70
|
+
// Copy file
|
|
71
|
+
await s3Source.copyFile('source.csv', 'archive/source.csv');
|
|
72
|
+
|
|
73
|
+
// Move file (copy + delete)
|
|
74
|
+
await s3Source.moveFile('incoming/file.csv', 'processed/file.csv');
|
|
75
|
+
|
|
76
|
+
// Check existence
|
|
77
|
+
// For S3 existence, list by prefix or handle 404 from download
|
|
78
|
+
|
|
79
|
+
// Validate connection
|
|
80
|
+
const isValid = await s3Source.validateConnection();
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Parquet Generation
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
// Generate Parquet buffer
|
|
87
|
+
const records = [
|
|
88
|
+
{ id: 1, sku: 'PROD-001', quantity: 100, price: 29.99 },
|
|
89
|
+
{ id: 2, sku: 'PROD-002', quantity: 200, price: 39.99 },
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
const parquetBuffer = await s3Source.writeParquetContent(records, {
|
|
93
|
+
compression: 'GZIP', // UNCOMPRESSED, GZIP, SNAPPY, BROTLI
|
|
94
|
+
rowGroupSize: 50000,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Upload to S3
|
|
98
|
+
await s3Source.writeFile('s3://bucket/data.parquet', parquetBuffer, {
|
|
99
|
+
contentType: 'application/octet-stream',
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## SftpDataSource - Quick Start
|
|
106
|
+
|
|
107
|
+
### Basic Configuration
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
import {
|
|
111
|
+
SftpDataSource,
|
|
112
|
+
createConsoleLogger,
|
|
113
|
+
toStructuredLogger
|
|
114
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
115
|
+
|
|
116
|
+
const logger = toStructuredLogger(createConsoleLogger(), {
|
|
117
|
+
logLevel: 'info'
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const sftpSource = new SftpDataSource(
|
|
121
|
+
{
|
|
122
|
+
type: 'SFTP_CSV', // SFTP_JSON, SFTP_JSONL, SFTP_PARQUET
|
|
123
|
+
connectionId: 'vendor-sftp',
|
|
124
|
+
name: 'Vendor SFTP Connection',
|
|
125
|
+
settings: {
|
|
126
|
+
host: 'sftp.vendor.com',
|
|
127
|
+
port: 22,
|
|
128
|
+
username: 'integration',
|
|
129
|
+
privateKey: process.env.SFTP_PRIVATE_KEY, // or password
|
|
130
|
+
remotePath: '/data/inventory',
|
|
131
|
+
filePattern: '*.csv',
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
logger
|
|
135
|
+
);
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Common Operations
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
// List files
|
|
142
|
+
const files = await sftpSource.listFiles({
|
|
143
|
+
remotePath: '/data/current',
|
|
144
|
+
filePattern: '*.csv',
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Read file
|
|
148
|
+
const content = await sftpSource.downloadFile('/data/inventory.csv');
|
|
149
|
+
|
|
150
|
+
// Write file
|
|
151
|
+
await sftpSource.writeFile('/exports/output.csv', csvData, {
|
|
152
|
+
createDirectories: true,
|
|
153
|
+
overwrite: false,
|
|
154
|
+
permissions: '0644',
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Delete file
|
|
158
|
+
await sftpSource.deleteFile('/temp/old-file.csv');
|
|
159
|
+
|
|
160
|
+
// Copy file (download + upload)
|
|
161
|
+
await sftpSource.copyFile(
|
|
162
|
+
'/source/file.csv',
|
|
163
|
+
'/backup/file.csv',
|
|
164
|
+
true // overwrite
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
// Move file (atomic rename)
|
|
168
|
+
await sftpSource.moveFile(
|
|
169
|
+
'/incoming/file.csv',
|
|
170
|
+
'/processed/file.csv',
|
|
171
|
+
false // don't overwrite
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
// Create directory
|
|
175
|
+
await sftpSource.createDirectory('/exports/daily', true, '0755');
|
|
176
|
+
|
|
177
|
+
// Check existence
|
|
178
|
+
const fileExists = await sftpSource.fileExists('/data/file.csv');
|
|
179
|
+
const dirExists = await sftpSource.directoryExists('/data/exports');
|
|
180
|
+
|
|
181
|
+
// Validate connection
|
|
182
|
+
const isValid = await sftpSource.validateConnection();
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Authentication
|
|
188
|
+
|
|
189
|
+
### S3 (AWS Credentials)
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
# Environment variables
|
|
193
|
+
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
|
|
194
|
+
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
|
|
195
|
+
AWS_REGION=us-east-1
|
|
196
|
+
AWS_SESSION_TOKEN=optional-session-token # For temporary credentials
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### SFTP (SSH)
|
|
200
|
+
|
|
201
|
+
**Standalone - Password Auth**:
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
settings: {
|
|
205
|
+
host: 'sftp.vendor.com',
|
|
206
|
+
username: 'integration',
|
|
207
|
+
password: process.env.SFTP_PASSWORD
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**Standalone - Private Key Auth (Recommended)**:
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
settings: {
|
|
215
|
+
host: 'sftp.vendor.com',
|
|
216
|
+
username: 'integration',
|
|
217
|
+
privateKey: process.env.SFTP_PRIVATE_KEY,
|
|
218
|
+
passphrase: process.env.SFTP_PASSPHRASE // If key is encrypted
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
# Environment variable (PEM format, NOT file path)
|
|
224
|
+
SFTP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
|
|
225
|
+
MIIEpAIBAAKCAQEA...
|
|
226
|
+
-----END RSA PRIVATE KEY-----"
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## SFTP Credentials (Versori Platform)
|
|
232
|
+
|
|
233
|
+
**For secure credential access in Versori connectors**, use one of these methods:
|
|
234
|
+
|
|
235
|
+
### Method 1: activation.connections (Recommended)
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
// Already decoded - no Buffer needed!
|
|
239
|
+
const allConns = ctx.activation.connections || [];
|
|
240
|
+
const sftpConn = allConns.find(c => c.name === 'versori_ftp_server');
|
|
241
|
+
const { username, password } = sftpConn.credentials[0].credential.data.basicAuth;
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Method 2: credentials().get()
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
import { Buffer } from 'node:buffer';
|
|
248
|
+
|
|
249
|
+
// Requires base64 decoding
|
|
250
|
+
const sftpCred = await ctx.credentials().getAccessToken('SFTP');
|
|
251
|
+
const rawBasicAuth = Buffer.from(sftpCred.accessToken, 'base64').toString('utf-8');
|
|
252
|
+
const [username, password] = rawBasicAuth.split(':');
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**See:** [sftp-credential-access-security.md](./data-sources-sftp-credential-access-security.md) for complete guide with error handling, validation, and security best practices.
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## Dual AWS Credentials (Source + Target)
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
// Source S3 (Vendor - READ)
|
|
263
|
+
const sourceS3 = new S3DataSource(
|
|
264
|
+
{
|
|
265
|
+
type: 'S3_CSV',
|
|
266
|
+
s3Config: {
|
|
267
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
268
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
269
|
+
region: 'us-east-1',
|
|
270
|
+
bucket: process.env.SOURCE_BUCKET,
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
logger
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
// Target S3 (Your Data Lake - WRITE)
|
|
277
|
+
const targetS3 = new S3DataSource(
|
|
278
|
+
{
|
|
279
|
+
type: 'S3_PARQUET',
|
|
280
|
+
s3Config: {
|
|
281
|
+
accessKeyId: process.env.TARGET_AWS_ACCESS_KEY_ID!,
|
|
282
|
+
secretAccessKey: process.env.TARGET_AWS_SECRET_ACCESS_KEY!,
|
|
283
|
+
region: 'us-west-2',
|
|
284
|
+
bucket: process.env.TARGET_BUCKET,
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
logger
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
// Read from vendor, write to your data lake
|
|
291
|
+
const content = await sourceS3.downloadFile('inventory.csv');
|
|
292
|
+
const processed = await processData(content);
|
|
293
|
+
await targetS3.writeFile('s3://your-bucket/processed.parquet', processed);
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## File Processing Patterns
|
|
299
|
+
|
|
300
|
+
### Buffering (Small Files < 10MB)
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
// Load entire file into memory
|
|
304
|
+
const content = await dataSource.downloadFile(file.path);
|
|
305
|
+
const records = await csvParser.parse(content as string);
|
|
306
|
+
await sendToFluent(records);
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Chunked Processing (Large Files > 10MB)
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
const content = await dataSource.downloadFile(file, { encoding: 'binary' });
|
|
313
|
+
|
|
314
|
+
const chunkSize = 1024 * 1024; // 1MB chunks
|
|
315
|
+
for (let offset = 0; offset < (content as Buffer).length; offset += chunkSize) {
|
|
316
|
+
const chunk = (content as Buffer).slice(offset, offset + chunkSize);
|
|
317
|
+
await processChunk(chunk);
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### Batch Processing
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
const batchSize = 100;
|
|
325
|
+
for (let i = 0; i < records.length; i += batchSize) {
|
|
326
|
+
const batch = records.slice(i, i + batchSize);
|
|
327
|
+
await client.sendBatch(jobId, { entities: batch });
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## State Management (Prevent Duplicates)
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
import { StateService, VersoriKVAdapter } from '@fluentcommerce/fc-connect-sdk';
|
|
337
|
+
|
|
338
|
+
const stateService = new StateService(new VersoriKVAdapter(openKv()));
|
|
339
|
+
|
|
340
|
+
for (const file of files) {
|
|
341
|
+
// Create unique state key
|
|
342
|
+
const stateKey = `processed:${file.path}:${file.lastModified.toISOString()}`;
|
|
343
|
+
|
|
344
|
+
// Check if already processed
|
|
345
|
+
if (await stateService.isFileProcessed(stateKey)) {
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Process file
|
|
350
|
+
const content = await dataSource.downloadFile(file.path);
|
|
351
|
+
await processContent(content);
|
|
352
|
+
|
|
353
|
+
// Mark as processed
|
|
354
|
+
await stateService.markFileProcessed(stateKey, {
|
|
355
|
+
fileName: file.name,
|
|
356
|
+
processedAt: new Date().toISOString(),
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
## Format-Specific Processing
|
|
364
|
+
|
|
365
|
+
### CSV
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
import { CSVParserService } from '@fluentcommerce/fc-connect-sdk';
|
|
369
|
+
|
|
370
|
+
const csvParser = new CSVParserService();
|
|
371
|
+
const content = await dataSource.downloadFile(file);
|
|
372
|
+
const records = await csvParser.parse(content as string, {
|
|
373
|
+
columns: true,
|
|
374
|
+
skip_empty_lines: true,
|
|
375
|
+
trim: true,
|
|
376
|
+
});
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### JSON
|
|
380
|
+
|
|
381
|
+
```typescript
|
|
382
|
+
const content = await dataSource.downloadFile(file);
|
|
383
|
+
const data = JSON.parse(content);
|
|
384
|
+
const records = Array.isArray(data) ? data : [data];
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### JSON Lines (JSONL)
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
const content = await dataSource.downloadFile(file);
|
|
391
|
+
const lines = content.split('\n').filter(line => line.trim());
|
|
392
|
+
const records = lines.map(line => JSON.parse(line));
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### Parquet
|
|
396
|
+
|
|
397
|
+
```typescript
|
|
398
|
+
import { ParquetParserService } from '@fluentcommerce/fc-connect-sdk';
|
|
399
|
+
|
|
400
|
+
const parquetParser = new ParquetParserService(logger);
|
|
401
|
+
const buffer = await dataSource.downloadFile(file, { encoding: 'binary' });
|
|
402
|
+
const records = await parquetParser.parse(buffer);
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
## Error Handling
|
|
408
|
+
|
|
409
|
+
### Retry with Exponential Backoff
|
|
410
|
+
|
|
411
|
+
```typescript
|
|
412
|
+
async function processWithRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {
|
|
413
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
414
|
+
try {
|
|
415
|
+
return await fn();
|
|
416
|
+
} catch (error: any) {
|
|
417
|
+
// Don't retry client errors (4xx)
|
|
418
|
+
if (error.statusCode && error.statusCode >= 400 && error.statusCode < 500) {
|
|
419
|
+
throw error;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (attempt < maxRetries) {
|
|
423
|
+
const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
|
|
424
|
+
await sleep(delay);
|
|
425
|
+
} else {
|
|
426
|
+
throw error;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function sleep(ms: number): Promise<void> {
|
|
433
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
434
|
+
}
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### Partial Failure Handling
|
|
438
|
+
|
|
439
|
+
```typescript
|
|
440
|
+
const results = { successful: 0, failed: 0, errors: [] };
|
|
441
|
+
|
|
442
|
+
for (let i = 0; i < records.length; i += batchSize) {
|
|
443
|
+
const batch = records.slice(i, i + batchSize);
|
|
444
|
+
|
|
445
|
+
try {
|
|
446
|
+
await client.sendBatch(jobId, { entities: batch });
|
|
447
|
+
results.successful += batch.length;
|
|
448
|
+
} catch (error) {
|
|
449
|
+
results.failed += batch.length;
|
|
450
|
+
results.errors.push({ batch: i / batchSize, error: error.message });
|
|
451
|
+
// Continue processing remaining batches
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
---
|
|
457
|
+
|
|
458
|
+
## Monitoring
|
|
459
|
+
|
|
460
|
+
### Structured Logging
|
|
461
|
+
|
|
462
|
+
```typescript
|
|
463
|
+
import {
|
|
464
|
+
createConsoleLogger,
|
|
465
|
+
toStructuredLogger
|
|
466
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
467
|
+
|
|
468
|
+
// Versori Platform - Use native log
|
|
469
|
+
const { log } = ctx; // Native Versori log
|
|
470
|
+
log.info('Processing file started', {
|
|
471
|
+
fileName: file.name,
|
|
472
|
+
fileSize: file.size,
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
// Standalone - Use function-based utilities
|
|
476
|
+
const logger = toStructuredLogger(createConsoleLogger(), {
|
|
477
|
+
service: 'inventory-sync',
|
|
478
|
+
version: '1.0',
|
|
479
|
+
logLevel: 'info'
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
logger.info('Processing file started', {
|
|
483
|
+
fileName: file.name,
|
|
484
|
+
fileSize: file.size,
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
logger.error('Processing failed', error, {
|
|
488
|
+
fileName: file.name,
|
|
489
|
+
duration: Date.now() - startTime,
|
|
490
|
+
});
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
### Health Checks
|
|
494
|
+
|
|
495
|
+
```typescript
|
|
496
|
+
async function healthCheck() {
|
|
497
|
+
const s3Valid = await s3Source.validateConnection();
|
|
498
|
+
const sftpValid = await sftpSource.validateConnection();
|
|
499
|
+
|
|
500
|
+
if (!s3Valid || !sftpValid) {
|
|
501
|
+
throw new Error('Data source connectivity check failed');
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
---
|
|
507
|
+
|
|
508
|
+
## Common Patterns
|
|
509
|
+
|
|
510
|
+
### Pattern 1: S3 Inventory Ingestion
|
|
511
|
+
|
|
512
|
+
```typescript
|
|
513
|
+
import {
|
|
514
|
+
S3DataSource,
|
|
515
|
+
CSVParserService,
|
|
516
|
+
UniversalMapper,
|
|
517
|
+
StateService,
|
|
518
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
519
|
+
|
|
520
|
+
async function ingestInventory() {
|
|
521
|
+
const s3 = new S3DataSource(
|
|
522
|
+
{
|
|
523
|
+
/* config */
|
|
524
|
+
},
|
|
525
|
+
logger
|
|
526
|
+
);
|
|
527
|
+
const csvParser = new CSVParserService();
|
|
528
|
+
const mapper = new UniversalMapper(mappingConfig);
|
|
529
|
+
const stateService = new StateService(kvAdapter);
|
|
530
|
+
|
|
531
|
+
const files = await s3.listFiles({ prefix: 'inventory/' });
|
|
532
|
+
|
|
533
|
+
for (const file of files) {
|
|
534
|
+
const stateKey = `processed:${file.etag}`;
|
|
535
|
+
if (await stateService.isFileProcessed(stateKey)) continue;
|
|
536
|
+
|
|
537
|
+
const content = await s3.downloadFile(file.path);
|
|
538
|
+
const records = await csvParser.parse(content);
|
|
539
|
+
const result = await mapper.map(records);
|
|
540
|
+
|
|
541
|
+
if (result.success) {
|
|
542
|
+
await sendToFluent(result.data);
|
|
543
|
+
await stateService.markFileProcessed(stateKey, {
|
|
544
|
+
fileName: file.name,
|
|
545
|
+
recordCount: records.length,
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
### Pattern 2: SFTP Vendor Sync
|
|
553
|
+
|
|
554
|
+
```typescript
|
|
555
|
+
import { SftpDataSource, CSVParserService, StateService } from '@fluentcommerce/fc-connect-sdk';
|
|
556
|
+
|
|
557
|
+
async function syncVendorInventory() {
|
|
558
|
+
const sftp = new SftpDataSource(
|
|
559
|
+
{
|
|
560
|
+
/* config */
|
|
561
|
+
},
|
|
562
|
+
logger
|
|
563
|
+
);
|
|
564
|
+
const stateService = new StateService(kvAdapter);
|
|
565
|
+
|
|
566
|
+
const files = await sftp.listFiles({
|
|
567
|
+
remotePath: '/outbound/inventory',
|
|
568
|
+
filePattern: 'inventory_*.csv',
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
for (const file of files) {
|
|
572
|
+
const stateKey = `processed:${file.name}:${file.lastModified.toISOString()}`;
|
|
573
|
+
if (await stateService.isFileProcessed(stateKey)) continue;
|
|
574
|
+
|
|
575
|
+
const content = await sftp.downloadFile(file.path);
|
|
576
|
+
const records = await csvParser.parse(content);
|
|
577
|
+
await sendToFluent(records);
|
|
578
|
+
|
|
579
|
+
await stateService.markFileProcessed(stateKey);
|
|
580
|
+
|
|
581
|
+
// Move to processed folder
|
|
582
|
+
await sftp.moveFile(file.path, file.path.replace('/outbound/', '/processed/'));
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
### Pattern 3: Dual AWS Accounts (Read + Write)
|
|
588
|
+
|
|
589
|
+
```typescript
|
|
590
|
+
const sourceS3 = new S3DataSource(
|
|
591
|
+
{
|
|
592
|
+
type: 'S3_CSV',
|
|
593
|
+
s3Config: {
|
|
594
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
595
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
596
|
+
region: 'us-east-1',
|
|
597
|
+
},
|
|
598
|
+
},
|
|
599
|
+
logger
|
|
600
|
+
);
|
|
601
|
+
|
|
602
|
+
const targetS3 = new S3DataSource(
|
|
603
|
+
{
|
|
604
|
+
type: 'S3_PARQUET',
|
|
605
|
+
s3Config: {
|
|
606
|
+
accessKeyId: process.env.TARGET_AWS_ACCESS_KEY_ID!,
|
|
607
|
+
secretAccessKey: process.env.TARGET_AWS_SECRET_ACCESS_KEY!,
|
|
608
|
+
region: 'us-west-2',
|
|
609
|
+
},
|
|
610
|
+
},
|
|
611
|
+
logger
|
|
612
|
+
);
|
|
613
|
+
|
|
614
|
+
// Read from vendor, write to data lake
|
|
615
|
+
const content = await sourceS3.downloadFile('inventory.csv');
|
|
616
|
+
const processed = await processData(content);
|
|
617
|
+
await targetS3.writeFile('s3://datalake/processed.parquet', processed);
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
---
|
|
621
|
+
|
|
622
|
+
## Configuration Reference
|
|
623
|
+
|
|
624
|
+
### S3DataSource Config
|
|
625
|
+
|
|
626
|
+
```typescript
|
|
627
|
+
interface S3DataSourceConfig {
|
|
628
|
+
type: 'S3_CSV' | 'S3_JSON' | 'S3_XML' | 'S3_PARQUET';
|
|
629
|
+
s3Config: {
|
|
630
|
+
accessKeyId: string;
|
|
631
|
+
secretAccessKey: string;
|
|
632
|
+
region: string;
|
|
633
|
+
bucket: string; // Required
|
|
634
|
+
sessionToken?: string;
|
|
635
|
+
endpoint?: string; // For LocalStack
|
|
636
|
+
maxAttempts?: number; // Default: 3
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
### SftpDataSource Config
|
|
642
|
+
|
|
643
|
+
```typescript
|
|
644
|
+
interface SftpDataSourceConfig {
|
|
645
|
+
type: 'SFTP_CSV' | 'SFTP_JSON' | 'SFTP_JSONL' | 'SFTP_PARQUET';
|
|
646
|
+
connectionId: string;
|
|
647
|
+
name?: string;
|
|
648
|
+
settings: {
|
|
649
|
+
host: string;
|
|
650
|
+
port?: number; // Default: 22
|
|
651
|
+
username: string;
|
|
652
|
+
password?: string;
|
|
653
|
+
privateKey?: string;
|
|
654
|
+
passphrase?: string;
|
|
655
|
+
remotePath?: string;
|
|
656
|
+
filePattern?: string;
|
|
657
|
+
maxConnections?: number; // Default: 5
|
|
658
|
+
// Note: connectionWaitTimeout is fixed at 30000ms (30 seconds), not user-configurable
|
|
659
|
+
connectionTimeout?: number; // Default: 30000ms
|
|
660
|
+
keepaliveInterval?: number; // Default: 5000ms
|
|
661
|
+
retry?: SftpRetryConfig; // Retry configuration
|
|
662
|
+
writeCreateDirectories?: boolean; // Default: true
|
|
663
|
+
writeOverwrite?: boolean; // Default: false
|
|
664
|
+
writePermissions?: string; // Default: '0644'
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
interface SftpRetryConfig {
|
|
669
|
+
maxAttempts?: number; // Default: 3
|
|
670
|
+
baseDelayMs?: number; // Default: 1000
|
|
671
|
+
backoffFactor?: number; // Default: 2 (exponential)
|
|
672
|
+
maxDelayMs?: number; // Default: 8000
|
|
673
|
+
jitter?: 'none' | 'full'; // Default: 'full'
|
|
674
|
+
reconnectOnFailure?: boolean; // Default: true
|
|
675
|
+
isRetryable?: (error: Error) => boolean; // Custom retry predicate
|
|
676
|
+
}
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
---
|
|
680
|
+
|
|
681
|
+
## Built-in Features
|
|
682
|
+
|
|
683
|
+
### S3 Automatic Retry
|
|
684
|
+
|
|
685
|
+
**S3DataSource includes built-in retry logic** - no additional code needed!
|
|
686
|
+
|
|
687
|
+
```typescript
|
|
688
|
+
const s3Source = new S3DataSource({
|
|
689
|
+
type: 'S3_CSV',
|
|
690
|
+
s3Config: {
|
|
691
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
692
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
693
|
+
region: 'us-east-1',
|
|
694
|
+
bucket: 'my-bucket',
|
|
695
|
+
maxAttempts: 5 // Default: 3
|
|
696
|
+
}
|
|
697
|
+
}, logger);
|
|
698
|
+
|
|
699
|
+
// All operations retry automatically:
|
|
700
|
+
await s3Source.downloadFile('file.csv'); // Retries on 429, 500, 502, 503, 504, network errors
|
|
701
|
+
await s3Source.uploadFile('output.json', data); // Retries automatically
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
**Retry Configuration:**
|
|
705
|
+
- **Default attempts**: 3
|
|
706
|
+
- **Retryable errors**: 429, 500, 502, 503, 504, network errors
|
|
707
|
+
- **Backoff**: Exponential (500ms × 2^attempt, max 8s)
|
|
708
|
+
- **Total wait time**: Up to 7.5s (with 5 attempts)
|
|
709
|
+
|
|
710
|
+
### SFTP Enhanced Retry
|
|
711
|
+
|
|
712
|
+
**SftpDataSource includes enhanced retry logic** - configurable with exponential backoff, jitter, and custom predicates!
|
|
713
|
+
|
|
714
|
+
```typescript
|
|
715
|
+
const sftpSource = new SftpDataSource({
|
|
716
|
+
type: 'SFTP_CSV',
|
|
717
|
+
connectionId: 'vendor-sftp',
|
|
718
|
+
settings: {
|
|
719
|
+
host: 'sftp.vendor.com',
|
|
720
|
+
username: 'integration',
|
|
721
|
+
privateKey: process.env.SFTP_PRIVATE_KEY,
|
|
722
|
+
|
|
723
|
+
// Enhanced retry configuration (optional)
|
|
724
|
+
retry: {
|
|
725
|
+
maxAttempts: 5, // Default: 3
|
|
726
|
+
baseDelayMs: 1000, // Default: 1000
|
|
727
|
+
backoffFactor: 2, // Default: 2 (exponential)
|
|
728
|
+
maxDelayMs: 8000, // Default: 8000
|
|
729
|
+
jitter: 'full', // Default: 'full' (randomization)
|
|
730
|
+
reconnectOnFailure: true, // Default: true
|
|
731
|
+
|
|
732
|
+
// Optional: Custom retry predicate
|
|
733
|
+
isRetryable: (error: Error) => {
|
|
734
|
+
// Return true to retry, false to fail immediately
|
|
735
|
+
return error.message.includes('ECONNRESET');
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}, logger);
|
|
740
|
+
|
|
741
|
+
// All operations retry automatically with your configuration:
|
|
742
|
+
await sftpSource.downloadFile('/data/file.csv');
|
|
743
|
+
await sftpSource.uploadFile('/output/file.csv', data);
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
**Retry Configuration:**
|
|
747
|
+
- **Default attempts**: 3
|
|
748
|
+
- **Retryable errors**: Network errors (ECONNRESET, ETIMEDOUT, etc.), transient messages (connection lost, timeout, etc.)
|
|
749
|
+
- **Non-retryable**: Authentication failures, file not found, configuration errors
|
|
750
|
+
- **Backoff**: Exponential (baseDelayMs × 2^attempt, capped at maxDelayMs)
|
|
751
|
+
- **Jitter**: Full jitter (randomization between 0 and calculated delay)
|
|
752
|
+
- **Reconnection**: Automatic reconnection between retry attempts (if enabled)
|
|
753
|
+
|
|
754
|
+
### SFTP Connection Pool Wait Queue
|
|
755
|
+
|
|
756
|
+
**SftpDataSource includes connection wait queue** - handles burst concurrency automatically!
|
|
757
|
+
|
|
758
|
+
```typescript
|
|
759
|
+
// Scenario: 10 simultaneous operations
|
|
760
|
+
const promises = files.map(file => sftpSource.downloadFile(file));
|
|
761
|
+
await Promise.all(promises);
|
|
762
|
+
// First 5: Get connections immediately
|
|
763
|
+
// Remaining 5: Wait in queue (up to 30s timeout)
|
|
764
|
+
// All complete successfully!
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
**Wait Queue Configuration:**
|
|
768
|
+
- **Pool size**: 5 connections (fixed)
|
|
769
|
+
- **Wait timeout**: 30 seconds (fixed)
|
|
770
|
+
- **Queue**: FIFO (First-In-First-Out)
|
|
771
|
+
- **Race protection**: Prevents over-allocation
|
|
772
|
+
|
|
773
|
+
**Production Pattern:**
|
|
774
|
+
|
|
775
|
+
```typescript
|
|
776
|
+
// Use p-limit to stay below pool limit
|
|
777
|
+
import pLimit from 'p-limit';
|
|
778
|
+
|
|
779
|
+
const limit = pLimit(3); // Lower than pool max (5)
|
|
780
|
+
const promises = files.map(file =>
|
|
781
|
+
limit(() => sftpSource.downloadFile(file))
|
|
782
|
+
);
|
|
783
|
+
await Promise.all(promises);
|
|
784
|
+
// Never hits wait queue, optimal performance
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
---
|
|
788
|
+
|
|
789
|
+
## Troubleshooting
|
|
790
|
+
|
|
791
|
+
| Issue | Solution |
|
|
792
|
+
| -------------------------- | ----------------------------------------------- |
|
|
793
|
+
| S3 Access Denied (403) | Check IAM policy, bucket policy, credentials |
|
|
794
|
+
| SFTP Connection Timeout | Check firewall (port 22), verify host reachable |
|
|
795
|
+
| File Already Exists (SFTP) | Use `overwrite: true` or check existence first |
|
|
796
|
+
| Presigned URL Expired | Complete operations within 1 hour |
|
|
797
|
+
| Out of Memory | Use chunked processing for large files |
|
|
798
|
+
|
|
799
|
+
---
|
|
800
|
+
|
|
801
|
+
## Performance Guidelines
|
|
802
|
+
|
|
803
|
+
| File Size | Strategy | Concurrency (S3) | Concurrency (SFTP) |
|
|
804
|
+
| --------- | ------------------ | ---------------- | ------------------ |
|
|
805
|
+
| < 10MB | Buffer entire file | 10-20 | 1-3 |
|
|
806
|
+
| 10-100MB | Buffer or chunked | 5-10 | 1-2 |
|
|
807
|
+
| > 100MB | Chunked processing | 3-5 | 1 |
|
|
808
|
+
|
|
809
|
+
---
|
|
810
|
+
|
|
811
|
+
## Best Practices
|
|
812
|
+
|
|
813
|
+
1. ✅ Use SSH keys for SFTP (not passwords)
|
|
814
|
+
2. ✅ Use dual AWS credentials for source/target separation
|
|
815
|
+
3. ✅ Implement state management to prevent duplicates
|
|
816
|
+
4. ✅ Use retry with exponential backoff for transient errors
|
|
817
|
+
5. ✅ Use structured logging with rich context
|
|
818
|
+
6. ✅ Validate files before processing (size, name pattern, age)
|
|
819
|
+
7. ✅ Use appropriate batch sizes (100-250 records)
|
|
820
|
+
8. ✅ Use chunked processing for large files (> 10MB)
|
|
821
|
+
9. ✅ Set restrictive SFTP file permissions (0600) for sensitive data
|
|
822
|
+
10. ✅ Monitor connection health with regular health checks
|
|
823
|
+
|
|
824
|
+
---
|
|
825
|
+
|
|
826
|
+
## Additional Resources
|
|
827
|
+
|
|
828
|
+
- [Module 1: Foundations](../auto-pagination/modules/auto-pagination-01-foundations.md) - S3 vs SFTP decision guide
|
|
829
|
+
- [Module 2: S3 Operations](./modules/data-sources-02-s3-operations.md) - Complete S3DataSource reference
|
|
830
|
+
- [Module 3: SFTP Operations](./modules/data-sources-03-sftp-operations.md) - Complete SftpDataSource reference
|
|
831
|
+
- [Module 4: File Processing Patterns](./modules/data-sources-04-file-patterns.md) - Streaming, batching, validation
|
|
832
|
+
- [Module 5: Advanced Topics](./modules/data-sources-05-advanced-topics.md) - Security, monitoring, testing
|
|
833
|
+
|
|
834
|
+
---
|
|
835
|
+
|
|
836
|
+
[← Back to Main Guide](./data-sources-readme.md)
|