@fluentcommerce/fc-connect-sdk 0.1.53 → 0.1.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -2
- package/README.md +39 -0
- package/dist/cjs/auth/index.d.ts +3 -0
- package/dist/cjs/auth/index.js +13 -0
- package/dist/cjs/auth/profile-loader.d.ts +18 -0
- package/dist/cjs/auth/profile-loader.js +208 -0
- package/dist/cjs/client-factory.d.ts +4 -0
- package/dist/cjs/client-factory.js +10 -0
- package/dist/cjs/clients/fluent-client.js +13 -6
- package/dist/cjs/index.d.ts +3 -1
- package/dist/cjs/index.js +8 -2
- package/dist/cjs/utils/pagination-helpers.js +38 -2
- package/dist/cjs/versori/fluent-versori-client.js +11 -5
- package/dist/esm/auth/index.d.ts +3 -0
- package/dist/esm/auth/index.js +2 -0
- package/dist/esm/auth/profile-loader.d.ts +18 -0
- package/dist/esm/auth/profile-loader.js +169 -0
- package/dist/esm/client-factory.d.ts +4 -0
- package/dist/esm/client-factory.js +9 -0
- package/dist/esm/clients/fluent-client.js +13 -6
- package/dist/esm/index.d.ts +3 -1
- package/dist/esm/index.js +2 -1
- package/dist/esm/utils/pagination-helpers.js +38 -2
- package/dist/esm/versori/fluent-versori-client.js +11 -5
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/tsconfig.types.tsbuildinfo +1 -1
- package/dist/types/auth/index.d.ts +3 -0
- package/dist/types/auth/profile-loader.d.ts +18 -0
- package/dist/types/client-factory.d.ts +4 -0
- package/dist/types/index.d.ts +3 -1
- package/docs/00-START-HERE/EXPORT-VALIDATION.md +158 -158
- package/docs/00-START-HERE/cli-analyze-source-structure-guide.md +655 -655
- package/docs/00-START-HERE/cli-documentation-index.md +202 -202
- package/docs/00-START-HERE/cli-quick-reference.md +252 -252
- package/docs/00-START-HERE/decision-tree.md +552 -552
- package/docs/00-START-HERE/getting-started.md +1070 -1070
- package/docs/00-START-HERE/mapper-quick-decision-guide.md +235 -235
- package/docs/00-START-HERE/readme.md +237 -237
- package/docs/00-START-HERE/retailerid-configuration.md +404 -404
- package/docs/00-START-HERE/sdk-philosophy.md +794 -794
- package/docs/00-START-HERE/troubleshooting-quick-reference.md +1086 -1086
- package/docs/01-TEMPLATES/faq.md +686 -686
- package/docs/01-TEMPLATES/patterns/pattern-templates-guide.md +68 -68
- package/docs/01-TEMPLATES/patterns/patterns-csv-schema-validation-and-rejection-report.md +233 -233
- package/docs/01-TEMPLATES/patterns/patterns-custom-resolvers.md +407 -407
- package/docs/01-TEMPLATES/patterns/patterns-error-handling-retry.md +511 -511
- package/docs/01-TEMPLATES/patterns/patterns-field-mapping-universal.md +701 -701
- package/docs/01-TEMPLATES/patterns/patterns-large-file-splitting.md +1430 -1430
- package/docs/01-TEMPLATES/patterns/patterns-master-data-etl.md +2399 -2399
- package/docs/01-TEMPLATES/patterns/patterns-pagination-streaming.md +447 -447
- package/docs/01-TEMPLATES/patterns/patterns-state-duplicate-prevention.md +385 -385
- package/docs/01-TEMPLATES/readme.md +957 -957
- package/docs/01-TEMPLATES/standalone/standalone-asn-inbound-processing.md +1209 -1209
- package/docs/01-TEMPLATES/standalone/standalone-graphql-query-export.md +1140 -1140
- package/docs/01-TEMPLATES/standalone/standalone-graphql-to-parquet-partitioned-s3.md +432 -432
- package/docs/01-TEMPLATES/standalone/standalone-multi-channel-inventory-sync.md +1185 -1185
- package/docs/01-TEMPLATES/standalone/standalone-multi-source-aggregation.md +1462 -1462
- package/docs/01-TEMPLATES/standalone/standalone-s3-csv-batch-api.md +1390 -1390
- package/docs/01-TEMPLATES/standalone/standalone-s3-csv-inventory-to-batch.md +330 -330
- package/docs/01-TEMPLATES/standalone/standalone-scripts-guide.md +87 -87
- package/docs/01-TEMPLATES/standalone/standalone-sftp-xml-graphql.md +1444 -1444
- package/docs/01-TEMPLATES/standalone/standalone-webhook-payload-processing.md +688 -688
- package/docs/01-TEMPLATES/versori/business-examples/business-examples-dropship-order-routing.md +193 -193
- package/docs/01-TEMPLATES/versori/business-examples/business-examples-graphql-parquet-extraction.md +518 -518
- package/docs/01-TEMPLATES/versori/business-examples/business-examples-inter-location-transfers.md +2162 -2162
- package/docs/01-TEMPLATES/versori/business-examples/business-examples-pre-order-allocation.md +2226 -2226
- package/docs/01-TEMPLATES/versori/business-examples/business-scenarios-guide.md +87 -87
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-connection-validation-pattern.md +656 -656
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-dual-workflow-connector.md +835 -835
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-guide.md +108 -108
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-kv-state-management.md +1533 -1533
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-xml-response-patterns.md +1160 -1160
- package/docs/01-TEMPLATES/versori/versori-platform-guide.md +201 -201
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-asn-purchase-order.md +1906 -1906
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-dropship-routing.md +1074 -1074
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-flash-sale-reserve.md +1395 -1395
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-generic-xml-order.md +888 -888
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-payment-gateway-integration.md +2478 -2478
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-rma-returns-comprehensive.md +2240 -2240
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-xml-order-ingestion.md +2029 -2029
- package/docs/01-TEMPLATES/versori/webhooks/webhook-templates-guide.md +140 -140
- package/docs/01-TEMPLATES/versori/workflows/_examples/sample-data/inventory-mapping.json +20 -20
- package/docs/01-TEMPLATES/versori/workflows/_examples/sample-data/products_2025-01-22.csv +11 -11
- package/docs/01-TEMPLATES/versori/workflows/_examples/sample-data/sample-data-guide.md +34 -34
- package/docs/01-TEMPLATES/versori/workflows/_examples/workflow-examples-guide.md +36 -36
- package/docs/01-TEMPLATES/versori/workflows/extraction/extraction-modes-guide.md +1038 -1038
- package/docs/01-TEMPLATES/versori/workflows/extraction/extraction-workflows-guide.md +138 -138
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/graphql-extraction-guide.md +63 -63
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-fulfillments-to-sftp-csv.md +2062 -2062
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-fulfillments-to-sftp-xml.md +2294 -2294
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-positions-to-s3-csv.md +2461 -2461
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-positions-to-sftp-xml.md +2529 -2529
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-quantities-to-s3-csv.md +2464 -2464
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-quantities-to-s3-json.md +1959 -1959
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-orders-to-s3-csv.md +1953 -1953
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-orders-to-sftp-xml.md +2541 -2541
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-products-to-s3-json.md +2384 -2384
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-products-to-sftp-xml.md +2445 -2445
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-virtual-positions-to-s3-csv.md +2355 -2355
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-virtual-positions-to-s3-json.md +2042 -2042
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-virtual-positions-to-sftp-xml.md +2726 -2726
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/batch-api-guide.md +206 -206
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-cycle-count-reconciliation.md +2030 -2030
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-multi-channel-inventory-sync.md +1882 -1882
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-s3-csv-inventory-batch.md +2827 -2827
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-s3-json-inventory-batch.md +1952 -1952
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-s3-xml-inventory-batch.md +3289 -3289
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-sftp-csv-inventory-batch.md +3064 -3064
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-sftp-json-inventory-batch.md +3238 -3238
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-sftp-xml-inventory-batch.md +2977 -2977
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/event-api-guide.md +321 -321
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-payload-json-order-cancel-event.md +959 -959
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-payload-xml-order-cancel-event.md +1170 -1170
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-csv-product-event.md +2312 -2312
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-json-product-event.md +2999 -2999
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-parquet-product-event.md +2836 -2836
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-xml-product-event.md +2395 -2395
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-csv-product-event.md +2295 -2295
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-json-product-event.md +2602 -2602
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-parquet-product-event.md +2589 -2589
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-xml-product-event.md +3578 -3578
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/graphql-mutations-guide.md +93 -93
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-payload-json-order-update-graphql.md +1260 -1260
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-payload-xml-order-update-graphql.md +1472 -1472
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-csv-control-graphql.md +2417 -2417
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-csv-location-graphql.md +2811 -2811
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-csv-price-graphql.md +2619 -2619
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-json-location-graphql.md +2807 -2807
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-xml-location-graphql.md +2373 -2373
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-sftp-csv-control-graphql.md +2740 -2740
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-sftp-csv-location-graphql.md +2760 -2760
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-sftp-json-location-graphql.md +1710 -1710
- package/docs/01-TEMPLATES/versori/workflows/ingestion/ingestion-workflows-guide.md +136 -136
- package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/rubix-webhooks-guide.md +520 -520
- package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/template-webhook-rubix-fulfilment-to-sftp-xml-inline.md +1418 -1418
- package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/template-webhook-rubix-fulfilment-to-sftp-xml-universal-mapper.md +1785 -1785
- package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/template-webhook-rubix-order-attribute-update.md +824 -824
- package/docs/01-TEMPLATES/versori/workflows/workflows-overview-guide.md +646 -646
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-batch-archival.md +724 -724
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-job-tracker.md +627 -627
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-partial-batch-recovery.md +561 -561
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-quick-reference.md +367 -367
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-readme.md +407 -407
- package/docs/02-CORE-GUIDES/advanced-services/readme.md +49 -49
- package/docs/02-CORE-GUIDES/api-reference/api-reference-quick-reference.md +548 -548
- package/docs/02-CORE-GUIDES/api-reference/event-api-input-output-reference.md +702 -1171
- package/docs/02-CORE-GUIDES/api-reference/examples/client-initialization.ts +286 -286
- package/docs/02-CORE-GUIDES/api-reference/graphql-error-classification.md +337 -337
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-01-client-api.md +399 -482
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-03-authentication.md +199 -199
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-04-graphql-mapping.md +925 -925
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-05-services.md +1198 -1198
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-06-data-sources.md +1083 -1083
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-07-parsers.md +1097 -1097
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-08-pagination.md +513 -513
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-08-types.md +545 -597
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-09-error-handling.md +527 -527
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-09-webhook-validation.md +514 -514
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-10-extraction.md +557 -557
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-10-utilities.md +412 -412
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-11-cli-tools.md +423 -423
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-11-error-handling.md +716 -716
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-analyze-source-structure.md +518 -518
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-partial-responses.md +212 -212
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-testing.md +300 -300
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-13-resolver-builder.md +322 -322
- package/docs/02-CORE-GUIDES/api-reference/readme.md +279 -279
- package/docs/02-CORE-GUIDES/auto-pagination/auto-pagination-quick-reference.md +351 -351
- package/docs/02-CORE-GUIDES/auto-pagination/auto-pagination-readme.md +277 -277
- package/docs/02-CORE-GUIDES/auto-pagination/examples/auto-pagination-readme.md +178 -178
- package/docs/02-CORE-GUIDES/auto-pagination/examples/common-patterns.ts +351 -351
- package/docs/02-CORE-GUIDES/auto-pagination/examples/paginate-products.ts +384 -384
- package/docs/02-CORE-GUIDES/auto-pagination/examples/paginate-virtual-positions.ts +308 -308
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-01-foundations.md +470 -470
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-02-quick-start.md +713 -713
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-03-configuration.md +754 -754
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-04-advanced-patterns.md +732 -732
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-05-sdk-integration.md +847 -847
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-06-troubleshooting.md +359 -359
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-07-api-reference.md +462 -462
- package/docs/02-CORE-GUIDES/auto-pagination/readme.md +54 -54
- package/docs/02-CORE-GUIDES/data-sources/data-sources-file-operations-error-handling.md +1487 -1487
- package/docs/02-CORE-GUIDES/data-sources/data-sources-quick-reference.md +836 -836
- package/docs/02-CORE-GUIDES/data-sources/data-sources-readme.md +276 -276
- package/docs/02-CORE-GUIDES/data-sources/data-sources-sftp-credential-access-security.md +553 -553
- package/docs/02-CORE-GUIDES/data-sources/examples/common-patterns.ts +409 -409
- package/docs/02-CORE-GUIDES/data-sources/examples/data-sources-readme.md +178 -178
- package/docs/02-CORE-GUIDES/data-sources/examples/s3-operations.ts +308 -308
- package/docs/02-CORE-GUIDES/data-sources/examples/sftp-operations.ts +371 -371
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-01-foundations.md +735 -735
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-02-s3-operations.md +1302 -1302
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-03-sftp-operations.md +1379 -1379
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-04-file-patterns.md +941 -941
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-05-advanced-topics.md +813 -813
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-06-integration-patterns.md +486 -486
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-07-troubleshooting.md +387 -387
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-08-api-reference.md +417 -417
- package/docs/02-CORE-GUIDES/data-sources/readme.md +77 -77
- package/docs/02-CORE-GUIDES/error-handling-guide.md +936 -936
- package/docs/02-CORE-GUIDES/extraction/examples/02-core-guides-extraction-readme.md +116 -116
- package/docs/02-CORE-GUIDES/extraction/examples/common-patterns.ts +428 -428
- package/docs/02-CORE-GUIDES/extraction/examples/extract-inventory-basic.ts +187 -187
- package/docs/02-CORE-GUIDES/extraction/extraction-quick-reference.md +596 -596
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-01-foundations.md +514 -514
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-02-basic-extraction.md +823 -823
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-03-parquet-processing.md +507 -507
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-04-data-enrichment.md +546 -546
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-05-transformation.md +494 -494
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-06-export-formats.md +458 -458
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-06-performance.md +138 -138
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-07-api-reference.md +148 -148
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-07-optimization.md +692 -692
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-08-extraction-orchestrator.md +1008 -1008
- package/docs/02-CORE-GUIDES/extraction/readme.md +151 -151
- package/docs/02-CORE-GUIDES/ingestion/examples/_simple-kv-store.ts +40 -40
- package/docs/02-CORE-GUIDES/ingestion/examples/error-recovery.ts +728 -728
- package/docs/02-CORE-GUIDES/ingestion/examples/event-driven.ts +501 -501
- package/docs/02-CORE-GUIDES/ingestion/examples/local-file-ingestion.ts +88 -88
- package/docs/02-CORE-GUIDES/ingestion/examples/parquet-ingestion.ts +117 -117
- package/docs/02-CORE-GUIDES/ingestion/examples/performance-optimized.ts +647 -647
- package/docs/02-CORE-GUIDES/ingestion/examples/s3-csv-ingestion.ts +169 -169
- package/docs/02-CORE-GUIDES/ingestion/examples/sftp-csv-ingestion.ts +134 -134
- package/docs/02-CORE-GUIDES/ingestion/ingestion-quick-reference.md +546 -546
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-01-introduction.md +626 -626
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-02-quick-start.md +658 -658
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-03-data-sources.md +1052 -1052
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-04-field-mapping.md +763 -763
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-05-advanced-parsers.md +676 -676
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-06-batch-api.md +1295 -1295
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-07-api-reference.md +138 -138
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-07-state-management.md +1037 -1037
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-08-performance-optimization.md +1349 -1349
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-09-best-practices.md +1893 -1893
- package/docs/02-CORE-GUIDES/ingestion/readme.md +160 -160
- package/docs/02-CORE-GUIDES/logging-guide.md +585 -585
- package/docs/02-CORE-GUIDES/mapping/error-handling-patterns.md +401 -401
- package/docs/02-CORE-GUIDES/mapping/examples/02-core-guides-mapping-readme.md +128 -128
- package/docs/02-CORE-GUIDES/mapping/examples/common-patterns.ts +273 -273
- package/docs/02-CORE-GUIDES/mapping/examples/csv-location-ingestion.json +36 -36
- package/docs/02-CORE-GUIDES/mapping/examples/csv-mapping.ts +242 -242
- package/docs/02-CORE-GUIDES/mapping/examples/graphql-to-parquet-extraction.json +36 -36
- package/docs/02-CORE-GUIDES/mapping/examples/json-mapping.ts +213 -213
- package/docs/02-CORE-GUIDES/mapping/examples/json-product-to-mutation.json +48 -48
- package/docs/02-CORE-GUIDES/mapping/examples/xml-mapping.ts +291 -291
- package/docs/02-CORE-GUIDES/mapping/examples/xml-order-to-mutation.json +45 -45
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/graphql-mutation-mapping-quick-reference.md +463 -463
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/graphql-mutation-mapping-readme.md +227 -227
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-01-introduction.md +222 -222
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-02-quick-start.md +351 -351
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-03-schema-validation.md +569 -569
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-04-mapping-patterns.md +471 -471
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-05-configuration-reference.md +611 -611
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-06-advanced-xpath.md +148 -148
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-06-path-syntax.md +464 -464
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-07-api-reference.md +94 -94
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-07-array-handling.md +307 -307
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-08-custom-resolvers.md +544 -544
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-09-advanced-patterns.md +427 -427
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-10-hooks-and-variables.md +336 -336
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-11-error-handling.md +488 -488
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-12-arguments-vs-nodes.md +383 -383
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-13-best-practices.md +477 -477
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/readme.md +62 -62
- package/docs/02-CORE-GUIDES/mapping/mapping-format-decision-tree.md +480 -480
- package/docs/02-CORE-GUIDES/mapping/mapping-graphql-alias-batching-guide.md +820 -820
- package/docs/02-CORE-GUIDES/mapping/mapping-javascript-objects.md +2369 -2369
- package/docs/02-CORE-GUIDES/mapping/mapping-mapper-comparison-guide.md +682 -682
- package/docs/02-CORE-GUIDES/mapping/modules/02-core-guides-mapping-07-api-reference.md +1327 -1327
- package/docs/02-CORE-GUIDES/mapping/modules/02-core-guides-mapping-08-error-handling.md +1142 -1142
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-04-use-cases.md +891 -891
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-06-helpers-resolvers.md +1126 -1126
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-06-sdk-resolvers.md +199 -199
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-07-api-reference.md +1319 -1319
- package/docs/02-CORE-GUIDES/mapping/readme.md +178 -178
- package/docs/02-CORE-GUIDES/mapping/resolver-registration.md +410 -410
- package/docs/02-CORE-GUIDES/mapping/resolvers/examples/common-patterns.ts +226 -226
- package/docs/02-CORE-GUIDES/mapping/resolvers/examples/custom-resolvers.ts +227 -227
- package/docs/02-CORE-GUIDES/mapping/resolvers/examples/sdk-resolvers-usage.ts +203 -203
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-readme.md +274 -274
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-api-reference.md +679 -679
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-cookbook.md +826 -826
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-guide.md +1330 -1330
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-helpers-reference.md +1437 -1437
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-parameters-reference.md +553 -553
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-troubleshooting.md +854 -854
- package/docs/02-CORE-GUIDES/mapping/resolvers/readme.md +75 -75
- package/docs/02-CORE-GUIDES/parsers/examples/02-core-guides-parsers-readme.md +161 -161
- package/docs/02-CORE-GUIDES/parsers/examples/csv-parser-examples.ts +110 -110
- package/docs/02-CORE-GUIDES/parsers/examples/json-parser-examples.ts +33 -33
- package/docs/02-CORE-GUIDES/parsers/examples/parquet-parser-examples.ts +47 -47
- package/docs/02-CORE-GUIDES/parsers/examples/xml-parser-examples.ts +38 -38
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-01-foundations.md +355 -355
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-02-csv-parser.md +772 -772
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-03-json-parser.md +789 -789
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-04-xml-parser.md +857 -857
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-05-parquet-parser.md +603 -603
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-06-integration-patterns.md +702 -702
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-06-streaming.md +121 -121
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-07-api-reference.md +89 -89
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-07-troubleshooting.md +727 -727
- package/docs/02-CORE-GUIDES/parsers/parsers-quick-reference.md +482 -482
- package/docs/02-CORE-GUIDES/parsers/parsers-readme.md +258 -258
- package/docs/02-CORE-GUIDES/parsers/readme.md +65 -65
- package/docs/02-CORE-GUIDES/readme.md +194 -194
- package/docs/02-CORE-GUIDES/webhook-validation/examples/basic-validation.ts +108 -108
- package/docs/02-CORE-GUIDES/webhook-validation/examples/common-patterns.ts +316 -316
- package/docs/02-CORE-GUIDES/webhook-validation/examples/webhook-validation-readme.md +61 -61
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-01-foundations.md +440 -440
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-02-quick-start.md +525 -525
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-03-versori-integration.md +741 -741
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-04-platform-integration.md +629 -629
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-05-configuration.md +535 -535
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-06-error-handling.md +611 -611
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-06-troubleshooting.md +124 -124
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-07-api-reference.md +511 -511
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-08-rubix-webhooks.md +590 -590
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-09-rubix-event-vs-http-call.md +432 -432
- package/docs/02-CORE-GUIDES/webhook-validation/readme.md +239 -239
- package/docs/02-CORE-GUIDES/webhook-validation/webhook-validation-quick-reference.md +392 -392
- package/docs/03-PATTERN-GUIDES/connector-scenarios/connector-scenarios-quick-reference.md +498 -498
- package/docs/03-PATTERN-GUIDES/connector-scenarios/connector-scenarios-readme.md +313 -313
- package/docs/03-PATTERN-GUIDES/connector-scenarios/examples/common-patterns.ts +612 -612
- package/docs/03-PATTERN-GUIDES/connector-scenarios/examples/connector-scenarios-readme.md +253 -253
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-01-foundations.md +452 -452
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-02-simple-scenarios.md +681 -681
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-03-intermediate-scenarios.md +637 -637
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-04-advanced-scenarios.md +650 -650
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-05-bidirectional-sync.md +233 -233
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-06-production-patterns.md +442 -442
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-07-reference.md +445 -445
- package/docs/03-PATTERN-GUIDES/connector-scenarios/readme.md +31 -31
- package/docs/03-PATTERN-GUIDES/enterprise-integration-patterns.md +1528 -1528
- package/docs/03-PATTERN-GUIDES/error-handling/comprehensive-error-handling-guide.md +1437 -1437
- package/docs/03-PATTERN-GUIDES/error-handling/error-handling-quick-reference.md +390 -390
- package/docs/03-PATTERN-GUIDES/error-handling/examples/common-patterns.ts +438 -438
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-01-foundations.md +362 -362
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-02-error-types.md +850 -850
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-03-utf8-handling.md +456 -456
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-04-error-scenarios.md +658 -658
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-05-calling-patterns.md +671 -671
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-06-retry-strategies.md +1034 -1034
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-07-monitoring.md +653 -653
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-08-api-reference.md +847 -847
- package/docs/03-PATTERN-GUIDES/error-handling/readme.md +36 -36
- package/docs/03-PATTERN-GUIDES/examples/__tests__/readme.md +40 -40
- package/docs/03-PATTERN-GUIDES/examples/__tests__/resolver-examples.test.js +282 -282
- package/docs/03-PATTERN-GUIDES/examples/test-data/03-pattern-guides-readme.md +110 -110
- package/docs/03-PATTERN-GUIDES/examples/test-data/canonical-inventory.json +123 -123
- package/docs/03-PATTERN-GUIDES/examples/test-data/canonical-order.json +171 -171
- package/docs/03-PATTERN-GUIDES/examples/test-data/readme.md +28 -28
- package/docs/03-PATTERN-GUIDES/extraction/extraction-readme.md +15 -15
- package/docs/03-PATTERN-GUIDES/extraction/readme.md +25 -25
- package/docs/03-PATTERN-GUIDES/file-operations/examples/common-patterns.ts +407 -407
- package/docs/03-PATTERN-GUIDES/file-operations/examples/file-operations-readme.md +142 -142
- package/docs/03-PATTERN-GUIDES/file-operations/file-operations-quick-reference.md +462 -462
- package/docs/03-PATTERN-GUIDES/file-operations/file-operations-readme.md +379 -379
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-01-foundations.md +430 -430
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-02-quick-start.md +484 -484
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-03-s3-operations.md +507 -507
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-04-sftp-operations.md +963 -963
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-05-streaming-performance.md +503 -503
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-06-archive-patterns.md +386 -386
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-06-error-handling.md +117 -117
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-07-api-reference.md +78 -78
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-07-testing-troubleshooting.md +567 -567
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-08-api-reference.md +1055 -1055
- package/docs/03-PATTERN-GUIDES/file-operations/readme.md +32 -32
- package/docs/03-PATTERN-GUIDES/ingestion/ingestion-readme.md +15 -15
- package/docs/03-PATTERN-GUIDES/ingestion/readme.md +25 -25
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/batch-processing.ts +130 -130
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/common-patterns.ts +360 -360
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/delta-sync.ts +130 -130
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/integration-patterns-readme.md +100 -100
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/real-time-webhook.ts +398 -398
- package/docs/03-PATTERN-GUIDES/integration-patterns/integration-patterns-quick-reference.md +962 -962
- package/docs/03-PATTERN-GUIDES/integration-patterns/integration-patterns-readme.md +134 -134
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-01-real-time-processing.md +991 -991
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-02-batch-processing.md +1547 -1547
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-03-delta-sync.md +1108 -1108
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-04-webhook-patterns.md +1181 -1181
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-05-error-handling.md +1061 -1061
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-06-advanced-integration-services.md +1547 -1547
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-06-performance.md +109 -109
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-07-api-reference.md +34 -34
- package/docs/03-PATTERN-GUIDES/integration-patterns/readme.md +30 -30
- package/docs/03-PATTERN-GUIDES/logging-minimal-mode.md +128 -128
- package/docs/03-PATTERN-GUIDES/multiple-connections/examples/common-patterns.ts +380 -380
- package/docs/03-PATTERN-GUIDES/multiple-connections/examples/multiple-connections-readme.md +139 -139
- package/docs/03-PATTERN-GUIDES/multiple-connections/examples/parallel-root-connections.ts +149 -149
- package/docs/03-PATTERN-GUIDES/multiple-connections/examples/real-world-scenarios.ts +405 -405
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-01-foundations.md +378 -378
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-02-quick-start.md +566 -566
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-03-targeting-connections.md +659 -659
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-04-parallel-queries.md +656 -656
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-05-best-practices.md +624 -624
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-06-api-reference.md +824 -824
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-06-versori.md +119 -119
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-07-api-reference.md +87 -87
- package/docs/03-PATTERN-GUIDES/multiple-connections/multiple-connections-quick-reference.md +353 -353
- package/docs/03-PATTERN-GUIDES/multiple-connections/multiple-connections-readme.md +270 -270
- package/docs/03-PATTERN-GUIDES/multiple-connections/readme.md +30 -30
- package/docs/03-PATTERN-GUIDES/pagination/pagination-readme.md +14 -14
- package/docs/03-PATTERN-GUIDES/pagination/readme.md +24 -24
- package/docs/03-PATTERN-GUIDES/parquet/examples/common-patterns.ts +180 -180
- package/docs/03-PATTERN-GUIDES/parquet/examples/read-parquet.ts +48 -48
- package/docs/03-PATTERN-GUIDES/parquet/examples/write-parquet.ts +65 -65
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-01-introduction.md +393 -393
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-02-quick-start.md +572 -572
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-03-reading-parquet.md +525 -525
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-04-writing-parquet.md +554 -554
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-05-graphql-extraction.md +405 -405
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-06-performance.md +104 -104
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-06-s3-integration.md +511 -511
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-07-api-reference.md +90 -90
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-07-performance-optimization.md +525 -525
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-08-best-practices.md +712 -712
- package/docs/03-PATTERN-GUIDES/parquet/parquet-quick-reference.md +683 -683
- package/docs/03-PATTERN-GUIDES/parquet/parquet-readme.md +248 -248
- package/docs/03-PATTERN-GUIDES/parquet/readme.md +32 -32
- package/docs/03-PATTERN-GUIDES/parsers/parsers-readme.md +12 -12
- package/docs/03-PATTERN-GUIDES/parsers/readme.md +24 -24
- package/docs/03-PATTERN-GUIDES/readme.md +159 -159
- package/docs/03-PATTERN-GUIDES/webhooks/readme.md +24 -24
- package/docs/03-PATTERN-GUIDES/webhooks/webhooks-readme.md +8 -8
- package/docs/04-REFERENCE/architecture/architecture-01-overview.md +427 -427
- package/docs/04-REFERENCE/architecture/architecture-02-client-architecture.md +424 -424
- package/docs/04-REFERENCE/architecture/architecture-03-data-flow.md +690 -690
- package/docs/04-REFERENCE/architecture/architecture-04-service-layer.md +834 -834
- package/docs/04-REFERENCE/architecture/architecture-05-integration-architecture.md +655 -655
- package/docs/04-REFERENCE/architecture/architecture-06-state-management.md +653 -653
- package/docs/04-REFERENCE/architecture/architecture-adding-new-data-sources.md +686 -686
- package/docs/04-REFERENCE/architecture/readme.md +279 -279
- package/docs/04-REFERENCE/platforms/deno/readme.md +117 -117
- package/docs/04-REFERENCE/platforms/nodejs/readme.md +146 -146
- package/docs/04-REFERENCE/platforms/readme.md +135 -135
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-01-introduction.md +398 -398
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-02-quick-start.md +560 -560
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-03-authentication.md +757 -757
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-04-workflows.md +2476 -2476
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-05-connections.md +1167 -1167
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-06-kv-storage.md +990 -990
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-06-state-management.md +121 -121
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-07-api-reference.md +68 -68
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-07-deployment.md +731 -731
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-08-best-practices.md +1111 -1111
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-09-signature-reference.md +766 -766
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-readme.md +299 -299
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-s3-sftp-configuration-guide.md +1425 -1425
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-webhook-api-key-security.md +816 -816
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-webhook-connection-security.md +681 -681
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-workflow-task-types.md +708 -708
- package/docs/04-REFERENCE/platforms/versori/readme.md +108 -108
- package/docs/04-REFERENCE/readme.md +148 -148
- package/docs/04-REFERENCE/resolver-signature/examples/advanced-resolvers.ts +482 -482
- package/docs/04-REFERENCE/resolver-signature/examples/async-resolvers.ts +496 -496
- package/docs/04-REFERENCE/resolver-signature/examples/basic-resolvers.ts +343 -343
- package/docs/04-REFERENCE/resolver-signature/examples/resolver-signature-readme.md +188 -188
- package/docs/04-REFERENCE/resolver-signature/examples/testing-resolvers.ts +463 -463
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-01-foundations.md +286 -286
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-02-parameter-reference.md +643 -643
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-03-basic-examples.md +521 -521
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-04-advanced-patterns.md +739 -739
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-05-sdk-resolvers.md +531 -531
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-06-migration-guide.md +650 -650
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-06-testing.md +125 -125
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-07-api-reference.md +794 -794
- package/docs/04-REFERENCE/resolver-signature/readme.md +64 -64
- package/docs/04-REFERENCE/resolver-signature/resolver-signature-quick-reference.md +270 -270
- package/docs/04-REFERENCE/resolver-signature/resolver-signature-readme.md +351 -351
- package/docs/04-REFERENCE/schema/fluent-commerce-schema.json +764 -764
- package/docs/04-REFERENCE/schema/readme.md +141 -141
- package/docs/04-REFERENCE/testing/examples/04-reference-testing-readme.md +158 -158
- package/docs/04-REFERENCE/testing/examples/fluent-testing.ts +62 -62
- package/docs/04-REFERENCE/testing/examples/health-check.ts +155 -155
- package/docs/04-REFERENCE/testing/examples/integration-test.ts +119 -119
- package/docs/04-REFERENCE/testing/examples/performance-test.ts +183 -183
- package/docs/04-REFERENCE/testing/examples/s3-testing.ts +127 -127
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-01-foundations.md +267 -267
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-02-s3-testing.md +599 -599
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-03-fluent-testing.md +589 -589
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-04-integration-testing.md +699 -699
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-05-debugging.md +478 -478
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-06-cicd-integration.md +463 -463
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-06-preflight-validation.md +131 -131
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-07-best-practices.md +499 -499
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-07-coverage-ci.md +165 -165
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-08-api-reference.md +634 -634
- package/docs/04-REFERENCE/testing/readme.md +86 -86
- package/docs/04-REFERENCE/testing/testing-quick-reference.md +667 -667
- package/docs/04-REFERENCE/testing/testing-readme.md +286 -286
- package/docs/04-REFERENCE/troubleshooting/readme.md +144 -144
- package/docs/04-REFERENCE/troubleshooting/troubleshooting-deno-sftp-compatibility.md +392 -392
- package/docs/template-loading-matrix.md +242 -242
- package/package.json +5 -3
|
@@ -1,847 +1,847 @@
|
|
|
1
|
-
# Module 5: SDK Ecosystem Integration
|
|
2
|
-
|
|
3
|
-
> **Learning Objective:** Integrate auto-pagination with other SDK services like UniversalMapper, ExtractionOrchestrator, S3DataSource, and Versori platform.
|
|
4
|
-
>
|
|
5
|
-
> **Level:** Advanced
|
|
6
|
-
|
|
7
|
-
## Table of Contents
|
|
8
|
-
|
|
9
|
-
1. `Pagination + UniversalMapper (Transform Data)`
|
|
10
|
-
2. `Pagination + ExtractionOrchestrator (Export to S3)`
|
|
11
|
-
3. `Pagination + Batch Ingestion (Reverse Flow)`
|
|
12
|
-
4. [Versori Platform Integration](#versori-platform-integration)
|
|
13
|
-
5. [Complete ETL Pipelines](#complete-etl-pipelines)
|
|
14
|
-
6. [Performance Optimization Patterns](#performance-optimization-patterns)
|
|
15
|
-
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
## Pagination + UniversalMapper
|
|
19
|
-
|
|
20
|
-
Transform paginated data using SDK's Universal Mapper.
|
|
21
|
-
|
|
22
|
-
### Pattern: Fetch → Transform → Process
|
|
23
|
-
|
|
24
|
-
```typescript
|
|
25
|
-
import { createClient, UniversalMapper } from '@fluentcommerce/fc-connect-sdk';
|
|
26
|
-
|
|
27
|
-
async function fetchAndTransformInventory() {
|
|
28
|
-
const client = await createClient({
|
|
29
|
-
config: {
|
|
30
|
-
baseUrl: process.env.FLUENT_BASE_URL!,
|
|
31
|
-
clientId: process.env.FLUENT_CLIENT_ID!,
|
|
32
|
-
clientSecret: process.env.FLUENT_CLIENT_SECRET!,
|
|
33
|
-
retailerId: process.env.FLUENT_RETAILER_ID!
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
// 1. Fetch data with auto-pagination
|
|
38
|
-
const result = await client.graphql({
|
|
39
|
-
query: `
|
|
40
|
-
query GetVirtualPositions($first: Int!, $after: String) {
|
|
41
|
-
virtualPositions(first: $first, after: $after) {
|
|
42
|
-
edges {
|
|
43
|
-
node {
|
|
44
|
-
id
|
|
45
|
-
productRef
|
|
46
|
-
quantity
|
|
47
|
-
groupRef
|
|
48
|
-
status
|
|
49
|
-
updatedOn
|
|
50
|
-
}
|
|
51
|
-
cursor
|
|
52
|
-
}
|
|
53
|
-
pageInfo { hasNextPage }
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
`,
|
|
57
|
-
variables: { first: 150 },
|
|
58
|
-
pagination: {
|
|
59
|
-
maxRecords: 10000,
|
|
60
|
-
onProgress: (page, records) => {
|
|
61
|
-
console.log(`📄 Fetched ${records} positions...`);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
// 2. Define mapping configuration
|
|
67
|
-
const mappingConfig = {
|
|
68
|
-
fields: {
|
|
69
|
-
sku: { source: 'node.productRef', required: true },
|
|
70
|
-
qty: { source: 'node.quantity', resolver: 'sdk.parseInt' },
|
|
71
|
-
location: { source: 'node.groupRef', required: true },
|
|
72
|
-
status: { source: 'node.status', resolver: 'sdk.lowercase' },
|
|
73
|
-
lastUpdated: { source: 'node.updatedOn', resolver: 'sdk.formatDate' }
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
// 3. Transform data with UniversalMapper
|
|
78
|
-
const mapper = new UniversalMapper(mappingConfig);
|
|
79
|
-
const transformed = await mapper.mapArray(result.data.virtualPositions.edges);
|
|
80
|
-
|
|
81
|
-
if (!transformed.success) {
|
|
82
|
-
console.error('Mapping errors:', transformed.errors);
|
|
83
|
-
throw new Error('Data transformation failed');
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// 4. Process transformed data
|
|
87
|
-
console.log(`✅ Transformed ${transformed.data.length} records`);
|
|
88
|
-
|
|
89
|
-
return transformed.data;
|
|
90
|
-
// [{ sku: 'SKU-001', qty: 150, location: 'WH-EAST', status: 'active', lastUpdated: '2024-01-15' }, ...]
|
|
91
|
-
}
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
### Custom Resolvers with Pagination
|
|
95
|
-
|
|
96
|
-
```typescript
|
|
97
|
-
// Define custom transformation logic
|
|
98
|
-
const mapper = new UniversalMapper(mappingConfig, {
|
|
99
|
-
customResolvers: {
|
|
100
|
-
'custom.calculateValue': (value, sourceData, helpers) => {
|
|
101
|
-
const qty = helpers.parseIntSafe(sourceData.node.quantity, 0);
|
|
102
|
-
const price = helpers.parseFloatSafe(sourceData.node.price, 0);
|
|
103
|
-
return qty * price;
|
|
104
|
-
},
|
|
105
|
-
'custom.determineAvailability': (value, sourceData) => {
|
|
106
|
-
return sourceData.node.quantity > 0 && sourceData.node.status === 'ACTIVE'
|
|
107
|
-
? 'IN_STOCK'
|
|
108
|
-
: 'OUT_OF_STOCK';
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
const paginatedResult = await client.graphql({ query, variables, pagination });
|
|
114
|
-
const transformed = await mapper.mapArray(paginatedResult.data.virtualPositions.edges);
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
---
|
|
118
|
-
|
|
119
|
-
## When to Use FluentClient.graphql() vs ExtractionOrchestrator
|
|
120
|
-
|
|
121
|
-
**Decision Guide:**
|
|
122
|
-
|
|
123
|
-
| Scenario | Use | Why |
|
|
124
|
-
|----------|-----|-----|
|
|
125
|
-
| **Query + Transform Only** | `FluentClient.graphql()` with `pagination` | Direct access, minimal overhead, full control |
|
|
126
|
-
| **Query + Export to S3/SFTP** | `ExtractionOrchestrator` | Built-in export, file generation, retry logic |
|
|
127
|
-
| **Custom Processing Logic** | `FluentClient.graphql()` with `pagination` | Maximum flexibility for your workflow |
|
|
128
|
-
| **Standard ETL Pipelines** | `ExtractionOrchestrator` | Pre-built patterns, less code |
|
|
129
|
-
| **Multiple Export Formats** | `ExtractionOrchestrator` | Supports JSON, XML, CSV, Parquet |
|
|
130
|
-
| **Need Progress Tracking** | Both support via callbacks | Use whichever fits your workflow |
|
|
131
|
-
|
|
132
|
-
### FluentClient.graphql() - Direct Approach
|
|
133
|
-
|
|
134
|
-
**Best for:** Custom workflows, transformation-only, direct data access
|
|
135
|
-
|
|
136
|
-
```typescript
|
|
137
|
-
import { createClient, UniversalMapper } from '@fluentcommerce/fc-connect-sdk';
|
|
138
|
-
|
|
139
|
-
const client = await createClient({ config: { /* ... */ } });
|
|
140
|
-
|
|
141
|
-
// 1. Fetch with auto-pagination
|
|
142
|
-
const result = await client.graphql({
|
|
143
|
-
query: `query GetVirtualPositions($first: Int!, $after: String) { ... }`,
|
|
144
|
-
variables: { first: 150 },
|
|
145
|
-
pagination: { maxRecords: 10000 }
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
// 2. Transform with UniversalMapper
|
|
149
|
-
const mapper = new UniversalMapper({ fields: { /* ... */ } });
|
|
150
|
-
const transformed = await mapper.mapArray(result.data.virtualPositions.edges);
|
|
151
|
-
|
|
152
|
-
// 3. Do whatever you want with the data
|
|
153
|
-
await yourCustomLogic(transformed.data);
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
### ExtractionOrchestrator - High-Level Approach
|
|
157
|
-
|
|
158
|
-
**Best for:** Standard exports to S3/SFTP, pre-built patterns
|
|
159
|
-
|
|
160
|
-
```typescript
|
|
161
|
-
import { ExtractionOrchestrator, S3DataSource } from '@fluentcommerce/fc-connect-sdk';
|
|
162
|
-
|
|
163
|
-
const orchestrator = new ExtractionOrchestrator(client, logger);
|
|
164
|
-
|
|
165
|
-
// All-in-one: Query + Transform + Export
|
|
166
|
-
const result = await orchestrator.extract({
|
|
167
|
-
query: `query GetVirtualPositions($first: Int!, $after: String) { ... }`,
|
|
168
|
-
extractionPath: 'virtualPositions.edges',
|
|
169
|
-
destination: s3DataSource,
|
|
170
|
-
format: 'json',
|
|
171
|
-
filename: 'inventory-export.json',
|
|
172
|
-
pagination: { maxRecords: 10000 }
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
// File is already on S3!
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
**Key Difference:** `ExtractionOrchestrator` = `FluentClient.graphql()` + file export + retry logic + pre-built patterns.
|
|
179
|
-
|
|
180
|
-
---
|
|
181
|
-
|
|
182
|
-
## Pagination + ExtractionOrchestrator
|
|
183
|
-
|
|
184
|
-
Export paginated data to S3 using ExtractionOrchestrator.
|
|
185
|
-
|
|
186
|
-
### Pattern: Fetch → Transform → Export
|
|
187
|
-
|
|
188
|
-
```typescript
|
|
189
|
-
import {
|
|
190
|
-
createClient,
|
|
191
|
-
ExtractionOrchestrator,
|
|
192
|
-
S3DataSource,
|
|
193
|
-
UniversalMapper,
|
|
194
|
-
createConsoleLogger
|
|
195
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
196
|
-
|
|
197
|
-
async function exportInventoryToS3() {
|
|
198
|
-
const logger = createConsoleLogger();
|
|
199
|
-
|
|
200
|
-
const client = await createClient({
|
|
201
|
-
config: {
|
|
202
|
-
baseUrl: process.env.FLUENT_BASE_URL!,
|
|
203
|
-
clientId: process.env.FLUENT_CLIENT_ID!,
|
|
204
|
-
clientSecret: process.env.FLUENT_CLIENT_SECRET!,
|
|
205
|
-
retailerId: process.env.FLUENT_RETAILER_ID!
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
// 1. Setup S3 destination
|
|
210
|
-
const s3 = new S3DataSource(
|
|
211
|
-
{
|
|
212
|
-
type: 'S3_JSON',
|
|
213
|
-
connectionId: 's3-export',
|
|
214
|
-
name: 'S3 Export',
|
|
215
|
-
s3Config: {
|
|
216
|
-
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
217
|
-
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
218
|
-
region: process.env.AWS_REGION!,
|
|
219
|
-
bucket: 'inventory-exports'
|
|
220
|
-
}
|
|
221
|
-
},
|
|
222
|
-
logger
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
// 2. Paginate inventory data
|
|
226
|
-
const result = await client.graphql({
|
|
227
|
-
query: `
|
|
228
|
-
query GetVirtualPositions($first: Int!, $after: String) {
|
|
229
|
-
virtualPositions(first: $first, after: $after) {
|
|
230
|
-
edges {
|
|
231
|
-
node {
|
|
232
|
-
id
|
|
233
|
-
productRef
|
|
234
|
-
quantity
|
|
235
|
-
groupRef
|
|
236
|
-
status
|
|
237
|
-
createdOn
|
|
238
|
-
updatedOn
|
|
239
|
-
}
|
|
240
|
-
cursor
|
|
241
|
-
}
|
|
242
|
-
pageInfo { hasNextPage }
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
`,
|
|
246
|
-
variables: { first: 150 },
|
|
247
|
-
pagination: {
|
|
248
|
-
maxRecords: 15000,
|
|
249
|
-
timeoutMs: 300000
|
|
250
|
-
}
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
console.log(`✅ Fetched ${result.extensions.autoPagination.totalRecords} positions`);
|
|
254
|
-
|
|
255
|
-
// 3. Transform data
|
|
256
|
-
const mapper = new UniversalMapper({
|
|
257
|
-
fields: {
|
|
258
|
-
sku: { source: 'node.productRef' },
|
|
259
|
-
quantity: { source: 'node.quantity', resolver: 'sdk.parseInt' },
|
|
260
|
-
warehouse: { source: 'node.groupRef' },
|
|
261
|
-
status: { source: 'node.status' },
|
|
262
|
-
lastModified: { source: 'node.updatedOn', resolver: 'sdk.formatDate' }
|
|
263
|
-
}
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
const transformed = await mapper.mapArray(result.data.virtualPositions.edges);
|
|
267
|
-
|
|
268
|
-
if (!transformed.success) {
|
|
269
|
-
throw new Error(`Transformation failed: ${transformed.errors.length} errors`);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// 4. Export to S3
|
|
273
|
-
const timestamp = new Date().toISOString().split('T')[0];
|
|
274
|
-
const filename = `inventory-export-${timestamp}.json`;
|
|
275
|
-
|
|
276
|
-
await s3.uploadFile(filename, JSON.stringify(transformed.data, null, 2));
|
|
277
|
-
|
|
278
|
-
console.log(`✅ Exported to s3://inventory-exports/${filename}`);
|
|
279
|
-
|
|
280
|
-
return {
|
|
281
|
-
filename,
|
|
282
|
-
recordCount: transformed.data.length,
|
|
283
|
-
stats: result.extensions.autoPagination
|
|
284
|
-
};
|
|
285
|
-
}
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
### Chunked S3 Export (Large Datasets)
|
|
289
|
-
|
|
290
|
-
For very large datasets, export in chunks to avoid memory issues:
|
|
291
|
-
|
|
292
|
-
```typescript
|
|
293
|
-
import { createClient, S3DataSource, UniversalMapper, createConsoleLogger } from '@fluentcommerce/fc-connect-sdk';
|
|
294
|
-
|
|
295
|
-
async function exportInventoryInChunks() {
|
|
296
|
-
const logger = createConsoleLogger();
|
|
297
|
-
|
|
298
|
-
const client = await createClient({
|
|
299
|
-
config: {
|
|
300
|
-
baseUrl: process.env.FLUENT_BASE_URL!,
|
|
301
|
-
clientId: process.env.FLUENT_CLIENT_ID!,
|
|
302
|
-
clientSecret: process.env.FLUENT_CLIENT_SECRET!,
|
|
303
|
-
retailerId: process.env.FLUENT_RETAILER_ID!
|
|
304
|
-
}
|
|
305
|
-
});
|
|
306
|
-
const s3 = new S3DataSource(
|
|
307
|
-
{
|
|
308
|
-
type: 'S3_JSON',
|
|
309
|
-
connectionId: 's3-chunked-export',
|
|
310
|
-
name: 'S3 Chunked Export',
|
|
311
|
-
s3Config: {
|
|
312
|
-
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
313
|
-
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
314
|
-
region: process.env.AWS_REGION!,
|
|
315
|
-
bucket: 'inventory-exports'
|
|
316
|
-
}
|
|
317
|
-
},
|
|
318
|
-
logger
|
|
319
|
-
);
|
|
320
|
-
|
|
321
|
-
let after: string | null = null;
|
|
322
|
-
let chunkNumber = 0;
|
|
323
|
-
const CHUNK_SIZE = 5000;
|
|
324
|
-
|
|
325
|
-
while (true) {
|
|
326
|
-
// Fetch chunk with pagination
|
|
327
|
-
const result = await client.graphql({
|
|
328
|
-
query: virtualPositionsQuery,
|
|
329
|
-
variables: { first: 150, after },
|
|
330
|
-
pagination: {
|
|
331
|
-
maxRecords: CHUNK_SIZE,
|
|
332
|
-
maxPages: 35 // ~35 pages * 150 = 5250 records
|
|
333
|
-
}
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
const edges = result.data.virtualPositions.edges;
|
|
337
|
-
if (edges.length === 0) break;
|
|
338
|
-
|
|
339
|
-
// Transform chunk
|
|
340
|
-
const mapper = new UniversalMapper(mappingConfig);
|
|
341
|
-
const transformed = await mapper.mapArray(edges);
|
|
342
|
-
|
|
343
|
-
// Export chunk to S3
|
|
344
|
-
const filename = `inventory-chunk-${chunkNumber.toString().padStart(4, '0')}.json`;
|
|
345
|
-
await s3.uploadFile(filename, JSON.stringify(transformed.data, null, 2));
|
|
346
|
-
|
|
347
|
-
console.log(`✅ Exported chunk ${chunkNumber}: ${edges.length} records`);
|
|
348
|
-
|
|
349
|
-
// Check if we got fewer records than max (means we're done)
|
|
350
|
-
if (result.extensions.autoPagination.truncated &&
|
|
351
|
-
result.extensions.autoPagination.truncationReason === 'maxRecords') {
|
|
352
|
-
after = edges[edges.length - 1].cursor;
|
|
353
|
-
chunkNumber++;
|
|
354
|
-
} else {
|
|
355
|
-
break; // No more data
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
console.log(`✅ Export complete: ${chunkNumber + 1} chunks`);
|
|
360
|
-
}
|
|
361
|
-
```
|
|
362
|
-
|
|
363
|
-
---
|
|
364
|
-
|
|
365
|
-
## Pagination + Batch Ingestion
|
|
366
|
-
|
|
367
|
-
Reverse flow: Fetch from S3 → Paginate → Transform → Batch Ingest to Fluent.
|
|
368
|
-
|
|
369
|
-
### Pattern: S3 → Transform → Batch API
|
|
370
|
-
|
|
371
|
-
```typescript
|
|
372
|
-
import {
|
|
373
|
-
createClient,
|
|
374
|
-
S3DataSource,
|
|
375
|
-
UniversalMapper,
|
|
376
|
-
createConsoleLogger
|
|
377
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
378
|
-
|
|
379
|
-
async function ingestInventoryFromS3() {
|
|
380
|
-
const logger = createConsoleLogger();
|
|
381
|
-
|
|
382
|
-
const client = await createClient({
|
|
383
|
-
config: {
|
|
384
|
-
baseUrl: process.env.FLUENT_BASE_URL!,
|
|
385
|
-
clientId: process.env.FLUENT_CLIENT_ID!,
|
|
386
|
-
clientSecret: process.env.FLUENT_CLIENT_SECRET!,
|
|
387
|
-
retailerId: process.env.FLUENT_RETAILER_ID!
|
|
388
|
-
}
|
|
389
|
-
});
|
|
390
|
-
const s3 = new S3DataSource(
|
|
391
|
-
{
|
|
392
|
-
type: 'S3_JSON',
|
|
393
|
-
connectionId: 's3-ingestion',
|
|
394
|
-
name: 'S3 Ingestion',
|
|
395
|
-
s3Config: {
|
|
396
|
-
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
397
|
-
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
398
|
-
region: process.env.AWS_REGION!,
|
|
399
|
-
bucket: 'inventory-updates'
|
|
400
|
-
}
|
|
401
|
-
},
|
|
402
|
-
logger
|
|
403
|
-
);
|
|
404
|
-
|
|
405
|
-
// 1. Read data from S3
|
|
406
|
-
const fileContent = await s3.downloadFile('inventory-updates.json');
|
|
407
|
-
const inventoryData = JSON.parse(fileContent);
|
|
408
|
-
|
|
409
|
-
console.log(`📥 Loaded ${inventoryData.length} records from S3`);
|
|
410
|
-
|
|
411
|
-
// 2. Transform to Fluent format
|
|
412
|
-
const mapper = new UniversalMapper({
|
|
413
|
-
fields: {
|
|
414
|
-
ref: { source: 'sku', required: true },
|
|
415
|
-
quantity: { source: 'qty', resolver: 'sdk.parseInt' },
|
|
416
|
-
groupRef: { source: 'warehouse', required: true },
|
|
417
|
-
catalogue: {
|
|
418
|
-
resolver: (value, sourceData) => ({
|
|
419
|
-
ref: process.env.FLUENT_CATALOGUE_REF!
|
|
420
|
-
})
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
const transformed = await mapper.mapArray(inventoryData);
|
|
426
|
-
|
|
427
|
-
if (!transformed.success) {
|
|
428
|
-
throw new Error(`Transformation failed: ${transformed.errors.length} errors`);
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
// 3. Create batch job
|
|
432
|
-
const job = await client.createJob({
|
|
433
|
-
name: `inventory-import-${Date.now()}`,
|
|
434
|
-
retailerId: process.env.FLUENT_RETAILER_ID!
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
console.log(`📦 Created job: ${job.id}`);
|
|
438
|
-
|
|
439
|
-
// 4. Send batch (use client methods directly)
|
|
440
|
-
const batchResult = await client.sendBatch(job.id, {
|
|
441
|
-
action: 'UPSERT',
|
|
442
|
-
entityType: 'INVENTORY',
|
|
443
|
-
source: 'S3_IMPORT',
|
|
444
|
-
event: 'INVENTORY_UPDATE',
|
|
445
|
-
entities: transformed.data
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
console.log(`✅ Batch sent: ${batchResult.id}`);
|
|
449
|
-
|
|
450
|
-
// 5. Poll for completion (manual polling)
|
|
451
|
-
let status = await client.getBatchStatus(job.id, batchResult.id);
|
|
452
|
-
while (status.status === 'PROCESSING') {
|
|
453
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
454
|
-
status = await client.getBatchStatus(job.id, batchResult.id);
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
console.log(`✅ Batch complete: ${status.processedCount} processed`);
|
|
458
|
-
|
|
459
|
-
return status;
|
|
460
|
-
}
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
---
|
|
464
|
-
|
|
465
|
-
## Versori Platform Integration
|
|
466
|
-
|
|
467
|
-
Use pagination within Versori workflows.
|
|
468
|
-
|
|
469
|
-
### Pattern: Versori Webhook → Paginate → Process
|
|
470
|
-
|
|
471
|
-
```typescript
|
|
472
|
-
import { webhook, fn } from '@versori/run';
|
|
473
|
-
import { createClient, UniversalMapper } from '@fluentcommerce/fc-connect-sdk';
|
|
474
|
-
|
|
475
|
-
export const inventorySync = webhook('inventory-sync', {
|
|
476
|
-
response: { mode: 'sync' }
|
|
477
|
-
})
|
|
478
|
-
.then(fn('sync-inventory', async (ctx) => {
|
|
479
|
-
// Auto-detects Versori context!
|
|
480
|
-
const client = await createClient(ctx); // Auto-detects Versori context
|
|
481
|
-
|
|
482
|
-
// Paginate inventory positions
|
|
483
|
-
const result = await client.graphql({
|
|
484
|
-
query: `
|
|
485
|
-
query GetVirtualPositions($first: Int!, $after: String) {
|
|
486
|
-
virtualPositions(first: $first, after: $after) {
|
|
487
|
-
edges {
|
|
488
|
-
node {
|
|
489
|
-
id
|
|
490
|
-
productRef
|
|
491
|
-
quantity
|
|
492
|
-
groupRef
|
|
493
|
-
status
|
|
494
|
-
}
|
|
495
|
-
cursor
|
|
496
|
-
}
|
|
497
|
-
pageInfo { hasNextPage }
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
`,
|
|
501
|
-
variables: { first: 150 },
|
|
502
|
-
pagination: {
|
|
503
|
-
maxRecords: 10000,
|
|
504
|
-
timeoutMs: 60000, // Versori has request timeouts
|
|
505
|
-
onProgress: (page, records) => {
|
|
506
|
-
ctx.log.info(`Fetched ${records} positions (page ${page})`);
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
const positions = result.data.virtualPositions.edges;
|
|
512
|
-
|
|
513
|
-
// Transform for external system
|
|
514
|
-
const mapper = new UniversalMapper({
|
|
515
|
-
fields: {
|
|
516
|
-
sku: { source: 'node.productRef' },
|
|
517
|
-
qty: { source: 'node.quantity' },
|
|
518
|
-
location: { source: 'node.groupRef' }
|
|
519
|
-
}
|
|
520
|
-
});
|
|
521
|
-
|
|
522
|
-
const transformed = await mapper.mapArray(positions);
|
|
523
|
-
|
|
524
|
-
// Send to external API, S3, or return
|
|
525
|
-
log.info(`✅ Processed ${transformed.data.length} positions`);
|
|
526
|
-
|
|
527
|
-
return {
|
|
528
|
-
status: 'success',
|
|
529
|
-
recordCount: transformed.data.length,
|
|
530
|
-
data: transformed.data
|
|
531
|
-
};
|
|
532
|
-
}));
|
|
533
|
-
```
|
|
534
|
-
|
|
535
|
-
### Scheduled Versori Job with Pagination
|
|
536
|
-
|
|
537
|
-
```typescript
|
|
538
|
-
import { schedule, fn } from '@versori/run';
|
|
539
|
-
import { createClient } from '@fluentcommerce/fc-connect-sdk';
|
|
540
|
-
|
|
541
|
-
export const dailyInventoryExport = schedule('daily-export', {
|
|
542
|
-
cron: '0 2 * * *' // 2 AM daily
|
|
543
|
-
})
|
|
544
|
-
.then(fn('export-inventory', async ({ connections, log, openKv, activation }) => {
|
|
545
|
-
const client = await createClient(ctx); // Auto-detects Versori context
|
|
546
|
-
const kv = openKv();
|
|
547
|
-
|
|
548
|
-
// Get last export timestamp
|
|
549
|
-
const lastExport = await kv.get('last-export-timestamp');
|
|
550
|
-
const fromDate = lastExport?.value || new Date(Date.now() - 86400000).toISOString();
|
|
551
|
-
|
|
552
|
-
log.info(`Exporting inventory changes since ${fromDate}`);
|
|
553
|
-
|
|
554
|
-
// Paginate only changed records
|
|
555
|
-
const result = await client.graphql({
|
|
556
|
-
query: `
|
|
557
|
-
query GetUpdatedPositions($first: Int!, $after: String, $from: DateTime!) {
|
|
558
|
-
virtualPositions(
|
|
559
|
-
first: $first
|
|
560
|
-
after: $after
|
|
561
|
-
updatedOn: { from: $from }
|
|
562
|
-
) {
|
|
563
|
-
edges {
|
|
564
|
-
node {
|
|
565
|
-
id
|
|
566
|
-
productRef
|
|
567
|
-
quantity
|
|
568
|
-
groupRef
|
|
569
|
-
updatedOn
|
|
570
|
-
}
|
|
571
|
-
cursor
|
|
572
|
-
}
|
|
573
|
-
pageInfo { hasNextPage }
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
`,
|
|
577
|
-
variables: { first: 150, from: fromDate },
|
|
578
|
-
pagination: { maxRecords: 50000 }
|
|
579
|
-
});
|
|
580
|
-
|
|
581
|
-
// Store current timestamp
|
|
582
|
-
await kv.set('last-export-timestamp', new Date().toISOString());
|
|
583
|
-
|
|
584
|
-
log.info(`✅ Exported ${result.extensions.autoPagination.totalRecords} positions`);
|
|
585
|
-
|
|
586
|
-
return {
|
|
587
|
-
status: 'success',
|
|
588
|
-
recordCount: result.extensions.autoPagination.totalRecords,
|
|
589
|
-
fromDate,
|
|
590
|
-
toDate: new Date().toISOString()
|
|
591
|
-
};
|
|
592
|
-
}));
|
|
593
|
-
```
|
|
594
|
-
|
|
595
|
-
---
|
|
596
|
-
|
|
597
|
-
## Complete ETL Pipelines
|
|
598
|
-
|
|
599
|
-
### ETL Example 1: Fluent → Transform → Database
|
|
600
|
-
|
|
601
|
-
```typescript
|
|
602
|
-
import { createClient, UniversalMapper, createConsoleLogger } from '@fluentcommerce/fc-connect-sdk';
|
|
603
|
-
import { Pool } from 'pg'; // PostgreSQL client
|
|
604
|
-
|
|
605
|
-
async function syncInventoryToDatabase() {
|
|
606
|
-
const logger = createConsoleLogger();
|
|
607
|
-
|
|
608
|
-
const client = await createClient({
|
|
609
|
-
config: {
|
|
610
|
-
baseUrl: process.env.FLUENT_BASE_URL!,
|
|
611
|
-
clientId: process.env.FLUENT_CLIENT_ID!,
|
|
612
|
-
clientSecret: process.env.FLUENT_CLIENT_SECRET!,
|
|
613
|
-
retailerId: process.env.FLUENT_RETAILER_ID!
|
|
614
|
-
}
|
|
615
|
-
});
|
|
616
|
-
const db = new Pool(/* db config */);
|
|
617
|
-
|
|
618
|
-
// 1. Paginate from Fluent
|
|
619
|
-
const result = await client.graphql({
|
|
620
|
-
query: virtualPositionsQuery,
|
|
621
|
-
variables: { first: 150 },
|
|
622
|
-
pagination: { maxRecords: 20000 }
|
|
623
|
-
});
|
|
624
|
-
|
|
625
|
-
// 2. Transform
|
|
626
|
-
const mapper = new UniversalMapper({
|
|
627
|
-
fields: {
|
|
628
|
-
sku: { source: 'node.productRef' },
|
|
629
|
-
quantity: { source: 'node.quantity', resolver: 'sdk.parseInt' },
|
|
630
|
-
warehouse: { source: 'node.groupRef' },
|
|
631
|
-
last_synced: { resolver: () => new Date().toISOString() }
|
|
632
|
-
}
|
|
633
|
-
});
|
|
634
|
-
|
|
635
|
-
const transformed = await mapper.mapArray(result.data.virtualPositions.edges);
|
|
636
|
-
|
|
637
|
-
// 3. Batch insert to database
|
|
638
|
-
const dbClient = await db.connect();
|
|
639
|
-
try {
|
|
640
|
-
await dbClient.query('BEGIN');
|
|
641
|
-
|
|
642
|
-
for (const record of transformed.data) {
|
|
643
|
-
await dbClient.query(`
|
|
644
|
-
INSERT INTO inventory (sku, quantity, warehouse, last_synced)
|
|
645
|
-
VALUES ($1, $2, $3, $4)
|
|
646
|
-
ON CONFLICT (sku, warehouse)
|
|
647
|
-
DO UPDATE SET quantity = $2, last_synced = $4
|
|
648
|
-
`, [record.sku, record.quantity, record.warehouse, record.last_synced]);
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
await dbClient.query('COMMIT');
|
|
652
|
-
console.log(`✅ Synced ${transformed.data.length} records to database`);
|
|
653
|
-
} catch (error) {
|
|
654
|
-
await dbClient.query('ROLLBACK');
|
|
655
|
-
throw error;
|
|
656
|
-
} finally {
|
|
657
|
-
dbClient.release();
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
```
|
|
661
|
-
|
|
662
|
-
### ETL Example 2: S3 → Transform → Paginate → Fluent
|
|
663
|
-
|
|
664
|
-
```typescript
|
|
665
|
-
import { createClient, S3DataSource, UniversalMapper, createConsoleLogger } from '@fluentcommerce/fc-connect-sdk';
|
|
666
|
-
|
|
667
|
-
async function importOrdersFromS3() {
|
|
668
|
-
const logger = createConsoleLogger();
|
|
669
|
-
|
|
670
|
-
const client = await createClient({
|
|
671
|
-
config: {
|
|
672
|
-
baseUrl: process.env.FLUENT_BASE_URL!,
|
|
673
|
-
clientId: process.env.FLUENT_CLIENT_ID!,
|
|
674
|
-
clientSecret: process.env.FLUENT_CLIENT_SECRET!,
|
|
675
|
-
retailerId: process.env.FLUENT_RETAILER_ID!
|
|
676
|
-
}
|
|
677
|
-
});
|
|
678
|
-
const s3 = new S3DataSource(
|
|
679
|
-
{
|
|
680
|
-
type: 'S3_JSON',
|
|
681
|
-
connectionId: 's3-orders',
|
|
682
|
-
name: 'S3 Orders',
|
|
683
|
-
s3Config: {
|
|
684
|
-
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
685
|
-
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
686
|
-
region: process.env.AWS_REGION!,
|
|
687
|
-
bucket: 'orders-bucket'
|
|
688
|
-
}
|
|
689
|
-
},
|
|
690
|
-
logger
|
|
691
|
-
);
|
|
692
|
-
|
|
693
|
-
// 1. Read from S3
|
|
694
|
-
const orders = JSON.parse(await s3.downloadFile('orders.json') as string);
|
|
695
|
-
|
|
696
|
-
// 2. Transform
|
|
697
|
-
const mapper = new UniversalMapper({
|
|
698
|
-
fields: {
|
|
699
|
-
ref: { source: 'order_id', required: true },
|
|
700
|
-
type: { resolver: () => 'STANDARD' },
|
|
701
|
-
totalPrice: { source: 'total', resolver: 'sdk.parseFloat' },
|
|
702
|
-
items: {
|
|
703
|
-
resolver: (value, sourceData) => {
|
|
704
|
-
return sourceData.line_items.map(item => ({
|
|
705
|
-
productRef: item.sku,
|
|
706
|
-
quantity: item.qty,
|
|
707
|
-
price: item.price
|
|
708
|
-
}));
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
});
|
|
713
|
-
|
|
714
|
-
const transformed = await mapper.mapArray(orders);
|
|
715
|
-
|
|
716
|
-
// 3. Create orders in Fluent (batched)
|
|
717
|
-
const job = await client.createJob({
|
|
718
|
-
name: `order-import-${Date.now()}`,
|
|
719
|
-
retailerId: process.env.FLUENT_RETAILER_ID!
|
|
720
|
-
});
|
|
721
|
-
|
|
722
|
-
const batch = await client.sendBatch(job.id, {
|
|
723
|
-
entities: transformed.data,
|
|
724
|
-
entityType: 'ORDER'
|
|
725
|
-
});
|
|
726
|
-
|
|
727
|
-
// Poll for batch completion
|
|
728
|
-
let status = await client.getBatchStatus(job.id, batch.id);
|
|
729
|
-
while (status.status === 'PROCESSING') {
|
|
730
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
731
|
-
status = await client.getBatchStatus(job.id, batch.id);
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
console.log(`✅ Imported ${transformed.data.length} orders`);
|
|
735
|
-
}
|
|
736
|
-
```
|
|
737
|
-
|
|
738
|
-
---
|
|
739
|
-
|
|
740
|
-
## Performance Optimization Patterns
|
|
741
|
-
|
|
742
|
-
### Pattern 1: Parallel Pagination (Multiple Entity Types)
|
|
743
|
-
|
|
744
|
-
```typescript
|
|
745
|
-
import { createClient, UniversalMapper, createConsoleLogger } from '@fluentcommerce/fc-connect-sdk';
|
|
746
|
-
|
|
747
|
-
async function fetchAllEntitiesInParallel() {
|
|
748
|
-
const logger = createConsoleLogger();
|
|
749
|
-
|
|
750
|
-
const client = await createClient({
|
|
751
|
-
config: {
|
|
752
|
-
baseUrl: process.env.FLUENT_BASE_URL!,
|
|
753
|
-
clientId: process.env.FLUENT_CLIENT_ID!,
|
|
754
|
-
clientSecret: process.env.FLUENT_CLIENT_SECRET!,
|
|
755
|
-
retailerId: process.env.FLUENT_RETAILER_ID!
|
|
756
|
-
}
|
|
757
|
-
});
|
|
758
|
-
|
|
759
|
-
// Fetch multiple entity types in parallel
|
|
760
|
-
const [positions, locations, products] = await Promise.all([
|
|
761
|
-
client.graphql({
|
|
762
|
-
query: virtualPositionsQuery,
|
|
763
|
-
variables: { first: 150 },
|
|
764
|
-
pagination: { maxRecords: 10000 }
|
|
765
|
-
}),
|
|
766
|
-
client.graphql({
|
|
767
|
-
query: locationsQuery,
|
|
768
|
-
variables: { first: 100 },
|
|
769
|
-
pagination: { maxRecords: 5000 }
|
|
770
|
-
}),
|
|
771
|
-
client.graphql({
|
|
772
|
-
query: productsQuery,
|
|
773
|
-
variables: { first: 75 },
|
|
774
|
-
pagination: { maxRecords: 5000 }
|
|
775
|
-
})
|
|
776
|
-
]);
|
|
777
|
-
|
|
778
|
-
return {
|
|
779
|
-
positions: positions.data.virtualPositions.edges,
|
|
780
|
-
locations: locations.data.locations.edges,
|
|
781
|
-
products: products.data.products.edges
|
|
782
|
-
};
|
|
783
|
-
}
|
|
784
|
-
```
|
|
785
|
-
|
|
786
|
-
### Pattern 2: Streaming Transformation
|
|
787
|
-
|
|
788
|
-
Transform data as it's paginated (memory efficient):
|
|
789
|
-
|
|
790
|
-
```typescript
|
|
791
|
-
import { createClient, UniversalMapper, createConsoleLogger } from '@fluentcommerce/fc-connect-sdk';
|
|
792
|
-
|
|
793
|
-
async function streamingTransform() {
|
|
794
|
-
const logger = createConsoleLogger();
|
|
795
|
-
|
|
796
|
-
const client = await createClient({
|
|
797
|
-
config: {
|
|
798
|
-
baseUrl: process.env.FLUENT_BASE_URL!,
|
|
799
|
-
clientId: process.env.FLUENT_CLIENT_ID!,
|
|
800
|
-
clientSecret: process.env.FLUENT_CLIENT_SECRET!,
|
|
801
|
-
retailerId: process.env.FLUENT_RETAILER_ID!
|
|
802
|
-
}
|
|
803
|
-
});
|
|
804
|
-
const mapper = new UniversalMapper(/* config */);
|
|
805
|
-
let processedCount = 0;
|
|
806
|
-
|
|
807
|
-
const result = await client.graphql({
|
|
808
|
-
query: virtualPositionsQuery,
|
|
809
|
-
variables: { first: 150 },
|
|
810
|
-
pagination: {
|
|
811
|
-
maxRecords: 50000,
|
|
812
|
-
onProgress: async (page, totalRecords) => {
|
|
813
|
-
// Note: This runs after EACH page, but we can't transform mid-pagination
|
|
814
|
-
// For true streaming, use manual pagination instead
|
|
815
|
-
console.log(`Progress: ${totalRecords} records`);
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
});
|
|
819
|
-
|
|
820
|
-
// Transform after all pages fetched
|
|
821
|
-
const transformed = await mapper.mapArray(result.data.virtualPositions.edges);
|
|
822
|
-
return transformed.data;
|
|
823
|
-
}
|
|
824
|
-
```
|
|
825
|
-
|
|
826
|
-
---
|
|
827
|
-
|
|
828
|
-
## Key Takeaways
|
|
829
|
-
|
|
830
|
-
✅ **UniversalMapper**: Transform paginated data with field mapping and resolvers
|
|
831
|
-
✅ **ExtractionOrchestrator**: Export paginated data to S3 in JSON/Parquet format
|
|
832
|
-
✅ **Batch Ingestion**: Reverse flow from S3/external → Fluent
|
|
833
|
-
✅ **Versori Integration**: Auto-detects context, works in webhooks/schedules
|
|
834
|
-
✅ **ETL Pipelines**: Chain pagination with transformation and destination writes
|
|
835
|
-
✅ **Performance**: Use parallel fetching for multiple entity types
|
|
836
|
-
|
|
837
|
-
---
|
|
838
|
-
|
|
839
|
-
## Next Steps
|
|
840
|
-
|
|
841
|
-
Learn how to troubleshoot and optimize pagination:
|
|
842
|
-
|
|
843
|
-
**Continue to:** [Module 6: Troubleshooting →](./auto-pagination-06-troubleshooting.md)
|
|
844
|
-
|
|
845
|
-
---
|
|
846
|
-
|
|
847
|
-
[← Back to Index](../../advanced-services/advanced-services-readme.md) | [← Previous: Advanced Patterns](./auto-pagination-04-advanced-patterns.md) | [Next: Troubleshooting →](./auto-pagination-06-troubleshooting.md)
|
|
1
|
+
# Module 5: SDK Ecosystem Integration
|
|
2
|
+
|
|
3
|
+
> **Learning Objective:** Integrate auto-pagination with other SDK services like UniversalMapper, ExtractionOrchestrator, S3DataSource, and Versori platform.
|
|
4
|
+
>
|
|
5
|
+
> **Level:** Advanced
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
1. `Pagination + UniversalMapper (Transform Data)`
|
|
10
|
+
2. `Pagination + ExtractionOrchestrator (Export to S3)`
|
|
11
|
+
3. `Pagination + Batch Ingestion (Reverse Flow)`
|
|
12
|
+
4. [Versori Platform Integration](#versori-platform-integration)
|
|
13
|
+
5. [Complete ETL Pipelines](#complete-etl-pipelines)
|
|
14
|
+
6. [Performance Optimization Patterns](#performance-optimization-patterns)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Pagination + UniversalMapper
|
|
19
|
+
|
|
20
|
+
Transform paginated data using SDK's Universal Mapper.
|
|
21
|
+
|
|
22
|
+
### Pattern: Fetch → Transform → Process
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { createClient, UniversalMapper } from '@fluentcommerce/fc-connect-sdk';
|
|
26
|
+
|
|
27
|
+
async function fetchAndTransformInventory() {
|
|
28
|
+
const client = await createClient({
|
|
29
|
+
config: {
|
|
30
|
+
baseUrl: process.env.FLUENT_BASE_URL!,
|
|
31
|
+
clientId: process.env.FLUENT_CLIENT_ID!,
|
|
32
|
+
clientSecret: process.env.FLUENT_CLIENT_SECRET!,
|
|
33
|
+
retailerId: process.env.FLUENT_RETAILER_ID!
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// 1. Fetch data with auto-pagination
|
|
38
|
+
const result = await client.graphql({
|
|
39
|
+
query: `
|
|
40
|
+
query GetVirtualPositions($first: Int!, $after: String) {
|
|
41
|
+
virtualPositions(first: $first, after: $after) {
|
|
42
|
+
edges {
|
|
43
|
+
node {
|
|
44
|
+
id
|
|
45
|
+
productRef
|
|
46
|
+
quantity
|
|
47
|
+
groupRef
|
|
48
|
+
status
|
|
49
|
+
updatedOn
|
|
50
|
+
}
|
|
51
|
+
cursor
|
|
52
|
+
}
|
|
53
|
+
pageInfo { hasNextPage }
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
`,
|
|
57
|
+
variables: { first: 150 },
|
|
58
|
+
pagination: {
|
|
59
|
+
maxRecords: 10000,
|
|
60
|
+
onProgress: (page, records) => {
|
|
61
|
+
console.log(`📄 Fetched ${records} positions...`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// 2. Define mapping configuration
|
|
67
|
+
const mappingConfig = {
|
|
68
|
+
fields: {
|
|
69
|
+
sku: { source: 'node.productRef', required: true },
|
|
70
|
+
qty: { source: 'node.quantity', resolver: 'sdk.parseInt' },
|
|
71
|
+
location: { source: 'node.groupRef', required: true },
|
|
72
|
+
status: { source: 'node.status', resolver: 'sdk.lowercase' },
|
|
73
|
+
lastUpdated: { source: 'node.updatedOn', resolver: 'sdk.formatDate' }
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// 3. Transform data with UniversalMapper
|
|
78
|
+
const mapper = new UniversalMapper(mappingConfig);
|
|
79
|
+
const transformed = await mapper.mapArray(result.data.virtualPositions.edges);
|
|
80
|
+
|
|
81
|
+
if (!transformed.success) {
|
|
82
|
+
console.error('Mapping errors:', transformed.errors);
|
|
83
|
+
throw new Error('Data transformation failed');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 4. Process transformed data
|
|
87
|
+
console.log(`✅ Transformed ${transformed.data.length} records`);
|
|
88
|
+
|
|
89
|
+
return transformed.data;
|
|
90
|
+
// [{ sku: 'SKU-001', qty: 150, location: 'WH-EAST', status: 'active', lastUpdated: '2024-01-15' }, ...]
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Custom Resolvers with Pagination
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
// Define custom transformation logic
|
|
98
|
+
const mapper = new UniversalMapper(mappingConfig, {
|
|
99
|
+
customResolvers: {
|
|
100
|
+
'custom.calculateValue': (value, sourceData, helpers) => {
|
|
101
|
+
const qty = helpers.parseIntSafe(sourceData.node.quantity, 0);
|
|
102
|
+
const price = helpers.parseFloatSafe(sourceData.node.price, 0);
|
|
103
|
+
return qty * price;
|
|
104
|
+
},
|
|
105
|
+
'custom.determineAvailability': (value, sourceData) => {
|
|
106
|
+
return sourceData.node.quantity > 0 && sourceData.node.status === 'ACTIVE'
|
|
107
|
+
? 'IN_STOCK'
|
|
108
|
+
: 'OUT_OF_STOCK';
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const paginatedResult = await client.graphql({ query, variables, pagination });
|
|
114
|
+
const transformed = await mapper.mapArray(paginatedResult.data.virtualPositions.edges);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## When to Use FluentClient.graphql() vs ExtractionOrchestrator
|
|
120
|
+
|
|
121
|
+
**Decision Guide:**
|
|
122
|
+
|
|
123
|
+
| Scenario | Use | Why |
|
|
124
|
+
|----------|-----|-----|
|
|
125
|
+
| **Query + Transform Only** | `FluentClient.graphql()` with `pagination` | Direct access, minimal overhead, full control |
|
|
126
|
+
| **Query + Export to S3/SFTP** | `ExtractionOrchestrator` | Built-in export, file generation, retry logic |
|
|
127
|
+
| **Custom Processing Logic** | `FluentClient.graphql()` with `pagination` | Maximum flexibility for your workflow |
|
|
128
|
+
| **Standard ETL Pipelines** | `ExtractionOrchestrator` | Pre-built patterns, less code |
|
|
129
|
+
| **Multiple Export Formats** | `ExtractionOrchestrator` | Supports JSON, XML, CSV, Parquet |
|
|
130
|
+
| **Need Progress Tracking** | Both support via callbacks | Use whichever fits your workflow |
|
|
131
|
+
|
|
132
|
+
### FluentClient.graphql() - Direct Approach
|
|
133
|
+
|
|
134
|
+
**Best for:** Custom workflows, transformation-only, direct data access
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import { createClient, UniversalMapper } from '@fluentcommerce/fc-connect-sdk';
|
|
138
|
+
|
|
139
|
+
const client = await createClient({ config: { /* ... */ } });
|
|
140
|
+
|
|
141
|
+
// 1. Fetch with auto-pagination
|
|
142
|
+
const result = await client.graphql({
|
|
143
|
+
query: `query GetVirtualPositions($first: Int!, $after: String) { ... }`,
|
|
144
|
+
variables: { first: 150 },
|
|
145
|
+
pagination: { maxRecords: 10000 }
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// 2. Transform with UniversalMapper
|
|
149
|
+
const mapper = new UniversalMapper({ fields: { /* ... */ } });
|
|
150
|
+
const transformed = await mapper.mapArray(result.data.virtualPositions.edges);
|
|
151
|
+
|
|
152
|
+
// 3. Do whatever you want with the data
|
|
153
|
+
await yourCustomLogic(transformed.data);
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### ExtractionOrchestrator - High-Level Approach
|
|
157
|
+
|
|
158
|
+
**Best for:** Standard exports to S3/SFTP, pre-built patterns
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
import { ExtractionOrchestrator, S3DataSource } from '@fluentcommerce/fc-connect-sdk';
|
|
162
|
+
|
|
163
|
+
const orchestrator = new ExtractionOrchestrator(client, logger);
|
|
164
|
+
|
|
165
|
+
// All-in-one: Query + Transform + Export
|
|
166
|
+
const result = await orchestrator.extract({
|
|
167
|
+
query: `query GetVirtualPositions($first: Int!, $after: String) { ... }`,
|
|
168
|
+
extractionPath: 'virtualPositions.edges',
|
|
169
|
+
destination: s3DataSource,
|
|
170
|
+
format: 'json',
|
|
171
|
+
filename: 'inventory-export.json',
|
|
172
|
+
pagination: { maxRecords: 10000 }
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// File is already on S3!
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Key Difference:** `ExtractionOrchestrator` = `FluentClient.graphql()` + file export + retry logic + pre-built patterns.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Pagination + ExtractionOrchestrator
|
|
183
|
+
|
|
184
|
+
Export paginated data to S3 using ExtractionOrchestrator.
|
|
185
|
+
|
|
186
|
+
### Pattern: Fetch → Transform → Export
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
import {
|
|
190
|
+
createClient,
|
|
191
|
+
ExtractionOrchestrator,
|
|
192
|
+
S3DataSource,
|
|
193
|
+
UniversalMapper,
|
|
194
|
+
createConsoleLogger
|
|
195
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
196
|
+
|
|
197
|
+
async function exportInventoryToS3() {
|
|
198
|
+
const logger = createConsoleLogger();
|
|
199
|
+
|
|
200
|
+
const client = await createClient({
|
|
201
|
+
config: {
|
|
202
|
+
baseUrl: process.env.FLUENT_BASE_URL!,
|
|
203
|
+
clientId: process.env.FLUENT_CLIENT_ID!,
|
|
204
|
+
clientSecret: process.env.FLUENT_CLIENT_SECRET!,
|
|
205
|
+
retailerId: process.env.FLUENT_RETAILER_ID!
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// 1. Setup S3 destination
|
|
210
|
+
const s3 = new S3DataSource(
|
|
211
|
+
{
|
|
212
|
+
type: 'S3_JSON',
|
|
213
|
+
connectionId: 's3-export',
|
|
214
|
+
name: 'S3 Export',
|
|
215
|
+
s3Config: {
|
|
216
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
217
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
218
|
+
region: process.env.AWS_REGION!,
|
|
219
|
+
bucket: 'inventory-exports'
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
logger
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
// 2. Paginate inventory data
|
|
226
|
+
const result = await client.graphql({
|
|
227
|
+
query: `
|
|
228
|
+
query GetVirtualPositions($first: Int!, $after: String) {
|
|
229
|
+
virtualPositions(first: $first, after: $after) {
|
|
230
|
+
edges {
|
|
231
|
+
node {
|
|
232
|
+
id
|
|
233
|
+
productRef
|
|
234
|
+
quantity
|
|
235
|
+
groupRef
|
|
236
|
+
status
|
|
237
|
+
createdOn
|
|
238
|
+
updatedOn
|
|
239
|
+
}
|
|
240
|
+
cursor
|
|
241
|
+
}
|
|
242
|
+
pageInfo { hasNextPage }
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
`,
|
|
246
|
+
variables: { first: 150 },
|
|
247
|
+
pagination: {
|
|
248
|
+
maxRecords: 15000,
|
|
249
|
+
timeoutMs: 300000
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
console.log(`✅ Fetched ${result.extensions.autoPagination.totalRecords} positions`);
|
|
254
|
+
|
|
255
|
+
// 3. Transform data
|
|
256
|
+
const mapper = new UniversalMapper({
|
|
257
|
+
fields: {
|
|
258
|
+
sku: { source: 'node.productRef' },
|
|
259
|
+
quantity: { source: 'node.quantity', resolver: 'sdk.parseInt' },
|
|
260
|
+
warehouse: { source: 'node.groupRef' },
|
|
261
|
+
status: { source: 'node.status' },
|
|
262
|
+
lastModified: { source: 'node.updatedOn', resolver: 'sdk.formatDate' }
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
const transformed = await mapper.mapArray(result.data.virtualPositions.edges);
|
|
267
|
+
|
|
268
|
+
if (!transformed.success) {
|
|
269
|
+
throw new Error(`Transformation failed: ${transformed.errors.length} errors`);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// 4. Export to S3
|
|
273
|
+
const timestamp = new Date().toISOString().split('T')[0];
|
|
274
|
+
const filename = `inventory-export-${timestamp}.json`;
|
|
275
|
+
|
|
276
|
+
await s3.uploadFile(filename, JSON.stringify(transformed.data, null, 2));
|
|
277
|
+
|
|
278
|
+
console.log(`✅ Exported to s3://inventory-exports/${filename}`);
|
|
279
|
+
|
|
280
|
+
return {
|
|
281
|
+
filename,
|
|
282
|
+
recordCount: transformed.data.length,
|
|
283
|
+
stats: result.extensions.autoPagination
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Chunked S3 Export (Large Datasets)
|
|
289
|
+
|
|
290
|
+
For very large datasets, export in chunks to avoid memory issues:
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
import { createClient, S3DataSource, UniversalMapper, createConsoleLogger } from '@fluentcommerce/fc-connect-sdk';
|
|
294
|
+
|
|
295
|
+
async function exportInventoryInChunks() {
|
|
296
|
+
const logger = createConsoleLogger();
|
|
297
|
+
|
|
298
|
+
const client = await createClient({
|
|
299
|
+
config: {
|
|
300
|
+
baseUrl: process.env.FLUENT_BASE_URL!,
|
|
301
|
+
clientId: process.env.FLUENT_CLIENT_ID!,
|
|
302
|
+
clientSecret: process.env.FLUENT_CLIENT_SECRET!,
|
|
303
|
+
retailerId: process.env.FLUENT_RETAILER_ID!
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
const s3 = new S3DataSource(
|
|
307
|
+
{
|
|
308
|
+
type: 'S3_JSON',
|
|
309
|
+
connectionId: 's3-chunked-export',
|
|
310
|
+
name: 'S3 Chunked Export',
|
|
311
|
+
s3Config: {
|
|
312
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
313
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
314
|
+
region: process.env.AWS_REGION!,
|
|
315
|
+
bucket: 'inventory-exports'
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
logger
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
let after: string | null = null;
|
|
322
|
+
let chunkNumber = 0;
|
|
323
|
+
const CHUNK_SIZE = 5000;
|
|
324
|
+
|
|
325
|
+
while (true) {
|
|
326
|
+
// Fetch chunk with pagination
|
|
327
|
+
const result = await client.graphql({
|
|
328
|
+
query: virtualPositionsQuery,
|
|
329
|
+
variables: { first: 150, after },
|
|
330
|
+
pagination: {
|
|
331
|
+
maxRecords: CHUNK_SIZE,
|
|
332
|
+
maxPages: 35 // ~35 pages * 150 = 5250 records
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
const edges = result.data.virtualPositions.edges;
|
|
337
|
+
if (edges.length === 0) break;
|
|
338
|
+
|
|
339
|
+
// Transform chunk
|
|
340
|
+
const mapper = new UniversalMapper(mappingConfig);
|
|
341
|
+
const transformed = await mapper.mapArray(edges);
|
|
342
|
+
|
|
343
|
+
// Export chunk to S3
|
|
344
|
+
const filename = `inventory-chunk-${chunkNumber.toString().padStart(4, '0')}.json`;
|
|
345
|
+
await s3.uploadFile(filename, JSON.stringify(transformed.data, null, 2));
|
|
346
|
+
|
|
347
|
+
console.log(`✅ Exported chunk ${chunkNumber}: ${edges.length} records`);
|
|
348
|
+
|
|
349
|
+
// Check if we got fewer records than max (means we're done)
|
|
350
|
+
if (result.extensions.autoPagination.truncated &&
|
|
351
|
+
result.extensions.autoPagination.truncationReason === 'maxRecords') {
|
|
352
|
+
after = edges[edges.length - 1].cursor;
|
|
353
|
+
chunkNumber++;
|
|
354
|
+
} else {
|
|
355
|
+
break; // No more data
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
console.log(`✅ Export complete: ${chunkNumber + 1} chunks`);
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## Pagination + Batch Ingestion
|
|
366
|
+
|
|
367
|
+
Reverse flow: Fetch from S3 → Paginate → Transform → Batch Ingest to Fluent.
|
|
368
|
+
|
|
369
|
+
### Pattern: S3 → Transform → Batch API
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
import {
|
|
373
|
+
createClient,
|
|
374
|
+
S3DataSource,
|
|
375
|
+
UniversalMapper,
|
|
376
|
+
createConsoleLogger
|
|
377
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
378
|
+
|
|
379
|
+
async function ingestInventoryFromS3() {
|
|
380
|
+
const logger = createConsoleLogger();
|
|
381
|
+
|
|
382
|
+
const client = await createClient({
|
|
383
|
+
config: {
|
|
384
|
+
baseUrl: process.env.FLUENT_BASE_URL!,
|
|
385
|
+
clientId: process.env.FLUENT_CLIENT_ID!,
|
|
386
|
+
clientSecret: process.env.FLUENT_CLIENT_SECRET!,
|
|
387
|
+
retailerId: process.env.FLUENT_RETAILER_ID!
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
const s3 = new S3DataSource(
|
|
391
|
+
{
|
|
392
|
+
type: 'S3_JSON',
|
|
393
|
+
connectionId: 's3-ingestion',
|
|
394
|
+
name: 'S3 Ingestion',
|
|
395
|
+
s3Config: {
|
|
396
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
397
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
398
|
+
region: process.env.AWS_REGION!,
|
|
399
|
+
bucket: 'inventory-updates'
|
|
400
|
+
}
|
|
401
|
+
},
|
|
402
|
+
logger
|
|
403
|
+
);
|
|
404
|
+
|
|
405
|
+
// 1. Read data from S3
|
|
406
|
+
const fileContent = await s3.downloadFile('inventory-updates.json');
|
|
407
|
+
const inventoryData = JSON.parse(fileContent);
|
|
408
|
+
|
|
409
|
+
console.log(`📥 Loaded ${inventoryData.length} records from S3`);
|
|
410
|
+
|
|
411
|
+
// 2. Transform to Fluent format
|
|
412
|
+
const mapper = new UniversalMapper({
|
|
413
|
+
fields: {
|
|
414
|
+
ref: { source: 'sku', required: true },
|
|
415
|
+
quantity: { source: 'qty', resolver: 'sdk.parseInt' },
|
|
416
|
+
groupRef: { source: 'warehouse', required: true },
|
|
417
|
+
catalogue: {
|
|
418
|
+
resolver: (value, sourceData) => ({
|
|
419
|
+
ref: process.env.FLUENT_CATALOGUE_REF!
|
|
420
|
+
})
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
const transformed = await mapper.mapArray(inventoryData);
|
|
426
|
+
|
|
427
|
+
if (!transformed.success) {
|
|
428
|
+
throw new Error(`Transformation failed: ${transformed.errors.length} errors`);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// 3. Create batch job
|
|
432
|
+
const job = await client.createJob({
|
|
433
|
+
name: `inventory-import-${Date.now()}`,
|
|
434
|
+
retailerId: process.env.FLUENT_RETAILER_ID!
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
console.log(`📦 Created job: ${job.id}`);
|
|
438
|
+
|
|
439
|
+
// 4. Send batch (use client methods directly)
|
|
440
|
+
const batchResult = await client.sendBatch(job.id, {
|
|
441
|
+
action: 'UPSERT',
|
|
442
|
+
entityType: 'INVENTORY',
|
|
443
|
+
source: 'S3_IMPORT',
|
|
444
|
+
event: 'INVENTORY_UPDATE',
|
|
445
|
+
entities: transformed.data
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
console.log(`✅ Batch sent: ${batchResult.id}`);
|
|
449
|
+
|
|
450
|
+
// 5. Poll for completion (manual polling)
|
|
451
|
+
let status = await client.getBatchStatus(job.id, batchResult.id);
|
|
452
|
+
while (status.status === 'PROCESSING') {
|
|
453
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
454
|
+
status = await client.getBatchStatus(job.id, batchResult.id);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
console.log(`✅ Batch complete: ${status.processedCount} processed`);
|
|
458
|
+
|
|
459
|
+
return status;
|
|
460
|
+
}
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## Versori Platform Integration
|
|
466
|
+
|
|
467
|
+
Use pagination within Versori workflows.
|
|
468
|
+
|
|
469
|
+
### Pattern: Versori Webhook → Paginate → Process
|
|
470
|
+
|
|
471
|
+
```typescript
|
|
472
|
+
import { webhook, fn } from '@versori/run';
|
|
473
|
+
import { createClient, UniversalMapper } from '@fluentcommerce/fc-connect-sdk';
|
|
474
|
+
|
|
475
|
+
export const inventorySync = webhook('inventory-sync', {
|
|
476
|
+
response: { mode: 'sync' }
|
|
477
|
+
})
|
|
478
|
+
.then(fn('sync-inventory', async (ctx) => {
|
|
479
|
+
// Auto-detects Versori context!
|
|
480
|
+
const client = await createClient(ctx); // Auto-detects Versori context
|
|
481
|
+
|
|
482
|
+
// Paginate inventory positions
|
|
483
|
+
const result = await client.graphql({
|
|
484
|
+
query: `
|
|
485
|
+
query GetVirtualPositions($first: Int!, $after: String) {
|
|
486
|
+
virtualPositions(first: $first, after: $after) {
|
|
487
|
+
edges {
|
|
488
|
+
node {
|
|
489
|
+
id
|
|
490
|
+
productRef
|
|
491
|
+
quantity
|
|
492
|
+
groupRef
|
|
493
|
+
status
|
|
494
|
+
}
|
|
495
|
+
cursor
|
|
496
|
+
}
|
|
497
|
+
pageInfo { hasNextPage }
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
`,
|
|
501
|
+
variables: { first: 150 },
|
|
502
|
+
pagination: {
|
|
503
|
+
maxRecords: 10000,
|
|
504
|
+
timeoutMs: 60000, // Versori has request timeouts
|
|
505
|
+
onProgress: (page, records) => {
|
|
506
|
+
ctx.log.info(`Fetched ${records} positions (page ${page})`);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
const positions = result.data.virtualPositions.edges;
|
|
512
|
+
|
|
513
|
+
// Transform for external system
|
|
514
|
+
const mapper = new UniversalMapper({
|
|
515
|
+
fields: {
|
|
516
|
+
sku: { source: 'node.productRef' },
|
|
517
|
+
qty: { source: 'node.quantity' },
|
|
518
|
+
location: { source: 'node.groupRef' }
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
const transformed = await mapper.mapArray(positions);
|
|
523
|
+
|
|
524
|
+
// Send to external API, S3, or return
|
|
525
|
+
log.info(`✅ Processed ${transformed.data.length} positions`);
|
|
526
|
+
|
|
527
|
+
return {
|
|
528
|
+
status: 'success',
|
|
529
|
+
recordCount: transformed.data.length,
|
|
530
|
+
data: transformed.data
|
|
531
|
+
};
|
|
532
|
+
}));
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
### Scheduled Versori Job with Pagination
|
|
536
|
+
|
|
537
|
+
```typescript
|
|
538
|
+
import { schedule, fn } from '@versori/run';
|
|
539
|
+
import { createClient } from '@fluentcommerce/fc-connect-sdk';
|
|
540
|
+
|
|
541
|
+
export const dailyInventoryExport = schedule('daily-export', {
|
|
542
|
+
cron: '0 2 * * *' // 2 AM daily
|
|
543
|
+
})
|
|
544
|
+
.then(fn('export-inventory', async ({ connections, log, openKv, activation }) => {
|
|
545
|
+
const client = await createClient(ctx); // Auto-detects Versori context
|
|
546
|
+
const kv = openKv();
|
|
547
|
+
|
|
548
|
+
// Get last export timestamp
|
|
549
|
+
const lastExport = await kv.get('last-export-timestamp');
|
|
550
|
+
const fromDate = lastExport?.value || new Date(Date.now() - 86400000).toISOString();
|
|
551
|
+
|
|
552
|
+
log.info(`Exporting inventory changes since ${fromDate}`);
|
|
553
|
+
|
|
554
|
+
// Paginate only changed records
|
|
555
|
+
const result = await client.graphql({
|
|
556
|
+
query: `
|
|
557
|
+
query GetUpdatedPositions($first: Int!, $after: String, $from: DateTime!) {
|
|
558
|
+
virtualPositions(
|
|
559
|
+
first: $first
|
|
560
|
+
after: $after
|
|
561
|
+
updatedOn: { from: $from }
|
|
562
|
+
) {
|
|
563
|
+
edges {
|
|
564
|
+
node {
|
|
565
|
+
id
|
|
566
|
+
productRef
|
|
567
|
+
quantity
|
|
568
|
+
groupRef
|
|
569
|
+
updatedOn
|
|
570
|
+
}
|
|
571
|
+
cursor
|
|
572
|
+
}
|
|
573
|
+
pageInfo { hasNextPage }
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
`,
|
|
577
|
+
variables: { first: 150, from: fromDate },
|
|
578
|
+
pagination: { maxRecords: 50000 }
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
// Store current timestamp
|
|
582
|
+
await kv.set('last-export-timestamp', new Date().toISOString());
|
|
583
|
+
|
|
584
|
+
log.info(`✅ Exported ${result.extensions.autoPagination.totalRecords} positions`);
|
|
585
|
+
|
|
586
|
+
return {
|
|
587
|
+
status: 'success',
|
|
588
|
+
recordCount: result.extensions.autoPagination.totalRecords,
|
|
589
|
+
fromDate,
|
|
590
|
+
toDate: new Date().toISOString()
|
|
591
|
+
};
|
|
592
|
+
}));
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
---
|
|
596
|
+
|
|
597
|
+
## Complete ETL Pipelines
|
|
598
|
+
|
|
599
|
+
### ETL Example 1: Fluent → Transform → Database
|
|
600
|
+
|
|
601
|
+
```typescript
|
|
602
|
+
import { createClient, UniversalMapper, createConsoleLogger } from '@fluentcommerce/fc-connect-sdk';
|
|
603
|
+
import { Pool } from 'pg'; // PostgreSQL client
|
|
604
|
+
|
|
605
|
+
async function syncInventoryToDatabase() {
|
|
606
|
+
const logger = createConsoleLogger();
|
|
607
|
+
|
|
608
|
+
const client = await createClient({
|
|
609
|
+
config: {
|
|
610
|
+
baseUrl: process.env.FLUENT_BASE_URL!,
|
|
611
|
+
clientId: process.env.FLUENT_CLIENT_ID!,
|
|
612
|
+
clientSecret: process.env.FLUENT_CLIENT_SECRET!,
|
|
613
|
+
retailerId: process.env.FLUENT_RETAILER_ID!
|
|
614
|
+
}
|
|
615
|
+
});
|
|
616
|
+
const db = new Pool(/* db config */);
|
|
617
|
+
|
|
618
|
+
// 1. Paginate from Fluent
|
|
619
|
+
const result = await client.graphql({
|
|
620
|
+
query: virtualPositionsQuery,
|
|
621
|
+
variables: { first: 150 },
|
|
622
|
+
pagination: { maxRecords: 20000 }
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
// 2. Transform
|
|
626
|
+
const mapper = new UniversalMapper({
|
|
627
|
+
fields: {
|
|
628
|
+
sku: { source: 'node.productRef' },
|
|
629
|
+
quantity: { source: 'node.quantity', resolver: 'sdk.parseInt' },
|
|
630
|
+
warehouse: { source: 'node.groupRef' },
|
|
631
|
+
last_synced: { resolver: () => new Date().toISOString() }
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
const transformed = await mapper.mapArray(result.data.virtualPositions.edges);
|
|
636
|
+
|
|
637
|
+
// 3. Batch insert to database
|
|
638
|
+
const dbClient = await db.connect();
|
|
639
|
+
try {
|
|
640
|
+
await dbClient.query('BEGIN');
|
|
641
|
+
|
|
642
|
+
for (const record of transformed.data) {
|
|
643
|
+
await dbClient.query(`
|
|
644
|
+
INSERT INTO inventory (sku, quantity, warehouse, last_synced)
|
|
645
|
+
VALUES ($1, $2, $3, $4)
|
|
646
|
+
ON CONFLICT (sku, warehouse)
|
|
647
|
+
DO UPDATE SET quantity = $2, last_synced = $4
|
|
648
|
+
`, [record.sku, record.quantity, record.warehouse, record.last_synced]);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
await dbClient.query('COMMIT');
|
|
652
|
+
console.log(`✅ Synced ${transformed.data.length} records to database`);
|
|
653
|
+
} catch (error) {
|
|
654
|
+
await dbClient.query('ROLLBACK');
|
|
655
|
+
throw error;
|
|
656
|
+
} finally {
|
|
657
|
+
dbClient.release();
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
### ETL Example 2: S3 → Transform → Paginate → Fluent
|
|
663
|
+
|
|
664
|
+
```typescript
|
|
665
|
+
import { createClient, S3DataSource, UniversalMapper, createConsoleLogger } from '@fluentcommerce/fc-connect-sdk';
|
|
666
|
+
|
|
667
|
+
async function importOrdersFromS3() {
|
|
668
|
+
const logger = createConsoleLogger();
|
|
669
|
+
|
|
670
|
+
const client = await createClient({
|
|
671
|
+
config: {
|
|
672
|
+
baseUrl: process.env.FLUENT_BASE_URL!,
|
|
673
|
+
clientId: process.env.FLUENT_CLIENT_ID!,
|
|
674
|
+
clientSecret: process.env.FLUENT_CLIENT_SECRET!,
|
|
675
|
+
retailerId: process.env.FLUENT_RETAILER_ID!
|
|
676
|
+
}
|
|
677
|
+
});
|
|
678
|
+
const s3 = new S3DataSource(
|
|
679
|
+
{
|
|
680
|
+
type: 'S3_JSON',
|
|
681
|
+
connectionId: 's3-orders',
|
|
682
|
+
name: 'S3 Orders',
|
|
683
|
+
s3Config: {
|
|
684
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
685
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
686
|
+
region: process.env.AWS_REGION!,
|
|
687
|
+
bucket: 'orders-bucket'
|
|
688
|
+
}
|
|
689
|
+
},
|
|
690
|
+
logger
|
|
691
|
+
);
|
|
692
|
+
|
|
693
|
+
// 1. Read from S3
|
|
694
|
+
const orders = JSON.parse(await s3.downloadFile('orders.json') as string);
|
|
695
|
+
|
|
696
|
+
// 2. Transform
|
|
697
|
+
const mapper = new UniversalMapper({
|
|
698
|
+
fields: {
|
|
699
|
+
ref: { source: 'order_id', required: true },
|
|
700
|
+
type: { resolver: () => 'STANDARD' },
|
|
701
|
+
totalPrice: { source: 'total', resolver: 'sdk.parseFloat' },
|
|
702
|
+
items: {
|
|
703
|
+
resolver: (value, sourceData) => {
|
|
704
|
+
return sourceData.line_items.map(item => ({
|
|
705
|
+
productRef: item.sku,
|
|
706
|
+
quantity: item.qty,
|
|
707
|
+
price: item.price
|
|
708
|
+
}));
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
const transformed = await mapper.mapArray(orders);
|
|
715
|
+
|
|
716
|
+
// 3. Create orders in Fluent (batched)
|
|
717
|
+
const job = await client.createJob({
|
|
718
|
+
name: `order-import-${Date.now()}`,
|
|
719
|
+
retailerId: process.env.FLUENT_RETAILER_ID!
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
const batch = await client.sendBatch(job.id, {
|
|
723
|
+
entities: transformed.data,
|
|
724
|
+
entityType: 'ORDER'
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
// Poll for batch completion
|
|
728
|
+
let status = await client.getBatchStatus(job.id, batch.id);
|
|
729
|
+
while (status.status === 'PROCESSING') {
|
|
730
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
731
|
+
status = await client.getBatchStatus(job.id, batch.id);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
console.log(`✅ Imported ${transformed.data.length} orders`);
|
|
735
|
+
}
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
---
|
|
739
|
+
|
|
740
|
+
## Performance Optimization Patterns
|
|
741
|
+
|
|
742
|
+
### Pattern 1: Parallel Pagination (Multiple Entity Types)
|
|
743
|
+
|
|
744
|
+
```typescript
|
|
745
|
+
import { createClient, UniversalMapper, createConsoleLogger } from '@fluentcommerce/fc-connect-sdk';
|
|
746
|
+
|
|
747
|
+
async function fetchAllEntitiesInParallel() {
|
|
748
|
+
const logger = createConsoleLogger();
|
|
749
|
+
|
|
750
|
+
const client = await createClient({
|
|
751
|
+
config: {
|
|
752
|
+
baseUrl: process.env.FLUENT_BASE_URL!,
|
|
753
|
+
clientId: process.env.FLUENT_CLIENT_ID!,
|
|
754
|
+
clientSecret: process.env.FLUENT_CLIENT_SECRET!,
|
|
755
|
+
retailerId: process.env.FLUENT_RETAILER_ID!
|
|
756
|
+
}
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
// Fetch multiple entity types in parallel
|
|
760
|
+
const [positions, locations, products] = await Promise.all([
|
|
761
|
+
client.graphql({
|
|
762
|
+
query: virtualPositionsQuery,
|
|
763
|
+
variables: { first: 150 },
|
|
764
|
+
pagination: { maxRecords: 10000 }
|
|
765
|
+
}),
|
|
766
|
+
client.graphql({
|
|
767
|
+
query: locationsQuery,
|
|
768
|
+
variables: { first: 100 },
|
|
769
|
+
pagination: { maxRecords: 5000 }
|
|
770
|
+
}),
|
|
771
|
+
client.graphql({
|
|
772
|
+
query: productsQuery,
|
|
773
|
+
variables: { first: 75 },
|
|
774
|
+
pagination: { maxRecords: 5000 }
|
|
775
|
+
})
|
|
776
|
+
]);
|
|
777
|
+
|
|
778
|
+
return {
|
|
779
|
+
positions: positions.data.virtualPositions.edges,
|
|
780
|
+
locations: locations.data.locations.edges,
|
|
781
|
+
products: products.data.products.edges
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
### Pattern 2: Streaming Transformation
|
|
787
|
+
|
|
788
|
+
Transform data as it's paginated (memory efficient):
|
|
789
|
+
|
|
790
|
+
```typescript
|
|
791
|
+
import { createClient, UniversalMapper, createConsoleLogger } from '@fluentcommerce/fc-connect-sdk';
|
|
792
|
+
|
|
793
|
+
async function streamingTransform() {
|
|
794
|
+
const logger = createConsoleLogger();
|
|
795
|
+
|
|
796
|
+
const client = await createClient({
|
|
797
|
+
config: {
|
|
798
|
+
baseUrl: process.env.FLUENT_BASE_URL!,
|
|
799
|
+
clientId: process.env.FLUENT_CLIENT_ID!,
|
|
800
|
+
clientSecret: process.env.FLUENT_CLIENT_SECRET!,
|
|
801
|
+
retailerId: process.env.FLUENT_RETAILER_ID!
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
const mapper = new UniversalMapper(/* config */);
|
|
805
|
+
let processedCount = 0;
|
|
806
|
+
|
|
807
|
+
const result = await client.graphql({
|
|
808
|
+
query: virtualPositionsQuery,
|
|
809
|
+
variables: { first: 150 },
|
|
810
|
+
pagination: {
|
|
811
|
+
maxRecords: 50000,
|
|
812
|
+
onProgress: async (page, totalRecords) => {
|
|
813
|
+
// Note: This runs after EACH page, but we can't transform mid-pagination
|
|
814
|
+
// For true streaming, use manual pagination instead
|
|
815
|
+
console.log(`Progress: ${totalRecords} records`);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
// Transform after all pages fetched
|
|
821
|
+
const transformed = await mapper.mapArray(result.data.virtualPositions.edges);
|
|
822
|
+
return transformed.data;
|
|
823
|
+
}
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
---
|
|
827
|
+
|
|
828
|
+
## Key Takeaways
|
|
829
|
+
|
|
830
|
+
✅ **UniversalMapper**: Transform paginated data with field mapping and resolvers
|
|
831
|
+
✅ **ExtractionOrchestrator**: Export paginated data to S3 in JSON/Parquet format
|
|
832
|
+
✅ **Batch Ingestion**: Reverse flow from S3/external → Fluent
|
|
833
|
+
✅ **Versori Integration**: Auto-detects context, works in webhooks/schedules
|
|
834
|
+
✅ **ETL Pipelines**: Chain pagination with transformation and destination writes
|
|
835
|
+
✅ **Performance**: Use parallel fetching for multiple entity types
|
|
836
|
+
|
|
837
|
+
---
|
|
838
|
+
|
|
839
|
+
## Next Steps
|
|
840
|
+
|
|
841
|
+
Learn how to troubleshoot and optimize pagination:
|
|
842
|
+
|
|
843
|
+
**Continue to:** [Module 6: Troubleshooting →](./auto-pagination-06-troubleshooting.md)
|
|
844
|
+
|
|
845
|
+
---
|
|
846
|
+
|
|
847
|
+
[← Back to Index](../../advanced-services/advanced-services-readme.md) | [← Previous: Advanced Patterns](./auto-pagination-04-advanced-patterns.md) | [Next: Troubleshooting →](./auto-pagination-06-troubleshooting.md)
|