@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,826 +1,826 @@
|
|
|
1
|
-
# Resolver Cookbook
|
|
2
|
-
|
|
3
|
-
**Advanced patterns and recipes for production resolvers**
|
|
4
|
-
|
|
5
|
-
Version: 2.0.0
|
|
6
|
-
Last Updated: 2025-01-14
|
|
7
|
-
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
## Table of Contents
|
|
11
|
-
|
|
12
|
-
1. [Async Patterns](#async-patterns)
|
|
13
|
-
2. [Order Management Recipes](#order-management-recipes)
|
|
14
|
-
3. [Inventory Management Recipes](#inventory-management-recipes)
|
|
15
|
-
4. [Performance Optimization](#performance-optimization)
|
|
16
|
-
5. [Error Handling](#error-handling)
|
|
17
|
-
6. [Testing Patterns](#testing-patterns)
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## Async Patterns
|
|
22
|
-
|
|
23
|
-
### Pattern 1: Lookup-or-Create Customer
|
|
24
|
-
|
|
25
|
-
**Use Case:** Ensure customer exists before creating order.
|
|
26
|
-
|
|
27
|
-
```typescript
|
|
28
|
-
builder.field('input.customer.id', async (data, config, helpers) => {
|
|
29
|
-
const customerId = helpers.get(data, 'orders.order.customer.customer-no');
|
|
30
|
-
const email = helpers.get(data, 'orders.order.customer.customer-email');
|
|
31
|
-
|
|
32
|
-
if (!helpers.fluentClient) {
|
|
33
|
-
throw new Error('FluentClient is required for customer lookup');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return await helpers.retry(async () => {
|
|
37
|
-
// Step 1: Try to find existing customer
|
|
38
|
-
const queryResult = await helpers.fluentClient.graphql({
|
|
39
|
-
query: `
|
|
40
|
-
query GetCustomer($username: [String]) {
|
|
41
|
-
customers(username: $username, first: 1) {
|
|
42
|
-
edges {
|
|
43
|
-
node {
|
|
44
|
-
id
|
|
45
|
-
username
|
|
46
|
-
primaryEmail
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
`,
|
|
52
|
-
variables: { username: [customerId] }
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
const existing = queryResult.data?.customers?.edges?.[0]?.node;
|
|
56
|
-
|
|
57
|
-
if (existing) {
|
|
58
|
-
helpers.log?.info('Found existing customer', { id: existing.id });
|
|
59
|
-
return existing.id;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Step 2: Create if not found
|
|
63
|
-
helpers.log?.info('Creating new customer', { customerId, email });
|
|
64
|
-
|
|
65
|
-
const createResult = await helpers.fluentClient.graphql({
|
|
66
|
-
query: `
|
|
67
|
-
mutation CreateCustomer($input: CreateCustomerInput!) {
|
|
68
|
-
createCustomer(input: $input) {
|
|
69
|
-
id
|
|
70
|
-
username
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
`,
|
|
74
|
-
variables: {
|
|
75
|
-
input: {
|
|
76
|
-
username: customerId,
|
|
77
|
-
primaryEmail: email,
|
|
78
|
-
retailer: { id: config.defaultRetailerId }
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
if (createResult.errors) {
|
|
84
|
-
throw new Error(`Failed to create customer: ${createResult.errors[0].message}`);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return createResult.data?.createCustomer?.id;
|
|
88
|
-
}, 3, 500); // Retry 3 times, 500ms delay
|
|
89
|
-
});
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
---
|
|
93
|
-
|
|
94
|
-
### Pattern 2: Parallel API Enrichment
|
|
95
|
-
|
|
96
|
-
**Use Case:** Enrich order items with external data efficiently.
|
|
97
|
-
|
|
98
|
-
```typescript
|
|
99
|
-
builder.field('input.items', async (data, config, helpers) => {
|
|
100
|
-
const items = helpers.ensureArray(
|
|
101
|
-
helpers.get(data, 'orders.order.product-lineitems.product-lineitem')
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
// Fetch all product details in parallel
|
|
105
|
-
const enrichedItems = await Promise.all(
|
|
106
|
-
items.map(async (item) => {
|
|
107
|
-
// Run multiple lookups in parallel for each item
|
|
108
|
-
const [productDetails, inventory, pricing] = await Promise.all([
|
|
109
|
-
fetchProductDetails(item['product-id']),
|
|
110
|
-
checkInventory(item['product-id'], config.defaultLocationId),
|
|
111
|
-
getPricing(item['product-id'], config.priceTier)
|
|
112
|
-
]);
|
|
113
|
-
|
|
114
|
-
return {
|
|
115
|
-
ref: item['@lineitem-id'],
|
|
116
|
-
productRef: item['product-id'],
|
|
117
|
-
quantity: helpers.parseIntSafe(item.quantity, 1),
|
|
118
|
-
price: helpers.parseFloatSafe(item['base-price'], 0),
|
|
119
|
-
|
|
120
|
-
// Enriched data
|
|
121
|
-
attributes: {
|
|
122
|
-
productName: productDetails.name,
|
|
123
|
-
category: productDetails.category,
|
|
124
|
-
availableQty: inventory.available,
|
|
125
|
-
currentPrice: pricing.currentPrice,
|
|
126
|
-
priceWas: pricing.previousPrice
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
})
|
|
130
|
-
);
|
|
131
|
-
|
|
132
|
-
return enrichedItems;
|
|
133
|
-
});
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
---
|
|
137
|
-
|
|
138
|
-
### Pattern 3: Rate-Limited API Calls with Batch Processing
|
|
139
|
-
|
|
140
|
-
**Use Case:** Process large item lists without overwhelming external APIs.
|
|
141
|
-
|
|
142
|
-
```typescript
|
|
143
|
-
builder.field('input.enrichedItems', async (data, config, helpers) => {
|
|
144
|
-
const items = helpers.ensureArray(
|
|
145
|
-
helpers.get(data, 'orders.order.product-lineitems.product-lineitem')
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
// Process in batches of 5 to respect API rate limits
|
|
149
|
-
const enrichedItems = await helpers.batchProcess(
|
|
150
|
-
items,
|
|
151
|
-
async (item, index) => {
|
|
152
|
-
try {
|
|
153
|
-
// Add delay to respect rate limits
|
|
154
|
-
await new Promise(resolve => setTimeout(resolve, 200)); // 5 req/sec
|
|
155
|
-
|
|
156
|
-
const productData = await helpers.retry(
|
|
157
|
-
async () => {
|
|
158
|
-
const response = await fetch(
|
|
159
|
-
`https://api.product-service.com/products/${item['product-id']}`,
|
|
160
|
-
{
|
|
161
|
-
headers: {
|
|
162
|
-
'Authorization': `Bearer ${config.productApiToken}`
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
if (!response.ok) {
|
|
168
|
-
throw new Error(`API error: ${response.status}`);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
return await response.json();
|
|
172
|
-
},
|
|
173
|
-
3, // Max 3 retries
|
|
174
|
-
1000 // 1 second delay between retries
|
|
175
|
-
);
|
|
176
|
-
|
|
177
|
-
return {
|
|
178
|
-
ref: item['@lineitem-id'],
|
|
179
|
-
productRef: item['product-id'],
|
|
180
|
-
quantity: helpers.parseIntSafe(item.quantity, 1),
|
|
181
|
-
price: helpers.parseFloatSafe(item['base-price'], 0),
|
|
182
|
-
attributes: {
|
|
183
|
-
...productData
|
|
184
|
-
}
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
} catch (error) {
|
|
188
|
-
helpers.log?.error(`Failed to enrich item ${item['@lineitem-id']}`, { error });
|
|
189
|
-
|
|
190
|
-
// Return basic item without enrichment
|
|
191
|
-
return {
|
|
192
|
-
ref: item['@lineitem-id'],
|
|
193
|
-
productRef: item['product-id'],
|
|
194
|
-
quantity: helpers.parseIntSafe(item.quantity, 1),
|
|
195
|
-
price: helpers.parseFloatSafe(item['base-price'], 0)
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
},
|
|
199
|
-
5 // Process 5 items at a time
|
|
200
|
-
);
|
|
201
|
-
|
|
202
|
-
return enrichedItems;
|
|
203
|
-
});
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
---
|
|
207
|
-
|
|
208
|
-
## Order Management Recipes
|
|
209
|
-
|
|
210
|
-
### Recipe 1: Multi-Source Total Calculation
|
|
211
|
-
|
|
212
|
-
**Use Case:** Calculate order total from various sources with validation.
|
|
213
|
-
|
|
214
|
-
```typescript
|
|
215
|
-
builder.calculate('input.totalPrice', (data, helpers) => {
|
|
216
|
-
// Get line items
|
|
217
|
-
const items = helpers.ensureArray(
|
|
218
|
-
helpers.get(data, 'orders.order.product-lineitems.product-lineitem', [])
|
|
219
|
-
);
|
|
220
|
-
|
|
221
|
-
// Calculate merchandise total
|
|
222
|
-
const merchandiseTotal = helpers.sum(items, item =>
|
|
223
|
-
helpers.parseFloatSafe(item['gross-price'], 0)
|
|
224
|
-
);
|
|
225
|
-
|
|
226
|
-
// Get shipping total
|
|
227
|
-
const shippingTotal = helpers.parseFloatSafe(
|
|
228
|
-
helpers.get(data, 'orders.order.totals.shipping-total.gross-price'),
|
|
229
|
-
0
|
|
230
|
-
);
|
|
231
|
-
|
|
232
|
-
// Get tax total
|
|
233
|
-
const taxTotal = helpers.parseFloatSafe(
|
|
234
|
-
helpers.get(data, 'orders.order.totals.order-total.tax'),
|
|
235
|
-
0
|
|
236
|
-
);
|
|
237
|
-
|
|
238
|
-
// Get adjustments (discounts/fees)
|
|
239
|
-
const adjustment = helpers.parseFloatSafe(
|
|
240
|
-
helpers.get(data, 'orders.order.totals.adjustment-total.gross-price'),
|
|
241
|
-
0
|
|
242
|
-
);
|
|
243
|
-
|
|
244
|
-
// Calculate final total
|
|
245
|
-
const total = merchandiseTotal + shippingTotal + taxTotal + adjustment;
|
|
246
|
-
|
|
247
|
-
// Validation
|
|
248
|
-
if (total < 0) {
|
|
249
|
-
helpers.log?.warn('Negative order total detected', {
|
|
250
|
-
merchandiseTotal,
|
|
251
|
-
shippingTotal,
|
|
252
|
-
taxTotal,
|
|
253
|
-
adjustment,
|
|
254
|
-
total
|
|
255
|
-
});
|
|
256
|
-
return 0;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
helpers.log?.debug('Calculated order total', {
|
|
260
|
-
merchandiseTotal,
|
|
261
|
-
shippingTotal,
|
|
262
|
-
taxTotal,
|
|
263
|
-
adjustment,
|
|
264
|
-
total: helpers.formatCurrency(total, 2)
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
return helpers.formatCurrency(total, 2);
|
|
268
|
-
});
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
---
|
|
272
|
-
|
|
273
|
-
### Recipe 2: Order Type Decision Tree
|
|
274
|
-
|
|
275
|
-
**Use Case:** Determine order type based on multiple business rules.
|
|
276
|
-
|
|
277
|
-
```typescript
|
|
278
|
-
builder.field('input.type', (data, config, helpers) => {
|
|
279
|
-
const channelType = helpers.get(data, 'orders.order.channel-type');
|
|
280
|
-
const isGift = helpers.get(data, 'orders.order.shipments.shipment.gift') === 'true';
|
|
281
|
-
const isExpress = helpers.get(data, 'orders.order.shipping-method')?.includes('Express');
|
|
282
|
-
const customAttributes = helpers.get(data, 'orders.order.custom-attributes.custom-attribute', []);
|
|
283
|
-
const isPrime = helpers.extractCustomAttribute(customAttributes, 'isPrime') === 'true';
|
|
284
|
-
|
|
285
|
-
// Decision tree
|
|
286
|
-
let orderType = 'HD'; // Default: Home Delivery
|
|
287
|
-
|
|
288
|
-
// Channel-based mapping
|
|
289
|
-
const channelMapping = {
|
|
290
|
-
'Storefront': 'HD',
|
|
291
|
-
'CallCenter': 'CC',
|
|
292
|
-
'Mobile': 'MOBILE',
|
|
293
|
-
'POS': 'STORE'
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
orderType = helpers.mapValue(channelType, channelMapping, orderType);
|
|
297
|
-
|
|
298
|
-
// Business rules override
|
|
299
|
-
if (isPrime && orderType === 'HD') {
|
|
300
|
-
orderType = 'PRIME_HD';
|
|
301
|
-
} else if (isGift && orderType === 'HD') {
|
|
302
|
-
orderType = 'GIFT_HD';
|
|
303
|
-
} else if (isExpress) {
|
|
304
|
-
orderType = `EXPRESS_${orderType}`;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
helpers.log?.info('Determined order type', {
|
|
308
|
-
channelType,
|
|
309
|
-
isGift,
|
|
310
|
-
isExpress,
|
|
311
|
-
isPrime,
|
|
312
|
-
orderType
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
return orderType;
|
|
316
|
-
});
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
---
|
|
320
|
-
|
|
321
|
-
### Recipe 3: Payment Method Transformation
|
|
322
|
-
|
|
323
|
-
**Use Case:** Transform payment data with sensitive data masking.
|
|
324
|
-
|
|
325
|
-
```typescript
|
|
326
|
-
builder.mapArray(
|
|
327
|
-
'input.financialTransactions',
|
|
328
|
-
'orders.order.payments.payment',
|
|
329
|
-
(payment, index, config, helpers) => {
|
|
330
|
-
const creditCard = payment['credit-card'];
|
|
331
|
-
const cardType = creditCard?.['card-type'] || 'UNKNOWN';
|
|
332
|
-
const cardNumber = creditCard?.['card-number'] || '';
|
|
333
|
-
|
|
334
|
-
// Map card type
|
|
335
|
-
const paymentTypeMapping = {
|
|
336
|
-
'Visa': 'CREDIT_CARD',
|
|
337
|
-
'MasterCard': 'CREDIT_CARD',
|
|
338
|
-
'American Express': 'CREDIT_CARD',
|
|
339
|
-
'Discover': 'CREDIT_CARD',
|
|
340
|
-
'PayPal': 'PAYPAL',
|
|
341
|
-
'Apple Pay': 'DIGITAL_WALLET',
|
|
342
|
-
'Google Pay': 'DIGITAL_WALLET'
|
|
343
|
-
};
|
|
344
|
-
|
|
345
|
-
const paymentType = helpers.mapValue(cardType, paymentTypeMapping, 'CREDIT_CARD');
|
|
346
|
-
|
|
347
|
-
// Mask sensitive data
|
|
348
|
-
const last4 = cardNumber.slice(-4);
|
|
349
|
-
const maskedNumber = `************${last4}`;
|
|
350
|
-
|
|
351
|
-
return {
|
|
352
|
-
ref: payment['@payment-id'] || `PAY-${index + 1}`,
|
|
353
|
-
type: paymentType,
|
|
354
|
-
amount: helpers.parseFloatSafe(payment.amount, 0),
|
|
355
|
-
|
|
356
|
-
attributes: {
|
|
357
|
-
cardType: cardType,
|
|
358
|
-
cardNumber: maskedNumber, // Masked
|
|
359
|
-
expirationMonth: creditCard?.['expiration-month'],
|
|
360
|
-
expirationYear: creditCard?.['expiration-year'],
|
|
361
|
-
processorId: payment['processor-id'],
|
|
362
|
-
transactionId: payment['transaction-id'],
|
|
363
|
-
authorizationCode: payment['authorization-code']
|
|
364
|
-
}
|
|
365
|
-
};
|
|
366
|
-
}
|
|
367
|
-
);
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
---
|
|
371
|
-
|
|
372
|
-
## Inventory Management Recipes
|
|
373
|
-
|
|
374
|
-
### Recipe 1: Multi-Location Inventory Aggregation
|
|
375
|
-
|
|
376
|
-
**Use Case:** Aggregate inventory across multiple locations.
|
|
377
|
-
|
|
378
|
-
```typescript
|
|
379
|
-
builder.field('input.totalAvailableInventory', (data, helpers) => {
|
|
380
|
-
const items = helpers.ensureArray(
|
|
381
|
-
helpers.get(data, 'inventory.items', [])
|
|
382
|
-
);
|
|
383
|
-
|
|
384
|
-
// Group by SKU
|
|
385
|
-
const inventoryBySku = helpers.groupBy(items, 'sku');
|
|
386
|
-
|
|
387
|
-
// Calculate totals for each SKU
|
|
388
|
-
const aggregated = Object.entries(inventoryBySku).map(([sku, locations]) => {
|
|
389
|
-
const totalOnHand = helpers.sum(locations, item =>
|
|
390
|
-
helpers.parseIntSafe(item.quantity['on-hand'], 0)
|
|
391
|
-
);
|
|
392
|
-
|
|
393
|
-
const totalAvailable = helpers.sum(locations, item =>
|
|
394
|
-
helpers.parseIntSafe(item.quantity.available, 0)
|
|
395
|
-
);
|
|
396
|
-
|
|
397
|
-
const totalReserved = helpers.sum(locations, item =>
|
|
398
|
-
helpers.parseIntSafe(item.quantity.reserved, 0)
|
|
399
|
-
);
|
|
400
|
-
|
|
401
|
-
const locationCount = locations.length;
|
|
402
|
-
|
|
403
|
-
return {
|
|
404
|
-
sku,
|
|
405
|
-
totalOnHand,
|
|
406
|
-
totalAvailable,
|
|
407
|
-
totalReserved,
|
|
408
|
-
locationCount,
|
|
409
|
-
locations: locations.map(loc => ({
|
|
410
|
-
locationId: loc.location.id,
|
|
411
|
-
locationName: loc.location.name,
|
|
412
|
-
onHand: helpers.parseIntSafe(loc.quantity['on-hand'], 0),
|
|
413
|
-
available: helpers.parseIntSafe(loc.quantity.available, 0)
|
|
414
|
-
}))
|
|
415
|
-
};
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
return aggregated;
|
|
419
|
-
});
|
|
420
|
-
```
|
|
421
|
-
|
|
422
|
-
---
|
|
423
|
-
|
|
424
|
-
### Recipe 2: Inventory Delta Calculation
|
|
425
|
-
|
|
426
|
-
**Use Case:** Calculate inventory changes for incremental updates.
|
|
427
|
-
|
|
428
|
-
```typescript
|
|
429
|
-
builder.mapArray(
|
|
430
|
-
'input.entities',
|
|
431
|
-
'inventory.items',
|
|
432
|
-
(item, index, config, helpers) => {
|
|
433
|
-
const sku = item.sku;
|
|
434
|
-
const newQuantity = helpers.parseIntSafe(item.quantity['on-hand'], 0);
|
|
435
|
-
|
|
436
|
-
// Get previous quantity from cache/state
|
|
437
|
-
const previousQuantity = config.previousInventory?.[sku] || 0;
|
|
438
|
-
|
|
439
|
-
// Calculate delta
|
|
440
|
-
const delta = newQuantity - previousQuantity;
|
|
441
|
-
const changePercent = previousQuantity > 0
|
|
442
|
-
? ((delta / previousQuantity) * 100).toFixed(2)
|
|
443
|
-
: 0;
|
|
444
|
-
|
|
445
|
-
// Determine if significant change
|
|
446
|
-
const isSignificantChange = Math.abs(delta) > config.significantChangeThreshold || 10;
|
|
447
|
-
|
|
448
|
-
return {
|
|
449
|
-
skuRef: sku,
|
|
450
|
-
locationRef: item.location.id,
|
|
451
|
-
qty: newQuantity,
|
|
452
|
-
|
|
453
|
-
attributes: {
|
|
454
|
-
available: helpers.parseIntSafe(item.quantity.available, 0),
|
|
455
|
-
reserved: helpers.parseIntSafe(item.quantity.reserved, 0),
|
|
456
|
-
|
|
457
|
-
// Delta information
|
|
458
|
-
previousQty: previousQuantity,
|
|
459
|
-
delta: delta,
|
|
460
|
-
changePercent: changePercent,
|
|
461
|
-
isSignificantChange: isSignificantChange,
|
|
462
|
-
|
|
463
|
-
// Metadata
|
|
464
|
-
lastUpdated: item.dates['last-updated'],
|
|
465
|
-
binLocation: item.attributes['bin-location'],
|
|
466
|
-
lotNumber: item.attributes['lot-number']
|
|
467
|
-
}
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
);
|
|
471
|
-
```
|
|
472
|
-
|
|
473
|
-
---
|
|
474
|
-
|
|
475
|
-
### Recipe 3: Low Stock Alerts
|
|
476
|
-
|
|
477
|
-
**Use Case:** Generate alerts for items below reorder point.
|
|
478
|
-
|
|
479
|
-
```typescript
|
|
480
|
-
builder.field('input.lowStockAlerts', (data, helpers) => {
|
|
481
|
-
const items = helpers.ensureArray(
|
|
482
|
-
helpers.get(data, 'inventory.items', [])
|
|
483
|
-
);
|
|
484
|
-
|
|
485
|
-
const reorderPoints = config.reorderPoints || {}; // SKU → reorder point
|
|
486
|
-
|
|
487
|
-
const lowStockItems = items
|
|
488
|
-
.map(item => {
|
|
489
|
-
const sku = item.sku;
|
|
490
|
-
const available = helpers.parseIntSafe(item.quantity.available, 0);
|
|
491
|
-
const reserved = helpers.parseIntSafe(item.quantity.reserved, 0);
|
|
492
|
-
const inTransit = helpers.parseIntSafe(item.quantity['in-transit'], 0);
|
|
493
|
-
const reorderPoint = reorderPoints[sku] || 20; // Default: 20 units
|
|
494
|
-
|
|
495
|
-
// Calculate effective available (available + in-transit - reserved)
|
|
496
|
-
const effectiveAvailable = available + inTransit - reserved;
|
|
497
|
-
|
|
498
|
-
// Determine alert severity
|
|
499
|
-
let severity = 'none';
|
|
500
|
-
if (effectiveAvailable <= 0) {
|
|
501
|
-
severity = 'critical'; // Out of stock
|
|
502
|
-
} else if (effectiveAvailable < reorderPoint * 0.25) {
|
|
503
|
-
severity = 'high';
|
|
504
|
-
} else if (effectiveAvailable < reorderPoint * 0.5) {
|
|
505
|
-
severity = 'medium';
|
|
506
|
-
} else if (effectiveAvailable < reorderPoint) {
|
|
507
|
-
severity = 'low';
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
return {
|
|
511
|
-
sku,
|
|
512
|
-
productName: item['product-name'],
|
|
513
|
-
locationId: item.location.id,
|
|
514
|
-
locationName: item.location.name,
|
|
515
|
-
available,
|
|
516
|
-
reserved,
|
|
517
|
-
inTransit,
|
|
518
|
-
effectiveAvailable,
|
|
519
|
-
reorderPoint,
|
|
520
|
-
severity,
|
|
521
|
-
needsReorder: effectiveAvailable < reorderPoint
|
|
522
|
-
};
|
|
523
|
-
})
|
|
524
|
-
.filter(item => item.needsReorder) // Only include items needing reorder
|
|
525
|
-
.sort((a, b) => {
|
|
526
|
-
// Sort by severity
|
|
527
|
-
const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
528
|
-
return severityOrder[a.severity] - severityOrder[b.severity];
|
|
529
|
-
});
|
|
530
|
-
|
|
531
|
-
helpers.log?.info('Low stock alerts generated', {
|
|
532
|
-
totalItems: items.length,
|
|
533
|
-
lowStockCount: lowStockItems.length,
|
|
534
|
-
criticalCount: lowStockItems.filter(i => i.severity === 'critical').length
|
|
535
|
-
});
|
|
536
|
-
|
|
537
|
-
return lowStockItems;
|
|
538
|
-
});
|
|
539
|
-
```
|
|
540
|
-
|
|
541
|
-
---
|
|
542
|
-
|
|
543
|
-
## Performance Optimization
|
|
544
|
-
|
|
545
|
-
### Pattern 1: Memoized Complex Calculations
|
|
546
|
-
|
|
547
|
-
**Use Case:** Cache expensive calculations that are called multiple times.
|
|
548
|
-
|
|
549
|
-
```typescript
|
|
550
|
-
// Create memoized functions outside resolver
|
|
551
|
-
const calculateComplexDiscount = helpers.memoize((items, discountRules) => {
|
|
552
|
-
// Expensive calculation
|
|
553
|
-
return items.reduce((total, item) => {
|
|
554
|
-
const basePrice = parseFloat(item['base-price'] || 0);
|
|
555
|
-
const quantity = parseInt(item.quantity || 1);
|
|
556
|
-
|
|
557
|
-
// Apply complex discount rules
|
|
558
|
-
let discount = 0;
|
|
559
|
-
for (const rule of discountRules) {
|
|
560
|
-
if (item.category === rule.category && quantity >= rule.minQuantity) {
|
|
561
|
-
discount += basePrice * quantity * rule.discountPercent;
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
return total + discount;
|
|
566
|
-
}, 0);
|
|
567
|
-
});
|
|
568
|
-
|
|
569
|
-
builder.field('input.totalDiscount', (data, config, helpers) => {
|
|
570
|
-
const items = helpers.ensureArray(
|
|
571
|
-
helpers.get(data, 'orders.order.product-lineitems.product-lineitem')
|
|
572
|
-
);
|
|
573
|
-
|
|
574
|
-
// Cached result
|
|
575
|
-
return calculateComplexDiscount(items, config.discountRules);
|
|
576
|
-
});
|
|
577
|
-
```
|
|
578
|
-
|
|
579
|
-
---
|
|
580
|
-
|
|
581
|
-
### Pattern 2: Lazy Evaluation
|
|
582
|
-
|
|
583
|
-
**Use Case:** Only perform expensive operations when actually needed.
|
|
584
|
-
|
|
585
|
-
```typescript
|
|
586
|
-
builder.field('input.enrichedData', async (data, config, helpers) => {
|
|
587
|
-
const requiresEnrichment = helpers.get(data, 'orders.order.enrichment-required') === 'true';
|
|
588
|
-
|
|
589
|
-
if (!requiresEnrichment) {
|
|
590
|
-
helpers.log?.debug('Skipping enrichment (not required)');
|
|
591
|
-
return null;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
// Only fetch if needed
|
|
595
|
-
helpers.log?.debug('Fetching enrichment data...');
|
|
596
|
-
const enrichmentData = await fetchEnrichmentData(data, config);
|
|
597
|
-
|
|
598
|
-
return enrichmentData;
|
|
599
|
-
});
|
|
600
|
-
```
|
|
601
|
-
|
|
602
|
-
---
|
|
603
|
-
|
|
604
|
-
### Pattern 3: Result Caching with TTL
|
|
605
|
-
|
|
606
|
-
**Use Case:** Cache API responses with time-to-live.
|
|
607
|
-
|
|
608
|
-
```typescript
|
|
609
|
-
const cache = new Map();
|
|
610
|
-
|
|
611
|
-
function getCachedOrFetch(key, fetcher, ttlMs = 300000) { // 5 min default
|
|
612
|
-
const cached = cache.get(key);
|
|
613
|
-
|
|
614
|
-
if (cached && Date.now() - cached.timestamp < ttlMs) {
|
|
615
|
-
return Promise.resolve(cached.data);
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
return fetcher().then(data => {
|
|
619
|
-
cache.set(key, {
|
|
620
|
-
data,
|
|
621
|
-
timestamp: Date.now()
|
|
622
|
-
});
|
|
623
|
-
return data;
|
|
624
|
-
});
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
builder.field('input.productCategory', async (data, config, helpers) => {
|
|
628
|
-
const sku = helpers.get(data, 'item.sku');
|
|
629
|
-
|
|
630
|
-
// Cache product data for 5 minutes
|
|
631
|
-
const productData = await getCachedOrFetch(
|
|
632
|
-
`product:${sku}`,
|
|
633
|
-
() => fetchProductData(sku),
|
|
634
|
-
300000 // 5 minutes
|
|
635
|
-
);
|
|
636
|
-
|
|
637
|
-
return productData.category;
|
|
638
|
-
});
|
|
639
|
-
```
|
|
640
|
-
|
|
641
|
-
---
|
|
642
|
-
|
|
643
|
-
## Error Handling
|
|
644
|
-
|
|
645
|
-
### Pattern 1: Graceful Degradation
|
|
646
|
-
|
|
647
|
-
**Use Case:** Provide fallback when optional services fail.
|
|
648
|
-
|
|
649
|
-
```typescript
|
|
650
|
-
builder.field('input.customer.loyaltyTier', async (data, config, helpers) => {
|
|
651
|
-
const customerId = helpers.get(data, 'orders.order.customer.customer-no');
|
|
652
|
-
|
|
653
|
-
try {
|
|
654
|
-
const loyaltyData = await helpers.retry(
|
|
655
|
-
async () => {
|
|
656
|
-
const response = await fetch(
|
|
657
|
-
`https://api.loyalty.com/customers/${customerId}/tier`,
|
|
658
|
-
{
|
|
659
|
-
headers: { 'Authorization': `Bearer ${config.loyaltyApiToken}` },
|
|
660
|
-
timeout: 5000 // 5 second timeout
|
|
661
|
-
}
|
|
662
|
-
);
|
|
663
|
-
|
|
664
|
-
if (!response.ok) {
|
|
665
|
-
throw new Error(`Loyalty API error: ${response.status}`);
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
return await response.json();
|
|
669
|
-
},
|
|
670
|
-
2, // Only retry twice for optional service
|
|
671
|
-
500
|
|
672
|
-
);
|
|
673
|
-
|
|
674
|
-
return loyaltyData.tier;
|
|
675
|
-
|
|
676
|
-
} catch (error) {
|
|
677
|
-
helpers.log?.warn('Failed to fetch loyalty tier, using default', {
|
|
678
|
-
customerId,
|
|
679
|
-
error: error.message
|
|
680
|
-
});
|
|
681
|
-
|
|
682
|
-
// Graceful degradation - use default tier
|
|
683
|
-
return config.defaultLoyaltyTier || 'STANDARD';
|
|
684
|
-
}
|
|
685
|
-
});
|
|
686
|
-
```
|
|
687
|
-
|
|
688
|
-
---
|
|
689
|
-
|
|
690
|
-
### Pattern 2: Validation with Custom Errors
|
|
691
|
-
|
|
692
|
-
**Use Case:** Provide clear, actionable error messages.
|
|
693
|
-
|
|
694
|
-
```typescript
|
|
695
|
-
builder.validate(
|
|
696
|
-
'input.totalPrice',
|
|
697
|
-
(data, config, helpers) => {
|
|
698
|
-
const grossPrice = helpers.get(data, 'orders.order.totals.order-total.gross-price');
|
|
699
|
-
|
|
700
|
-
if (!grossPrice) {
|
|
701
|
-
throw new Error(
|
|
702
|
-
'Missing required field: orders.order.totals.order-total.gross-price. ' +
|
|
703
|
-
'Ensure the order data includes total pricing information.'
|
|
704
|
-
);
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
const parsed = helpers.parseFloatSafe(grossPrice, -1);
|
|
708
|
-
|
|
709
|
-
if (parsed < 0) {
|
|
710
|
-
throw new Error(
|
|
711
|
-
`Invalid price value: "${grossPrice}". ` +
|
|
712
|
-
'Price must be a positive number.'
|
|
713
|
-
);
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
return parsed;
|
|
717
|
-
},
|
|
718
|
-
(value) => {
|
|
719
|
-
if (value <= 0) {
|
|
720
|
-
return `Order total must be greater than zero. Received: ${value}`;
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
if (value > 1000000) {
|
|
724
|
-
return `Order total exceeds maximum allowed value. Received: ${value}`;
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
return true; // Validation passed
|
|
728
|
-
}
|
|
729
|
-
);
|
|
730
|
-
```
|
|
731
|
-
|
|
732
|
-
---
|
|
733
|
-
|
|
734
|
-
## Testing Patterns
|
|
735
|
-
|
|
736
|
-
### Pattern 1: Test with Canonical Data
|
|
737
|
-
|
|
738
|
-
```typescript
|
|
739
|
-
import { canonicalOrder } from '../../../03-PATTERN-GUIDES/examples/test-data/canonical-order.json';
|
|
740
|
-
import { ResolverHelpers } from '@fluentcommerce/fc-connect-sdk';
|
|
741
|
-
|
|
742
|
-
describe('Order Resolvers', () => {
|
|
743
|
-
const config = {
|
|
744
|
-
defaultRetailerId: '1',
|
|
745
|
-
defaultCurrency: 'USD'
|
|
746
|
-
};
|
|
747
|
-
|
|
748
|
-
it('should extract order reference', () => {
|
|
749
|
-
const result = resolvers\['input.ref'](canonicalOrder, config, ResolverHelpers);
|
|
750
|
-
expect(result).toBe('ORD-2025-001');
|
|
751
|
-
});
|
|
752
|
-
|
|
753
|
-
it('should calculate total price', () => {
|
|
754
|
-
const result = resolvers\['input.totalPrice'](canonicalOrder, config, ResolverHelpers);
|
|
755
|
-
expect(result).toBe('151.17');
|
|
756
|
-
});
|
|
757
|
-
|
|
758
|
-
it('should extract customer email', () => {
|
|
759
|
-
const result = resolvers\['input.customer.email'](canonicalOrder, config, ResolverHelpers);
|
|
760
|
-
expect(result).toBe('john.doe@example.com');
|
|
761
|
-
});
|
|
762
|
-
});
|
|
763
|
-
```
|
|
764
|
-
|
|
765
|
-
---
|
|
766
|
-
|
|
767
|
-
### Pattern 2: Mock External Dependencies
|
|
768
|
-
|
|
769
|
-
```typescript
|
|
770
|
-
describe('Customer Lookup Resolver', () => {
|
|
771
|
-
it('should use existing customer when found', async () => {
|
|
772
|
-
const mockFluentClient = {
|
|
773
|
-
graphql: jest.fn().mockResolvedValue({
|
|
774
|
-
data: {
|
|
775
|
-
customers: {
|
|
776
|
-
edges: [{
|
|
777
|
-
node: { id: '123', username: 'CUST-001' }
|
|
778
|
-
}]
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
})
|
|
782
|
-
};
|
|
783
|
-
|
|
784
|
-
const mockHelpers = {
|
|
785
|
-
...ResolverHelpers,
|
|
786
|
-
fluentClient: mockFluentClient,
|
|
787
|
-
retry: (fn) => fn(), // Skip retry logic in tests
|
|
788
|
-
log: {
|
|
789
|
-
info: jest.fn(),
|
|
790
|
-
warn: jest.fn(),
|
|
791
|
-
error: jest.fn()
|
|
792
|
-
}
|
|
793
|
-
};
|
|
794
|
-
|
|
795
|
-
const result = await resolvers`'input.customer.id'`;
|
|
796
|
-
|
|
797
|
-
expect(result).toBe('123');
|
|
798
|
-
expect(mockFluentClient.graphql).toHaveBeenCalledTimes(1);
|
|
799
|
-
expect(mockHelpers.log.info).toHaveBeenCalledWith(
|
|
800
|
-
'Found existing customer',
|
|
801
|
-
{ id: '123' }
|
|
802
|
-
);
|
|
803
|
-
});
|
|
804
|
-
});
|
|
805
|
-
```
|
|
806
|
-
|
|
807
|
-
---
|
|
808
|
-
|
|
809
|
-
## See Also
|
|
810
|
-
|
|
811
|
-
- [Main Resolver Guide](./mapping-resolvers-resolver-guide.md)
|
|
812
|
-
- [API Reference](./mapping-resolvers-resolver-api-reference.md)
|
|
813
|
-
- [Helper Functions Reference](./mapping-resolvers-resolver-helpers-reference.md)
|
|
814
|
-
- [Parameters Reference](./mapping-resolvers-resolver-parameters-reference.md)
|
|
815
|
-
- [Troubleshooting](./mapping-resolvers-resolver-troubleshooting.md)
|
|
816
|
-
|
|
817
|
-
---
|
|
818
|
-
|
|
819
|
-
**Document Version:** 2.0.0
|
|
820
|
-
**SDK Version:** 1.5.0+
|
|
821
|
-
**Last Updated:** 2025-01-14
|
|
822
|
-
|
|
823
|
-
---
|
|
824
|
-
|
|
825
|
-
**License:** MIT
|
|
826
|
-
**Copyright:** © 2025 Fluent Commerce
|
|
1
|
+
# Resolver Cookbook
|
|
2
|
+
|
|
3
|
+
**Advanced patterns and recipes for production resolvers**
|
|
4
|
+
|
|
5
|
+
Version: 2.0.0
|
|
6
|
+
Last Updated: 2025-01-14
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Table of Contents
|
|
11
|
+
|
|
12
|
+
1. [Async Patterns](#async-patterns)
|
|
13
|
+
2. [Order Management Recipes](#order-management-recipes)
|
|
14
|
+
3. [Inventory Management Recipes](#inventory-management-recipes)
|
|
15
|
+
4. [Performance Optimization](#performance-optimization)
|
|
16
|
+
5. [Error Handling](#error-handling)
|
|
17
|
+
6. [Testing Patterns](#testing-patterns)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Async Patterns
|
|
22
|
+
|
|
23
|
+
### Pattern 1: Lookup-or-Create Customer
|
|
24
|
+
|
|
25
|
+
**Use Case:** Ensure customer exists before creating order.
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
builder.field('input.customer.id', async (data, config, helpers) => {
|
|
29
|
+
const customerId = helpers.get(data, 'orders.order.customer.customer-no');
|
|
30
|
+
const email = helpers.get(data, 'orders.order.customer.customer-email');
|
|
31
|
+
|
|
32
|
+
if (!helpers.fluentClient) {
|
|
33
|
+
throw new Error('FluentClient is required for customer lookup');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return await helpers.retry(async () => {
|
|
37
|
+
// Step 1: Try to find existing customer
|
|
38
|
+
const queryResult = await helpers.fluentClient.graphql({
|
|
39
|
+
query: `
|
|
40
|
+
query GetCustomer($username: [String]) {
|
|
41
|
+
customers(username: $username, first: 1) {
|
|
42
|
+
edges {
|
|
43
|
+
node {
|
|
44
|
+
id
|
|
45
|
+
username
|
|
46
|
+
primaryEmail
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
`,
|
|
52
|
+
variables: { username: [customerId] }
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const existing = queryResult.data?.customers?.edges?.[0]?.node;
|
|
56
|
+
|
|
57
|
+
if (existing) {
|
|
58
|
+
helpers.log?.info('Found existing customer', { id: existing.id });
|
|
59
|
+
return existing.id;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Step 2: Create if not found
|
|
63
|
+
helpers.log?.info('Creating new customer', { customerId, email });
|
|
64
|
+
|
|
65
|
+
const createResult = await helpers.fluentClient.graphql({
|
|
66
|
+
query: `
|
|
67
|
+
mutation CreateCustomer($input: CreateCustomerInput!) {
|
|
68
|
+
createCustomer(input: $input) {
|
|
69
|
+
id
|
|
70
|
+
username
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
`,
|
|
74
|
+
variables: {
|
|
75
|
+
input: {
|
|
76
|
+
username: customerId,
|
|
77
|
+
primaryEmail: email,
|
|
78
|
+
retailer: { id: config.defaultRetailerId }
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
if (createResult.errors) {
|
|
84
|
+
throw new Error(`Failed to create customer: ${createResult.errors[0].message}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return createResult.data?.createCustomer?.id;
|
|
88
|
+
}, 3, 500); // Retry 3 times, 500ms delay
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
### Pattern 2: Parallel API Enrichment
|
|
95
|
+
|
|
96
|
+
**Use Case:** Enrich order items with external data efficiently.
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
builder.field('input.items', async (data, config, helpers) => {
|
|
100
|
+
const items = helpers.ensureArray(
|
|
101
|
+
helpers.get(data, 'orders.order.product-lineitems.product-lineitem')
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
// Fetch all product details in parallel
|
|
105
|
+
const enrichedItems = await Promise.all(
|
|
106
|
+
items.map(async (item) => {
|
|
107
|
+
// Run multiple lookups in parallel for each item
|
|
108
|
+
const [productDetails, inventory, pricing] = await Promise.all([
|
|
109
|
+
fetchProductDetails(item['product-id']),
|
|
110
|
+
checkInventory(item['product-id'], config.defaultLocationId),
|
|
111
|
+
getPricing(item['product-id'], config.priceTier)
|
|
112
|
+
]);
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
ref: item['@lineitem-id'],
|
|
116
|
+
productRef: item['product-id'],
|
|
117
|
+
quantity: helpers.parseIntSafe(item.quantity, 1),
|
|
118
|
+
price: helpers.parseFloatSafe(item['base-price'], 0),
|
|
119
|
+
|
|
120
|
+
// Enriched data
|
|
121
|
+
attributes: {
|
|
122
|
+
productName: productDetails.name,
|
|
123
|
+
category: productDetails.category,
|
|
124
|
+
availableQty: inventory.available,
|
|
125
|
+
currentPrice: pricing.currentPrice,
|
|
126
|
+
priceWas: pricing.previousPrice
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
})
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
return enrichedItems;
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
### Pattern 3: Rate-Limited API Calls with Batch Processing
|
|
139
|
+
|
|
140
|
+
**Use Case:** Process large item lists without overwhelming external APIs.
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
builder.field('input.enrichedItems', async (data, config, helpers) => {
|
|
144
|
+
const items = helpers.ensureArray(
|
|
145
|
+
helpers.get(data, 'orders.order.product-lineitems.product-lineitem')
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
// Process in batches of 5 to respect API rate limits
|
|
149
|
+
const enrichedItems = await helpers.batchProcess(
|
|
150
|
+
items,
|
|
151
|
+
async (item, index) => {
|
|
152
|
+
try {
|
|
153
|
+
// Add delay to respect rate limits
|
|
154
|
+
await new Promise(resolve => setTimeout(resolve, 200)); // 5 req/sec
|
|
155
|
+
|
|
156
|
+
const productData = await helpers.retry(
|
|
157
|
+
async () => {
|
|
158
|
+
const response = await fetch(
|
|
159
|
+
`https://api.product-service.com/products/${item['product-id']}`,
|
|
160
|
+
{
|
|
161
|
+
headers: {
|
|
162
|
+
'Authorization': `Bearer ${config.productApiToken}`
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
if (!response.ok) {
|
|
168
|
+
throw new Error(`API error: ${response.status}`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return await response.json();
|
|
172
|
+
},
|
|
173
|
+
3, // Max 3 retries
|
|
174
|
+
1000 // 1 second delay between retries
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
return {
|
|
178
|
+
ref: item['@lineitem-id'],
|
|
179
|
+
productRef: item['product-id'],
|
|
180
|
+
quantity: helpers.parseIntSafe(item.quantity, 1),
|
|
181
|
+
price: helpers.parseFloatSafe(item['base-price'], 0),
|
|
182
|
+
attributes: {
|
|
183
|
+
...productData
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
} catch (error) {
|
|
188
|
+
helpers.log?.error(`Failed to enrich item ${item['@lineitem-id']}`, { error });
|
|
189
|
+
|
|
190
|
+
// Return basic item without enrichment
|
|
191
|
+
return {
|
|
192
|
+
ref: item['@lineitem-id'],
|
|
193
|
+
productRef: item['product-id'],
|
|
194
|
+
quantity: helpers.parseIntSafe(item.quantity, 1),
|
|
195
|
+
price: helpers.parseFloatSafe(item['base-price'], 0)
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
5 // Process 5 items at a time
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
return enrichedItems;
|
|
203
|
+
});
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Order Management Recipes
|
|
209
|
+
|
|
210
|
+
### Recipe 1: Multi-Source Total Calculation
|
|
211
|
+
|
|
212
|
+
**Use Case:** Calculate order total from various sources with validation.
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
builder.calculate('input.totalPrice', (data, helpers) => {
|
|
216
|
+
// Get line items
|
|
217
|
+
const items = helpers.ensureArray(
|
|
218
|
+
helpers.get(data, 'orders.order.product-lineitems.product-lineitem', [])
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
// Calculate merchandise total
|
|
222
|
+
const merchandiseTotal = helpers.sum(items, item =>
|
|
223
|
+
helpers.parseFloatSafe(item['gross-price'], 0)
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
// Get shipping total
|
|
227
|
+
const shippingTotal = helpers.parseFloatSafe(
|
|
228
|
+
helpers.get(data, 'orders.order.totals.shipping-total.gross-price'),
|
|
229
|
+
0
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
// Get tax total
|
|
233
|
+
const taxTotal = helpers.parseFloatSafe(
|
|
234
|
+
helpers.get(data, 'orders.order.totals.order-total.tax'),
|
|
235
|
+
0
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
// Get adjustments (discounts/fees)
|
|
239
|
+
const adjustment = helpers.parseFloatSafe(
|
|
240
|
+
helpers.get(data, 'orders.order.totals.adjustment-total.gross-price'),
|
|
241
|
+
0
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
// Calculate final total
|
|
245
|
+
const total = merchandiseTotal + shippingTotal + taxTotal + adjustment;
|
|
246
|
+
|
|
247
|
+
// Validation
|
|
248
|
+
if (total < 0) {
|
|
249
|
+
helpers.log?.warn('Negative order total detected', {
|
|
250
|
+
merchandiseTotal,
|
|
251
|
+
shippingTotal,
|
|
252
|
+
taxTotal,
|
|
253
|
+
adjustment,
|
|
254
|
+
total
|
|
255
|
+
});
|
|
256
|
+
return 0;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
helpers.log?.debug('Calculated order total', {
|
|
260
|
+
merchandiseTotal,
|
|
261
|
+
shippingTotal,
|
|
262
|
+
taxTotal,
|
|
263
|
+
adjustment,
|
|
264
|
+
total: helpers.formatCurrency(total, 2)
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
return helpers.formatCurrency(total, 2);
|
|
268
|
+
});
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
### Recipe 2: Order Type Decision Tree
|
|
274
|
+
|
|
275
|
+
**Use Case:** Determine order type based on multiple business rules.
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
builder.field('input.type', (data, config, helpers) => {
|
|
279
|
+
const channelType = helpers.get(data, 'orders.order.channel-type');
|
|
280
|
+
const isGift = helpers.get(data, 'orders.order.shipments.shipment.gift') === 'true';
|
|
281
|
+
const isExpress = helpers.get(data, 'orders.order.shipping-method')?.includes('Express');
|
|
282
|
+
const customAttributes = helpers.get(data, 'orders.order.custom-attributes.custom-attribute', []);
|
|
283
|
+
const isPrime = helpers.extractCustomAttribute(customAttributes, 'isPrime') === 'true';
|
|
284
|
+
|
|
285
|
+
// Decision tree
|
|
286
|
+
let orderType = 'HD'; // Default: Home Delivery
|
|
287
|
+
|
|
288
|
+
// Channel-based mapping
|
|
289
|
+
const channelMapping = {
|
|
290
|
+
'Storefront': 'HD',
|
|
291
|
+
'CallCenter': 'CC',
|
|
292
|
+
'Mobile': 'MOBILE',
|
|
293
|
+
'POS': 'STORE'
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
orderType = helpers.mapValue(channelType, channelMapping, orderType);
|
|
297
|
+
|
|
298
|
+
// Business rules override
|
|
299
|
+
if (isPrime && orderType === 'HD') {
|
|
300
|
+
orderType = 'PRIME_HD';
|
|
301
|
+
} else if (isGift && orderType === 'HD') {
|
|
302
|
+
orderType = 'GIFT_HD';
|
|
303
|
+
} else if (isExpress) {
|
|
304
|
+
orderType = `EXPRESS_${orderType}`;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
helpers.log?.info('Determined order type', {
|
|
308
|
+
channelType,
|
|
309
|
+
isGift,
|
|
310
|
+
isExpress,
|
|
311
|
+
isPrime,
|
|
312
|
+
orderType
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
return orderType;
|
|
316
|
+
});
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
### Recipe 3: Payment Method Transformation
|
|
322
|
+
|
|
323
|
+
**Use Case:** Transform payment data with sensitive data masking.
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
builder.mapArray(
|
|
327
|
+
'input.financialTransactions',
|
|
328
|
+
'orders.order.payments.payment',
|
|
329
|
+
(payment, index, config, helpers) => {
|
|
330
|
+
const creditCard = payment['credit-card'];
|
|
331
|
+
const cardType = creditCard?.['card-type'] || 'UNKNOWN';
|
|
332
|
+
const cardNumber = creditCard?.['card-number'] || '';
|
|
333
|
+
|
|
334
|
+
// Map card type
|
|
335
|
+
const paymentTypeMapping = {
|
|
336
|
+
'Visa': 'CREDIT_CARD',
|
|
337
|
+
'MasterCard': 'CREDIT_CARD',
|
|
338
|
+
'American Express': 'CREDIT_CARD',
|
|
339
|
+
'Discover': 'CREDIT_CARD',
|
|
340
|
+
'PayPal': 'PAYPAL',
|
|
341
|
+
'Apple Pay': 'DIGITAL_WALLET',
|
|
342
|
+
'Google Pay': 'DIGITAL_WALLET'
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
const paymentType = helpers.mapValue(cardType, paymentTypeMapping, 'CREDIT_CARD');
|
|
346
|
+
|
|
347
|
+
// Mask sensitive data
|
|
348
|
+
const last4 = cardNumber.slice(-4);
|
|
349
|
+
const maskedNumber = `************${last4}`;
|
|
350
|
+
|
|
351
|
+
return {
|
|
352
|
+
ref: payment['@payment-id'] || `PAY-${index + 1}`,
|
|
353
|
+
type: paymentType,
|
|
354
|
+
amount: helpers.parseFloatSafe(payment.amount, 0),
|
|
355
|
+
|
|
356
|
+
attributes: {
|
|
357
|
+
cardType: cardType,
|
|
358
|
+
cardNumber: maskedNumber, // Masked
|
|
359
|
+
expirationMonth: creditCard?.['expiration-month'],
|
|
360
|
+
expirationYear: creditCard?.['expiration-year'],
|
|
361
|
+
processorId: payment['processor-id'],
|
|
362
|
+
transactionId: payment['transaction-id'],
|
|
363
|
+
authorizationCode: payment['authorization-code']
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
);
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## Inventory Management Recipes
|
|
373
|
+
|
|
374
|
+
### Recipe 1: Multi-Location Inventory Aggregation
|
|
375
|
+
|
|
376
|
+
**Use Case:** Aggregate inventory across multiple locations.
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
builder.field('input.totalAvailableInventory', (data, helpers) => {
|
|
380
|
+
const items = helpers.ensureArray(
|
|
381
|
+
helpers.get(data, 'inventory.items', [])
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
// Group by SKU
|
|
385
|
+
const inventoryBySku = helpers.groupBy(items, 'sku');
|
|
386
|
+
|
|
387
|
+
// Calculate totals for each SKU
|
|
388
|
+
const aggregated = Object.entries(inventoryBySku).map(([sku, locations]) => {
|
|
389
|
+
const totalOnHand = helpers.sum(locations, item =>
|
|
390
|
+
helpers.parseIntSafe(item.quantity['on-hand'], 0)
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
const totalAvailable = helpers.sum(locations, item =>
|
|
394
|
+
helpers.parseIntSafe(item.quantity.available, 0)
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
const totalReserved = helpers.sum(locations, item =>
|
|
398
|
+
helpers.parseIntSafe(item.quantity.reserved, 0)
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
const locationCount = locations.length;
|
|
402
|
+
|
|
403
|
+
return {
|
|
404
|
+
sku,
|
|
405
|
+
totalOnHand,
|
|
406
|
+
totalAvailable,
|
|
407
|
+
totalReserved,
|
|
408
|
+
locationCount,
|
|
409
|
+
locations: locations.map(loc => ({
|
|
410
|
+
locationId: loc.location.id,
|
|
411
|
+
locationName: loc.location.name,
|
|
412
|
+
onHand: helpers.parseIntSafe(loc.quantity['on-hand'], 0),
|
|
413
|
+
available: helpers.parseIntSafe(loc.quantity.available, 0)
|
|
414
|
+
}))
|
|
415
|
+
};
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
return aggregated;
|
|
419
|
+
});
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
### Recipe 2: Inventory Delta Calculation
|
|
425
|
+
|
|
426
|
+
**Use Case:** Calculate inventory changes for incremental updates.
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
builder.mapArray(
|
|
430
|
+
'input.entities',
|
|
431
|
+
'inventory.items',
|
|
432
|
+
(item, index, config, helpers) => {
|
|
433
|
+
const sku = item.sku;
|
|
434
|
+
const newQuantity = helpers.parseIntSafe(item.quantity['on-hand'], 0);
|
|
435
|
+
|
|
436
|
+
// Get previous quantity from cache/state
|
|
437
|
+
const previousQuantity = config.previousInventory?.[sku] || 0;
|
|
438
|
+
|
|
439
|
+
// Calculate delta
|
|
440
|
+
const delta = newQuantity - previousQuantity;
|
|
441
|
+
const changePercent = previousQuantity > 0
|
|
442
|
+
? ((delta / previousQuantity) * 100).toFixed(2)
|
|
443
|
+
: 0;
|
|
444
|
+
|
|
445
|
+
// Determine if significant change
|
|
446
|
+
const isSignificantChange = Math.abs(delta) > config.significantChangeThreshold || 10;
|
|
447
|
+
|
|
448
|
+
return {
|
|
449
|
+
skuRef: sku,
|
|
450
|
+
locationRef: item.location.id,
|
|
451
|
+
qty: newQuantity,
|
|
452
|
+
|
|
453
|
+
attributes: {
|
|
454
|
+
available: helpers.parseIntSafe(item.quantity.available, 0),
|
|
455
|
+
reserved: helpers.parseIntSafe(item.quantity.reserved, 0),
|
|
456
|
+
|
|
457
|
+
// Delta information
|
|
458
|
+
previousQty: previousQuantity,
|
|
459
|
+
delta: delta,
|
|
460
|
+
changePercent: changePercent,
|
|
461
|
+
isSignificantChange: isSignificantChange,
|
|
462
|
+
|
|
463
|
+
// Metadata
|
|
464
|
+
lastUpdated: item.dates['last-updated'],
|
|
465
|
+
binLocation: item.attributes['bin-location'],
|
|
466
|
+
lotNumber: item.attributes['lot-number']
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
);
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
### Recipe 3: Low Stock Alerts
|
|
476
|
+
|
|
477
|
+
**Use Case:** Generate alerts for items below reorder point.
|
|
478
|
+
|
|
479
|
+
```typescript
|
|
480
|
+
builder.field('input.lowStockAlerts', (data, helpers) => {
|
|
481
|
+
const items = helpers.ensureArray(
|
|
482
|
+
helpers.get(data, 'inventory.items', [])
|
|
483
|
+
);
|
|
484
|
+
|
|
485
|
+
const reorderPoints = config.reorderPoints || {}; // SKU → reorder point
|
|
486
|
+
|
|
487
|
+
const lowStockItems = items
|
|
488
|
+
.map(item => {
|
|
489
|
+
const sku = item.sku;
|
|
490
|
+
const available = helpers.parseIntSafe(item.quantity.available, 0);
|
|
491
|
+
const reserved = helpers.parseIntSafe(item.quantity.reserved, 0);
|
|
492
|
+
const inTransit = helpers.parseIntSafe(item.quantity['in-transit'], 0);
|
|
493
|
+
const reorderPoint = reorderPoints[sku] || 20; // Default: 20 units
|
|
494
|
+
|
|
495
|
+
// Calculate effective available (available + in-transit - reserved)
|
|
496
|
+
const effectiveAvailable = available + inTransit - reserved;
|
|
497
|
+
|
|
498
|
+
// Determine alert severity
|
|
499
|
+
let severity = 'none';
|
|
500
|
+
if (effectiveAvailable <= 0) {
|
|
501
|
+
severity = 'critical'; // Out of stock
|
|
502
|
+
} else if (effectiveAvailable < reorderPoint * 0.25) {
|
|
503
|
+
severity = 'high';
|
|
504
|
+
} else if (effectiveAvailable < reorderPoint * 0.5) {
|
|
505
|
+
severity = 'medium';
|
|
506
|
+
} else if (effectiveAvailable < reorderPoint) {
|
|
507
|
+
severity = 'low';
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
return {
|
|
511
|
+
sku,
|
|
512
|
+
productName: item['product-name'],
|
|
513
|
+
locationId: item.location.id,
|
|
514
|
+
locationName: item.location.name,
|
|
515
|
+
available,
|
|
516
|
+
reserved,
|
|
517
|
+
inTransit,
|
|
518
|
+
effectiveAvailable,
|
|
519
|
+
reorderPoint,
|
|
520
|
+
severity,
|
|
521
|
+
needsReorder: effectiveAvailable < reorderPoint
|
|
522
|
+
};
|
|
523
|
+
})
|
|
524
|
+
.filter(item => item.needsReorder) // Only include items needing reorder
|
|
525
|
+
.sort((a, b) => {
|
|
526
|
+
// Sort by severity
|
|
527
|
+
const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
528
|
+
return severityOrder[a.severity] - severityOrder[b.severity];
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
helpers.log?.info('Low stock alerts generated', {
|
|
532
|
+
totalItems: items.length,
|
|
533
|
+
lowStockCount: lowStockItems.length,
|
|
534
|
+
criticalCount: lowStockItems.filter(i => i.severity === 'critical').length
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
return lowStockItems;
|
|
538
|
+
});
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
---
|
|
542
|
+
|
|
543
|
+
## Performance Optimization
|
|
544
|
+
|
|
545
|
+
### Pattern 1: Memoized Complex Calculations
|
|
546
|
+
|
|
547
|
+
**Use Case:** Cache expensive calculations that are called multiple times.
|
|
548
|
+
|
|
549
|
+
```typescript
|
|
550
|
+
// Create memoized functions outside resolver
|
|
551
|
+
const calculateComplexDiscount = helpers.memoize((items, discountRules) => {
|
|
552
|
+
// Expensive calculation
|
|
553
|
+
return items.reduce((total, item) => {
|
|
554
|
+
const basePrice = parseFloat(item['base-price'] || 0);
|
|
555
|
+
const quantity = parseInt(item.quantity || 1);
|
|
556
|
+
|
|
557
|
+
// Apply complex discount rules
|
|
558
|
+
let discount = 0;
|
|
559
|
+
for (const rule of discountRules) {
|
|
560
|
+
if (item.category === rule.category && quantity >= rule.minQuantity) {
|
|
561
|
+
discount += basePrice * quantity * rule.discountPercent;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
return total + discount;
|
|
566
|
+
}, 0);
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
builder.field('input.totalDiscount', (data, config, helpers) => {
|
|
570
|
+
const items = helpers.ensureArray(
|
|
571
|
+
helpers.get(data, 'orders.order.product-lineitems.product-lineitem')
|
|
572
|
+
);
|
|
573
|
+
|
|
574
|
+
// Cached result
|
|
575
|
+
return calculateComplexDiscount(items, config.discountRules);
|
|
576
|
+
});
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
---
|
|
580
|
+
|
|
581
|
+
### Pattern 2: Lazy Evaluation
|
|
582
|
+
|
|
583
|
+
**Use Case:** Only perform expensive operations when actually needed.
|
|
584
|
+
|
|
585
|
+
```typescript
|
|
586
|
+
builder.field('input.enrichedData', async (data, config, helpers) => {
|
|
587
|
+
const requiresEnrichment = helpers.get(data, 'orders.order.enrichment-required') === 'true';
|
|
588
|
+
|
|
589
|
+
if (!requiresEnrichment) {
|
|
590
|
+
helpers.log?.debug('Skipping enrichment (not required)');
|
|
591
|
+
return null;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// Only fetch if needed
|
|
595
|
+
helpers.log?.debug('Fetching enrichment data...');
|
|
596
|
+
const enrichmentData = await fetchEnrichmentData(data, config);
|
|
597
|
+
|
|
598
|
+
return enrichmentData;
|
|
599
|
+
});
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
---
|
|
603
|
+
|
|
604
|
+
### Pattern 3: Result Caching with TTL
|
|
605
|
+
|
|
606
|
+
**Use Case:** Cache API responses with time-to-live.
|
|
607
|
+
|
|
608
|
+
```typescript
|
|
609
|
+
const cache = new Map();
|
|
610
|
+
|
|
611
|
+
function getCachedOrFetch(key, fetcher, ttlMs = 300000) { // 5 min default
|
|
612
|
+
const cached = cache.get(key);
|
|
613
|
+
|
|
614
|
+
if (cached && Date.now() - cached.timestamp < ttlMs) {
|
|
615
|
+
return Promise.resolve(cached.data);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
return fetcher().then(data => {
|
|
619
|
+
cache.set(key, {
|
|
620
|
+
data,
|
|
621
|
+
timestamp: Date.now()
|
|
622
|
+
});
|
|
623
|
+
return data;
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
builder.field('input.productCategory', async (data, config, helpers) => {
|
|
628
|
+
const sku = helpers.get(data, 'item.sku');
|
|
629
|
+
|
|
630
|
+
// Cache product data for 5 minutes
|
|
631
|
+
const productData = await getCachedOrFetch(
|
|
632
|
+
`product:${sku}`,
|
|
633
|
+
() => fetchProductData(sku),
|
|
634
|
+
300000 // 5 minutes
|
|
635
|
+
);
|
|
636
|
+
|
|
637
|
+
return productData.category;
|
|
638
|
+
});
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
---
|
|
642
|
+
|
|
643
|
+
## Error Handling
|
|
644
|
+
|
|
645
|
+
### Pattern 1: Graceful Degradation
|
|
646
|
+
|
|
647
|
+
**Use Case:** Provide fallback when optional services fail.
|
|
648
|
+
|
|
649
|
+
```typescript
|
|
650
|
+
builder.field('input.customer.loyaltyTier', async (data, config, helpers) => {
|
|
651
|
+
const customerId = helpers.get(data, 'orders.order.customer.customer-no');
|
|
652
|
+
|
|
653
|
+
try {
|
|
654
|
+
const loyaltyData = await helpers.retry(
|
|
655
|
+
async () => {
|
|
656
|
+
const response = await fetch(
|
|
657
|
+
`https://api.loyalty.com/customers/${customerId}/tier`,
|
|
658
|
+
{
|
|
659
|
+
headers: { 'Authorization': `Bearer ${config.loyaltyApiToken}` },
|
|
660
|
+
timeout: 5000 // 5 second timeout
|
|
661
|
+
}
|
|
662
|
+
);
|
|
663
|
+
|
|
664
|
+
if (!response.ok) {
|
|
665
|
+
throw new Error(`Loyalty API error: ${response.status}`);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
return await response.json();
|
|
669
|
+
},
|
|
670
|
+
2, // Only retry twice for optional service
|
|
671
|
+
500
|
|
672
|
+
);
|
|
673
|
+
|
|
674
|
+
return loyaltyData.tier;
|
|
675
|
+
|
|
676
|
+
} catch (error) {
|
|
677
|
+
helpers.log?.warn('Failed to fetch loyalty tier, using default', {
|
|
678
|
+
customerId,
|
|
679
|
+
error: error.message
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
// Graceful degradation - use default tier
|
|
683
|
+
return config.defaultLoyaltyTier || 'STANDARD';
|
|
684
|
+
}
|
|
685
|
+
});
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
---
|
|
689
|
+
|
|
690
|
+
### Pattern 2: Validation with Custom Errors
|
|
691
|
+
|
|
692
|
+
**Use Case:** Provide clear, actionable error messages.
|
|
693
|
+
|
|
694
|
+
```typescript
|
|
695
|
+
builder.validate(
|
|
696
|
+
'input.totalPrice',
|
|
697
|
+
(data, config, helpers) => {
|
|
698
|
+
const grossPrice = helpers.get(data, 'orders.order.totals.order-total.gross-price');
|
|
699
|
+
|
|
700
|
+
if (!grossPrice) {
|
|
701
|
+
throw new Error(
|
|
702
|
+
'Missing required field: orders.order.totals.order-total.gross-price. ' +
|
|
703
|
+
'Ensure the order data includes total pricing information.'
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
const parsed = helpers.parseFloatSafe(grossPrice, -1);
|
|
708
|
+
|
|
709
|
+
if (parsed < 0) {
|
|
710
|
+
throw new Error(
|
|
711
|
+
`Invalid price value: "${grossPrice}". ` +
|
|
712
|
+
'Price must be a positive number.'
|
|
713
|
+
);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
return parsed;
|
|
717
|
+
},
|
|
718
|
+
(value) => {
|
|
719
|
+
if (value <= 0) {
|
|
720
|
+
return `Order total must be greater than zero. Received: ${value}`;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
if (value > 1000000) {
|
|
724
|
+
return `Order total exceeds maximum allowed value. Received: ${value}`;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
return true; // Validation passed
|
|
728
|
+
}
|
|
729
|
+
);
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
---
|
|
733
|
+
|
|
734
|
+
## Testing Patterns
|
|
735
|
+
|
|
736
|
+
### Pattern 1: Test with Canonical Data
|
|
737
|
+
|
|
738
|
+
```typescript
|
|
739
|
+
import { canonicalOrder } from '../../../03-PATTERN-GUIDES/examples/test-data/canonical-order.json';
|
|
740
|
+
import { ResolverHelpers } from '@fluentcommerce/fc-connect-sdk';
|
|
741
|
+
|
|
742
|
+
describe('Order Resolvers', () => {
|
|
743
|
+
const config = {
|
|
744
|
+
defaultRetailerId: '1',
|
|
745
|
+
defaultCurrency: 'USD'
|
|
746
|
+
};
|
|
747
|
+
|
|
748
|
+
it('should extract order reference', () => {
|
|
749
|
+
const result = resolvers\['input.ref'](canonicalOrder, config, ResolverHelpers);
|
|
750
|
+
expect(result).toBe('ORD-2025-001');
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
it('should calculate total price', () => {
|
|
754
|
+
const result = resolvers\['input.totalPrice'](canonicalOrder, config, ResolverHelpers);
|
|
755
|
+
expect(result).toBe('151.17');
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
it('should extract customer email', () => {
|
|
759
|
+
const result = resolvers\['input.customer.email'](canonicalOrder, config, ResolverHelpers);
|
|
760
|
+
expect(result).toBe('john.doe@example.com');
|
|
761
|
+
});
|
|
762
|
+
});
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
---
|
|
766
|
+
|
|
767
|
+
### Pattern 2: Mock External Dependencies
|
|
768
|
+
|
|
769
|
+
```typescript
|
|
770
|
+
describe('Customer Lookup Resolver', () => {
|
|
771
|
+
it('should use existing customer when found', async () => {
|
|
772
|
+
const mockFluentClient = {
|
|
773
|
+
graphql: jest.fn().mockResolvedValue({
|
|
774
|
+
data: {
|
|
775
|
+
customers: {
|
|
776
|
+
edges: [{
|
|
777
|
+
node: { id: '123', username: 'CUST-001' }
|
|
778
|
+
}]
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
})
|
|
782
|
+
};
|
|
783
|
+
|
|
784
|
+
const mockHelpers = {
|
|
785
|
+
...ResolverHelpers,
|
|
786
|
+
fluentClient: mockFluentClient,
|
|
787
|
+
retry: (fn) => fn(), // Skip retry logic in tests
|
|
788
|
+
log: {
|
|
789
|
+
info: jest.fn(),
|
|
790
|
+
warn: jest.fn(),
|
|
791
|
+
error: jest.fn()
|
|
792
|
+
}
|
|
793
|
+
};
|
|
794
|
+
|
|
795
|
+
const result = await resolvers`'input.customer.id'`;
|
|
796
|
+
|
|
797
|
+
expect(result).toBe('123');
|
|
798
|
+
expect(mockFluentClient.graphql).toHaveBeenCalledTimes(1);
|
|
799
|
+
expect(mockHelpers.log.info).toHaveBeenCalledWith(
|
|
800
|
+
'Found existing customer',
|
|
801
|
+
{ id: '123' }
|
|
802
|
+
);
|
|
803
|
+
});
|
|
804
|
+
});
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
---
|
|
808
|
+
|
|
809
|
+
## See Also
|
|
810
|
+
|
|
811
|
+
- [Main Resolver Guide](./mapping-resolvers-resolver-guide.md)
|
|
812
|
+
- [API Reference](./mapping-resolvers-resolver-api-reference.md)
|
|
813
|
+
- [Helper Functions Reference](./mapping-resolvers-resolver-helpers-reference.md)
|
|
814
|
+
- [Parameters Reference](./mapping-resolvers-resolver-parameters-reference.md)
|
|
815
|
+
- [Troubleshooting](./mapping-resolvers-resolver-troubleshooting.md)
|
|
816
|
+
|
|
817
|
+
---
|
|
818
|
+
|
|
819
|
+
**Document Version:** 2.0.0
|
|
820
|
+
**SDK Version:** 1.5.0+
|
|
821
|
+
**Last Updated:** 2025-01-14
|
|
822
|
+
|
|
823
|
+
---
|
|
824
|
+
|
|
825
|
+
**License:** MIT
|
|
826
|
+
**Copyright:** © 2025 Fluent Commerce
|