@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,514 +1,514 @@
|
|
|
1
|
-
# Module 09: Webhook Validation
|
|
2
|
-
|
|
3
|
-
**Level:** Intermediate
|
|
4
|
-
**Category:** Security & Integration
|
|
5
|
-
|
|
6
|
-
## Overview
|
|
7
|
-
|
|
8
|
-
Webhook validation ensures that incoming webhook requests from Fluent Commerce are authentic and haven't been tampered with. The SDK provides `parseWebhookRequest()` for HMAC signature verification, preventing security vulnerabilities from forged webhooks.
|
|
9
|
-
|
|
10
|
-
## Table of Contents
|
|
11
|
-
|
|
12
|
-
- [Why Webhook Validation](#why-webhook-validation)
|
|
13
|
-
- [parseWebhookRequest() Function](#parsewebhookrequest-function)
|
|
14
|
-
- [HMAC Signature Verification](#hmac-signature-verification)
|
|
15
|
-
- [Webhook Security](#webhook-security)
|
|
16
|
-
- [Platform Integration](#platform-integration)
|
|
17
|
-
- [Error Handling](#error-handling)
|
|
18
|
-
- [Common Patterns](#common-patterns)
|
|
19
|
-
- [Troubleshooting](#troubleshooting)
|
|
20
|
-
- [See Also](#see-also)
|
|
21
|
-
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
## Why Webhook Validation
|
|
25
|
-
|
|
26
|
-
Without validation, malicious actors can:
|
|
27
|
-
- Send forged events to trigger unintended actions
|
|
28
|
-
- Inject malicious payloads
|
|
29
|
-
- Cause data corruption or unauthorized access
|
|
30
|
-
- Perform denial-of-service attacks
|
|
31
|
-
|
|
32
|
-
**Security Benefits:**
|
|
33
|
-
- Cryptographic proof of authenticity
|
|
34
|
-
- Tamper detection
|
|
35
|
-
- Replay attack prevention
|
|
36
|
-
- Integration integrity
|
|
37
|
-
|
|
38
|
-
---
|
|
39
|
-
|
|
40
|
-
## parseWebhookRequest() Function
|
|
41
|
-
|
|
42
|
-
The SDK provides a top-level function for webhook validation:
|
|
43
|
-
|
|
44
|
-
```typescript
|
|
45
|
-
import { parseWebhookRequest } from '@fluentcommerce/fc-connect-sdk';
|
|
46
|
-
|
|
47
|
-
// Validate incoming webhook
|
|
48
|
-
const result = await parseWebhookRequest({
|
|
49
|
-
body: requestBody, // Raw request body (string or Buffer)
|
|
50
|
-
signature: headerValue, // X-Fluent-Signature header
|
|
51
|
-
secret: process.env.WEBHOOK_SECRET, // Your webhook secret
|
|
52
|
-
timestamp: Date.now(), // Current timestamp
|
|
53
|
-
toleranceMs: 300000 // 5 minutes tolerance (default)
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
if (result.valid) {
|
|
57
|
-
console.log('Webhook authenticated:', result.payload);
|
|
58
|
-
// Process webhook event...
|
|
59
|
-
} else {
|
|
60
|
-
console.error('Invalid webhook:', result.error);
|
|
61
|
-
// Reject request with 401
|
|
62
|
-
}
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
**Function Signature:**
|
|
66
|
-
|
|
67
|
-
```typescript
|
|
68
|
-
function parseWebhookRequest(options: {
|
|
69
|
-
body: string | Buffer; // Request body
|
|
70
|
-
signature: string; // X-Fluent-Signature header
|
|
71
|
-
secret: string; // Webhook secret from Fluent
|
|
72
|
-
timestamp?: number; // Current time (default: Date.now())
|
|
73
|
-
toleranceMs?: number; // Clock skew tolerance (default: 300000)
|
|
74
|
-
}): Promise<WebhookValidationResult>;
|
|
75
|
-
|
|
76
|
-
interface WebhookValidationResult {
|
|
77
|
-
valid: boolean; // True if signature valid
|
|
78
|
-
payload: any; // Parsed webhook payload (if valid)
|
|
79
|
-
error?: string; // Error message (if invalid)
|
|
80
|
-
timestamp?: number; // Webhook timestamp
|
|
81
|
-
}
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
---
|
|
85
|
-
|
|
86
|
-
## HMAC Signature Verification
|
|
87
|
-
|
|
88
|
-
Fluent Commerce signs webhooks using HMAC-SHA256:
|
|
89
|
-
|
|
90
|
-
### How It Works
|
|
91
|
-
|
|
92
|
-
1. **Fluent sends webhook:**
|
|
93
|
-
- Generates HMAC signature: `HMAC-SHA256(secret, payload)`
|
|
94
|
-
- Includes signature in `X-Fluent-Signature` header
|
|
95
|
-
|
|
96
|
-
2. **Your service receives webhook:**
|
|
97
|
-
- Extracts body and signature
|
|
98
|
-
- Recomputes HMAC with your secret
|
|
99
|
-
- Compares computed signature with header value
|
|
100
|
-
|
|
101
|
-
3. **SDK validates:**
|
|
102
|
-
- Parses signature format: `t=timestamp,v1=signature`
|
|
103
|
-
- Checks timestamp within tolerance window
|
|
104
|
-
- Verifies HMAC matches
|
|
105
|
-
|
|
106
|
-
### Signature Format
|
|
107
|
-
|
|
108
|
-
```
|
|
109
|
-
X-Fluent-Signature: t=1640995200,v1=abc123def456...
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
**Components:**
|
|
113
|
-
- `t`: Unix timestamp when webhook sent
|
|
114
|
-
- `v1`: HMAC-SHA256 signature (hex)
|
|
115
|
-
|
|
116
|
-
### Verification Algorithm
|
|
117
|
-
|
|
118
|
-
```typescript
|
|
119
|
-
// Simplified (SDK does this internally)
|
|
120
|
-
function verifySignature(body: string, signature: string, secret: string): boolean {
|
|
121
|
-
// Parse signature header
|
|
122
|
-
const parts = signature.split(',');
|
|
123
|
-
const timestamp = parts.find(p => p.startsWith('t=')).split('=')[1];
|
|
124
|
-
const receivedSig = parts.find(p => p.startsWith('v1=')).split('=')[1];
|
|
125
|
-
|
|
126
|
-
// Compute expected signature
|
|
127
|
-
const payload = `${timestamp}.${body}`;
|
|
128
|
-
const expectedSig = crypto
|
|
129
|
-
.createHmac('sha256', secret)
|
|
130
|
-
.update(payload)
|
|
131
|
-
.digest('hex');
|
|
132
|
-
|
|
133
|
-
// Constant-time comparison
|
|
134
|
-
return crypto.timingSafeEqual(
|
|
135
|
-
Buffer.from(receivedSig, 'hex'),
|
|
136
|
-
Buffer.from(expectedSig, 'hex')
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
---
|
|
142
|
-
|
|
143
|
-
## Webhook Security
|
|
144
|
-
|
|
145
|
-
### Best Practices
|
|
146
|
-
|
|
147
|
-
**1. Always Validate Signatures**
|
|
148
|
-
|
|
149
|
-
```typescript
|
|
150
|
-
// ✅ CORRECT: Validate before processing
|
|
151
|
-
const result = await parseWebhookRequest({ body, signature, secret });
|
|
152
|
-
if (!result.valid) {
|
|
153
|
-
return new Response('Unauthorized', { status: 401 });
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Process event...
|
|
157
|
-
|
|
158
|
-
// ❌ WRONG: Process without validation
|
|
159
|
-
const payload = JSON.parse(body);
|
|
160
|
-
await processEvent(payload); // Insecure!
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
**2. Use Environment Variables for Secrets**
|
|
164
|
-
|
|
165
|
-
```typescript
|
|
166
|
-
// ✅ CORRECT: Secret from environment
|
|
167
|
-
const secret = process.env.FLUENT_WEBHOOK_SECRET;
|
|
168
|
-
|
|
169
|
-
// ❌ WRONG: Hardcoded secret
|
|
170
|
-
const secret = 'my-secret-key'; // Never do this!
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
**3. Set Appropriate Tolerance**
|
|
174
|
-
|
|
175
|
-
```typescript
|
|
176
|
-
// ✅ CORRECT: 5-minute tolerance (default)
|
|
177
|
-
const result = await parseWebhookRequest({
|
|
178
|
-
body,
|
|
179
|
-
signature,
|
|
180
|
-
secret,
|
|
181
|
-
toleranceMs: 300000 // 5 minutes
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
// ❌ WRONG: Too permissive
|
|
185
|
-
const result = await parseWebhookRequest({
|
|
186
|
-
body,
|
|
187
|
-
signature,
|
|
188
|
-
secret,
|
|
189
|
-
toleranceMs: 3600000 // 1 hour - allows replay attacks!
|
|
190
|
-
});
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
**4. Reject Invalid Webhooks**
|
|
194
|
-
|
|
195
|
-
```typescript
|
|
196
|
-
// ✅ CORRECT: Return 401 for invalid signatures
|
|
197
|
-
if (!result.valid) {
|
|
198
|
-
console.warn('Invalid webhook signature:', result.error);
|
|
199
|
-
return new Response('Unauthorized', { status: 401 });
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// ❌ WRONG: Process anyway
|
|
203
|
-
if (!result.valid) {
|
|
204
|
-
console.warn('Invalid signature, but processing anyway...');
|
|
205
|
-
await processEvent(result.payload); // Dangerous!
|
|
206
|
-
}
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
### Common Security Mistakes
|
|
210
|
-
|
|
211
|
-
| Mistake | Risk | Fix |
|
|
212
|
-
|---------|------|-----|
|
|
213
|
-
| Skip validation | Forged requests | Always validate |
|
|
214
|
-
| Hardcode secrets | Secret leakage | Use environment variables |
|
|
215
|
-
| Large tolerance | Replay attacks | Use 5-minute default |
|
|
216
|
-
| Ignore errors | Data corruption | Return 401 on failure |
|
|
217
|
-
|
|
218
|
-
---
|
|
219
|
-
|
|
220
|
-
## Platform Integration
|
|
221
|
-
|
|
222
|
-
### Versori Webhooks
|
|
223
|
-
|
|
224
|
-
```typescript
|
|
225
|
-
import { webhook } from '@versori/run';
|
|
226
|
-
import { parseWebhookRequest } from '@fluentcommerce/fc-connect-sdk';
|
|
227
|
-
|
|
228
|
-
export const fluentWebhook = webhook('fluent-events', async (req, ctx) => {
|
|
229
|
-
const { log } = ctx;
|
|
230
|
-
|
|
231
|
-
// Extract signature header
|
|
232
|
-
const signature = req.headers.get('X-Fluent-Signature');
|
|
233
|
-
if (!signature) {
|
|
234
|
-
return new Response('Missing signature', { status: 401 });
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Validate webhook
|
|
238
|
-
const result = await parseWebhookRequest({
|
|
239
|
-
body: await req.text(),
|
|
240
|
-
signature,
|
|
241
|
-
secret: process.env.FLUENT_WEBHOOK_SECRET
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
if (!result.valid) {
|
|
245
|
-
log.warn('Invalid webhook', { error: result.error });
|
|
246
|
-
return new Response('Unauthorized', { status: 401 });
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// Process validated event
|
|
250
|
-
log.info('Webhook received', { event: result.payload.eventName });
|
|
251
|
-
await processFluentEvent(result.payload);
|
|
252
|
-
|
|
253
|
-
return new Response('OK', { status: 200 });
|
|
254
|
-
});
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
### Node.js Express
|
|
258
|
-
|
|
259
|
-
```typescript
|
|
260
|
-
import express from 'express';
|
|
261
|
-
import { parseWebhookRequest } from '@fluentcommerce/fc-connect-sdk';
|
|
262
|
-
|
|
263
|
-
const app = express();
|
|
264
|
-
|
|
265
|
-
// IMPORTANT: Use raw body for signature verification
|
|
266
|
-
app.post('/webhooks/fluent', express.raw({ type: 'application/json' }), async (req, res) => {
|
|
267
|
-
const signature = req.headers['x-fluent-signature'];
|
|
268
|
-
|
|
269
|
-
const result = await parseWebhookRequest({
|
|
270
|
-
body: req.body, // Raw buffer
|
|
271
|
-
signature,
|
|
272
|
-
secret: process.env.FLUENT_WEBHOOK_SECRET
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
if (!result.valid) {
|
|
276
|
-
return res.status(401).send('Unauthorized');
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
await processEvent(result.payload);
|
|
280
|
-
res.status(200).send('OK');
|
|
281
|
-
});
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
### Deno HTTP Server
|
|
285
|
-
|
|
286
|
-
```typescript
|
|
287
|
-
import { serve } from 'https://deno.land/std/http/server.ts';
|
|
288
|
-
import { parseWebhookRequest } from '@fluentcommerce/fc-connect-sdk';
|
|
289
|
-
|
|
290
|
-
serve(async (req) => {
|
|
291
|
-
if (req.url === '/webhooks/fluent') {
|
|
292
|
-
const signature = req.headers.get('X-Fluent-Signature');
|
|
293
|
-
const body = await req.text();
|
|
294
|
-
|
|
295
|
-
const result = await parseWebhookRequest({
|
|
296
|
-
body,
|
|
297
|
-
signature,
|
|
298
|
-
secret: Deno.env.get('FLUENT_WEBHOOK_SECRET')
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
if (!result.valid) {
|
|
302
|
-
return new Response('Unauthorized', { status: 401 });
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
await processEvent(result.payload);
|
|
306
|
-
return new Response('OK', { status: 200 });
|
|
307
|
-
}
|
|
308
|
-
});
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
---
|
|
312
|
-
|
|
313
|
-
## Error Handling
|
|
314
|
-
|
|
315
|
-
### Common Errors
|
|
316
|
-
|
|
317
|
-
```typescript
|
|
318
|
-
const result = await parseWebhookRequest({ body, signature, secret });
|
|
319
|
-
|
|
320
|
-
if (!result.valid) {
|
|
321
|
-
switch (result.error) {
|
|
322
|
-
case 'Missing signature':
|
|
323
|
-
// No X-Fluent-Signature header
|
|
324
|
-
return new Response('Missing signature header', { status: 400 });
|
|
325
|
-
|
|
326
|
-
case 'Invalid signature format':
|
|
327
|
-
// Malformed signature (not t=...,v1=...)
|
|
328
|
-
return new Response('Bad signature format', { status: 400 });
|
|
329
|
-
|
|
330
|
-
case 'Timestamp too old':
|
|
331
|
-
// Request older than tolerance window
|
|
332
|
-
return new Response('Request expired', { status: 401 });
|
|
333
|
-
|
|
334
|
-
case 'Signature mismatch':
|
|
335
|
-
// HMAC doesn't match (wrong secret or tampered body)
|
|
336
|
-
return new Response('Unauthorized', { status: 401 });
|
|
337
|
-
|
|
338
|
-
default:
|
|
339
|
-
return new Response('Validation failed', { status: 401 });
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
### Logging Validation Failures
|
|
345
|
-
|
|
346
|
-
```typescript
|
|
347
|
-
const result = await parseWebhookRequest({ body, signature, secret });
|
|
348
|
-
|
|
349
|
-
if (!result.valid) {
|
|
350
|
-
// Log for security monitoring
|
|
351
|
-
log.warn('Webhook validation failed', {
|
|
352
|
-
error: result.error,
|
|
353
|
-
signature: signature.substring(0, 20) + '...', // Truncate for logs
|
|
354
|
-
bodyLength: body.length,
|
|
355
|
-
timestamp: result.timestamp,
|
|
356
|
-
ip: req.headers.get('X-Forwarded-For')
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
return new Response('Unauthorized', { status: 401 });
|
|
360
|
-
}
|
|
361
|
-
```
|
|
362
|
-
|
|
363
|
-
---
|
|
364
|
-
|
|
365
|
-
## Common Patterns
|
|
366
|
-
|
|
367
|
-
### Pattern 1: Idempotent Processing
|
|
368
|
-
|
|
369
|
-
```typescript
|
|
370
|
-
const result = await parseWebhookRequest({ body, signature, secret });
|
|
371
|
-
if (!result.valid) return unauthorized();
|
|
372
|
-
|
|
373
|
-
const eventId = result.payload.id;
|
|
374
|
-
|
|
375
|
-
// Check if already processed
|
|
376
|
-
if (await stateService.isEventProcessed(eventId)) {
|
|
377
|
-
return new Response('Already processed', { status: 200 });
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
// Process event
|
|
381
|
-
await processEvent(result.payload);
|
|
382
|
-
|
|
383
|
-
// Mark as processed
|
|
384
|
-
await stateService.markEventProcessed(eventId);
|
|
385
|
-
|
|
386
|
-
return new Response('OK', { status: 200 });
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
### Pattern 2: Event Type Routing
|
|
390
|
-
|
|
391
|
-
```typescript
|
|
392
|
-
const result = await parseWebhookRequest({ body, signature, secret });
|
|
393
|
-
if (!result.valid) return unauthorized();
|
|
394
|
-
|
|
395
|
-
const { eventName, entity } = result.payload;
|
|
396
|
-
|
|
397
|
-
switch (eventName) {
|
|
398
|
-
case 'ORDER.CREATED':
|
|
399
|
-
await handleOrderCreated(entity);
|
|
400
|
-
break;
|
|
401
|
-
|
|
402
|
-
case 'FULFILMENT.UPDATED':
|
|
403
|
-
await handleFulfilmentUpdated(entity);
|
|
404
|
-
break;
|
|
405
|
-
|
|
406
|
-
case 'INVENTORY.ADJUSTED':
|
|
407
|
-
await handleInventoryAdjusted(entity);
|
|
408
|
-
break;
|
|
409
|
-
|
|
410
|
-
default:
|
|
411
|
-
console.log(`Unhandled event: ${eventName}`);
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
return new Response('OK', { status: 200 });
|
|
415
|
-
```
|
|
416
|
-
|
|
417
|
-
### Pattern 3: Async Processing
|
|
418
|
-
|
|
419
|
-
```typescript
|
|
420
|
-
const result = await parseWebhookRequest({ body, signature, secret });
|
|
421
|
-
if (!result.valid) return unauthorized();
|
|
422
|
-
|
|
423
|
-
// Acknowledge webhook immediately
|
|
424
|
-
queueForProcessing(result.payload);
|
|
425
|
-
|
|
426
|
-
return new Response('Accepted', { status: 202 });
|
|
427
|
-
|
|
428
|
-
// Process asynchronously
|
|
429
|
-
async function queueForProcessing(payload) {
|
|
430
|
-
await messageQueue.publish('fluent-events', payload);
|
|
431
|
-
}
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
---
|
|
435
|
-
|
|
436
|
-
## Troubleshooting
|
|
437
|
-
|
|
438
|
-
### Issue 1: Always Getting "Signature mismatch"
|
|
439
|
-
|
|
440
|
-
**Causes:**
|
|
441
|
-
- Wrong webhook secret
|
|
442
|
-
- Body modified before validation
|
|
443
|
-
- JSON parsing before validation
|
|
444
|
-
|
|
445
|
-
**Solutions:**
|
|
446
|
-
```typescript
|
|
447
|
-
// ✅ Use raw body for validation
|
|
448
|
-
app.post('/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
|
|
449
|
-
const result = await parseWebhookRequest({
|
|
450
|
-
body: req.body, // Raw buffer, not parsed JSON
|
|
451
|
-
signature: req.headers['x-fluent-signature'],
|
|
452
|
-
secret: process.env.FLUENT_WEBHOOK_SECRET
|
|
453
|
-
});
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
// ❌ WRONG: Parse JSON first
|
|
457
|
-
app.post('/webhook', express.json(), async (req, res) => {
|
|
458
|
-
// Body already parsed - signature will fail!
|
|
459
|
-
});
|
|
460
|
-
```
|
|
461
|
-
|
|
462
|
-
### Issue 2: "Timestamp too old" Errors
|
|
463
|
-
|
|
464
|
-
**Causes:**
|
|
465
|
-
- Server clock skew
|
|
466
|
-
- Network delays
|
|
467
|
-
- Tolerance too strict
|
|
468
|
-
|
|
469
|
-
**Solutions:**
|
|
470
|
-
```typescript
|
|
471
|
-
// ✅ Increase tolerance if needed
|
|
472
|
-
const result = await parseWebhookRequest({
|
|
473
|
-
body,
|
|
474
|
-
signature,
|
|
475
|
-
secret,
|
|
476
|
-
toleranceMs: 600000 // 10 minutes for slow networks
|
|
477
|
-
});
|
|
478
|
-
```
|
|
479
|
-
|
|
480
|
-
### Issue 3: Missing Signature Header
|
|
481
|
-
|
|
482
|
-
**Cause:** Header not forwarded by proxy/load balancer
|
|
483
|
-
|
|
484
|
-
**Solution:**
|
|
485
|
-
```typescript
|
|
486
|
-
// Check different header names
|
|
487
|
-
const signature =
|
|
488
|
-
req.headers.get('X-Fluent-Signature') ||
|
|
489
|
-
req.headers.get('x-fluent-signature') ||
|
|
490
|
-
req.headers.get('X-Fluent-Signature'.toLowerCase());
|
|
491
|
-
```
|
|
492
|
-
|
|
493
|
-
---
|
|
494
|
-
|
|
495
|
-
## See Also
|
|
496
|
-
|
|
497
|
-
**Related Modules:**
|
|
498
|
-
- [Module 01: Client API](./api-reference-01-client-api.md) - FluentClient creation
|
|
499
|
-
- [Module 11: Error Handling](./api-reference-11-error-handling.md) - Error strategies
|
|
500
|
-
|
|
501
|
-
**Complete Guides:**
|
|
502
|
-
- [Webhook Validation Guide](../../webhook-validation/webhook-validation-readme.md) - Full webhook validation series
|
|
503
|
-
- [Versori Webhook Patterns](../../../04-REFERENCE/platforms/versori/platforms-versori-webhook-connection-security.md)
|
|
504
|
-
|
|
505
|
-
**Templates:**
|
|
506
|
-
- [Generic XML Order Webhook](../../../01-TEMPLATES/versori/webhooks/template-webhook-generic-xml-order.md)
|
|
507
|
-
- [ASN Purchase Order Webhook](../../../01-TEMPLATES/versori/webhooks/template-webhook-asn-purchase-order.md)
|
|
508
|
-
|
|
509
|
-
---
|
|
510
|
-
|
|
511
|
-
**Navigation:**
|
|
512
|
-
- [Back to Module List](./readme.md)
|
|
513
|
-
- [Previous: Module 08 - Pagination](./api-reference-08-pagination.md)
|
|
514
|
-
- [Next: Module 10 - Extraction](./api-reference-10-extraction.md)
|
|
1
|
+
# Module 09: Webhook Validation
|
|
2
|
+
|
|
3
|
+
**Level:** Intermediate
|
|
4
|
+
**Category:** Security & Integration
|
|
5
|
+
|
|
6
|
+
## Overview
|
|
7
|
+
|
|
8
|
+
Webhook validation ensures that incoming webhook requests from Fluent Commerce are authentic and haven't been tampered with. The SDK provides `parseWebhookRequest()` for HMAC signature verification, preventing security vulnerabilities from forged webhooks.
|
|
9
|
+
|
|
10
|
+
## Table of Contents
|
|
11
|
+
|
|
12
|
+
- [Why Webhook Validation](#why-webhook-validation)
|
|
13
|
+
- [parseWebhookRequest() Function](#parsewebhookrequest-function)
|
|
14
|
+
- [HMAC Signature Verification](#hmac-signature-verification)
|
|
15
|
+
- [Webhook Security](#webhook-security)
|
|
16
|
+
- [Platform Integration](#platform-integration)
|
|
17
|
+
- [Error Handling](#error-handling)
|
|
18
|
+
- [Common Patterns](#common-patterns)
|
|
19
|
+
- [Troubleshooting](#troubleshooting)
|
|
20
|
+
- [See Also](#see-also)
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Why Webhook Validation
|
|
25
|
+
|
|
26
|
+
Without validation, malicious actors can:
|
|
27
|
+
- Send forged events to trigger unintended actions
|
|
28
|
+
- Inject malicious payloads
|
|
29
|
+
- Cause data corruption or unauthorized access
|
|
30
|
+
- Perform denial-of-service attacks
|
|
31
|
+
|
|
32
|
+
**Security Benefits:**
|
|
33
|
+
- Cryptographic proof of authenticity
|
|
34
|
+
- Tamper detection
|
|
35
|
+
- Replay attack prevention
|
|
36
|
+
- Integration integrity
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## parseWebhookRequest() Function
|
|
41
|
+
|
|
42
|
+
The SDK provides a top-level function for webhook validation:
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import { parseWebhookRequest } from '@fluentcommerce/fc-connect-sdk';
|
|
46
|
+
|
|
47
|
+
// Validate incoming webhook
|
|
48
|
+
const result = await parseWebhookRequest({
|
|
49
|
+
body: requestBody, // Raw request body (string or Buffer)
|
|
50
|
+
signature: headerValue, // X-Fluent-Signature header
|
|
51
|
+
secret: process.env.WEBHOOK_SECRET, // Your webhook secret
|
|
52
|
+
timestamp: Date.now(), // Current timestamp
|
|
53
|
+
toleranceMs: 300000 // 5 minutes tolerance (default)
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (result.valid) {
|
|
57
|
+
console.log('Webhook authenticated:', result.payload);
|
|
58
|
+
// Process webhook event...
|
|
59
|
+
} else {
|
|
60
|
+
console.error('Invalid webhook:', result.error);
|
|
61
|
+
// Reject request with 401
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Function Signature:**
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
function parseWebhookRequest(options: {
|
|
69
|
+
body: string | Buffer; // Request body
|
|
70
|
+
signature: string; // X-Fluent-Signature header
|
|
71
|
+
secret: string; // Webhook secret from Fluent
|
|
72
|
+
timestamp?: number; // Current time (default: Date.now())
|
|
73
|
+
toleranceMs?: number; // Clock skew tolerance (default: 300000)
|
|
74
|
+
}): Promise<WebhookValidationResult>;
|
|
75
|
+
|
|
76
|
+
interface WebhookValidationResult {
|
|
77
|
+
valid: boolean; // True if signature valid
|
|
78
|
+
payload: any; // Parsed webhook payload (if valid)
|
|
79
|
+
error?: string; // Error message (if invalid)
|
|
80
|
+
timestamp?: number; // Webhook timestamp
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## HMAC Signature Verification
|
|
87
|
+
|
|
88
|
+
Fluent Commerce signs webhooks using HMAC-SHA256:
|
|
89
|
+
|
|
90
|
+
### How It Works
|
|
91
|
+
|
|
92
|
+
1. **Fluent sends webhook:**
|
|
93
|
+
- Generates HMAC signature: `HMAC-SHA256(secret, payload)`
|
|
94
|
+
- Includes signature in `X-Fluent-Signature` header
|
|
95
|
+
|
|
96
|
+
2. **Your service receives webhook:**
|
|
97
|
+
- Extracts body and signature
|
|
98
|
+
- Recomputes HMAC with your secret
|
|
99
|
+
- Compares computed signature with header value
|
|
100
|
+
|
|
101
|
+
3. **SDK validates:**
|
|
102
|
+
- Parses signature format: `t=timestamp,v1=signature`
|
|
103
|
+
- Checks timestamp within tolerance window
|
|
104
|
+
- Verifies HMAC matches
|
|
105
|
+
|
|
106
|
+
### Signature Format
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
X-Fluent-Signature: t=1640995200,v1=abc123def456...
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Components:**
|
|
113
|
+
- `t`: Unix timestamp when webhook sent
|
|
114
|
+
- `v1`: HMAC-SHA256 signature (hex)
|
|
115
|
+
|
|
116
|
+
### Verification Algorithm
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
// Simplified (SDK does this internally)
|
|
120
|
+
function verifySignature(body: string, signature: string, secret: string): boolean {
|
|
121
|
+
// Parse signature header
|
|
122
|
+
const parts = signature.split(',');
|
|
123
|
+
const timestamp = parts.find(p => p.startsWith('t=')).split('=')[1];
|
|
124
|
+
const receivedSig = parts.find(p => p.startsWith('v1=')).split('=')[1];
|
|
125
|
+
|
|
126
|
+
// Compute expected signature
|
|
127
|
+
const payload = `${timestamp}.${body}`;
|
|
128
|
+
const expectedSig = crypto
|
|
129
|
+
.createHmac('sha256', secret)
|
|
130
|
+
.update(payload)
|
|
131
|
+
.digest('hex');
|
|
132
|
+
|
|
133
|
+
// Constant-time comparison
|
|
134
|
+
return crypto.timingSafeEqual(
|
|
135
|
+
Buffer.from(receivedSig, 'hex'),
|
|
136
|
+
Buffer.from(expectedSig, 'hex')
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Webhook Security
|
|
144
|
+
|
|
145
|
+
### Best Practices
|
|
146
|
+
|
|
147
|
+
**1. Always Validate Signatures**
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
// ✅ CORRECT: Validate before processing
|
|
151
|
+
const result = await parseWebhookRequest({ body, signature, secret });
|
|
152
|
+
if (!result.valid) {
|
|
153
|
+
return new Response('Unauthorized', { status: 401 });
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Process event...
|
|
157
|
+
|
|
158
|
+
// ❌ WRONG: Process without validation
|
|
159
|
+
const payload = JSON.parse(body);
|
|
160
|
+
await processEvent(payload); // Insecure!
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**2. Use Environment Variables for Secrets**
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
// ✅ CORRECT: Secret from environment
|
|
167
|
+
const secret = process.env.FLUENT_WEBHOOK_SECRET;
|
|
168
|
+
|
|
169
|
+
// ❌ WRONG: Hardcoded secret
|
|
170
|
+
const secret = 'my-secret-key'; // Never do this!
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**3. Set Appropriate Tolerance**
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
// ✅ CORRECT: 5-minute tolerance (default)
|
|
177
|
+
const result = await parseWebhookRequest({
|
|
178
|
+
body,
|
|
179
|
+
signature,
|
|
180
|
+
secret,
|
|
181
|
+
toleranceMs: 300000 // 5 minutes
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// ❌ WRONG: Too permissive
|
|
185
|
+
const result = await parseWebhookRequest({
|
|
186
|
+
body,
|
|
187
|
+
signature,
|
|
188
|
+
secret,
|
|
189
|
+
toleranceMs: 3600000 // 1 hour - allows replay attacks!
|
|
190
|
+
});
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**4. Reject Invalid Webhooks**
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
// ✅ CORRECT: Return 401 for invalid signatures
|
|
197
|
+
if (!result.valid) {
|
|
198
|
+
console.warn('Invalid webhook signature:', result.error);
|
|
199
|
+
return new Response('Unauthorized', { status: 401 });
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ❌ WRONG: Process anyway
|
|
203
|
+
if (!result.valid) {
|
|
204
|
+
console.warn('Invalid signature, but processing anyway...');
|
|
205
|
+
await processEvent(result.payload); // Dangerous!
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Common Security Mistakes
|
|
210
|
+
|
|
211
|
+
| Mistake | Risk | Fix |
|
|
212
|
+
|---------|------|-----|
|
|
213
|
+
| Skip validation | Forged requests | Always validate |
|
|
214
|
+
| Hardcode secrets | Secret leakage | Use environment variables |
|
|
215
|
+
| Large tolerance | Replay attacks | Use 5-minute default |
|
|
216
|
+
| Ignore errors | Data corruption | Return 401 on failure |
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Platform Integration
|
|
221
|
+
|
|
222
|
+
### Versori Webhooks
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
import { webhook } from '@versori/run';
|
|
226
|
+
import { parseWebhookRequest } from '@fluentcommerce/fc-connect-sdk';
|
|
227
|
+
|
|
228
|
+
export const fluentWebhook = webhook('fluent-events', async (req, ctx) => {
|
|
229
|
+
const { log } = ctx;
|
|
230
|
+
|
|
231
|
+
// Extract signature header
|
|
232
|
+
const signature = req.headers.get('X-Fluent-Signature');
|
|
233
|
+
if (!signature) {
|
|
234
|
+
return new Response('Missing signature', { status: 401 });
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Validate webhook
|
|
238
|
+
const result = await parseWebhookRequest({
|
|
239
|
+
body: await req.text(),
|
|
240
|
+
signature,
|
|
241
|
+
secret: process.env.FLUENT_WEBHOOK_SECRET
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
if (!result.valid) {
|
|
245
|
+
log.warn('Invalid webhook', { error: result.error });
|
|
246
|
+
return new Response('Unauthorized', { status: 401 });
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Process validated event
|
|
250
|
+
log.info('Webhook received', { event: result.payload.eventName });
|
|
251
|
+
await processFluentEvent(result.payload);
|
|
252
|
+
|
|
253
|
+
return new Response('OK', { status: 200 });
|
|
254
|
+
});
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Node.js Express
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
import express from 'express';
|
|
261
|
+
import { parseWebhookRequest } from '@fluentcommerce/fc-connect-sdk';
|
|
262
|
+
|
|
263
|
+
const app = express();
|
|
264
|
+
|
|
265
|
+
// IMPORTANT: Use raw body for signature verification
|
|
266
|
+
app.post('/webhooks/fluent', express.raw({ type: 'application/json' }), async (req, res) => {
|
|
267
|
+
const signature = req.headers['x-fluent-signature'];
|
|
268
|
+
|
|
269
|
+
const result = await parseWebhookRequest({
|
|
270
|
+
body: req.body, // Raw buffer
|
|
271
|
+
signature,
|
|
272
|
+
secret: process.env.FLUENT_WEBHOOK_SECRET
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
if (!result.valid) {
|
|
276
|
+
return res.status(401).send('Unauthorized');
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
await processEvent(result.payload);
|
|
280
|
+
res.status(200).send('OK');
|
|
281
|
+
});
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Deno HTTP Server
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
import { serve } from 'https://deno.land/std/http/server.ts';
|
|
288
|
+
import { parseWebhookRequest } from '@fluentcommerce/fc-connect-sdk';
|
|
289
|
+
|
|
290
|
+
serve(async (req) => {
|
|
291
|
+
if (req.url === '/webhooks/fluent') {
|
|
292
|
+
const signature = req.headers.get('X-Fluent-Signature');
|
|
293
|
+
const body = await req.text();
|
|
294
|
+
|
|
295
|
+
const result = await parseWebhookRequest({
|
|
296
|
+
body,
|
|
297
|
+
signature,
|
|
298
|
+
secret: Deno.env.get('FLUENT_WEBHOOK_SECRET')
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
if (!result.valid) {
|
|
302
|
+
return new Response('Unauthorized', { status: 401 });
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
await processEvent(result.payload);
|
|
306
|
+
return new Response('OK', { status: 200 });
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## Error Handling
|
|
314
|
+
|
|
315
|
+
### Common Errors
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
const result = await parseWebhookRequest({ body, signature, secret });
|
|
319
|
+
|
|
320
|
+
if (!result.valid) {
|
|
321
|
+
switch (result.error) {
|
|
322
|
+
case 'Missing signature':
|
|
323
|
+
// No X-Fluent-Signature header
|
|
324
|
+
return new Response('Missing signature header', { status: 400 });
|
|
325
|
+
|
|
326
|
+
case 'Invalid signature format':
|
|
327
|
+
// Malformed signature (not t=...,v1=...)
|
|
328
|
+
return new Response('Bad signature format', { status: 400 });
|
|
329
|
+
|
|
330
|
+
case 'Timestamp too old':
|
|
331
|
+
// Request older than tolerance window
|
|
332
|
+
return new Response('Request expired', { status: 401 });
|
|
333
|
+
|
|
334
|
+
case 'Signature mismatch':
|
|
335
|
+
// HMAC doesn't match (wrong secret or tampered body)
|
|
336
|
+
return new Response('Unauthorized', { status: 401 });
|
|
337
|
+
|
|
338
|
+
default:
|
|
339
|
+
return new Response('Validation failed', { status: 401 });
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### Logging Validation Failures
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
const result = await parseWebhookRequest({ body, signature, secret });
|
|
348
|
+
|
|
349
|
+
if (!result.valid) {
|
|
350
|
+
// Log for security monitoring
|
|
351
|
+
log.warn('Webhook validation failed', {
|
|
352
|
+
error: result.error,
|
|
353
|
+
signature: signature.substring(0, 20) + '...', // Truncate for logs
|
|
354
|
+
bodyLength: body.length,
|
|
355
|
+
timestamp: result.timestamp,
|
|
356
|
+
ip: req.headers.get('X-Forwarded-For')
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
return new Response('Unauthorized', { status: 401 });
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## Common Patterns
|
|
366
|
+
|
|
367
|
+
### Pattern 1: Idempotent Processing
|
|
368
|
+
|
|
369
|
+
```typescript
|
|
370
|
+
const result = await parseWebhookRequest({ body, signature, secret });
|
|
371
|
+
if (!result.valid) return unauthorized();
|
|
372
|
+
|
|
373
|
+
const eventId = result.payload.id;
|
|
374
|
+
|
|
375
|
+
// Check if already processed
|
|
376
|
+
if (await stateService.isEventProcessed(eventId)) {
|
|
377
|
+
return new Response('Already processed', { status: 200 });
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Process event
|
|
381
|
+
await processEvent(result.payload);
|
|
382
|
+
|
|
383
|
+
// Mark as processed
|
|
384
|
+
await stateService.markEventProcessed(eventId);
|
|
385
|
+
|
|
386
|
+
return new Response('OK', { status: 200 });
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Pattern 2: Event Type Routing
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
const result = await parseWebhookRequest({ body, signature, secret });
|
|
393
|
+
if (!result.valid) return unauthorized();
|
|
394
|
+
|
|
395
|
+
const { eventName, entity } = result.payload;
|
|
396
|
+
|
|
397
|
+
switch (eventName) {
|
|
398
|
+
case 'ORDER.CREATED':
|
|
399
|
+
await handleOrderCreated(entity);
|
|
400
|
+
break;
|
|
401
|
+
|
|
402
|
+
case 'FULFILMENT.UPDATED':
|
|
403
|
+
await handleFulfilmentUpdated(entity);
|
|
404
|
+
break;
|
|
405
|
+
|
|
406
|
+
case 'INVENTORY.ADJUSTED':
|
|
407
|
+
await handleInventoryAdjusted(entity);
|
|
408
|
+
break;
|
|
409
|
+
|
|
410
|
+
default:
|
|
411
|
+
console.log(`Unhandled event: ${eventName}`);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return new Response('OK', { status: 200 });
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### Pattern 3: Async Processing
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
const result = await parseWebhookRequest({ body, signature, secret });
|
|
421
|
+
if (!result.valid) return unauthorized();
|
|
422
|
+
|
|
423
|
+
// Acknowledge webhook immediately
|
|
424
|
+
queueForProcessing(result.payload);
|
|
425
|
+
|
|
426
|
+
return new Response('Accepted', { status: 202 });
|
|
427
|
+
|
|
428
|
+
// Process asynchronously
|
|
429
|
+
async function queueForProcessing(payload) {
|
|
430
|
+
await messageQueue.publish('fluent-events', payload);
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
## Troubleshooting
|
|
437
|
+
|
|
438
|
+
### Issue 1: Always Getting "Signature mismatch"
|
|
439
|
+
|
|
440
|
+
**Causes:**
|
|
441
|
+
- Wrong webhook secret
|
|
442
|
+
- Body modified before validation
|
|
443
|
+
- JSON parsing before validation
|
|
444
|
+
|
|
445
|
+
**Solutions:**
|
|
446
|
+
```typescript
|
|
447
|
+
// ✅ Use raw body for validation
|
|
448
|
+
app.post('/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
|
|
449
|
+
const result = await parseWebhookRequest({
|
|
450
|
+
body: req.body, // Raw buffer, not parsed JSON
|
|
451
|
+
signature: req.headers['x-fluent-signature'],
|
|
452
|
+
secret: process.env.FLUENT_WEBHOOK_SECRET
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
// ❌ WRONG: Parse JSON first
|
|
457
|
+
app.post('/webhook', express.json(), async (req, res) => {
|
|
458
|
+
// Body already parsed - signature will fail!
|
|
459
|
+
});
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### Issue 2: "Timestamp too old" Errors
|
|
463
|
+
|
|
464
|
+
**Causes:**
|
|
465
|
+
- Server clock skew
|
|
466
|
+
- Network delays
|
|
467
|
+
- Tolerance too strict
|
|
468
|
+
|
|
469
|
+
**Solutions:**
|
|
470
|
+
```typescript
|
|
471
|
+
// ✅ Increase tolerance if needed
|
|
472
|
+
const result = await parseWebhookRequest({
|
|
473
|
+
body,
|
|
474
|
+
signature,
|
|
475
|
+
secret,
|
|
476
|
+
toleranceMs: 600000 // 10 minutes for slow networks
|
|
477
|
+
});
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
### Issue 3: Missing Signature Header
|
|
481
|
+
|
|
482
|
+
**Cause:** Header not forwarded by proxy/load balancer
|
|
483
|
+
|
|
484
|
+
**Solution:**
|
|
485
|
+
```typescript
|
|
486
|
+
// Check different header names
|
|
487
|
+
const signature =
|
|
488
|
+
req.headers.get('X-Fluent-Signature') ||
|
|
489
|
+
req.headers.get('x-fluent-signature') ||
|
|
490
|
+
req.headers.get('X-Fluent-Signature'.toLowerCase());
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
---
|
|
494
|
+
|
|
495
|
+
## See Also
|
|
496
|
+
|
|
497
|
+
**Related Modules:**
|
|
498
|
+
- [Module 01: Client API](./api-reference-01-client-api.md) - FluentClient creation
|
|
499
|
+
- [Module 11: Error Handling](./api-reference-11-error-handling.md) - Error strategies
|
|
500
|
+
|
|
501
|
+
**Complete Guides:**
|
|
502
|
+
- [Webhook Validation Guide](../../webhook-validation/webhook-validation-readme.md) - Full webhook validation series
|
|
503
|
+
- [Versori Webhook Patterns](../../../04-REFERENCE/platforms/versori/platforms-versori-webhook-connection-security.md)
|
|
504
|
+
|
|
505
|
+
**Templates:**
|
|
506
|
+
- [Generic XML Order Webhook](../../../01-TEMPLATES/versori/webhooks/template-webhook-generic-xml-order.md)
|
|
507
|
+
- [ASN Purchase Order Webhook](../../../01-TEMPLATES/versori/webhooks/template-webhook-asn-purchase-order.md)
|
|
508
|
+
|
|
509
|
+
---
|
|
510
|
+
|
|
511
|
+
**Navigation:**
|
|
512
|
+
- [Back to Module List](./readme.md)
|
|
513
|
+
- [Previous: Module 08 - Pagination](./api-reference-08-pagination.md)
|
|
514
|
+
- [Next: Module 10 - Extraction](./api-reference-10-extraction.md)
|