@fluentcommerce/fc-connect-sdk 0.1.54 → 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 +12 -0
- package/dist/cjs/clients/fluent-client.js +13 -6
- package/dist/cjs/utils/pagination-helpers.js +38 -2
- package/dist/cjs/versori/fluent-versori-client.js +11 -5
- package/dist/esm/clients/fluent-client.js +13 -6
- package/dist/esm/utils/pagination-helpers.js +38 -2
- package/dist/esm/versori/fluent-versori-client.js +11 -5
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/tsconfig.types.tsbuildinfo +1 -1
- package/docs/00-START-HERE/EXPORT-VALIDATION.md +158 -158
- package/docs/00-START-HERE/cli-analyze-source-structure-guide.md +655 -655
- package/docs/00-START-HERE/cli-documentation-index.md +202 -202
- package/docs/00-START-HERE/cli-quick-reference.md +252 -252
- package/docs/00-START-HERE/decision-tree.md +552 -552
- package/docs/00-START-HERE/getting-started.md +1070 -1070
- package/docs/00-START-HERE/mapper-quick-decision-guide.md +235 -235
- package/docs/00-START-HERE/readme.md +237 -237
- package/docs/00-START-HERE/retailerid-configuration.md +404 -404
- package/docs/00-START-HERE/sdk-philosophy.md +794 -794
- package/docs/00-START-HERE/troubleshooting-quick-reference.md +1086 -1086
- package/docs/01-TEMPLATES/faq.md +686 -686
- package/docs/01-TEMPLATES/patterns/pattern-templates-guide.md +68 -68
- package/docs/01-TEMPLATES/patterns/patterns-csv-schema-validation-and-rejection-report.md +233 -233
- package/docs/01-TEMPLATES/patterns/patterns-custom-resolvers.md +407 -407
- package/docs/01-TEMPLATES/patterns/patterns-error-handling-retry.md +511 -511
- package/docs/01-TEMPLATES/patterns/patterns-field-mapping-universal.md +701 -701
- package/docs/01-TEMPLATES/patterns/patterns-large-file-splitting.md +1430 -1430
- package/docs/01-TEMPLATES/patterns/patterns-master-data-etl.md +2399 -2399
- package/docs/01-TEMPLATES/patterns/patterns-pagination-streaming.md +447 -447
- package/docs/01-TEMPLATES/patterns/patterns-state-duplicate-prevention.md +385 -385
- package/docs/01-TEMPLATES/readme.md +957 -957
- package/docs/01-TEMPLATES/standalone/standalone-asn-inbound-processing.md +1209 -1209
- package/docs/01-TEMPLATES/standalone/standalone-graphql-query-export.md +1140 -1140
- package/docs/01-TEMPLATES/standalone/standalone-graphql-to-parquet-partitioned-s3.md +432 -432
- package/docs/01-TEMPLATES/standalone/standalone-multi-channel-inventory-sync.md +1185 -1185
- package/docs/01-TEMPLATES/standalone/standalone-multi-source-aggregation.md +1462 -1462
- package/docs/01-TEMPLATES/standalone/standalone-s3-csv-batch-api.md +1390 -1390
- package/docs/01-TEMPLATES/standalone/standalone-s3-csv-inventory-to-batch.md +330 -330
- package/docs/01-TEMPLATES/standalone/standalone-scripts-guide.md +87 -87
- package/docs/01-TEMPLATES/standalone/standalone-sftp-xml-graphql.md +1444 -1444
- package/docs/01-TEMPLATES/standalone/standalone-webhook-payload-processing.md +688 -688
- package/docs/01-TEMPLATES/versori/business-examples/business-examples-dropship-order-routing.md +193 -193
- package/docs/01-TEMPLATES/versori/business-examples/business-examples-graphql-parquet-extraction.md +518 -518
- package/docs/01-TEMPLATES/versori/business-examples/business-examples-inter-location-transfers.md +2162 -2162
- package/docs/01-TEMPLATES/versori/business-examples/business-examples-pre-order-allocation.md +2226 -2226
- package/docs/01-TEMPLATES/versori/business-examples/business-scenarios-guide.md +87 -87
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-connection-validation-pattern.md +656 -656
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-dual-workflow-connector.md +835 -835
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-guide.md +108 -108
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-kv-state-management.md +1533 -1533
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-xml-response-patterns.md +1160 -1160
- package/docs/01-TEMPLATES/versori/versori-platform-guide.md +201 -201
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-asn-purchase-order.md +1906 -1906
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-dropship-routing.md +1074 -1074
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-flash-sale-reserve.md +1395 -1395
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-generic-xml-order.md +888 -888
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-payment-gateway-integration.md +2478 -2478
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-rma-returns-comprehensive.md +2240 -2240
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-xml-order-ingestion.md +2029 -2029
- package/docs/01-TEMPLATES/versori/webhooks/webhook-templates-guide.md +140 -140
- package/docs/01-TEMPLATES/versori/workflows/_examples/sample-data/inventory-mapping.json +20 -20
- package/docs/01-TEMPLATES/versori/workflows/_examples/sample-data/products_2025-01-22.csv +11 -11
- package/docs/01-TEMPLATES/versori/workflows/_examples/sample-data/sample-data-guide.md +34 -34
- package/docs/01-TEMPLATES/versori/workflows/_examples/workflow-examples-guide.md +36 -36
- package/docs/01-TEMPLATES/versori/workflows/extraction/extraction-modes-guide.md +1038 -1038
- package/docs/01-TEMPLATES/versori/workflows/extraction/extraction-workflows-guide.md +138 -138
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/graphql-extraction-guide.md +63 -63
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-fulfillments-to-sftp-csv.md +2062 -2062
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-fulfillments-to-sftp-xml.md +2294 -2294
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-positions-to-s3-csv.md +2461 -2461
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-positions-to-sftp-xml.md +2529 -2529
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-quantities-to-s3-csv.md +2464 -2464
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-quantities-to-s3-json.md +1959 -1959
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-orders-to-s3-csv.md +1953 -1953
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-orders-to-sftp-xml.md +2541 -2541
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-products-to-s3-json.md +2384 -2384
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-products-to-sftp-xml.md +2445 -2445
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-virtual-positions-to-s3-csv.md +2355 -2355
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-virtual-positions-to-s3-json.md +2042 -2042
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-virtual-positions-to-sftp-xml.md +2726 -2726
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/batch-api-guide.md +206 -206
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-cycle-count-reconciliation.md +2030 -2030
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-multi-channel-inventory-sync.md +1882 -1882
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-s3-csv-inventory-batch.md +2827 -2827
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-s3-json-inventory-batch.md +1952 -1952
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-s3-xml-inventory-batch.md +3289 -3289
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-sftp-csv-inventory-batch.md +3064 -3064
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-sftp-json-inventory-batch.md +3238 -3238
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-sftp-xml-inventory-batch.md +2977 -2977
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/event-api-guide.md +321 -321
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-payload-json-order-cancel-event.md +959 -959
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-payload-xml-order-cancel-event.md +1170 -1170
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-csv-product-event.md +2312 -2312
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-json-product-event.md +2999 -2999
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-parquet-product-event.md +2836 -2836
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-xml-product-event.md +2395 -2395
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-csv-product-event.md +2295 -2295
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-json-product-event.md +2602 -2602
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-parquet-product-event.md +2589 -2589
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-xml-product-event.md +3578 -3578
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/graphql-mutations-guide.md +93 -93
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-payload-json-order-update-graphql.md +1260 -1260
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-payload-xml-order-update-graphql.md +1472 -1472
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-csv-control-graphql.md +2417 -2417
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-csv-location-graphql.md +2811 -2811
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-csv-price-graphql.md +2619 -2619
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-json-location-graphql.md +2807 -2807
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-xml-location-graphql.md +2373 -2373
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-sftp-csv-control-graphql.md +2740 -2740
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-sftp-csv-location-graphql.md +2760 -2760
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-sftp-json-location-graphql.md +1710 -1710
- package/docs/01-TEMPLATES/versori/workflows/ingestion/ingestion-workflows-guide.md +136 -136
- package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/rubix-webhooks-guide.md +520 -520
- package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/template-webhook-rubix-fulfilment-to-sftp-xml-inline.md +1418 -1418
- package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/template-webhook-rubix-fulfilment-to-sftp-xml-universal-mapper.md +1785 -1785
- package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/template-webhook-rubix-order-attribute-update.md +824 -824
- package/docs/01-TEMPLATES/versori/workflows/workflows-overview-guide.md +646 -646
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-batch-archival.md +724 -724
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-job-tracker.md +627 -627
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-partial-batch-recovery.md +561 -561
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-quick-reference.md +367 -367
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-readme.md +407 -407
- package/docs/02-CORE-GUIDES/advanced-services/readme.md +49 -49
- package/docs/02-CORE-GUIDES/api-reference/api-reference-quick-reference.md +548 -548
- package/docs/02-CORE-GUIDES/api-reference/event-api-input-output-reference.md +702 -1171
- package/docs/02-CORE-GUIDES/api-reference/examples/client-initialization.ts +286 -286
- package/docs/02-CORE-GUIDES/api-reference/graphql-error-classification.md +337 -337
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-01-client-api.md +399 -520
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-03-authentication.md +199 -199
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-04-graphql-mapping.md +925 -925
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-05-services.md +1198 -1198
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-06-data-sources.md +1083 -1083
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-07-parsers.md +1097 -1097
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-08-pagination.md +513 -513
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-08-types.md +545 -597
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-09-error-handling.md +527 -527
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-09-webhook-validation.md +514 -514
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-10-extraction.md +557 -557
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-10-utilities.md +412 -412
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-11-cli-tools.md +423 -423
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-11-error-handling.md +716 -716
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-analyze-source-structure.md +518 -518
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-partial-responses.md +212 -212
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-testing.md +300 -300
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-13-resolver-builder.md +322 -322
- package/docs/02-CORE-GUIDES/api-reference/readme.md +279 -279
- package/docs/02-CORE-GUIDES/auto-pagination/auto-pagination-quick-reference.md +351 -351
- package/docs/02-CORE-GUIDES/auto-pagination/auto-pagination-readme.md +277 -277
- package/docs/02-CORE-GUIDES/auto-pagination/examples/auto-pagination-readme.md +178 -178
- package/docs/02-CORE-GUIDES/auto-pagination/examples/common-patterns.ts +351 -351
- package/docs/02-CORE-GUIDES/auto-pagination/examples/paginate-products.ts +384 -384
- package/docs/02-CORE-GUIDES/auto-pagination/examples/paginate-virtual-positions.ts +308 -308
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-01-foundations.md +470 -470
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-02-quick-start.md +713 -713
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-03-configuration.md +754 -754
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-04-advanced-patterns.md +732 -732
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-05-sdk-integration.md +847 -847
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-06-troubleshooting.md +359 -359
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-07-api-reference.md +462 -462
- package/docs/02-CORE-GUIDES/auto-pagination/readme.md +54 -54
- package/docs/02-CORE-GUIDES/data-sources/data-sources-file-operations-error-handling.md +1487 -1487
- package/docs/02-CORE-GUIDES/data-sources/data-sources-quick-reference.md +836 -836
- package/docs/02-CORE-GUIDES/data-sources/data-sources-readme.md +276 -276
- package/docs/02-CORE-GUIDES/data-sources/data-sources-sftp-credential-access-security.md +553 -553
- package/docs/02-CORE-GUIDES/data-sources/examples/common-patterns.ts +409 -409
- package/docs/02-CORE-GUIDES/data-sources/examples/data-sources-readme.md +178 -178
- package/docs/02-CORE-GUIDES/data-sources/examples/s3-operations.ts +308 -308
- package/docs/02-CORE-GUIDES/data-sources/examples/sftp-operations.ts +371 -371
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-01-foundations.md +735 -735
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-02-s3-operations.md +1302 -1302
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-03-sftp-operations.md +1379 -1379
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-04-file-patterns.md +941 -941
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-05-advanced-topics.md +813 -813
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-06-integration-patterns.md +486 -486
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-07-troubleshooting.md +387 -387
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-08-api-reference.md +417 -417
- package/docs/02-CORE-GUIDES/data-sources/readme.md +77 -77
- package/docs/02-CORE-GUIDES/error-handling-guide.md +936 -936
- package/docs/02-CORE-GUIDES/extraction/examples/02-core-guides-extraction-readme.md +116 -116
- package/docs/02-CORE-GUIDES/extraction/examples/common-patterns.ts +428 -428
- package/docs/02-CORE-GUIDES/extraction/examples/extract-inventory-basic.ts +187 -187
- package/docs/02-CORE-GUIDES/extraction/extraction-quick-reference.md +596 -596
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-01-foundations.md +514 -514
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-02-basic-extraction.md +823 -823
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-03-parquet-processing.md +507 -507
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-04-data-enrichment.md +546 -546
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-05-transformation.md +494 -494
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-06-export-formats.md +458 -458
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-06-performance.md +138 -138
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-07-api-reference.md +148 -148
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-07-optimization.md +692 -692
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-08-extraction-orchestrator.md +1008 -1008
- package/docs/02-CORE-GUIDES/extraction/readme.md +151 -151
- package/docs/02-CORE-GUIDES/ingestion/examples/_simple-kv-store.ts +40 -40
- package/docs/02-CORE-GUIDES/ingestion/examples/error-recovery.ts +728 -728
- package/docs/02-CORE-GUIDES/ingestion/examples/event-driven.ts +501 -501
- package/docs/02-CORE-GUIDES/ingestion/examples/local-file-ingestion.ts +88 -88
- package/docs/02-CORE-GUIDES/ingestion/examples/parquet-ingestion.ts +117 -117
- package/docs/02-CORE-GUIDES/ingestion/examples/performance-optimized.ts +647 -647
- package/docs/02-CORE-GUIDES/ingestion/examples/s3-csv-ingestion.ts +169 -169
- package/docs/02-CORE-GUIDES/ingestion/examples/sftp-csv-ingestion.ts +134 -134
- package/docs/02-CORE-GUIDES/ingestion/ingestion-quick-reference.md +546 -546
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-01-introduction.md +626 -626
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-02-quick-start.md +658 -658
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-03-data-sources.md +1052 -1052
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-04-field-mapping.md +763 -763
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-05-advanced-parsers.md +676 -676
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-06-batch-api.md +1295 -1295
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-07-api-reference.md +138 -138
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-07-state-management.md +1037 -1037
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-08-performance-optimization.md +1349 -1349
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-09-best-practices.md +1893 -1893
- package/docs/02-CORE-GUIDES/ingestion/readme.md +160 -160
- package/docs/02-CORE-GUIDES/logging-guide.md +585 -585
- package/docs/02-CORE-GUIDES/mapping/error-handling-patterns.md +401 -401
- package/docs/02-CORE-GUIDES/mapping/examples/02-core-guides-mapping-readme.md +128 -128
- package/docs/02-CORE-GUIDES/mapping/examples/common-patterns.ts +273 -273
- package/docs/02-CORE-GUIDES/mapping/examples/csv-location-ingestion.json +36 -36
- package/docs/02-CORE-GUIDES/mapping/examples/csv-mapping.ts +242 -242
- package/docs/02-CORE-GUIDES/mapping/examples/graphql-to-parquet-extraction.json +36 -36
- package/docs/02-CORE-GUIDES/mapping/examples/json-mapping.ts +213 -213
- package/docs/02-CORE-GUIDES/mapping/examples/json-product-to-mutation.json +48 -48
- package/docs/02-CORE-GUIDES/mapping/examples/xml-mapping.ts +291 -291
- package/docs/02-CORE-GUIDES/mapping/examples/xml-order-to-mutation.json +45 -45
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/graphql-mutation-mapping-quick-reference.md +463 -463
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/graphql-mutation-mapping-readme.md +227 -227
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-01-introduction.md +222 -222
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-02-quick-start.md +351 -351
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-03-schema-validation.md +569 -569
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-04-mapping-patterns.md +471 -471
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-05-configuration-reference.md +611 -611
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-06-advanced-xpath.md +148 -148
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-06-path-syntax.md +464 -464
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-07-api-reference.md +94 -94
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-07-array-handling.md +307 -307
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-08-custom-resolvers.md +544 -544
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-09-advanced-patterns.md +427 -427
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-10-hooks-and-variables.md +336 -336
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-11-error-handling.md +488 -488
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-12-arguments-vs-nodes.md +383 -383
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-13-best-practices.md +477 -477
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/readme.md +62 -62
- package/docs/02-CORE-GUIDES/mapping/mapping-format-decision-tree.md +480 -480
- package/docs/02-CORE-GUIDES/mapping/mapping-graphql-alias-batching-guide.md +820 -820
- package/docs/02-CORE-GUIDES/mapping/mapping-javascript-objects.md +2369 -2369
- package/docs/02-CORE-GUIDES/mapping/mapping-mapper-comparison-guide.md +682 -682
- package/docs/02-CORE-GUIDES/mapping/modules/02-core-guides-mapping-07-api-reference.md +1327 -1327
- package/docs/02-CORE-GUIDES/mapping/modules/02-core-guides-mapping-08-error-handling.md +1142 -1142
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-04-use-cases.md +891 -891
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-06-helpers-resolvers.md +1126 -1126
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-06-sdk-resolvers.md +199 -199
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-07-api-reference.md +1319 -1319
- package/docs/02-CORE-GUIDES/mapping/readme.md +178 -178
- package/docs/02-CORE-GUIDES/mapping/resolver-registration.md +410 -410
- package/docs/02-CORE-GUIDES/mapping/resolvers/examples/common-patterns.ts +226 -226
- package/docs/02-CORE-GUIDES/mapping/resolvers/examples/custom-resolvers.ts +227 -227
- package/docs/02-CORE-GUIDES/mapping/resolvers/examples/sdk-resolvers-usage.ts +203 -203
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-readme.md +274 -274
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-api-reference.md +679 -679
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-cookbook.md +826 -826
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-guide.md +1330 -1330
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-helpers-reference.md +1437 -1437
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-parameters-reference.md +553 -553
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-troubleshooting.md +854 -854
- package/docs/02-CORE-GUIDES/mapping/resolvers/readme.md +75 -75
- package/docs/02-CORE-GUIDES/parsers/examples/02-core-guides-parsers-readme.md +161 -161
- package/docs/02-CORE-GUIDES/parsers/examples/csv-parser-examples.ts +110 -110
- package/docs/02-CORE-GUIDES/parsers/examples/json-parser-examples.ts +33 -33
- package/docs/02-CORE-GUIDES/parsers/examples/parquet-parser-examples.ts +47 -47
- package/docs/02-CORE-GUIDES/parsers/examples/xml-parser-examples.ts +38 -38
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-01-foundations.md +355 -355
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-02-csv-parser.md +772 -772
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-03-json-parser.md +789 -789
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-04-xml-parser.md +857 -857
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-05-parquet-parser.md +603 -603
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-06-integration-patterns.md +702 -702
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-06-streaming.md +121 -121
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-07-api-reference.md +89 -89
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-07-troubleshooting.md +727 -727
- package/docs/02-CORE-GUIDES/parsers/parsers-quick-reference.md +482 -482
- package/docs/02-CORE-GUIDES/parsers/parsers-readme.md +258 -258
- package/docs/02-CORE-GUIDES/parsers/readme.md +65 -65
- package/docs/02-CORE-GUIDES/readme.md +194 -194
- package/docs/02-CORE-GUIDES/webhook-validation/examples/basic-validation.ts +108 -108
- package/docs/02-CORE-GUIDES/webhook-validation/examples/common-patterns.ts +316 -316
- package/docs/02-CORE-GUIDES/webhook-validation/examples/webhook-validation-readme.md +61 -61
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-01-foundations.md +440 -440
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-02-quick-start.md +525 -525
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-03-versori-integration.md +741 -741
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-04-platform-integration.md +629 -629
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-05-configuration.md +535 -535
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-06-error-handling.md +611 -611
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-06-troubleshooting.md +124 -124
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-07-api-reference.md +511 -511
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-08-rubix-webhooks.md +590 -590
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-09-rubix-event-vs-http-call.md +432 -432
- package/docs/02-CORE-GUIDES/webhook-validation/readme.md +239 -239
- package/docs/02-CORE-GUIDES/webhook-validation/webhook-validation-quick-reference.md +392 -392
- package/docs/03-PATTERN-GUIDES/connector-scenarios/connector-scenarios-quick-reference.md +498 -498
- package/docs/03-PATTERN-GUIDES/connector-scenarios/connector-scenarios-readme.md +313 -313
- package/docs/03-PATTERN-GUIDES/connector-scenarios/examples/common-patterns.ts +612 -612
- package/docs/03-PATTERN-GUIDES/connector-scenarios/examples/connector-scenarios-readme.md +253 -253
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-01-foundations.md +452 -452
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-02-simple-scenarios.md +681 -681
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-03-intermediate-scenarios.md +637 -637
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-04-advanced-scenarios.md +650 -650
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-05-bidirectional-sync.md +233 -233
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-06-production-patterns.md +442 -442
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-07-reference.md +445 -445
- package/docs/03-PATTERN-GUIDES/connector-scenarios/readme.md +31 -31
- package/docs/03-PATTERN-GUIDES/enterprise-integration-patterns.md +1528 -1528
- package/docs/03-PATTERN-GUIDES/error-handling/comprehensive-error-handling-guide.md +1437 -1437
- package/docs/03-PATTERN-GUIDES/error-handling/error-handling-quick-reference.md +390 -390
- package/docs/03-PATTERN-GUIDES/error-handling/examples/common-patterns.ts +438 -438
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-01-foundations.md +362 -362
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-02-error-types.md +850 -850
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-03-utf8-handling.md +456 -456
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-04-error-scenarios.md +658 -658
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-05-calling-patterns.md +671 -671
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-06-retry-strategies.md +1034 -1034
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-07-monitoring.md +653 -653
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-08-api-reference.md +847 -847
- package/docs/03-PATTERN-GUIDES/error-handling/readme.md +36 -36
- package/docs/03-PATTERN-GUIDES/examples/__tests__/readme.md +40 -40
- package/docs/03-PATTERN-GUIDES/examples/__tests__/resolver-examples.test.js +282 -282
- package/docs/03-PATTERN-GUIDES/examples/test-data/03-pattern-guides-readme.md +110 -110
- package/docs/03-PATTERN-GUIDES/examples/test-data/canonical-inventory.json +123 -123
- package/docs/03-PATTERN-GUIDES/examples/test-data/canonical-order.json +171 -171
- package/docs/03-PATTERN-GUIDES/examples/test-data/readme.md +28 -28
- package/docs/03-PATTERN-GUIDES/extraction/extraction-readme.md +15 -15
- package/docs/03-PATTERN-GUIDES/extraction/readme.md +25 -25
- package/docs/03-PATTERN-GUIDES/file-operations/examples/common-patterns.ts +407 -407
- package/docs/03-PATTERN-GUIDES/file-operations/examples/file-operations-readme.md +142 -142
- package/docs/03-PATTERN-GUIDES/file-operations/file-operations-quick-reference.md +462 -462
- package/docs/03-PATTERN-GUIDES/file-operations/file-operations-readme.md +379 -379
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-01-foundations.md +430 -430
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-02-quick-start.md +484 -484
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-03-s3-operations.md +507 -507
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-04-sftp-operations.md +963 -963
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-05-streaming-performance.md +503 -503
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-06-archive-patterns.md +386 -386
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-06-error-handling.md +117 -117
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-07-api-reference.md +78 -78
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-07-testing-troubleshooting.md +567 -567
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-08-api-reference.md +1055 -1055
- package/docs/03-PATTERN-GUIDES/file-operations/readme.md +32 -32
- package/docs/03-PATTERN-GUIDES/ingestion/ingestion-readme.md +15 -15
- package/docs/03-PATTERN-GUIDES/ingestion/readme.md +25 -25
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/batch-processing.ts +130 -130
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/common-patterns.ts +360 -360
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/delta-sync.ts +130 -130
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/integration-patterns-readme.md +100 -100
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/real-time-webhook.ts +398 -398
- package/docs/03-PATTERN-GUIDES/integration-patterns/integration-patterns-quick-reference.md +962 -962
- package/docs/03-PATTERN-GUIDES/integration-patterns/integration-patterns-readme.md +134 -134
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-01-real-time-processing.md +991 -991
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-02-batch-processing.md +1547 -1547
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-03-delta-sync.md +1108 -1108
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-04-webhook-patterns.md +1181 -1181
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-05-error-handling.md +1061 -1061
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-06-advanced-integration-services.md +1547 -1547
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-06-performance.md +109 -109
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-07-api-reference.md +34 -34
- package/docs/03-PATTERN-GUIDES/integration-patterns/readme.md +30 -30
- package/docs/03-PATTERN-GUIDES/logging-minimal-mode.md +128 -128
- package/docs/03-PATTERN-GUIDES/multiple-connections/examples/common-patterns.ts +380 -380
- package/docs/03-PATTERN-GUIDES/multiple-connections/examples/multiple-connections-readme.md +139 -139
- package/docs/03-PATTERN-GUIDES/multiple-connections/examples/parallel-root-connections.ts +149 -149
- package/docs/03-PATTERN-GUIDES/multiple-connections/examples/real-world-scenarios.ts +405 -405
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-01-foundations.md +378 -378
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-02-quick-start.md +566 -566
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-03-targeting-connections.md +659 -659
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-04-parallel-queries.md +656 -656
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-05-best-practices.md +624 -624
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-06-api-reference.md +824 -824
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-06-versori.md +119 -119
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-07-api-reference.md +87 -87
- package/docs/03-PATTERN-GUIDES/multiple-connections/multiple-connections-quick-reference.md +353 -353
- package/docs/03-PATTERN-GUIDES/multiple-connections/multiple-connections-readme.md +270 -270
- package/docs/03-PATTERN-GUIDES/multiple-connections/readme.md +30 -30
- package/docs/03-PATTERN-GUIDES/pagination/pagination-readme.md +14 -14
- package/docs/03-PATTERN-GUIDES/pagination/readme.md +24 -24
- package/docs/03-PATTERN-GUIDES/parquet/examples/common-patterns.ts +180 -180
- package/docs/03-PATTERN-GUIDES/parquet/examples/read-parquet.ts +48 -48
- package/docs/03-PATTERN-GUIDES/parquet/examples/write-parquet.ts +65 -65
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-01-introduction.md +393 -393
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-02-quick-start.md +572 -572
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-03-reading-parquet.md +525 -525
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-04-writing-parquet.md +554 -554
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-05-graphql-extraction.md +405 -405
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-06-performance.md +104 -104
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-06-s3-integration.md +511 -511
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-07-api-reference.md +90 -90
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-07-performance-optimization.md +525 -525
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-08-best-practices.md +712 -712
- package/docs/03-PATTERN-GUIDES/parquet/parquet-quick-reference.md +683 -683
- package/docs/03-PATTERN-GUIDES/parquet/parquet-readme.md +248 -248
- package/docs/03-PATTERN-GUIDES/parquet/readme.md +32 -32
- package/docs/03-PATTERN-GUIDES/parsers/parsers-readme.md +12 -12
- package/docs/03-PATTERN-GUIDES/parsers/readme.md +24 -24
- package/docs/03-PATTERN-GUIDES/readme.md +159 -159
- package/docs/03-PATTERN-GUIDES/webhooks/readme.md +24 -24
- package/docs/03-PATTERN-GUIDES/webhooks/webhooks-readme.md +8 -8
- package/docs/04-REFERENCE/architecture/architecture-01-overview.md +427 -427
- package/docs/04-REFERENCE/architecture/architecture-02-client-architecture.md +424 -424
- package/docs/04-REFERENCE/architecture/architecture-03-data-flow.md +690 -690
- package/docs/04-REFERENCE/architecture/architecture-04-service-layer.md +834 -834
- package/docs/04-REFERENCE/architecture/architecture-05-integration-architecture.md +655 -655
- package/docs/04-REFERENCE/architecture/architecture-06-state-management.md +653 -653
- package/docs/04-REFERENCE/architecture/architecture-adding-new-data-sources.md +686 -686
- package/docs/04-REFERENCE/architecture/readme.md +279 -279
- package/docs/04-REFERENCE/platforms/deno/readme.md +117 -117
- package/docs/04-REFERENCE/platforms/nodejs/readme.md +146 -146
- package/docs/04-REFERENCE/platforms/readme.md +135 -135
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-01-introduction.md +398 -398
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-02-quick-start.md +560 -560
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-03-authentication.md +757 -757
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-04-workflows.md +2476 -2476
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-05-connections.md +1167 -1167
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-06-kv-storage.md +990 -990
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-06-state-management.md +121 -121
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-07-api-reference.md +68 -68
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-07-deployment.md +731 -731
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-08-best-practices.md +1111 -1111
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-09-signature-reference.md +766 -766
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-readme.md +299 -299
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-s3-sftp-configuration-guide.md +1425 -1425
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-webhook-api-key-security.md +816 -816
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-webhook-connection-security.md +681 -681
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-workflow-task-types.md +708 -708
- package/docs/04-REFERENCE/platforms/versori/readme.md +108 -108
- package/docs/04-REFERENCE/readme.md +148 -148
- package/docs/04-REFERENCE/resolver-signature/examples/advanced-resolvers.ts +482 -482
- package/docs/04-REFERENCE/resolver-signature/examples/async-resolvers.ts +496 -496
- package/docs/04-REFERENCE/resolver-signature/examples/basic-resolvers.ts +343 -343
- package/docs/04-REFERENCE/resolver-signature/examples/resolver-signature-readme.md +188 -188
- package/docs/04-REFERENCE/resolver-signature/examples/testing-resolvers.ts +463 -463
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-01-foundations.md +286 -286
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-02-parameter-reference.md +643 -643
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-03-basic-examples.md +521 -521
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-04-advanced-patterns.md +739 -739
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-05-sdk-resolvers.md +531 -531
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-06-migration-guide.md +650 -650
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-06-testing.md +125 -125
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-07-api-reference.md +794 -794
- package/docs/04-REFERENCE/resolver-signature/readme.md +64 -64
- package/docs/04-REFERENCE/resolver-signature/resolver-signature-quick-reference.md +270 -270
- package/docs/04-REFERENCE/resolver-signature/resolver-signature-readme.md +351 -351
- package/docs/04-REFERENCE/schema/fluent-commerce-schema.json +764 -764
- package/docs/04-REFERENCE/schema/readme.md +141 -141
- package/docs/04-REFERENCE/testing/examples/04-reference-testing-readme.md +158 -158
- package/docs/04-REFERENCE/testing/examples/fluent-testing.ts +62 -62
- package/docs/04-REFERENCE/testing/examples/health-check.ts +155 -155
- package/docs/04-REFERENCE/testing/examples/integration-test.ts +119 -119
- package/docs/04-REFERENCE/testing/examples/performance-test.ts +183 -183
- package/docs/04-REFERENCE/testing/examples/s3-testing.ts +127 -127
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-01-foundations.md +267 -267
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-02-s3-testing.md +599 -599
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-03-fluent-testing.md +589 -589
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-04-integration-testing.md +699 -699
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-05-debugging.md +478 -478
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-06-cicd-integration.md +463 -463
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-06-preflight-validation.md +131 -131
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-07-best-practices.md +499 -499
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-07-coverage-ci.md +165 -165
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-08-api-reference.md +634 -634
- package/docs/04-REFERENCE/testing/readme.md +86 -86
- package/docs/04-REFERENCE/testing/testing-quick-reference.md +667 -667
- package/docs/04-REFERENCE/testing/testing-readme.md +286 -286
- package/docs/04-REFERENCE/troubleshooting/readme.md +144 -144
- package/docs/04-REFERENCE/troubleshooting/troubleshooting-deno-sftp-compatibility.md +392 -392
- package/docs/template-loading-matrix.md +242 -242
- package/package.json +5 -3
- package/docs/02-CORE-GUIDES/api-reference/cli-profile-integration.md +0 -377
package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-06-error-handling.md
CHANGED
|
@@ -1,611 +1,611 @@
|
|
|
1
|
-
# Module 6: Error Handling
|
|
2
|
-
|
|
3
|
-
**Level:** Intermediate
|
|
4
|
-
**Estimated Time:** 25 minutes
|
|
5
|
-
|
|
6
|
-
## Overview
|
|
7
|
-
|
|
8
|
-
This module covers comprehensive error handling for webhook validation, including validation result structures, common error scenarios, debugging techniques, and recovery strategies.
|
|
9
|
-
|
|
10
|
-
## Learning Objectives
|
|
11
|
-
|
|
12
|
-
By the end of this module, you will:
|
|
13
|
-
- Understand WebhookValidationResult structure
|
|
14
|
-
- Handle common validation error scenarios
|
|
15
|
-
- Debug validation failures effectively
|
|
16
|
-
- Implement logging and monitoring
|
|
17
|
-
- Build resilient webhook handlers
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## Validation Result Structure
|
|
22
|
-
|
|
23
|
-
### WebhookValidationResult Interface
|
|
24
|
-
|
|
25
|
-
```typescript
|
|
26
|
-
interface WebhookValidationResult {
|
|
27
|
-
/**
|
|
28
|
-
* Whether the webhook signature is valid
|
|
29
|
-
*/
|
|
30
|
-
isValid: boolean;
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Algorithm used for validation
|
|
34
|
-
*/
|
|
35
|
-
algorithm: SignatureAlgorithm;
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Error message if validation failed
|
|
39
|
-
* undefined if isValid === true
|
|
40
|
-
*/
|
|
41
|
-
error?: string;
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Timestamp when validation occurred
|
|
45
|
-
*/
|
|
46
|
-
timestamp: Date;
|
|
47
|
-
}
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### Success Result
|
|
51
|
-
|
|
52
|
-
```typescript
|
|
53
|
-
const result = await validator.validateWebhook(rawBody, headers, publicKey);
|
|
54
|
-
|
|
55
|
-
if (result.isValid) {
|
|
56
|
-
console.log({
|
|
57
|
-
isValid: true,
|
|
58
|
-
algorithm: 'SHA512withRSA', // or 'MD5withRSA'
|
|
59
|
-
timestamp: '2025-01-14T10:30:00.000Z',
|
|
60
|
-
error: undefined
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### Failure Result
|
|
66
|
-
|
|
67
|
-
```typescript
|
|
68
|
-
const result = await validator.validateWebhook(rawBody, headers, publicKey);
|
|
69
|
-
|
|
70
|
-
if (!result.isValid) {
|
|
71
|
-
console.log({
|
|
72
|
-
isValid: false,
|
|
73
|
-
algorithm: 'SHA512withRSA', // Attempted algorithm
|
|
74
|
-
error: 'Signature verification failed',
|
|
75
|
-
timestamp: '2025-01-14T10:30:00.000Z'
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
---
|
|
81
|
-
|
|
82
|
-
## Common Error Scenarios
|
|
83
|
-
|
|
84
|
-
### Error 1: Missing Signature Header
|
|
85
|
-
|
|
86
|
-
```typescript
|
|
87
|
-
// Webhook request without signature header
|
|
88
|
-
const headers = {
|
|
89
|
-
'content-type': 'application/json'
|
|
90
|
-
// No 'fluent-signature' or 'flex.signature'
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
const result = await validator.validateWebhook(rawBody, headers, publicKey);
|
|
94
|
-
|
|
95
|
-
// Result:
|
|
96
|
-
{
|
|
97
|
-
isValid: false,
|
|
98
|
-
algorithm: 'SHA512withRSA', // Default algorithm
|
|
99
|
-
error: 'No signature header found',
|
|
100
|
-
timestamp: Date
|
|
101
|
-
}
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
**Causes**:
|
|
105
|
-
- Webhook not from Fluent Commerce Rubix
|
|
106
|
-
- Misconfigured Rubix workflow
|
|
107
|
-
- Testing with manually crafted requests
|
|
108
|
-
|
|
109
|
-
**Solution**:
|
|
110
|
-
```typescript
|
|
111
|
-
if (!result.isValid && result.error?.includes('No signature header')) {
|
|
112
|
-
logger.error('Webhook missing signature - not from Fluent Commerce');
|
|
113
|
-
return { statusCode: 400, body: 'Missing signature header' };
|
|
114
|
-
}
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
### Error 2: Invalid Signature
|
|
118
|
-
|
|
119
|
-
```typescript
|
|
120
|
-
// Signature doesn't match payload
|
|
121
|
-
const result = await validator.validateWebhook(rawBody, headers, publicKey);
|
|
122
|
-
|
|
123
|
-
// Result:
|
|
124
|
-
{
|
|
125
|
-
isValid: false,
|
|
126
|
-
algorithm: 'SHA512withRSA',
|
|
127
|
-
error: 'Signature verification failed',
|
|
128
|
-
timestamp: Date
|
|
129
|
-
}
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
**Causes**:
|
|
133
|
-
- Wrong public key (production key used for sandbox webhook)
|
|
134
|
-
- Body was modified before validation
|
|
135
|
-
- Signature header corrupted in transit
|
|
136
|
-
- Webhook not from Fluent Commerce
|
|
137
|
-
|
|
138
|
-
**Solution**:
|
|
139
|
-
```typescript
|
|
140
|
-
if (!result.isValid && result.error?.includes('verification failed')) {
|
|
141
|
-
logger.warn('Invalid signature detected', {
|
|
142
|
-
error: result.error,
|
|
143
|
-
algorithm: result.algorithm,
|
|
144
|
-
hasSignature: !!headers['fluent-signature'] || !!headers['flex.signature'],
|
|
145
|
-
bodyLength: rawBody.length
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
return { statusCode: 401, body: 'Invalid signature' };
|
|
149
|
-
}
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### Error 3: Missing Public Key
|
|
153
|
-
|
|
154
|
-
```typescript
|
|
155
|
-
const publicKey = process.env.FLUENT_WEBHOOK_PUBLIC_KEY;
|
|
156
|
-
if (!publicKey) {
|
|
157
|
-
// This will throw error before validation
|
|
158
|
-
throw new Error('FLUENT_WEBHOOK_PUBLIC_KEY not configured');
|
|
159
|
-
}
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
**Causes**:
|
|
163
|
-
- Environment variable not set
|
|
164
|
-
- Configuration not loaded
|
|
165
|
-
- Versori variable not defined
|
|
166
|
-
|
|
167
|
-
**Solution**:
|
|
168
|
-
```typescript
|
|
169
|
-
const publicKey = process.env.FLUENT_WEBHOOK_PUBLIC_KEY;
|
|
170
|
-
if (!publicKey) {
|
|
171
|
-
logger.error('Missing public key configuration');
|
|
172
|
-
return { statusCode: 500, body: 'Server configuration error' };
|
|
173
|
-
}
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
### Error 4: Invalid Public Key Format
|
|
177
|
-
|
|
178
|
-
```typescript
|
|
179
|
-
const result = await validator.validateWebhook(rawBody, headers, invalidKey);
|
|
180
|
-
|
|
181
|
-
// May throw error:
|
|
182
|
-
// "Invalid public key format"
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
**Causes**:
|
|
186
|
-
- Truncated public key
|
|
187
|
-
- Wrong key format (not PEM or base64)
|
|
188
|
-
- Corrupted key in storage
|
|
189
|
-
|
|
190
|
-
**Solution**:
|
|
191
|
-
```typescript
|
|
192
|
-
try {
|
|
193
|
-
const result = await validator.validateWebhook(rawBody, headers, publicKey);
|
|
194
|
-
} catch (error) {
|
|
195
|
-
if (error.message.includes('Invalid public key')) {
|
|
196
|
-
logger.error('Public key format error', { publicKeyPrefix: publicKey.substring(0, 30) });
|
|
197
|
-
return { statusCode: 500, body: 'Configuration error' };
|
|
198
|
-
}
|
|
199
|
-
throw error;
|
|
200
|
-
}
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
### Error 5: Body Modified Before Validation
|
|
204
|
-
|
|
205
|
-
```typescript
|
|
206
|
-
// ❌ WRONG - Body was parsed and re-stringified
|
|
207
|
-
const data = JSON.parse(rawBody);
|
|
208
|
-
const modifiedBody = JSON.stringify(data); // May change whitespace, key order
|
|
209
|
-
|
|
210
|
-
const result = await validator.validateWebhook(modifiedBody, headers, publicKey);
|
|
211
|
-
// isValid: false, error: 'Signature verification failed'
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
**Causes**:
|
|
215
|
-
- JSON parsed before validation
|
|
216
|
-
- Whitespace trimmed
|
|
217
|
-
- Character encoding changed
|
|
218
|
-
|
|
219
|
-
**Solution**:
|
|
220
|
-
```typescript
|
|
221
|
-
// ✅ CORRECT - Use original raw body
|
|
222
|
-
const rawBody = await request.text(); // Original bytes
|
|
223
|
-
const result = await validator.validateWebhook(rawBody, headers, publicKey);
|
|
224
|
-
|
|
225
|
-
if (result.isValid) {
|
|
226
|
-
const data = JSON.parse(rawBody); // Parse after validation
|
|
227
|
-
}
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
---
|
|
231
|
-
|
|
232
|
-
## Debugging Validation Failures
|
|
233
|
-
|
|
234
|
-
### Debug Pattern 1: Log All Validation Attempts
|
|
235
|
-
|
|
236
|
-
```typescript
|
|
237
|
-
const result = await validator.validateWebhook(rawBody, headers, publicKey);
|
|
238
|
-
|
|
239
|
-
logger.info('Validation attempt', {
|
|
240
|
-
isValid: result.isValid,
|
|
241
|
-
algorithm: result.algorithm,
|
|
242
|
-
error: result.error,
|
|
243
|
-
timestamp: result.timestamp,
|
|
244
|
-
bodyLength: rawBody.length,
|
|
245
|
-
headers: Object.keys(headers),
|
|
246
|
-
hasFluentSig: !!headers['fluent-signature'],
|
|
247
|
-
hasFlexSig: !!headers['flex.signature'],
|
|
248
|
-
publicKeyPrefix: publicKey.substring(0, 30)
|
|
249
|
-
});
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
### Debug Pattern 2: Compare Algorithms
|
|
253
|
-
|
|
254
|
-
```typescript
|
|
255
|
-
// Try both algorithms to see which should work
|
|
256
|
-
const sha512Result = await validator.validateWebhookSignature(
|
|
257
|
-
rawBody,
|
|
258
|
-
headers['fluent-signature'],
|
|
259
|
-
publicKey,
|
|
260
|
-
SignatureAlgorithm.SHA512_WITH_RSA
|
|
261
|
-
);
|
|
262
|
-
|
|
263
|
-
const md5Result = await validator.validateWebhookSignature(
|
|
264
|
-
rawBody,
|
|
265
|
-
headers['flex.signature'],
|
|
266
|
-
publicKey,
|
|
267
|
-
SignatureAlgorithm.MD5_WITH_RSA
|
|
268
|
-
);
|
|
269
|
-
|
|
270
|
-
console.log('SHA512 validation:', sha512Result);
|
|
271
|
-
console.log('MD5 validation:', md5Result);
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
### Debug Pattern 3: Validate Key Format
|
|
275
|
-
|
|
276
|
-
```typescript
|
|
277
|
-
function validatePublicKeyFormat(publicKey: string): boolean {
|
|
278
|
-
// Check for PEM format
|
|
279
|
-
if (publicKey.includes('-----BEGIN PUBLIC KEY-----')) {
|
|
280
|
-
return publicKey.includes('-----END PUBLIC KEY-----');
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// Check for base64 format
|
|
284
|
-
const base64Regex = /^[A-Za-z0-9+/]+=*$/;
|
|
285
|
-
return base64Regex.test(publicKey.trim());
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
const isValidFormat = validatePublicKeyFormat(publicKey);
|
|
289
|
-
if (!isValidFormat) {
|
|
290
|
-
logger.error('Invalid public key format detected');
|
|
291
|
-
}
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
### Debug Pattern 4: Check Environment Mismatch
|
|
295
|
-
|
|
296
|
-
```typescript
|
|
297
|
-
const result = await validator.validateWebhook(rawBody, headers, publicKey);
|
|
298
|
-
|
|
299
|
-
if (!result.isValid) {
|
|
300
|
-
logger.warn('Validation failed - check environment mismatch', {
|
|
301
|
-
error: result.error,
|
|
302
|
-
currentEnv: process.env.NODE_ENV,
|
|
303
|
-
fluentEnv: process.env.FLUENT_ENV,
|
|
304
|
-
keySource: 'Check if production key used for sandbox webhook'
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
---
|
|
310
|
-
|
|
311
|
-
## Logging and Monitoring
|
|
312
|
-
|
|
313
|
-
### Security Event Logging
|
|
314
|
-
|
|
315
|
-
```typescript
|
|
316
|
-
async function validateAndLogWebhook(
|
|
317
|
-
rawBody: string,
|
|
318
|
-
headers: Record<string, string>,
|
|
319
|
-
publicKey: string,
|
|
320
|
-
logger: Logger
|
|
321
|
-
): Promise<WebhookValidationResult> {
|
|
322
|
-
const result = await validator.validateWebhook(rawBody, headers, publicKey);
|
|
323
|
-
|
|
324
|
-
if (!result.isValid) {
|
|
325
|
-
// Log security event
|
|
326
|
-
logger.warn('Webhook validation failed', {
|
|
327
|
-
severity: 'SECURITY',
|
|
328
|
-
error: result.error,
|
|
329
|
-
algorithm: result.algorithm,
|
|
330
|
-
timestamp: result.timestamp.toISOString(),
|
|
331
|
-
sourceIp: headers['x-forwarded-for'] || headers['x-real-ip'] || 'unknown',
|
|
332
|
-
userAgent: headers['user-agent'] || 'unknown',
|
|
333
|
-
signaturePresent: !!headers['fluent-signature'] || !!headers['flex.signature']
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
// Optionally alert security team
|
|
337
|
-
if (await shouldAlertSecurity(result.error)) {
|
|
338
|
-
await alertSecurityTeam({
|
|
339
|
-
event: 'Invalid webhook signature',
|
|
340
|
-
details: result.error,
|
|
341
|
-
timestamp: result.timestamp
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
return result;
|
|
347
|
-
}
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
### Metrics Collection
|
|
351
|
-
|
|
352
|
-
```typescript
|
|
353
|
-
interface ValidationMetrics {
|
|
354
|
-
totalAttempts: number;
|
|
355
|
-
successCount: number;
|
|
356
|
-
failureCount: number;
|
|
357
|
-
errorTypes: Record<string, number>;
|
|
358
|
-
algorithms: Record<string, number>;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
const metrics: ValidationMetrics = {
|
|
362
|
-
totalAttempts: 0,
|
|
363
|
-
successCount: 0,
|
|
364
|
-
failureCount: 0,
|
|
365
|
-
errorTypes: {},
|
|
366
|
-
algorithms: {}
|
|
367
|
-
};
|
|
368
|
-
|
|
369
|
-
async function validateWithMetrics(
|
|
370
|
-
rawBody: string,
|
|
371
|
-
headers: Record<string, string>,
|
|
372
|
-
publicKey: string
|
|
373
|
-
): Promise<WebhookValidationResult> {
|
|
374
|
-
const result = await validator.validateWebhook(rawBody, headers, publicKey);
|
|
375
|
-
|
|
376
|
-
metrics.totalAttempts++;
|
|
377
|
-
|
|
378
|
-
if (result.isValid) {
|
|
379
|
-
metrics.successCount++;
|
|
380
|
-
} else {
|
|
381
|
-
metrics.failureCount++;
|
|
382
|
-
const errorType = result.error || 'unknown';
|
|
383
|
-
metrics.errorTypes[errorType] = (metrics.errorTypes[errorType] || 0) + 1;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
metrics.algorithms[result.algorithm] = (metrics.algorithms[result.algorithm] || 0) + 1;
|
|
387
|
-
|
|
388
|
-
return result;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// Report metrics periodically
|
|
392
|
-
setInterval(() => {
|
|
393
|
-
console.log('Webhook validation metrics:', metrics);
|
|
394
|
-
}, 60000); // Every minute
|
|
395
|
-
```
|
|
396
|
-
|
|
397
|
-
---
|
|
398
|
-
|
|
399
|
-
## Error Recovery Strategies
|
|
400
|
-
|
|
401
|
-
### Strategy 1: Retry with Fallback Algorithm
|
|
402
|
-
|
|
403
|
-
```typescript
|
|
404
|
-
async function validateWithFallback(
|
|
405
|
-
rawBody: string,
|
|
406
|
-
headers: Record<string, string>,
|
|
407
|
-
publicKey: string
|
|
408
|
-
): Promise<WebhookValidationResult> {
|
|
409
|
-
// Try auto-detection first
|
|
410
|
-
let result = await validator.validateWebhook(rawBody, headers, publicKey);
|
|
411
|
-
|
|
412
|
-
if (!result.isValid && result.error?.includes('No signature header')) {
|
|
413
|
-
// Try manual algorithm selection
|
|
414
|
-
if (headers['fluent-signature']) {
|
|
415
|
-
result = await validator.validateWebhookSignature(
|
|
416
|
-
rawBody,
|
|
417
|
-
headers['fluent-signature'],
|
|
418
|
-
publicKey,
|
|
419
|
-
SignatureAlgorithm.SHA512_WITH_RSA
|
|
420
|
-
);
|
|
421
|
-
} else if (headers['flex.signature']) {
|
|
422
|
-
result = await validator.validateWebhookSignature(
|
|
423
|
-
rawBody,
|
|
424
|
-
headers['flex.signature'],
|
|
425
|
-
publicKey,
|
|
426
|
-
SignatureAlgorithm.MD5_WITH_RSA
|
|
427
|
-
);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
return result;
|
|
432
|
-
}
|
|
433
|
-
```
|
|
434
|
-
|
|
435
|
-
### Strategy 2: Environment-Specific Key Selection
|
|
436
|
-
|
|
437
|
-
```typescript
|
|
438
|
-
async function validateWithEnvironmentKey(
|
|
439
|
-
rawBody: string,
|
|
440
|
-
headers: Record<string, string>
|
|
441
|
-
): Promise<WebhookValidationResult> {
|
|
442
|
-
// Detect environment from webhook metadata
|
|
443
|
-
const webhookData = JSON.parse(rawBody);
|
|
444
|
-
const sourceEnv = webhookData.environment || 'production';
|
|
445
|
-
|
|
446
|
-
// Select appropriate public key
|
|
447
|
-
const publicKey = sourceEnv === 'production'
|
|
448
|
-
? process.env.FLUENT_WEBHOOK_PUBLIC_KEY_PROD!
|
|
449
|
-
: process.env.FLUENT_WEBHOOK_PUBLIC_KEY_SANDBOX!;
|
|
450
|
-
|
|
451
|
-
return await validator.validateWebhook(rawBody, headers, publicKey);
|
|
452
|
-
}
|
|
453
|
-
```
|
|
454
|
-
|
|
455
|
-
### Strategy 3: Graceful Degradation
|
|
456
|
-
|
|
457
|
-
```typescript
|
|
458
|
-
async function validateWithGracefulDegradation(
|
|
459
|
-
rawBody: string,
|
|
460
|
-
headers: Record<string, string>,
|
|
461
|
-
publicKey: string
|
|
462
|
-
): Promise<{ isValid: boolean; degraded: boolean }> {
|
|
463
|
-
try {
|
|
464
|
-
const result = await validator.validateWebhook(rawBody, headers, publicKey);
|
|
465
|
-
|
|
466
|
-
if (result.isValid) {
|
|
467
|
-
return { isValid: true, degraded: false };
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// In development, allow processing without signature
|
|
471
|
-
if (process.env.NODE_ENV === 'development') {
|
|
472
|
-
logger.warn('Allowing invalid webhook in development mode');
|
|
473
|
-
return { isValid: true, degraded: true };
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
return { isValid: false, degraded: false };
|
|
477
|
-
|
|
478
|
-
} catch (error) {
|
|
479
|
-
logger.error('Validation error', { error });
|
|
480
|
-
return { isValid: false, degraded: false };
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
```
|
|
484
|
-
|
|
485
|
-
---
|
|
486
|
-
|
|
487
|
-
## Complete Error Handling Example
|
|
488
|
-
|
|
489
|
-
```typescript
|
|
490
|
-
import {
|
|
491
|
-
WebhookValidationService,
|
|
492
|
-
WebhookValidationFactory,
|
|
493
|
-
WebhookValidationResult,
|
|
494
|
-
createConsoleLogger,
|
|
495
|
-
toStructuredLogger
|
|
496
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
497
|
-
|
|
498
|
-
async function handleWebhookWithComprehensiveErrorHandling(
|
|
499
|
-
rawBody: string,
|
|
500
|
-
headers: Record<string, string>
|
|
501
|
-
): Promise<{ statusCode: number; body: any }> {
|
|
502
|
-
const logger = toStructuredLogger(createConsoleLogger(), { service: 'webhook' });
|
|
503
|
-
const validator = WebhookValidationFactory.createProduction(logger);
|
|
504
|
-
|
|
505
|
-
try {
|
|
506
|
-
// 1. Check public key
|
|
507
|
-
const publicKey = process.env.FLUENT_WEBHOOK_PUBLIC_KEY;
|
|
508
|
-
if (!publicKey) {
|
|
509
|
-
logger.error('Missing FLUENT_WEBHOOK_PUBLIC_KEY environment variable');
|
|
510
|
-
return {
|
|
511
|
-
statusCode: 500,
|
|
512
|
-
body: { error: 'Server configuration error' }
|
|
513
|
-
};
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
// 2. Validate webhook
|
|
517
|
-
const result: WebhookValidationResult = await validator.validateWebhook(
|
|
518
|
-
rawBody,
|
|
519
|
-
headers,
|
|
520
|
-
publicKey
|
|
521
|
-
);
|
|
522
|
-
|
|
523
|
-
// 3. Handle validation failure
|
|
524
|
-
if (!result.isValid) {
|
|
525
|
-
logger.warn('Webhook validation failed', {
|
|
526
|
-
error: result.error,
|
|
527
|
-
algorithm: result.algorithm,
|
|
528
|
-
timestamp: result.timestamp.toISOString(),
|
|
529
|
-
headers: Object.keys(headers)
|
|
530
|
-
});
|
|
531
|
-
|
|
532
|
-
// Specific error handling
|
|
533
|
-
if (result.error?.includes('No signature header')) {
|
|
534
|
-
return {
|
|
535
|
-
statusCode: 400,
|
|
536
|
-
body: { error: 'Missing signature header' }
|
|
537
|
-
};
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
if (result.error?.includes('verification failed')) {
|
|
541
|
-
// Log security event
|
|
542
|
-
logger.error('SECURITY: Invalid webhook signature detected', {
|
|
543
|
-
sourceIp: headers['x-forwarded-for'] || 'unknown'
|
|
544
|
-
});
|
|
545
|
-
|
|
546
|
-
return {
|
|
547
|
-
statusCode: 401,
|
|
548
|
-
body: { error: 'Invalid signature' }
|
|
549
|
-
};
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
// Generic validation failure
|
|
553
|
-
return {
|
|
554
|
-
statusCode: 401,
|
|
555
|
-
body: { error: 'Validation failed', details: result.error }
|
|
556
|
-
};
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
// 4. Process valid webhook
|
|
560
|
-
logger.info('Webhook validated successfully', {
|
|
561
|
-
algorithm: result.algorithm,
|
|
562
|
-
timestamp: result.timestamp.toISOString()
|
|
563
|
-
});
|
|
564
|
-
|
|
565
|
-
const data = JSON.parse(rawBody);
|
|
566
|
-
await processWebhook(data);
|
|
567
|
-
|
|
568
|
-
return {
|
|
569
|
-
statusCode: 200,
|
|
570
|
-
body: { success: true, eventId: data.eventId }
|
|
571
|
-
};
|
|
572
|
-
|
|
573
|
-
} catch (error: any) {
|
|
574
|
-
logger.error('Unexpected error processing webhook', { error: error.message });
|
|
575
|
-
return {
|
|
576
|
-
statusCode: 500,
|
|
577
|
-
body: { error: 'Internal server error' }
|
|
578
|
-
};
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
async function processWebhook(data: any): Promise<void> {
|
|
583
|
-
console.log('Processing webhook:', data);
|
|
584
|
-
}
|
|
585
|
-
```
|
|
586
|
-
|
|
587
|
-
---
|
|
588
|
-
|
|
589
|
-
## Key Takeaways
|
|
590
|
-
|
|
591
|
-
- **Check isValid first** - always validate before processing
|
|
592
|
-
- **Log validation failures** - track security events and patterns
|
|
593
|
-
- **Return appropriate HTTP codes** - 400 for missing signature, 401 for invalid signature, 500 for config errors
|
|
594
|
-
- **Use structured logging** - include error details, algorithm, timestamp
|
|
595
|
-
- **Monitor metrics** - track success/failure rates over time
|
|
596
|
-
- **Handle environment mismatches** - use correct public key for environment
|
|
597
|
-
- **Never log raw signatures** - security risk
|
|
598
|
-
|
|
599
|
-
---
|
|
600
|
-
|
|
601
|
-
## Next Steps
|
|
602
|
-
|
|
603
|
-
Continue to [Module 7: API Reference](../../auto-pagination/modules/auto-pagination-07-api-reference.md) for complete API documentation and TypeScript type definitions.
|
|
604
|
-
|
|
605
|
-
---
|
|
606
|
-
|
|
607
|
-
## Further Reading
|
|
608
|
-
|
|
609
|
-
- [Module 5: Configuration](./webhook-validation-05-configuration.md) - Configuration options
|
|
610
|
-
- [Module 2: Quick Start](../../auto-pagination/modules/auto-pagination-02-quick-start.md) - Basic usage patterns
|
|
611
|
-
- [Quick Reference](../../advanced-services/advanced-services-quick-reference.md) - One-page cheat sheet
|
|
1
|
+
# Module 6: Error Handling
|
|
2
|
+
|
|
3
|
+
**Level:** Intermediate
|
|
4
|
+
**Estimated Time:** 25 minutes
|
|
5
|
+
|
|
6
|
+
## Overview
|
|
7
|
+
|
|
8
|
+
This module covers comprehensive error handling for webhook validation, including validation result structures, common error scenarios, debugging techniques, and recovery strategies.
|
|
9
|
+
|
|
10
|
+
## Learning Objectives
|
|
11
|
+
|
|
12
|
+
By the end of this module, you will:
|
|
13
|
+
- Understand WebhookValidationResult structure
|
|
14
|
+
- Handle common validation error scenarios
|
|
15
|
+
- Debug validation failures effectively
|
|
16
|
+
- Implement logging and monitoring
|
|
17
|
+
- Build resilient webhook handlers
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Validation Result Structure
|
|
22
|
+
|
|
23
|
+
### WebhookValidationResult Interface
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
interface WebhookValidationResult {
|
|
27
|
+
/**
|
|
28
|
+
* Whether the webhook signature is valid
|
|
29
|
+
*/
|
|
30
|
+
isValid: boolean;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Algorithm used for validation
|
|
34
|
+
*/
|
|
35
|
+
algorithm: SignatureAlgorithm;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Error message if validation failed
|
|
39
|
+
* undefined if isValid === true
|
|
40
|
+
*/
|
|
41
|
+
error?: string;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Timestamp when validation occurred
|
|
45
|
+
*/
|
|
46
|
+
timestamp: Date;
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Success Result
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
const result = await validator.validateWebhook(rawBody, headers, publicKey);
|
|
54
|
+
|
|
55
|
+
if (result.isValid) {
|
|
56
|
+
console.log({
|
|
57
|
+
isValid: true,
|
|
58
|
+
algorithm: 'SHA512withRSA', // or 'MD5withRSA'
|
|
59
|
+
timestamp: '2025-01-14T10:30:00.000Z',
|
|
60
|
+
error: undefined
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Failure Result
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
const result = await validator.validateWebhook(rawBody, headers, publicKey);
|
|
69
|
+
|
|
70
|
+
if (!result.isValid) {
|
|
71
|
+
console.log({
|
|
72
|
+
isValid: false,
|
|
73
|
+
algorithm: 'SHA512withRSA', // Attempted algorithm
|
|
74
|
+
error: 'Signature verification failed',
|
|
75
|
+
timestamp: '2025-01-14T10:30:00.000Z'
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Common Error Scenarios
|
|
83
|
+
|
|
84
|
+
### Error 1: Missing Signature Header
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
// Webhook request without signature header
|
|
88
|
+
const headers = {
|
|
89
|
+
'content-type': 'application/json'
|
|
90
|
+
// No 'fluent-signature' or 'flex.signature'
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const result = await validator.validateWebhook(rawBody, headers, publicKey);
|
|
94
|
+
|
|
95
|
+
// Result:
|
|
96
|
+
{
|
|
97
|
+
isValid: false,
|
|
98
|
+
algorithm: 'SHA512withRSA', // Default algorithm
|
|
99
|
+
error: 'No signature header found',
|
|
100
|
+
timestamp: Date
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Causes**:
|
|
105
|
+
- Webhook not from Fluent Commerce Rubix
|
|
106
|
+
- Misconfigured Rubix workflow
|
|
107
|
+
- Testing with manually crafted requests
|
|
108
|
+
|
|
109
|
+
**Solution**:
|
|
110
|
+
```typescript
|
|
111
|
+
if (!result.isValid && result.error?.includes('No signature header')) {
|
|
112
|
+
logger.error('Webhook missing signature - not from Fluent Commerce');
|
|
113
|
+
return { statusCode: 400, body: 'Missing signature header' };
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Error 2: Invalid Signature
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
// Signature doesn't match payload
|
|
121
|
+
const result = await validator.validateWebhook(rawBody, headers, publicKey);
|
|
122
|
+
|
|
123
|
+
// Result:
|
|
124
|
+
{
|
|
125
|
+
isValid: false,
|
|
126
|
+
algorithm: 'SHA512withRSA',
|
|
127
|
+
error: 'Signature verification failed',
|
|
128
|
+
timestamp: Date
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Causes**:
|
|
133
|
+
- Wrong public key (production key used for sandbox webhook)
|
|
134
|
+
- Body was modified before validation
|
|
135
|
+
- Signature header corrupted in transit
|
|
136
|
+
- Webhook not from Fluent Commerce
|
|
137
|
+
|
|
138
|
+
**Solution**:
|
|
139
|
+
```typescript
|
|
140
|
+
if (!result.isValid && result.error?.includes('verification failed')) {
|
|
141
|
+
logger.warn('Invalid signature detected', {
|
|
142
|
+
error: result.error,
|
|
143
|
+
algorithm: result.algorithm,
|
|
144
|
+
hasSignature: !!headers['fluent-signature'] || !!headers['flex.signature'],
|
|
145
|
+
bodyLength: rawBody.length
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
return { statusCode: 401, body: 'Invalid signature' };
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Error 3: Missing Public Key
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
const publicKey = process.env.FLUENT_WEBHOOK_PUBLIC_KEY;
|
|
156
|
+
if (!publicKey) {
|
|
157
|
+
// This will throw error before validation
|
|
158
|
+
throw new Error('FLUENT_WEBHOOK_PUBLIC_KEY not configured');
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Causes**:
|
|
163
|
+
- Environment variable not set
|
|
164
|
+
- Configuration not loaded
|
|
165
|
+
- Versori variable not defined
|
|
166
|
+
|
|
167
|
+
**Solution**:
|
|
168
|
+
```typescript
|
|
169
|
+
const publicKey = process.env.FLUENT_WEBHOOK_PUBLIC_KEY;
|
|
170
|
+
if (!publicKey) {
|
|
171
|
+
logger.error('Missing public key configuration');
|
|
172
|
+
return { statusCode: 500, body: 'Server configuration error' };
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Error 4: Invalid Public Key Format
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
const result = await validator.validateWebhook(rawBody, headers, invalidKey);
|
|
180
|
+
|
|
181
|
+
// May throw error:
|
|
182
|
+
// "Invalid public key format"
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Causes**:
|
|
186
|
+
- Truncated public key
|
|
187
|
+
- Wrong key format (not PEM or base64)
|
|
188
|
+
- Corrupted key in storage
|
|
189
|
+
|
|
190
|
+
**Solution**:
|
|
191
|
+
```typescript
|
|
192
|
+
try {
|
|
193
|
+
const result = await validator.validateWebhook(rawBody, headers, publicKey);
|
|
194
|
+
} catch (error) {
|
|
195
|
+
if (error.message.includes('Invalid public key')) {
|
|
196
|
+
logger.error('Public key format error', { publicKeyPrefix: publicKey.substring(0, 30) });
|
|
197
|
+
return { statusCode: 500, body: 'Configuration error' };
|
|
198
|
+
}
|
|
199
|
+
throw error;
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Error 5: Body Modified Before Validation
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
// ❌ WRONG - Body was parsed and re-stringified
|
|
207
|
+
const data = JSON.parse(rawBody);
|
|
208
|
+
const modifiedBody = JSON.stringify(data); // May change whitespace, key order
|
|
209
|
+
|
|
210
|
+
const result = await validator.validateWebhook(modifiedBody, headers, publicKey);
|
|
211
|
+
// isValid: false, error: 'Signature verification failed'
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**Causes**:
|
|
215
|
+
- JSON parsed before validation
|
|
216
|
+
- Whitespace trimmed
|
|
217
|
+
- Character encoding changed
|
|
218
|
+
|
|
219
|
+
**Solution**:
|
|
220
|
+
```typescript
|
|
221
|
+
// ✅ CORRECT - Use original raw body
|
|
222
|
+
const rawBody = await request.text(); // Original bytes
|
|
223
|
+
const result = await validator.validateWebhook(rawBody, headers, publicKey);
|
|
224
|
+
|
|
225
|
+
if (result.isValid) {
|
|
226
|
+
const data = JSON.parse(rawBody); // Parse after validation
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Debugging Validation Failures
|
|
233
|
+
|
|
234
|
+
### Debug Pattern 1: Log All Validation Attempts
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
const result = await validator.validateWebhook(rawBody, headers, publicKey);
|
|
238
|
+
|
|
239
|
+
logger.info('Validation attempt', {
|
|
240
|
+
isValid: result.isValid,
|
|
241
|
+
algorithm: result.algorithm,
|
|
242
|
+
error: result.error,
|
|
243
|
+
timestamp: result.timestamp,
|
|
244
|
+
bodyLength: rawBody.length,
|
|
245
|
+
headers: Object.keys(headers),
|
|
246
|
+
hasFluentSig: !!headers['fluent-signature'],
|
|
247
|
+
hasFlexSig: !!headers['flex.signature'],
|
|
248
|
+
publicKeyPrefix: publicKey.substring(0, 30)
|
|
249
|
+
});
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Debug Pattern 2: Compare Algorithms
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
// Try both algorithms to see which should work
|
|
256
|
+
const sha512Result = await validator.validateWebhookSignature(
|
|
257
|
+
rawBody,
|
|
258
|
+
headers['fluent-signature'],
|
|
259
|
+
publicKey,
|
|
260
|
+
SignatureAlgorithm.SHA512_WITH_RSA
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
const md5Result = await validator.validateWebhookSignature(
|
|
264
|
+
rawBody,
|
|
265
|
+
headers['flex.signature'],
|
|
266
|
+
publicKey,
|
|
267
|
+
SignatureAlgorithm.MD5_WITH_RSA
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
console.log('SHA512 validation:', sha512Result);
|
|
271
|
+
console.log('MD5 validation:', md5Result);
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Debug Pattern 3: Validate Key Format
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
function validatePublicKeyFormat(publicKey: string): boolean {
|
|
278
|
+
// Check for PEM format
|
|
279
|
+
if (publicKey.includes('-----BEGIN PUBLIC KEY-----')) {
|
|
280
|
+
return publicKey.includes('-----END PUBLIC KEY-----');
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Check for base64 format
|
|
284
|
+
const base64Regex = /^[A-Za-z0-9+/]+=*$/;
|
|
285
|
+
return base64Regex.test(publicKey.trim());
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const isValidFormat = validatePublicKeyFormat(publicKey);
|
|
289
|
+
if (!isValidFormat) {
|
|
290
|
+
logger.error('Invalid public key format detected');
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Debug Pattern 4: Check Environment Mismatch
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
const result = await validator.validateWebhook(rawBody, headers, publicKey);
|
|
298
|
+
|
|
299
|
+
if (!result.isValid) {
|
|
300
|
+
logger.warn('Validation failed - check environment mismatch', {
|
|
301
|
+
error: result.error,
|
|
302
|
+
currentEnv: process.env.NODE_ENV,
|
|
303
|
+
fluentEnv: process.env.FLUENT_ENV,
|
|
304
|
+
keySource: 'Check if production key used for sandbox webhook'
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## Logging and Monitoring
|
|
312
|
+
|
|
313
|
+
### Security Event Logging
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
async function validateAndLogWebhook(
|
|
317
|
+
rawBody: string,
|
|
318
|
+
headers: Record<string, string>,
|
|
319
|
+
publicKey: string,
|
|
320
|
+
logger: Logger
|
|
321
|
+
): Promise<WebhookValidationResult> {
|
|
322
|
+
const result = await validator.validateWebhook(rawBody, headers, publicKey);
|
|
323
|
+
|
|
324
|
+
if (!result.isValid) {
|
|
325
|
+
// Log security event
|
|
326
|
+
logger.warn('Webhook validation failed', {
|
|
327
|
+
severity: 'SECURITY',
|
|
328
|
+
error: result.error,
|
|
329
|
+
algorithm: result.algorithm,
|
|
330
|
+
timestamp: result.timestamp.toISOString(),
|
|
331
|
+
sourceIp: headers['x-forwarded-for'] || headers['x-real-ip'] || 'unknown',
|
|
332
|
+
userAgent: headers['user-agent'] || 'unknown',
|
|
333
|
+
signaturePresent: !!headers['fluent-signature'] || !!headers['flex.signature']
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
// Optionally alert security team
|
|
337
|
+
if (await shouldAlertSecurity(result.error)) {
|
|
338
|
+
await alertSecurityTeam({
|
|
339
|
+
event: 'Invalid webhook signature',
|
|
340
|
+
details: result.error,
|
|
341
|
+
timestamp: result.timestamp
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return result;
|
|
347
|
+
}
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### Metrics Collection
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
interface ValidationMetrics {
|
|
354
|
+
totalAttempts: number;
|
|
355
|
+
successCount: number;
|
|
356
|
+
failureCount: number;
|
|
357
|
+
errorTypes: Record<string, number>;
|
|
358
|
+
algorithms: Record<string, number>;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const metrics: ValidationMetrics = {
|
|
362
|
+
totalAttempts: 0,
|
|
363
|
+
successCount: 0,
|
|
364
|
+
failureCount: 0,
|
|
365
|
+
errorTypes: {},
|
|
366
|
+
algorithms: {}
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
async function validateWithMetrics(
|
|
370
|
+
rawBody: string,
|
|
371
|
+
headers: Record<string, string>,
|
|
372
|
+
publicKey: string
|
|
373
|
+
): Promise<WebhookValidationResult> {
|
|
374
|
+
const result = await validator.validateWebhook(rawBody, headers, publicKey);
|
|
375
|
+
|
|
376
|
+
metrics.totalAttempts++;
|
|
377
|
+
|
|
378
|
+
if (result.isValid) {
|
|
379
|
+
metrics.successCount++;
|
|
380
|
+
} else {
|
|
381
|
+
metrics.failureCount++;
|
|
382
|
+
const errorType = result.error || 'unknown';
|
|
383
|
+
metrics.errorTypes[errorType] = (metrics.errorTypes[errorType] || 0) + 1;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
metrics.algorithms[result.algorithm] = (metrics.algorithms[result.algorithm] || 0) + 1;
|
|
387
|
+
|
|
388
|
+
return result;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Report metrics periodically
|
|
392
|
+
setInterval(() => {
|
|
393
|
+
console.log('Webhook validation metrics:', metrics);
|
|
394
|
+
}, 60000); // Every minute
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
## Error Recovery Strategies
|
|
400
|
+
|
|
401
|
+
### Strategy 1: Retry with Fallback Algorithm
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
async function validateWithFallback(
|
|
405
|
+
rawBody: string,
|
|
406
|
+
headers: Record<string, string>,
|
|
407
|
+
publicKey: string
|
|
408
|
+
): Promise<WebhookValidationResult> {
|
|
409
|
+
// Try auto-detection first
|
|
410
|
+
let result = await validator.validateWebhook(rawBody, headers, publicKey);
|
|
411
|
+
|
|
412
|
+
if (!result.isValid && result.error?.includes('No signature header')) {
|
|
413
|
+
// Try manual algorithm selection
|
|
414
|
+
if (headers['fluent-signature']) {
|
|
415
|
+
result = await validator.validateWebhookSignature(
|
|
416
|
+
rawBody,
|
|
417
|
+
headers['fluent-signature'],
|
|
418
|
+
publicKey,
|
|
419
|
+
SignatureAlgorithm.SHA512_WITH_RSA
|
|
420
|
+
);
|
|
421
|
+
} else if (headers['flex.signature']) {
|
|
422
|
+
result = await validator.validateWebhookSignature(
|
|
423
|
+
rawBody,
|
|
424
|
+
headers['flex.signature'],
|
|
425
|
+
publicKey,
|
|
426
|
+
SignatureAlgorithm.MD5_WITH_RSA
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return result;
|
|
432
|
+
}
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
### Strategy 2: Environment-Specific Key Selection
|
|
436
|
+
|
|
437
|
+
```typescript
|
|
438
|
+
async function validateWithEnvironmentKey(
|
|
439
|
+
rawBody: string,
|
|
440
|
+
headers: Record<string, string>
|
|
441
|
+
): Promise<WebhookValidationResult> {
|
|
442
|
+
// Detect environment from webhook metadata
|
|
443
|
+
const webhookData = JSON.parse(rawBody);
|
|
444
|
+
const sourceEnv = webhookData.environment || 'production';
|
|
445
|
+
|
|
446
|
+
// Select appropriate public key
|
|
447
|
+
const publicKey = sourceEnv === 'production'
|
|
448
|
+
? process.env.FLUENT_WEBHOOK_PUBLIC_KEY_PROD!
|
|
449
|
+
: process.env.FLUENT_WEBHOOK_PUBLIC_KEY_SANDBOX!;
|
|
450
|
+
|
|
451
|
+
return await validator.validateWebhook(rawBody, headers, publicKey);
|
|
452
|
+
}
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### Strategy 3: Graceful Degradation
|
|
456
|
+
|
|
457
|
+
```typescript
|
|
458
|
+
async function validateWithGracefulDegradation(
|
|
459
|
+
rawBody: string,
|
|
460
|
+
headers: Record<string, string>,
|
|
461
|
+
publicKey: string
|
|
462
|
+
): Promise<{ isValid: boolean; degraded: boolean }> {
|
|
463
|
+
try {
|
|
464
|
+
const result = await validator.validateWebhook(rawBody, headers, publicKey);
|
|
465
|
+
|
|
466
|
+
if (result.isValid) {
|
|
467
|
+
return { isValid: true, degraded: false };
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// In development, allow processing without signature
|
|
471
|
+
if (process.env.NODE_ENV === 'development') {
|
|
472
|
+
logger.warn('Allowing invalid webhook in development mode');
|
|
473
|
+
return { isValid: true, degraded: true };
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
return { isValid: false, degraded: false };
|
|
477
|
+
|
|
478
|
+
} catch (error) {
|
|
479
|
+
logger.error('Validation error', { error });
|
|
480
|
+
return { isValid: false, degraded: false };
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
---
|
|
486
|
+
|
|
487
|
+
## Complete Error Handling Example
|
|
488
|
+
|
|
489
|
+
```typescript
|
|
490
|
+
import {
|
|
491
|
+
WebhookValidationService,
|
|
492
|
+
WebhookValidationFactory,
|
|
493
|
+
WebhookValidationResult,
|
|
494
|
+
createConsoleLogger,
|
|
495
|
+
toStructuredLogger
|
|
496
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
497
|
+
|
|
498
|
+
async function handleWebhookWithComprehensiveErrorHandling(
|
|
499
|
+
rawBody: string,
|
|
500
|
+
headers: Record<string, string>
|
|
501
|
+
): Promise<{ statusCode: number; body: any }> {
|
|
502
|
+
const logger = toStructuredLogger(createConsoleLogger(), { service: 'webhook' });
|
|
503
|
+
const validator = WebhookValidationFactory.createProduction(logger);
|
|
504
|
+
|
|
505
|
+
try {
|
|
506
|
+
// 1. Check public key
|
|
507
|
+
const publicKey = process.env.FLUENT_WEBHOOK_PUBLIC_KEY;
|
|
508
|
+
if (!publicKey) {
|
|
509
|
+
logger.error('Missing FLUENT_WEBHOOK_PUBLIC_KEY environment variable');
|
|
510
|
+
return {
|
|
511
|
+
statusCode: 500,
|
|
512
|
+
body: { error: 'Server configuration error' }
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// 2. Validate webhook
|
|
517
|
+
const result: WebhookValidationResult = await validator.validateWebhook(
|
|
518
|
+
rawBody,
|
|
519
|
+
headers,
|
|
520
|
+
publicKey
|
|
521
|
+
);
|
|
522
|
+
|
|
523
|
+
// 3. Handle validation failure
|
|
524
|
+
if (!result.isValid) {
|
|
525
|
+
logger.warn('Webhook validation failed', {
|
|
526
|
+
error: result.error,
|
|
527
|
+
algorithm: result.algorithm,
|
|
528
|
+
timestamp: result.timestamp.toISOString(),
|
|
529
|
+
headers: Object.keys(headers)
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
// Specific error handling
|
|
533
|
+
if (result.error?.includes('No signature header')) {
|
|
534
|
+
return {
|
|
535
|
+
statusCode: 400,
|
|
536
|
+
body: { error: 'Missing signature header' }
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (result.error?.includes('verification failed')) {
|
|
541
|
+
// Log security event
|
|
542
|
+
logger.error('SECURITY: Invalid webhook signature detected', {
|
|
543
|
+
sourceIp: headers['x-forwarded-for'] || 'unknown'
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
return {
|
|
547
|
+
statusCode: 401,
|
|
548
|
+
body: { error: 'Invalid signature' }
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// Generic validation failure
|
|
553
|
+
return {
|
|
554
|
+
statusCode: 401,
|
|
555
|
+
body: { error: 'Validation failed', details: result.error }
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// 4. Process valid webhook
|
|
560
|
+
logger.info('Webhook validated successfully', {
|
|
561
|
+
algorithm: result.algorithm,
|
|
562
|
+
timestamp: result.timestamp.toISOString()
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
const data = JSON.parse(rawBody);
|
|
566
|
+
await processWebhook(data);
|
|
567
|
+
|
|
568
|
+
return {
|
|
569
|
+
statusCode: 200,
|
|
570
|
+
body: { success: true, eventId: data.eventId }
|
|
571
|
+
};
|
|
572
|
+
|
|
573
|
+
} catch (error: any) {
|
|
574
|
+
logger.error('Unexpected error processing webhook', { error: error.message });
|
|
575
|
+
return {
|
|
576
|
+
statusCode: 500,
|
|
577
|
+
body: { error: 'Internal server error' }
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
async function processWebhook(data: any): Promise<void> {
|
|
583
|
+
console.log('Processing webhook:', data);
|
|
584
|
+
}
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
---
|
|
588
|
+
|
|
589
|
+
## Key Takeaways
|
|
590
|
+
|
|
591
|
+
- **Check isValid first** - always validate before processing
|
|
592
|
+
- **Log validation failures** - track security events and patterns
|
|
593
|
+
- **Return appropriate HTTP codes** - 400 for missing signature, 401 for invalid signature, 500 for config errors
|
|
594
|
+
- **Use structured logging** - include error details, algorithm, timestamp
|
|
595
|
+
- **Monitor metrics** - track success/failure rates over time
|
|
596
|
+
- **Handle environment mismatches** - use correct public key for environment
|
|
597
|
+
- **Never log raw signatures** - security risk
|
|
598
|
+
|
|
599
|
+
---
|
|
600
|
+
|
|
601
|
+
## Next Steps
|
|
602
|
+
|
|
603
|
+
Continue to [Module 7: API Reference](../../auto-pagination/modules/auto-pagination-07-api-reference.md) for complete API documentation and TypeScript type definitions.
|
|
604
|
+
|
|
605
|
+
---
|
|
606
|
+
|
|
607
|
+
## Further Reading
|
|
608
|
+
|
|
609
|
+
- [Module 5: Configuration](./webhook-validation-05-configuration.md) - Configuration options
|
|
610
|
+
- [Module 2: Quick Start](../../auto-pagination/modules/auto-pagination-02-quick-start.md) - Basic usage patterns
|
|
611
|
+
- [Quick Reference](../../advanced-services/advanced-services-quick-reference.md) - One-page cheat sheet
|