@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,1198 +1,1198 @@
|
|
|
1
|
-
# Module 5: Services
|
|
2
|
-
|
|
3
|
-
**Level:** Intermediate
|
|
4
|
-
**Category:** Core Services
|
|
5
|
-
|
|
6
|
-
## Overview
|
|
7
|
-
|
|
8
|
-
The SDK provides a comprehensive set of services for data transformation, state management, extraction, webhook validation, logging, and S3 operations.
|
|
9
|
-
|
|
10
|
-
## Table of Contents
|
|
11
|
-
|
|
12
|
-
- [Universal Mapper](#universal-mapper)
|
|
13
|
-
- [FluentBatchManager](#fluentbatchmanager)
|
|
14
|
-
- [State Management Service](#state-management-service)
|
|
15
|
-
- [Webhook Validation Service](#webhook-validation-service)
|
|
16
|
-
- [Logging Utilities](#logging-utilities)
|
|
17
|
-
- [S3Service](#s3service)
|
|
18
|
-
- [S3PresignService](#s3presignservice)
|
|
19
|
-
- [JobTracker](#jobtracker-new)
|
|
20
|
-
- [PartialBatchRecovery](#partialbatchrecovery-new)
|
|
21
|
-
- [PreflightValidator](#preflightvalidator-new)
|
|
22
|
-
- [Versori Adapters](#versori-adapters-new)
|
|
23
|
-
- [See Also](#see-also)
|
|
24
|
-
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
## Universal Mapper
|
|
28
|
-
|
|
29
|
-
Map and transform data between schemas using the universal `fields` configuration.
|
|
30
|
-
|
|
31
|
-
```typescript
|
|
32
|
-
import { UniversalMapper } from '@fluentcommerce/fc-connect-sdk';
|
|
33
|
-
|
|
34
|
-
const mapper = new UniversalMapper({
|
|
35
|
-
version: '1.0',
|
|
36
|
-
description: 'Inventory ingestion',
|
|
37
|
-
fields: {
|
|
38
|
-
skuRef: { source: 'SKU', required: true },
|
|
39
|
-
locationRef: { source: 'LOCATION', required: true },
|
|
40
|
-
qty: { source: 'QTY', resolver: 'sdk.parseInt', required: true },
|
|
41
|
-
status: { source: 'status', resolver: 'sdk.uppercase' },
|
|
42
|
-
},
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
const result = await mapper.map(record);
|
|
46
|
-
if (result.success) {
|
|
47
|
-
console.log(result.data);
|
|
48
|
-
}
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## FluentBatchManager
|
|
52
|
-
|
|
53
|
-
Thin orchestration layer for Fluent Commerce job and batch operations. Wraps FluentClient with stateless methods for job creation, batch submission, and status polling.
|
|
54
|
-
|
|
55
|
-
**Export Name:**
|
|
56
|
-
- Use: `import { FluentBatchManager } from '@fluentcommerce/fc-connect-sdk';`
|
|
57
|
-
|
|
58
|
-
```typescript
|
|
59
|
-
import { FluentBatchManager } from '@fluentcommerce/fc-connect-sdk';
|
|
60
|
-
|
|
61
|
-
const manager = new FluentBatchManager(fluentVersoriClient, logger);
|
|
62
|
-
|
|
63
|
-
// Create a new job
|
|
64
|
-
const job = await manager.createJob('Daily Inventory Sync', {
|
|
65
|
-
retailerId: '1',
|
|
66
|
-
description: 'Daily inventory synchronization',
|
|
67
|
-
maxBatchCount: 100,
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
// Submit a batch to the job
|
|
71
|
-
await manager.submitBatch(job.id, {
|
|
72
|
-
action: 'UPSERT',
|
|
73
|
-
entityType: 'INVENTORY',
|
|
74
|
-
entities: inventoryRecords,
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
// Check job status
|
|
78
|
-
const jobStatus = await manager.getJobStatus(job.id);
|
|
79
|
-
console.log(`Job status: ${jobStatus.status}`);
|
|
80
|
-
|
|
81
|
-
// Check specific batch status
|
|
82
|
-
const batchStatus = await manager.getBatchStatus(job.id, batch.id);
|
|
83
|
-
console.log(`Batch status: ${batchStatus.status}`);
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
**Constructor:**
|
|
87
|
-
|
|
88
|
-
```typescript
|
|
89
|
-
new FluentBatchManager(
|
|
90
|
-
client: FluentVersoriClient, // Fluent Versori client
|
|
91
|
-
logger?: Logger // Optional logger
|
|
92
|
-
)
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
**Methods:**
|
|
96
|
-
|
|
97
|
-
- `createJob(name: string, metadata?: any): Promise<FluentJobResponse>` - Create a new job with optional metadata
|
|
98
|
-
- `submitBatch(jobId: string, batch: FluentBatchPayload): Promise<void>` - Submit a batch to a job
|
|
99
|
-
- `getJobStatus(jobId: string): Promise<any>` - Get current job status
|
|
100
|
-
- `getBatchStatus(jobId: string, batchId: string): Promise<any>` - Get specific batch status
|
|
101
|
-
|
|
102
|
-
**Use Cases:**
|
|
103
|
-
|
|
104
|
-
- Simple ingestion flows (createJob → sendBatch → pollStatus)
|
|
105
|
-
- Direct job management without state tracking
|
|
106
|
-
- Thin wrapper around FluentVersoriClient batch operations
|
|
107
|
-
|
|
108
|
-
**Note:** For complex workflows with state management, compose SDK services manually with StateService.
|
|
109
|
-
|
|
110
|
-
## State Management Service
|
|
111
|
-
|
|
112
|
-
Track processing state and prevent duplicate operations.
|
|
113
|
-
|
|
114
|
-
```typescript
|
|
115
|
-
import { StateService, VersoriKVAdapter } from '@fluentcommerce/fc-connect-sdk';
|
|
116
|
-
// ✅ CORRECT: Access openKv from Versori context
|
|
117
|
-
// import { openKv } from '@versori/run'; // ❌ WRONG - Not a direct export
|
|
118
|
-
|
|
119
|
-
const logger = console;
|
|
120
|
-
const stateService = new StateService(logger);
|
|
121
|
-
// In Versori workflow handler:
|
|
122
|
-
const { openKv } = ctx;
|
|
123
|
-
const kv = new VersoriKVAdapter(openKv(':project:')); // Implements KVStore
|
|
124
|
-
|
|
125
|
-
// Acquire/release distributed lock
|
|
126
|
-
const acquired = await stateService.acquireLock('critical-section', kv, 15);
|
|
127
|
-
try {
|
|
128
|
-
if (acquired) {
|
|
129
|
-
// Critical section
|
|
130
|
-
}
|
|
131
|
-
} finally {
|
|
132
|
-
await stateService.releaseLock('critical-section', kv);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Check if a file was processed (deduplication)
|
|
136
|
-
const isProcessed = await stateService.isFileProcessed(kv as any, 'file.csv', 'inventory-sync');
|
|
137
|
-
|
|
138
|
-
// Update sync state after processing
|
|
139
|
-
await stateService.updateSyncState(
|
|
140
|
-
kv as any,
|
|
141
|
-
[
|
|
142
|
-
{
|
|
143
|
-
fileName: 'file.csv',
|
|
144
|
-
lastModified: new Date().toISOString(),
|
|
145
|
-
recordCount: 1000,
|
|
146
|
-
},
|
|
147
|
-
],
|
|
148
|
-
'inventory-sync'
|
|
149
|
-
);
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
**Methods:**
|
|
153
|
-
|
|
154
|
-
- `acquireLock(lockName: string, kv: KVStore, timeoutMinutes?: number): Promise<boolean>`
|
|
155
|
-
- `releaseLock(lockName: string, kv: KVStore): Promise<void>`
|
|
156
|
-
- `getSyncState(kv: KVStore, workflowId?: string): Promise<SyncState>`
|
|
157
|
-
- `updateSyncState(kv: KVStore, processedFiles: ProcessedFile[], workflowId?: string): Promise<void>`
|
|
158
|
-
- `getDailyJob(kv: KVStore, workflowId: string): Promise<DailyJob | null>`
|
|
159
|
-
- `setDailyJob(kv: KVStore, workflowId: string, jobId: string, expirationHours?: number): Promise<void>`
|
|
160
|
-
- `isFileProcessed(kv: KVStore, fileName: string, workflowId?: string): Promise<boolean>`
|
|
161
|
-
- `getMetrics(kv: KVStore, workflowId?: string): Promise<{ totalFiles: number; totalRecords: number; lastProcessedTime?: string; }>`
|
|
162
|
-
- `clearState(kv: KVStore, workflowId: string): Promise<void>`
|
|
163
|
-
|
|
164
|
-
## Webhook Validation Service
|
|
165
|
-
|
|
166
|
-
> **Warning: IMPORTANT WEBHOOK VALIDATION NOTICE**
|
|
167
|
-
>
|
|
168
|
-
> This service is **ONLY** for validating webhooks sent from **Fluent Commerce Rubix workflows**.
|
|
169
|
-
>
|
|
170
|
-
> These methods will **NOT** work with:
|
|
171
|
-
>
|
|
172
|
-
> - Generic HTTP webhooks
|
|
173
|
-
> - Third-party service webhooks
|
|
174
|
-
> - Custom webhook implementations
|
|
175
|
-
>
|
|
176
|
-
> Only use this service if you are receiving webhooks directly from Fluent Commerce Rubix workflow executions.
|
|
177
|
-
|
|
178
|
-
The service uses RSA public key cryptography for signature validation.
|
|
179
|
-
|
|
180
|
-
```typescript
|
|
181
|
-
import {
|
|
182
|
-
WebhookValidationService,
|
|
183
|
-
WebhookValidationFactory,
|
|
184
|
-
SignatureAlgorithm,
|
|
185
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
186
|
-
|
|
187
|
-
// IMPORTANT: This validation is ONLY for webhooks from Fluent Commerce Rubix workflows
|
|
188
|
-
const validator = new WebhookValidationService(
|
|
189
|
-
{
|
|
190
|
-
algorithm: SignatureAlgorithm.SHA512_WITH_RSA, // Recommended
|
|
191
|
-
strictValidation: true, // Enforce strict checks
|
|
192
|
-
maxTimestampAge: 300000, // 5 minutes (optional)
|
|
193
|
-
},
|
|
194
|
-
logger
|
|
195
|
-
);
|
|
196
|
-
|
|
197
|
-
// Get public key from environment or connector variables
|
|
198
|
-
const publicKey = process.env.FLUENT_WEBHOOK_PUBLIC_KEY;
|
|
199
|
-
|
|
200
|
-
// Validate Fluent Commerce Rubix webhook signature
|
|
201
|
-
const result = await validator.validateWebhookSignature(
|
|
202
|
-
JSON.stringify(requestBody),
|
|
203
|
-
headers['fluent-signature'],
|
|
204
|
-
publicKey,
|
|
205
|
-
SignatureAlgorithm.SHA512_WITH_RSA
|
|
206
|
-
);
|
|
207
|
-
|
|
208
|
-
if (!result.isValid) {
|
|
209
|
-
throw new Error(`Validation failed: ${result.error}`);
|
|
210
|
-
}
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
**Configuration Options:**
|
|
214
|
-
|
|
215
|
-
```typescript
|
|
216
|
-
interface WebhookValidationConfig {
|
|
217
|
-
algorithm?: SignatureAlgorithm; // Default: SHA512_WITH_RSA
|
|
218
|
-
strictValidation?: boolean; // Default: true
|
|
219
|
-
maxTimestampAge?: number; // Max age in ms (optional)
|
|
220
|
-
}
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
**SignatureAlgorithm Enum:**
|
|
224
|
-
|
|
225
|
-
```typescript
|
|
226
|
-
enum SignatureAlgorithm {
|
|
227
|
-
SHA512_WITH_RSA = 'SHA512withRSA',
|
|
228
|
-
MD5_WITH_RSA = 'MD5withRSA',
|
|
229
|
-
}
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
**Methods:**
|
|
233
|
-
|
|
234
|
-
- `validateWebhookSignature(payload: string, signature: string, publicKey: string, algorithm?: SignatureAlgorithm): Promise<ValidationResult>` - Validate Fluent Rubix webhook signature
|
|
235
|
-
- `validateSignature(payload: string, signature: string, publicKey: string): Promise<boolean>` - Simple signature validation
|
|
236
|
-
- `verifyTimestamp(timestamp: string | number): boolean` - Verify timestamp freshness
|
|
237
|
-
|
|
238
|
-
**ValidationResult Interface:**
|
|
239
|
-
|
|
240
|
-
```typescript
|
|
241
|
-
interface ValidationResult {
|
|
242
|
-
isValid: boolean;
|
|
243
|
-
error?: string;
|
|
244
|
-
details?: {
|
|
245
|
-
signatureValid?: boolean;
|
|
246
|
-
timestampValid?: boolean;
|
|
247
|
-
algorithm?: string;
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
**Factory Pattern:**
|
|
253
|
-
|
|
254
|
-
```typescript
|
|
255
|
-
// Use factory for creating validators with different configs
|
|
256
|
-
const factory = new WebhookValidationFactory();
|
|
257
|
-
|
|
258
|
-
// Create validator for SHA512 (for Fluent Commerce Rubix workflows)
|
|
259
|
-
const sha512Validator = factory.createValidator(SignatureAlgorithm.SHA512_WITH_RSA, logger);
|
|
260
|
-
|
|
261
|
-
// Create validator for MD5 (for Flex workflows)
|
|
262
|
-
const md5Validator = factory.createValidator(SignatureAlgorithm.MD5_WITH_RSA, logger);
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
**Public Key Format:**
|
|
266
|
-
The public key should be in PEM format. You can obtain this from:
|
|
267
|
-
|
|
268
|
-
- Fluent Commerce support team
|
|
269
|
-
- Your Fluent Commerce dashboard (if available)
|
|
270
|
-
- Different retailers may have different public keys
|
|
271
|
-
|
|
272
|
-
```bash
|
|
273
|
-
# Example public key format in .env file
|
|
274
|
-
FLUENT_WEBHOOK_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
|
|
275
|
-
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
|
|
276
|
-
...
|
|
277
|
-
-----END PUBLIC KEY-----"
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
**Complete Example:**
|
|
281
|
-
|
|
282
|
-
```typescript
|
|
283
|
-
import {
|
|
284
|
-
WebhookValidationService,
|
|
285
|
-
SignatureAlgorithm,
|
|
286
|
-
parseWebhookRequest,
|
|
287
|
-
validateFluentEvent,
|
|
288
|
-
createConsoleLogger,
|
|
289
|
-
toStructuredLogger,
|
|
290
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
291
|
-
|
|
292
|
-
const logger = toStructuredLogger(createConsoleLogger(), {
|
|
293
|
-
logLevel: 'info',
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
// IMPORTANT: This validation is ONLY for webhooks from Fluent Commerce Rubix workflows
|
|
297
|
-
const validator = new WebhookValidationService(
|
|
298
|
-
{
|
|
299
|
-
algorithm: SignatureAlgorithm.SHA512_WITH_RSA,
|
|
300
|
-
strictValidation: true,
|
|
301
|
-
},
|
|
302
|
-
logger
|
|
303
|
-
);
|
|
304
|
-
|
|
305
|
-
export async function handleFluentWebhook(req, res) {
|
|
306
|
-
try {
|
|
307
|
-
// Extract signature from headers
|
|
308
|
-
const signature = req.headers['fluent-signature'] || req.headers['x-fluent-signature'];
|
|
309
|
-
|
|
310
|
-
if (!signature) {
|
|
311
|
-
return res.status(401).json({ error: 'Missing signature' });
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// Get public key from environment
|
|
315
|
-
const publicKey = process.env.FLUENT_WEBHOOK_PUBLIC_KEY;
|
|
316
|
-
|
|
317
|
-
// Validate Fluent Commerce Rubix webhook
|
|
318
|
-
const validationResult = await validator.validateWebhookSignature(
|
|
319
|
-
JSON.stringify(req.body),
|
|
320
|
-
signature,
|
|
321
|
-
publicKey,
|
|
322
|
-
SignatureAlgorithm.SHA512_WITH_RSA
|
|
323
|
-
);
|
|
324
|
-
|
|
325
|
-
if (!validationResult.isValid) {
|
|
326
|
-
logger.error('Fluent Commerce Rubix webhook validation failed', validationResult);
|
|
327
|
-
return res.status(401).json({
|
|
328
|
-
error: 'Invalid Fluent Rubix webhook signature',
|
|
329
|
-
details: validationResult.error,
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// Parse and validate payload
|
|
334
|
-
const payload = parseWebhookRequest(req.body, logger, 'webhook');
|
|
335
|
-
const validation = validateFluentEvent(payload, logger, 'webhook');
|
|
336
|
-
|
|
337
|
-
if (!validation.isValid) {
|
|
338
|
-
return res.status(400).json({
|
|
339
|
-
error: 'Invalid payload',
|
|
340
|
-
warnings: validation.warnings,
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// Process webhook
|
|
345
|
-
await processWebhookPayload(payload);
|
|
346
|
-
|
|
347
|
-
return res.status(200).json({ success: true });
|
|
348
|
-
} catch (error) {
|
|
349
|
-
logger.error('Webhook processing failed', { error: error.message });
|
|
350
|
-
return res.status(500).json({ error: 'Internal server error' });
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
## Logging Utilities
|
|
356
|
-
|
|
357
|
-
The SDK provides minimal logging utilities for standalone environments. **Versori users should always use native platform logs.**
|
|
358
|
-
|
|
359
|
-
**Historical Note:** The `LoggingService` class (684 lines) was refactored to function-based utilities (~390 lines) for better composability.
|
|
360
|
-
|
|
361
|
-
### Platform-Specific Guidance
|
|
362
|
-
|
|
363
|
-
| Runtime | Use | Example |
|
|
364
|
-
|---------|-----|---------|
|
|
365
|
-
| **Versori Platform** | Native `log` from context | `const { log } = ctx;` |
|
|
366
|
-
| **Standalone Node.js** | Function-based utilities | `createConsoleLogger()` + `toStructuredLogger()` |
|
|
367
|
-
| **Standalone Deno** | Function-based utilities | Same as Node.js |
|
|
368
|
-
| **Other Platforms** | Implement `Logger` interface | Custom adapter |
|
|
369
|
-
|
|
370
|
-
**✅ Versori Platform (Recommended - Use Native Log):**
|
|
371
|
-
|
|
372
|
-
Always use native `log` from context - don't wrap it:
|
|
373
|
-
|
|
374
|
-
```typescript
|
|
375
|
-
import { schedule, http } from '@versori/run';
|
|
376
|
-
import { createClient, SftpDataSource } from '@fluentcommerce/fc-connect-sdk';
|
|
377
|
-
|
|
378
|
-
export const myWorkflow = schedule('my-workflow', '0 * * * *')
|
|
379
|
-
.then(http('process', { connection: 'fluent' }, async (ctx) => {
|
|
380
|
-
const { log } = ctx; // Only extract what you need
|
|
381
|
-
|
|
382
|
-
// ✅ Pass native log directly to SDK services
|
|
383
|
-
const client = await createClient(ctx); // Auto-detects Versori context
|
|
384
|
-
const sftp = new SftpDataSource(config, log);
|
|
385
|
-
|
|
386
|
-
log.info('Processing started');
|
|
387
|
-
log.error('Failed to process', { error: err.message });
|
|
388
|
-
}));
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
**✅ Standalone Node.js/Deno (Use Minimal Utilities):**
|
|
392
|
-
|
|
393
|
-
```typescript
|
|
394
|
-
import {
|
|
395
|
-
FluentClient,
|
|
396
|
-
createConsoleLogger,
|
|
397
|
-
toStructuredLogger,
|
|
398
|
-
SftpDataSource,
|
|
399
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
400
|
-
|
|
401
|
-
// Simple console logger
|
|
402
|
-
const logger = createConsoleLogger();
|
|
403
|
-
|
|
404
|
-
// Or with context
|
|
405
|
-
const contextLogger = toStructuredLogger(logger, {
|
|
406
|
-
service: 'InventorySync',
|
|
407
|
-
logLevel: 'debug',
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
const client = await createClient({
|
|
411
|
-
config: config,
|
|
412
|
-
logger: contextLogger,
|
|
413
|
-
});
|
|
414
|
-
const sftp = new SftpDataSource(config, contextLogger);
|
|
415
|
-
|
|
416
|
-
contextLogger.info('Processing started', { file: 'data.csv' });
|
|
417
|
-
```
|
|
418
|
-
|
|
419
|
-
### Core Functions
|
|
420
|
-
|
|
421
|
-
**Logger Creation:**
|
|
422
|
-
|
|
423
|
-
- `createConsoleLogger(): Logger` - Create basic console logger for standalone use
|
|
424
|
-
- `toStructuredLogger(logger: Logger, context: LogContext): Logger` - Add context metadata to logger
|
|
425
|
-
- `generateCorrelationId(): string` - Generate unique correlation ID for request tracking
|
|
426
|
-
|
|
427
|
-
**Convenience Functions:**
|
|
428
|
-
|
|
429
|
-
- `createWorkflowLogger(baseLogger: Logger, workflowName: string, debugMode?: boolean): Logger` - Pre-configured workflow logger with correlationId
|
|
430
|
-
- `createServiceLogger(baseLogger: Logger, serviceName: string, debugMode?: boolean): Logger` - Pre-configured service logger
|
|
431
|
-
- `createErrorLogger(baseLogger: Logger, error: Error, context?: Record<string, any>): Logger` - Error-specific logger with context
|
|
432
|
-
|
|
433
|
-
### Logger Interface
|
|
434
|
-
|
|
435
|
-
All logging utilities implement the `Logger` interface:
|
|
436
|
-
|
|
437
|
-
```typescript
|
|
438
|
-
interface Logger {
|
|
439
|
-
debug(message: string, meta?: Record<string, any>): void;
|
|
440
|
-
info(message: string, meta?: Record<string, any>): void;
|
|
441
|
-
warn(message: string, meta?: Record<string, any>): void;
|
|
442
|
-
error(message: string, err?: Error | unknown, meta?: Record<string, any>): void;
|
|
443
|
-
}
|
|
444
|
-
```
|
|
445
|
-
|
|
446
|
-
### Quick Decision Guide
|
|
447
|
-
|
|
448
|
-
| Runtime | Use | Reason |
|
|
449
|
-
| ---------------------- | ------------------------------------------------ | ------------------------------ |
|
|
450
|
-
| **Versori Platform** | Native `log` from context | Platform provides logging |
|
|
451
|
-
| **Standalone Node.js** | `createConsoleLogger()` + `toStructuredLogger()` | Lightweight structured logging |
|
|
452
|
-
| **Standalone Deno** | `createConsoleLogger()` + `toStructuredLogger()` | Lightweight structured logging |
|
|
453
|
-
| **Vercel/CF/Other** | Implement `Logger` interface | Easy extensibility |
|
|
454
|
-
|
|
455
|
-
### Migration from LoggingService
|
|
456
|
-
|
|
457
|
-
If you have existing code using `LoggingService`, update it as follows:
|
|
458
|
-
|
|
459
|
-
```typescript
|
|
460
|
-
// ❌ OLD (LoggingService class - removed)
|
|
461
|
-
import { LoggingService } from '@fluentcommerce/fc-connect-sdk';
|
|
462
|
-
const logger = new LoggingService({ service: 'MyService', debug: true });
|
|
463
|
-
|
|
464
|
-
// ✅ NEW (function-based utilities)
|
|
465
|
-
import {
|
|
466
|
-
createConsoleLogger,
|
|
467
|
-
createServiceLogger,
|
|
468
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
469
|
-
const logger = createServiceLogger(createConsoleLogger(), 'MyService', true);
|
|
470
|
-
```
|
|
471
|
-
|
|
472
|
-
**See:** [Logging Guide](../../logging-guide.md) for complete documentation
|
|
473
|
-
|
|
474
|
-
## S3Service
|
|
475
|
-
|
|
476
|
-
High-performance S3 service for file operations with support for both SDK and presigned URL approaches.
|
|
477
|
-
|
|
478
|
-
### Constructor
|
|
479
|
-
|
|
480
|
-
```typescript
|
|
481
|
-
import { S3Service } from '@fluentcommerce/fc-connect-sdk';
|
|
482
|
-
|
|
483
|
-
// Default - uses AWS SDK
|
|
484
|
-
const s3Service = new S3Service({
|
|
485
|
-
accessKeyId: 'your-access-key',
|
|
486
|
-
secretAccessKey: 'your-secret-key',
|
|
487
|
-
region: 'us-east-1',
|
|
488
|
-
sessionToken: 'optional-session-token', // Optional
|
|
489
|
-
bucket: 'my-bucket', // Optional default bucket
|
|
490
|
-
endpoint: 'https://s3.amazonaws.com', // Optional custom endpoint
|
|
491
|
-
forcePathStyle: false, // Optional path style
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
// Explicit presigned (for Versori with ctx.fetch)
|
|
495
|
-
const s3ServicePresigned = new S3Service({
|
|
496
|
-
accessKeyId: 'your-access-key',
|
|
497
|
-
secretAccessKey: 'your-secret-key',
|
|
498
|
-
region: 'us-east-1',
|
|
499
|
-
usePresigned: true, // Enable presigned URL mode
|
|
500
|
-
fetch: ctx.fetch, // Required when usePresigned is true
|
|
501
|
-
});
|
|
502
|
-
```
|
|
503
|
-
|
|
504
|
-
### listObjects Method
|
|
505
|
-
|
|
506
|
-
List objects in an S3 bucket.
|
|
507
|
-
|
|
508
|
-
```typescript
|
|
509
|
-
const objects = await s3Service.listObjects(
|
|
510
|
-
bucket: string,
|
|
511
|
-
options?: ListOptions
|
|
512
|
-
): Promise<S3Object[]>
|
|
513
|
-
```
|
|
514
|
-
|
|
515
|
-
**ListOptions:**
|
|
516
|
-
|
|
517
|
-
```typescript
|
|
518
|
-
interface ListOptions {
|
|
519
|
-
prefix?: string; // Filter by prefix
|
|
520
|
-
delimiter?: string; // Delimiter for grouping
|
|
521
|
-
maxKeys?: number; // Maximum objects to return
|
|
522
|
-
continuationToken?: string; // Pagination token
|
|
523
|
-
startAfter?: string; // Start after specific key
|
|
524
|
-
}
|
|
525
|
-
```
|
|
526
|
-
|
|
527
|
-
**Example:**
|
|
528
|
-
|
|
529
|
-
```typescript
|
|
530
|
-
const objects = await s3Service.listObjects('my-bucket', {
|
|
531
|
-
prefix: 'inventory/2024/',
|
|
532
|
-
maxKeys: 1000,
|
|
533
|
-
});
|
|
534
|
-
|
|
535
|
-
objects.forEach(obj => {
|
|
536
|
-
console.log(`${obj.key}: ${obj.size} bytes, modified ${obj.lastModified}`);
|
|
537
|
-
});
|
|
538
|
-
```
|
|
539
|
-
|
|
540
|
-
### getObject Method
|
|
541
|
-
|
|
542
|
-
Retrieve an object from S3.
|
|
543
|
-
|
|
544
|
-
```typescript
|
|
545
|
-
const data = await s3Service.getObject(
|
|
546
|
-
bucket: string,
|
|
547
|
-
key: string,
|
|
548
|
-
options?: GetOptions
|
|
549
|
-
): Promise<Buffer | string>
|
|
550
|
-
```
|
|
551
|
-
|
|
552
|
-
**Example:**
|
|
553
|
-
|
|
554
|
-
```typescript
|
|
555
|
-
const csvData = await s3Service.getObject('my-bucket', 'inventory/data.csv');
|
|
556
|
-
const content = csvData.toString('utf-8');
|
|
557
|
-
```
|
|
558
|
-
|
|
559
|
-
### putObject Method
|
|
560
|
-
|
|
561
|
-
Upload an object to S3.
|
|
562
|
-
|
|
563
|
-
```typescript
|
|
564
|
-
await s3Service.putObject(
|
|
565
|
-
bucket: string,
|
|
566
|
-
key: string,
|
|
567
|
-
body: Buffer | string | Uint8Array,
|
|
568
|
-
options?: PutOptions
|
|
569
|
-
): Promise<void>
|
|
570
|
-
```
|
|
571
|
-
|
|
572
|
-
**PutOptions:**
|
|
573
|
-
|
|
574
|
-
```typescript
|
|
575
|
-
interface PutOptions {
|
|
576
|
-
contentType?: string;
|
|
577
|
-
contentEncoding?: string;
|
|
578
|
-
metadata?: Record<string, string>;
|
|
579
|
-
storageClass?: 'STANDARD' | 'REDUCED_REDUNDANCY' | 'GLACIER' | 'DEEP_ARCHIVE';
|
|
580
|
-
serverSideEncryption?: string;
|
|
581
|
-
tagging?: string;
|
|
582
|
-
}
|
|
583
|
-
```
|
|
584
|
-
|
|
585
|
-
**Example:**
|
|
586
|
-
|
|
587
|
-
```typescript
|
|
588
|
-
await s3Service.putObject('my-bucket', 'results/output.json', JSON.stringify(data), {
|
|
589
|
-
contentType: 'application/json',
|
|
590
|
-
metadata: {
|
|
591
|
-
'processed-date': new Date().toISOString(),
|
|
592
|
-
'record-count': '1000',
|
|
593
|
-
},
|
|
594
|
-
});
|
|
595
|
-
```
|
|
596
|
-
|
|
597
|
-
### deleteObject Method
|
|
598
|
-
|
|
599
|
-
Delete an object from S3.
|
|
600
|
-
|
|
601
|
-
```typescript
|
|
602
|
-
await s3Service.deleteObject(
|
|
603
|
-
bucket: string,
|
|
604
|
-
key: string
|
|
605
|
-
): Promise<void>
|
|
606
|
-
```
|
|
607
|
-
|
|
608
|
-
### copyObject Method
|
|
609
|
-
|
|
610
|
-
Copy an object within S3.
|
|
611
|
-
|
|
612
|
-
```typescript
|
|
613
|
-
await s3Service.copyObject(
|
|
614
|
-
sourceBucket: string,
|
|
615
|
-
sourceKey: string,
|
|
616
|
-
destBucket: string,
|
|
617
|
-
destKey: string,
|
|
618
|
-
options?: CopyOptions
|
|
619
|
-
): Promise<void>
|
|
620
|
-
```
|
|
621
|
-
|
|
622
|
-
**Example:**
|
|
623
|
-
|
|
624
|
-
```typescript
|
|
625
|
-
// Archive processed file
|
|
626
|
-
await s3Service.copyObject(
|
|
627
|
-
'inventory-incoming',
|
|
628
|
-
'data/file.csv',
|
|
629
|
-
'inventory-archive',
|
|
630
|
-
`processed/${new Date().toISOString()}/file.csv`
|
|
631
|
-
);
|
|
632
|
-
```
|
|
633
|
-
|
|
634
|
-
### headObject Method
|
|
635
|
-
|
|
636
|
-
Get object metadata without downloading content.
|
|
637
|
-
|
|
638
|
-
```typescript
|
|
639
|
-
const metadata = await s3Service.headObject(
|
|
640
|
-
bucket: string,
|
|
641
|
-
key: string
|
|
642
|
-
): Promise<S3Object>
|
|
643
|
-
```
|
|
644
|
-
|
|
645
|
-
### getPresignedUrl Method
|
|
646
|
-
|
|
647
|
-
Generate presigned URLs for temporary access.
|
|
648
|
-
|
|
649
|
-
```typescript
|
|
650
|
-
const url = await s3Service.getPresignedUrl(
|
|
651
|
-
bucket: string,
|
|
652
|
-
key: string,
|
|
653
|
-
operation?: 'get' | 'put', // Default: 'get'
|
|
654
|
-
expiresIn?: number // Seconds, default 3600
|
|
655
|
-
): Promise<string>
|
|
656
|
-
```
|
|
657
|
-
|
|
658
|
-
**Example:**
|
|
659
|
-
|
|
660
|
-
```typescript
|
|
661
|
-
// Generate download URL valid for 1 hour
|
|
662
|
-
const downloadUrl = await s3Service.getPresignedUrl(
|
|
663
|
-
'my-bucket',
|
|
664
|
-
'reports/summary.pdf',
|
|
665
|
-
'get',
|
|
666
|
-
3600
|
|
667
|
-
);
|
|
668
|
-
|
|
669
|
-
// Generate upload URL valid for 10 minutes
|
|
670
|
-
const uploadUrl = await s3Service.getPresignedUrl('my-bucket', 'uploads/new-file.csv', 'put', 600);
|
|
671
|
-
```
|
|
672
|
-
|
|
673
|
-
### S3ServiceError
|
|
674
|
-
|
|
675
|
-
Custom error class for S3 operations.
|
|
676
|
-
|
|
677
|
-
```typescript
|
|
678
|
-
try {
|
|
679
|
-
await s3Service.getObject('bucket', 'key');
|
|
680
|
-
} catch (error) {
|
|
681
|
-
if (error instanceof S3ServiceError) {
|
|
682
|
-
console.error(`S3 Error: ${error.message}`);
|
|
683
|
-
console.error(`Code: ${error.code}`);
|
|
684
|
-
console.error(`Status: ${error.statusCode}`);
|
|
685
|
-
console.error(`Request ID: ${error.requestId}`);
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
```
|
|
689
|
-
|
|
690
|
-
## S3PresignService
|
|
691
|
-
|
|
692
|
-
AWS S3 Presigned URL Service that generates presigned URLs for S3 operations using AWS Signature Version 4.
|
|
693
|
-
|
|
694
|
-
```typescript
|
|
695
|
-
import { S3PresignService } from '@fluentcommerce/fc-connect-sdk';
|
|
696
|
-
|
|
697
|
-
const presignService = new S3PresignService(
|
|
698
|
-
{
|
|
699
|
-
accessKeyId: 'your-access-key',
|
|
700
|
-
secretAccessKey: 'your-secret-key',
|
|
701
|
-
region: 'us-east-1',
|
|
702
|
-
bucket: 'my-bucket', // Optional default bucket
|
|
703
|
-
sessionToken: 'xxx', // Optional session token
|
|
704
|
-
},
|
|
705
|
-
logger
|
|
706
|
-
);
|
|
707
|
-
|
|
708
|
-
// Generate presigned URL for getting an object
|
|
709
|
-
const getUrl = presignService.presignGetObject({
|
|
710
|
-
bucket: 'my-bucket',
|
|
711
|
-
key: 'data/file.csv',
|
|
712
|
-
accessKeyId: 'xxx',
|
|
713
|
-
secretAccessKey: 'yyy',
|
|
714
|
-
region: 'us-east-1',
|
|
715
|
-
expiresIn: 3600, // Optional, defaults to 3600 seconds (1 hour)
|
|
716
|
-
});
|
|
717
|
-
|
|
718
|
-
// Generate presigned URL for putting an object
|
|
719
|
-
const { url, headers } = presignService.presignPutObject({
|
|
720
|
-
bucket: 'my-bucket',
|
|
721
|
-
key: 'uploads/new-file.csv',
|
|
722
|
-
accessKeyId: 'xxx',
|
|
723
|
-
secretAccessKey: 'yyy',
|
|
724
|
-
region: 'us-east-1',
|
|
725
|
-
expiresIn: 600, // 10 minutes
|
|
726
|
-
});
|
|
727
|
-
|
|
728
|
-
// Generate presigned URL for copying an object
|
|
729
|
-
const { url: copyUrl, headers: copyHeaders } = presignService.presignCopyObject({
|
|
730
|
-
sourceBucket: 'source-bucket',
|
|
731
|
-
sourceKey: 'path/to/source.csv',
|
|
732
|
-
bucket: 'dest-bucket',
|
|
733
|
-
key: 'path/to/dest.csv',
|
|
734
|
-
accessKeyId: 'xxx',
|
|
735
|
-
secretAccessKey: 'yyy',
|
|
736
|
-
region: 'us-east-1',
|
|
737
|
-
metadata: { 'custom-header': 'value' }, // Optional metadata
|
|
738
|
-
});
|
|
739
|
-
|
|
740
|
-
// Generate presigned URL for deleting an object
|
|
741
|
-
const deleteUrl = presignService.presignDeleteObject({
|
|
742
|
-
bucket: 'my-bucket',
|
|
743
|
-
key: 'file-to-delete.csv',
|
|
744
|
-
accessKeyId: 'xxx',
|
|
745
|
-
secretAccessKey: 'yyy',
|
|
746
|
-
region: 'us-east-1',
|
|
747
|
-
});
|
|
748
|
-
|
|
749
|
-
// Generate presigned URL for listing objects
|
|
750
|
-
const listUrl = presignService.presignListObjects({
|
|
751
|
-
bucket: 'my-bucket',
|
|
752
|
-
prefix: 'data/', // Optional prefix filter
|
|
753
|
-
delimiter: '/', // Optional delimiter
|
|
754
|
-
maxKeys: 1000, // Optional max keys
|
|
755
|
-
accessKeyId: 'xxx',
|
|
756
|
-
secretAccessKey: 'yyy',
|
|
757
|
-
region: 'us-east-1',
|
|
758
|
-
expiresIn: 3600,
|
|
759
|
-
});
|
|
760
|
-
```
|
|
761
|
-
|
|
762
|
-
**Constructor:**
|
|
763
|
-
|
|
764
|
-
```typescript
|
|
765
|
-
new S3PresignService(
|
|
766
|
-
config: S3Config, // S3 configuration
|
|
767
|
-
logger?: StructuredLogger // Optional logger
|
|
768
|
-
)
|
|
769
|
-
```
|
|
770
|
-
|
|
771
|
-
**Methods:**
|
|
772
|
-
|
|
773
|
-
- `presignListObjects(params: PresignListParams): string` - Generate presigned URL for listing objects
|
|
774
|
-
- `presignGetObject(params: PresignObjectParams): string` - Generate presigned URL for getting an object
|
|
775
|
-
- `presignPutObject(params: PresignObjectParams): { url: string; headers: Record<string, string> }` - Generate presigned URL and headers for putting an object
|
|
776
|
-
- `presignCopyObject(params: PresignCopyParams): { url: string; headers: Record<string, string> }` - Generate presigned URL and headers for copying an object
|
|
777
|
-
- `presignDeleteObject(params: PresignDeleteParams): string` - Generate presigned URL for deleting an object
|
|
778
|
-
- `updateConfig(config: Partial<S3Config>): void` - Update S3 configuration
|
|
779
|
-
- `getConfigSummary(): Omit<S3Config, 'secretAccessKey' | 'sessionToken'>` - Get current configuration (without secrets)
|
|
780
|
-
- `validateConfig(): { isValid: boolean; errors: string[] }` - Validate S3 configuration
|
|
781
|
-
|
|
782
|
-
**Parameter Types:**
|
|
783
|
-
|
|
784
|
-
```typescript
|
|
785
|
-
interface PresignObjectParams {
|
|
786
|
-
bucket: string;
|
|
787
|
-
key: string;
|
|
788
|
-
accessKeyId: string;
|
|
789
|
-
secretAccessKey: string;
|
|
790
|
-
region: string;
|
|
791
|
-
sessionToken?: string;
|
|
792
|
-
expiresIn?: number; // Seconds, default 3600
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
interface PresignListParams {
|
|
796
|
-
bucket: string;
|
|
797
|
-
prefix?: string;
|
|
798
|
-
delimiter?: string;
|
|
799
|
-
maxKeys?: number;
|
|
800
|
-
accessKeyId: string;
|
|
801
|
-
secretAccessKey: string;
|
|
802
|
-
region: string;
|
|
803
|
-
sessionToken?: string;
|
|
804
|
-
expiresIn?: number;
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
interface PresignCopyParams {
|
|
808
|
-
sourceBucket?: string; // Defaults to bucket if not specified
|
|
809
|
-
sourceKey: string;
|
|
810
|
-
bucket: string;
|
|
811
|
-
key: string;
|
|
812
|
-
accessKeyId: string;
|
|
813
|
-
secretAccessKey: string;
|
|
814
|
-
region: string;
|
|
815
|
-
sessionToken?: string;
|
|
816
|
-
metadata?: Record<string, string>;
|
|
817
|
-
expiresIn?: number;
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
interface PresignDeleteParams {
|
|
821
|
-
bucket: string;
|
|
822
|
-
key: string;
|
|
823
|
-
accessKeyId: string;
|
|
824
|
-
secretAccessKey: string;
|
|
825
|
-
region: string;
|
|
826
|
-
sessionToken?: string;
|
|
827
|
-
expiresIn?: number;
|
|
828
|
-
}
|
|
829
|
-
```
|
|
830
|
-
|
|
831
|
-
## JobTracker (NEW)
|
|
832
|
-
|
|
833
|
-
Track job lifecycle and metrics in a KV store. Provides standardized job status tracking for async workflows with automatic timestamp management and TTL support.
|
|
834
|
-
|
|
835
|
-
```typescript
|
|
836
|
-
import { JobTracker } from '@fluentcommerce/fc-connect-sdk';
|
|
837
|
-
import { VersoriKVAdapter } from '@fluentcommerce/fc-connect-sdk';
|
|
838
|
-
// ✅ CORRECT: Access openKv from Versori context
|
|
839
|
-
// import { openKv } from '@versori/run'; // ❌ WRONG - Not a direct export
|
|
840
|
-
|
|
841
|
-
// In Versori workflow handler:
|
|
842
|
-
const { openKv } = ctx;
|
|
843
|
-
const kvAdapter = new VersoriKVAdapter(openKv(':project:'));
|
|
844
|
-
const tracker = new JobTracker(
|
|
845
|
-
kvAdapter,
|
|
846
|
-
logger,
|
|
847
|
-
604800 // Optional TTL in seconds (default: 7 days)
|
|
848
|
-
);
|
|
849
|
-
|
|
850
|
-
// Create a new job
|
|
851
|
-
await tracker.createJob('scheduled_1729681200000', {
|
|
852
|
-
triggeredBy: 'schedule',
|
|
853
|
-
stage: 'initialization',
|
|
854
|
-
details: { catalogueRef: 'DEFAULT:1' },
|
|
855
|
-
});
|
|
856
|
-
|
|
857
|
-
// Update job status
|
|
858
|
-
await tracker.updateJob('scheduled_1729681200000', {
|
|
859
|
-
status: 'processing',
|
|
860
|
-
stage: 'extraction',
|
|
861
|
-
message: 'Extracting 1000 records',
|
|
862
|
-
details: { recordCount: 1000 },
|
|
863
|
-
});
|
|
864
|
-
|
|
865
|
-
// Mark as completed
|
|
866
|
-
await tracker.markCompleted('scheduled_1729681200000', {
|
|
867
|
-
recordCount: 1000,
|
|
868
|
-
fileName: 'export.xml',
|
|
869
|
-
sftpPath: '/uploads/export.xml',
|
|
870
|
-
});
|
|
871
|
-
|
|
872
|
-
// Mark as failed (in catch block)
|
|
873
|
-
try {
|
|
874
|
-
// ... job logic ...
|
|
875
|
-
} catch (error) {
|
|
876
|
-
await tracker.markFailed('scheduled_1729681200000', error);
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
// Get job status
|
|
880
|
-
const jobStatus = await tracker.getJob('scheduled_1729681200000');
|
|
881
|
-
console.log(jobStatus);
|
|
882
|
-
// {
|
|
883
|
-
// status: 'completed',
|
|
884
|
-
// stage: 'finished',
|
|
885
|
-
// message: 'Job completed successfully',
|
|
886
|
-
// createdAt: '2025-01-24T10:00:00.000Z',
|
|
887
|
-
// startedAt: '2025-01-24T10:00:05.000Z',
|
|
888
|
-
// completedAt: '2025-01-24T10:05:30.000Z',
|
|
889
|
-
// triggeredBy: 'schedule',
|
|
890
|
-
// details: { recordCount: 1000, fileName: 'export.xml', ... }
|
|
891
|
-
// }
|
|
892
|
-
```
|
|
893
|
-
|
|
894
|
-
**Constructor:**
|
|
895
|
-
|
|
896
|
-
```typescript
|
|
897
|
-
new JobTracker(
|
|
898
|
-
kv: KVAdapter, // KV adapter (VersoriKVAdapter or custom)
|
|
899
|
-
logger: StructuredLogger, // Logger instance
|
|
900
|
-
ttl?: number // Optional TTL in seconds (default: 604800 = 7 days)
|
|
901
|
-
)
|
|
902
|
-
```
|
|
903
|
-
|
|
904
|
-
**Methods:**
|
|
905
|
-
|
|
906
|
-
- `createJob(jobId: string, metadata: Partial<Omit<JobStatus, 'status' | 'createdAt'>>): Promise<void>` - Create a new job with 'queued' status
|
|
907
|
-
- `updateJob(jobId: string, update: Partial<JobStatus>): Promise<void>` - Update job status (auto-updates timestamps)
|
|
908
|
-
- `getJob(jobId: string): Promise<JobStatus | undefined>` - Retrieve job status
|
|
909
|
-
- `markCompleted(jobId: string, details?: Record<string, any>): Promise<void>` - Mark job as completed
|
|
910
|
-
- `markFailed(jobId: string, error: Error | string): Promise<void>` - Mark job as failed
|
|
911
|
-
|
|
912
|
-
**JobStatus Interface:**
|
|
913
|
-
|
|
914
|
-
```typescript
|
|
915
|
-
interface JobStatus {
|
|
916
|
-
status: 'queued' | 'processing' | 'completed' | 'failed';
|
|
917
|
-
stage?: string;
|
|
918
|
-
message?: string;
|
|
919
|
-
createdAt?: string;
|
|
920
|
-
startedAt?: string;
|
|
921
|
-
completedAt?: string;
|
|
922
|
-
failedAt?: string;
|
|
923
|
-
triggeredBy: 'schedule' | 'webhook' | 'manual';
|
|
924
|
-
details?: Record<string, any>;
|
|
925
|
-
error?: string;
|
|
926
|
-
errorStack?: string;
|
|
927
|
-
}
|
|
928
|
-
```
|
|
929
|
-
|
|
930
|
-
**Key Features:**
|
|
931
|
-
|
|
932
|
-
- Standardized job status tracking
|
|
933
|
-
- Automatic timestamp management (createdAt, startedAt, completedAt, failedAt)
|
|
934
|
-
- TTL support for automatic cleanup
|
|
935
|
-
- Safe error handling (never throws on KV errors)
|
|
936
|
-
- Detailed logging
|
|
937
|
-
|
|
938
|
-
## PartialBatchRecovery (NEW)
|
|
939
|
-
|
|
940
|
-
Tracks per-record success/failure in batch operations and enables retrying only failed records instead of entire batches. Provides checkpoint/resume functionality and detailed error reporting.
|
|
941
|
-
|
|
942
|
-
```typescript
|
|
943
|
-
import { PartialBatchRecovery } from '@fluentcommerce/fc-connect-sdk';
|
|
944
|
-
|
|
945
|
-
const recovery = new PartialBatchRecovery<InventoryRecord>(logger);
|
|
946
|
-
|
|
947
|
-
// Process batch with automatic recovery and retry
|
|
948
|
-
const result = await recovery.processBatchWithRecovery(
|
|
949
|
-
records,
|
|
950
|
-
async batch =>
|
|
951
|
-
await client.sendBatch(jobId, {
|
|
952
|
-
action: 'UPSERT',
|
|
953
|
-
entityType: 'INVENTORY',
|
|
954
|
-
entities: batch,
|
|
955
|
-
}),
|
|
956
|
-
{
|
|
957
|
-
maxRetries: 3,
|
|
958
|
-
retryOnlyFailed: true,
|
|
959
|
-
retryBatchSize: 100,
|
|
960
|
-
checkpointKey: 'inventory-2025-01-24',
|
|
961
|
-
retryDelayMs: 1000,
|
|
962
|
-
shouldRetry: (error, attemptCount) => {
|
|
963
|
-
// Custom retry logic
|
|
964
|
-
return attemptCount <= 3 && !error.message.includes('permanent');
|
|
965
|
-
},
|
|
966
|
-
}
|
|
967
|
-
);
|
|
968
|
-
|
|
969
|
-
console.log(`Success: ${result.successCount}, Failed: ${result.failedCount}`);
|
|
970
|
-
console.log(`Duration: ${result.durationMs}ms`);
|
|
971
|
-
console.log(result.summary);
|
|
972
|
-
|
|
973
|
-
if (result.failedRecords.length > 0) {
|
|
974
|
-
console.log('Failed records:', result.failedRecords);
|
|
975
|
-
|
|
976
|
-
// Export failed records for manual review
|
|
977
|
-
const failures = result.failedRecords.map(f => ({
|
|
978
|
-
index: f.index,
|
|
979
|
-
record: f.record,
|
|
980
|
-
error: f.error.message,
|
|
981
|
-
attempts: f.attemptCount,
|
|
982
|
-
}));
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
// Resume from checkpoint later
|
|
986
|
-
if (result.checkpointId) {
|
|
987
|
-
const resumeResult = await recovery.resumeFromCheckpoint(
|
|
988
|
-
result.checkpointId,
|
|
989
|
-
async batch =>
|
|
990
|
-
await client.sendBatch(jobId, {
|
|
991
|
-
action: 'UPSERT',
|
|
992
|
-
entityType: 'INVENTORY',
|
|
993
|
-
entities: batch,
|
|
994
|
-
})
|
|
995
|
-
);
|
|
996
|
-
}
|
|
997
|
-
```
|
|
998
|
-
|
|
999
|
-
**Constructor:**
|
|
1000
|
-
|
|
1001
|
-
```typescript
|
|
1002
|
-
new PartialBatchRecovery<T = any>(
|
|
1003
|
-
logger?: StructuredLogger // Optional logger
|
|
1004
|
-
)
|
|
1005
|
-
```
|
|
1006
|
-
|
|
1007
|
-
**Methods:**
|
|
1008
|
-
|
|
1009
|
-
- `processBatchWithRecovery(records: T[], batchProcessor: (records: T[]) => Promise<any>, options?: BatchRecoveryOptions): Promise<BatchRecoveryResult<T>>` - Process batch with automatic recovery and retry
|
|
1010
|
-
- `resumeFromCheckpoint(checkpointId: string, batchProcessor: (records: T[]) => Promise<any>, options?: BatchRecoveryOptions): Promise<BatchRecoveryResult<T>>` - Resume processing from a saved checkpoint
|
|
1011
|
-
- `getCheckpoint(checkpointId: string): BatchCheckpoint<T> | undefined` - Get checkpoint by ID
|
|
1012
|
-
- `listCheckpoints(): BatchCheckpoint<T>[]` - List all checkpoints
|
|
1013
|
-
- `deleteCheckpoint(checkpointId: string): boolean` - Delete a checkpoint
|
|
1014
|
-
- `clearCheckpoints(): void` - Clear all checkpoints
|
|
1015
|
-
|
|
1016
|
-
**BatchRecoveryOptions:**
|
|
1017
|
-
|
|
1018
|
-
```typescript
|
|
1019
|
-
interface BatchRecoveryOptions {
|
|
1020
|
-
maxRetries?: number; // Max retry attempts per record (default: 3)
|
|
1021
|
-
retryOnlyFailed?: boolean; // Retry only failed records (default: true)
|
|
1022
|
-
checkpointKey?: string; // Checkpoint key for resume
|
|
1023
|
-
retryDelayMs?: number; // Delay between retries in ms (default: 1000)
|
|
1024
|
-
shouldRetry?: (error: Error, attemptCount: number) => boolean; // Custom retry predicate
|
|
1025
|
-
retryBatchSize?: number; // Batch size for retries (default: 100)
|
|
1026
|
-
}
|
|
1027
|
-
```
|
|
1028
|
-
|
|
1029
|
-
**BatchRecoveryResult:**
|
|
1030
|
-
|
|
1031
|
-
```typescript
|
|
1032
|
-
interface BatchRecoveryResult<T = any> {
|
|
1033
|
-
totalRecords: number; // Total records processed
|
|
1034
|
-
successCount: number; // Successfully processed records
|
|
1035
|
-
failedCount: number; // Failed records after all retries
|
|
1036
|
-
failedRecords: RecordFailure<T>[]; // Details of failed records
|
|
1037
|
-
checkpointId?: string; // Checkpoint ID for resuming
|
|
1038
|
-
summary: string; // Processing summary
|
|
1039
|
-
durationMs: number; // Total time taken
|
|
1040
|
-
}
|
|
1041
|
-
|
|
1042
|
-
interface RecordFailure<T = any> {
|
|
1043
|
-
index: number; // Original record index
|
|
1044
|
-
record: T; // The record that failed
|
|
1045
|
-
error: Error; // Error that occurred
|
|
1046
|
-
attemptCount: number; // Number of retry attempts
|
|
1047
|
-
timestamp: string; // Timestamp of failure
|
|
1048
|
-
}
|
|
1049
|
-
```
|
|
1050
|
-
|
|
1051
|
-
**Key Features:**
|
|
1052
|
-
|
|
1053
|
-
- Per-record retry logic (avoids reprocessing successful records)
|
|
1054
|
-
- Exponential backoff for retries
|
|
1055
|
-
- Checkpoint/resume functionality
|
|
1056
|
-
- Detailed failure tracking with attempt counts
|
|
1057
|
-
- Custom retry predicates
|
|
1058
|
-
- Configurable batch sizes for retries
|
|
1059
|
-
|
|
1060
|
-
## PreflightValidator (NEW)
|
|
1061
|
-
|
|
1062
|
-
Validates data BEFORE sending to Fluent API to catch errors early, save API quota, validate against GraphQL schema requirements, and check for data quality issues.
|
|
1063
|
-
|
|
1064
|
-
```typescript
|
|
1065
|
-
import { PreflightValidator } from '@fluentcommerce/fc-connect-sdk';
|
|
1066
|
-
|
|
1067
|
-
const validator = new PreflightValidator(logger);
|
|
1068
|
-
|
|
1069
|
-
// Validate batch before sending
|
|
1070
|
-
const result = await validator.validateBatch(records, {
|
|
1071
|
-
entityType: 'INVENTORY',
|
|
1072
|
-
requiredFields: ['ref', 'qty'],
|
|
1073
|
-
checkDuplicates: true,
|
|
1074
|
-
maxBatchSize: 1000,
|
|
1075
|
-
validateTypes: true,
|
|
1076
|
-
customValidator: (record, index) => {
|
|
1077
|
-
if (record.qty < 0) {
|
|
1078
|
-
return `Record ${index}: qty must be non-negative`;
|
|
1079
|
-
}
|
|
1080
|
-
return null;
|
|
1081
|
-
},
|
|
1082
|
-
});
|
|
1083
|
-
|
|
1084
|
-
if (!result.isValid) {
|
|
1085
|
-
console.error(`Validation failed: ${result.errors.length} errors`);
|
|
1086
|
-
result.errors.forEach(err => {
|
|
1087
|
-
console.error(`[Record ${err.index}] ${err.field}: ${err.message}`);
|
|
1088
|
-
});
|
|
1089
|
-
throw new Error('Batch validation failed');
|
|
1090
|
-
}
|
|
1091
|
-
|
|
1092
|
-
console.log(result.summary);
|
|
1093
|
-
// "Validation passed: 1000/1000 records valid"
|
|
1094
|
-
|
|
1095
|
-
// Quick validation - returns first error only (faster)
|
|
1096
|
-
const quickResult = await validator.validateQuick(records, {
|
|
1097
|
-
entityType: 'INVENTORY',
|
|
1098
|
-
requiredFields: ['ref', 'qty'],
|
|
1099
|
-
maxBatchSize: 1000,
|
|
1100
|
-
});
|
|
1101
|
-
|
|
1102
|
-
if (!quickResult.isValid) {
|
|
1103
|
-
console.error(`Quick validation failed: ${quickResult.error}`);
|
|
1104
|
-
}
|
|
1105
|
-
```
|
|
1106
|
-
|
|
1107
|
-
**Constructor:**
|
|
1108
|
-
|
|
1109
|
-
```typescript
|
|
1110
|
-
new PreflightValidator(
|
|
1111
|
-
logger?: StructuredLogger // Optional logger
|
|
1112
|
-
)
|
|
1113
|
-
```
|
|
1114
|
-
|
|
1115
|
-
**Methods:**
|
|
1116
|
-
|
|
1117
|
-
- `validateBatch(records: any[], options: PreflightValidationOptions): Promise<PreflightValidationResult>` - Validate entire batch with detailed error reporting
|
|
1118
|
-
- `validateQuick(records: any[], options: PreflightValidationOptions): Promise<{ isValid: boolean; error?: string }>` - Quick validation that returns first error only (faster for fail-fast scenarios)
|
|
1119
|
-
|
|
1120
|
-
**PreflightValidationOptions:**
|
|
1121
|
-
|
|
1122
|
-
```typescript
|
|
1123
|
-
interface PreflightValidationOptions {
|
|
1124
|
-
entityType: 'INVENTORY' | 'CONTROL' | 'LOCATION' | 'ARTICLE' | string;
|
|
1125
|
-
requiredFields?: string[]; // Fields that must be present
|
|
1126
|
-
checkDuplicates?: boolean; // Check for duplicate refs
|
|
1127
|
-
maxBatchSize?: number; // Maximum batch size
|
|
1128
|
-
validateTypes?: boolean; // Validate field types
|
|
1129
|
-
customValidator?: (record: any, index: number) => string | null; // Custom validation function
|
|
1130
|
-
}
|
|
1131
|
-
```
|
|
1132
|
-
|
|
1133
|
-
**PreflightValidationResult:**
|
|
1134
|
-
|
|
1135
|
-
```typescript
|
|
1136
|
-
interface PreflightValidationResult {
|
|
1137
|
-
isValid: boolean; // Whether validation passed
|
|
1138
|
-
totalRecords: number; // Total records validated
|
|
1139
|
-
validRecords: number; // Number of valid records
|
|
1140
|
-
errors: PreflightValidationError[]; // All validation errors
|
|
1141
|
-
warnings: PreflightValidationError[]; // All validation warnings
|
|
1142
|
-
summary: string; // Summary message
|
|
1143
|
-
duplicates?: string[]; // Duplicate refs found (if checkDuplicates enabled)
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
|
-
interface PreflightValidationError {
|
|
1147
|
-
index: number; // Record index where error occurred
|
|
1148
|
-
field?: string; // Field that failed validation
|
|
1149
|
-
message: string; // Error message
|
|
1150
|
-
severity: 'error' | 'warning'; // Error severity
|
|
1151
|
-
value?: any; // The problematic value
|
|
1152
|
-
}
|
|
1153
|
-
```
|
|
1154
|
-
|
|
1155
|
-
**Key Features:**
|
|
1156
|
-
|
|
1157
|
-
- Catches errors early (saves API quota)
|
|
1158
|
-
- Validates against GraphQL schema requirements
|
|
1159
|
-
- Checks for duplicates and data quality issues
|
|
1160
|
-
- Provides actionable error messages
|
|
1161
|
-
- Type validation for common fields (qty, ref, etc.)
|
|
1162
|
-
- Custom validation functions
|
|
1163
|
-
- Fast "quick validation" mode for fail-fast scenarios
|
|
1164
|
-
|
|
1165
|
-
## Versori Adapters (NEW)
|
|
1166
|
-
|
|
1167
|
-
Helpers for Versori platform integrations.
|
|
1168
|
-
|
|
1169
|
-
```typescript
|
|
1170
|
-
import { VersoriKVAdapter, VersoriFileTracker } from '@fluentcommerce/fc-connect-sdk';
|
|
1171
|
-
// ✅ CORRECT: Access openKv from Versori context
|
|
1172
|
-
// import { openKv } from '@versori/run'; // ❌ WRONG - Not a direct export
|
|
1173
|
-
|
|
1174
|
-
// In Versori workflow handler:
|
|
1175
|
-
const { openKv } = ctx;
|
|
1176
|
-
const kv = new VersoriKVAdapter(openKv(':project:'));
|
|
1177
|
-
await kv.set('processed:file1', { at: Date.now() }, 3600);
|
|
1178
|
-
|
|
1179
|
-
// File tracker to dedupe processed files
|
|
1180
|
-
const fileTracker = new VersoriFileTracker(openKv(':project:'), 'my-workflow');
|
|
1181
|
-
const already = await fileTracker.wasFileProcessed('incoming/products_20250101.xml');
|
|
1182
|
-
if (!already) {
|
|
1183
|
-
await fileTracker.markFileProcessed('incoming/products_20250101.xml', { size: 2048 });
|
|
1184
|
-
}
|
|
1185
|
-
```
|
|
1186
|
-
|
|
1187
|
-
## See Also
|
|
1188
|
-
|
|
1189
|
-
- [Ingestion Guide](../../ingestion/ingestion-readme.md) - Complete ingestion workflows
|
|
1190
|
-
- [Extraction Guide](../../extraction/extraction-readme.md) - Data extraction patterns
|
|
1191
|
-
- [Module 3: Authentication](./api-reference-03-authentication.md) - OAuth2 and auth providers
|
|
1192
|
-
- [Module 6: Data Sources](./api-reference-06-data-sources.md) - S3 and SFTP data sources
|
|
1193
|
-
- [Module 9: Error Handling](./api-reference-09-error-handling.md) - Error types and handling
|
|
1194
|
-
- [Module 8: Types](./api-reference-08-types.md) - Service-related types
|
|
1195
|
-
|
|
1196
|
-
---
|
|
1197
|
-
|
|
1198
|
-
**[← Previous: GraphQL Mapping](./api-reference-04-graphql-mapping.md)** | **[API Reference Home](../api-reference-readme.md)** | **[Next: Data Sources →](./api-reference-06-data-sources.md)**
|
|
1
|
+
# Module 5: Services
|
|
2
|
+
|
|
3
|
+
**Level:** Intermediate
|
|
4
|
+
**Category:** Core Services
|
|
5
|
+
|
|
6
|
+
## Overview
|
|
7
|
+
|
|
8
|
+
The SDK provides a comprehensive set of services for data transformation, state management, extraction, webhook validation, logging, and S3 operations.
|
|
9
|
+
|
|
10
|
+
## Table of Contents
|
|
11
|
+
|
|
12
|
+
- [Universal Mapper](#universal-mapper)
|
|
13
|
+
- [FluentBatchManager](#fluentbatchmanager)
|
|
14
|
+
- [State Management Service](#state-management-service)
|
|
15
|
+
- [Webhook Validation Service](#webhook-validation-service)
|
|
16
|
+
- [Logging Utilities](#logging-utilities)
|
|
17
|
+
- [S3Service](#s3service)
|
|
18
|
+
- [S3PresignService](#s3presignservice)
|
|
19
|
+
- [JobTracker](#jobtracker-new)
|
|
20
|
+
- [PartialBatchRecovery](#partialbatchrecovery-new)
|
|
21
|
+
- [PreflightValidator](#preflightvalidator-new)
|
|
22
|
+
- [Versori Adapters](#versori-adapters-new)
|
|
23
|
+
- [See Also](#see-also)
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Universal Mapper
|
|
28
|
+
|
|
29
|
+
Map and transform data between schemas using the universal `fields` configuration.
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { UniversalMapper } from '@fluentcommerce/fc-connect-sdk';
|
|
33
|
+
|
|
34
|
+
const mapper = new UniversalMapper({
|
|
35
|
+
version: '1.0',
|
|
36
|
+
description: 'Inventory ingestion',
|
|
37
|
+
fields: {
|
|
38
|
+
skuRef: { source: 'SKU', required: true },
|
|
39
|
+
locationRef: { source: 'LOCATION', required: true },
|
|
40
|
+
qty: { source: 'QTY', resolver: 'sdk.parseInt', required: true },
|
|
41
|
+
status: { source: 'status', resolver: 'sdk.uppercase' },
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const result = await mapper.map(record);
|
|
46
|
+
if (result.success) {
|
|
47
|
+
console.log(result.data);
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## FluentBatchManager
|
|
52
|
+
|
|
53
|
+
Thin orchestration layer for Fluent Commerce job and batch operations. Wraps FluentClient with stateless methods for job creation, batch submission, and status polling.
|
|
54
|
+
|
|
55
|
+
**Export Name:**
|
|
56
|
+
- Use: `import { FluentBatchManager } from '@fluentcommerce/fc-connect-sdk';`
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { FluentBatchManager } from '@fluentcommerce/fc-connect-sdk';
|
|
60
|
+
|
|
61
|
+
const manager = new FluentBatchManager(fluentVersoriClient, logger);
|
|
62
|
+
|
|
63
|
+
// Create a new job
|
|
64
|
+
const job = await manager.createJob('Daily Inventory Sync', {
|
|
65
|
+
retailerId: '1',
|
|
66
|
+
description: 'Daily inventory synchronization',
|
|
67
|
+
maxBatchCount: 100,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Submit a batch to the job
|
|
71
|
+
await manager.submitBatch(job.id, {
|
|
72
|
+
action: 'UPSERT',
|
|
73
|
+
entityType: 'INVENTORY',
|
|
74
|
+
entities: inventoryRecords,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Check job status
|
|
78
|
+
const jobStatus = await manager.getJobStatus(job.id);
|
|
79
|
+
console.log(`Job status: ${jobStatus.status}`);
|
|
80
|
+
|
|
81
|
+
// Check specific batch status
|
|
82
|
+
const batchStatus = await manager.getBatchStatus(job.id, batch.id);
|
|
83
|
+
console.log(`Batch status: ${batchStatus.status}`);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Constructor:**
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
new FluentBatchManager(
|
|
90
|
+
client: FluentVersoriClient, // Fluent Versori client
|
|
91
|
+
logger?: Logger // Optional logger
|
|
92
|
+
)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Methods:**
|
|
96
|
+
|
|
97
|
+
- `createJob(name: string, metadata?: any): Promise<FluentJobResponse>` - Create a new job with optional metadata
|
|
98
|
+
- `submitBatch(jobId: string, batch: FluentBatchPayload): Promise<void>` - Submit a batch to a job
|
|
99
|
+
- `getJobStatus(jobId: string): Promise<any>` - Get current job status
|
|
100
|
+
- `getBatchStatus(jobId: string, batchId: string): Promise<any>` - Get specific batch status
|
|
101
|
+
|
|
102
|
+
**Use Cases:**
|
|
103
|
+
|
|
104
|
+
- Simple ingestion flows (createJob → sendBatch → pollStatus)
|
|
105
|
+
- Direct job management without state tracking
|
|
106
|
+
- Thin wrapper around FluentVersoriClient batch operations
|
|
107
|
+
|
|
108
|
+
**Note:** For complex workflows with state management, compose SDK services manually with StateService.
|
|
109
|
+
|
|
110
|
+
## State Management Service
|
|
111
|
+
|
|
112
|
+
Track processing state and prevent duplicate operations.
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
import { StateService, VersoriKVAdapter } from '@fluentcommerce/fc-connect-sdk';
|
|
116
|
+
// ✅ CORRECT: Access openKv from Versori context
|
|
117
|
+
// import { openKv } from '@versori/run'; // ❌ WRONG - Not a direct export
|
|
118
|
+
|
|
119
|
+
const logger = console;
|
|
120
|
+
const stateService = new StateService(logger);
|
|
121
|
+
// In Versori workflow handler:
|
|
122
|
+
const { openKv } = ctx;
|
|
123
|
+
const kv = new VersoriKVAdapter(openKv(':project:')); // Implements KVStore
|
|
124
|
+
|
|
125
|
+
// Acquire/release distributed lock
|
|
126
|
+
const acquired = await stateService.acquireLock('critical-section', kv, 15);
|
|
127
|
+
try {
|
|
128
|
+
if (acquired) {
|
|
129
|
+
// Critical section
|
|
130
|
+
}
|
|
131
|
+
} finally {
|
|
132
|
+
await stateService.releaseLock('critical-section', kv);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Check if a file was processed (deduplication)
|
|
136
|
+
const isProcessed = await stateService.isFileProcessed(kv as any, 'file.csv', 'inventory-sync');
|
|
137
|
+
|
|
138
|
+
// Update sync state after processing
|
|
139
|
+
await stateService.updateSyncState(
|
|
140
|
+
kv as any,
|
|
141
|
+
[
|
|
142
|
+
{
|
|
143
|
+
fileName: 'file.csv',
|
|
144
|
+
lastModified: new Date().toISOString(),
|
|
145
|
+
recordCount: 1000,
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
'inventory-sync'
|
|
149
|
+
);
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Methods:**
|
|
153
|
+
|
|
154
|
+
- `acquireLock(lockName: string, kv: KVStore, timeoutMinutes?: number): Promise<boolean>`
|
|
155
|
+
- `releaseLock(lockName: string, kv: KVStore): Promise<void>`
|
|
156
|
+
- `getSyncState(kv: KVStore, workflowId?: string): Promise<SyncState>`
|
|
157
|
+
- `updateSyncState(kv: KVStore, processedFiles: ProcessedFile[], workflowId?: string): Promise<void>`
|
|
158
|
+
- `getDailyJob(kv: KVStore, workflowId: string): Promise<DailyJob | null>`
|
|
159
|
+
- `setDailyJob(kv: KVStore, workflowId: string, jobId: string, expirationHours?: number): Promise<void>`
|
|
160
|
+
- `isFileProcessed(kv: KVStore, fileName: string, workflowId?: string): Promise<boolean>`
|
|
161
|
+
- `getMetrics(kv: KVStore, workflowId?: string): Promise<{ totalFiles: number; totalRecords: number; lastProcessedTime?: string; }>`
|
|
162
|
+
- `clearState(kv: KVStore, workflowId: string): Promise<void>`
|
|
163
|
+
|
|
164
|
+
## Webhook Validation Service
|
|
165
|
+
|
|
166
|
+
> **Warning: IMPORTANT WEBHOOK VALIDATION NOTICE**
|
|
167
|
+
>
|
|
168
|
+
> This service is **ONLY** for validating webhooks sent from **Fluent Commerce Rubix workflows**.
|
|
169
|
+
>
|
|
170
|
+
> These methods will **NOT** work with:
|
|
171
|
+
>
|
|
172
|
+
> - Generic HTTP webhooks
|
|
173
|
+
> - Third-party service webhooks
|
|
174
|
+
> - Custom webhook implementations
|
|
175
|
+
>
|
|
176
|
+
> Only use this service if you are receiving webhooks directly from Fluent Commerce Rubix workflow executions.
|
|
177
|
+
|
|
178
|
+
The service uses RSA public key cryptography for signature validation.
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
import {
|
|
182
|
+
WebhookValidationService,
|
|
183
|
+
WebhookValidationFactory,
|
|
184
|
+
SignatureAlgorithm,
|
|
185
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
186
|
+
|
|
187
|
+
// IMPORTANT: This validation is ONLY for webhooks from Fluent Commerce Rubix workflows
|
|
188
|
+
const validator = new WebhookValidationService(
|
|
189
|
+
{
|
|
190
|
+
algorithm: SignatureAlgorithm.SHA512_WITH_RSA, // Recommended
|
|
191
|
+
strictValidation: true, // Enforce strict checks
|
|
192
|
+
maxTimestampAge: 300000, // 5 minutes (optional)
|
|
193
|
+
},
|
|
194
|
+
logger
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
// Get public key from environment or connector variables
|
|
198
|
+
const publicKey = process.env.FLUENT_WEBHOOK_PUBLIC_KEY;
|
|
199
|
+
|
|
200
|
+
// Validate Fluent Commerce Rubix webhook signature
|
|
201
|
+
const result = await validator.validateWebhookSignature(
|
|
202
|
+
JSON.stringify(requestBody),
|
|
203
|
+
headers['fluent-signature'],
|
|
204
|
+
publicKey,
|
|
205
|
+
SignatureAlgorithm.SHA512_WITH_RSA
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
if (!result.isValid) {
|
|
209
|
+
throw new Error(`Validation failed: ${result.error}`);
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**Configuration Options:**
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
interface WebhookValidationConfig {
|
|
217
|
+
algorithm?: SignatureAlgorithm; // Default: SHA512_WITH_RSA
|
|
218
|
+
strictValidation?: boolean; // Default: true
|
|
219
|
+
maxTimestampAge?: number; // Max age in ms (optional)
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**SignatureAlgorithm Enum:**
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
enum SignatureAlgorithm {
|
|
227
|
+
SHA512_WITH_RSA = 'SHA512withRSA',
|
|
228
|
+
MD5_WITH_RSA = 'MD5withRSA',
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
**Methods:**
|
|
233
|
+
|
|
234
|
+
- `validateWebhookSignature(payload: string, signature: string, publicKey: string, algorithm?: SignatureAlgorithm): Promise<ValidationResult>` - Validate Fluent Rubix webhook signature
|
|
235
|
+
- `validateSignature(payload: string, signature: string, publicKey: string): Promise<boolean>` - Simple signature validation
|
|
236
|
+
- `verifyTimestamp(timestamp: string | number): boolean` - Verify timestamp freshness
|
|
237
|
+
|
|
238
|
+
**ValidationResult Interface:**
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
interface ValidationResult {
|
|
242
|
+
isValid: boolean;
|
|
243
|
+
error?: string;
|
|
244
|
+
details?: {
|
|
245
|
+
signatureValid?: boolean;
|
|
246
|
+
timestampValid?: boolean;
|
|
247
|
+
algorithm?: string;
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
**Factory Pattern:**
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
// Use factory for creating validators with different configs
|
|
256
|
+
const factory = new WebhookValidationFactory();
|
|
257
|
+
|
|
258
|
+
// Create validator for SHA512 (for Fluent Commerce Rubix workflows)
|
|
259
|
+
const sha512Validator = factory.createValidator(SignatureAlgorithm.SHA512_WITH_RSA, logger);
|
|
260
|
+
|
|
261
|
+
// Create validator for MD5 (for Flex workflows)
|
|
262
|
+
const md5Validator = factory.createValidator(SignatureAlgorithm.MD5_WITH_RSA, logger);
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**Public Key Format:**
|
|
266
|
+
The public key should be in PEM format. You can obtain this from:
|
|
267
|
+
|
|
268
|
+
- Fluent Commerce support team
|
|
269
|
+
- Your Fluent Commerce dashboard (if available)
|
|
270
|
+
- Different retailers may have different public keys
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
# Example public key format in .env file
|
|
274
|
+
FLUENT_WEBHOOK_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
|
|
275
|
+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
|
|
276
|
+
...
|
|
277
|
+
-----END PUBLIC KEY-----"
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
**Complete Example:**
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
import {
|
|
284
|
+
WebhookValidationService,
|
|
285
|
+
SignatureAlgorithm,
|
|
286
|
+
parseWebhookRequest,
|
|
287
|
+
validateFluentEvent,
|
|
288
|
+
createConsoleLogger,
|
|
289
|
+
toStructuredLogger,
|
|
290
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
291
|
+
|
|
292
|
+
const logger = toStructuredLogger(createConsoleLogger(), {
|
|
293
|
+
logLevel: 'info',
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// IMPORTANT: This validation is ONLY for webhooks from Fluent Commerce Rubix workflows
|
|
297
|
+
const validator = new WebhookValidationService(
|
|
298
|
+
{
|
|
299
|
+
algorithm: SignatureAlgorithm.SHA512_WITH_RSA,
|
|
300
|
+
strictValidation: true,
|
|
301
|
+
},
|
|
302
|
+
logger
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
export async function handleFluentWebhook(req, res) {
|
|
306
|
+
try {
|
|
307
|
+
// Extract signature from headers
|
|
308
|
+
const signature = req.headers['fluent-signature'] || req.headers['x-fluent-signature'];
|
|
309
|
+
|
|
310
|
+
if (!signature) {
|
|
311
|
+
return res.status(401).json({ error: 'Missing signature' });
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Get public key from environment
|
|
315
|
+
const publicKey = process.env.FLUENT_WEBHOOK_PUBLIC_KEY;
|
|
316
|
+
|
|
317
|
+
// Validate Fluent Commerce Rubix webhook
|
|
318
|
+
const validationResult = await validator.validateWebhookSignature(
|
|
319
|
+
JSON.stringify(req.body),
|
|
320
|
+
signature,
|
|
321
|
+
publicKey,
|
|
322
|
+
SignatureAlgorithm.SHA512_WITH_RSA
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
if (!validationResult.isValid) {
|
|
326
|
+
logger.error('Fluent Commerce Rubix webhook validation failed', validationResult);
|
|
327
|
+
return res.status(401).json({
|
|
328
|
+
error: 'Invalid Fluent Rubix webhook signature',
|
|
329
|
+
details: validationResult.error,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Parse and validate payload
|
|
334
|
+
const payload = parseWebhookRequest(req.body, logger, 'webhook');
|
|
335
|
+
const validation = validateFluentEvent(payload, logger, 'webhook');
|
|
336
|
+
|
|
337
|
+
if (!validation.isValid) {
|
|
338
|
+
return res.status(400).json({
|
|
339
|
+
error: 'Invalid payload',
|
|
340
|
+
warnings: validation.warnings,
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Process webhook
|
|
345
|
+
await processWebhookPayload(payload);
|
|
346
|
+
|
|
347
|
+
return res.status(200).json({ success: true });
|
|
348
|
+
} catch (error) {
|
|
349
|
+
logger.error('Webhook processing failed', { error: error.message });
|
|
350
|
+
return res.status(500).json({ error: 'Internal server error' });
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
## Logging Utilities
|
|
356
|
+
|
|
357
|
+
The SDK provides minimal logging utilities for standalone environments. **Versori users should always use native platform logs.**
|
|
358
|
+
|
|
359
|
+
**Historical Note:** The `LoggingService` class (684 lines) was refactored to function-based utilities (~390 lines) for better composability.
|
|
360
|
+
|
|
361
|
+
### Platform-Specific Guidance
|
|
362
|
+
|
|
363
|
+
| Runtime | Use | Example |
|
|
364
|
+
|---------|-----|---------|
|
|
365
|
+
| **Versori Platform** | Native `log` from context | `const { log } = ctx;` |
|
|
366
|
+
| **Standalone Node.js** | Function-based utilities | `createConsoleLogger()` + `toStructuredLogger()` |
|
|
367
|
+
| **Standalone Deno** | Function-based utilities | Same as Node.js |
|
|
368
|
+
| **Other Platforms** | Implement `Logger` interface | Custom adapter |
|
|
369
|
+
|
|
370
|
+
**✅ Versori Platform (Recommended - Use Native Log):**
|
|
371
|
+
|
|
372
|
+
Always use native `log` from context - don't wrap it:
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
import { schedule, http } from '@versori/run';
|
|
376
|
+
import { createClient, SftpDataSource } from '@fluentcommerce/fc-connect-sdk';
|
|
377
|
+
|
|
378
|
+
export const myWorkflow = schedule('my-workflow', '0 * * * *')
|
|
379
|
+
.then(http('process', { connection: 'fluent' }, async (ctx) => {
|
|
380
|
+
const { log } = ctx; // Only extract what you need
|
|
381
|
+
|
|
382
|
+
// ✅ Pass native log directly to SDK services
|
|
383
|
+
const client = await createClient(ctx); // Auto-detects Versori context
|
|
384
|
+
const sftp = new SftpDataSource(config, log);
|
|
385
|
+
|
|
386
|
+
log.info('Processing started');
|
|
387
|
+
log.error('Failed to process', { error: err.message });
|
|
388
|
+
}));
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
**✅ Standalone Node.js/Deno (Use Minimal Utilities):**
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
import {
|
|
395
|
+
FluentClient,
|
|
396
|
+
createConsoleLogger,
|
|
397
|
+
toStructuredLogger,
|
|
398
|
+
SftpDataSource,
|
|
399
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
400
|
+
|
|
401
|
+
// Simple console logger
|
|
402
|
+
const logger = createConsoleLogger();
|
|
403
|
+
|
|
404
|
+
// Or with context
|
|
405
|
+
const contextLogger = toStructuredLogger(logger, {
|
|
406
|
+
service: 'InventorySync',
|
|
407
|
+
logLevel: 'debug',
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
const client = await createClient({
|
|
411
|
+
config: config,
|
|
412
|
+
logger: contextLogger,
|
|
413
|
+
});
|
|
414
|
+
const sftp = new SftpDataSource(config, contextLogger);
|
|
415
|
+
|
|
416
|
+
contextLogger.info('Processing started', { file: 'data.csv' });
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### Core Functions
|
|
420
|
+
|
|
421
|
+
**Logger Creation:**
|
|
422
|
+
|
|
423
|
+
- `createConsoleLogger(): Logger` - Create basic console logger for standalone use
|
|
424
|
+
- `toStructuredLogger(logger: Logger, context: LogContext): Logger` - Add context metadata to logger
|
|
425
|
+
- `generateCorrelationId(): string` - Generate unique correlation ID for request tracking
|
|
426
|
+
|
|
427
|
+
**Convenience Functions:**
|
|
428
|
+
|
|
429
|
+
- `createWorkflowLogger(baseLogger: Logger, workflowName: string, debugMode?: boolean): Logger` - Pre-configured workflow logger with correlationId
|
|
430
|
+
- `createServiceLogger(baseLogger: Logger, serviceName: string, debugMode?: boolean): Logger` - Pre-configured service logger
|
|
431
|
+
- `createErrorLogger(baseLogger: Logger, error: Error, context?: Record<string, any>): Logger` - Error-specific logger with context
|
|
432
|
+
|
|
433
|
+
### Logger Interface
|
|
434
|
+
|
|
435
|
+
All logging utilities implement the `Logger` interface:
|
|
436
|
+
|
|
437
|
+
```typescript
|
|
438
|
+
interface Logger {
|
|
439
|
+
debug(message: string, meta?: Record<string, any>): void;
|
|
440
|
+
info(message: string, meta?: Record<string, any>): void;
|
|
441
|
+
warn(message: string, meta?: Record<string, any>): void;
|
|
442
|
+
error(message: string, err?: Error | unknown, meta?: Record<string, any>): void;
|
|
443
|
+
}
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### Quick Decision Guide
|
|
447
|
+
|
|
448
|
+
| Runtime | Use | Reason |
|
|
449
|
+
| ---------------------- | ------------------------------------------------ | ------------------------------ |
|
|
450
|
+
| **Versori Platform** | Native `log` from context | Platform provides logging |
|
|
451
|
+
| **Standalone Node.js** | `createConsoleLogger()` + `toStructuredLogger()` | Lightweight structured logging |
|
|
452
|
+
| **Standalone Deno** | `createConsoleLogger()` + `toStructuredLogger()` | Lightweight structured logging |
|
|
453
|
+
| **Vercel/CF/Other** | Implement `Logger` interface | Easy extensibility |
|
|
454
|
+
|
|
455
|
+
### Migration from LoggingService
|
|
456
|
+
|
|
457
|
+
If you have existing code using `LoggingService`, update it as follows:
|
|
458
|
+
|
|
459
|
+
```typescript
|
|
460
|
+
// ❌ OLD (LoggingService class - removed)
|
|
461
|
+
import { LoggingService } from '@fluentcommerce/fc-connect-sdk';
|
|
462
|
+
const logger = new LoggingService({ service: 'MyService', debug: true });
|
|
463
|
+
|
|
464
|
+
// ✅ NEW (function-based utilities)
|
|
465
|
+
import {
|
|
466
|
+
createConsoleLogger,
|
|
467
|
+
createServiceLogger,
|
|
468
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
469
|
+
const logger = createServiceLogger(createConsoleLogger(), 'MyService', true);
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
**See:** [Logging Guide](../../logging-guide.md) for complete documentation
|
|
473
|
+
|
|
474
|
+
## S3Service
|
|
475
|
+
|
|
476
|
+
High-performance S3 service for file operations with support for both SDK and presigned URL approaches.
|
|
477
|
+
|
|
478
|
+
### Constructor
|
|
479
|
+
|
|
480
|
+
```typescript
|
|
481
|
+
import { S3Service } from '@fluentcommerce/fc-connect-sdk';
|
|
482
|
+
|
|
483
|
+
// Default - uses AWS SDK
|
|
484
|
+
const s3Service = new S3Service({
|
|
485
|
+
accessKeyId: 'your-access-key',
|
|
486
|
+
secretAccessKey: 'your-secret-key',
|
|
487
|
+
region: 'us-east-1',
|
|
488
|
+
sessionToken: 'optional-session-token', // Optional
|
|
489
|
+
bucket: 'my-bucket', // Optional default bucket
|
|
490
|
+
endpoint: 'https://s3.amazonaws.com', // Optional custom endpoint
|
|
491
|
+
forcePathStyle: false, // Optional path style
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
// Explicit presigned (for Versori with ctx.fetch)
|
|
495
|
+
const s3ServicePresigned = new S3Service({
|
|
496
|
+
accessKeyId: 'your-access-key',
|
|
497
|
+
secretAccessKey: 'your-secret-key',
|
|
498
|
+
region: 'us-east-1',
|
|
499
|
+
usePresigned: true, // Enable presigned URL mode
|
|
500
|
+
fetch: ctx.fetch, // Required when usePresigned is true
|
|
501
|
+
});
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
### listObjects Method
|
|
505
|
+
|
|
506
|
+
List objects in an S3 bucket.
|
|
507
|
+
|
|
508
|
+
```typescript
|
|
509
|
+
const objects = await s3Service.listObjects(
|
|
510
|
+
bucket: string,
|
|
511
|
+
options?: ListOptions
|
|
512
|
+
): Promise<S3Object[]>
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
**ListOptions:**
|
|
516
|
+
|
|
517
|
+
```typescript
|
|
518
|
+
interface ListOptions {
|
|
519
|
+
prefix?: string; // Filter by prefix
|
|
520
|
+
delimiter?: string; // Delimiter for grouping
|
|
521
|
+
maxKeys?: number; // Maximum objects to return
|
|
522
|
+
continuationToken?: string; // Pagination token
|
|
523
|
+
startAfter?: string; // Start after specific key
|
|
524
|
+
}
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
**Example:**
|
|
528
|
+
|
|
529
|
+
```typescript
|
|
530
|
+
const objects = await s3Service.listObjects('my-bucket', {
|
|
531
|
+
prefix: 'inventory/2024/',
|
|
532
|
+
maxKeys: 1000,
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
objects.forEach(obj => {
|
|
536
|
+
console.log(`${obj.key}: ${obj.size} bytes, modified ${obj.lastModified}`);
|
|
537
|
+
});
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
### getObject Method
|
|
541
|
+
|
|
542
|
+
Retrieve an object from S3.
|
|
543
|
+
|
|
544
|
+
```typescript
|
|
545
|
+
const data = await s3Service.getObject(
|
|
546
|
+
bucket: string,
|
|
547
|
+
key: string,
|
|
548
|
+
options?: GetOptions
|
|
549
|
+
): Promise<Buffer | string>
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
**Example:**
|
|
553
|
+
|
|
554
|
+
```typescript
|
|
555
|
+
const csvData = await s3Service.getObject('my-bucket', 'inventory/data.csv');
|
|
556
|
+
const content = csvData.toString('utf-8');
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
### putObject Method
|
|
560
|
+
|
|
561
|
+
Upload an object to S3.
|
|
562
|
+
|
|
563
|
+
```typescript
|
|
564
|
+
await s3Service.putObject(
|
|
565
|
+
bucket: string,
|
|
566
|
+
key: string,
|
|
567
|
+
body: Buffer | string | Uint8Array,
|
|
568
|
+
options?: PutOptions
|
|
569
|
+
): Promise<void>
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
**PutOptions:**
|
|
573
|
+
|
|
574
|
+
```typescript
|
|
575
|
+
interface PutOptions {
|
|
576
|
+
contentType?: string;
|
|
577
|
+
contentEncoding?: string;
|
|
578
|
+
metadata?: Record<string, string>;
|
|
579
|
+
storageClass?: 'STANDARD' | 'REDUCED_REDUNDANCY' | 'GLACIER' | 'DEEP_ARCHIVE';
|
|
580
|
+
serverSideEncryption?: string;
|
|
581
|
+
tagging?: string;
|
|
582
|
+
}
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
**Example:**
|
|
586
|
+
|
|
587
|
+
```typescript
|
|
588
|
+
await s3Service.putObject('my-bucket', 'results/output.json', JSON.stringify(data), {
|
|
589
|
+
contentType: 'application/json',
|
|
590
|
+
metadata: {
|
|
591
|
+
'processed-date': new Date().toISOString(),
|
|
592
|
+
'record-count': '1000',
|
|
593
|
+
},
|
|
594
|
+
});
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
### deleteObject Method
|
|
598
|
+
|
|
599
|
+
Delete an object from S3.
|
|
600
|
+
|
|
601
|
+
```typescript
|
|
602
|
+
await s3Service.deleteObject(
|
|
603
|
+
bucket: string,
|
|
604
|
+
key: string
|
|
605
|
+
): Promise<void>
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
### copyObject Method
|
|
609
|
+
|
|
610
|
+
Copy an object within S3.
|
|
611
|
+
|
|
612
|
+
```typescript
|
|
613
|
+
await s3Service.copyObject(
|
|
614
|
+
sourceBucket: string,
|
|
615
|
+
sourceKey: string,
|
|
616
|
+
destBucket: string,
|
|
617
|
+
destKey: string,
|
|
618
|
+
options?: CopyOptions
|
|
619
|
+
): Promise<void>
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
**Example:**
|
|
623
|
+
|
|
624
|
+
```typescript
|
|
625
|
+
// Archive processed file
|
|
626
|
+
await s3Service.copyObject(
|
|
627
|
+
'inventory-incoming',
|
|
628
|
+
'data/file.csv',
|
|
629
|
+
'inventory-archive',
|
|
630
|
+
`processed/${new Date().toISOString()}/file.csv`
|
|
631
|
+
);
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
### headObject Method
|
|
635
|
+
|
|
636
|
+
Get object metadata without downloading content.
|
|
637
|
+
|
|
638
|
+
```typescript
|
|
639
|
+
const metadata = await s3Service.headObject(
|
|
640
|
+
bucket: string,
|
|
641
|
+
key: string
|
|
642
|
+
): Promise<S3Object>
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
### getPresignedUrl Method
|
|
646
|
+
|
|
647
|
+
Generate presigned URLs for temporary access.
|
|
648
|
+
|
|
649
|
+
```typescript
|
|
650
|
+
const url = await s3Service.getPresignedUrl(
|
|
651
|
+
bucket: string,
|
|
652
|
+
key: string,
|
|
653
|
+
operation?: 'get' | 'put', // Default: 'get'
|
|
654
|
+
expiresIn?: number // Seconds, default 3600
|
|
655
|
+
): Promise<string>
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
**Example:**
|
|
659
|
+
|
|
660
|
+
```typescript
|
|
661
|
+
// Generate download URL valid for 1 hour
|
|
662
|
+
const downloadUrl = await s3Service.getPresignedUrl(
|
|
663
|
+
'my-bucket',
|
|
664
|
+
'reports/summary.pdf',
|
|
665
|
+
'get',
|
|
666
|
+
3600
|
|
667
|
+
);
|
|
668
|
+
|
|
669
|
+
// Generate upload URL valid for 10 minutes
|
|
670
|
+
const uploadUrl = await s3Service.getPresignedUrl('my-bucket', 'uploads/new-file.csv', 'put', 600);
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
### S3ServiceError
|
|
674
|
+
|
|
675
|
+
Custom error class for S3 operations.
|
|
676
|
+
|
|
677
|
+
```typescript
|
|
678
|
+
try {
|
|
679
|
+
await s3Service.getObject('bucket', 'key');
|
|
680
|
+
} catch (error) {
|
|
681
|
+
if (error instanceof S3ServiceError) {
|
|
682
|
+
console.error(`S3 Error: ${error.message}`);
|
|
683
|
+
console.error(`Code: ${error.code}`);
|
|
684
|
+
console.error(`Status: ${error.statusCode}`);
|
|
685
|
+
console.error(`Request ID: ${error.requestId}`);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
## S3PresignService
|
|
691
|
+
|
|
692
|
+
AWS S3 Presigned URL Service that generates presigned URLs for S3 operations using AWS Signature Version 4.
|
|
693
|
+
|
|
694
|
+
```typescript
|
|
695
|
+
import { S3PresignService } from '@fluentcommerce/fc-connect-sdk';
|
|
696
|
+
|
|
697
|
+
const presignService = new S3PresignService(
|
|
698
|
+
{
|
|
699
|
+
accessKeyId: 'your-access-key',
|
|
700
|
+
secretAccessKey: 'your-secret-key',
|
|
701
|
+
region: 'us-east-1',
|
|
702
|
+
bucket: 'my-bucket', // Optional default bucket
|
|
703
|
+
sessionToken: 'xxx', // Optional session token
|
|
704
|
+
},
|
|
705
|
+
logger
|
|
706
|
+
);
|
|
707
|
+
|
|
708
|
+
// Generate presigned URL for getting an object
|
|
709
|
+
const getUrl = presignService.presignGetObject({
|
|
710
|
+
bucket: 'my-bucket',
|
|
711
|
+
key: 'data/file.csv',
|
|
712
|
+
accessKeyId: 'xxx',
|
|
713
|
+
secretAccessKey: 'yyy',
|
|
714
|
+
region: 'us-east-1',
|
|
715
|
+
expiresIn: 3600, // Optional, defaults to 3600 seconds (1 hour)
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
// Generate presigned URL for putting an object
|
|
719
|
+
const { url, headers } = presignService.presignPutObject({
|
|
720
|
+
bucket: 'my-bucket',
|
|
721
|
+
key: 'uploads/new-file.csv',
|
|
722
|
+
accessKeyId: 'xxx',
|
|
723
|
+
secretAccessKey: 'yyy',
|
|
724
|
+
region: 'us-east-1',
|
|
725
|
+
expiresIn: 600, // 10 minutes
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
// Generate presigned URL for copying an object
|
|
729
|
+
const { url: copyUrl, headers: copyHeaders } = presignService.presignCopyObject({
|
|
730
|
+
sourceBucket: 'source-bucket',
|
|
731
|
+
sourceKey: 'path/to/source.csv',
|
|
732
|
+
bucket: 'dest-bucket',
|
|
733
|
+
key: 'path/to/dest.csv',
|
|
734
|
+
accessKeyId: 'xxx',
|
|
735
|
+
secretAccessKey: 'yyy',
|
|
736
|
+
region: 'us-east-1',
|
|
737
|
+
metadata: { 'custom-header': 'value' }, // Optional metadata
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
// Generate presigned URL for deleting an object
|
|
741
|
+
const deleteUrl = presignService.presignDeleteObject({
|
|
742
|
+
bucket: 'my-bucket',
|
|
743
|
+
key: 'file-to-delete.csv',
|
|
744
|
+
accessKeyId: 'xxx',
|
|
745
|
+
secretAccessKey: 'yyy',
|
|
746
|
+
region: 'us-east-1',
|
|
747
|
+
});
|
|
748
|
+
|
|
749
|
+
// Generate presigned URL for listing objects
|
|
750
|
+
const listUrl = presignService.presignListObjects({
|
|
751
|
+
bucket: 'my-bucket',
|
|
752
|
+
prefix: 'data/', // Optional prefix filter
|
|
753
|
+
delimiter: '/', // Optional delimiter
|
|
754
|
+
maxKeys: 1000, // Optional max keys
|
|
755
|
+
accessKeyId: 'xxx',
|
|
756
|
+
secretAccessKey: 'yyy',
|
|
757
|
+
region: 'us-east-1',
|
|
758
|
+
expiresIn: 3600,
|
|
759
|
+
});
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
**Constructor:**
|
|
763
|
+
|
|
764
|
+
```typescript
|
|
765
|
+
new S3PresignService(
|
|
766
|
+
config: S3Config, // S3 configuration
|
|
767
|
+
logger?: StructuredLogger // Optional logger
|
|
768
|
+
)
|
|
769
|
+
```
|
|
770
|
+
|
|
771
|
+
**Methods:**
|
|
772
|
+
|
|
773
|
+
- `presignListObjects(params: PresignListParams): string` - Generate presigned URL for listing objects
|
|
774
|
+
- `presignGetObject(params: PresignObjectParams): string` - Generate presigned URL for getting an object
|
|
775
|
+
- `presignPutObject(params: PresignObjectParams): { url: string; headers: Record<string, string> }` - Generate presigned URL and headers for putting an object
|
|
776
|
+
- `presignCopyObject(params: PresignCopyParams): { url: string; headers: Record<string, string> }` - Generate presigned URL and headers for copying an object
|
|
777
|
+
- `presignDeleteObject(params: PresignDeleteParams): string` - Generate presigned URL for deleting an object
|
|
778
|
+
- `updateConfig(config: Partial<S3Config>): void` - Update S3 configuration
|
|
779
|
+
- `getConfigSummary(): Omit<S3Config, 'secretAccessKey' | 'sessionToken'>` - Get current configuration (without secrets)
|
|
780
|
+
- `validateConfig(): { isValid: boolean; errors: string[] }` - Validate S3 configuration
|
|
781
|
+
|
|
782
|
+
**Parameter Types:**
|
|
783
|
+
|
|
784
|
+
```typescript
|
|
785
|
+
interface PresignObjectParams {
|
|
786
|
+
bucket: string;
|
|
787
|
+
key: string;
|
|
788
|
+
accessKeyId: string;
|
|
789
|
+
secretAccessKey: string;
|
|
790
|
+
region: string;
|
|
791
|
+
sessionToken?: string;
|
|
792
|
+
expiresIn?: number; // Seconds, default 3600
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
interface PresignListParams {
|
|
796
|
+
bucket: string;
|
|
797
|
+
prefix?: string;
|
|
798
|
+
delimiter?: string;
|
|
799
|
+
maxKeys?: number;
|
|
800
|
+
accessKeyId: string;
|
|
801
|
+
secretAccessKey: string;
|
|
802
|
+
region: string;
|
|
803
|
+
sessionToken?: string;
|
|
804
|
+
expiresIn?: number;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
interface PresignCopyParams {
|
|
808
|
+
sourceBucket?: string; // Defaults to bucket if not specified
|
|
809
|
+
sourceKey: string;
|
|
810
|
+
bucket: string;
|
|
811
|
+
key: string;
|
|
812
|
+
accessKeyId: string;
|
|
813
|
+
secretAccessKey: string;
|
|
814
|
+
region: string;
|
|
815
|
+
sessionToken?: string;
|
|
816
|
+
metadata?: Record<string, string>;
|
|
817
|
+
expiresIn?: number;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
interface PresignDeleteParams {
|
|
821
|
+
bucket: string;
|
|
822
|
+
key: string;
|
|
823
|
+
accessKeyId: string;
|
|
824
|
+
secretAccessKey: string;
|
|
825
|
+
region: string;
|
|
826
|
+
sessionToken?: string;
|
|
827
|
+
expiresIn?: number;
|
|
828
|
+
}
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
## JobTracker (NEW)
|
|
832
|
+
|
|
833
|
+
Track job lifecycle and metrics in a KV store. Provides standardized job status tracking for async workflows with automatic timestamp management and TTL support.
|
|
834
|
+
|
|
835
|
+
```typescript
|
|
836
|
+
import { JobTracker } from '@fluentcommerce/fc-connect-sdk';
|
|
837
|
+
import { VersoriKVAdapter } from '@fluentcommerce/fc-connect-sdk';
|
|
838
|
+
// ✅ CORRECT: Access openKv from Versori context
|
|
839
|
+
// import { openKv } from '@versori/run'; // ❌ WRONG - Not a direct export
|
|
840
|
+
|
|
841
|
+
// In Versori workflow handler:
|
|
842
|
+
const { openKv } = ctx;
|
|
843
|
+
const kvAdapter = new VersoriKVAdapter(openKv(':project:'));
|
|
844
|
+
const tracker = new JobTracker(
|
|
845
|
+
kvAdapter,
|
|
846
|
+
logger,
|
|
847
|
+
604800 // Optional TTL in seconds (default: 7 days)
|
|
848
|
+
);
|
|
849
|
+
|
|
850
|
+
// Create a new job
|
|
851
|
+
await tracker.createJob('scheduled_1729681200000', {
|
|
852
|
+
triggeredBy: 'schedule',
|
|
853
|
+
stage: 'initialization',
|
|
854
|
+
details: { catalogueRef: 'DEFAULT:1' },
|
|
855
|
+
});
|
|
856
|
+
|
|
857
|
+
// Update job status
|
|
858
|
+
await tracker.updateJob('scheduled_1729681200000', {
|
|
859
|
+
status: 'processing',
|
|
860
|
+
stage: 'extraction',
|
|
861
|
+
message: 'Extracting 1000 records',
|
|
862
|
+
details: { recordCount: 1000 },
|
|
863
|
+
});
|
|
864
|
+
|
|
865
|
+
// Mark as completed
|
|
866
|
+
await tracker.markCompleted('scheduled_1729681200000', {
|
|
867
|
+
recordCount: 1000,
|
|
868
|
+
fileName: 'export.xml',
|
|
869
|
+
sftpPath: '/uploads/export.xml',
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
// Mark as failed (in catch block)
|
|
873
|
+
try {
|
|
874
|
+
// ... job logic ...
|
|
875
|
+
} catch (error) {
|
|
876
|
+
await tracker.markFailed('scheduled_1729681200000', error);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
// Get job status
|
|
880
|
+
const jobStatus = await tracker.getJob('scheduled_1729681200000');
|
|
881
|
+
console.log(jobStatus);
|
|
882
|
+
// {
|
|
883
|
+
// status: 'completed',
|
|
884
|
+
// stage: 'finished',
|
|
885
|
+
// message: 'Job completed successfully',
|
|
886
|
+
// createdAt: '2025-01-24T10:00:00.000Z',
|
|
887
|
+
// startedAt: '2025-01-24T10:00:05.000Z',
|
|
888
|
+
// completedAt: '2025-01-24T10:05:30.000Z',
|
|
889
|
+
// triggeredBy: 'schedule',
|
|
890
|
+
// details: { recordCount: 1000, fileName: 'export.xml', ... }
|
|
891
|
+
// }
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
**Constructor:**
|
|
895
|
+
|
|
896
|
+
```typescript
|
|
897
|
+
new JobTracker(
|
|
898
|
+
kv: KVAdapter, // KV adapter (VersoriKVAdapter or custom)
|
|
899
|
+
logger: StructuredLogger, // Logger instance
|
|
900
|
+
ttl?: number // Optional TTL in seconds (default: 604800 = 7 days)
|
|
901
|
+
)
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
**Methods:**
|
|
905
|
+
|
|
906
|
+
- `createJob(jobId: string, metadata: Partial<Omit<JobStatus, 'status' | 'createdAt'>>): Promise<void>` - Create a new job with 'queued' status
|
|
907
|
+
- `updateJob(jobId: string, update: Partial<JobStatus>): Promise<void>` - Update job status (auto-updates timestamps)
|
|
908
|
+
- `getJob(jobId: string): Promise<JobStatus | undefined>` - Retrieve job status
|
|
909
|
+
- `markCompleted(jobId: string, details?: Record<string, any>): Promise<void>` - Mark job as completed
|
|
910
|
+
- `markFailed(jobId: string, error: Error | string): Promise<void>` - Mark job as failed
|
|
911
|
+
|
|
912
|
+
**JobStatus Interface:**
|
|
913
|
+
|
|
914
|
+
```typescript
|
|
915
|
+
interface JobStatus {
|
|
916
|
+
status: 'queued' | 'processing' | 'completed' | 'failed';
|
|
917
|
+
stage?: string;
|
|
918
|
+
message?: string;
|
|
919
|
+
createdAt?: string;
|
|
920
|
+
startedAt?: string;
|
|
921
|
+
completedAt?: string;
|
|
922
|
+
failedAt?: string;
|
|
923
|
+
triggeredBy: 'schedule' | 'webhook' | 'manual';
|
|
924
|
+
details?: Record<string, any>;
|
|
925
|
+
error?: string;
|
|
926
|
+
errorStack?: string;
|
|
927
|
+
}
|
|
928
|
+
```
|
|
929
|
+
|
|
930
|
+
**Key Features:**
|
|
931
|
+
|
|
932
|
+
- Standardized job status tracking
|
|
933
|
+
- Automatic timestamp management (createdAt, startedAt, completedAt, failedAt)
|
|
934
|
+
- TTL support for automatic cleanup
|
|
935
|
+
- Safe error handling (never throws on KV errors)
|
|
936
|
+
- Detailed logging
|
|
937
|
+
|
|
938
|
+
## PartialBatchRecovery (NEW)
|
|
939
|
+
|
|
940
|
+
Tracks per-record success/failure in batch operations and enables retrying only failed records instead of entire batches. Provides checkpoint/resume functionality and detailed error reporting.
|
|
941
|
+
|
|
942
|
+
```typescript
|
|
943
|
+
import { PartialBatchRecovery } from '@fluentcommerce/fc-connect-sdk';
|
|
944
|
+
|
|
945
|
+
const recovery = new PartialBatchRecovery<InventoryRecord>(logger);
|
|
946
|
+
|
|
947
|
+
// Process batch with automatic recovery and retry
|
|
948
|
+
const result = await recovery.processBatchWithRecovery(
|
|
949
|
+
records,
|
|
950
|
+
async batch =>
|
|
951
|
+
await client.sendBatch(jobId, {
|
|
952
|
+
action: 'UPSERT',
|
|
953
|
+
entityType: 'INVENTORY',
|
|
954
|
+
entities: batch,
|
|
955
|
+
}),
|
|
956
|
+
{
|
|
957
|
+
maxRetries: 3,
|
|
958
|
+
retryOnlyFailed: true,
|
|
959
|
+
retryBatchSize: 100,
|
|
960
|
+
checkpointKey: 'inventory-2025-01-24',
|
|
961
|
+
retryDelayMs: 1000,
|
|
962
|
+
shouldRetry: (error, attemptCount) => {
|
|
963
|
+
// Custom retry logic
|
|
964
|
+
return attemptCount <= 3 && !error.message.includes('permanent');
|
|
965
|
+
},
|
|
966
|
+
}
|
|
967
|
+
);
|
|
968
|
+
|
|
969
|
+
console.log(`Success: ${result.successCount}, Failed: ${result.failedCount}`);
|
|
970
|
+
console.log(`Duration: ${result.durationMs}ms`);
|
|
971
|
+
console.log(result.summary);
|
|
972
|
+
|
|
973
|
+
if (result.failedRecords.length > 0) {
|
|
974
|
+
console.log('Failed records:', result.failedRecords);
|
|
975
|
+
|
|
976
|
+
// Export failed records for manual review
|
|
977
|
+
const failures = result.failedRecords.map(f => ({
|
|
978
|
+
index: f.index,
|
|
979
|
+
record: f.record,
|
|
980
|
+
error: f.error.message,
|
|
981
|
+
attempts: f.attemptCount,
|
|
982
|
+
}));
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
// Resume from checkpoint later
|
|
986
|
+
if (result.checkpointId) {
|
|
987
|
+
const resumeResult = await recovery.resumeFromCheckpoint(
|
|
988
|
+
result.checkpointId,
|
|
989
|
+
async batch =>
|
|
990
|
+
await client.sendBatch(jobId, {
|
|
991
|
+
action: 'UPSERT',
|
|
992
|
+
entityType: 'INVENTORY',
|
|
993
|
+
entities: batch,
|
|
994
|
+
})
|
|
995
|
+
);
|
|
996
|
+
}
|
|
997
|
+
```
|
|
998
|
+
|
|
999
|
+
**Constructor:**
|
|
1000
|
+
|
|
1001
|
+
```typescript
|
|
1002
|
+
new PartialBatchRecovery<T = any>(
|
|
1003
|
+
logger?: StructuredLogger // Optional logger
|
|
1004
|
+
)
|
|
1005
|
+
```
|
|
1006
|
+
|
|
1007
|
+
**Methods:**
|
|
1008
|
+
|
|
1009
|
+
- `processBatchWithRecovery(records: T[], batchProcessor: (records: T[]) => Promise<any>, options?: BatchRecoveryOptions): Promise<BatchRecoveryResult<T>>` - Process batch with automatic recovery and retry
|
|
1010
|
+
- `resumeFromCheckpoint(checkpointId: string, batchProcessor: (records: T[]) => Promise<any>, options?: BatchRecoveryOptions): Promise<BatchRecoveryResult<T>>` - Resume processing from a saved checkpoint
|
|
1011
|
+
- `getCheckpoint(checkpointId: string): BatchCheckpoint<T> | undefined` - Get checkpoint by ID
|
|
1012
|
+
- `listCheckpoints(): BatchCheckpoint<T>[]` - List all checkpoints
|
|
1013
|
+
- `deleteCheckpoint(checkpointId: string): boolean` - Delete a checkpoint
|
|
1014
|
+
- `clearCheckpoints(): void` - Clear all checkpoints
|
|
1015
|
+
|
|
1016
|
+
**BatchRecoveryOptions:**
|
|
1017
|
+
|
|
1018
|
+
```typescript
|
|
1019
|
+
interface BatchRecoveryOptions {
|
|
1020
|
+
maxRetries?: number; // Max retry attempts per record (default: 3)
|
|
1021
|
+
retryOnlyFailed?: boolean; // Retry only failed records (default: true)
|
|
1022
|
+
checkpointKey?: string; // Checkpoint key for resume
|
|
1023
|
+
retryDelayMs?: number; // Delay between retries in ms (default: 1000)
|
|
1024
|
+
shouldRetry?: (error: Error, attemptCount: number) => boolean; // Custom retry predicate
|
|
1025
|
+
retryBatchSize?: number; // Batch size for retries (default: 100)
|
|
1026
|
+
}
|
|
1027
|
+
```
|
|
1028
|
+
|
|
1029
|
+
**BatchRecoveryResult:**
|
|
1030
|
+
|
|
1031
|
+
```typescript
|
|
1032
|
+
interface BatchRecoveryResult<T = any> {
|
|
1033
|
+
totalRecords: number; // Total records processed
|
|
1034
|
+
successCount: number; // Successfully processed records
|
|
1035
|
+
failedCount: number; // Failed records after all retries
|
|
1036
|
+
failedRecords: RecordFailure<T>[]; // Details of failed records
|
|
1037
|
+
checkpointId?: string; // Checkpoint ID for resuming
|
|
1038
|
+
summary: string; // Processing summary
|
|
1039
|
+
durationMs: number; // Total time taken
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
interface RecordFailure<T = any> {
|
|
1043
|
+
index: number; // Original record index
|
|
1044
|
+
record: T; // The record that failed
|
|
1045
|
+
error: Error; // Error that occurred
|
|
1046
|
+
attemptCount: number; // Number of retry attempts
|
|
1047
|
+
timestamp: string; // Timestamp of failure
|
|
1048
|
+
}
|
|
1049
|
+
```
|
|
1050
|
+
|
|
1051
|
+
**Key Features:**
|
|
1052
|
+
|
|
1053
|
+
- Per-record retry logic (avoids reprocessing successful records)
|
|
1054
|
+
- Exponential backoff for retries
|
|
1055
|
+
- Checkpoint/resume functionality
|
|
1056
|
+
- Detailed failure tracking with attempt counts
|
|
1057
|
+
- Custom retry predicates
|
|
1058
|
+
- Configurable batch sizes for retries
|
|
1059
|
+
|
|
1060
|
+
## PreflightValidator (NEW)
|
|
1061
|
+
|
|
1062
|
+
Validates data BEFORE sending to Fluent API to catch errors early, save API quota, validate against GraphQL schema requirements, and check for data quality issues.
|
|
1063
|
+
|
|
1064
|
+
```typescript
|
|
1065
|
+
import { PreflightValidator } from '@fluentcommerce/fc-connect-sdk';
|
|
1066
|
+
|
|
1067
|
+
const validator = new PreflightValidator(logger);
|
|
1068
|
+
|
|
1069
|
+
// Validate batch before sending
|
|
1070
|
+
const result = await validator.validateBatch(records, {
|
|
1071
|
+
entityType: 'INVENTORY',
|
|
1072
|
+
requiredFields: ['ref', 'qty'],
|
|
1073
|
+
checkDuplicates: true,
|
|
1074
|
+
maxBatchSize: 1000,
|
|
1075
|
+
validateTypes: true,
|
|
1076
|
+
customValidator: (record, index) => {
|
|
1077
|
+
if (record.qty < 0) {
|
|
1078
|
+
return `Record ${index}: qty must be non-negative`;
|
|
1079
|
+
}
|
|
1080
|
+
return null;
|
|
1081
|
+
},
|
|
1082
|
+
});
|
|
1083
|
+
|
|
1084
|
+
if (!result.isValid) {
|
|
1085
|
+
console.error(`Validation failed: ${result.errors.length} errors`);
|
|
1086
|
+
result.errors.forEach(err => {
|
|
1087
|
+
console.error(`[Record ${err.index}] ${err.field}: ${err.message}`);
|
|
1088
|
+
});
|
|
1089
|
+
throw new Error('Batch validation failed');
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
console.log(result.summary);
|
|
1093
|
+
// "Validation passed: 1000/1000 records valid"
|
|
1094
|
+
|
|
1095
|
+
// Quick validation - returns first error only (faster)
|
|
1096
|
+
const quickResult = await validator.validateQuick(records, {
|
|
1097
|
+
entityType: 'INVENTORY',
|
|
1098
|
+
requiredFields: ['ref', 'qty'],
|
|
1099
|
+
maxBatchSize: 1000,
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
if (!quickResult.isValid) {
|
|
1103
|
+
console.error(`Quick validation failed: ${quickResult.error}`);
|
|
1104
|
+
}
|
|
1105
|
+
```
|
|
1106
|
+
|
|
1107
|
+
**Constructor:**
|
|
1108
|
+
|
|
1109
|
+
```typescript
|
|
1110
|
+
new PreflightValidator(
|
|
1111
|
+
logger?: StructuredLogger // Optional logger
|
|
1112
|
+
)
|
|
1113
|
+
```
|
|
1114
|
+
|
|
1115
|
+
**Methods:**
|
|
1116
|
+
|
|
1117
|
+
- `validateBatch(records: any[], options: PreflightValidationOptions): Promise<PreflightValidationResult>` - Validate entire batch with detailed error reporting
|
|
1118
|
+
- `validateQuick(records: any[], options: PreflightValidationOptions): Promise<{ isValid: boolean; error?: string }>` - Quick validation that returns first error only (faster for fail-fast scenarios)
|
|
1119
|
+
|
|
1120
|
+
**PreflightValidationOptions:**
|
|
1121
|
+
|
|
1122
|
+
```typescript
|
|
1123
|
+
interface PreflightValidationOptions {
|
|
1124
|
+
entityType: 'INVENTORY' | 'CONTROL' | 'LOCATION' | 'ARTICLE' | string;
|
|
1125
|
+
requiredFields?: string[]; // Fields that must be present
|
|
1126
|
+
checkDuplicates?: boolean; // Check for duplicate refs
|
|
1127
|
+
maxBatchSize?: number; // Maximum batch size
|
|
1128
|
+
validateTypes?: boolean; // Validate field types
|
|
1129
|
+
customValidator?: (record: any, index: number) => string | null; // Custom validation function
|
|
1130
|
+
}
|
|
1131
|
+
```
|
|
1132
|
+
|
|
1133
|
+
**PreflightValidationResult:**
|
|
1134
|
+
|
|
1135
|
+
```typescript
|
|
1136
|
+
interface PreflightValidationResult {
|
|
1137
|
+
isValid: boolean; // Whether validation passed
|
|
1138
|
+
totalRecords: number; // Total records validated
|
|
1139
|
+
validRecords: number; // Number of valid records
|
|
1140
|
+
errors: PreflightValidationError[]; // All validation errors
|
|
1141
|
+
warnings: PreflightValidationError[]; // All validation warnings
|
|
1142
|
+
summary: string; // Summary message
|
|
1143
|
+
duplicates?: string[]; // Duplicate refs found (if checkDuplicates enabled)
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
interface PreflightValidationError {
|
|
1147
|
+
index: number; // Record index where error occurred
|
|
1148
|
+
field?: string; // Field that failed validation
|
|
1149
|
+
message: string; // Error message
|
|
1150
|
+
severity: 'error' | 'warning'; // Error severity
|
|
1151
|
+
value?: any; // The problematic value
|
|
1152
|
+
}
|
|
1153
|
+
```
|
|
1154
|
+
|
|
1155
|
+
**Key Features:**
|
|
1156
|
+
|
|
1157
|
+
- Catches errors early (saves API quota)
|
|
1158
|
+
- Validates against GraphQL schema requirements
|
|
1159
|
+
- Checks for duplicates and data quality issues
|
|
1160
|
+
- Provides actionable error messages
|
|
1161
|
+
- Type validation for common fields (qty, ref, etc.)
|
|
1162
|
+
- Custom validation functions
|
|
1163
|
+
- Fast "quick validation" mode for fail-fast scenarios
|
|
1164
|
+
|
|
1165
|
+
## Versori Adapters (NEW)
|
|
1166
|
+
|
|
1167
|
+
Helpers for Versori platform integrations.
|
|
1168
|
+
|
|
1169
|
+
```typescript
|
|
1170
|
+
import { VersoriKVAdapter, VersoriFileTracker } from '@fluentcommerce/fc-connect-sdk';
|
|
1171
|
+
// ✅ CORRECT: Access openKv from Versori context
|
|
1172
|
+
// import { openKv } from '@versori/run'; // ❌ WRONG - Not a direct export
|
|
1173
|
+
|
|
1174
|
+
// In Versori workflow handler:
|
|
1175
|
+
const { openKv } = ctx;
|
|
1176
|
+
const kv = new VersoriKVAdapter(openKv(':project:'));
|
|
1177
|
+
await kv.set('processed:file1', { at: Date.now() }, 3600);
|
|
1178
|
+
|
|
1179
|
+
// File tracker to dedupe processed files
|
|
1180
|
+
const fileTracker = new VersoriFileTracker(openKv(':project:'), 'my-workflow');
|
|
1181
|
+
const already = await fileTracker.wasFileProcessed('incoming/products_20250101.xml');
|
|
1182
|
+
if (!already) {
|
|
1183
|
+
await fileTracker.markFileProcessed('incoming/products_20250101.xml', { size: 2048 });
|
|
1184
|
+
}
|
|
1185
|
+
```
|
|
1186
|
+
|
|
1187
|
+
## See Also
|
|
1188
|
+
|
|
1189
|
+
- [Ingestion Guide](../../ingestion/ingestion-readme.md) - Complete ingestion workflows
|
|
1190
|
+
- [Extraction Guide](../../extraction/extraction-readme.md) - Data extraction patterns
|
|
1191
|
+
- [Module 3: Authentication](./api-reference-03-authentication.md) - OAuth2 and auth providers
|
|
1192
|
+
- [Module 6: Data Sources](./api-reference-06-data-sources.md) - S3 and SFTP data sources
|
|
1193
|
+
- [Module 9: Error Handling](./api-reference-09-error-handling.md) - Error types and handling
|
|
1194
|
+
- [Module 8: Types](./api-reference-08-types.md) - Service-related types
|
|
1195
|
+
|
|
1196
|
+
---
|
|
1197
|
+
|
|
1198
|
+
**[← Previous: GraphQL Mapping](./api-reference-04-graphql-mapping.md)** | **[API Reference Home](../api-reference-readme.md)** | **[Next: Data Sources →](./api-reference-06-data-sources.md)**
|