@fluentcommerce/fc-connect-sdk 0.1.54 → 0.1.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +11 -0
- package/dist/cjs/clients/fluent-client.js +13 -6
- package/dist/cjs/utils/pagination-helpers.js +38 -2
- package/dist/cjs/versori/fluent-versori-client.js +11 -5
- package/dist/esm/clients/fluent-client.js +13 -6
- 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/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 -520
- 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
- package/docs/02-CORE-GUIDES/api-reference/cli-profile-integration.md +0 -377
|
@@ -1,962 +1,962 @@
|
|
|
1
|
-
# Integration Patterns - Quick Reference
|
|
2
|
-
|
|
3
|
-
> Cheat sheet for common integration patterns with the Fluent Connect SDK
|
|
4
|
-
|
|
5
|
-
## Pattern Selection Guide
|
|
6
|
-
|
|
7
|
-
### Decision Tree
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
What's your integration scenario?
|
|
11
|
-
|
|
12
|
-
├─ Single event (< 10/hour)
|
|
13
|
-
│ └─ Use: Real-Time Processing (Module 1)
|
|
14
|
-
│ └─ Pattern: Webhook → GraphQL mutation
|
|
15
|
-
│
|
|
16
|
-
├─ Multiple events (10-1000/hour)
|
|
17
|
-
│ ├─ Immediate response required?
|
|
18
|
-
│ │ ├─ YES → Real-Time Processing (Module 1)
|
|
19
|
-
│ │ └─ NO → Batch Processing (Module 2)
|
|
20
|
-
│ │
|
|
21
|
-
│ └─ Pattern: Hourly batch jobs
|
|
22
|
-
│
|
|
23
|
-
├─ Large files (1K+ records)
|
|
24
|
-
│ └─ Use: Batch Processing (Module 2)
|
|
25
|
-
│ └─ Pattern: S3 CSV → Batch API
|
|
26
|
-
│
|
|
27
|
-
└─ Frequent syncs (hourly/15min)
|
|
28
|
-
└─ Use: Delta Sync (Module 3)
|
|
29
|
-
└─ Pattern: Timestamp-based change detection
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
---
|
|
33
|
-
|
|
34
|
-
## Quick Patterns
|
|
35
|
-
|
|
36
|
-
### Real-Time: JSON Webhook → Fluent
|
|
37
|
-
|
|
38
|
-
```typescript
|
|
39
|
-
import { createClient } from '@fluentcommerce/fc-connect-sdk';
|
|
40
|
-
|
|
41
|
-
export default async function webhook(ctx) {
|
|
42
|
-
const client = await createClient(ctx); // Auto-detects Versori context
|
|
43
|
-
|
|
44
|
-
const result = await client.graphql({
|
|
45
|
-
query: `mutation CreateOrder($input: CreateOrderInput!) {
|
|
46
|
-
createOrder(input: $input) { id ref }
|
|
47
|
-
}`,
|
|
48
|
-
variables: { input: mapData(activation.body) },
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
return { status: 200, body: { orderId: result.id } };
|
|
52
|
-
}
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
**Use when**: < 100 events/hour, need < 5 sec response
|
|
56
|
-
|
|
57
|
-
---
|
|
58
|
-
|
|
59
|
-
### Batch: CSV File → Fluent Batch API
|
|
60
|
-
|
|
61
|
-
```typescript
|
|
62
|
-
import {
|
|
63
|
-
createClient,
|
|
64
|
-
S3DataSource,
|
|
65
|
-
CSVParserService,
|
|
66
|
-
UniversalMapper,
|
|
67
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
68
|
-
|
|
69
|
-
async function batchSync() {
|
|
70
|
-
// Download CSV
|
|
71
|
-
const s3 = new S3DataSource(config, console);
|
|
72
|
-
const csv = await s3.downloadFile('inventory.csv');
|
|
73
|
-
|
|
74
|
-
// Parse
|
|
75
|
-
const parser = new CSVParserService({ headers: true });
|
|
76
|
-
const records = await parser.parse(csv);
|
|
77
|
-
|
|
78
|
-
// Map
|
|
79
|
-
const mapper = new UniversalMapper(mappingConfig);
|
|
80
|
-
const mapped = records.map(r => mapper.map(r).data);
|
|
81
|
-
|
|
82
|
-
// Batch API
|
|
83
|
-
const client = await createClient({ config });
|
|
84
|
-
const job = await client.createJob({ name: 'Sync', retailerId: '2' });
|
|
85
|
-
|
|
86
|
-
for (let i = 0; i < mapped.length; i += 100) {
|
|
87
|
-
await client.sendBatch(job.id, {
|
|
88
|
-
entities: mapped.slice(i, i + 100),
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Poll
|
|
93
|
-
let status = await client.getJobStatus(job.id);
|
|
94
|
-
while (status.status === 'PROCESSING') {
|
|
95
|
-
await sleep(30000);
|
|
96
|
-
status = await client.getJobStatus(job.id);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
**Use when**: 1K+ records, scheduled processing, non-critical
|
|
102
|
-
|
|
103
|
-
---
|
|
104
|
-
|
|
105
|
-
### Delta Sync: Timestamp-Based
|
|
106
|
-
|
|
107
|
-
```typescript
|
|
108
|
-
import { StateService, FileKVAdapter } from '@fluentcommerce/fc-connect-sdk';
|
|
109
|
-
|
|
110
|
-
async function deltaSync() {
|
|
111
|
-
const stateService = new StateService(logger);
|
|
112
|
-
|
|
113
|
-
// Get last sync time
|
|
114
|
-
let lastSync = (await stateService.getState('last-sync')) || new Date(0).toISOString();
|
|
115
|
-
|
|
116
|
-
// Filter changed records
|
|
117
|
-
const allRecords = await loadAllRecords();
|
|
118
|
-
const changed = allRecords.filter(r => new Date(r.lastModified).toISOString() > lastSync);
|
|
119
|
-
|
|
120
|
-
console.log(`${changed.length} of ${allRecords.length} changed`);
|
|
121
|
-
|
|
122
|
-
// Process only changed
|
|
123
|
-
await processBatch(changed);
|
|
124
|
-
|
|
125
|
-
// Update timestamp
|
|
126
|
-
await stateService.setState('last-sync', new Date().toISOString());
|
|
127
|
-
}
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
**Use when**: Frequent syncs, large datasets, < 5% change rate
|
|
131
|
-
|
|
132
|
-
---
|
|
133
|
-
|
|
134
|
-
### Webhook: XML → GraphQL
|
|
135
|
-
|
|
136
|
-
```typescript
|
|
137
|
-
import { GraphQLMutationMapper } from '@fluentcommerce/fc-connect-sdk';
|
|
138
|
-
|
|
139
|
-
const mappingConfig = {
|
|
140
|
-
mutation: 'createOrder',
|
|
141
|
-
nodes: {
|
|
142
|
-
radial: {
|
|
143
|
-
extract: 'orders.order.custom-attribute[attribute-id=CreateOrderServiceRequest]',
|
|
144
|
-
parse: 'xml',
|
|
145
|
-
},
|
|
146
|
-
},
|
|
147
|
-
fields: {
|
|
148
|
-
ref: { source: '$radial.Order@customerOrderId' },
|
|
149
|
-
totalPrice: { source: '$radial.Order.Payment.Amount', resolver: 'sdk.parseFloat' },
|
|
150
|
-
},
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
const mapper = new GraphQLMutationMapper(mappingConfig, log, { fluentClient: client });
|
|
154
|
-
const result = await mapper.mapWithNodes(xmlData, customResolvers, context);
|
|
155
|
-
|
|
156
|
-
// Check success (errors are returned, not thrown)
|
|
157
|
-
if (!result.success) {
|
|
158
|
-
console.error('Mapping failed:', result.errors);
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Execute mutation (query is auto-generated in result)
|
|
163
|
-
await client.graphql({ query: result.query, variables: result.variables }); // ✅ Use variables
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
**Use when**: SFCC, Radial, SOAP webhooks with nested XML
|
|
167
|
-
|
|
168
|
-
---
|
|
169
|
-
|
|
170
|
-
### Error Handling: Retry with Backoff
|
|
171
|
-
|
|
172
|
-
```typescript
|
|
173
|
-
async function retryWithBackoff<T>(fn: () => Promise<T>, maxRetries = 5): Promise<T> {
|
|
174
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
175
|
-
try {
|
|
176
|
-
return await fn();
|
|
177
|
-
} catch (error: any) {
|
|
178
|
-
if (error.statusCode < 500) throw error; // Don't retry client errors
|
|
179
|
-
|
|
180
|
-
if (attempt < maxRetries) {
|
|
181
|
-
const delay = Math.min(1000 * Math.pow(2, attempt), 30000);
|
|
182
|
-
await sleep(delay);
|
|
183
|
-
} else {
|
|
184
|
-
throw error;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Usage
|
|
191
|
-
const result = await retryWithBackoff(() => client.graphql({ query, variables }));
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
**Use when**: Network errors, rate limits, server errors
|
|
195
|
-
|
|
196
|
-
---
|
|
197
|
-
|
|
198
|
-
### Partial Batch Recovery
|
|
199
|
-
|
|
200
|
-
```typescript
|
|
201
|
-
import { PartialBatchRecovery, createClient } from '@fluentcommerce/fc-connect-sdk';
|
|
202
|
-
|
|
203
|
-
async function resilientBatchSync() {
|
|
204
|
-
const client = await createClient({ config });
|
|
205
|
-
const recovery = new PartialBatchRecovery(logger);
|
|
206
|
-
|
|
207
|
-
// Process batch with automatic recovery
|
|
208
|
-
const result = await recovery.processBatchWithRecovery(
|
|
209
|
-
records,
|
|
210
|
-
async batch => {
|
|
211
|
-
// Your batch processing logic
|
|
212
|
-
return await client.sendBatch(jobId, {
|
|
213
|
-
action: 'UPSERT',
|
|
214
|
-
entityType: 'INVENTORY',
|
|
215
|
-
entities: batch,
|
|
216
|
-
});
|
|
217
|
-
},
|
|
218
|
-
{
|
|
219
|
-
maxRetries: 3,
|
|
220
|
-
retryOnlyFailed: true, // Only retry failed records
|
|
221
|
-
retryDelayMs: 1000,
|
|
222
|
-
checkpointKey: 'daily-inventory-sync',
|
|
223
|
-
}
|
|
224
|
-
);
|
|
225
|
-
|
|
226
|
-
console.log(`Success: ${result.successCount}/${result.totalRecords}`);
|
|
227
|
-
console.log(`Failed: ${result.failedCount} records`);
|
|
228
|
-
|
|
229
|
-
if (result.failedCount > 0) {
|
|
230
|
-
console.error('Failed records:', result.failedRecords);
|
|
231
|
-
console.log(`Checkpoint saved: ${result.checkpointId}`);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// Later: Resume from checkpoint
|
|
235
|
-
if (result.checkpointId) {
|
|
236
|
-
const resumeResult = await recovery.resumeFromCheckpoint(result.checkpointId, processBatch, {
|
|
237
|
-
maxRetries: 5,
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
**Use when**: Sending 50+ batches, unstable network, long-running jobs
|
|
244
|
-
|
|
245
|
-
---
|
|
246
|
-
|
|
247
|
-
### Job Tracker
|
|
248
|
-
|
|
249
|
-
```typescript
|
|
250
|
-
import { JobTracker, VersoriKVAdapter, createClient } from '@fluentcommerce/fc-connect-sdk';
|
|
251
|
-
|
|
252
|
-
async function batchSyncWithTracking(kv, logger) {
|
|
253
|
-
const client = await createClient({ config });
|
|
254
|
-
const tracker = new JobTracker(new VersoriKVAdapter(kv), logger);
|
|
255
|
-
|
|
256
|
-
const jobId = `batch_${Date.now()}`;
|
|
257
|
-
|
|
258
|
-
try {
|
|
259
|
-
// Create job
|
|
260
|
-
await tracker.createJob(jobId, {
|
|
261
|
-
triggeredBy: 'schedule',
|
|
262
|
-
stage: 'initialization',
|
|
263
|
-
details: {
|
|
264
|
-
fileName: 'inventory.csv',
|
|
265
|
-
recordCount: 5000,
|
|
266
|
-
},
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
// Update progress
|
|
270
|
-
await tracker.updateJob(jobId, {
|
|
271
|
-
status: 'processing',
|
|
272
|
-
stage: 'extraction',
|
|
273
|
-
message: 'Extracting records',
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
// Create Fluent job
|
|
277
|
-
const batchJob = await client.createJob({
|
|
278
|
-
name: 'Daily Inventory Sync',
|
|
279
|
-
retailerId: '2',
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
await tracker.updateJob(jobId, {
|
|
283
|
-
stage: 'ingestion',
|
|
284
|
-
message: `Sending batches`,
|
|
285
|
-
details: { fluentJobId: batchJob.id },
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
// Send batches
|
|
289
|
-
for (let i = 0; i < batches.length; i++) {
|
|
290
|
-
await client.sendBatch(batchJob.id, { entities: batches[i] });
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Complete
|
|
294
|
-
await tracker.markCompleted(jobId, {
|
|
295
|
-
batchesSent: batches.length,
|
|
296
|
-
fluentJobId: batchJob.id,
|
|
297
|
-
});
|
|
298
|
-
} catch (error) {
|
|
299
|
-
await tracker.markFailed(jobId, error);
|
|
300
|
-
throw error;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Query job status
|
|
305
|
-
const status = await tracker.getJob(jobId);
|
|
306
|
-
console.log(`Job ${jobId}: ${status.status} at stage ${status.stage}`);
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
**Use when**: Need job history, monitoring, debugging, performance tracking
|
|
310
|
-
|
|
311
|
-
---
|
|
312
|
-
|
|
313
|
-
### Preflight Validator: Validate Before API
|
|
314
|
-
|
|
315
|
-
```typescript
|
|
316
|
-
import { PreflightValidator } from '@fluentcommerce/fc-connect-sdk';
|
|
317
|
-
|
|
318
|
-
const validator = new PreflightValidator(logger);
|
|
319
|
-
|
|
320
|
-
// Validate before sending to API
|
|
321
|
-
const result = await validator.validateBatch(records, {
|
|
322
|
-
entityType: 'INVENTORY',
|
|
323
|
-
requiredFields: ['ref', 'qty', 'productRef'],
|
|
324
|
-
checkDuplicates: true,
|
|
325
|
-
maxBatchSize: 5000,
|
|
326
|
-
validateTypes: true,
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
if (!result.isValid) {
|
|
330
|
-
console.error(`Validation failed: ${result.errors.length} errors`);
|
|
331
|
-
throw new Error(`Validation failed: ${result.summary}`);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
// Safe to send to API (saves quota)
|
|
335
|
-
await sendToFluentAPI(records);
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
**Use when**: Large batch operations, cost optimization
|
|
339
|
-
|
|
340
|
-
---
|
|
341
|
-
|
|
342
|
-
## SDK Components Cheat Sheet
|
|
343
|
-
|
|
344
|
-
### Client Creation
|
|
345
|
-
|
|
346
|
-
```typescript
|
|
347
|
-
// Versori
|
|
348
|
-
const client = await createClient(ctx); // Auto-detects Versori context
|
|
349
|
-
|
|
350
|
-
// Standalone
|
|
351
|
-
const client = await createClient({
|
|
352
|
-
config: {
|
|
353
|
-
baseUrl: 'https://api.fluentcommerce.com',
|
|
354
|
-
clientId: process.env.FLUENT_CLIENT_ID,
|
|
355
|
-
clientSecret: process.env.FLUENT_CLIENT_SECRET,
|
|
356
|
-
retailerId: process.env.FLUENT_RETAILER_ID,
|
|
357
|
-
},
|
|
358
|
-
});
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
### Client Methods
|
|
362
|
-
|
|
363
|
-
```typescript
|
|
364
|
-
// Query (simple)
|
|
365
|
-
const data = await client.graphql({ query, variables });
|
|
366
|
-
|
|
367
|
-
// Mutation (simple)
|
|
368
|
-
const data = await client.graphql({ query, variables });
|
|
369
|
-
|
|
370
|
-
// GraphQL (advanced - with pagination)
|
|
371
|
-
const result = await client.graphql({
|
|
372
|
-
query,
|
|
373
|
-
variables,
|
|
374
|
-
pagination: { maxPages: 50 },
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
// Batch API
|
|
378
|
-
const job = await client.createJob({ name: 'Job', retailerId: '2' });
|
|
379
|
-
const batch = await client.sendBatch(job.id, { entities: records });
|
|
380
|
-
const status = await client.getJobStatus(job.id);
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
### Data Sources
|
|
384
|
-
|
|
385
|
-
```typescript
|
|
386
|
-
// S3
|
|
387
|
-
import { S3DataSource } from '@fluentcommerce/fc-connect-sdk';
|
|
388
|
-
|
|
389
|
-
const s3 = new S3DataSource(
|
|
390
|
-
{
|
|
391
|
-
type: 'S3_CSV',
|
|
392
|
-
connectionId: 'my-s3',
|
|
393
|
-
name: 'My S3 Source',
|
|
394
|
-
s3Config: {
|
|
395
|
-
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
396
|
-
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
397
|
-
region: 'us-east-1',
|
|
398
|
-
bucket: 'my-bucket',
|
|
399
|
-
},
|
|
400
|
-
},
|
|
401
|
-
logger
|
|
402
|
-
);
|
|
403
|
-
|
|
404
|
-
const files = await s3.listFiles('prefix/');
|
|
405
|
-
const content = await s3.downloadFile('file.csv');
|
|
406
|
-
await s3.uploadFile('output.json', data);
|
|
407
|
-
await s3.moveFile('source.csv', 'archive/source.csv');
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
### Parsers
|
|
411
|
-
|
|
412
|
-
```typescript
|
|
413
|
-
// CSV
|
|
414
|
-
import { CSVParserService } from '@fluentcommerce/fc-connect-sdk';
|
|
415
|
-
|
|
416
|
-
const parser = new CSVParserService({ headers: true });
|
|
417
|
-
const records = await parser.parse(csvContent);
|
|
418
|
-
|
|
419
|
-
// XML
|
|
420
|
-
import { XMLParserService } from '@fluentcommerce/fc-connect-sdk';
|
|
421
|
-
|
|
422
|
-
const parser = new XMLParserService();
|
|
423
|
-
const data = await parser.parse(xmlString);
|
|
424
|
-
const value = await parser.parseWithPath(xmlString, 'order.items[0].sku');
|
|
425
|
-
```
|
|
426
|
-
|
|
427
|
-
### Mapping
|
|
428
|
-
|
|
429
|
-
```typescript
|
|
430
|
-
// UniversalMapper (for CSV, JSON, simple XML)
|
|
431
|
-
import { UniversalMapper } from '@fluentcommerce/fc-connect-sdk';
|
|
432
|
-
|
|
433
|
-
const mapper = new UniversalMapper({
|
|
434
|
-
fields: {
|
|
435
|
-
ref: { source: 'sku', required: true },
|
|
436
|
-
onHand: { source: 'qty', resolver: 'sdk.parseInt' },
|
|
437
|
-
},
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
const result = await mapper.map(sourceData);
|
|
441
|
-
|
|
442
|
-
// GraphQLMutationMapper (for complex XML → GraphQL)
|
|
443
|
-
import { GraphQLMutationMapper } from '@fluentcommerce/fc-connect-sdk';
|
|
444
|
-
|
|
445
|
-
const mapper = new GraphQLMutationMapper(mappingConfig, logger, { fluentClient: client });
|
|
446
|
-
const result = await mapper.mapWithNodes(xmlData, customResolvers, context);
|
|
447
|
-
// Query is auto-generated in result.query - no need to call buildMutation()
|
|
448
|
-
```
|
|
449
|
-
|
|
450
|
-
### State Management
|
|
451
|
-
|
|
452
|
-
```typescript
|
|
453
|
-
import { StateService, VersoriKVAdapter, FileKVAdapter } from '@fluentcommerce/fc-connect-sdk';
|
|
454
|
-
|
|
455
|
-
// Versori
|
|
456
|
-
const kvAdapter = new VersoriKVAdapter(openKv());
|
|
457
|
-
|
|
458
|
-
// Standalone
|
|
459
|
-
const kvAdapter = new FileKVAdapter('./state');
|
|
460
|
-
|
|
461
|
-
const stateService = new StateService(logger);
|
|
462
|
-
|
|
463
|
-
await stateService.setState('key', value);
|
|
464
|
-
const value = await stateService.getState('key');
|
|
465
|
-
|
|
466
|
-
await stateService.markFileProcessed('file-key', { processedAt: new Date() });
|
|
467
|
-
const processed = await stateService.isFileProcessed('file-key');
|
|
468
|
-
```
|
|
469
|
-
|
|
470
|
-
### Advanced Integration Services
|
|
471
|
-
|
|
472
|
-
**See Module 6 for complete examples and detailed documentation**
|
|
473
|
-
|
|
474
|
-
```typescript
|
|
475
|
-
// Webhook Validation (Fluent Commerce webhooks only)
|
|
476
|
-
const client = await createClient(ctx); // Auto-detects Versori context
|
|
477
|
-
const isValid = await client.validateWebhook(payload, signature, rawBody);
|
|
478
|
-
|
|
479
|
-
// Partial Batch Recovery
|
|
480
|
-
const recovery = new PartialBatchRecovery(logger);
|
|
481
|
-
const result = await recovery.processBatchWithRecovery(
|
|
482
|
-
records,
|
|
483
|
-
async batch => {
|
|
484
|
-
return await client.sendBatch(jobId, {
|
|
485
|
-
action: 'UPSERT',
|
|
486
|
-
entityType: 'INVENTORY',
|
|
487
|
-
entities: batch,
|
|
488
|
-
});
|
|
489
|
-
},
|
|
490
|
-
{
|
|
491
|
-
maxRetries: 3,
|
|
492
|
-
retryOnlyFailed: true,
|
|
493
|
-
checkpointKey: 'daily-sync',
|
|
494
|
-
}
|
|
495
|
-
);
|
|
496
|
-
|
|
497
|
-
// Job Tracker (for Versori KV storage)
|
|
498
|
-
const tracker = new JobTracker(new VersoriKVAdapter(kv), logger);
|
|
499
|
-
await tracker.createJob(jobId, { triggeredBy: 'schedule', stage: 'start' });
|
|
500
|
-
await tracker.updateJob(jobId, { status: 'processing', stage: 'extraction' });
|
|
501
|
-
await tracker.markCompleted(jobId, { recordCount: 1000 });
|
|
502
|
-
|
|
503
|
-
// Preflight Validator
|
|
504
|
-
const validator = new PreflightValidator(logger);
|
|
505
|
-
const result = await validator.validateBatch(records, {
|
|
506
|
-
entityType: 'INVENTORY',
|
|
507
|
-
requiredFields: ['ref', 'qty'],
|
|
508
|
-
checkDuplicates: true,
|
|
509
|
-
maxBatchSize: 5000,
|
|
510
|
-
});
|
|
511
|
-
```
|
|
512
|
-
|
|
513
|
-
---
|
|
514
|
-
|
|
515
|
-
## SDK Resolvers
|
|
516
|
-
|
|
517
|
-
### Built-in Resolvers
|
|
518
|
-
|
|
519
|
-
```typescript
|
|
520
|
-
// String
|
|
521
|
-
'sdk.uppercase'; // "hello" → "HELLO"
|
|
522
|
-
'sdk.lowercase'; // "HELLO" → "hello"
|
|
523
|
-
'sdk.trim'; // " hello " → "hello"
|
|
524
|
-
'sdk.toString'; // 123 → "123"
|
|
525
|
-
|
|
526
|
-
// Number
|
|
527
|
-
'sdk.parseInt'; // "123" → 123
|
|
528
|
-
'sdk.parseFloat'; // "12.99" → 12.99
|
|
529
|
-
'sdk.number'; // "123" → 123
|
|
530
|
-
|
|
531
|
-
// Date
|
|
532
|
-
'sdk.formatDate'; // "2025-01-15" → "2025-01-15T00:00:00.000Z"
|
|
533
|
-
'sdk.parseDate'; // Parse various date formats
|
|
534
|
-
|
|
535
|
-
// Type
|
|
536
|
-
'sdk.boolean'; // "true" → true
|
|
537
|
-
'sdk.toJson'; // { foo: 'bar' } → '{"foo":"bar"}'
|
|
538
|
-
'sdk.parseJson'; // '{"foo":"bar"}' → { foo: 'bar' }
|
|
539
|
-
|
|
540
|
-
// Utility
|
|
541
|
-
'sdk.identity'; // Returns value unchanged
|
|
542
|
-
'sdk.coalesce'; // Returns first non-null value
|
|
543
|
-
```
|
|
544
|
-
|
|
545
|
-
### Custom Resolvers
|
|
546
|
-
|
|
547
|
-
```typescript
|
|
548
|
-
const customResolvers = {
|
|
549
|
-
'custom.buildRef': (value, data) => {
|
|
550
|
-
return `${data.sku}-${data.location}`;
|
|
551
|
-
},
|
|
552
|
-
|
|
553
|
-
'custom.lookupCustomer': async (value, data, config, helpers) => {
|
|
554
|
-
const result = await helpers.fluentClient.graphql({
|
|
555
|
-
query: CUSTOMER_QUERY,
|
|
556
|
-
variables: { email: data.email },
|
|
557
|
-
});
|
|
558
|
-
return result?.customers?.edges?.[0]?.node?.id;
|
|
559
|
-
},
|
|
560
|
-
};
|
|
561
|
-
|
|
562
|
-
// Use in mapper
|
|
563
|
-
const mapper = new UniversalMapper(mappingConfig, { customResolvers });
|
|
564
|
-
```
|
|
565
|
-
|
|
566
|
-
---
|
|
567
|
-
|
|
568
|
-
## Common GraphQL Patterns
|
|
569
|
-
|
|
570
|
-
### Query with Pagination
|
|
571
|
-
|
|
572
|
-
```typescript
|
|
573
|
-
const result = await client.graphql({
|
|
574
|
-
query: `
|
|
575
|
-
query GetProducts($first: Int!, $after: String) {
|
|
576
|
-
products(first: $first, after: $after) {
|
|
577
|
-
edges {
|
|
578
|
-
node {
|
|
579
|
-
id
|
|
580
|
-
ref
|
|
581
|
-
name
|
|
582
|
-
}
|
|
583
|
-
cursor
|
|
584
|
-
}
|
|
585
|
-
pageInfo {
|
|
586
|
-
hasNextPage
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
`,
|
|
591
|
-
variables: { first: 100 },
|
|
592
|
-
pagination: {
|
|
593
|
-
maxPages: 50,
|
|
594
|
-
maxRecords: 5000,
|
|
595
|
-
onProgress: (page, total) => console.log(`Page ${page}/${total}`),
|
|
596
|
-
},
|
|
597
|
-
});
|
|
598
|
-
|
|
599
|
-
const products = result.data.products.edges.map(e => e.node);
|
|
600
|
-
```
|
|
601
|
-
|
|
602
|
-
### Create Mutation
|
|
603
|
-
|
|
604
|
-
```typescript
|
|
605
|
-
const result = await client.graphql({
|
|
606
|
-
query: `
|
|
607
|
-
mutation CreateOrder($input: CreateOrderInput!) {
|
|
608
|
-
createOrder(input: $input) {
|
|
609
|
-
id
|
|
610
|
-
ref
|
|
611
|
-
status
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
`,
|
|
615
|
-
variables: {
|
|
616
|
-
input: {
|
|
617
|
-
ref: 'ORD-123',
|
|
618
|
-
type: 'HD',
|
|
619
|
-
totalPrice: 99.99,
|
|
620
|
-
currency: 'USD',
|
|
621
|
-
retailer: { id: '2' },
|
|
622
|
-
items: [
|
|
623
|
-
{
|
|
624
|
-
ref: '1',
|
|
625
|
-
productRef: 'SKU-001',
|
|
626
|
-
quantity: 2,
|
|
627
|
-
price: 49.99,
|
|
628
|
-
currency: 'USD',
|
|
629
|
-
},
|
|
630
|
-
],
|
|
631
|
-
},
|
|
632
|
-
},
|
|
633
|
-
});
|
|
634
|
-
|
|
635
|
-
const order = result.data.createOrder;
|
|
636
|
-
```
|
|
637
|
-
|
|
638
|
-
### Update Mutation
|
|
639
|
-
|
|
640
|
-
```typescript
|
|
641
|
-
const result = await client.graphql({
|
|
642
|
-
query: `
|
|
643
|
-
mutation UpdateInventory($input: UpdateInventoryQuantityInput!) {
|
|
644
|
-
updateInventoryQuantity(input: $input) {
|
|
645
|
-
id
|
|
646
|
-
ref
|
|
647
|
-
onHand
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
`,
|
|
651
|
-
variables: {
|
|
652
|
-
input: {
|
|
653
|
-
ref: 'SKU-001-WH01',
|
|
654
|
-
onHand: 100,
|
|
655
|
-
},
|
|
656
|
-
},
|
|
657
|
-
});
|
|
658
|
-
```
|
|
659
|
-
|
|
660
|
-
---
|
|
661
|
-
|
|
662
|
-
## Error Handling Patterns
|
|
663
|
-
|
|
664
|
-
### Error Categories
|
|
665
|
-
|
|
666
|
-
```typescript
|
|
667
|
-
// Transient (RETRY)
|
|
668
|
-
429 - Rate Limit
|
|
669
|
-
500 - Server Error
|
|
670
|
-
503 - Service Unavailable
|
|
671
|
-
504 - Gateway Timeout
|
|
672
|
-
|
|
673
|
-
// Permanent (DON'T RETRY)
|
|
674
|
-
400 - Bad Request
|
|
675
|
-
401 - Unauthorized
|
|
676
|
-
403 - Forbidden
|
|
677
|
-
404 - Not Found
|
|
678
|
-
422 - Unprocessable Entity
|
|
679
|
-
```
|
|
680
|
-
|
|
681
|
-
### Try-Catch Pattern
|
|
682
|
-
|
|
683
|
-
```typescript
|
|
684
|
-
try {
|
|
685
|
-
const result = await client.graphql({ query, variables });
|
|
686
|
-
return { status: 200, body: { orderId: result.id } };
|
|
687
|
-
} catch (error: any) {
|
|
688
|
-
if (error.message.includes('required field')) {
|
|
689
|
-
return { status: 400, body: { error: 'Validation failed' } };
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
if (error.statusCode === 429) {
|
|
693
|
-
return { status: 429, body: { error: 'Rate limit', retryAfter: 60 } };
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
return { status: 500, body: { error: 'Internal error', retryable: true } };
|
|
697
|
-
}
|
|
698
|
-
```
|
|
699
|
-
|
|
700
|
-
### Logging Pattern
|
|
701
|
-
|
|
702
|
-
```typescript
|
|
703
|
-
import {
|
|
704
|
-
createConsoleLogger,
|
|
705
|
-
toStructuredLogger,
|
|
706
|
-
generateCorrelationId,
|
|
707
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
708
|
-
|
|
709
|
-
// Basic console logger
|
|
710
|
-
const logger = createConsoleLogger();
|
|
711
|
-
|
|
712
|
-
// Or with structured context
|
|
713
|
-
const contextLogger = toStructuredLogger(logger, {
|
|
714
|
-
service: 'InventorySync',
|
|
715
|
-
correlationId: generateCorrelationId(),
|
|
716
|
-
});
|
|
717
|
-
|
|
718
|
-
contextLogger.info('Processing file', { fileName: 'inventory.csv', recordCount: 5000 });
|
|
719
|
-
contextLogger.error('Batch failed', error, { jobId: job.id });
|
|
720
|
-
|
|
721
|
-
// For Versori: Use native log from context
|
|
722
|
-
export const workflow = schedule('sync', '0 * * * *').execute(async ({ log }) => {
|
|
723
|
-
log.info('Processing started');
|
|
724
|
-
log.error('Processing failed', error);
|
|
725
|
-
});
|
|
726
|
-
```
|
|
727
|
-
|
|
728
|
-
---
|
|
729
|
-
|
|
730
|
-
## Performance Optimization
|
|
731
|
-
|
|
732
|
-
### Batch Size Guidelines
|
|
733
|
-
|
|
734
|
-
| Total Records | Batch Size | Processing Time |
|
|
735
|
-
| ------------- | ---------- | --------------- |
|
|
736
|
-
| < 1,000 | 100 | 1-2 minutes |
|
|
737
|
-
| 1,000-10,000 | 150 | 3-10 minutes |
|
|
738
|
-
| 10,000-50,000 | 200 | 5-15 minutes |
|
|
739
|
-
| > 50,000 | 250 (max) | 10-30 minutes |
|
|
740
|
-
|
|
741
|
-
### Polling Intervals
|
|
742
|
-
|
|
743
|
-
| Job Size | Poll Interval |
|
|
744
|
-
| -------------- | ------------- |
|
|
745
|
-
| < 10 batches | 10 seconds |
|
|
746
|
-
| 10-100 batches | 30 seconds |
|
|
747
|
-
| > 100 batches | 60 seconds |
|
|
748
|
-
|
|
749
|
-
### Memory Optimization
|
|
750
|
-
|
|
751
|
-
```typescript
|
|
752
|
-
// ❌ BAD - Load entire file
|
|
753
|
-
const allRecords = await parser.parse(csvContent);
|
|
754
|
-
for (const record of allRecords) {
|
|
755
|
-
/* process */
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
// ✅ GOOD - Stream records
|
|
759
|
-
for await (const record of parser.streamParse(csvStream)) {
|
|
760
|
-
await processRecord(record);
|
|
761
|
-
}
|
|
762
|
-
```
|
|
763
|
-
|
|
764
|
-
---
|
|
765
|
-
|
|
766
|
-
## Common Recipes
|
|
767
|
-
|
|
768
|
-
### Recipe: S3 CSV → Batch API (Complete)
|
|
769
|
-
|
|
770
|
-
```typescript
|
|
771
|
-
import {
|
|
772
|
-
createClient,
|
|
773
|
-
S3DataSource,
|
|
774
|
-
CSVParserService,
|
|
775
|
-
UniversalMapper,
|
|
776
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
777
|
-
|
|
778
|
-
async function s3ToBatchAPI() {
|
|
779
|
-
// 1. Download CSV
|
|
780
|
-
const s3 = new S3DataSource(
|
|
781
|
-
{
|
|
782
|
-
type: 'S3_CSV',
|
|
783
|
-
connectionId: 'my-s3',
|
|
784
|
-
name: 'My S3 Source',
|
|
785
|
-
s3Config: {
|
|
786
|
-
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
787
|
-
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
788
|
-
region: process.env.AWS_REGION,
|
|
789
|
-
bucket: process.env.AWS_BUCKET,
|
|
790
|
-
},
|
|
791
|
-
},
|
|
792
|
-
console
|
|
793
|
-
);
|
|
794
|
-
|
|
795
|
-
const csv = await s3.downloadFile('inventory/current.csv');
|
|
796
|
-
|
|
797
|
-
// 2. Parse CSV
|
|
798
|
-
const parser = new CSVParserService({ headers: true });
|
|
799
|
-
const records = await parser.parse(csv);
|
|
800
|
-
|
|
801
|
-
// 3. Map fields
|
|
802
|
-
const mapper = new UniversalMapper(
|
|
803
|
-
{
|
|
804
|
-
fields: {
|
|
805
|
-
ref: { source: 'sku', resolver: 'custom.buildRef' },
|
|
806
|
-
type: { value: 'INVENTORY' },
|
|
807
|
-
productRef: { source: 'sku', required: true },
|
|
808
|
-
locationRef: { source: 'location', required: true },
|
|
809
|
-
onHand: { source: 'qty', resolver: 'sdk.parseInt' },
|
|
810
|
-
},
|
|
811
|
-
},
|
|
812
|
-
{
|
|
813
|
-
customResolvers: {
|
|
814
|
-
'custom.buildRef': (v, d) => `${d.sku}-${d.location}`,
|
|
815
|
-
},
|
|
816
|
-
}
|
|
817
|
-
);
|
|
818
|
-
|
|
819
|
-
const mapped = [];
|
|
820
|
-
for (const r of records) {
|
|
821
|
-
const result = await mapper.map(r);
|
|
822
|
-
if (result.success) mapped.push(result.data);
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
// 4. Create job
|
|
826
|
-
const client = await createClient({
|
|
827
|
-
config: {
|
|
828
|
-
baseUrl: process.env.FLUENT_BASE_URL,
|
|
829
|
-
clientId: process.env.FLUENT_CLIENT_ID,
|
|
830
|
-
clientSecret: process.env.FLUENT_CLIENT_SECRET,
|
|
831
|
-
retailerId: process.env.FLUENT_RETAILER_ID,
|
|
832
|
-
},
|
|
833
|
-
});
|
|
834
|
-
|
|
835
|
-
const job = await client.createJob({ name: 'Inventory Sync', retailerId: '2' });
|
|
836
|
-
|
|
837
|
-
// 5. Send batches
|
|
838
|
-
for (let i = 0; i < mapped.length; i += 100) {
|
|
839
|
-
await client.sendBatch(job.id, {
|
|
840
|
-
entities: mapped.slice(i, i + 100),
|
|
841
|
-
});
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
// 6. Poll status
|
|
845
|
-
let status = await client.getJobStatus(job.id);
|
|
846
|
-
while (status.status === 'PENDING' || status.status === 'PROCESSING') {
|
|
847
|
-
await new Promise(r => setTimeout(r, 30000));
|
|
848
|
-
status = await client.getJobStatus(job.id);
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
// 7. Archive
|
|
852
|
-
if (status.status === 'COMPLETED') {
|
|
853
|
-
await s3.moveFile('inventory/current.csv', 'inventory/archive/current.csv');
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
console.log(`✓ Processed ${mapped.length} records`);
|
|
857
|
-
}
|
|
858
|
-
```
|
|
859
|
-
|
|
860
|
-
### Recipe: Webhook → GraphQL (JSON)
|
|
861
|
-
|
|
862
|
-
```typescript
|
|
863
|
-
import { createClient, UniversalMapper } from '@fluentcommerce/fc-connect-sdk';
|
|
864
|
-
|
|
865
|
-
export default async function jsonWebhook(ctx) {
|
|
866
|
-
const client = await createClient(ctx); // Auto-detects Versori context
|
|
867
|
-
|
|
868
|
-
const mapper = new UniversalMapper({
|
|
869
|
-
fields: {
|
|
870
|
-
ref: { source: 'order_id', required: true },
|
|
871
|
-
type: { value: 'HD' },
|
|
872
|
-
totalPrice: { source: 'total', resolver: 'sdk.parseFloat' },
|
|
873
|
-
currency: { source: 'currency' },
|
|
874
|
-
'retailer.id': { value: '2' },
|
|
875
|
-
},
|
|
876
|
-
});
|
|
877
|
-
|
|
878
|
-
const mapResult = await mapper.map(ctx.activation.body);
|
|
879
|
-
|
|
880
|
-
if (!mapResult.success) {
|
|
881
|
-
return { status: 400, body: { error: 'Mapping failed' } };
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
const order = await client.graphql({
|
|
885
|
-
query: `mutation CreateOrder($input: CreateOrderInput!) {
|
|
886
|
-
createOrder(input: $input) { id ref }
|
|
887
|
-
}`,
|
|
888
|
-
variables: { input: mapResult.data },
|
|
889
|
-
});
|
|
890
|
-
|
|
891
|
-
return { status: 200, body: { orderId: order.id } };
|
|
892
|
-
}
|
|
893
|
-
```
|
|
894
|
-
|
|
895
|
-
### Recipe: Delta Sync (Hash-Based)
|
|
896
|
-
|
|
897
|
-
```typescript
|
|
898
|
-
import crypto from 'crypto';
|
|
899
|
-
import { StateService, FileKVAdapter } from '@fluentcommerce/fc-connect-sdk';
|
|
900
|
-
|
|
901
|
-
function hash(record: any): string {
|
|
902
|
-
const str = `${record.sku}|${record.location}|${record.qty}`;
|
|
903
|
-
return crypto.createHash('md5').update(str).digest('hex');
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
async function hashDeltaSync() {
|
|
907
|
-
const stateService = new StateService(logger);
|
|
908
|
-
|
|
909
|
-
const prevHashes = (await stateService.getState('hashes')) || {};
|
|
910
|
-
const currHashes: Record<string, string> = {};
|
|
911
|
-
const changed = [];
|
|
912
|
-
|
|
913
|
-
const records = await loadAllRecords();
|
|
914
|
-
|
|
915
|
-
for (const record of records) {
|
|
916
|
-
const key = `${record.sku}-${record.location}`;
|
|
917
|
-
const currHash = hash(record);
|
|
918
|
-
currHashes[key] = currHash;
|
|
919
|
-
|
|
920
|
-
if (prevHashes[key] !== currHash) {
|
|
921
|
-
changed.push(record);
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
console.log(`${changed.length} of ${records.length} changed`);
|
|
926
|
-
|
|
927
|
-
await processBatch(changed);
|
|
928
|
-
|
|
929
|
-
await stateService.setState('hashes', currHashes);
|
|
930
|
-
}
|
|
931
|
-
```
|
|
932
|
-
|
|
933
|
-
---
|
|
934
|
-
|
|
935
|
-
## Troubleshooting
|
|
936
|
-
|
|
937
|
-
### Common Issues
|
|
938
|
-
|
|
939
|
-
| Issue | Cause | Solution |
|
|
940
|
-
| -------------------- | ---------------------- | ----------------------------------- |
|
|
941
|
-
| **401 Unauthorized** | Invalid credentials | Check clientId/clientSecret |
|
|
942
|
-
| **429 Rate Limit** | Too many requests | Add retry with backoff |
|
|
943
|
-
| **504 Timeout** | Request too slow | Reduce batch size or use pagination |
|
|
944
|
-
| **Validation Error** | Missing required field | Check mapping config |
|
|
945
|
-
| **Hash mismatch** | State out of sync | Reset state, run full sync |
|
|
946
|
-
|
|
947
|
-
### Debug Commands
|
|
948
|
-
|
|
949
|
-
```bash
|
|
950
|
-
# Introspect GraphQL schema
|
|
951
|
-
npx @fluentcommerce/fc-connect-sdk introspect-schema --url https://api.fluentcommerce.com/graphql
|
|
952
|
-
|
|
953
|
-
# Validate mapping config
|
|
954
|
-
npx @fluentcommerce/fc-connect-sdk validate-schema --mapping mappings/config.json
|
|
955
|
-
|
|
956
|
-
# Test Deno compatibility
|
|
957
|
-
npm run test:deno
|
|
958
|
-
```
|
|
959
|
-
|
|
960
|
-
---
|
|
961
|
-
|
|
962
|
-
[← Back to Index](./integration-patterns-readme.md) | [View Complete Modules](./modules/)
|
|
1
|
+
# Integration Patterns - Quick Reference
|
|
2
|
+
|
|
3
|
+
> Cheat sheet for common integration patterns with the Fluent Connect SDK
|
|
4
|
+
|
|
5
|
+
## Pattern Selection Guide
|
|
6
|
+
|
|
7
|
+
### Decision Tree
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
What's your integration scenario?
|
|
11
|
+
|
|
12
|
+
├─ Single event (< 10/hour)
|
|
13
|
+
│ └─ Use: Real-Time Processing (Module 1)
|
|
14
|
+
│ └─ Pattern: Webhook → GraphQL mutation
|
|
15
|
+
│
|
|
16
|
+
├─ Multiple events (10-1000/hour)
|
|
17
|
+
│ ├─ Immediate response required?
|
|
18
|
+
│ │ ├─ YES → Real-Time Processing (Module 1)
|
|
19
|
+
│ │ └─ NO → Batch Processing (Module 2)
|
|
20
|
+
│ │
|
|
21
|
+
│ └─ Pattern: Hourly batch jobs
|
|
22
|
+
│
|
|
23
|
+
├─ Large files (1K+ records)
|
|
24
|
+
│ └─ Use: Batch Processing (Module 2)
|
|
25
|
+
│ └─ Pattern: S3 CSV → Batch API
|
|
26
|
+
│
|
|
27
|
+
└─ Frequent syncs (hourly/15min)
|
|
28
|
+
└─ Use: Delta Sync (Module 3)
|
|
29
|
+
└─ Pattern: Timestamp-based change detection
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Quick Patterns
|
|
35
|
+
|
|
36
|
+
### Real-Time: JSON Webhook → Fluent
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { createClient } from '@fluentcommerce/fc-connect-sdk';
|
|
40
|
+
|
|
41
|
+
export default async function webhook(ctx) {
|
|
42
|
+
const client = await createClient(ctx); // Auto-detects Versori context
|
|
43
|
+
|
|
44
|
+
const result = await client.graphql({
|
|
45
|
+
query: `mutation CreateOrder($input: CreateOrderInput!) {
|
|
46
|
+
createOrder(input: $input) { id ref }
|
|
47
|
+
}`,
|
|
48
|
+
variables: { input: mapData(activation.body) },
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return { status: 200, body: { orderId: result.id } };
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Use when**: < 100 events/hour, need < 5 sec response
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
### Batch: CSV File → Fluent Batch API
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import {
|
|
63
|
+
createClient,
|
|
64
|
+
S3DataSource,
|
|
65
|
+
CSVParserService,
|
|
66
|
+
UniversalMapper,
|
|
67
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
68
|
+
|
|
69
|
+
async function batchSync() {
|
|
70
|
+
// Download CSV
|
|
71
|
+
const s3 = new S3DataSource(config, console);
|
|
72
|
+
const csv = await s3.downloadFile('inventory.csv');
|
|
73
|
+
|
|
74
|
+
// Parse
|
|
75
|
+
const parser = new CSVParserService({ headers: true });
|
|
76
|
+
const records = await parser.parse(csv);
|
|
77
|
+
|
|
78
|
+
// Map
|
|
79
|
+
const mapper = new UniversalMapper(mappingConfig);
|
|
80
|
+
const mapped = records.map(r => mapper.map(r).data);
|
|
81
|
+
|
|
82
|
+
// Batch API
|
|
83
|
+
const client = await createClient({ config });
|
|
84
|
+
const job = await client.createJob({ name: 'Sync', retailerId: '2' });
|
|
85
|
+
|
|
86
|
+
for (let i = 0; i < mapped.length; i += 100) {
|
|
87
|
+
await client.sendBatch(job.id, {
|
|
88
|
+
entities: mapped.slice(i, i + 100),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Poll
|
|
93
|
+
let status = await client.getJobStatus(job.id);
|
|
94
|
+
while (status.status === 'PROCESSING') {
|
|
95
|
+
await sleep(30000);
|
|
96
|
+
status = await client.getJobStatus(job.id);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Use when**: 1K+ records, scheduled processing, non-critical
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
### Delta Sync: Timestamp-Based
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
import { StateService, FileKVAdapter } from '@fluentcommerce/fc-connect-sdk';
|
|
109
|
+
|
|
110
|
+
async function deltaSync() {
|
|
111
|
+
const stateService = new StateService(logger);
|
|
112
|
+
|
|
113
|
+
// Get last sync time
|
|
114
|
+
let lastSync = (await stateService.getState('last-sync')) || new Date(0).toISOString();
|
|
115
|
+
|
|
116
|
+
// Filter changed records
|
|
117
|
+
const allRecords = await loadAllRecords();
|
|
118
|
+
const changed = allRecords.filter(r => new Date(r.lastModified).toISOString() > lastSync);
|
|
119
|
+
|
|
120
|
+
console.log(`${changed.length} of ${allRecords.length} changed`);
|
|
121
|
+
|
|
122
|
+
// Process only changed
|
|
123
|
+
await processBatch(changed);
|
|
124
|
+
|
|
125
|
+
// Update timestamp
|
|
126
|
+
await stateService.setState('last-sync', new Date().toISOString());
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Use when**: Frequent syncs, large datasets, < 5% change rate
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
### Webhook: XML → GraphQL
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import { GraphQLMutationMapper } from '@fluentcommerce/fc-connect-sdk';
|
|
138
|
+
|
|
139
|
+
const mappingConfig = {
|
|
140
|
+
mutation: 'createOrder',
|
|
141
|
+
nodes: {
|
|
142
|
+
radial: {
|
|
143
|
+
extract: 'orders.order.custom-attribute[attribute-id=CreateOrderServiceRequest]',
|
|
144
|
+
parse: 'xml',
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
fields: {
|
|
148
|
+
ref: { source: '$radial.Order@customerOrderId' },
|
|
149
|
+
totalPrice: { source: '$radial.Order.Payment.Amount', resolver: 'sdk.parseFloat' },
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const mapper = new GraphQLMutationMapper(mappingConfig, log, { fluentClient: client });
|
|
154
|
+
const result = await mapper.mapWithNodes(xmlData, customResolvers, context);
|
|
155
|
+
|
|
156
|
+
// Check success (errors are returned, not thrown)
|
|
157
|
+
if (!result.success) {
|
|
158
|
+
console.error('Mapping failed:', result.errors);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Execute mutation (query is auto-generated in result)
|
|
163
|
+
await client.graphql({ query: result.query, variables: result.variables }); // ✅ Use variables
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Use when**: SFCC, Radial, SOAP webhooks with nested XML
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
### Error Handling: Retry with Backoff
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
async function retryWithBackoff<T>(fn: () => Promise<T>, maxRetries = 5): Promise<T> {
|
|
174
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
175
|
+
try {
|
|
176
|
+
return await fn();
|
|
177
|
+
} catch (error: any) {
|
|
178
|
+
if (error.statusCode < 500) throw error; // Don't retry client errors
|
|
179
|
+
|
|
180
|
+
if (attempt < maxRetries) {
|
|
181
|
+
const delay = Math.min(1000 * Math.pow(2, attempt), 30000);
|
|
182
|
+
await sleep(delay);
|
|
183
|
+
} else {
|
|
184
|
+
throw error;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Usage
|
|
191
|
+
const result = await retryWithBackoff(() => client.graphql({ query, variables }));
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**Use when**: Network errors, rate limits, server errors
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
### Partial Batch Recovery
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
import { PartialBatchRecovery, createClient } from '@fluentcommerce/fc-connect-sdk';
|
|
202
|
+
|
|
203
|
+
async function resilientBatchSync() {
|
|
204
|
+
const client = await createClient({ config });
|
|
205
|
+
const recovery = new PartialBatchRecovery(logger);
|
|
206
|
+
|
|
207
|
+
// Process batch with automatic recovery
|
|
208
|
+
const result = await recovery.processBatchWithRecovery(
|
|
209
|
+
records,
|
|
210
|
+
async batch => {
|
|
211
|
+
// Your batch processing logic
|
|
212
|
+
return await client.sendBatch(jobId, {
|
|
213
|
+
action: 'UPSERT',
|
|
214
|
+
entityType: 'INVENTORY',
|
|
215
|
+
entities: batch,
|
|
216
|
+
});
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
maxRetries: 3,
|
|
220
|
+
retryOnlyFailed: true, // Only retry failed records
|
|
221
|
+
retryDelayMs: 1000,
|
|
222
|
+
checkpointKey: 'daily-inventory-sync',
|
|
223
|
+
}
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
console.log(`Success: ${result.successCount}/${result.totalRecords}`);
|
|
227
|
+
console.log(`Failed: ${result.failedCount} records`);
|
|
228
|
+
|
|
229
|
+
if (result.failedCount > 0) {
|
|
230
|
+
console.error('Failed records:', result.failedRecords);
|
|
231
|
+
console.log(`Checkpoint saved: ${result.checkpointId}`);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Later: Resume from checkpoint
|
|
235
|
+
if (result.checkpointId) {
|
|
236
|
+
const resumeResult = await recovery.resumeFromCheckpoint(result.checkpointId, processBatch, {
|
|
237
|
+
maxRetries: 5,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**Use when**: Sending 50+ batches, unstable network, long-running jobs
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
### Job Tracker
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
import { JobTracker, VersoriKVAdapter, createClient } from '@fluentcommerce/fc-connect-sdk';
|
|
251
|
+
|
|
252
|
+
async function batchSyncWithTracking(kv, logger) {
|
|
253
|
+
const client = await createClient({ config });
|
|
254
|
+
const tracker = new JobTracker(new VersoriKVAdapter(kv), logger);
|
|
255
|
+
|
|
256
|
+
const jobId = `batch_${Date.now()}`;
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
// Create job
|
|
260
|
+
await tracker.createJob(jobId, {
|
|
261
|
+
triggeredBy: 'schedule',
|
|
262
|
+
stage: 'initialization',
|
|
263
|
+
details: {
|
|
264
|
+
fileName: 'inventory.csv',
|
|
265
|
+
recordCount: 5000,
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// Update progress
|
|
270
|
+
await tracker.updateJob(jobId, {
|
|
271
|
+
status: 'processing',
|
|
272
|
+
stage: 'extraction',
|
|
273
|
+
message: 'Extracting records',
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// Create Fluent job
|
|
277
|
+
const batchJob = await client.createJob({
|
|
278
|
+
name: 'Daily Inventory Sync',
|
|
279
|
+
retailerId: '2',
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
await tracker.updateJob(jobId, {
|
|
283
|
+
stage: 'ingestion',
|
|
284
|
+
message: `Sending batches`,
|
|
285
|
+
details: { fluentJobId: batchJob.id },
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// Send batches
|
|
289
|
+
for (let i = 0; i < batches.length; i++) {
|
|
290
|
+
await client.sendBatch(batchJob.id, { entities: batches[i] });
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Complete
|
|
294
|
+
await tracker.markCompleted(jobId, {
|
|
295
|
+
batchesSent: batches.length,
|
|
296
|
+
fluentJobId: batchJob.id,
|
|
297
|
+
});
|
|
298
|
+
} catch (error) {
|
|
299
|
+
await tracker.markFailed(jobId, error);
|
|
300
|
+
throw error;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Query job status
|
|
305
|
+
const status = await tracker.getJob(jobId);
|
|
306
|
+
console.log(`Job ${jobId}: ${status.status} at stage ${status.stage}`);
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
**Use when**: Need job history, monitoring, debugging, performance tracking
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
### Preflight Validator: Validate Before API
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
import { PreflightValidator } from '@fluentcommerce/fc-connect-sdk';
|
|
317
|
+
|
|
318
|
+
const validator = new PreflightValidator(logger);
|
|
319
|
+
|
|
320
|
+
// Validate before sending to API
|
|
321
|
+
const result = await validator.validateBatch(records, {
|
|
322
|
+
entityType: 'INVENTORY',
|
|
323
|
+
requiredFields: ['ref', 'qty', 'productRef'],
|
|
324
|
+
checkDuplicates: true,
|
|
325
|
+
maxBatchSize: 5000,
|
|
326
|
+
validateTypes: true,
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
if (!result.isValid) {
|
|
330
|
+
console.error(`Validation failed: ${result.errors.length} errors`);
|
|
331
|
+
throw new Error(`Validation failed: ${result.summary}`);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Safe to send to API (saves quota)
|
|
335
|
+
await sendToFluentAPI(records);
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
**Use when**: Large batch operations, cost optimization
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## SDK Components Cheat Sheet
|
|
343
|
+
|
|
344
|
+
### Client Creation
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
// Versori
|
|
348
|
+
const client = await createClient(ctx); // Auto-detects Versori context
|
|
349
|
+
|
|
350
|
+
// Standalone
|
|
351
|
+
const client = await createClient({
|
|
352
|
+
config: {
|
|
353
|
+
baseUrl: 'https://api.fluentcommerce.com',
|
|
354
|
+
clientId: process.env.FLUENT_CLIENT_ID,
|
|
355
|
+
clientSecret: process.env.FLUENT_CLIENT_SECRET,
|
|
356
|
+
retailerId: process.env.FLUENT_RETAILER_ID,
|
|
357
|
+
},
|
|
358
|
+
});
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Client Methods
|
|
362
|
+
|
|
363
|
+
```typescript
|
|
364
|
+
// Query (simple)
|
|
365
|
+
const data = await client.graphql({ query, variables });
|
|
366
|
+
|
|
367
|
+
// Mutation (simple)
|
|
368
|
+
const data = await client.graphql({ query, variables });
|
|
369
|
+
|
|
370
|
+
// GraphQL (advanced - with pagination)
|
|
371
|
+
const result = await client.graphql({
|
|
372
|
+
query,
|
|
373
|
+
variables,
|
|
374
|
+
pagination: { maxPages: 50 },
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
// Batch API
|
|
378
|
+
const job = await client.createJob({ name: 'Job', retailerId: '2' });
|
|
379
|
+
const batch = await client.sendBatch(job.id, { entities: records });
|
|
380
|
+
const status = await client.getJobStatus(job.id);
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Data Sources
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
// S3
|
|
387
|
+
import { S3DataSource } from '@fluentcommerce/fc-connect-sdk';
|
|
388
|
+
|
|
389
|
+
const s3 = new S3DataSource(
|
|
390
|
+
{
|
|
391
|
+
type: 'S3_CSV',
|
|
392
|
+
connectionId: 'my-s3',
|
|
393
|
+
name: 'My S3 Source',
|
|
394
|
+
s3Config: {
|
|
395
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
396
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
397
|
+
region: 'us-east-1',
|
|
398
|
+
bucket: 'my-bucket',
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
logger
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
const files = await s3.listFiles('prefix/');
|
|
405
|
+
const content = await s3.downloadFile('file.csv');
|
|
406
|
+
await s3.uploadFile('output.json', data);
|
|
407
|
+
await s3.moveFile('source.csv', 'archive/source.csv');
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### Parsers
|
|
411
|
+
|
|
412
|
+
```typescript
|
|
413
|
+
// CSV
|
|
414
|
+
import { CSVParserService } from '@fluentcommerce/fc-connect-sdk';
|
|
415
|
+
|
|
416
|
+
const parser = new CSVParserService({ headers: true });
|
|
417
|
+
const records = await parser.parse(csvContent);
|
|
418
|
+
|
|
419
|
+
// XML
|
|
420
|
+
import { XMLParserService } from '@fluentcommerce/fc-connect-sdk';
|
|
421
|
+
|
|
422
|
+
const parser = new XMLParserService();
|
|
423
|
+
const data = await parser.parse(xmlString);
|
|
424
|
+
const value = await parser.parseWithPath(xmlString, 'order.items[0].sku');
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
### Mapping
|
|
428
|
+
|
|
429
|
+
```typescript
|
|
430
|
+
// UniversalMapper (for CSV, JSON, simple XML)
|
|
431
|
+
import { UniversalMapper } from '@fluentcommerce/fc-connect-sdk';
|
|
432
|
+
|
|
433
|
+
const mapper = new UniversalMapper({
|
|
434
|
+
fields: {
|
|
435
|
+
ref: { source: 'sku', required: true },
|
|
436
|
+
onHand: { source: 'qty', resolver: 'sdk.parseInt' },
|
|
437
|
+
},
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
const result = await mapper.map(sourceData);
|
|
441
|
+
|
|
442
|
+
// GraphQLMutationMapper (for complex XML → GraphQL)
|
|
443
|
+
import { GraphQLMutationMapper } from '@fluentcommerce/fc-connect-sdk';
|
|
444
|
+
|
|
445
|
+
const mapper = new GraphQLMutationMapper(mappingConfig, logger, { fluentClient: client });
|
|
446
|
+
const result = await mapper.mapWithNodes(xmlData, customResolvers, context);
|
|
447
|
+
// Query is auto-generated in result.query - no need to call buildMutation()
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
### State Management
|
|
451
|
+
|
|
452
|
+
```typescript
|
|
453
|
+
import { StateService, VersoriKVAdapter, FileKVAdapter } from '@fluentcommerce/fc-connect-sdk';
|
|
454
|
+
|
|
455
|
+
// Versori
|
|
456
|
+
const kvAdapter = new VersoriKVAdapter(openKv());
|
|
457
|
+
|
|
458
|
+
// Standalone
|
|
459
|
+
const kvAdapter = new FileKVAdapter('./state');
|
|
460
|
+
|
|
461
|
+
const stateService = new StateService(logger);
|
|
462
|
+
|
|
463
|
+
await stateService.setState('key', value);
|
|
464
|
+
const value = await stateService.getState('key');
|
|
465
|
+
|
|
466
|
+
await stateService.markFileProcessed('file-key', { processedAt: new Date() });
|
|
467
|
+
const processed = await stateService.isFileProcessed('file-key');
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
### Advanced Integration Services
|
|
471
|
+
|
|
472
|
+
**See Module 6 for complete examples and detailed documentation**
|
|
473
|
+
|
|
474
|
+
```typescript
|
|
475
|
+
// Webhook Validation (Fluent Commerce webhooks only)
|
|
476
|
+
const client = await createClient(ctx); // Auto-detects Versori context
|
|
477
|
+
const isValid = await client.validateWebhook(payload, signature, rawBody);
|
|
478
|
+
|
|
479
|
+
// Partial Batch Recovery
|
|
480
|
+
const recovery = new PartialBatchRecovery(logger);
|
|
481
|
+
const result = await recovery.processBatchWithRecovery(
|
|
482
|
+
records,
|
|
483
|
+
async batch => {
|
|
484
|
+
return await client.sendBatch(jobId, {
|
|
485
|
+
action: 'UPSERT',
|
|
486
|
+
entityType: 'INVENTORY',
|
|
487
|
+
entities: batch,
|
|
488
|
+
});
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
maxRetries: 3,
|
|
492
|
+
retryOnlyFailed: true,
|
|
493
|
+
checkpointKey: 'daily-sync',
|
|
494
|
+
}
|
|
495
|
+
);
|
|
496
|
+
|
|
497
|
+
// Job Tracker (for Versori KV storage)
|
|
498
|
+
const tracker = new JobTracker(new VersoriKVAdapter(kv), logger);
|
|
499
|
+
await tracker.createJob(jobId, { triggeredBy: 'schedule', stage: 'start' });
|
|
500
|
+
await tracker.updateJob(jobId, { status: 'processing', stage: 'extraction' });
|
|
501
|
+
await tracker.markCompleted(jobId, { recordCount: 1000 });
|
|
502
|
+
|
|
503
|
+
// Preflight Validator
|
|
504
|
+
const validator = new PreflightValidator(logger);
|
|
505
|
+
const result = await validator.validateBatch(records, {
|
|
506
|
+
entityType: 'INVENTORY',
|
|
507
|
+
requiredFields: ['ref', 'qty'],
|
|
508
|
+
checkDuplicates: true,
|
|
509
|
+
maxBatchSize: 5000,
|
|
510
|
+
});
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
---
|
|
514
|
+
|
|
515
|
+
## SDK Resolvers
|
|
516
|
+
|
|
517
|
+
### Built-in Resolvers
|
|
518
|
+
|
|
519
|
+
```typescript
|
|
520
|
+
// String
|
|
521
|
+
'sdk.uppercase'; // "hello" → "HELLO"
|
|
522
|
+
'sdk.lowercase'; // "HELLO" → "hello"
|
|
523
|
+
'sdk.trim'; // " hello " → "hello"
|
|
524
|
+
'sdk.toString'; // 123 → "123"
|
|
525
|
+
|
|
526
|
+
// Number
|
|
527
|
+
'sdk.parseInt'; // "123" → 123
|
|
528
|
+
'sdk.parseFloat'; // "12.99" → 12.99
|
|
529
|
+
'sdk.number'; // "123" → 123
|
|
530
|
+
|
|
531
|
+
// Date
|
|
532
|
+
'sdk.formatDate'; // "2025-01-15" → "2025-01-15T00:00:00.000Z"
|
|
533
|
+
'sdk.parseDate'; // Parse various date formats
|
|
534
|
+
|
|
535
|
+
// Type
|
|
536
|
+
'sdk.boolean'; // "true" → true
|
|
537
|
+
'sdk.toJson'; // { foo: 'bar' } → '{"foo":"bar"}'
|
|
538
|
+
'sdk.parseJson'; // '{"foo":"bar"}' → { foo: 'bar' }
|
|
539
|
+
|
|
540
|
+
// Utility
|
|
541
|
+
'sdk.identity'; // Returns value unchanged
|
|
542
|
+
'sdk.coalesce'; // Returns first non-null value
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
### Custom Resolvers
|
|
546
|
+
|
|
547
|
+
```typescript
|
|
548
|
+
const customResolvers = {
|
|
549
|
+
'custom.buildRef': (value, data) => {
|
|
550
|
+
return `${data.sku}-${data.location}`;
|
|
551
|
+
},
|
|
552
|
+
|
|
553
|
+
'custom.lookupCustomer': async (value, data, config, helpers) => {
|
|
554
|
+
const result = await helpers.fluentClient.graphql({
|
|
555
|
+
query: CUSTOMER_QUERY,
|
|
556
|
+
variables: { email: data.email },
|
|
557
|
+
});
|
|
558
|
+
return result?.customers?.edges?.[0]?.node?.id;
|
|
559
|
+
},
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
// Use in mapper
|
|
563
|
+
const mapper = new UniversalMapper(mappingConfig, { customResolvers });
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
---
|
|
567
|
+
|
|
568
|
+
## Common GraphQL Patterns
|
|
569
|
+
|
|
570
|
+
### Query with Pagination
|
|
571
|
+
|
|
572
|
+
```typescript
|
|
573
|
+
const result = await client.graphql({
|
|
574
|
+
query: `
|
|
575
|
+
query GetProducts($first: Int!, $after: String) {
|
|
576
|
+
products(first: $first, after: $after) {
|
|
577
|
+
edges {
|
|
578
|
+
node {
|
|
579
|
+
id
|
|
580
|
+
ref
|
|
581
|
+
name
|
|
582
|
+
}
|
|
583
|
+
cursor
|
|
584
|
+
}
|
|
585
|
+
pageInfo {
|
|
586
|
+
hasNextPage
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
`,
|
|
591
|
+
variables: { first: 100 },
|
|
592
|
+
pagination: {
|
|
593
|
+
maxPages: 50,
|
|
594
|
+
maxRecords: 5000,
|
|
595
|
+
onProgress: (page, total) => console.log(`Page ${page}/${total}`),
|
|
596
|
+
},
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
const products = result.data.products.edges.map(e => e.node);
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
### Create Mutation
|
|
603
|
+
|
|
604
|
+
```typescript
|
|
605
|
+
const result = await client.graphql({
|
|
606
|
+
query: `
|
|
607
|
+
mutation CreateOrder($input: CreateOrderInput!) {
|
|
608
|
+
createOrder(input: $input) {
|
|
609
|
+
id
|
|
610
|
+
ref
|
|
611
|
+
status
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
`,
|
|
615
|
+
variables: {
|
|
616
|
+
input: {
|
|
617
|
+
ref: 'ORD-123',
|
|
618
|
+
type: 'HD',
|
|
619
|
+
totalPrice: 99.99,
|
|
620
|
+
currency: 'USD',
|
|
621
|
+
retailer: { id: '2' },
|
|
622
|
+
items: [
|
|
623
|
+
{
|
|
624
|
+
ref: '1',
|
|
625
|
+
productRef: 'SKU-001',
|
|
626
|
+
quantity: 2,
|
|
627
|
+
price: 49.99,
|
|
628
|
+
currency: 'USD',
|
|
629
|
+
},
|
|
630
|
+
],
|
|
631
|
+
},
|
|
632
|
+
},
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
const order = result.data.createOrder;
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
### Update Mutation
|
|
639
|
+
|
|
640
|
+
```typescript
|
|
641
|
+
const result = await client.graphql({
|
|
642
|
+
query: `
|
|
643
|
+
mutation UpdateInventory($input: UpdateInventoryQuantityInput!) {
|
|
644
|
+
updateInventoryQuantity(input: $input) {
|
|
645
|
+
id
|
|
646
|
+
ref
|
|
647
|
+
onHand
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
`,
|
|
651
|
+
variables: {
|
|
652
|
+
input: {
|
|
653
|
+
ref: 'SKU-001-WH01',
|
|
654
|
+
onHand: 100,
|
|
655
|
+
},
|
|
656
|
+
},
|
|
657
|
+
});
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
---
|
|
661
|
+
|
|
662
|
+
## Error Handling Patterns
|
|
663
|
+
|
|
664
|
+
### Error Categories
|
|
665
|
+
|
|
666
|
+
```typescript
|
|
667
|
+
// Transient (RETRY)
|
|
668
|
+
429 - Rate Limit
|
|
669
|
+
500 - Server Error
|
|
670
|
+
503 - Service Unavailable
|
|
671
|
+
504 - Gateway Timeout
|
|
672
|
+
|
|
673
|
+
// Permanent (DON'T RETRY)
|
|
674
|
+
400 - Bad Request
|
|
675
|
+
401 - Unauthorized
|
|
676
|
+
403 - Forbidden
|
|
677
|
+
404 - Not Found
|
|
678
|
+
422 - Unprocessable Entity
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
### Try-Catch Pattern
|
|
682
|
+
|
|
683
|
+
```typescript
|
|
684
|
+
try {
|
|
685
|
+
const result = await client.graphql({ query, variables });
|
|
686
|
+
return { status: 200, body: { orderId: result.id } };
|
|
687
|
+
} catch (error: any) {
|
|
688
|
+
if (error.message.includes('required field')) {
|
|
689
|
+
return { status: 400, body: { error: 'Validation failed' } };
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
if (error.statusCode === 429) {
|
|
693
|
+
return { status: 429, body: { error: 'Rate limit', retryAfter: 60 } };
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
return { status: 500, body: { error: 'Internal error', retryable: true } };
|
|
697
|
+
}
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
### Logging Pattern
|
|
701
|
+
|
|
702
|
+
```typescript
|
|
703
|
+
import {
|
|
704
|
+
createConsoleLogger,
|
|
705
|
+
toStructuredLogger,
|
|
706
|
+
generateCorrelationId,
|
|
707
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
708
|
+
|
|
709
|
+
// Basic console logger
|
|
710
|
+
const logger = createConsoleLogger();
|
|
711
|
+
|
|
712
|
+
// Or with structured context
|
|
713
|
+
const contextLogger = toStructuredLogger(logger, {
|
|
714
|
+
service: 'InventorySync',
|
|
715
|
+
correlationId: generateCorrelationId(),
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
contextLogger.info('Processing file', { fileName: 'inventory.csv', recordCount: 5000 });
|
|
719
|
+
contextLogger.error('Batch failed', error, { jobId: job.id });
|
|
720
|
+
|
|
721
|
+
// For Versori: Use native log from context
|
|
722
|
+
export const workflow = schedule('sync', '0 * * * *').execute(async ({ log }) => {
|
|
723
|
+
log.info('Processing started');
|
|
724
|
+
log.error('Processing failed', error);
|
|
725
|
+
});
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
---
|
|
729
|
+
|
|
730
|
+
## Performance Optimization
|
|
731
|
+
|
|
732
|
+
### Batch Size Guidelines
|
|
733
|
+
|
|
734
|
+
| Total Records | Batch Size | Processing Time |
|
|
735
|
+
| ------------- | ---------- | --------------- |
|
|
736
|
+
| < 1,000 | 100 | 1-2 minutes |
|
|
737
|
+
| 1,000-10,000 | 150 | 3-10 minutes |
|
|
738
|
+
| 10,000-50,000 | 200 | 5-15 minutes |
|
|
739
|
+
| > 50,000 | 250 (max) | 10-30 minutes |
|
|
740
|
+
|
|
741
|
+
### Polling Intervals
|
|
742
|
+
|
|
743
|
+
| Job Size | Poll Interval |
|
|
744
|
+
| -------------- | ------------- |
|
|
745
|
+
| < 10 batches | 10 seconds |
|
|
746
|
+
| 10-100 batches | 30 seconds |
|
|
747
|
+
| > 100 batches | 60 seconds |
|
|
748
|
+
|
|
749
|
+
### Memory Optimization
|
|
750
|
+
|
|
751
|
+
```typescript
|
|
752
|
+
// ❌ BAD - Load entire file
|
|
753
|
+
const allRecords = await parser.parse(csvContent);
|
|
754
|
+
for (const record of allRecords) {
|
|
755
|
+
/* process */
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// ✅ GOOD - Stream records
|
|
759
|
+
for await (const record of parser.streamParse(csvStream)) {
|
|
760
|
+
await processRecord(record);
|
|
761
|
+
}
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
---
|
|
765
|
+
|
|
766
|
+
## Common Recipes
|
|
767
|
+
|
|
768
|
+
### Recipe: S3 CSV → Batch API (Complete)
|
|
769
|
+
|
|
770
|
+
```typescript
|
|
771
|
+
import {
|
|
772
|
+
createClient,
|
|
773
|
+
S3DataSource,
|
|
774
|
+
CSVParserService,
|
|
775
|
+
UniversalMapper,
|
|
776
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
777
|
+
|
|
778
|
+
async function s3ToBatchAPI() {
|
|
779
|
+
// 1. Download CSV
|
|
780
|
+
const s3 = new S3DataSource(
|
|
781
|
+
{
|
|
782
|
+
type: 'S3_CSV',
|
|
783
|
+
connectionId: 'my-s3',
|
|
784
|
+
name: 'My S3 Source',
|
|
785
|
+
s3Config: {
|
|
786
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
787
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
788
|
+
region: process.env.AWS_REGION,
|
|
789
|
+
bucket: process.env.AWS_BUCKET,
|
|
790
|
+
},
|
|
791
|
+
},
|
|
792
|
+
console
|
|
793
|
+
);
|
|
794
|
+
|
|
795
|
+
const csv = await s3.downloadFile('inventory/current.csv');
|
|
796
|
+
|
|
797
|
+
// 2. Parse CSV
|
|
798
|
+
const parser = new CSVParserService({ headers: true });
|
|
799
|
+
const records = await parser.parse(csv);
|
|
800
|
+
|
|
801
|
+
// 3. Map fields
|
|
802
|
+
const mapper = new UniversalMapper(
|
|
803
|
+
{
|
|
804
|
+
fields: {
|
|
805
|
+
ref: { source: 'sku', resolver: 'custom.buildRef' },
|
|
806
|
+
type: { value: 'INVENTORY' },
|
|
807
|
+
productRef: { source: 'sku', required: true },
|
|
808
|
+
locationRef: { source: 'location', required: true },
|
|
809
|
+
onHand: { source: 'qty', resolver: 'sdk.parseInt' },
|
|
810
|
+
},
|
|
811
|
+
},
|
|
812
|
+
{
|
|
813
|
+
customResolvers: {
|
|
814
|
+
'custom.buildRef': (v, d) => `${d.sku}-${d.location}`,
|
|
815
|
+
},
|
|
816
|
+
}
|
|
817
|
+
);
|
|
818
|
+
|
|
819
|
+
const mapped = [];
|
|
820
|
+
for (const r of records) {
|
|
821
|
+
const result = await mapper.map(r);
|
|
822
|
+
if (result.success) mapped.push(result.data);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// 4. Create job
|
|
826
|
+
const client = await createClient({
|
|
827
|
+
config: {
|
|
828
|
+
baseUrl: process.env.FLUENT_BASE_URL,
|
|
829
|
+
clientId: process.env.FLUENT_CLIENT_ID,
|
|
830
|
+
clientSecret: process.env.FLUENT_CLIENT_SECRET,
|
|
831
|
+
retailerId: process.env.FLUENT_RETAILER_ID,
|
|
832
|
+
},
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
const job = await client.createJob({ name: 'Inventory Sync', retailerId: '2' });
|
|
836
|
+
|
|
837
|
+
// 5. Send batches
|
|
838
|
+
for (let i = 0; i < mapped.length; i += 100) {
|
|
839
|
+
await client.sendBatch(job.id, {
|
|
840
|
+
entities: mapped.slice(i, i + 100),
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
// 6. Poll status
|
|
845
|
+
let status = await client.getJobStatus(job.id);
|
|
846
|
+
while (status.status === 'PENDING' || status.status === 'PROCESSING') {
|
|
847
|
+
await new Promise(r => setTimeout(r, 30000));
|
|
848
|
+
status = await client.getJobStatus(job.id);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// 7. Archive
|
|
852
|
+
if (status.status === 'COMPLETED') {
|
|
853
|
+
await s3.moveFile('inventory/current.csv', 'inventory/archive/current.csv');
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
console.log(`✓ Processed ${mapped.length} records`);
|
|
857
|
+
}
|
|
858
|
+
```
|
|
859
|
+
|
|
860
|
+
### Recipe: Webhook → GraphQL (JSON)
|
|
861
|
+
|
|
862
|
+
```typescript
|
|
863
|
+
import { createClient, UniversalMapper } from '@fluentcommerce/fc-connect-sdk';
|
|
864
|
+
|
|
865
|
+
export default async function jsonWebhook(ctx) {
|
|
866
|
+
const client = await createClient(ctx); // Auto-detects Versori context
|
|
867
|
+
|
|
868
|
+
const mapper = new UniversalMapper({
|
|
869
|
+
fields: {
|
|
870
|
+
ref: { source: 'order_id', required: true },
|
|
871
|
+
type: { value: 'HD' },
|
|
872
|
+
totalPrice: { source: 'total', resolver: 'sdk.parseFloat' },
|
|
873
|
+
currency: { source: 'currency' },
|
|
874
|
+
'retailer.id': { value: '2' },
|
|
875
|
+
},
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
const mapResult = await mapper.map(ctx.activation.body);
|
|
879
|
+
|
|
880
|
+
if (!mapResult.success) {
|
|
881
|
+
return { status: 400, body: { error: 'Mapping failed' } };
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
const order = await client.graphql({
|
|
885
|
+
query: `mutation CreateOrder($input: CreateOrderInput!) {
|
|
886
|
+
createOrder(input: $input) { id ref }
|
|
887
|
+
}`,
|
|
888
|
+
variables: { input: mapResult.data },
|
|
889
|
+
});
|
|
890
|
+
|
|
891
|
+
return { status: 200, body: { orderId: order.id } };
|
|
892
|
+
}
|
|
893
|
+
```
|
|
894
|
+
|
|
895
|
+
### Recipe: Delta Sync (Hash-Based)
|
|
896
|
+
|
|
897
|
+
```typescript
|
|
898
|
+
import crypto from 'crypto';
|
|
899
|
+
import { StateService, FileKVAdapter } from '@fluentcommerce/fc-connect-sdk';
|
|
900
|
+
|
|
901
|
+
function hash(record: any): string {
|
|
902
|
+
const str = `${record.sku}|${record.location}|${record.qty}`;
|
|
903
|
+
return crypto.createHash('md5').update(str).digest('hex');
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
async function hashDeltaSync() {
|
|
907
|
+
const stateService = new StateService(logger);
|
|
908
|
+
|
|
909
|
+
const prevHashes = (await stateService.getState('hashes')) || {};
|
|
910
|
+
const currHashes: Record<string, string> = {};
|
|
911
|
+
const changed = [];
|
|
912
|
+
|
|
913
|
+
const records = await loadAllRecords();
|
|
914
|
+
|
|
915
|
+
for (const record of records) {
|
|
916
|
+
const key = `${record.sku}-${record.location}`;
|
|
917
|
+
const currHash = hash(record);
|
|
918
|
+
currHashes[key] = currHash;
|
|
919
|
+
|
|
920
|
+
if (prevHashes[key] !== currHash) {
|
|
921
|
+
changed.push(record);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
console.log(`${changed.length} of ${records.length} changed`);
|
|
926
|
+
|
|
927
|
+
await processBatch(changed);
|
|
928
|
+
|
|
929
|
+
await stateService.setState('hashes', currHashes);
|
|
930
|
+
}
|
|
931
|
+
```
|
|
932
|
+
|
|
933
|
+
---
|
|
934
|
+
|
|
935
|
+
## Troubleshooting
|
|
936
|
+
|
|
937
|
+
### Common Issues
|
|
938
|
+
|
|
939
|
+
| Issue | Cause | Solution |
|
|
940
|
+
| -------------------- | ---------------------- | ----------------------------------- |
|
|
941
|
+
| **401 Unauthorized** | Invalid credentials | Check clientId/clientSecret |
|
|
942
|
+
| **429 Rate Limit** | Too many requests | Add retry with backoff |
|
|
943
|
+
| **504 Timeout** | Request too slow | Reduce batch size or use pagination |
|
|
944
|
+
| **Validation Error** | Missing required field | Check mapping config |
|
|
945
|
+
| **Hash mismatch** | State out of sync | Reset state, run full sync |
|
|
946
|
+
|
|
947
|
+
### Debug Commands
|
|
948
|
+
|
|
949
|
+
```bash
|
|
950
|
+
# Introspect GraphQL schema
|
|
951
|
+
npx @fluentcommerce/fc-connect-sdk introspect-schema --url https://api.fluentcommerce.com/graphql
|
|
952
|
+
|
|
953
|
+
# Validate mapping config
|
|
954
|
+
npx @fluentcommerce/fc-connect-sdk validate-schema --mapping mappings/config.json
|
|
955
|
+
|
|
956
|
+
# Test Deno compatibility
|
|
957
|
+
npm run test:deno
|
|
958
|
+
```
|
|
959
|
+
|
|
960
|
+
---
|
|
961
|
+
|
|
962
|
+
[← Back to Index](./integration-patterns-readme.md) | [View Complete Modules](./modules/)
|