@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,681 +1,681 @@
|
|
|
1
|
-
# Module 2: Simple Scenarios
|
|
2
|
-
|
|
3
|
-
**Level:** Beginner
|
|
4
|
-
**Estimated Time:** 30 minutes
|
|
5
|
-
|
|
6
|
-
## Overview
|
|
7
|
-
|
|
8
|
-
This module covers simple connector scenarios focusing on SFTP CSV ingestion. You'll build a complete inventory update connector from scratch, learning the fundamental patterns that apply to all connector types.
|
|
9
|
-
|
|
10
|
-
## Learning Objectives
|
|
11
|
-
|
|
12
|
-
By the end of this module, you will:
|
|
13
|
-
|
|
14
|
-
- ✅ Build a complete SFTP CSV to Fluent inventory connector
|
|
15
|
-
- ✅ Configure data sources and parsers
|
|
16
|
-
- ✅ Implement field mapping and validation
|
|
17
|
-
- ✅ Send data via Fluent Batch API
|
|
18
|
-
- ✅ Handle file archival and error management
|
|
19
|
-
- ✅ Understand the complete end-to-end flow
|
|
20
|
-
|
|
21
|
-
## Scenario 1: SFTP CSV to Fluent Inventory Update
|
|
22
|
-
|
|
23
|
-
**Goal:** Read inventory CSV files from SFTP, validate, transform, and update inventory in Fluent Commerce.
|
|
24
|
-
|
|
25
|
-
**Complexity:** Low
|
|
26
|
-
**Components:** SftpDataSource, CSVParserService, UniversalMapper, FluentClient
|
|
27
|
-
**Estimated Lines:** ~250 lines
|
|
28
|
-
|
|
29
|
-
### Problem Statement
|
|
30
|
-
|
|
31
|
-
Your warehouse partner sends daily inventory updates via SFTP as CSV files. Each file contains:
|
|
32
|
-
|
|
33
|
-
- SKU identifier
|
|
34
|
-
- Warehouse location code
|
|
35
|
-
- Available quantity
|
|
36
|
-
- Status (available, unavailable, reserved)
|
|
37
|
-
|
|
38
|
-
You need to:
|
|
39
|
-
|
|
40
|
-
1. Connect to SFTP daily
|
|
41
|
-
2. Process all new CSV files
|
|
42
|
-
3. Validate data quality
|
|
43
|
-
4. Transform to Fluent schema
|
|
44
|
-
5. Update inventory positions
|
|
45
|
-
6. Archive processed files
|
|
46
|
-
7. Move failed files to error folder
|
|
47
|
-
|
|
48
|
-
### CSV File Format
|
|
49
|
-
|
|
50
|
-
```csv
|
|
51
|
-
sku,location,quantity,status
|
|
52
|
-
SKU-001,WAREHOUSE-A,100,available
|
|
53
|
-
SKU-002,WAREHOUSE-A,50,available
|
|
54
|
-
SKU-003,WAREHOUSE-B,25,reserved
|
|
55
|
-
SKU-001,WAREHOUSE-B,75,available
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### Complete Implementation
|
|
59
|
-
|
|
60
|
-
```typescript
|
|
61
|
-
import {
|
|
62
|
-
createClient,
|
|
63
|
-
SftpDataSource,
|
|
64
|
-
UniversalMapper,
|
|
65
|
-
StateService,
|
|
66
|
-
CSVParserService,
|
|
67
|
-
createConsoleLogger,
|
|
68
|
-
toStructuredLogger
|
|
69
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
70
|
-
|
|
71
|
-
// ====================
|
|
72
|
-
// 1. CONFIGURATION
|
|
73
|
-
// ====================
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* IMPORTANT: Credential Access Methods
|
|
77
|
-
*
|
|
78
|
-
* This example shows standalone Node.js configuration using environment variables.
|
|
79
|
-
*
|
|
80
|
-
* For Versori Platform deployments, use secure credential access methods:
|
|
81
|
-
*
|
|
82
|
-
* ✅ RECOMMENDED: activation.connections (already decoded, works everywhere)
|
|
83
|
-
* ⚠️ ALTERNATIVE: credentials().get() (requires base64 decoding)
|
|
84
|
-
* ❌ NEVER: Hardcoded credentials or unsecured environment variables
|
|
85
|
-
*
|
|
86
|
-
* See: docs/02-CORE-GUIDES/data-sources/sftp-credential-access-security.md
|
|
87
|
-
* For complete credential access patterns including:
|
|
88
|
-
* - Method comparison (credentials().get vs activation.connections)
|
|
89
|
-
* - Connection configuration in Versori UI
|
|
90
|
-
* - Security best practices
|
|
91
|
-
* - Error handling examples
|
|
92
|
-
* - Debug utilities
|
|
93
|
-
*/
|
|
94
|
-
const config = {
|
|
95
|
-
sftp: {
|
|
96
|
-
host: process.env.SFTP_HOST!,
|
|
97
|
-
username: process.env.SFTP_USERNAME!,
|
|
98
|
-
password: process.env.SFTP_PASSWORD!,
|
|
99
|
-
remotePath: '/inventory/updates',
|
|
100
|
-
filePattern: 'INV_*.csv',
|
|
101
|
-
},
|
|
102
|
-
fluent: {
|
|
103
|
-
baseUrl: process.env.FLUENT_BASE_URL!,
|
|
104
|
-
clientId: process.env.FLUENT_CLIENT_ID!,
|
|
105
|
-
clientSecret: process.env.FLUENT_CLIENT_SECRET!,
|
|
106
|
-
username: process.env.FLUENT_USERNAME!,
|
|
107
|
-
password: process.env.FLUENT_PASSWORD!,
|
|
108
|
-
retailerId: process.env.FLUENT_RETAILER_ID!,
|
|
109
|
-
},
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
// ====================
|
|
113
|
-
// 2. INITIALIZE SERVICES
|
|
114
|
-
// ====================
|
|
115
|
-
|
|
116
|
-
const logger = toStructuredLogger(createConsoleLogger(), {
|
|
117
|
-
logLevel: 'info'
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
const stateService = new StateService(logger);
|
|
121
|
-
|
|
122
|
-
// ====================
|
|
123
|
-
// 3. SETUP SFTP DATA SOURCE
|
|
124
|
-
// ====================
|
|
125
|
-
|
|
126
|
-
const sftpSource = new SftpDataSource(
|
|
127
|
-
{
|
|
128
|
-
type: 'SFTP_CSV',
|
|
129
|
-
connectionId: 'inventory-sftp',
|
|
130
|
-
name: 'Inventory SFTP',
|
|
131
|
-
settings: {
|
|
132
|
-
...config.sftp,
|
|
133
|
-
csvDelimiter: ',',
|
|
134
|
-
csvHeaders: ['sku', 'location', 'quantity', 'status'],
|
|
135
|
-
csvSkipEmptyLines: true,
|
|
136
|
-
csvTrimValues: true,
|
|
137
|
-
},
|
|
138
|
-
},
|
|
139
|
-
logger
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
// ====================
|
|
143
|
-
// 4. SETUP FIELD MAPPING
|
|
144
|
-
// ====================
|
|
145
|
-
|
|
146
|
-
const fieldMapper = new UniversalMapper({
|
|
147
|
-
fields: {
|
|
148
|
-
// Map 'sku' to 'ref', convert to uppercase
|
|
149
|
-
ref: {
|
|
150
|
-
source: 'sku',
|
|
151
|
-
resolver: 'sdk.uppercase',
|
|
152
|
-
},
|
|
153
|
-
|
|
154
|
-
// Map 'location' to 'locationRef' with prefix
|
|
155
|
-
locationRef: {
|
|
156
|
-
source: 'location',
|
|
157
|
-
resolver: value => `LOC:${value}`,
|
|
158
|
-
},
|
|
159
|
-
|
|
160
|
-
// Parse quantity as integer
|
|
161
|
-
qty: {
|
|
162
|
-
source: 'quantity',
|
|
163
|
-
resolver: 'sdk.parseInt',
|
|
164
|
-
},
|
|
165
|
-
|
|
166
|
-
// Transform status values
|
|
167
|
-
status: {
|
|
168
|
-
source: 'status',
|
|
169
|
-
resolver: value => {
|
|
170
|
-
const statusMap: Record<string, string> = {
|
|
171
|
-
available: 'ACTIVE',
|
|
172
|
-
unavailable: 'INACTIVE',
|
|
173
|
-
reserved: 'RESERVED',
|
|
174
|
-
};
|
|
175
|
-
return statusMap[String(value).toLowerCase()] || 'INACTIVE';
|
|
176
|
-
},
|
|
177
|
-
},
|
|
178
|
-
},
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
// ====================
|
|
182
|
-
// 5. CREATE FLUENT CLIENT
|
|
183
|
-
// ====================
|
|
184
|
-
|
|
185
|
-
const fluentClient = await createClient({ config: config.fluent });
|
|
186
|
-
|
|
187
|
-
// ====================
|
|
188
|
-
// 6. MAIN PROCESSING FUNCTION
|
|
189
|
-
// ====================
|
|
190
|
-
|
|
191
|
-
async function processInventoryUpdates() {
|
|
192
|
-
logger.info('Starting inventory update process');
|
|
193
|
-
|
|
194
|
-
try {
|
|
195
|
-
// 7. LIST FILES FROM SFTP
|
|
196
|
-
// Get last processed timestamp from sync state
|
|
197
|
-
// Initialize KV store (implementation depends on runtime)
|
|
198
|
-
// const kv = openKv(); // For Deno or Versori: ctx.openKv(':project:')
|
|
199
|
-
// const syncState = await stateService.getSyncState(kv, 'inventory-sync');
|
|
200
|
-
// const lastProcessedTimestamp = syncState.lastProcessedTimestamp;
|
|
201
|
-
|
|
202
|
-
const files = await sftpSource.listFiles({
|
|
203
|
-
filePattern: 'INV_*.csv'
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
logger.info(`Found ${files.length} new files to process`);
|
|
207
|
-
|
|
208
|
-
// 8. PROCESS EACH FILE
|
|
209
|
-
for (const file of files) {
|
|
210
|
-
logger.info(`Processing file: ${file.name}`);
|
|
211
|
-
|
|
212
|
-
try {
|
|
213
|
-
// 9. DOWNLOAD FILE
|
|
214
|
-
const content = await sftpSource.downloadFile(file.name);
|
|
215
|
-
|
|
216
|
-
// 10. PARSE CSV
|
|
217
|
-
const parser = new CSVParserService();
|
|
218
|
-
const records = await parser.parse(content as string);
|
|
219
|
-
logger.info(`Parsed ${records.length} records`);
|
|
220
|
-
|
|
221
|
-
// 11. VALIDATION
|
|
222
|
-
const { validRecords, validationErrors } = validateRecords(records);
|
|
223
|
-
|
|
224
|
-
logger.info(
|
|
225
|
-
`Validation complete: ${validRecords.length} valid, ${validationErrors.length} errors`
|
|
226
|
-
);
|
|
227
|
-
|
|
228
|
-
if (validationErrors.length > 0) {
|
|
229
|
-
logger.warn('Validation errors found:', validationErrors.slice(0, 5));
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// 12. TRANSFORM DATA
|
|
233
|
-
const transformedRecords: any[] = [];
|
|
234
|
-
for (const record of validRecords) {
|
|
235
|
-
const result = await fieldMapper.map(record);
|
|
236
|
-
if (!result.success) {
|
|
237
|
-
logger.warn('Mapping errors', result.errors);
|
|
238
|
-
continue;
|
|
239
|
-
}
|
|
240
|
-
transformedRecords.push(result.data);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// 13. CREATE FLUENT JOB
|
|
244
|
-
const job = await fluentClient.createJob({
|
|
245
|
-
name: `Inventory Update - ${file.name}`,
|
|
246
|
-
retailerId: config.fluent.retailerId,
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
// 14. SEND TO FLUENT VIA BATCH API
|
|
250
|
-
const batchResult = await fluentClient.sendBatch(job.id, {
|
|
251
|
-
action: 'UPSERT',
|
|
252
|
-
entityType: 'INVENTORY',
|
|
253
|
-
entities: transformedRecords.map(record => ({
|
|
254
|
-
ref: record.ref,
|
|
255
|
-
productRef: record.ref,
|
|
256
|
-
locationRef: record.locationRef,
|
|
257
|
-
qty: record.qty,
|
|
258
|
-
status: record.status,
|
|
259
|
-
type: 'ADJUSTMENT',
|
|
260
|
-
})),
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
logger.info('Batch sent to Fluent', {
|
|
264
|
-
batchId: batchResult.id,
|
|
265
|
-
recordCount: transformedRecords.length,
|
|
266
|
-
jobId: job.id,
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
// 15. MONITOR BATCH STATUS
|
|
270
|
-
await waitForBatchCompletion(fluentClient, job.id, batchResult.id);
|
|
271
|
-
|
|
272
|
-
// 16. ARCHIVE PROCESSED FILE
|
|
273
|
-
await sftpSource.moveFile(
|
|
274
|
-
`/inventory/updates/${file.name}`,
|
|
275
|
-
`/inventory/archive/${new Date().toISOString().split('T')[0]}/${file.name}`,
|
|
276
|
-
false
|
|
277
|
-
);
|
|
278
|
-
|
|
279
|
-
// 17. UPDATE STATE
|
|
280
|
-
// Update sync state using KV adapter (tracks file automatically)
|
|
281
|
-
// Initialize KV store (implementation depends on runtime)
|
|
282
|
-
// const kv = openKv(); // For Deno or Versori: ctx.openKv(':project:')
|
|
283
|
-
// await stateService.updateSyncState(kv, [{
|
|
284
|
-
// fileName: file.name,
|
|
285
|
-
// lastModified: new Date().toISOString(),
|
|
286
|
-
// recordCount: transformedRecords.length
|
|
287
|
-
// }], 'inventory-sync');
|
|
288
|
-
|
|
289
|
-
logger.info(`Successfully processed ${file.name}`);
|
|
290
|
-
} catch (error) {
|
|
291
|
-
logger.error(`Failed to process file ${file.name}:`, error);
|
|
292
|
-
|
|
293
|
-
// Move to error folder
|
|
294
|
-
await sftpSource.moveFile(
|
|
295
|
-
`/inventory/updates/${file.name}`,
|
|
296
|
-
`/inventory/errors/${file.name}`,
|
|
297
|
-
true
|
|
298
|
-
);
|
|
299
|
-
|
|
300
|
-
// Log error details (use KV directly for error tracking)
|
|
301
|
-
// const kv = openKv(); // Initialize KV store
|
|
302
|
-
// await kv.set(['error-log', 'inventory', file.name], {
|
|
303
|
-
// error: (error as Error).message,
|
|
304
|
-
// timestamp: new Date().toISOString(),
|
|
305
|
-
// stack: (error as Error).stack
|
|
306
|
-
// });
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
logger.info('Inventory update process completed');
|
|
311
|
-
} catch (error) {
|
|
312
|
-
logger.error('Process failed:', error);
|
|
313
|
-
throw error;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// ====================
|
|
318
|
-
// HELPER FUNCTIONS
|
|
319
|
-
// ====================
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* Validate CSV records
|
|
323
|
-
*/
|
|
324
|
-
function validateRecords(records: any[]) {
|
|
325
|
-
const validationErrors: any[] = [];
|
|
326
|
-
const validRecords = records.filter(record => {
|
|
327
|
-
// Required field validation
|
|
328
|
-
if (!record.sku || !record.location || record.quantity === undefined) {
|
|
329
|
-
validationErrors.push({
|
|
330
|
-
record,
|
|
331
|
-
error: 'Missing required fields',
|
|
332
|
-
});
|
|
333
|
-
return false;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// Numeric validation
|
|
337
|
-
if (isNaN(Number(record.quantity))) {
|
|
338
|
-
validationErrors.push({
|
|
339
|
-
record,
|
|
340
|
-
error: 'Invalid quantity',
|
|
341
|
-
});
|
|
342
|
-
return false;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// SKU format validation
|
|
346
|
-
if (!/^[A-Z0-9-]+$/i.test(record.sku)) {
|
|
347
|
-
validationErrors.push({
|
|
348
|
-
record,
|
|
349
|
-
error: 'Invalid SKU format',
|
|
350
|
-
});
|
|
351
|
-
return false;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
return true;
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
return { validRecords, validationErrors };
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
/**
|
|
361
|
-
* Wait for batch processing to complete
|
|
362
|
-
*/
|
|
363
|
-
async function waitForBatchCompletion(client: any, jobId: string, batchId: string) {
|
|
364
|
-
let batchStatus = 'PENDING';
|
|
365
|
-
let attempts = 0;
|
|
366
|
-
const maxAttempts = 60; // 5 minutes max
|
|
367
|
-
|
|
368
|
-
while (batchStatus !== 'COMPLETED' && batchStatus !== 'FAILED' && attempts < maxAttempts) {
|
|
369
|
-
await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5 seconds
|
|
370
|
-
|
|
371
|
-
const statusResult = await client.getBatchStatus(jobId, batchId);
|
|
372
|
-
batchStatus = statusResult.status;
|
|
373
|
-
|
|
374
|
-
logger.debug(`Batch status: ${batchStatus}`);
|
|
375
|
-
attempts++;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
if (batchStatus === 'COMPLETED') {
|
|
379
|
-
logger.info('Batch processing completed successfully');
|
|
380
|
-
} else if (batchStatus === 'FAILED') {
|
|
381
|
-
throw new Error('Batch processing failed');
|
|
382
|
-
} else {
|
|
383
|
-
throw new Error('Batch processing timeout');
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
// ====================
|
|
388
|
-
// EXECUTE
|
|
389
|
-
// ====================
|
|
390
|
-
|
|
391
|
-
processInventoryUpdates()
|
|
392
|
-
.then(() => console.log('✅ Inventory sync complete'))
|
|
393
|
-
.catch(err => console.error('❌ Sync failed:', err));
|
|
394
|
-
```
|
|
395
|
-
|
|
396
|
-
### Code Walkthrough
|
|
397
|
-
|
|
398
|
-
#### Step 1-2: Configuration and Service Setup
|
|
399
|
-
|
|
400
|
-
```typescript
|
|
401
|
-
// Configuration separates environment-specific values
|
|
402
|
-
const config = {
|
|
403
|
-
sftp: { host, username, password, remotePath },
|
|
404
|
-
fluent: { baseUrl, clientId, clientSecret, retailerId },
|
|
405
|
-
};
|
|
406
|
-
|
|
407
|
-
// Initialize core services
|
|
408
|
-
const logger = toStructuredLogger(createConsoleLogger(), {
|
|
409
|
-
logLevel: 'info'
|
|
410
|
-
});
|
|
411
|
-
const stateService = new StateService(logger);
|
|
412
|
-
```
|
|
413
|
-
|
|
414
|
-
**Why this matters:**
|
|
415
|
-
|
|
416
|
-
- Configuration centralization makes deployment easier
|
|
417
|
-
- Logger provides structured output for debugging
|
|
418
|
-
- StateService prevents re-processing the same file
|
|
419
|
-
|
|
420
|
-
**IMPORTANT - Credential Access:**
|
|
421
|
-
|
|
422
|
-
This example shows **standalone Node.js** configuration using environment variables. For **Versori Platform** deployments:
|
|
423
|
-
|
|
424
|
-
- ✅ **Use `activation.connections`** for secure credential access (recommended)
|
|
425
|
-
- ⚠️ **Or `credentials().get()`** if needed (requires base64 decoding)
|
|
426
|
-
- ❌ **Never hardcode** credentials in code
|
|
427
|
-
|
|
428
|
-
**See:** [SFTP Credential Access & Security](../../../02-CORE-GUIDES/data-sources/data-sources-sftp-credential-access-security.md) for complete patterns including:
|
|
429
|
-
- Two credential access methods compared
|
|
430
|
-
- Connection setup in Versori UI
|
|
431
|
-
- Security best practices
|
|
432
|
-
- Complete code examples for both methods
|
|
433
|
-
|
|
434
|
-
#### Step 3: SFTP Data Source Configuration
|
|
435
|
-
|
|
436
|
-
```typescript
|
|
437
|
-
const sftpSource = new SftpDataSource(
|
|
438
|
-
{
|
|
439
|
-
type: 'SFTP_CSV',
|
|
440
|
-
connectionId: 'inventory-sftp',
|
|
441
|
-
name: 'Inventory SFTP',
|
|
442
|
-
settings: {
|
|
443
|
-
...config.sftp,
|
|
444
|
-
csvDelimiter: ',',
|
|
445
|
-
csvHeaders: ['sku', 'location', 'quantity', 'status'],
|
|
446
|
-
csvSkipEmptyLines: true,
|
|
447
|
-
csvTrimValues: true,
|
|
448
|
-
},
|
|
449
|
-
},
|
|
450
|
-
logger
|
|
451
|
-
);
|
|
452
|
-
```
|
|
453
|
-
|
|
454
|
-
**Key options:**
|
|
455
|
-
|
|
456
|
-
- `csvHeaders`: Column names (auto-detected if CSV has header row)
|
|
457
|
-
- `csvSkipEmptyLines`: Ignore blank lines
|
|
458
|
-
- `csvTrimValues`: Remove whitespace from values
|
|
459
|
-
|
|
460
|
-
#### Step 4: Field Mapping
|
|
461
|
-
|
|
462
|
-
```typescript
|
|
463
|
-
const fieldMapper = new UniversalMapper({
|
|
464
|
-
fields: {
|
|
465
|
-
ref: { source: 'sku', resolver: 'sdk.uppercase' },
|
|
466
|
-
locationRef: { source: 'location', resolver: v => `LOC:${v}` },
|
|
467
|
-
qty: { source: 'quantity', resolver: 'sdk.parseInt' },
|
|
468
|
-
status: { source: 'status', resolver: statusMapFunction },
|
|
469
|
-
},
|
|
470
|
-
});
|
|
471
|
-
```
|
|
472
|
-
|
|
473
|
-
**Mapping strategies:**
|
|
474
|
-
|
|
475
|
-
- **Built-in resolvers**: `sdk.uppercase`, `sdk.parseInt`, `sdk.formatDate`
|
|
476
|
-
- **Custom functions**: `(value) => transformedValue`
|
|
477
|
-
- **Prefixing**: Add prefixes to IDs (`LOC:WAREHOUSE-A`)
|
|
478
|
-
- **Mapping tables**: Convert codes to values
|
|
479
|
-
|
|
480
|
-
#### Step 11: Validation
|
|
481
|
-
|
|
482
|
-
```typescript
|
|
483
|
-
function validateRecords(records: any[]) {
|
|
484
|
-
const validRecords = records.filter(record => {
|
|
485
|
-
// Check required fields
|
|
486
|
-
if (!record.sku || !record.location) return false;
|
|
487
|
-
|
|
488
|
-
// Validate data types
|
|
489
|
-
if (isNaN(Number(record.quantity))) return false;
|
|
490
|
-
|
|
491
|
-
// Validate formats
|
|
492
|
-
if (!/^[A-Z0-9-]+$/i.test(record.sku)) return false;
|
|
493
|
-
|
|
494
|
-
return true;
|
|
495
|
-
});
|
|
496
|
-
|
|
497
|
-
return { validRecords, validationErrors };
|
|
498
|
-
}
|
|
499
|
-
```
|
|
500
|
-
|
|
501
|
-
**Validation levels:**
|
|
502
|
-
|
|
503
|
-
1. **Required fields**: Must be present
|
|
504
|
-
2. **Data types**: Must be correct type (number, string, etc.)
|
|
505
|
-
3. **Formats**: Must match expected patterns (SKU format, email, etc.)
|
|
506
|
-
4. **Business rules**: Must satisfy domain logic (quantity > 0, etc.)
|
|
507
|
-
|
|
508
|
-
#### Step 14: Batch API Call
|
|
509
|
-
|
|
510
|
-
```typescript
|
|
511
|
-
await fluentClient.sendBatch(job.id, {
|
|
512
|
-
action: 'UPSERT', // Create or update
|
|
513
|
-
entityType: 'INVENTORY', // Entity type
|
|
514
|
-
entities: transformedRecords.map(record => ({
|
|
515
|
-
ref: record.ref,
|
|
516
|
-
productRef: record.ref,
|
|
517
|
-
locationRef: record.locationRef,
|
|
518
|
-
qty: record.qty,
|
|
519
|
-
status: record.status,
|
|
520
|
-
type: 'ADJUSTMENT',
|
|
521
|
-
})),
|
|
522
|
-
});
|
|
523
|
-
```
|
|
524
|
-
|
|
525
|
-
**Batch API notes:**
|
|
526
|
-
|
|
527
|
-
- `action: 'UPSERT'` - Creates new or updates existing
|
|
528
|
-
- `entityType: 'INVENTORY'` - Only entity type supported
|
|
529
|
-
- Required fields: `ref`, `productRef`, `locationRef`, `qty`
|
|
530
|
-
- Optional fields: `status`, `type`
|
|
531
|
-
|
|
532
|
-
#### Step 15: Status Monitoring
|
|
533
|
-
|
|
534
|
-
```typescript
|
|
535
|
-
async function waitForBatchCompletion(client, jobId, batchId) {
|
|
536
|
-
let batchStatus = 'PENDING';
|
|
537
|
-
let attempts = 0;
|
|
538
|
-
const maxAttempts = 60; // 5 minutes max
|
|
539
|
-
|
|
540
|
-
while (batchStatus !== 'COMPLETED' && batchStatus !== 'FAILED' && attempts < maxAttempts) {
|
|
541
|
-
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
542
|
-
const statusResult = await client.getBatchStatus(jobId, batchId);
|
|
543
|
-
batchStatus = statusResult.status;
|
|
544
|
-
attempts++;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
if (batchStatus !== 'COMPLETED') {
|
|
548
|
-
throw new Error('Batch failed or timed out');
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
```
|
|
552
|
-
|
|
553
|
-
**Why poll for status:**
|
|
554
|
-
|
|
555
|
-
- Batch API is asynchronous (returns immediately)
|
|
556
|
-
- Need to wait for processing to complete
|
|
557
|
-
- Detect failures early
|
|
558
|
-
- Implement timeout to prevent infinite loops
|
|
559
|
-
|
|
560
|
-
### Error Handling Strategy
|
|
561
|
-
|
|
562
|
-
```typescript
|
|
563
|
-
try {
|
|
564
|
-
// Process file
|
|
565
|
-
await processFile(file);
|
|
566
|
-
|
|
567
|
-
// Archive on success
|
|
568
|
-
await sftpSource.moveFile(file.name, 'archive/');
|
|
569
|
-
} catch (error) {
|
|
570
|
-
logger.error(`Failed to process ${file.name}:`, error);
|
|
571
|
-
|
|
572
|
-
// Move to error folder for review
|
|
573
|
-
await sftpSource.moveFile(file.name, 'errors/');
|
|
574
|
-
|
|
575
|
-
// Log error in state for audit trail (use KV directly)
|
|
576
|
-
// const kv = openKv(); // Initialize KV store
|
|
577
|
-
// await kv.set(['errors', 'inventory', file.name], {
|
|
578
|
-
// error: error.message,
|
|
579
|
-
// timestamp: new Date().toISOString()
|
|
580
|
-
// });
|
|
581
|
-
|
|
582
|
-
// Continue processing next file (don't throw)
|
|
583
|
-
}
|
|
584
|
-
```
|
|
585
|
-
|
|
586
|
-
**Error handling principles:**
|
|
587
|
-
|
|
588
|
-
1. **Log detailed errors** - Include file name, error message, stack trace
|
|
589
|
-
2. **Move failed files** - Quarantine for manual review
|
|
590
|
-
3. **Track failures** - Record in state management
|
|
591
|
-
4. **Continue processing** - One failure shouldn't stop entire batch
|
|
592
|
-
5. **Notify on critical errors** - Alert administrators when needed
|
|
593
|
-
|
|
594
|
-
### File Archival Pattern
|
|
595
|
-
|
|
596
|
-
```typescript
|
|
597
|
-
// Archive processed files by date
|
|
598
|
-
await sftpSource.moveFile(
|
|
599
|
-
'/inventory/updates/INV_2025-01-15.csv',
|
|
600
|
-
'/inventory/archive/2025-01-15/INV_2025-01-15.csv',
|
|
601
|
-
false // Don't overwrite if exists
|
|
602
|
-
);
|
|
603
|
-
|
|
604
|
-
// Move errors to dedicated folder
|
|
605
|
-
await sftpSource.moveFile(
|
|
606
|
-
'/inventory/updates/BAD_FILE.csv',
|
|
607
|
-
'/inventory/errors/BAD_FILE.csv',
|
|
608
|
-
true // Overwrite if exists (append timestamp to avoid loss)
|
|
609
|
-
);
|
|
610
|
-
```
|
|
611
|
-
|
|
612
|
-
**Archival best practices:**
|
|
613
|
-
|
|
614
|
-
- Organize by date: `archive/YYYY-MM-DD/filename.csv`
|
|
615
|
-
- Keep processed files for audit trail (at least 30 days)
|
|
616
|
-
- Separate error files for investigation
|
|
617
|
-
- Use retention policies (auto-delete after N days)
|
|
618
|
-
|
|
619
|
-
## Practice Exercise
|
|
620
|
-
|
|
621
|
-
**Challenge:** Modify the connector to handle a CSV with additional fields
|
|
622
|
-
|
|
623
|
-
**New CSV format:**
|
|
624
|
-
|
|
625
|
-
```csv
|
|
626
|
-
sku,location,on_hand,allocated,damaged,status,last_updated
|
|
627
|
-
SKU-001,WH-A,100,20,5,active,2025-01-15T10:00:00Z
|
|
628
|
-
```
|
|
629
|
-
|
|
630
|
-
**Requirements:**
|
|
631
|
-
|
|
632
|
-
1. Calculate available quantity: `on_hand - allocated - damaged`
|
|
633
|
-
2. Map `last_updated` to ISO date format
|
|
634
|
-
3. Add validation: `on_hand >= allocated + damaged`
|
|
635
|
-
4. Include `lastUpdated` attribute in Fluent payload
|
|
636
|
-
|
|
637
|
-
**Solution:**
|
|
638
|
-
|
|
639
|
-
```typescript
|
|
640
|
-
const fieldMapper = new UniversalMapper({
|
|
641
|
-
fields: {
|
|
642
|
-
ref: { source: 'sku', resolver: 'sdk.uppercase' },
|
|
643
|
-
locationRef: { source: 'location' },
|
|
644
|
-
qty: {
|
|
645
|
-
source: ['on_hand', 'allocated', 'damaged'],
|
|
646
|
-
resolver: values => {
|
|
647
|
-
const [onHand, allocated, damaged] = values.map(Number);
|
|
648
|
-
return onHand - allocated - damaged;
|
|
649
|
-
},
|
|
650
|
-
},
|
|
651
|
-
status: { source: 'status', resolver: 'sdk.uppercase' },
|
|
652
|
-
lastUpdated: { source: 'last_updated', resolver: 'sdk.formatDate' },
|
|
653
|
-
},
|
|
654
|
-
});
|
|
655
|
-
|
|
656
|
-
// Add validation
|
|
657
|
-
function validateRecords(records: any[]) {
|
|
658
|
-
return records.filter(record => {
|
|
659
|
-
const onHand = Number(record.on_hand);
|
|
660
|
-
const allocated = Number(record.allocated);
|
|
661
|
-
const damaged = Number(record.damaged);
|
|
662
|
-
|
|
663
|
-
return onHand >= allocated + damaged;
|
|
664
|
-
});
|
|
665
|
-
}
|
|
666
|
-
```
|
|
667
|
-
|
|
668
|
-
## Key Takeaways
|
|
669
|
-
|
|
670
|
-
- 🎯 Simple connectors follow a clear pattern: List → Download → Parse → Validate → Transform → Send → Archive
|
|
671
|
-
- 🎯 SftpDataSource handles all SFTP operations (list, download, move)
|
|
672
|
-
- 🎯 UniversalMapper provides flexible field transformation with built-in resolvers
|
|
673
|
-
- 🎯 Validation should happen before transformation to catch bad data early
|
|
674
|
-
- 🎯 Batch API requires job creation + batch send + status monitoring
|
|
675
|
-
- 🎯 Error handling should be graceful (log, quarantine, continue)
|
|
676
|
-
- 🎯 State management prevents duplicate processing
|
|
677
|
-
- 🎯 **Security:** Use secure credential access methods on Versori platform (see [SFTP Credential Access & Security](../../../02-CORE-GUIDES/data-sources/data-sources-sftp-credential-access-security.md))
|
|
678
|
-
|
|
679
|
-
## Next Steps
|
|
680
|
-
|
|
681
|
-
Continue to [Module 3: Intermediate Scenarios](./connector-scenarios-03-intermediate-scenarios.md) to learn S3 Parquet processing, JSON order creation, and advanced validation patterns.
|
|
1
|
+
# Module 2: Simple Scenarios
|
|
2
|
+
|
|
3
|
+
**Level:** Beginner
|
|
4
|
+
**Estimated Time:** 30 minutes
|
|
5
|
+
|
|
6
|
+
## Overview
|
|
7
|
+
|
|
8
|
+
This module covers simple connector scenarios focusing on SFTP CSV ingestion. You'll build a complete inventory update connector from scratch, learning the fundamental patterns that apply to all connector types.
|
|
9
|
+
|
|
10
|
+
## Learning Objectives
|
|
11
|
+
|
|
12
|
+
By the end of this module, you will:
|
|
13
|
+
|
|
14
|
+
- ✅ Build a complete SFTP CSV to Fluent inventory connector
|
|
15
|
+
- ✅ Configure data sources and parsers
|
|
16
|
+
- ✅ Implement field mapping and validation
|
|
17
|
+
- ✅ Send data via Fluent Batch API
|
|
18
|
+
- ✅ Handle file archival and error management
|
|
19
|
+
- ✅ Understand the complete end-to-end flow
|
|
20
|
+
|
|
21
|
+
## Scenario 1: SFTP CSV to Fluent Inventory Update
|
|
22
|
+
|
|
23
|
+
**Goal:** Read inventory CSV files from SFTP, validate, transform, and update inventory in Fluent Commerce.
|
|
24
|
+
|
|
25
|
+
**Complexity:** Low
|
|
26
|
+
**Components:** SftpDataSource, CSVParserService, UniversalMapper, FluentClient
|
|
27
|
+
**Estimated Lines:** ~250 lines
|
|
28
|
+
|
|
29
|
+
### Problem Statement
|
|
30
|
+
|
|
31
|
+
Your warehouse partner sends daily inventory updates via SFTP as CSV files. Each file contains:
|
|
32
|
+
|
|
33
|
+
- SKU identifier
|
|
34
|
+
- Warehouse location code
|
|
35
|
+
- Available quantity
|
|
36
|
+
- Status (available, unavailable, reserved)
|
|
37
|
+
|
|
38
|
+
You need to:
|
|
39
|
+
|
|
40
|
+
1. Connect to SFTP daily
|
|
41
|
+
2. Process all new CSV files
|
|
42
|
+
3. Validate data quality
|
|
43
|
+
4. Transform to Fluent schema
|
|
44
|
+
5. Update inventory positions
|
|
45
|
+
6. Archive processed files
|
|
46
|
+
7. Move failed files to error folder
|
|
47
|
+
|
|
48
|
+
### CSV File Format
|
|
49
|
+
|
|
50
|
+
```csv
|
|
51
|
+
sku,location,quantity,status
|
|
52
|
+
SKU-001,WAREHOUSE-A,100,available
|
|
53
|
+
SKU-002,WAREHOUSE-A,50,available
|
|
54
|
+
SKU-003,WAREHOUSE-B,25,reserved
|
|
55
|
+
SKU-001,WAREHOUSE-B,75,available
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Complete Implementation
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import {
|
|
62
|
+
createClient,
|
|
63
|
+
SftpDataSource,
|
|
64
|
+
UniversalMapper,
|
|
65
|
+
StateService,
|
|
66
|
+
CSVParserService,
|
|
67
|
+
createConsoleLogger,
|
|
68
|
+
toStructuredLogger
|
|
69
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
70
|
+
|
|
71
|
+
// ====================
|
|
72
|
+
// 1. CONFIGURATION
|
|
73
|
+
// ====================
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* IMPORTANT: Credential Access Methods
|
|
77
|
+
*
|
|
78
|
+
* This example shows standalone Node.js configuration using environment variables.
|
|
79
|
+
*
|
|
80
|
+
* For Versori Platform deployments, use secure credential access methods:
|
|
81
|
+
*
|
|
82
|
+
* ✅ RECOMMENDED: activation.connections (already decoded, works everywhere)
|
|
83
|
+
* ⚠️ ALTERNATIVE: credentials().get() (requires base64 decoding)
|
|
84
|
+
* ❌ NEVER: Hardcoded credentials or unsecured environment variables
|
|
85
|
+
*
|
|
86
|
+
* See: docs/02-CORE-GUIDES/data-sources/sftp-credential-access-security.md
|
|
87
|
+
* For complete credential access patterns including:
|
|
88
|
+
* - Method comparison (credentials().get vs activation.connections)
|
|
89
|
+
* - Connection configuration in Versori UI
|
|
90
|
+
* - Security best practices
|
|
91
|
+
* - Error handling examples
|
|
92
|
+
* - Debug utilities
|
|
93
|
+
*/
|
|
94
|
+
const config = {
|
|
95
|
+
sftp: {
|
|
96
|
+
host: process.env.SFTP_HOST!,
|
|
97
|
+
username: process.env.SFTP_USERNAME!,
|
|
98
|
+
password: process.env.SFTP_PASSWORD!,
|
|
99
|
+
remotePath: '/inventory/updates',
|
|
100
|
+
filePattern: 'INV_*.csv',
|
|
101
|
+
},
|
|
102
|
+
fluent: {
|
|
103
|
+
baseUrl: process.env.FLUENT_BASE_URL!,
|
|
104
|
+
clientId: process.env.FLUENT_CLIENT_ID!,
|
|
105
|
+
clientSecret: process.env.FLUENT_CLIENT_SECRET!,
|
|
106
|
+
username: process.env.FLUENT_USERNAME!,
|
|
107
|
+
password: process.env.FLUENT_PASSWORD!,
|
|
108
|
+
retailerId: process.env.FLUENT_RETAILER_ID!,
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// ====================
|
|
113
|
+
// 2. INITIALIZE SERVICES
|
|
114
|
+
// ====================
|
|
115
|
+
|
|
116
|
+
const logger = toStructuredLogger(createConsoleLogger(), {
|
|
117
|
+
logLevel: 'info'
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const stateService = new StateService(logger);
|
|
121
|
+
|
|
122
|
+
// ====================
|
|
123
|
+
// 3. SETUP SFTP DATA SOURCE
|
|
124
|
+
// ====================
|
|
125
|
+
|
|
126
|
+
const sftpSource = new SftpDataSource(
|
|
127
|
+
{
|
|
128
|
+
type: 'SFTP_CSV',
|
|
129
|
+
connectionId: 'inventory-sftp',
|
|
130
|
+
name: 'Inventory SFTP',
|
|
131
|
+
settings: {
|
|
132
|
+
...config.sftp,
|
|
133
|
+
csvDelimiter: ',',
|
|
134
|
+
csvHeaders: ['sku', 'location', 'quantity', 'status'],
|
|
135
|
+
csvSkipEmptyLines: true,
|
|
136
|
+
csvTrimValues: true,
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
logger
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
// ====================
|
|
143
|
+
// 4. SETUP FIELD MAPPING
|
|
144
|
+
// ====================
|
|
145
|
+
|
|
146
|
+
const fieldMapper = new UniversalMapper({
|
|
147
|
+
fields: {
|
|
148
|
+
// Map 'sku' to 'ref', convert to uppercase
|
|
149
|
+
ref: {
|
|
150
|
+
source: 'sku',
|
|
151
|
+
resolver: 'sdk.uppercase',
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
// Map 'location' to 'locationRef' with prefix
|
|
155
|
+
locationRef: {
|
|
156
|
+
source: 'location',
|
|
157
|
+
resolver: value => `LOC:${value}`,
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
// Parse quantity as integer
|
|
161
|
+
qty: {
|
|
162
|
+
source: 'quantity',
|
|
163
|
+
resolver: 'sdk.parseInt',
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
// Transform status values
|
|
167
|
+
status: {
|
|
168
|
+
source: 'status',
|
|
169
|
+
resolver: value => {
|
|
170
|
+
const statusMap: Record<string, string> = {
|
|
171
|
+
available: 'ACTIVE',
|
|
172
|
+
unavailable: 'INACTIVE',
|
|
173
|
+
reserved: 'RESERVED',
|
|
174
|
+
};
|
|
175
|
+
return statusMap[String(value).toLowerCase()] || 'INACTIVE';
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// ====================
|
|
182
|
+
// 5. CREATE FLUENT CLIENT
|
|
183
|
+
// ====================
|
|
184
|
+
|
|
185
|
+
const fluentClient = await createClient({ config: config.fluent });
|
|
186
|
+
|
|
187
|
+
// ====================
|
|
188
|
+
// 6. MAIN PROCESSING FUNCTION
|
|
189
|
+
// ====================
|
|
190
|
+
|
|
191
|
+
async function processInventoryUpdates() {
|
|
192
|
+
logger.info('Starting inventory update process');
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
// 7. LIST FILES FROM SFTP
|
|
196
|
+
// Get last processed timestamp from sync state
|
|
197
|
+
// Initialize KV store (implementation depends on runtime)
|
|
198
|
+
// const kv = openKv(); // For Deno or Versori: ctx.openKv(':project:')
|
|
199
|
+
// const syncState = await stateService.getSyncState(kv, 'inventory-sync');
|
|
200
|
+
// const lastProcessedTimestamp = syncState.lastProcessedTimestamp;
|
|
201
|
+
|
|
202
|
+
const files = await sftpSource.listFiles({
|
|
203
|
+
filePattern: 'INV_*.csv'
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
logger.info(`Found ${files.length} new files to process`);
|
|
207
|
+
|
|
208
|
+
// 8. PROCESS EACH FILE
|
|
209
|
+
for (const file of files) {
|
|
210
|
+
logger.info(`Processing file: ${file.name}`);
|
|
211
|
+
|
|
212
|
+
try {
|
|
213
|
+
// 9. DOWNLOAD FILE
|
|
214
|
+
const content = await sftpSource.downloadFile(file.name);
|
|
215
|
+
|
|
216
|
+
// 10. PARSE CSV
|
|
217
|
+
const parser = new CSVParserService();
|
|
218
|
+
const records = await parser.parse(content as string);
|
|
219
|
+
logger.info(`Parsed ${records.length} records`);
|
|
220
|
+
|
|
221
|
+
// 11. VALIDATION
|
|
222
|
+
const { validRecords, validationErrors } = validateRecords(records);
|
|
223
|
+
|
|
224
|
+
logger.info(
|
|
225
|
+
`Validation complete: ${validRecords.length} valid, ${validationErrors.length} errors`
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
if (validationErrors.length > 0) {
|
|
229
|
+
logger.warn('Validation errors found:', validationErrors.slice(0, 5));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// 12. TRANSFORM DATA
|
|
233
|
+
const transformedRecords: any[] = [];
|
|
234
|
+
for (const record of validRecords) {
|
|
235
|
+
const result = await fieldMapper.map(record);
|
|
236
|
+
if (!result.success) {
|
|
237
|
+
logger.warn('Mapping errors', result.errors);
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
transformedRecords.push(result.data);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// 13. CREATE FLUENT JOB
|
|
244
|
+
const job = await fluentClient.createJob({
|
|
245
|
+
name: `Inventory Update - ${file.name}`,
|
|
246
|
+
retailerId: config.fluent.retailerId,
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// 14. SEND TO FLUENT VIA BATCH API
|
|
250
|
+
const batchResult = await fluentClient.sendBatch(job.id, {
|
|
251
|
+
action: 'UPSERT',
|
|
252
|
+
entityType: 'INVENTORY',
|
|
253
|
+
entities: transformedRecords.map(record => ({
|
|
254
|
+
ref: record.ref,
|
|
255
|
+
productRef: record.ref,
|
|
256
|
+
locationRef: record.locationRef,
|
|
257
|
+
qty: record.qty,
|
|
258
|
+
status: record.status,
|
|
259
|
+
type: 'ADJUSTMENT',
|
|
260
|
+
})),
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
logger.info('Batch sent to Fluent', {
|
|
264
|
+
batchId: batchResult.id,
|
|
265
|
+
recordCount: transformedRecords.length,
|
|
266
|
+
jobId: job.id,
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// 15. MONITOR BATCH STATUS
|
|
270
|
+
await waitForBatchCompletion(fluentClient, job.id, batchResult.id);
|
|
271
|
+
|
|
272
|
+
// 16. ARCHIVE PROCESSED FILE
|
|
273
|
+
await sftpSource.moveFile(
|
|
274
|
+
`/inventory/updates/${file.name}`,
|
|
275
|
+
`/inventory/archive/${new Date().toISOString().split('T')[0]}/${file.name}`,
|
|
276
|
+
false
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
// 17. UPDATE STATE
|
|
280
|
+
// Update sync state using KV adapter (tracks file automatically)
|
|
281
|
+
// Initialize KV store (implementation depends on runtime)
|
|
282
|
+
// const kv = openKv(); // For Deno or Versori: ctx.openKv(':project:')
|
|
283
|
+
// await stateService.updateSyncState(kv, [{
|
|
284
|
+
// fileName: file.name,
|
|
285
|
+
// lastModified: new Date().toISOString(),
|
|
286
|
+
// recordCount: transformedRecords.length
|
|
287
|
+
// }], 'inventory-sync');
|
|
288
|
+
|
|
289
|
+
logger.info(`Successfully processed ${file.name}`);
|
|
290
|
+
} catch (error) {
|
|
291
|
+
logger.error(`Failed to process file ${file.name}:`, error);
|
|
292
|
+
|
|
293
|
+
// Move to error folder
|
|
294
|
+
await sftpSource.moveFile(
|
|
295
|
+
`/inventory/updates/${file.name}`,
|
|
296
|
+
`/inventory/errors/${file.name}`,
|
|
297
|
+
true
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
// Log error details (use KV directly for error tracking)
|
|
301
|
+
// const kv = openKv(); // Initialize KV store
|
|
302
|
+
// await kv.set(['error-log', 'inventory', file.name], {
|
|
303
|
+
// error: (error as Error).message,
|
|
304
|
+
// timestamp: new Date().toISOString(),
|
|
305
|
+
// stack: (error as Error).stack
|
|
306
|
+
// });
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
logger.info('Inventory update process completed');
|
|
311
|
+
} catch (error) {
|
|
312
|
+
logger.error('Process failed:', error);
|
|
313
|
+
throw error;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// ====================
|
|
318
|
+
// HELPER FUNCTIONS
|
|
319
|
+
// ====================
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Validate CSV records
|
|
323
|
+
*/
|
|
324
|
+
function validateRecords(records: any[]) {
|
|
325
|
+
const validationErrors: any[] = [];
|
|
326
|
+
const validRecords = records.filter(record => {
|
|
327
|
+
// Required field validation
|
|
328
|
+
if (!record.sku || !record.location || record.quantity === undefined) {
|
|
329
|
+
validationErrors.push({
|
|
330
|
+
record,
|
|
331
|
+
error: 'Missing required fields',
|
|
332
|
+
});
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Numeric validation
|
|
337
|
+
if (isNaN(Number(record.quantity))) {
|
|
338
|
+
validationErrors.push({
|
|
339
|
+
record,
|
|
340
|
+
error: 'Invalid quantity',
|
|
341
|
+
});
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// SKU format validation
|
|
346
|
+
if (!/^[A-Z0-9-]+$/i.test(record.sku)) {
|
|
347
|
+
validationErrors.push({
|
|
348
|
+
record,
|
|
349
|
+
error: 'Invalid SKU format',
|
|
350
|
+
});
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return true;
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
return { validRecords, validationErrors };
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Wait for batch processing to complete
|
|
362
|
+
*/
|
|
363
|
+
async function waitForBatchCompletion(client: any, jobId: string, batchId: string) {
|
|
364
|
+
let batchStatus = 'PENDING';
|
|
365
|
+
let attempts = 0;
|
|
366
|
+
const maxAttempts = 60; // 5 minutes max
|
|
367
|
+
|
|
368
|
+
while (batchStatus !== 'COMPLETED' && batchStatus !== 'FAILED' && attempts < maxAttempts) {
|
|
369
|
+
await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5 seconds
|
|
370
|
+
|
|
371
|
+
const statusResult = await client.getBatchStatus(jobId, batchId);
|
|
372
|
+
batchStatus = statusResult.status;
|
|
373
|
+
|
|
374
|
+
logger.debug(`Batch status: ${batchStatus}`);
|
|
375
|
+
attempts++;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (batchStatus === 'COMPLETED') {
|
|
379
|
+
logger.info('Batch processing completed successfully');
|
|
380
|
+
} else if (batchStatus === 'FAILED') {
|
|
381
|
+
throw new Error('Batch processing failed');
|
|
382
|
+
} else {
|
|
383
|
+
throw new Error('Batch processing timeout');
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// ====================
|
|
388
|
+
// EXECUTE
|
|
389
|
+
// ====================
|
|
390
|
+
|
|
391
|
+
processInventoryUpdates()
|
|
392
|
+
.then(() => console.log('✅ Inventory sync complete'))
|
|
393
|
+
.catch(err => console.error('❌ Sync failed:', err));
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### Code Walkthrough
|
|
397
|
+
|
|
398
|
+
#### Step 1-2: Configuration and Service Setup
|
|
399
|
+
|
|
400
|
+
```typescript
|
|
401
|
+
// Configuration separates environment-specific values
|
|
402
|
+
const config = {
|
|
403
|
+
sftp: { host, username, password, remotePath },
|
|
404
|
+
fluent: { baseUrl, clientId, clientSecret, retailerId },
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
// Initialize core services
|
|
408
|
+
const logger = toStructuredLogger(createConsoleLogger(), {
|
|
409
|
+
logLevel: 'info'
|
|
410
|
+
});
|
|
411
|
+
const stateService = new StateService(logger);
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
**Why this matters:**
|
|
415
|
+
|
|
416
|
+
- Configuration centralization makes deployment easier
|
|
417
|
+
- Logger provides structured output for debugging
|
|
418
|
+
- StateService prevents re-processing the same file
|
|
419
|
+
|
|
420
|
+
**IMPORTANT - Credential Access:**
|
|
421
|
+
|
|
422
|
+
This example shows **standalone Node.js** configuration using environment variables. For **Versori Platform** deployments:
|
|
423
|
+
|
|
424
|
+
- ✅ **Use `activation.connections`** for secure credential access (recommended)
|
|
425
|
+
- ⚠️ **Or `credentials().get()`** if needed (requires base64 decoding)
|
|
426
|
+
- ❌ **Never hardcode** credentials in code
|
|
427
|
+
|
|
428
|
+
**See:** [SFTP Credential Access & Security](../../../02-CORE-GUIDES/data-sources/data-sources-sftp-credential-access-security.md) for complete patterns including:
|
|
429
|
+
- Two credential access methods compared
|
|
430
|
+
- Connection setup in Versori UI
|
|
431
|
+
- Security best practices
|
|
432
|
+
- Complete code examples for both methods
|
|
433
|
+
|
|
434
|
+
#### Step 3: SFTP Data Source Configuration
|
|
435
|
+
|
|
436
|
+
```typescript
|
|
437
|
+
const sftpSource = new SftpDataSource(
|
|
438
|
+
{
|
|
439
|
+
type: 'SFTP_CSV',
|
|
440
|
+
connectionId: 'inventory-sftp',
|
|
441
|
+
name: 'Inventory SFTP',
|
|
442
|
+
settings: {
|
|
443
|
+
...config.sftp,
|
|
444
|
+
csvDelimiter: ',',
|
|
445
|
+
csvHeaders: ['sku', 'location', 'quantity', 'status'],
|
|
446
|
+
csvSkipEmptyLines: true,
|
|
447
|
+
csvTrimValues: true,
|
|
448
|
+
},
|
|
449
|
+
},
|
|
450
|
+
logger
|
|
451
|
+
);
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
**Key options:**
|
|
455
|
+
|
|
456
|
+
- `csvHeaders`: Column names (auto-detected if CSV has header row)
|
|
457
|
+
- `csvSkipEmptyLines`: Ignore blank lines
|
|
458
|
+
- `csvTrimValues`: Remove whitespace from values
|
|
459
|
+
|
|
460
|
+
#### Step 4: Field Mapping
|
|
461
|
+
|
|
462
|
+
```typescript
|
|
463
|
+
const fieldMapper = new UniversalMapper({
|
|
464
|
+
fields: {
|
|
465
|
+
ref: { source: 'sku', resolver: 'sdk.uppercase' },
|
|
466
|
+
locationRef: { source: 'location', resolver: v => `LOC:${v}` },
|
|
467
|
+
qty: { source: 'quantity', resolver: 'sdk.parseInt' },
|
|
468
|
+
status: { source: 'status', resolver: statusMapFunction },
|
|
469
|
+
},
|
|
470
|
+
});
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
**Mapping strategies:**
|
|
474
|
+
|
|
475
|
+
- **Built-in resolvers**: `sdk.uppercase`, `sdk.parseInt`, `sdk.formatDate`
|
|
476
|
+
- **Custom functions**: `(value) => transformedValue`
|
|
477
|
+
- **Prefixing**: Add prefixes to IDs (`LOC:WAREHOUSE-A`)
|
|
478
|
+
- **Mapping tables**: Convert codes to values
|
|
479
|
+
|
|
480
|
+
#### Step 11: Validation
|
|
481
|
+
|
|
482
|
+
```typescript
|
|
483
|
+
function validateRecords(records: any[]) {
|
|
484
|
+
const validRecords = records.filter(record => {
|
|
485
|
+
// Check required fields
|
|
486
|
+
if (!record.sku || !record.location) return false;
|
|
487
|
+
|
|
488
|
+
// Validate data types
|
|
489
|
+
if (isNaN(Number(record.quantity))) return false;
|
|
490
|
+
|
|
491
|
+
// Validate formats
|
|
492
|
+
if (!/^[A-Z0-9-]+$/i.test(record.sku)) return false;
|
|
493
|
+
|
|
494
|
+
return true;
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
return { validRecords, validationErrors };
|
|
498
|
+
}
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
**Validation levels:**
|
|
502
|
+
|
|
503
|
+
1. **Required fields**: Must be present
|
|
504
|
+
2. **Data types**: Must be correct type (number, string, etc.)
|
|
505
|
+
3. **Formats**: Must match expected patterns (SKU format, email, etc.)
|
|
506
|
+
4. **Business rules**: Must satisfy domain logic (quantity > 0, etc.)
|
|
507
|
+
|
|
508
|
+
#### Step 14: Batch API Call
|
|
509
|
+
|
|
510
|
+
```typescript
|
|
511
|
+
await fluentClient.sendBatch(job.id, {
|
|
512
|
+
action: 'UPSERT', // Create or update
|
|
513
|
+
entityType: 'INVENTORY', // Entity type
|
|
514
|
+
entities: transformedRecords.map(record => ({
|
|
515
|
+
ref: record.ref,
|
|
516
|
+
productRef: record.ref,
|
|
517
|
+
locationRef: record.locationRef,
|
|
518
|
+
qty: record.qty,
|
|
519
|
+
status: record.status,
|
|
520
|
+
type: 'ADJUSTMENT',
|
|
521
|
+
})),
|
|
522
|
+
});
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
**Batch API notes:**
|
|
526
|
+
|
|
527
|
+
- `action: 'UPSERT'` - Creates new or updates existing
|
|
528
|
+
- `entityType: 'INVENTORY'` - Only entity type supported
|
|
529
|
+
- Required fields: `ref`, `productRef`, `locationRef`, `qty`
|
|
530
|
+
- Optional fields: `status`, `type`
|
|
531
|
+
|
|
532
|
+
#### Step 15: Status Monitoring
|
|
533
|
+
|
|
534
|
+
```typescript
|
|
535
|
+
async function waitForBatchCompletion(client, jobId, batchId) {
|
|
536
|
+
let batchStatus = 'PENDING';
|
|
537
|
+
let attempts = 0;
|
|
538
|
+
const maxAttempts = 60; // 5 minutes max
|
|
539
|
+
|
|
540
|
+
while (batchStatus !== 'COMPLETED' && batchStatus !== 'FAILED' && attempts < maxAttempts) {
|
|
541
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
542
|
+
const statusResult = await client.getBatchStatus(jobId, batchId);
|
|
543
|
+
batchStatus = statusResult.status;
|
|
544
|
+
attempts++;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if (batchStatus !== 'COMPLETED') {
|
|
548
|
+
throw new Error('Batch failed or timed out');
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
**Why poll for status:**
|
|
554
|
+
|
|
555
|
+
- Batch API is asynchronous (returns immediately)
|
|
556
|
+
- Need to wait for processing to complete
|
|
557
|
+
- Detect failures early
|
|
558
|
+
- Implement timeout to prevent infinite loops
|
|
559
|
+
|
|
560
|
+
### Error Handling Strategy
|
|
561
|
+
|
|
562
|
+
```typescript
|
|
563
|
+
try {
|
|
564
|
+
// Process file
|
|
565
|
+
await processFile(file);
|
|
566
|
+
|
|
567
|
+
// Archive on success
|
|
568
|
+
await sftpSource.moveFile(file.name, 'archive/');
|
|
569
|
+
} catch (error) {
|
|
570
|
+
logger.error(`Failed to process ${file.name}:`, error);
|
|
571
|
+
|
|
572
|
+
// Move to error folder for review
|
|
573
|
+
await sftpSource.moveFile(file.name, 'errors/');
|
|
574
|
+
|
|
575
|
+
// Log error in state for audit trail (use KV directly)
|
|
576
|
+
// const kv = openKv(); // Initialize KV store
|
|
577
|
+
// await kv.set(['errors', 'inventory', file.name], {
|
|
578
|
+
// error: error.message,
|
|
579
|
+
// timestamp: new Date().toISOString()
|
|
580
|
+
// });
|
|
581
|
+
|
|
582
|
+
// Continue processing next file (don't throw)
|
|
583
|
+
}
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
**Error handling principles:**
|
|
587
|
+
|
|
588
|
+
1. **Log detailed errors** - Include file name, error message, stack trace
|
|
589
|
+
2. **Move failed files** - Quarantine for manual review
|
|
590
|
+
3. **Track failures** - Record in state management
|
|
591
|
+
4. **Continue processing** - One failure shouldn't stop entire batch
|
|
592
|
+
5. **Notify on critical errors** - Alert administrators when needed
|
|
593
|
+
|
|
594
|
+
### File Archival Pattern
|
|
595
|
+
|
|
596
|
+
```typescript
|
|
597
|
+
// Archive processed files by date
|
|
598
|
+
await sftpSource.moveFile(
|
|
599
|
+
'/inventory/updates/INV_2025-01-15.csv',
|
|
600
|
+
'/inventory/archive/2025-01-15/INV_2025-01-15.csv',
|
|
601
|
+
false // Don't overwrite if exists
|
|
602
|
+
);
|
|
603
|
+
|
|
604
|
+
// Move errors to dedicated folder
|
|
605
|
+
await sftpSource.moveFile(
|
|
606
|
+
'/inventory/updates/BAD_FILE.csv',
|
|
607
|
+
'/inventory/errors/BAD_FILE.csv',
|
|
608
|
+
true // Overwrite if exists (append timestamp to avoid loss)
|
|
609
|
+
);
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
**Archival best practices:**
|
|
613
|
+
|
|
614
|
+
- Organize by date: `archive/YYYY-MM-DD/filename.csv`
|
|
615
|
+
- Keep processed files for audit trail (at least 30 days)
|
|
616
|
+
- Separate error files for investigation
|
|
617
|
+
- Use retention policies (auto-delete after N days)
|
|
618
|
+
|
|
619
|
+
## Practice Exercise
|
|
620
|
+
|
|
621
|
+
**Challenge:** Modify the connector to handle a CSV with additional fields
|
|
622
|
+
|
|
623
|
+
**New CSV format:**
|
|
624
|
+
|
|
625
|
+
```csv
|
|
626
|
+
sku,location,on_hand,allocated,damaged,status,last_updated
|
|
627
|
+
SKU-001,WH-A,100,20,5,active,2025-01-15T10:00:00Z
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
**Requirements:**
|
|
631
|
+
|
|
632
|
+
1. Calculate available quantity: `on_hand - allocated - damaged`
|
|
633
|
+
2. Map `last_updated` to ISO date format
|
|
634
|
+
3. Add validation: `on_hand >= allocated + damaged`
|
|
635
|
+
4. Include `lastUpdated` attribute in Fluent payload
|
|
636
|
+
|
|
637
|
+
**Solution:**
|
|
638
|
+
|
|
639
|
+
```typescript
|
|
640
|
+
const fieldMapper = new UniversalMapper({
|
|
641
|
+
fields: {
|
|
642
|
+
ref: { source: 'sku', resolver: 'sdk.uppercase' },
|
|
643
|
+
locationRef: { source: 'location' },
|
|
644
|
+
qty: {
|
|
645
|
+
source: ['on_hand', 'allocated', 'damaged'],
|
|
646
|
+
resolver: values => {
|
|
647
|
+
const [onHand, allocated, damaged] = values.map(Number);
|
|
648
|
+
return onHand - allocated - damaged;
|
|
649
|
+
},
|
|
650
|
+
},
|
|
651
|
+
status: { source: 'status', resolver: 'sdk.uppercase' },
|
|
652
|
+
lastUpdated: { source: 'last_updated', resolver: 'sdk.formatDate' },
|
|
653
|
+
},
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
// Add validation
|
|
657
|
+
function validateRecords(records: any[]) {
|
|
658
|
+
return records.filter(record => {
|
|
659
|
+
const onHand = Number(record.on_hand);
|
|
660
|
+
const allocated = Number(record.allocated);
|
|
661
|
+
const damaged = Number(record.damaged);
|
|
662
|
+
|
|
663
|
+
return onHand >= allocated + damaged;
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
## Key Takeaways
|
|
669
|
+
|
|
670
|
+
- 🎯 Simple connectors follow a clear pattern: List → Download → Parse → Validate → Transform → Send → Archive
|
|
671
|
+
- 🎯 SftpDataSource handles all SFTP operations (list, download, move)
|
|
672
|
+
- 🎯 UniversalMapper provides flexible field transformation with built-in resolvers
|
|
673
|
+
- 🎯 Validation should happen before transformation to catch bad data early
|
|
674
|
+
- 🎯 Batch API requires job creation + batch send + status monitoring
|
|
675
|
+
- 🎯 Error handling should be graceful (log, quarantine, continue)
|
|
676
|
+
- 🎯 State management prevents duplicate processing
|
|
677
|
+
- 🎯 **Security:** Use secure credential access methods on Versori platform (see [SFTP Credential Access & Security](../../../02-CORE-GUIDES/data-sources/data-sources-sftp-credential-access-security.md))
|
|
678
|
+
|
|
679
|
+
## Next Steps
|
|
680
|
+
|
|
681
|
+
Continue to [Module 3: Intermediate Scenarios](./connector-scenarios-03-intermediate-scenarios.md) to learn S3 Parquet processing, JSON order creation, and advanced validation patterns.
|