@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,1061 +1,1061 @@
|
|
|
1
|
-
# Module 5: Error Handling and Resilience
|
|
2
|
-
|
|
3
|
-
> **Learning Objective:** Implement robust error handling, retry strategies, and resilience patterns for production-grade integrations.
|
|
4
|
-
>
|
|
5
|
-
> **Level:** Intermediate
|
|
6
|
-
|
|
7
|
-
## Table of Contents
|
|
8
|
-
|
|
9
|
-
1. [Why Error Handling Matters](#why-error-handling-matters)
|
|
10
|
-
2. [Error Categories](#error-categories)
|
|
11
|
-
3. [SDK Error Handling](#sdk-error-handling)
|
|
12
|
-
4. [Retry Strategies](#retry-strategies)
|
|
13
|
-
5. [Circuit Breaker Pattern](#circuit-breaker-pattern)
|
|
14
|
-
6. [Graceful Degradation](#graceful-degradation)
|
|
15
|
-
7. [Logging and Monitoring](#logging-and-monitoring)
|
|
16
|
-
8. [Error Recovery Patterns](#error-recovery-patterns)
|
|
17
|
-
9. [Production Checklist](#production-checklist)
|
|
18
|
-
10. [Complete Examples](#complete-examples)
|
|
19
|
-
11. [Summary](#summary)
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
## Why Error Handling Matters
|
|
24
|
-
|
|
25
|
-
### Production Realities
|
|
26
|
-
|
|
27
|
-
| Failure Type | Frequency | Impact | Recovery Time |
|
|
28
|
-
|--------------|-----------|--------|---------------|
|
|
29
|
-
| **Network Timeouts** | 1-5% of requests | Temporary | Seconds (retry) |
|
|
30
|
-
| **API Rate Limits** | Variable | Temporary | Minutes (backoff) |
|
|
31
|
-
| **Validation Errors** | 0.1-1% | Permanent | Manual fix |
|
|
32
|
-
| **Service Outages** | Rare | Temporary | Minutes to hours |
|
|
33
|
-
| **Bad Data** | 0.5-2% | Permanent | Manual fix |
|
|
34
|
-
|
|
35
|
-
### Cost of Poor Error Handling
|
|
36
|
-
|
|
37
|
-
```
|
|
38
|
-
Scenario: 10,000 daily inventory updates, 1% error rate
|
|
39
|
-
|
|
40
|
-
WITHOUT proper error handling:
|
|
41
|
-
- 100 failed updates/day
|
|
42
|
-
- No retry → permanent data loss
|
|
43
|
-
- Manual investigation: 2 hours/day
|
|
44
|
-
- Customer impact: Out-of-stock items shown available
|
|
45
|
-
|
|
46
|
-
WITH proper error handling:
|
|
47
|
-
- 100 initial failures
|
|
48
|
-
- 95 recovered via retry (5 seconds)
|
|
49
|
-
- 5 logged for manual review (15 minutes)
|
|
50
|
-
- Customer impact: Minimal
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### Error Handling Goals
|
|
54
|
-
|
|
55
|
-
1. **Resilience**: Recover automatically from transient failures
|
|
56
|
-
2. **Transparency**: Log errors with context for debugging
|
|
57
|
-
3. **User Experience**: Provide meaningful error messages
|
|
58
|
-
4. **Data Integrity**: Never lose data silently
|
|
59
|
-
5. **Observability**: Monitor error rates and patterns
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
## Error Categories
|
|
64
|
-
|
|
65
|
-
### 1. Transient Errors (Retryable)
|
|
66
|
-
|
|
67
|
-
**Cause**: Temporary conditions that may resolve
|
|
68
|
-
|
|
69
|
-
| Error Type | HTTP Status | Retry? | Backoff | Example |
|
|
70
|
-
|------------|-------------|--------|---------|---------|
|
|
71
|
-
| **Network Timeout** | 504 | Yes | Exponential | Request timeout |
|
|
72
|
-
| **Rate Limit** | 429 | Yes | Linear | Too many requests |
|
|
73
|
-
| **Service Unavailable** | 503 | Yes | Exponential | API maintenance |
|
|
74
|
-
| **Server Error** | 500 | Yes | Exponential | Internal error |
|
|
75
|
-
|
|
76
|
-
### 2. Permanent Errors (Non-Retryable)
|
|
77
|
-
|
|
78
|
-
**Cause**: Data or configuration issues requiring manual intervention
|
|
79
|
-
|
|
80
|
-
| Error Type | HTTP Status | Retry? | Action | Example |
|
|
81
|
-
|------------|-------------|--------|--------|---------|
|
|
82
|
-
| **Validation Error** | 400 | No | Fix data | Missing required field |
|
|
83
|
-
| **Authentication** | 401 | No | Fix credentials | Invalid token |
|
|
84
|
-
| **Authorization** | 403 | No | Fix permissions | Insufficient access |
|
|
85
|
-
| **Not Found** | 404 | No | Check config | Invalid endpoint |
|
|
86
|
-
| **Unprocessable** | 422 | No | Fix data | Invalid field value |
|
|
87
|
-
|
|
88
|
-
### 3. Partial Errors (Record-Level)
|
|
89
|
-
|
|
90
|
-
**Cause**: Some records fail while others succeed
|
|
91
|
-
|
|
92
|
-
| Scenario | Impact | Action | Example |
|
|
93
|
-
|----------|--------|--------|---------|
|
|
94
|
-
| **Batch API Errors** | Some records fail | Log failed records | 98/100 succeed |
|
|
95
|
-
| **Mapping Errors** | Some records skip | Log validation errors | Missing SKU |
|
|
96
|
-
| **GraphQL Errors** | Some mutations fail | Log errors, continue | Duplicate ref |
|
|
97
|
-
|
|
98
|
-
---
|
|
99
|
-
|
|
100
|
-
## SDK Error Handling
|
|
101
|
-
|
|
102
|
-
### FluentClient Error Handling
|
|
103
|
-
|
|
104
|
-
The SDK automatically handles some errors:
|
|
105
|
-
|
|
106
|
-
```typescript
|
|
107
|
-
import { createClient } from '@fluentcommerce/fc-connect-sdk';
|
|
108
|
-
|
|
109
|
-
const client = await createClient({ /* config */ });
|
|
110
|
-
|
|
111
|
-
try {
|
|
112
|
-
const result = await client.graphql({ query, variables });
|
|
113
|
-
|
|
114
|
-
// Check for GraphQL errors
|
|
115
|
-
if (result.errors && result.errors.length > 0) {
|
|
116
|
-
// Classify errors to determine retry strategy
|
|
117
|
-
const { classifyErrors } = await import('@fluentcommerce/fc-connect-sdk');
|
|
118
|
-
const classification = classifyErrors(result.errors);
|
|
119
|
-
|
|
120
|
-
console.error('GraphQL errors:', {
|
|
121
|
-
errorCode: classification.errorCode,
|
|
122
|
-
retryable: classification.retryable,
|
|
123
|
-
action: classification.action,
|
|
124
|
-
message: classification.userMessage,
|
|
125
|
-
errors: result.errors,
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
// Don't retry non-retryable errors
|
|
129
|
-
if (!classification.retryable) {
|
|
130
|
-
throw new Error(classification.userMessage);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// result.data may still be partially populated for retryable errors
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
} catch (error: any) {
|
|
137
|
-
// Network or authentication errors
|
|
138
|
-
console.error('Request failed:', error.message);
|
|
139
|
-
|
|
140
|
-
// Error types:
|
|
141
|
-
if (error.code === 'ECONNREFUSED') {
|
|
142
|
-
// Network error
|
|
143
|
-
} else if (error.statusCode === 401) {
|
|
144
|
-
// Authentication error
|
|
145
|
-
} else if (error.statusCode === 429) {
|
|
146
|
-
// Rate limit
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
### Batch API Error Handling
|
|
152
|
-
|
|
153
|
-
```typescript
|
|
154
|
-
// Create job
|
|
155
|
-
const job = await client.createJob({ name: 'Inventory Sync', retailerId: '2' });
|
|
156
|
-
|
|
157
|
-
// Send batches
|
|
158
|
-
for (const batch of batches) {
|
|
159
|
-
try {
|
|
160
|
-
await client.sendBatch(job.id, { entities: batch });
|
|
161
|
-
} catch (error: any) {
|
|
162
|
-
console.error('Batch send failed:', error.message);
|
|
163
|
-
// Log failed batch for retry
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Check job status
|
|
168
|
-
const status = await client.getJobStatus(job.id);
|
|
169
|
-
|
|
170
|
-
if (status.errorSummary && status.errorSummary.totalErrors > 0) {
|
|
171
|
-
console.warn(`Job completed with ${status.errorSummary.totalErrors} errors`);
|
|
172
|
-
|
|
173
|
-
// Get error details
|
|
174
|
-
const errors = await client.graphql({
|
|
175
|
-
query: `
|
|
176
|
-
query GetJobErrors($jobId: ID!) {
|
|
177
|
-
job(id: $jobId) {
|
|
178
|
-
batches(first: 100) {
|
|
179
|
-
edges {
|
|
180
|
-
node {
|
|
181
|
-
errors {
|
|
182
|
-
recordRef
|
|
183
|
-
errorType
|
|
184
|
-
errorMessage
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
`,
|
|
192
|
-
variables: { jobId: job.id }
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
// Log errors for manual review
|
|
196
|
-
console.error('Batch errors:', JSON.stringify(errors, null, 2));
|
|
197
|
-
}
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
### UniversalMapper Error Handling
|
|
201
|
-
|
|
202
|
-
```typescript
|
|
203
|
-
import { UniversalMapper } from '@fluentcommerce/fc-connect-sdk';
|
|
204
|
-
|
|
205
|
-
const mapper = new UniversalMapper(mappingConfig);
|
|
206
|
-
|
|
207
|
-
const successfulRecords = [];
|
|
208
|
-
const failedRecords = [];
|
|
209
|
-
|
|
210
|
-
for (const record of records) {
|
|
211
|
-
const result = await mapper.map(record);
|
|
212
|
-
|
|
213
|
-
if (result.success) {
|
|
214
|
-
successfulRecords.push(result.data);
|
|
215
|
-
} else {
|
|
216
|
-
failedRecords.push({
|
|
217
|
-
record,
|
|
218
|
-
errors: result.errors
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
console.log(`Mapped ${successfulRecords.length} records, ${failedRecords.length} failures`);
|
|
224
|
-
|
|
225
|
-
// Write failed records to error file
|
|
226
|
-
if (failedRecords.length > 0) {
|
|
227
|
-
await fs.writeFile(
|
|
228
|
-
'mapping-errors.json',
|
|
229
|
-
JSON.stringify(failedRecords, null, 2)
|
|
230
|
-
);
|
|
231
|
-
}
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
---
|
|
235
|
-
|
|
236
|
-
## Retry Strategies
|
|
237
|
-
|
|
238
|
-
### Pattern 1: Exponential Backoff
|
|
239
|
-
|
|
240
|
-
**Best for**: Network errors, rate limits, server errors
|
|
241
|
-
|
|
242
|
-
```typescript
|
|
243
|
-
/**
|
|
244
|
-
* Exponential Backoff Retry
|
|
245
|
-
*
|
|
246
|
-
* Delay = initialDelay * (2 ^ attempt)
|
|
247
|
-
* Example: 1s, 2s, 4s, 8s, 16s
|
|
248
|
-
*/
|
|
249
|
-
|
|
250
|
-
async function exponentialBackoff<T>(
|
|
251
|
-
fn: () => Promise<T>,
|
|
252
|
-
options = {
|
|
253
|
-
maxRetries: 5,
|
|
254
|
-
initialDelay: 1000,
|
|
255
|
-
maxDelay: 30000
|
|
256
|
-
}
|
|
257
|
-
): Promise<T> {
|
|
258
|
-
let lastError: Error;
|
|
259
|
-
|
|
260
|
-
for (let attempt = 0; attempt <= options.maxRetries; attempt++) {
|
|
261
|
-
try {
|
|
262
|
-
return await fn();
|
|
263
|
-
|
|
264
|
-
} catch (error: any) {
|
|
265
|
-
lastError = error;
|
|
266
|
-
|
|
267
|
-
// Don't retry permanent errors
|
|
268
|
-
if (error.statusCode && error.statusCode < 500) {
|
|
269
|
-
throw error;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
if (attempt < options.maxRetries) {
|
|
273
|
-
// Calculate delay
|
|
274
|
-
const delay = Math.min(
|
|
275
|
-
options.initialDelay * Math.pow(2, attempt),
|
|
276
|
-
options.maxDelay
|
|
277
|
-
);
|
|
278
|
-
|
|
279
|
-
console.log(`Retry ${attempt + 1}/${options.maxRetries} after ${delay}ms`);
|
|
280
|
-
|
|
281
|
-
await sleep(delay);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
throw lastError!;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
function sleep(ms: number): Promise<void> {
|
|
290
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Usage
|
|
294
|
-
const result = await exponentialBackoff(() =>
|
|
295
|
-
client.graphql({ query, variables })
|
|
296
|
-
);
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
### Pattern 2: Linear Backoff (Rate Limits)
|
|
300
|
-
|
|
301
|
-
**Best for**: Rate limit errors (429)
|
|
302
|
-
|
|
303
|
-
```typescript
|
|
304
|
-
/**
|
|
305
|
-
* Linear Backoff for Rate Limits
|
|
306
|
-
*
|
|
307
|
-
* Delay = retryAfter (from response header)
|
|
308
|
-
*/
|
|
309
|
-
|
|
310
|
-
async function linearBackoff<T>(
|
|
311
|
-
fn: () => Promise<T>,
|
|
312
|
-
maxRetries = 3
|
|
313
|
-
): Promise<T> {
|
|
314
|
-
let lastError: Error;
|
|
315
|
-
|
|
316
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
317
|
-
try {
|
|
318
|
-
return await fn();
|
|
319
|
-
|
|
320
|
-
} catch (error: any) {
|
|
321
|
-
lastError = error;
|
|
322
|
-
|
|
323
|
-
if (error.statusCode === 429) {
|
|
324
|
-
// Check for Retry-After header
|
|
325
|
-
const retryAfter = error.headers?.['retry-after'];
|
|
326
|
-
const delay = retryAfter ? parseInt(retryAfter) * 1000 : 60000;
|
|
327
|
-
|
|
328
|
-
console.log(`Rate limited - retry after ${delay}ms`);
|
|
329
|
-
|
|
330
|
-
await sleep(delay);
|
|
331
|
-
} else {
|
|
332
|
-
throw error; // Not a rate limit error
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
throw lastError!;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// Usage
|
|
341
|
-
const result = await linearBackoff(() =>
|
|
342
|
-
client.graphql({ query, variables })
|
|
343
|
-
);
|
|
344
|
-
```
|
|
345
|
-
|
|
346
|
-
### Pattern 3: Jittered Backoff
|
|
347
|
-
|
|
348
|
-
**Best for**: High concurrency to avoid thundering herd
|
|
349
|
-
|
|
350
|
-
```typescript
|
|
351
|
-
/**
|
|
352
|
-
* Jittered Exponential Backoff
|
|
353
|
-
*
|
|
354
|
-
* Adds randomness to avoid synchronized retries
|
|
355
|
-
*/
|
|
356
|
-
|
|
357
|
-
function jitteredBackoff(attempt: number, baseDelay = 1000, maxDelay = 30000): number {
|
|
358
|
-
const exponentialDelay = baseDelay * Math.pow(2, attempt);
|
|
359
|
-
const jitter = Math.random() * exponentialDelay * 0.3; // ±30% jitter
|
|
360
|
-
return Math.min(exponentialDelay + jitter, maxDelay);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
async function retryWithJitter<T>(
|
|
364
|
-
fn: () => Promise<T>,
|
|
365
|
-
maxRetries = 5
|
|
366
|
-
): Promise<T> {
|
|
367
|
-
let lastError: Error;
|
|
368
|
-
|
|
369
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
370
|
-
try {
|
|
371
|
-
return await fn();
|
|
372
|
-
|
|
373
|
-
} catch (error: any) {
|
|
374
|
-
lastError = error;
|
|
375
|
-
|
|
376
|
-
if (error.statusCode && error.statusCode < 500) {
|
|
377
|
-
throw error; // Don't retry permanent errors
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
if (attempt < maxRetries) {
|
|
381
|
-
const delay = jitteredBackoff(attempt);
|
|
382
|
-
console.log(`Retry ${attempt + 1} after ${delay.toFixed(0)}ms`);
|
|
383
|
-
await sleep(delay);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
throw lastError!;
|
|
389
|
-
}
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
---
|
|
393
|
-
|
|
394
|
-
## Circuit Breaker Pattern
|
|
395
|
-
|
|
396
|
-
### Concept
|
|
397
|
-
|
|
398
|
-
**Prevent cascading failures** by stopping requests to failing services.
|
|
399
|
-
|
|
400
|
-
```
|
|
401
|
-
States:
|
|
402
|
-
┌─────────┐
|
|
403
|
-
│ CLOSED │ Normal operation, allow all requests
|
|
404
|
-
└────┬────┘
|
|
405
|
-
│ Failure threshold reached (e.g., 5 failures)
|
|
406
|
-
▼
|
|
407
|
-
┌─────────┐
|
|
408
|
-
│ OPEN │ Block all requests, return error immediately
|
|
409
|
-
└────┬────┘
|
|
410
|
-
│ Timeout elapsed (e.g., 60 seconds)
|
|
411
|
-
▼
|
|
412
|
-
┌─────────┐
|
|
413
|
-
│HALF-OPEN│ Allow limited requests to test recovery
|
|
414
|
-
└────┬────┘
|
|
415
|
-
│ Success → CLOSED
|
|
416
|
-
│ Failure → OPEN
|
|
417
|
-
```
|
|
418
|
-
|
|
419
|
-
### Implementation
|
|
420
|
-
|
|
421
|
-
```typescript
|
|
422
|
-
/**
|
|
423
|
-
* Circuit Breaker
|
|
424
|
-
*
|
|
425
|
-
* Prevents cascading failures by stopping requests to failing services
|
|
426
|
-
*/
|
|
427
|
-
|
|
428
|
-
class CircuitBreaker {
|
|
429
|
-
private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';
|
|
430
|
-
private failures = 0;
|
|
431
|
-
private lastFailureTime = 0;
|
|
432
|
-
|
|
433
|
-
constructor(
|
|
434
|
-
private options = {
|
|
435
|
-
failureThreshold: 5,
|
|
436
|
-
resetTimeout: 60000,
|
|
437
|
-
halfOpenRequests: 3
|
|
438
|
-
}
|
|
439
|
-
) {}
|
|
440
|
-
|
|
441
|
-
async execute<T>(fn: () => Promise<T>): Promise<T> {
|
|
442
|
-
// Check state
|
|
443
|
-
if (this.state === 'OPEN') {
|
|
444
|
-
// Check if timeout elapsed
|
|
445
|
-
if (Date.now() - this.lastFailureTime > this.options.resetTimeout) {
|
|
446
|
-
console.log('Circuit HALF-OPEN');
|
|
447
|
-
this.state = 'HALF_OPEN';
|
|
448
|
-
} else {
|
|
449
|
-
throw new Error('Circuit breaker is OPEN - blocking requests');
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
try {
|
|
454
|
-
const result = await fn();
|
|
455
|
-
|
|
456
|
-
// Success - reset failures
|
|
457
|
-
if (this.state === 'HALF_OPEN') {
|
|
458
|
-
console.log('Circuit CLOSED');
|
|
459
|
-
this.state = 'CLOSED';
|
|
460
|
-
}
|
|
461
|
-
this.failures = 0;
|
|
462
|
-
|
|
463
|
-
return result;
|
|
464
|
-
|
|
465
|
-
} catch (error) {
|
|
466
|
-
this.failures++;
|
|
467
|
-
this.lastFailureTime = Date.now();
|
|
468
|
-
|
|
469
|
-
if (this.failures >= this.options.failureThreshold) {
|
|
470
|
-
console.log('Circuit OPEN - failure threshold reached');
|
|
471
|
-
this.state = 'OPEN';
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
throw error;
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
// Usage
|
|
480
|
-
const breaker = new CircuitBreaker({
|
|
481
|
-
failureThreshold: 5,
|
|
482
|
-
resetTimeout: 60000
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
try {
|
|
486
|
-
const result = await breaker.execute(() =>
|
|
487
|
-
client.graphql({ query, variables })
|
|
488
|
-
);
|
|
489
|
-
} catch (error: any) {
|
|
490
|
-
if (error.message.includes('Circuit breaker is OPEN')) {
|
|
491
|
-
console.error('Service is down - circuit breaker active');
|
|
492
|
-
// Use fallback or queue for later
|
|
493
|
-
} else {
|
|
494
|
-
throw error;
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
```
|
|
498
|
-
|
|
499
|
-
---
|
|
500
|
-
|
|
501
|
-
## Graceful Degradation
|
|
502
|
-
|
|
503
|
-
### Pattern 1: Fallback Values
|
|
504
|
-
|
|
505
|
-
**Provide defaults** when non-critical data is unavailable.
|
|
506
|
-
|
|
507
|
-
```typescript
|
|
508
|
-
async function getProductDetails(sku: string): Promise<any> {
|
|
509
|
-
try {
|
|
510
|
-
const product = await client.graphql({
|
|
511
|
-
query: `query GetProduct($sku: String!) {
|
|
512
|
-
products(ref: $sku, first: 1) {
|
|
513
|
-
edges {
|
|
514
|
-
node {
|
|
515
|
-
id
|
|
516
|
-
ref
|
|
517
|
-
name
|
|
518
|
-
price
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
}`,
|
|
523
|
-
variables: { sku }
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
return product?.products?.edges?.[0]?.node;
|
|
527
|
-
|
|
528
|
-
} catch (error: any) {
|
|
529
|
-
console.warn(`Failed to fetch product ${sku}, using defaults`, error);
|
|
530
|
-
|
|
531
|
-
// Return fallback
|
|
532
|
-
return {
|
|
533
|
-
id: null,
|
|
534
|
-
ref: sku,
|
|
535
|
-
name: 'Product details unavailable',
|
|
536
|
-
price: 0
|
|
537
|
-
};
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
```
|
|
541
|
-
|
|
542
|
-
### Pattern 2: Partial Failure Handling
|
|
543
|
-
|
|
544
|
-
**Process successful records** even if some fail.
|
|
545
|
-
|
|
546
|
-
```typescript
|
|
547
|
-
async function processOrders(orders: any[]): Promise<{
|
|
548
|
-
successful: any[];
|
|
549
|
-
failed: any[];
|
|
550
|
-
}> {
|
|
551
|
-
const successful = [];
|
|
552
|
-
const failed = [];
|
|
553
|
-
|
|
554
|
-
for (const order of orders) {
|
|
555
|
-
try {
|
|
556
|
-
const result = await client.graphql({
|
|
557
|
-
query: CREATE_ORDER_MUTATION,
|
|
558
|
-
variables: { input: order }
|
|
559
|
-
});
|
|
560
|
-
|
|
561
|
-
successful.push({ order, result });
|
|
562
|
-
|
|
563
|
-
} catch (error: any) {
|
|
564
|
-
console.error(`Failed to process order ${order.ref}`, error);
|
|
565
|
-
|
|
566
|
-
failed.push({ order, error: error.message });
|
|
567
|
-
|
|
568
|
-
// Continue with next order (don't throw)
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
console.log(`Processed ${successful.length} orders, ${failed.length} failures`);
|
|
573
|
-
|
|
574
|
-
// Write failed orders for retry
|
|
575
|
-
if (failed.length > 0) {
|
|
576
|
-
await fs.writeFile('failed-orders.json', JSON.stringify(failed, null, 2));
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
return { successful, failed };
|
|
580
|
-
}
|
|
581
|
-
```
|
|
582
|
-
|
|
583
|
-
### Pattern 3: Queue for Retry
|
|
584
|
-
|
|
585
|
-
**Queue failed operations** for later retry.
|
|
586
|
-
|
|
587
|
-
```typescript
|
|
588
|
-
import { StateService, VersoriKVAdapter } from '@fluentcommerce/fc-connect-sdk';
|
|
589
|
-
|
|
590
|
-
async function processWithQueue(records: any[]) {
|
|
591
|
-
const kvAdapter = new VersoriKVAdapter(openKv());
|
|
592
|
-
const stateService = new StateService(logger);
|
|
593
|
-
|
|
594
|
-
for (const record of records) {
|
|
595
|
-
try {
|
|
596
|
-
await processRecord(record);
|
|
597
|
-
|
|
598
|
-
} catch (error: any) {
|
|
599
|
-
console.error(`Failed to process record ${record.id}`, error);
|
|
600
|
-
|
|
601
|
-
// Add to retry queue
|
|
602
|
-
const queue = await stateService.getState('retry-queue') || [];
|
|
603
|
-
queue.push({
|
|
604
|
-
record,
|
|
605
|
-
error: error.message,
|
|
606
|
-
timestamp: Date.now(),
|
|
607
|
-
retryCount: 0
|
|
608
|
-
});
|
|
609
|
-
await stateService.setState('retry-queue', queue);
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
// Separate process to retry queued items
|
|
615
|
-
async function retryQueue() {
|
|
616
|
-
const kvAdapter = new VersoriKVAdapter(openKv());
|
|
617
|
-
const stateService = new StateService(logger);
|
|
618
|
-
|
|
619
|
-
const queue = await stateService.getState('retry-queue') || [];
|
|
620
|
-
const remainingQueue = [];
|
|
621
|
-
|
|
622
|
-
for (const item of queue) {
|
|
623
|
-
try {
|
|
624
|
-
await processRecord(item.record);
|
|
625
|
-
console.log(`✓ Retry succeeded for ${item.record.id}`);
|
|
626
|
-
|
|
627
|
-
} catch (error: any) {
|
|
628
|
-
item.retryCount++;
|
|
629
|
-
|
|
630
|
-
if (item.retryCount < 3) {
|
|
631
|
-
// Re-queue
|
|
632
|
-
remainingQueue.push(item);
|
|
633
|
-
} else {
|
|
634
|
-
// Max retries reached - log as permanent failure
|
|
635
|
-
console.error(`✗ Max retries reached for ${item.record.id}`);
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
await stateService.setState('retry-queue', remainingQueue);
|
|
641
|
-
}
|
|
642
|
-
```
|
|
643
|
-
|
|
644
|
-
---
|
|
645
|
-
|
|
646
|
-
## Logging and Monitoring
|
|
647
|
-
|
|
648
|
-
### Structured Logging
|
|
649
|
-
|
|
650
|
-
**Use SDK logging utilities** for consistent format and easy parsing.
|
|
651
|
-
|
|
652
|
-
```typescript
|
|
653
|
-
import {
|
|
654
|
-
createConsoleLogger,
|
|
655
|
-
toStructuredLogger,
|
|
656
|
-
generateCorrelationId
|
|
657
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
658
|
-
|
|
659
|
-
// Basic console logger
|
|
660
|
-
const logger = createConsoleLogger();
|
|
661
|
-
|
|
662
|
-
// Or with structured context
|
|
663
|
-
const contextLogger = toStructuredLogger(logger, {
|
|
664
|
-
service: 'InventorySync',
|
|
665
|
-
correlationId: generateCorrelationId(),
|
|
666
|
-
environment: 'production'
|
|
667
|
-
});
|
|
668
|
-
|
|
669
|
-
// Usage
|
|
670
|
-
contextLogger.info('Processing inventory file', {
|
|
671
|
-
fileName: 'inventory.csv',
|
|
672
|
-
recordCount: 5000
|
|
673
|
-
});
|
|
674
|
-
// Output: [INFO] Processing inventory file
|
|
675
|
-
// {"service":"InventorySync","correlationId":"...","fileName":"inventory.csv","recordCount":5000}
|
|
676
|
-
|
|
677
|
-
contextLogger.error('Batch API error', error, {
|
|
678
|
-
jobId: job.id,
|
|
679
|
-
batchId: batch.id
|
|
680
|
-
});
|
|
681
|
-
// Output: [ERROR] Batch API error
|
|
682
|
-
// {"service":"InventorySync","error":"...","stack":"...","jobId":"...","batchId":"..."}
|
|
683
|
-
```
|
|
684
|
-
|
|
685
|
-
**For Versori Platform:**
|
|
686
|
-
|
|
687
|
-
```typescript
|
|
688
|
-
// ✅ CORRECT - Use native log from context
|
|
689
|
-
import { schedule } from '@versori/run';
|
|
690
|
-
|
|
691
|
-
export const myWorkflow = schedule('workflow', '0 * * * *')
|
|
692
|
-
.execute(async ({ log }) => {
|
|
693
|
-
// Use native log directly - don't wrap it!
|
|
694
|
-
log.info('Processing started');
|
|
695
|
-
log.error('Processing failed', error);
|
|
696
|
-
});
|
|
697
|
-
```
|
|
698
|
-
|
|
699
|
-
### Error Metrics
|
|
700
|
-
|
|
701
|
-
**Track error rates** for monitoring.
|
|
702
|
-
|
|
703
|
-
```typescript
|
|
704
|
-
class ErrorMetrics {
|
|
705
|
-
private errors: Map<string, number> = new Map();
|
|
706
|
-
private total = 0;
|
|
707
|
-
|
|
708
|
-
recordError(type: string) {
|
|
709
|
-
this.errors.set(type, (this.errors.get(type) || 0) + 1);
|
|
710
|
-
this.total++;
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
getMetrics() {
|
|
714
|
-
const metrics: any = {
|
|
715
|
-
totalErrors: this.total,
|
|
716
|
-
errorsByType: {}
|
|
717
|
-
};
|
|
718
|
-
|
|
719
|
-
for (const [type, count] of this.errors.entries()) {
|
|
720
|
-
metrics.errorsByType[type] = {
|
|
721
|
-
count,
|
|
722
|
-
percentage: ((count / this.total) * 100).toFixed(2) + '%'
|
|
723
|
-
};
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
return metrics;
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
reset() {
|
|
730
|
-
this.errors.clear();
|
|
731
|
-
this.total = 0;
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
// Usage
|
|
736
|
-
const metrics = new ErrorMetrics();
|
|
737
|
-
|
|
738
|
-
try {
|
|
739
|
-
await processRecord(record);
|
|
740
|
-
} catch (error: any) {
|
|
741
|
-
if (error.statusCode === 429) {
|
|
742
|
-
metrics.recordError('RATE_LIMIT');
|
|
743
|
-
} else if (error.statusCode >= 500) {
|
|
744
|
-
metrics.recordError('SERVER_ERROR');
|
|
745
|
-
} else {
|
|
746
|
-
metrics.recordError('VALIDATION_ERROR');
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
// Log metrics periodically
|
|
751
|
-
console.log('Error metrics:', metrics.getMetrics());
|
|
752
|
-
```
|
|
753
|
-
|
|
754
|
-
---
|
|
755
|
-
|
|
756
|
-
## Error Recovery Patterns
|
|
757
|
-
|
|
758
|
-
### Pattern 1: Dead Letter Queue
|
|
759
|
-
|
|
760
|
-
**Store failed records** for manual review.
|
|
761
|
-
|
|
762
|
-
```typescript
|
|
763
|
-
async function processWithDLQ(records: any[]) {
|
|
764
|
-
const successCount = 0;
|
|
765
|
-
const dlq: any[] = [];
|
|
766
|
-
|
|
767
|
-
for (const record of records) {
|
|
768
|
-
try {
|
|
769
|
-
await processRecord(record);
|
|
770
|
-
successCount++;
|
|
771
|
-
|
|
772
|
-
} catch (error: any) {
|
|
773
|
-
// Add to dead letter queue
|
|
774
|
-
dlq.push({
|
|
775
|
-
record,
|
|
776
|
-
error: error.message,
|
|
777
|
-
stack: error.stack,
|
|
778
|
-
timestamp: new Date().toISOString(),
|
|
779
|
-
context: {
|
|
780
|
-
fileName: 'inventory.csv',
|
|
781
|
-
lineNumber: record._lineNumber
|
|
782
|
-
}
|
|
783
|
-
});
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
// Write DLQ to file
|
|
788
|
-
if (dlq.length > 0) {
|
|
789
|
-
await fs.writeFile(
|
|
790
|
-
`dlq-${Date.now()}.json`,
|
|
791
|
-
JSON.stringify(dlq, null, 2)
|
|
792
|
-
);
|
|
793
|
-
|
|
794
|
-
console.log(`Processed ${successCount} records, ${dlq.length} in DLQ`);
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
```
|
|
798
|
-
|
|
799
|
-
### Pattern 2: Compensating Transaction
|
|
800
|
-
|
|
801
|
-
**Undo partial changes** on failure.
|
|
802
|
-
|
|
803
|
-
```typescript
|
|
804
|
-
async function createOrderWithRollback(orderData: any) {
|
|
805
|
-
let customerId: string | null = null;
|
|
806
|
-
|
|
807
|
-
try {
|
|
808
|
-
// Step 1: Create customer
|
|
809
|
-
const customer = await client.graphql({
|
|
810
|
-
query: CREATE_CUSTOMER_MUTATION,
|
|
811
|
-
variables: { input: orderData.customer }
|
|
812
|
-
});
|
|
813
|
-
customerId = customer.id;
|
|
814
|
-
|
|
815
|
-
// Step 2: Create order
|
|
816
|
-
const order = await client.graphql({
|
|
817
|
-
query: CREATE_ORDER_MUTATION,
|
|
818
|
-
variables: {
|
|
819
|
-
input: {
|
|
820
|
-
...orderData,
|
|
821
|
-
customer: { id: customerId }
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
});
|
|
825
|
-
|
|
826
|
-
return order;
|
|
827
|
-
|
|
828
|
-
} catch (error: any) {
|
|
829
|
-
console.error('Order creation failed', error);
|
|
830
|
-
|
|
831
|
-
// Rollback: Delete customer if created
|
|
832
|
-
if (customerId) {
|
|
833
|
-
try {
|
|
834
|
-
await client.graphql({
|
|
835
|
-
query: DELETE_CUSTOMER_MUTATION,
|
|
836
|
-
variables: { id: customerId }
|
|
837
|
-
});
|
|
838
|
-
console.log(`Rolled back customer ${customerId}`);
|
|
839
|
-
} catch (rollbackError) {
|
|
840
|
-
console.error('Rollback failed', rollbackError);
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
throw error;
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
```
|
|
848
|
-
|
|
849
|
-
---
|
|
850
|
-
|
|
851
|
-
## Production Checklist
|
|
852
|
-
|
|
853
|
-
### Before Deployment
|
|
854
|
-
|
|
855
|
-
- [ ] **Error handling** for all API calls
|
|
856
|
-
- [ ] **Retry logic** for transient errors
|
|
857
|
-
- [ ] **Validation** for all input data
|
|
858
|
-
- [ ] **Logging** with structured format
|
|
859
|
-
- [ ] **Error metrics** and monitoring
|
|
860
|
-
- [ ] **Circuit breaker** for external dependencies
|
|
861
|
-
- [ ] **Graceful degradation** for non-critical failures
|
|
862
|
-
- [ ] **Dead letter queue** for failed records
|
|
863
|
-
- [ ] **Idempotency** for duplicate prevention
|
|
864
|
-
- [ ] **Timeout configuration** for all operations
|
|
865
|
-
- [ ] **Rate limit handling** with backoff
|
|
866
|
-
- [ ] **Security** (signature verification, IP allowlist)
|
|
867
|
-
- [ ] **Health check** endpoint
|
|
868
|
-
- [ ] **Error documentation** for ops team
|
|
869
|
-
|
|
870
|
-
### Monitoring
|
|
871
|
-
|
|
872
|
-
- [ ] **Error rate** (< 1% target)
|
|
873
|
-
- [ ] **Retry rate** (< 5% target)
|
|
874
|
-
- [ ] **Response time** (p50, p95, p99)
|
|
875
|
-
- [ ] **Success rate** (> 99% target)
|
|
876
|
-
- [ ] **DLQ size** (alert if > 100)
|
|
877
|
-
- [ ] **Circuit breaker** state changes
|
|
878
|
-
- [ ] **Queue depth** (for async processing)
|
|
879
|
-
|
|
880
|
-
---
|
|
881
|
-
|
|
882
|
-
## Complete Examples
|
|
883
|
-
|
|
884
|
-
### Production-Ready Webhook with Full Error Handling
|
|
885
|
-
|
|
886
|
-
```typescript
|
|
887
|
-
/**
|
|
888
|
-
* Production-ready webhook with comprehensive error handling
|
|
889
|
-
*/
|
|
890
|
-
|
|
891
|
-
import { createClient, GraphQLMutationMapper, StateService, VersoriKVAdapter } from '@fluentcommerce/fc-connect-sdk';
|
|
892
|
-
|
|
893
|
-
export default async function productionWebhook(activation: any, log: any, connections: any) {
|
|
894
|
-
const startTime = Date.now();
|
|
895
|
-
const webhookId = activation.headers['x-webhook-id'] || crypto.randomUUID();
|
|
896
|
-
|
|
897
|
-
try {
|
|
898
|
-
log.info('Webhook received', {
|
|
899
|
-
webhookId,
|
|
900
|
-
source: activation.headers['user-agent']
|
|
901
|
-
});
|
|
902
|
-
|
|
903
|
-
// Idempotency check
|
|
904
|
-
const kvAdapter = new VersoriKVAdapter(openKv());
|
|
905
|
-
const stateService = new StateService(logger);
|
|
906
|
-
|
|
907
|
-
const processed = await stateService.getState(`webhook:${webhookId}`);
|
|
908
|
-
if (processed) {
|
|
909
|
-
log.info('Webhook already processed', { webhookId });
|
|
910
|
-
return {
|
|
911
|
-
status: 200,
|
|
912
|
-
body: {
|
|
913
|
-
success: true,
|
|
914
|
-
message: 'Already processed',
|
|
915
|
-
orderId: processed.orderId
|
|
916
|
-
}
|
|
917
|
-
};
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
// Validate payload
|
|
921
|
-
if (!activation.body || !activation.body.orderRef) {
|
|
922
|
-
throw new Error('Missing required field: orderRef');
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
// Create client with retry
|
|
926
|
-
const client = await retryWithJitter(() =>
|
|
927
|
-
createClient({
|
|
928
|
-
connection: connections.fluent_commerce,
|
|
929
|
-
logger: log
|
|
930
|
-
})
|
|
931
|
-
);
|
|
932
|
-
|
|
933
|
-
// Process order
|
|
934
|
-
const result = await retryWithJitter(() =>
|
|
935
|
-
client.graphql({
|
|
936
|
-
query: CREATE_ORDER_MUTATION,
|
|
937
|
-
variables: { input: mapOrderData(activation.body) }
|
|
938
|
-
})
|
|
939
|
-
);
|
|
940
|
-
|
|
941
|
-
// Mark as processed
|
|
942
|
-
await stateService.setState(`webhook:${webhookId}`, {
|
|
943
|
-
orderId: result.id,
|
|
944
|
-
processedAt: new Date().toISOString()
|
|
945
|
-
});
|
|
946
|
-
|
|
947
|
-
const duration = Date.now() - startTime;
|
|
948
|
-
|
|
949
|
-
log.info('Webhook processed successfully', {
|
|
950
|
-
webhookId,
|
|
951
|
-
orderId: result.id,
|
|
952
|
-
duration
|
|
953
|
-
});
|
|
954
|
-
|
|
955
|
-
return {
|
|
956
|
-
status: 200,
|
|
957
|
-
body: {
|
|
958
|
-
success: true,
|
|
959
|
-
orderId: result.id,
|
|
960
|
-
duration
|
|
961
|
-
}
|
|
962
|
-
};
|
|
963
|
-
|
|
964
|
-
} catch (error: any) {
|
|
965
|
-
const duration = Date.now() - startTime;
|
|
966
|
-
|
|
967
|
-
log.error('Webhook processing failed', error, {
|
|
968
|
-
webhookId,
|
|
969
|
-
duration
|
|
970
|
-
});
|
|
971
|
-
|
|
972
|
-
// Categorize error
|
|
973
|
-
if (error.message.includes('required field')) {
|
|
974
|
-
return {
|
|
975
|
-
status: 400,
|
|
976
|
-
body: {
|
|
977
|
-
success: false,
|
|
978
|
-
error: 'Validation failed',
|
|
979
|
-
details: error.message
|
|
980
|
-
}
|
|
981
|
-
};
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
if (error.statusCode === 429) {
|
|
985
|
-
return {
|
|
986
|
-
status: 429,
|
|
987
|
-
body: {
|
|
988
|
-
success: false,
|
|
989
|
-
error: 'Rate limit exceeded',
|
|
990
|
-
retryAfter: 60
|
|
991
|
-
}
|
|
992
|
-
};
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
return {
|
|
996
|
-
status: 500,
|
|
997
|
-
body: {
|
|
998
|
-
success: false,
|
|
999
|
-
error: 'Internal server error',
|
|
1000
|
-
retryable: true
|
|
1001
|
-
}
|
|
1002
|
-
};
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
function mapOrderData(data: any): any {
|
|
1007
|
-
// Mapping logic
|
|
1008
|
-
return data;
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1011
|
-
async function retryWithJitter<T>(fn: () => Promise<T>): Promise<T> {
|
|
1012
|
-
// Retry implementation from earlier
|
|
1013
|
-
return fn();
|
|
1014
|
-
}
|
|
1015
|
-
```
|
|
1016
|
-
|
|
1017
|
-
---
|
|
1018
|
-
|
|
1019
|
-
## Summary
|
|
1020
|
-
|
|
1021
|
-
### Key Takeaways
|
|
1022
|
-
|
|
1023
|
-
1. **Categorize Errors**: Transient vs Permanent
|
|
1024
|
-
2. **Retry Smart**: Exponential backoff for transient errors
|
|
1025
|
-
3. **Fail Gracefully**: Partial success > total failure
|
|
1026
|
-
4. **Log Everything**: Structured logging for debugging
|
|
1027
|
-
5. **Monitor Metrics**: Track error rates and patterns
|
|
1028
|
-
6. **Plan Recovery**: Dead letter queue, compensation, rollback
|
|
1029
|
-
|
|
1030
|
-
### Error Handling Hierarchy
|
|
1031
|
-
|
|
1032
|
-
```
|
|
1033
|
-
1. Prevent errors (validation, type checking)
|
|
1034
|
-
↓
|
|
1035
|
-
2. Retry transient errors (exponential backoff)
|
|
1036
|
-
↓
|
|
1037
|
-
3. Degrade gracefully (fallback values, partial success)
|
|
1038
|
-
↓
|
|
1039
|
-
4. Log and monitor (structured logging, metrics)
|
|
1040
|
-
↓
|
|
1041
|
-
5. Queue for manual review (DLQ, error reports)
|
|
1042
|
-
```
|
|
1043
|
-
|
|
1044
|
-
---
|
|
1045
|
-
|
|
1046
|
-
## Next Steps
|
|
1047
|
-
|
|
1048
|
-
You've completed all integration patterns modules! Now explore:
|
|
1049
|
-
|
|
1050
|
-
- [Quick Reference Guide](../integration-patterns-quick-reference.md) - Cheat sheet for all patterns
|
|
1051
|
-
- [Complete Examples](../examples/) - Production-ready implementations
|
|
1052
|
-
- [Use Cases](../../../01-TEMPLATES/) - Real-world scenarios
|
|
1053
|
-
|
|
1054
|
-
Or dive deeper:
|
|
1055
|
-
- [Auto-Pagination Guide](../../../02-CORE-GUIDES/auto-pagination/auto-pagination-readme.md)
|
|
1056
|
-
- [Resolvers Guide](../../../02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-readme.md)
|
|
1057
|
-
- [Universal Mapping Guide](../../../02-CORE-GUIDES/mapping/mapping-readme.md)
|
|
1058
|
-
|
|
1059
|
-
---
|
|
1060
|
-
|
|
1061
|
-
[← Back to Index](../integration-patterns-readme.md) | [Previous: Webhook Patterns →](./integration-patterns-04-webhook-patterns.md)
|
|
1
|
+
# Module 5: Error Handling and Resilience
|
|
2
|
+
|
|
3
|
+
> **Learning Objective:** Implement robust error handling, retry strategies, and resilience patterns for production-grade integrations.
|
|
4
|
+
>
|
|
5
|
+
> **Level:** Intermediate
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
1. [Why Error Handling Matters](#why-error-handling-matters)
|
|
10
|
+
2. [Error Categories](#error-categories)
|
|
11
|
+
3. [SDK Error Handling](#sdk-error-handling)
|
|
12
|
+
4. [Retry Strategies](#retry-strategies)
|
|
13
|
+
5. [Circuit Breaker Pattern](#circuit-breaker-pattern)
|
|
14
|
+
6. [Graceful Degradation](#graceful-degradation)
|
|
15
|
+
7. [Logging and Monitoring](#logging-and-monitoring)
|
|
16
|
+
8. [Error Recovery Patterns](#error-recovery-patterns)
|
|
17
|
+
9. [Production Checklist](#production-checklist)
|
|
18
|
+
10. [Complete Examples](#complete-examples)
|
|
19
|
+
11. [Summary](#summary)
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Why Error Handling Matters
|
|
24
|
+
|
|
25
|
+
### Production Realities
|
|
26
|
+
|
|
27
|
+
| Failure Type | Frequency | Impact | Recovery Time |
|
|
28
|
+
|--------------|-----------|--------|---------------|
|
|
29
|
+
| **Network Timeouts** | 1-5% of requests | Temporary | Seconds (retry) |
|
|
30
|
+
| **API Rate Limits** | Variable | Temporary | Minutes (backoff) |
|
|
31
|
+
| **Validation Errors** | 0.1-1% | Permanent | Manual fix |
|
|
32
|
+
| **Service Outages** | Rare | Temporary | Minutes to hours |
|
|
33
|
+
| **Bad Data** | 0.5-2% | Permanent | Manual fix |
|
|
34
|
+
|
|
35
|
+
### Cost of Poor Error Handling
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
Scenario: 10,000 daily inventory updates, 1% error rate
|
|
39
|
+
|
|
40
|
+
WITHOUT proper error handling:
|
|
41
|
+
- 100 failed updates/day
|
|
42
|
+
- No retry → permanent data loss
|
|
43
|
+
- Manual investigation: 2 hours/day
|
|
44
|
+
- Customer impact: Out-of-stock items shown available
|
|
45
|
+
|
|
46
|
+
WITH proper error handling:
|
|
47
|
+
- 100 initial failures
|
|
48
|
+
- 95 recovered via retry (5 seconds)
|
|
49
|
+
- 5 logged for manual review (15 minutes)
|
|
50
|
+
- Customer impact: Minimal
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Error Handling Goals
|
|
54
|
+
|
|
55
|
+
1. **Resilience**: Recover automatically from transient failures
|
|
56
|
+
2. **Transparency**: Log errors with context for debugging
|
|
57
|
+
3. **User Experience**: Provide meaningful error messages
|
|
58
|
+
4. **Data Integrity**: Never lose data silently
|
|
59
|
+
5. **Observability**: Monitor error rates and patterns
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Error Categories
|
|
64
|
+
|
|
65
|
+
### 1. Transient Errors (Retryable)
|
|
66
|
+
|
|
67
|
+
**Cause**: Temporary conditions that may resolve
|
|
68
|
+
|
|
69
|
+
| Error Type | HTTP Status | Retry? | Backoff | Example |
|
|
70
|
+
|------------|-------------|--------|---------|---------|
|
|
71
|
+
| **Network Timeout** | 504 | Yes | Exponential | Request timeout |
|
|
72
|
+
| **Rate Limit** | 429 | Yes | Linear | Too many requests |
|
|
73
|
+
| **Service Unavailable** | 503 | Yes | Exponential | API maintenance |
|
|
74
|
+
| **Server Error** | 500 | Yes | Exponential | Internal error |
|
|
75
|
+
|
|
76
|
+
### 2. Permanent Errors (Non-Retryable)
|
|
77
|
+
|
|
78
|
+
**Cause**: Data or configuration issues requiring manual intervention
|
|
79
|
+
|
|
80
|
+
| Error Type | HTTP Status | Retry? | Action | Example |
|
|
81
|
+
|------------|-------------|--------|--------|---------|
|
|
82
|
+
| **Validation Error** | 400 | No | Fix data | Missing required field |
|
|
83
|
+
| **Authentication** | 401 | No | Fix credentials | Invalid token |
|
|
84
|
+
| **Authorization** | 403 | No | Fix permissions | Insufficient access |
|
|
85
|
+
| **Not Found** | 404 | No | Check config | Invalid endpoint |
|
|
86
|
+
| **Unprocessable** | 422 | No | Fix data | Invalid field value |
|
|
87
|
+
|
|
88
|
+
### 3. Partial Errors (Record-Level)
|
|
89
|
+
|
|
90
|
+
**Cause**: Some records fail while others succeed
|
|
91
|
+
|
|
92
|
+
| Scenario | Impact | Action | Example |
|
|
93
|
+
|----------|--------|--------|---------|
|
|
94
|
+
| **Batch API Errors** | Some records fail | Log failed records | 98/100 succeed |
|
|
95
|
+
| **Mapping Errors** | Some records skip | Log validation errors | Missing SKU |
|
|
96
|
+
| **GraphQL Errors** | Some mutations fail | Log errors, continue | Duplicate ref |
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## SDK Error Handling
|
|
101
|
+
|
|
102
|
+
### FluentClient Error Handling
|
|
103
|
+
|
|
104
|
+
The SDK automatically handles some errors:
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import { createClient } from '@fluentcommerce/fc-connect-sdk';
|
|
108
|
+
|
|
109
|
+
const client = await createClient({ /* config */ });
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
const result = await client.graphql({ query, variables });
|
|
113
|
+
|
|
114
|
+
// Check for GraphQL errors
|
|
115
|
+
if (result.errors && result.errors.length > 0) {
|
|
116
|
+
// Classify errors to determine retry strategy
|
|
117
|
+
const { classifyErrors } = await import('@fluentcommerce/fc-connect-sdk');
|
|
118
|
+
const classification = classifyErrors(result.errors);
|
|
119
|
+
|
|
120
|
+
console.error('GraphQL errors:', {
|
|
121
|
+
errorCode: classification.errorCode,
|
|
122
|
+
retryable: classification.retryable,
|
|
123
|
+
action: classification.action,
|
|
124
|
+
message: classification.userMessage,
|
|
125
|
+
errors: result.errors,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// Don't retry non-retryable errors
|
|
129
|
+
if (!classification.retryable) {
|
|
130
|
+
throw new Error(classification.userMessage);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// result.data may still be partially populated for retryable errors
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
} catch (error: any) {
|
|
137
|
+
// Network or authentication errors
|
|
138
|
+
console.error('Request failed:', error.message);
|
|
139
|
+
|
|
140
|
+
// Error types:
|
|
141
|
+
if (error.code === 'ECONNREFUSED') {
|
|
142
|
+
// Network error
|
|
143
|
+
} else if (error.statusCode === 401) {
|
|
144
|
+
// Authentication error
|
|
145
|
+
} else if (error.statusCode === 429) {
|
|
146
|
+
// Rate limit
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Batch API Error Handling
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
// Create job
|
|
155
|
+
const job = await client.createJob({ name: 'Inventory Sync', retailerId: '2' });
|
|
156
|
+
|
|
157
|
+
// Send batches
|
|
158
|
+
for (const batch of batches) {
|
|
159
|
+
try {
|
|
160
|
+
await client.sendBatch(job.id, { entities: batch });
|
|
161
|
+
} catch (error: any) {
|
|
162
|
+
console.error('Batch send failed:', error.message);
|
|
163
|
+
// Log failed batch for retry
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Check job status
|
|
168
|
+
const status = await client.getJobStatus(job.id);
|
|
169
|
+
|
|
170
|
+
if (status.errorSummary && status.errorSummary.totalErrors > 0) {
|
|
171
|
+
console.warn(`Job completed with ${status.errorSummary.totalErrors} errors`);
|
|
172
|
+
|
|
173
|
+
// Get error details
|
|
174
|
+
const errors = await client.graphql({
|
|
175
|
+
query: `
|
|
176
|
+
query GetJobErrors($jobId: ID!) {
|
|
177
|
+
job(id: $jobId) {
|
|
178
|
+
batches(first: 100) {
|
|
179
|
+
edges {
|
|
180
|
+
node {
|
|
181
|
+
errors {
|
|
182
|
+
recordRef
|
|
183
|
+
errorType
|
|
184
|
+
errorMessage
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
`,
|
|
192
|
+
variables: { jobId: job.id }
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// Log errors for manual review
|
|
196
|
+
console.error('Batch errors:', JSON.stringify(errors, null, 2));
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### UniversalMapper Error Handling
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
import { UniversalMapper } from '@fluentcommerce/fc-connect-sdk';
|
|
204
|
+
|
|
205
|
+
const mapper = new UniversalMapper(mappingConfig);
|
|
206
|
+
|
|
207
|
+
const successfulRecords = [];
|
|
208
|
+
const failedRecords = [];
|
|
209
|
+
|
|
210
|
+
for (const record of records) {
|
|
211
|
+
const result = await mapper.map(record);
|
|
212
|
+
|
|
213
|
+
if (result.success) {
|
|
214
|
+
successfulRecords.push(result.data);
|
|
215
|
+
} else {
|
|
216
|
+
failedRecords.push({
|
|
217
|
+
record,
|
|
218
|
+
errors: result.errors
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
console.log(`Mapped ${successfulRecords.length} records, ${failedRecords.length} failures`);
|
|
224
|
+
|
|
225
|
+
// Write failed records to error file
|
|
226
|
+
if (failedRecords.length > 0) {
|
|
227
|
+
await fs.writeFile(
|
|
228
|
+
'mapping-errors.json',
|
|
229
|
+
JSON.stringify(failedRecords, null, 2)
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Retry Strategies
|
|
237
|
+
|
|
238
|
+
### Pattern 1: Exponential Backoff
|
|
239
|
+
|
|
240
|
+
**Best for**: Network errors, rate limits, server errors
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
/**
|
|
244
|
+
* Exponential Backoff Retry
|
|
245
|
+
*
|
|
246
|
+
* Delay = initialDelay * (2 ^ attempt)
|
|
247
|
+
* Example: 1s, 2s, 4s, 8s, 16s
|
|
248
|
+
*/
|
|
249
|
+
|
|
250
|
+
async function exponentialBackoff<T>(
|
|
251
|
+
fn: () => Promise<T>,
|
|
252
|
+
options = {
|
|
253
|
+
maxRetries: 5,
|
|
254
|
+
initialDelay: 1000,
|
|
255
|
+
maxDelay: 30000
|
|
256
|
+
}
|
|
257
|
+
): Promise<T> {
|
|
258
|
+
let lastError: Error;
|
|
259
|
+
|
|
260
|
+
for (let attempt = 0; attempt <= options.maxRetries; attempt++) {
|
|
261
|
+
try {
|
|
262
|
+
return await fn();
|
|
263
|
+
|
|
264
|
+
} catch (error: any) {
|
|
265
|
+
lastError = error;
|
|
266
|
+
|
|
267
|
+
// Don't retry permanent errors
|
|
268
|
+
if (error.statusCode && error.statusCode < 500) {
|
|
269
|
+
throw error;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (attempt < options.maxRetries) {
|
|
273
|
+
// Calculate delay
|
|
274
|
+
const delay = Math.min(
|
|
275
|
+
options.initialDelay * Math.pow(2, attempt),
|
|
276
|
+
options.maxDelay
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
console.log(`Retry ${attempt + 1}/${options.maxRetries} after ${delay}ms`);
|
|
280
|
+
|
|
281
|
+
await sleep(delay);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
throw lastError!;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function sleep(ms: number): Promise<void> {
|
|
290
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Usage
|
|
294
|
+
const result = await exponentialBackoff(() =>
|
|
295
|
+
client.graphql({ query, variables })
|
|
296
|
+
);
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Pattern 2: Linear Backoff (Rate Limits)
|
|
300
|
+
|
|
301
|
+
**Best for**: Rate limit errors (429)
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
/**
|
|
305
|
+
* Linear Backoff for Rate Limits
|
|
306
|
+
*
|
|
307
|
+
* Delay = retryAfter (from response header)
|
|
308
|
+
*/
|
|
309
|
+
|
|
310
|
+
async function linearBackoff<T>(
|
|
311
|
+
fn: () => Promise<T>,
|
|
312
|
+
maxRetries = 3
|
|
313
|
+
): Promise<T> {
|
|
314
|
+
let lastError: Error;
|
|
315
|
+
|
|
316
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
317
|
+
try {
|
|
318
|
+
return await fn();
|
|
319
|
+
|
|
320
|
+
} catch (error: any) {
|
|
321
|
+
lastError = error;
|
|
322
|
+
|
|
323
|
+
if (error.statusCode === 429) {
|
|
324
|
+
// Check for Retry-After header
|
|
325
|
+
const retryAfter = error.headers?.['retry-after'];
|
|
326
|
+
const delay = retryAfter ? parseInt(retryAfter) * 1000 : 60000;
|
|
327
|
+
|
|
328
|
+
console.log(`Rate limited - retry after ${delay}ms`);
|
|
329
|
+
|
|
330
|
+
await sleep(delay);
|
|
331
|
+
} else {
|
|
332
|
+
throw error; // Not a rate limit error
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
throw lastError!;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Usage
|
|
341
|
+
const result = await linearBackoff(() =>
|
|
342
|
+
client.graphql({ query, variables })
|
|
343
|
+
);
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Pattern 3: Jittered Backoff
|
|
347
|
+
|
|
348
|
+
**Best for**: High concurrency to avoid thundering herd
|
|
349
|
+
|
|
350
|
+
```typescript
|
|
351
|
+
/**
|
|
352
|
+
* Jittered Exponential Backoff
|
|
353
|
+
*
|
|
354
|
+
* Adds randomness to avoid synchronized retries
|
|
355
|
+
*/
|
|
356
|
+
|
|
357
|
+
function jitteredBackoff(attempt: number, baseDelay = 1000, maxDelay = 30000): number {
|
|
358
|
+
const exponentialDelay = baseDelay * Math.pow(2, attempt);
|
|
359
|
+
const jitter = Math.random() * exponentialDelay * 0.3; // ±30% jitter
|
|
360
|
+
return Math.min(exponentialDelay + jitter, maxDelay);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async function retryWithJitter<T>(
|
|
364
|
+
fn: () => Promise<T>,
|
|
365
|
+
maxRetries = 5
|
|
366
|
+
): Promise<T> {
|
|
367
|
+
let lastError: Error;
|
|
368
|
+
|
|
369
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
370
|
+
try {
|
|
371
|
+
return await fn();
|
|
372
|
+
|
|
373
|
+
} catch (error: any) {
|
|
374
|
+
lastError = error;
|
|
375
|
+
|
|
376
|
+
if (error.statusCode && error.statusCode < 500) {
|
|
377
|
+
throw error; // Don't retry permanent errors
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (attempt < maxRetries) {
|
|
381
|
+
const delay = jitteredBackoff(attempt);
|
|
382
|
+
console.log(`Retry ${attempt + 1} after ${delay.toFixed(0)}ms`);
|
|
383
|
+
await sleep(delay);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
throw lastError!;
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
## Circuit Breaker Pattern
|
|
395
|
+
|
|
396
|
+
### Concept
|
|
397
|
+
|
|
398
|
+
**Prevent cascading failures** by stopping requests to failing services.
|
|
399
|
+
|
|
400
|
+
```
|
|
401
|
+
States:
|
|
402
|
+
┌─────────┐
|
|
403
|
+
│ CLOSED │ Normal operation, allow all requests
|
|
404
|
+
└────┬────┘
|
|
405
|
+
│ Failure threshold reached (e.g., 5 failures)
|
|
406
|
+
▼
|
|
407
|
+
┌─────────┐
|
|
408
|
+
│ OPEN │ Block all requests, return error immediately
|
|
409
|
+
└────┬────┘
|
|
410
|
+
│ Timeout elapsed (e.g., 60 seconds)
|
|
411
|
+
▼
|
|
412
|
+
┌─────────┐
|
|
413
|
+
│HALF-OPEN│ Allow limited requests to test recovery
|
|
414
|
+
└────┬────┘
|
|
415
|
+
│ Success → CLOSED
|
|
416
|
+
│ Failure → OPEN
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### Implementation
|
|
420
|
+
|
|
421
|
+
```typescript
|
|
422
|
+
/**
|
|
423
|
+
* Circuit Breaker
|
|
424
|
+
*
|
|
425
|
+
* Prevents cascading failures by stopping requests to failing services
|
|
426
|
+
*/
|
|
427
|
+
|
|
428
|
+
class CircuitBreaker {
|
|
429
|
+
private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';
|
|
430
|
+
private failures = 0;
|
|
431
|
+
private lastFailureTime = 0;
|
|
432
|
+
|
|
433
|
+
constructor(
|
|
434
|
+
private options = {
|
|
435
|
+
failureThreshold: 5,
|
|
436
|
+
resetTimeout: 60000,
|
|
437
|
+
halfOpenRequests: 3
|
|
438
|
+
}
|
|
439
|
+
) {}
|
|
440
|
+
|
|
441
|
+
async execute<T>(fn: () => Promise<T>): Promise<T> {
|
|
442
|
+
// Check state
|
|
443
|
+
if (this.state === 'OPEN') {
|
|
444
|
+
// Check if timeout elapsed
|
|
445
|
+
if (Date.now() - this.lastFailureTime > this.options.resetTimeout) {
|
|
446
|
+
console.log('Circuit HALF-OPEN');
|
|
447
|
+
this.state = 'HALF_OPEN';
|
|
448
|
+
} else {
|
|
449
|
+
throw new Error('Circuit breaker is OPEN - blocking requests');
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
try {
|
|
454
|
+
const result = await fn();
|
|
455
|
+
|
|
456
|
+
// Success - reset failures
|
|
457
|
+
if (this.state === 'HALF_OPEN') {
|
|
458
|
+
console.log('Circuit CLOSED');
|
|
459
|
+
this.state = 'CLOSED';
|
|
460
|
+
}
|
|
461
|
+
this.failures = 0;
|
|
462
|
+
|
|
463
|
+
return result;
|
|
464
|
+
|
|
465
|
+
} catch (error) {
|
|
466
|
+
this.failures++;
|
|
467
|
+
this.lastFailureTime = Date.now();
|
|
468
|
+
|
|
469
|
+
if (this.failures >= this.options.failureThreshold) {
|
|
470
|
+
console.log('Circuit OPEN - failure threshold reached');
|
|
471
|
+
this.state = 'OPEN';
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
throw error;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Usage
|
|
480
|
+
const breaker = new CircuitBreaker({
|
|
481
|
+
failureThreshold: 5,
|
|
482
|
+
resetTimeout: 60000
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
try {
|
|
486
|
+
const result = await breaker.execute(() =>
|
|
487
|
+
client.graphql({ query, variables })
|
|
488
|
+
);
|
|
489
|
+
} catch (error: any) {
|
|
490
|
+
if (error.message.includes('Circuit breaker is OPEN')) {
|
|
491
|
+
console.error('Service is down - circuit breaker active');
|
|
492
|
+
// Use fallback or queue for later
|
|
493
|
+
} else {
|
|
494
|
+
throw error;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
---
|
|
500
|
+
|
|
501
|
+
## Graceful Degradation
|
|
502
|
+
|
|
503
|
+
### Pattern 1: Fallback Values
|
|
504
|
+
|
|
505
|
+
**Provide defaults** when non-critical data is unavailable.
|
|
506
|
+
|
|
507
|
+
```typescript
|
|
508
|
+
async function getProductDetails(sku: string): Promise<any> {
|
|
509
|
+
try {
|
|
510
|
+
const product = await client.graphql({
|
|
511
|
+
query: `query GetProduct($sku: String!) {
|
|
512
|
+
products(ref: $sku, first: 1) {
|
|
513
|
+
edges {
|
|
514
|
+
node {
|
|
515
|
+
id
|
|
516
|
+
ref
|
|
517
|
+
name
|
|
518
|
+
price
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}`,
|
|
523
|
+
variables: { sku }
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
return product?.products?.edges?.[0]?.node;
|
|
527
|
+
|
|
528
|
+
} catch (error: any) {
|
|
529
|
+
console.warn(`Failed to fetch product ${sku}, using defaults`, error);
|
|
530
|
+
|
|
531
|
+
// Return fallback
|
|
532
|
+
return {
|
|
533
|
+
id: null,
|
|
534
|
+
ref: sku,
|
|
535
|
+
name: 'Product details unavailable',
|
|
536
|
+
price: 0
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
### Pattern 2: Partial Failure Handling
|
|
543
|
+
|
|
544
|
+
**Process successful records** even if some fail.
|
|
545
|
+
|
|
546
|
+
```typescript
|
|
547
|
+
async function processOrders(orders: any[]): Promise<{
|
|
548
|
+
successful: any[];
|
|
549
|
+
failed: any[];
|
|
550
|
+
}> {
|
|
551
|
+
const successful = [];
|
|
552
|
+
const failed = [];
|
|
553
|
+
|
|
554
|
+
for (const order of orders) {
|
|
555
|
+
try {
|
|
556
|
+
const result = await client.graphql({
|
|
557
|
+
query: CREATE_ORDER_MUTATION,
|
|
558
|
+
variables: { input: order }
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
successful.push({ order, result });
|
|
562
|
+
|
|
563
|
+
} catch (error: any) {
|
|
564
|
+
console.error(`Failed to process order ${order.ref}`, error);
|
|
565
|
+
|
|
566
|
+
failed.push({ order, error: error.message });
|
|
567
|
+
|
|
568
|
+
// Continue with next order (don't throw)
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
console.log(`Processed ${successful.length} orders, ${failed.length} failures`);
|
|
573
|
+
|
|
574
|
+
// Write failed orders for retry
|
|
575
|
+
if (failed.length > 0) {
|
|
576
|
+
await fs.writeFile('failed-orders.json', JSON.stringify(failed, null, 2));
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
return { successful, failed };
|
|
580
|
+
}
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
### Pattern 3: Queue for Retry
|
|
584
|
+
|
|
585
|
+
**Queue failed operations** for later retry.
|
|
586
|
+
|
|
587
|
+
```typescript
|
|
588
|
+
import { StateService, VersoriKVAdapter } from '@fluentcommerce/fc-connect-sdk';
|
|
589
|
+
|
|
590
|
+
async function processWithQueue(records: any[]) {
|
|
591
|
+
const kvAdapter = new VersoriKVAdapter(openKv());
|
|
592
|
+
const stateService = new StateService(logger);
|
|
593
|
+
|
|
594
|
+
for (const record of records) {
|
|
595
|
+
try {
|
|
596
|
+
await processRecord(record);
|
|
597
|
+
|
|
598
|
+
} catch (error: any) {
|
|
599
|
+
console.error(`Failed to process record ${record.id}`, error);
|
|
600
|
+
|
|
601
|
+
// Add to retry queue
|
|
602
|
+
const queue = await stateService.getState('retry-queue') || [];
|
|
603
|
+
queue.push({
|
|
604
|
+
record,
|
|
605
|
+
error: error.message,
|
|
606
|
+
timestamp: Date.now(),
|
|
607
|
+
retryCount: 0
|
|
608
|
+
});
|
|
609
|
+
await stateService.setState('retry-queue', queue);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// Separate process to retry queued items
|
|
615
|
+
async function retryQueue() {
|
|
616
|
+
const kvAdapter = new VersoriKVAdapter(openKv());
|
|
617
|
+
const stateService = new StateService(logger);
|
|
618
|
+
|
|
619
|
+
const queue = await stateService.getState('retry-queue') || [];
|
|
620
|
+
const remainingQueue = [];
|
|
621
|
+
|
|
622
|
+
for (const item of queue) {
|
|
623
|
+
try {
|
|
624
|
+
await processRecord(item.record);
|
|
625
|
+
console.log(`✓ Retry succeeded for ${item.record.id}`);
|
|
626
|
+
|
|
627
|
+
} catch (error: any) {
|
|
628
|
+
item.retryCount++;
|
|
629
|
+
|
|
630
|
+
if (item.retryCount < 3) {
|
|
631
|
+
// Re-queue
|
|
632
|
+
remainingQueue.push(item);
|
|
633
|
+
} else {
|
|
634
|
+
// Max retries reached - log as permanent failure
|
|
635
|
+
console.error(`✗ Max retries reached for ${item.record.id}`);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
await stateService.setState('retry-queue', remainingQueue);
|
|
641
|
+
}
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
---
|
|
645
|
+
|
|
646
|
+
## Logging and Monitoring
|
|
647
|
+
|
|
648
|
+
### Structured Logging
|
|
649
|
+
|
|
650
|
+
**Use SDK logging utilities** for consistent format and easy parsing.
|
|
651
|
+
|
|
652
|
+
```typescript
|
|
653
|
+
import {
|
|
654
|
+
createConsoleLogger,
|
|
655
|
+
toStructuredLogger,
|
|
656
|
+
generateCorrelationId
|
|
657
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
658
|
+
|
|
659
|
+
// Basic console logger
|
|
660
|
+
const logger = createConsoleLogger();
|
|
661
|
+
|
|
662
|
+
// Or with structured context
|
|
663
|
+
const contextLogger = toStructuredLogger(logger, {
|
|
664
|
+
service: 'InventorySync',
|
|
665
|
+
correlationId: generateCorrelationId(),
|
|
666
|
+
environment: 'production'
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
// Usage
|
|
670
|
+
contextLogger.info('Processing inventory file', {
|
|
671
|
+
fileName: 'inventory.csv',
|
|
672
|
+
recordCount: 5000
|
|
673
|
+
});
|
|
674
|
+
// Output: [INFO] Processing inventory file
|
|
675
|
+
// {"service":"InventorySync","correlationId":"...","fileName":"inventory.csv","recordCount":5000}
|
|
676
|
+
|
|
677
|
+
contextLogger.error('Batch API error', error, {
|
|
678
|
+
jobId: job.id,
|
|
679
|
+
batchId: batch.id
|
|
680
|
+
});
|
|
681
|
+
// Output: [ERROR] Batch API error
|
|
682
|
+
// {"service":"InventorySync","error":"...","stack":"...","jobId":"...","batchId":"..."}
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
**For Versori Platform:**
|
|
686
|
+
|
|
687
|
+
```typescript
|
|
688
|
+
// ✅ CORRECT - Use native log from context
|
|
689
|
+
import { schedule } from '@versori/run';
|
|
690
|
+
|
|
691
|
+
export const myWorkflow = schedule('workflow', '0 * * * *')
|
|
692
|
+
.execute(async ({ log }) => {
|
|
693
|
+
// Use native log directly - don't wrap it!
|
|
694
|
+
log.info('Processing started');
|
|
695
|
+
log.error('Processing failed', error);
|
|
696
|
+
});
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
### Error Metrics
|
|
700
|
+
|
|
701
|
+
**Track error rates** for monitoring.
|
|
702
|
+
|
|
703
|
+
```typescript
|
|
704
|
+
class ErrorMetrics {
|
|
705
|
+
private errors: Map<string, number> = new Map();
|
|
706
|
+
private total = 0;
|
|
707
|
+
|
|
708
|
+
recordError(type: string) {
|
|
709
|
+
this.errors.set(type, (this.errors.get(type) || 0) + 1);
|
|
710
|
+
this.total++;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
getMetrics() {
|
|
714
|
+
const metrics: any = {
|
|
715
|
+
totalErrors: this.total,
|
|
716
|
+
errorsByType: {}
|
|
717
|
+
};
|
|
718
|
+
|
|
719
|
+
for (const [type, count] of this.errors.entries()) {
|
|
720
|
+
metrics.errorsByType[type] = {
|
|
721
|
+
count,
|
|
722
|
+
percentage: ((count / this.total) * 100).toFixed(2) + '%'
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
return metrics;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
reset() {
|
|
730
|
+
this.errors.clear();
|
|
731
|
+
this.total = 0;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
// Usage
|
|
736
|
+
const metrics = new ErrorMetrics();
|
|
737
|
+
|
|
738
|
+
try {
|
|
739
|
+
await processRecord(record);
|
|
740
|
+
} catch (error: any) {
|
|
741
|
+
if (error.statusCode === 429) {
|
|
742
|
+
metrics.recordError('RATE_LIMIT');
|
|
743
|
+
} else if (error.statusCode >= 500) {
|
|
744
|
+
metrics.recordError('SERVER_ERROR');
|
|
745
|
+
} else {
|
|
746
|
+
metrics.recordError('VALIDATION_ERROR');
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// Log metrics periodically
|
|
751
|
+
console.log('Error metrics:', metrics.getMetrics());
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
---
|
|
755
|
+
|
|
756
|
+
## Error Recovery Patterns
|
|
757
|
+
|
|
758
|
+
### Pattern 1: Dead Letter Queue
|
|
759
|
+
|
|
760
|
+
**Store failed records** for manual review.
|
|
761
|
+
|
|
762
|
+
```typescript
|
|
763
|
+
async function processWithDLQ(records: any[]) {
|
|
764
|
+
const successCount = 0;
|
|
765
|
+
const dlq: any[] = [];
|
|
766
|
+
|
|
767
|
+
for (const record of records) {
|
|
768
|
+
try {
|
|
769
|
+
await processRecord(record);
|
|
770
|
+
successCount++;
|
|
771
|
+
|
|
772
|
+
} catch (error: any) {
|
|
773
|
+
// Add to dead letter queue
|
|
774
|
+
dlq.push({
|
|
775
|
+
record,
|
|
776
|
+
error: error.message,
|
|
777
|
+
stack: error.stack,
|
|
778
|
+
timestamp: new Date().toISOString(),
|
|
779
|
+
context: {
|
|
780
|
+
fileName: 'inventory.csv',
|
|
781
|
+
lineNumber: record._lineNumber
|
|
782
|
+
}
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// Write DLQ to file
|
|
788
|
+
if (dlq.length > 0) {
|
|
789
|
+
await fs.writeFile(
|
|
790
|
+
`dlq-${Date.now()}.json`,
|
|
791
|
+
JSON.stringify(dlq, null, 2)
|
|
792
|
+
);
|
|
793
|
+
|
|
794
|
+
console.log(`Processed ${successCount} records, ${dlq.length} in DLQ`);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
### Pattern 2: Compensating Transaction
|
|
800
|
+
|
|
801
|
+
**Undo partial changes** on failure.
|
|
802
|
+
|
|
803
|
+
```typescript
|
|
804
|
+
async function createOrderWithRollback(orderData: any) {
|
|
805
|
+
let customerId: string | null = null;
|
|
806
|
+
|
|
807
|
+
try {
|
|
808
|
+
// Step 1: Create customer
|
|
809
|
+
const customer = await client.graphql({
|
|
810
|
+
query: CREATE_CUSTOMER_MUTATION,
|
|
811
|
+
variables: { input: orderData.customer }
|
|
812
|
+
});
|
|
813
|
+
customerId = customer.id;
|
|
814
|
+
|
|
815
|
+
// Step 2: Create order
|
|
816
|
+
const order = await client.graphql({
|
|
817
|
+
query: CREATE_ORDER_MUTATION,
|
|
818
|
+
variables: {
|
|
819
|
+
input: {
|
|
820
|
+
...orderData,
|
|
821
|
+
customer: { id: customerId }
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
return order;
|
|
827
|
+
|
|
828
|
+
} catch (error: any) {
|
|
829
|
+
console.error('Order creation failed', error);
|
|
830
|
+
|
|
831
|
+
// Rollback: Delete customer if created
|
|
832
|
+
if (customerId) {
|
|
833
|
+
try {
|
|
834
|
+
await client.graphql({
|
|
835
|
+
query: DELETE_CUSTOMER_MUTATION,
|
|
836
|
+
variables: { id: customerId }
|
|
837
|
+
});
|
|
838
|
+
console.log(`Rolled back customer ${customerId}`);
|
|
839
|
+
} catch (rollbackError) {
|
|
840
|
+
console.error('Rollback failed', rollbackError);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
throw error;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
---
|
|
850
|
+
|
|
851
|
+
## Production Checklist
|
|
852
|
+
|
|
853
|
+
### Before Deployment
|
|
854
|
+
|
|
855
|
+
- [ ] **Error handling** for all API calls
|
|
856
|
+
- [ ] **Retry logic** for transient errors
|
|
857
|
+
- [ ] **Validation** for all input data
|
|
858
|
+
- [ ] **Logging** with structured format
|
|
859
|
+
- [ ] **Error metrics** and monitoring
|
|
860
|
+
- [ ] **Circuit breaker** for external dependencies
|
|
861
|
+
- [ ] **Graceful degradation** for non-critical failures
|
|
862
|
+
- [ ] **Dead letter queue** for failed records
|
|
863
|
+
- [ ] **Idempotency** for duplicate prevention
|
|
864
|
+
- [ ] **Timeout configuration** for all operations
|
|
865
|
+
- [ ] **Rate limit handling** with backoff
|
|
866
|
+
- [ ] **Security** (signature verification, IP allowlist)
|
|
867
|
+
- [ ] **Health check** endpoint
|
|
868
|
+
- [ ] **Error documentation** for ops team
|
|
869
|
+
|
|
870
|
+
### Monitoring
|
|
871
|
+
|
|
872
|
+
- [ ] **Error rate** (< 1% target)
|
|
873
|
+
- [ ] **Retry rate** (< 5% target)
|
|
874
|
+
- [ ] **Response time** (p50, p95, p99)
|
|
875
|
+
- [ ] **Success rate** (> 99% target)
|
|
876
|
+
- [ ] **DLQ size** (alert if > 100)
|
|
877
|
+
- [ ] **Circuit breaker** state changes
|
|
878
|
+
- [ ] **Queue depth** (for async processing)
|
|
879
|
+
|
|
880
|
+
---
|
|
881
|
+
|
|
882
|
+
## Complete Examples
|
|
883
|
+
|
|
884
|
+
### Production-Ready Webhook with Full Error Handling
|
|
885
|
+
|
|
886
|
+
```typescript
|
|
887
|
+
/**
|
|
888
|
+
* Production-ready webhook with comprehensive error handling
|
|
889
|
+
*/
|
|
890
|
+
|
|
891
|
+
import { createClient, GraphQLMutationMapper, StateService, VersoriKVAdapter } from '@fluentcommerce/fc-connect-sdk';
|
|
892
|
+
|
|
893
|
+
export default async function productionWebhook(activation: any, log: any, connections: any) {
|
|
894
|
+
const startTime = Date.now();
|
|
895
|
+
const webhookId = activation.headers['x-webhook-id'] || crypto.randomUUID();
|
|
896
|
+
|
|
897
|
+
try {
|
|
898
|
+
log.info('Webhook received', {
|
|
899
|
+
webhookId,
|
|
900
|
+
source: activation.headers['user-agent']
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
// Idempotency check
|
|
904
|
+
const kvAdapter = new VersoriKVAdapter(openKv());
|
|
905
|
+
const stateService = new StateService(logger);
|
|
906
|
+
|
|
907
|
+
const processed = await stateService.getState(`webhook:${webhookId}`);
|
|
908
|
+
if (processed) {
|
|
909
|
+
log.info('Webhook already processed', { webhookId });
|
|
910
|
+
return {
|
|
911
|
+
status: 200,
|
|
912
|
+
body: {
|
|
913
|
+
success: true,
|
|
914
|
+
message: 'Already processed',
|
|
915
|
+
orderId: processed.orderId
|
|
916
|
+
}
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// Validate payload
|
|
921
|
+
if (!activation.body || !activation.body.orderRef) {
|
|
922
|
+
throw new Error('Missing required field: orderRef');
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
// Create client with retry
|
|
926
|
+
const client = await retryWithJitter(() =>
|
|
927
|
+
createClient({
|
|
928
|
+
connection: connections.fluent_commerce,
|
|
929
|
+
logger: log
|
|
930
|
+
})
|
|
931
|
+
);
|
|
932
|
+
|
|
933
|
+
// Process order
|
|
934
|
+
const result = await retryWithJitter(() =>
|
|
935
|
+
client.graphql({
|
|
936
|
+
query: CREATE_ORDER_MUTATION,
|
|
937
|
+
variables: { input: mapOrderData(activation.body) }
|
|
938
|
+
})
|
|
939
|
+
);
|
|
940
|
+
|
|
941
|
+
// Mark as processed
|
|
942
|
+
await stateService.setState(`webhook:${webhookId}`, {
|
|
943
|
+
orderId: result.id,
|
|
944
|
+
processedAt: new Date().toISOString()
|
|
945
|
+
});
|
|
946
|
+
|
|
947
|
+
const duration = Date.now() - startTime;
|
|
948
|
+
|
|
949
|
+
log.info('Webhook processed successfully', {
|
|
950
|
+
webhookId,
|
|
951
|
+
orderId: result.id,
|
|
952
|
+
duration
|
|
953
|
+
});
|
|
954
|
+
|
|
955
|
+
return {
|
|
956
|
+
status: 200,
|
|
957
|
+
body: {
|
|
958
|
+
success: true,
|
|
959
|
+
orderId: result.id,
|
|
960
|
+
duration
|
|
961
|
+
}
|
|
962
|
+
};
|
|
963
|
+
|
|
964
|
+
} catch (error: any) {
|
|
965
|
+
const duration = Date.now() - startTime;
|
|
966
|
+
|
|
967
|
+
log.error('Webhook processing failed', error, {
|
|
968
|
+
webhookId,
|
|
969
|
+
duration
|
|
970
|
+
});
|
|
971
|
+
|
|
972
|
+
// Categorize error
|
|
973
|
+
if (error.message.includes('required field')) {
|
|
974
|
+
return {
|
|
975
|
+
status: 400,
|
|
976
|
+
body: {
|
|
977
|
+
success: false,
|
|
978
|
+
error: 'Validation failed',
|
|
979
|
+
details: error.message
|
|
980
|
+
}
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
if (error.statusCode === 429) {
|
|
985
|
+
return {
|
|
986
|
+
status: 429,
|
|
987
|
+
body: {
|
|
988
|
+
success: false,
|
|
989
|
+
error: 'Rate limit exceeded',
|
|
990
|
+
retryAfter: 60
|
|
991
|
+
}
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
return {
|
|
996
|
+
status: 500,
|
|
997
|
+
body: {
|
|
998
|
+
success: false,
|
|
999
|
+
error: 'Internal server error',
|
|
1000
|
+
retryable: true
|
|
1001
|
+
}
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
function mapOrderData(data: any): any {
|
|
1007
|
+
// Mapping logic
|
|
1008
|
+
return data;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
async function retryWithJitter<T>(fn: () => Promise<T>): Promise<T> {
|
|
1012
|
+
// Retry implementation from earlier
|
|
1013
|
+
return fn();
|
|
1014
|
+
}
|
|
1015
|
+
```
|
|
1016
|
+
|
|
1017
|
+
---
|
|
1018
|
+
|
|
1019
|
+
## Summary
|
|
1020
|
+
|
|
1021
|
+
### Key Takeaways
|
|
1022
|
+
|
|
1023
|
+
1. **Categorize Errors**: Transient vs Permanent
|
|
1024
|
+
2. **Retry Smart**: Exponential backoff for transient errors
|
|
1025
|
+
3. **Fail Gracefully**: Partial success > total failure
|
|
1026
|
+
4. **Log Everything**: Structured logging for debugging
|
|
1027
|
+
5. **Monitor Metrics**: Track error rates and patterns
|
|
1028
|
+
6. **Plan Recovery**: Dead letter queue, compensation, rollback
|
|
1029
|
+
|
|
1030
|
+
### Error Handling Hierarchy
|
|
1031
|
+
|
|
1032
|
+
```
|
|
1033
|
+
1. Prevent errors (validation, type checking)
|
|
1034
|
+
↓
|
|
1035
|
+
2. Retry transient errors (exponential backoff)
|
|
1036
|
+
↓
|
|
1037
|
+
3. Degrade gracefully (fallback values, partial success)
|
|
1038
|
+
↓
|
|
1039
|
+
4. Log and monitor (structured logging, metrics)
|
|
1040
|
+
↓
|
|
1041
|
+
5. Queue for manual review (DLQ, error reports)
|
|
1042
|
+
```
|
|
1043
|
+
|
|
1044
|
+
---
|
|
1045
|
+
|
|
1046
|
+
## Next Steps
|
|
1047
|
+
|
|
1048
|
+
You've completed all integration patterns modules! Now explore:
|
|
1049
|
+
|
|
1050
|
+
- [Quick Reference Guide](../integration-patterns-quick-reference.md) - Cheat sheet for all patterns
|
|
1051
|
+
- [Complete Examples](../examples/) - Production-ready implementations
|
|
1052
|
+
- [Use Cases](../../../01-TEMPLATES/) - Real-world scenarios
|
|
1053
|
+
|
|
1054
|
+
Or dive deeper:
|
|
1055
|
+
- [Auto-Pagination Guide](../../../02-CORE-GUIDES/auto-pagination/auto-pagination-readme.md)
|
|
1056
|
+
- [Resolvers Guide](../../../02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-readme.md)
|
|
1057
|
+
- [Universal Mapping Guide](../../../02-CORE-GUIDES/mapping/mapping-readme.md)
|
|
1058
|
+
|
|
1059
|
+
---
|
|
1060
|
+
|
|
1061
|
+
[← Back to Index](../integration-patterns-readme.md) | [Previous: Webhook Patterns →](./integration-patterns-04-webhook-patterns.md)
|