@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
package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-04-platform-integration.md
CHANGED
|
@@ -1,629 +1,629 @@
|
|
|
1
|
-
# Module 4: Platform Integration
|
|
2
|
-
|
|
3
|
-
**Level:** Intermediate
|
|
4
|
-
**Estimated Time:** 30 minutes
|
|
5
|
-
|
|
6
|
-
## Overview
|
|
7
|
-
|
|
8
|
-
This module covers webhook validation integration across different platforms and frameworks. You'll learn patterns for Express.js, Next.js, AWS Lambda, and other Node.js environments.
|
|
9
|
-
|
|
10
|
-
## Learning Objectives
|
|
11
|
-
|
|
12
|
-
By the end of this module, you will:
|
|
13
|
-
- Integrate webhook validation in Express.js applications
|
|
14
|
-
- Implement validation in Next.js API routes
|
|
15
|
-
- Create AWS Lambda webhook handlers with validation
|
|
16
|
-
- Handle raw body extraction across platforms
|
|
17
|
-
- Build serverless webhook validators
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## Express.js Integration
|
|
22
|
-
|
|
23
|
-
### Complete Express Webhook Endpoint
|
|
24
|
-
|
|
25
|
-
```typescript
|
|
26
|
-
import express from 'express';
|
|
27
|
-
import {
|
|
28
|
-
WebhookValidationService,
|
|
29
|
-
createConsoleLogger,
|
|
30
|
-
toStructuredLogger
|
|
31
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
32
|
-
|
|
33
|
-
const app = express();
|
|
34
|
-
|
|
35
|
-
// CRITICAL: Use express.raw() to preserve original body
|
|
36
|
-
app.use(express.raw({ type: 'application/json' }));
|
|
37
|
-
|
|
38
|
-
// Initialize services
|
|
39
|
-
const logger = toStructuredLogger(createConsoleLogger(), { service: 'webhook' });
|
|
40
|
-
const validator = new WebhookValidationService({
|
|
41
|
-
strictValidation: true
|
|
42
|
-
}, logger);
|
|
43
|
-
|
|
44
|
-
app.post('/webhooks/fluent', async (req, res) => {
|
|
45
|
-
try {
|
|
46
|
-
// Get public key
|
|
47
|
-
const publicKey = process.env.FLUENT_WEBHOOK_PUBLIC_KEY;
|
|
48
|
-
if (!publicKey) {
|
|
49
|
-
logger.error('Missing FLUENT_WEBHOOK_PUBLIC_KEY environment variable');
|
|
50
|
-
return res.status(500).json({ error: 'Server configuration error' });
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Extract raw body (Buffer to string)
|
|
54
|
-
const rawBody = req.body.toString('utf8');
|
|
55
|
-
|
|
56
|
-
// Validate webhook
|
|
57
|
-
const result = await validator.validateWebhook(
|
|
58
|
-
rawBody,
|
|
59
|
-
req.headers as Record<string, string>,
|
|
60
|
-
publicKey
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
// Handle validation failure
|
|
64
|
-
if (!result.isValid) {
|
|
65
|
-
logger.warn('Webhook validation failed', {
|
|
66
|
-
error: result.error,
|
|
67
|
-
algorithm: result.algorithm,
|
|
68
|
-
ip: req.ip
|
|
69
|
-
});
|
|
70
|
-
return res.status(401).json({ error: 'Invalid signature' });
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Log successful validation
|
|
74
|
-
logger.info('Webhook validated successfully', {
|
|
75
|
-
algorithm: result.algorithm
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
// Process webhook
|
|
79
|
-
const data = JSON.parse(rawBody);
|
|
80
|
-
await processWebhook(data);
|
|
81
|
-
|
|
82
|
-
res.status(200).json({ success: true, eventId: data.eventId });
|
|
83
|
-
|
|
84
|
-
} catch (error) {
|
|
85
|
-
logger.error('Webhook processing error', { error });
|
|
86
|
-
res.status(500).json({ error: 'Internal server error' });
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
app.listen(3000, () => {
|
|
91
|
-
console.log('Webhook server listening on port 3000');
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
async function processWebhook(data: any): Promise<void> {
|
|
95
|
-
console.log('Processing webhook:', data);
|
|
96
|
-
// Your business logic here
|
|
97
|
-
}
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
### Express Raw Body Middleware
|
|
101
|
-
|
|
102
|
-
```typescript
|
|
103
|
-
import express, { Request, Response, NextFunction } from 'express';
|
|
104
|
-
|
|
105
|
-
// Custom middleware to capture raw body
|
|
106
|
-
function rawBodyMiddleware(req: Request, res: Response, next: NextFunction) {
|
|
107
|
-
let data = '';
|
|
108
|
-
|
|
109
|
-
req.setEncoding('utf8');
|
|
110
|
-
req.on('data', (chunk) => {
|
|
111
|
-
data += chunk;
|
|
112
|
-
});
|
|
113
|
-
req.on('end', () => {
|
|
114
|
-
(req as any).rawBody = data;
|
|
115
|
-
next();
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
app.use(rawBodyMiddleware);
|
|
120
|
-
app.use(express.json()); // Also parse JSON for convenience
|
|
121
|
-
|
|
122
|
-
app.post('/webhooks/fluent', async (req, res) => {
|
|
123
|
-
const rawBody = (req as any).rawBody; // Use raw body for validation
|
|
124
|
-
const data = req.body; // Use parsed body for processing
|
|
125
|
-
|
|
126
|
-
const result = await validator.validateWebhook(rawBody, req.headers, publicKey);
|
|
127
|
-
// ...
|
|
128
|
-
});
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
---
|
|
132
|
-
|
|
133
|
-
## Next.js API Routes
|
|
134
|
-
|
|
135
|
-
### Next.js 13+ App Router
|
|
136
|
-
|
|
137
|
-
```typescript
|
|
138
|
-
// app/api/webhooks/fluent/route.ts
|
|
139
|
-
import { NextRequest, NextResponse } from 'next/server';
|
|
140
|
-
import {
|
|
141
|
-
WebhookValidationService,
|
|
142
|
-
createConsoleLogger,
|
|
143
|
-
toStructuredLogger
|
|
144
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
145
|
-
|
|
146
|
-
export async function POST(request: NextRequest) {
|
|
147
|
-
try {
|
|
148
|
-
// Initialize services
|
|
149
|
-
const logger = toStructuredLogger(createConsoleLogger(), { service: 'webhook' });
|
|
150
|
-
const validator = new WebhookValidationService({
|
|
151
|
-
strictValidation: true
|
|
152
|
-
}, logger);
|
|
153
|
-
|
|
154
|
-
// Get public key
|
|
155
|
-
const publicKey = process.env.FLUENT_WEBHOOK_PUBLIC_KEY;
|
|
156
|
-
if (!publicKey) {
|
|
157
|
-
return NextResponse.json(
|
|
158
|
-
{ error: 'Server configuration error' },
|
|
159
|
-
{ status: 500 }
|
|
160
|
-
);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Extract raw body
|
|
164
|
-
const rawBody = await request.text();
|
|
165
|
-
|
|
166
|
-
// Convert headers to object
|
|
167
|
-
const headers: Record<string, string> = {};
|
|
168
|
-
request.headers.forEach((value, key) => {
|
|
169
|
-
headers[key] = value;
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
// Validate webhook
|
|
173
|
-
const result = await validator.validateWebhook(
|
|
174
|
-
rawBody,
|
|
175
|
-
headers,
|
|
176
|
-
publicKey
|
|
177
|
-
);
|
|
178
|
-
|
|
179
|
-
if (!result.isValid) {
|
|
180
|
-
logger.warn('Invalid webhook signature', { error: result.error });
|
|
181
|
-
return NextResponse.json(
|
|
182
|
-
{ error: 'Invalid signature' },
|
|
183
|
-
{ status: 401 }
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Process webhook
|
|
188
|
-
const data = JSON.parse(rawBody);
|
|
189
|
-
await processWebhook(data);
|
|
190
|
-
|
|
191
|
-
return NextResponse.json({
|
|
192
|
-
success: true,
|
|
193
|
-
eventId: data.eventId
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
} catch (error) {
|
|
197
|
-
console.error('Webhook error:', error);
|
|
198
|
-
return NextResponse.json(
|
|
199
|
-
{ error: 'Internal server error' },
|
|
200
|
-
{ status: 500 }
|
|
201
|
-
);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
async function processWebhook(data: any): Promise<void> {
|
|
206
|
-
console.log('Processing webhook:', data);
|
|
207
|
-
}
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
### Next.js Pages Router
|
|
211
|
-
|
|
212
|
-
```typescript
|
|
213
|
-
// pages/api/webhooks/fluent.ts
|
|
214
|
-
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
215
|
-
import {
|
|
216
|
-
WebhookValidationService,
|
|
217
|
-
createConsoleLogger,
|
|
218
|
-
toStructuredLogger
|
|
219
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
220
|
-
|
|
221
|
-
// Disable body parsing to get raw body
|
|
222
|
-
export const config = {
|
|
223
|
-
api: {
|
|
224
|
-
bodyParser: false
|
|
225
|
-
}
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
export default async function handler(
|
|
229
|
-
req: NextApiRequest,
|
|
230
|
-
res: NextApiResponse
|
|
231
|
-
) {
|
|
232
|
-
if (req.method !== 'POST') {
|
|
233
|
-
return res.status(405).json({ error: 'Method not allowed' });
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
try {
|
|
237
|
-
// Read raw body
|
|
238
|
-
const rawBody = await getRawBody(req);
|
|
239
|
-
|
|
240
|
-
// Initialize validator
|
|
241
|
-
const logger = toStructuredLogger(createConsoleLogger(), { service: 'webhook' });
|
|
242
|
-
const validator = new WebhookValidationService({
|
|
243
|
-
strictValidation: true
|
|
244
|
-
}, logger);
|
|
245
|
-
|
|
246
|
-
const publicKey = process.env.FLUENT_WEBHOOK_PUBLIC_KEY;
|
|
247
|
-
if (!publicKey) {
|
|
248
|
-
return res.status(500).json({ error: 'Configuration error' });
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// Validate
|
|
252
|
-
const result = await validator.validateWebhook(
|
|
253
|
-
rawBody,
|
|
254
|
-
req.headers as Record<string, string>,
|
|
255
|
-
publicKey
|
|
256
|
-
);
|
|
257
|
-
|
|
258
|
-
if (!result.isValid) {
|
|
259
|
-
return res.status(401).json({ error: 'Invalid signature' });
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Process
|
|
263
|
-
const data = JSON.parse(rawBody);
|
|
264
|
-
await processWebhook(data);
|
|
265
|
-
|
|
266
|
-
res.status(200).json({ success: true });
|
|
267
|
-
|
|
268
|
-
} catch (error) {
|
|
269
|
-
console.error('Error:', error);
|
|
270
|
-
res.status(500).json({ error: 'Internal server error' });
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// Helper to read raw body
|
|
275
|
-
async function getRawBody(req: NextApiRequest): Promise<string> {
|
|
276
|
-
return new Promise((resolve, reject) => {
|
|
277
|
-
let data = '';
|
|
278
|
-
req.setEncoding('utf8');
|
|
279
|
-
req.on('data', (chunk) => (data += chunk));
|
|
280
|
-
req.on('end', () => resolve(data));
|
|
281
|
-
req.on('error', reject);
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
async function processWebhook(data: any): Promise<void> {
|
|
286
|
-
console.log('Processing:', data);
|
|
287
|
-
}
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
---
|
|
291
|
-
|
|
292
|
-
## AWS Lambda
|
|
293
|
-
|
|
294
|
-
### Lambda Handler with Validation
|
|
295
|
-
|
|
296
|
-
```typescript
|
|
297
|
-
import {
|
|
298
|
-
APIGatewayProxyEvent,
|
|
299
|
-
APIGatewayProxyResult,
|
|
300
|
-
Context
|
|
301
|
-
} from 'aws-lambda';
|
|
302
|
-
import {
|
|
303
|
-
WebhookValidationService,
|
|
304
|
-
createConsoleLogger,
|
|
305
|
-
toStructuredLogger
|
|
306
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
307
|
-
|
|
308
|
-
export async function handler(
|
|
309
|
-
event: APIGatewayProxyEvent,
|
|
310
|
-
context: Context
|
|
311
|
-
): Promise<APIGatewayProxyResult> {
|
|
312
|
-
try {
|
|
313
|
-
// Initialize services
|
|
314
|
-
const logger = toStructuredLogger(createConsoleLogger(), { service: 'lambda-webhook' });
|
|
315
|
-
const validator = new WebhookValidationService({
|
|
316
|
-
strictValidation: true
|
|
317
|
-
}, logger);
|
|
318
|
-
|
|
319
|
-
// Get public key from environment
|
|
320
|
-
const publicKey = process.env.FLUENT_WEBHOOK_PUBLIC_KEY;
|
|
321
|
-
if (!publicKey) {
|
|
322
|
-
return {
|
|
323
|
-
statusCode: 500,
|
|
324
|
-
body: JSON.stringify({ error: 'Configuration error' })
|
|
325
|
-
};
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// Extract raw body (base64 decode if needed)
|
|
329
|
-
const rawBody = event.isBase64Encoded
|
|
330
|
-
? Buffer.from(event.body || '', 'base64').toString('utf8')
|
|
331
|
-
: event.body || '';
|
|
332
|
-
|
|
333
|
-
// Convert headers to lowercase (API Gateway normalizes)
|
|
334
|
-
const headers: Record<string, string> = {};
|
|
335
|
-
Object.entries(event.headers || {}).forEach(([key, value]) => {
|
|
336
|
-
headers[key.toLowerCase()] = value || '';
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
// Validate webhook
|
|
340
|
-
const result = await validator.validateWebhook(
|
|
341
|
-
rawBody,
|
|
342
|
-
headers,
|
|
343
|
-
publicKey
|
|
344
|
-
);
|
|
345
|
-
|
|
346
|
-
if (!result.isValid) {
|
|
347
|
-
logger.warn('Validation failed', { error: result.error });
|
|
348
|
-
return {
|
|
349
|
-
statusCode: 401,
|
|
350
|
-
body: JSON.stringify({ error: 'Invalid signature' })
|
|
351
|
-
};
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// Process webhook
|
|
355
|
-
const data = JSON.parse(rawBody);
|
|
356
|
-
await processWebhook(data);
|
|
357
|
-
|
|
358
|
-
return {
|
|
359
|
-
statusCode: 200,
|
|
360
|
-
body: JSON.stringify({ success: true, eventId: data.eventId })
|
|
361
|
-
};
|
|
362
|
-
|
|
363
|
-
} catch (error) {
|
|
364
|
-
console.error('Lambda error:', error);
|
|
365
|
-
return {
|
|
366
|
-
statusCode: 500,
|
|
367
|
-
body: JSON.stringify({ error: 'Internal server error' })
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
async function processWebhook(data: any): Promise<void> {
|
|
373
|
-
console.log('Processing webhook:', data);
|
|
374
|
-
}
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
---
|
|
378
|
-
|
|
379
|
-
## Fastify
|
|
380
|
-
|
|
381
|
-
### Fastify Webhook Route
|
|
382
|
-
|
|
383
|
-
```typescript
|
|
384
|
-
import Fastify from 'fastify';
|
|
385
|
-
import {
|
|
386
|
-
WebhookValidationService,
|
|
387
|
-
createConsoleLogger,
|
|
388
|
-
toStructuredLogger
|
|
389
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
390
|
-
|
|
391
|
-
const fastify = Fastify({ logger: true });
|
|
392
|
-
|
|
393
|
-
// Register raw body parser
|
|
394
|
-
fastify.addContentTypeParser(
|
|
395
|
-
'application/json',
|
|
396
|
-
{ parseAs: 'string' },
|
|
397
|
-
(req, body, done) => {
|
|
398
|
-
done(null, body);
|
|
399
|
-
}
|
|
400
|
-
);
|
|
401
|
-
|
|
402
|
-
fastify.post('/webhooks/fluent', async (request, reply) => {
|
|
403
|
-
try {
|
|
404
|
-
// Initialize validator
|
|
405
|
-
const logger = toStructuredLogger(createConsoleLogger(), { service: 'webhook' });
|
|
406
|
-
const validator = new WebhookValidationService({
|
|
407
|
-
strictValidation: true
|
|
408
|
-
}, logger);
|
|
409
|
-
|
|
410
|
-
const publicKey = process.env.FLUENT_WEBHOOK_PUBLIC_KEY;
|
|
411
|
-
if (!publicKey) {
|
|
412
|
-
return reply.code(500).send({ error: 'Configuration error' });
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
// Raw body is preserved by custom content type parser
|
|
416
|
-
const rawBody = request.body as string;
|
|
417
|
-
|
|
418
|
-
// Validate
|
|
419
|
-
const result = await validator.validateWebhook(
|
|
420
|
-
rawBody,
|
|
421
|
-
request.headers as Record<string, string>,
|
|
422
|
-
publicKey
|
|
423
|
-
);
|
|
424
|
-
|
|
425
|
-
if (!result.isValid) {
|
|
426
|
-
return reply.code(401).send({ error: 'Invalid signature' });
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// Process
|
|
430
|
-
const data = JSON.parse(rawBody);
|
|
431
|
-
await processWebhook(data);
|
|
432
|
-
|
|
433
|
-
return reply.code(200).send({ success: true });
|
|
434
|
-
|
|
435
|
-
} catch (error) {
|
|
436
|
-
request.log.error(error);
|
|
437
|
-
return reply.code(500).send({ error: 'Internal error' });
|
|
438
|
-
}
|
|
439
|
-
});
|
|
440
|
-
|
|
441
|
-
fastify.listen({ port: 3000 }, (err) => {
|
|
442
|
-
if (err) throw err;
|
|
443
|
-
console.log('Server listening on port 3000');
|
|
444
|
-
});
|
|
445
|
-
|
|
446
|
-
async function processWebhook(data: any): Promise<void> {
|
|
447
|
-
console.log('Processing:', data);
|
|
448
|
-
}
|
|
449
|
-
```
|
|
450
|
-
|
|
451
|
-
---
|
|
452
|
-
|
|
453
|
-
## Node.js HTTP Server
|
|
454
|
-
|
|
455
|
-
### Raw Node.js Webhook Server
|
|
456
|
-
|
|
457
|
-
```typescript
|
|
458
|
-
import http from 'http';
|
|
459
|
-
import {
|
|
460
|
-
WebhookValidationService,
|
|
461
|
-
createConsoleLogger,
|
|
462
|
-
toStructuredLogger
|
|
463
|
-
} from '@fluentcommerce/fc-connect-sdk';
|
|
464
|
-
|
|
465
|
-
const logger = toStructuredLogger(createConsoleLogger(), { service: 'webhook' });
|
|
466
|
-
const validator = new WebhookValidationService({
|
|
467
|
-
strictValidation: true
|
|
468
|
-
}, logger);
|
|
469
|
-
|
|
470
|
-
const server = http.createServer(async (req, res) => {
|
|
471
|
-
if (req.method !== 'POST' || req.url !== '/webhooks/fluent') {
|
|
472
|
-
res.writeHead(404);
|
|
473
|
-
res.end('Not found');
|
|
474
|
-
return;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
try {
|
|
478
|
-
// Read raw body
|
|
479
|
-
const rawBody = await new Promise<string>((resolve, reject) => {
|
|
480
|
-
let data = '';
|
|
481
|
-
req.setEncoding('utf8');
|
|
482
|
-
req.on('data', (chunk) => (data += chunk));
|
|
483
|
-
req.on('end', () => resolve(data));
|
|
484
|
-
req.on('error', reject);
|
|
485
|
-
});
|
|
486
|
-
|
|
487
|
-
// Get public key
|
|
488
|
-
const publicKey = process.env.FLUENT_WEBHOOK_PUBLIC_KEY;
|
|
489
|
-
if (!publicKey) {
|
|
490
|
-
res.writeHead(500);
|
|
491
|
-
res.end('Configuration error');
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
// Validate
|
|
496
|
-
const result = await validator.validateWebhook(
|
|
497
|
-
rawBody,
|
|
498
|
-
req.headers as Record<string, string>,
|
|
499
|
-
publicKey
|
|
500
|
-
);
|
|
501
|
-
|
|
502
|
-
if (!result.isValid) {
|
|
503
|
-
res.writeHead(401);
|
|
504
|
-
res.end(JSON.stringify({ error: 'Invalid signature' }));
|
|
505
|
-
return;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
// Process
|
|
509
|
-
const data = JSON.parse(rawBody);
|
|
510
|
-
await processWebhook(data);
|
|
511
|
-
|
|
512
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
513
|
-
res.end(JSON.stringify({ success: true }));
|
|
514
|
-
|
|
515
|
-
} catch (error) {
|
|
516
|
-
console.error('Error:', error);
|
|
517
|
-
res.writeHead(500);
|
|
518
|
-
res.end('Internal error');
|
|
519
|
-
}
|
|
520
|
-
});
|
|
521
|
-
|
|
522
|
-
server.listen(3000, () => {
|
|
523
|
-
console.log('Server listening on port 3000');
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
async function processWebhook(data: any): Promise<void> {
|
|
527
|
-
console.log('Processing:', data);
|
|
528
|
-
}
|
|
529
|
-
```
|
|
530
|
-
|
|
531
|
-
---
|
|
532
|
-
|
|
533
|
-
## Platform-Specific Considerations
|
|
534
|
-
|
|
535
|
-
### Express.js
|
|
536
|
-
**Raw Body**: Use `express.raw({ type: 'application/json' })`
|
|
537
|
-
**Headers**: `req.headers` (already object)
|
|
538
|
-
**Body**: `req.body.toString('utf8')`
|
|
539
|
-
|
|
540
|
-
### Next.js
|
|
541
|
-
**Raw Body**: `await request.text()` (App Router) or custom parser (Pages Router)
|
|
542
|
-
**Headers**: Convert `request.headers` to object
|
|
543
|
-
**Config**: Disable body parser in Pages Router
|
|
544
|
-
|
|
545
|
-
### AWS Lambda
|
|
546
|
-
**Raw Body**: Handle `isBase64Encoded` flag
|
|
547
|
-
**Headers**: Normalize to lowercase (API Gateway does this)
|
|
548
|
-
**Body**: `event.body` (may be base64)
|
|
549
|
-
|
|
550
|
-
### Fastify
|
|
551
|
-
**Raw Body**: Custom content type parser with `parseAs: 'string'`
|
|
552
|
-
**Headers**: `request.headers`
|
|
553
|
-
**Body**: Cast to string after custom parser
|
|
554
|
-
|
|
555
|
-
### Node.js HTTP
|
|
556
|
-
**Raw Body**: Manual stream reading
|
|
557
|
-
**Headers**: `req.headers`
|
|
558
|
-
**Body**: UTF-8 encoded stream
|
|
559
|
-
|
|
560
|
-
---
|
|
561
|
-
|
|
562
|
-
## Common Patterns Across Platforms
|
|
563
|
-
|
|
564
|
-
### Pattern 1: Environment Variable for Public Key
|
|
565
|
-
|
|
566
|
-
```typescript
|
|
567
|
-
// All platforms
|
|
568
|
-
const publicKey = process.env.FLUENT_WEBHOOK_PUBLIC_KEY;
|
|
569
|
-
if (!publicKey) {
|
|
570
|
-
// Return 500 error
|
|
571
|
-
}
|
|
572
|
-
```
|
|
573
|
-
|
|
574
|
-
### Pattern 2: Structured Error Response
|
|
575
|
-
|
|
576
|
-
```typescript
|
|
577
|
-
// All platforms should return consistent error format
|
|
578
|
-
if (!result.isValid) {
|
|
579
|
-
return errorResponse(401, {
|
|
580
|
-
error: 'Invalid signature',
|
|
581
|
-
details: result.error,
|
|
582
|
-
timestamp: new Date().toISOString()
|
|
583
|
-
});
|
|
584
|
-
}
|
|
585
|
-
```
|
|
586
|
-
|
|
587
|
-
### Pattern 3: Security Logging
|
|
588
|
-
|
|
589
|
-
```typescript
|
|
590
|
-
// Log validation failures across all platforms
|
|
591
|
-
if (!result.isValid) {
|
|
592
|
-
logger.warn('Webhook validation failed', {
|
|
593
|
-
error: result.error,
|
|
594
|
-
algorithm: result.algorithm,
|
|
595
|
-
sourceIp: getClientIp(request),
|
|
596
|
-
timestamp: new Date().toISOString()
|
|
597
|
-
});
|
|
598
|
-
}
|
|
599
|
-
```
|
|
600
|
-
|
|
601
|
-
---
|
|
602
|
-
|
|
603
|
-
## Key Takeaways
|
|
604
|
-
|
|
605
|
-
- **Raw body is critical**: Each platform has different ways to access it
|
|
606
|
-
- **Disable JSON parsing**: Use raw body parsers to preserve original bytes
|
|
607
|
-
- **Headers vary**: Some platforms normalize headers (lowercase, etc.)
|
|
608
|
-
- **Environment variables**: Consistent pattern across all platforms
|
|
609
|
-
- **Error responses**: Return 401 for invalid signatures, 500 for config errors
|
|
610
|
-
- **Logging**: Track validation failures for security monitoring
|
|
611
|
-
|
|
612
|
-
---
|
|
613
|
-
|
|
614
|
-
## Practice Exercise
|
|
615
|
-
|
|
616
|
-
Choose your platform and implement a complete webhook endpoint with validation.
|
|
617
|
-
|
|
618
|
-
---
|
|
619
|
-
|
|
620
|
-
## Next Steps
|
|
621
|
-
|
|
622
|
-
Continue to [Module 5: Configuration](./webhook-validation-05-configuration.md) to learn about advanced configuration options and factory methods.
|
|
623
|
-
|
|
624
|
-
---
|
|
625
|
-
|
|
626
|
-
## Further Reading
|
|
627
|
-
|
|
628
|
-
- [Module 6: Error Handling](./webhook-validation-06-error-handling.md) - Comprehensive error handling
|
|
629
|
-
- [Module 7: API Reference](../../auto-pagination/modules/auto-pagination-07-api-reference.md) - Complete API documentation
|
|
1
|
+
# Module 4: Platform Integration
|
|
2
|
+
|
|
3
|
+
**Level:** Intermediate
|
|
4
|
+
**Estimated Time:** 30 minutes
|
|
5
|
+
|
|
6
|
+
## Overview
|
|
7
|
+
|
|
8
|
+
This module covers webhook validation integration across different platforms and frameworks. You'll learn patterns for Express.js, Next.js, AWS Lambda, and other Node.js environments.
|
|
9
|
+
|
|
10
|
+
## Learning Objectives
|
|
11
|
+
|
|
12
|
+
By the end of this module, you will:
|
|
13
|
+
- Integrate webhook validation in Express.js applications
|
|
14
|
+
- Implement validation in Next.js API routes
|
|
15
|
+
- Create AWS Lambda webhook handlers with validation
|
|
16
|
+
- Handle raw body extraction across platforms
|
|
17
|
+
- Build serverless webhook validators
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Express.js Integration
|
|
22
|
+
|
|
23
|
+
### Complete Express Webhook Endpoint
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import express from 'express';
|
|
27
|
+
import {
|
|
28
|
+
WebhookValidationService,
|
|
29
|
+
createConsoleLogger,
|
|
30
|
+
toStructuredLogger
|
|
31
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
32
|
+
|
|
33
|
+
const app = express();
|
|
34
|
+
|
|
35
|
+
// CRITICAL: Use express.raw() to preserve original body
|
|
36
|
+
app.use(express.raw({ type: 'application/json' }));
|
|
37
|
+
|
|
38
|
+
// Initialize services
|
|
39
|
+
const logger = toStructuredLogger(createConsoleLogger(), { service: 'webhook' });
|
|
40
|
+
const validator = new WebhookValidationService({
|
|
41
|
+
strictValidation: true
|
|
42
|
+
}, logger);
|
|
43
|
+
|
|
44
|
+
app.post('/webhooks/fluent', async (req, res) => {
|
|
45
|
+
try {
|
|
46
|
+
// Get public key
|
|
47
|
+
const publicKey = process.env.FLUENT_WEBHOOK_PUBLIC_KEY;
|
|
48
|
+
if (!publicKey) {
|
|
49
|
+
logger.error('Missing FLUENT_WEBHOOK_PUBLIC_KEY environment variable');
|
|
50
|
+
return res.status(500).json({ error: 'Server configuration error' });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Extract raw body (Buffer to string)
|
|
54
|
+
const rawBody = req.body.toString('utf8');
|
|
55
|
+
|
|
56
|
+
// Validate webhook
|
|
57
|
+
const result = await validator.validateWebhook(
|
|
58
|
+
rawBody,
|
|
59
|
+
req.headers as Record<string, string>,
|
|
60
|
+
publicKey
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
// Handle validation failure
|
|
64
|
+
if (!result.isValid) {
|
|
65
|
+
logger.warn('Webhook validation failed', {
|
|
66
|
+
error: result.error,
|
|
67
|
+
algorithm: result.algorithm,
|
|
68
|
+
ip: req.ip
|
|
69
|
+
});
|
|
70
|
+
return res.status(401).json({ error: 'Invalid signature' });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Log successful validation
|
|
74
|
+
logger.info('Webhook validated successfully', {
|
|
75
|
+
algorithm: result.algorithm
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Process webhook
|
|
79
|
+
const data = JSON.parse(rawBody);
|
|
80
|
+
await processWebhook(data);
|
|
81
|
+
|
|
82
|
+
res.status(200).json({ success: true, eventId: data.eventId });
|
|
83
|
+
|
|
84
|
+
} catch (error) {
|
|
85
|
+
logger.error('Webhook processing error', { error });
|
|
86
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
app.listen(3000, () => {
|
|
91
|
+
console.log('Webhook server listening on port 3000');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
async function processWebhook(data: any): Promise<void> {
|
|
95
|
+
console.log('Processing webhook:', data);
|
|
96
|
+
// Your business logic here
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Express Raw Body Middleware
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import express, { Request, Response, NextFunction } from 'express';
|
|
104
|
+
|
|
105
|
+
// Custom middleware to capture raw body
|
|
106
|
+
function rawBodyMiddleware(req: Request, res: Response, next: NextFunction) {
|
|
107
|
+
let data = '';
|
|
108
|
+
|
|
109
|
+
req.setEncoding('utf8');
|
|
110
|
+
req.on('data', (chunk) => {
|
|
111
|
+
data += chunk;
|
|
112
|
+
});
|
|
113
|
+
req.on('end', () => {
|
|
114
|
+
(req as any).rawBody = data;
|
|
115
|
+
next();
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
app.use(rawBodyMiddleware);
|
|
120
|
+
app.use(express.json()); // Also parse JSON for convenience
|
|
121
|
+
|
|
122
|
+
app.post('/webhooks/fluent', async (req, res) => {
|
|
123
|
+
const rawBody = (req as any).rawBody; // Use raw body for validation
|
|
124
|
+
const data = req.body; // Use parsed body for processing
|
|
125
|
+
|
|
126
|
+
const result = await validator.validateWebhook(rawBody, req.headers, publicKey);
|
|
127
|
+
// ...
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Next.js API Routes
|
|
134
|
+
|
|
135
|
+
### Next.js 13+ App Router
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
// app/api/webhooks/fluent/route.ts
|
|
139
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
140
|
+
import {
|
|
141
|
+
WebhookValidationService,
|
|
142
|
+
createConsoleLogger,
|
|
143
|
+
toStructuredLogger
|
|
144
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
145
|
+
|
|
146
|
+
export async function POST(request: NextRequest) {
|
|
147
|
+
try {
|
|
148
|
+
// Initialize services
|
|
149
|
+
const logger = toStructuredLogger(createConsoleLogger(), { service: 'webhook' });
|
|
150
|
+
const validator = new WebhookValidationService({
|
|
151
|
+
strictValidation: true
|
|
152
|
+
}, logger);
|
|
153
|
+
|
|
154
|
+
// Get public key
|
|
155
|
+
const publicKey = process.env.FLUENT_WEBHOOK_PUBLIC_KEY;
|
|
156
|
+
if (!publicKey) {
|
|
157
|
+
return NextResponse.json(
|
|
158
|
+
{ error: 'Server configuration error' },
|
|
159
|
+
{ status: 500 }
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Extract raw body
|
|
164
|
+
const rawBody = await request.text();
|
|
165
|
+
|
|
166
|
+
// Convert headers to object
|
|
167
|
+
const headers: Record<string, string> = {};
|
|
168
|
+
request.headers.forEach((value, key) => {
|
|
169
|
+
headers[key] = value;
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Validate webhook
|
|
173
|
+
const result = await validator.validateWebhook(
|
|
174
|
+
rawBody,
|
|
175
|
+
headers,
|
|
176
|
+
publicKey
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
if (!result.isValid) {
|
|
180
|
+
logger.warn('Invalid webhook signature', { error: result.error });
|
|
181
|
+
return NextResponse.json(
|
|
182
|
+
{ error: 'Invalid signature' },
|
|
183
|
+
{ status: 401 }
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Process webhook
|
|
188
|
+
const data = JSON.parse(rawBody);
|
|
189
|
+
await processWebhook(data);
|
|
190
|
+
|
|
191
|
+
return NextResponse.json({
|
|
192
|
+
success: true,
|
|
193
|
+
eventId: data.eventId
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
} catch (error) {
|
|
197
|
+
console.error('Webhook error:', error);
|
|
198
|
+
return NextResponse.json(
|
|
199
|
+
{ error: 'Internal server error' },
|
|
200
|
+
{ status: 500 }
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async function processWebhook(data: any): Promise<void> {
|
|
206
|
+
console.log('Processing webhook:', data);
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Next.js Pages Router
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
// pages/api/webhooks/fluent.ts
|
|
214
|
+
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
215
|
+
import {
|
|
216
|
+
WebhookValidationService,
|
|
217
|
+
createConsoleLogger,
|
|
218
|
+
toStructuredLogger
|
|
219
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
220
|
+
|
|
221
|
+
// Disable body parsing to get raw body
|
|
222
|
+
export const config = {
|
|
223
|
+
api: {
|
|
224
|
+
bodyParser: false
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
export default async function handler(
|
|
229
|
+
req: NextApiRequest,
|
|
230
|
+
res: NextApiResponse
|
|
231
|
+
) {
|
|
232
|
+
if (req.method !== 'POST') {
|
|
233
|
+
return res.status(405).json({ error: 'Method not allowed' });
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
try {
|
|
237
|
+
// Read raw body
|
|
238
|
+
const rawBody = await getRawBody(req);
|
|
239
|
+
|
|
240
|
+
// Initialize validator
|
|
241
|
+
const logger = toStructuredLogger(createConsoleLogger(), { service: 'webhook' });
|
|
242
|
+
const validator = new WebhookValidationService({
|
|
243
|
+
strictValidation: true
|
|
244
|
+
}, logger);
|
|
245
|
+
|
|
246
|
+
const publicKey = process.env.FLUENT_WEBHOOK_PUBLIC_KEY;
|
|
247
|
+
if (!publicKey) {
|
|
248
|
+
return res.status(500).json({ error: 'Configuration error' });
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Validate
|
|
252
|
+
const result = await validator.validateWebhook(
|
|
253
|
+
rawBody,
|
|
254
|
+
req.headers as Record<string, string>,
|
|
255
|
+
publicKey
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
if (!result.isValid) {
|
|
259
|
+
return res.status(401).json({ error: 'Invalid signature' });
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Process
|
|
263
|
+
const data = JSON.parse(rawBody);
|
|
264
|
+
await processWebhook(data);
|
|
265
|
+
|
|
266
|
+
res.status(200).json({ success: true });
|
|
267
|
+
|
|
268
|
+
} catch (error) {
|
|
269
|
+
console.error('Error:', error);
|
|
270
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Helper to read raw body
|
|
275
|
+
async function getRawBody(req: NextApiRequest): Promise<string> {
|
|
276
|
+
return new Promise((resolve, reject) => {
|
|
277
|
+
let data = '';
|
|
278
|
+
req.setEncoding('utf8');
|
|
279
|
+
req.on('data', (chunk) => (data += chunk));
|
|
280
|
+
req.on('end', () => resolve(data));
|
|
281
|
+
req.on('error', reject);
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async function processWebhook(data: any): Promise<void> {
|
|
286
|
+
console.log('Processing:', data);
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## AWS Lambda
|
|
293
|
+
|
|
294
|
+
### Lambda Handler with Validation
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
import {
|
|
298
|
+
APIGatewayProxyEvent,
|
|
299
|
+
APIGatewayProxyResult,
|
|
300
|
+
Context
|
|
301
|
+
} from 'aws-lambda';
|
|
302
|
+
import {
|
|
303
|
+
WebhookValidationService,
|
|
304
|
+
createConsoleLogger,
|
|
305
|
+
toStructuredLogger
|
|
306
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
307
|
+
|
|
308
|
+
export async function handler(
|
|
309
|
+
event: APIGatewayProxyEvent,
|
|
310
|
+
context: Context
|
|
311
|
+
): Promise<APIGatewayProxyResult> {
|
|
312
|
+
try {
|
|
313
|
+
// Initialize services
|
|
314
|
+
const logger = toStructuredLogger(createConsoleLogger(), { service: 'lambda-webhook' });
|
|
315
|
+
const validator = new WebhookValidationService({
|
|
316
|
+
strictValidation: true
|
|
317
|
+
}, logger);
|
|
318
|
+
|
|
319
|
+
// Get public key from environment
|
|
320
|
+
const publicKey = process.env.FLUENT_WEBHOOK_PUBLIC_KEY;
|
|
321
|
+
if (!publicKey) {
|
|
322
|
+
return {
|
|
323
|
+
statusCode: 500,
|
|
324
|
+
body: JSON.stringify({ error: 'Configuration error' })
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Extract raw body (base64 decode if needed)
|
|
329
|
+
const rawBody = event.isBase64Encoded
|
|
330
|
+
? Buffer.from(event.body || '', 'base64').toString('utf8')
|
|
331
|
+
: event.body || '';
|
|
332
|
+
|
|
333
|
+
// Convert headers to lowercase (API Gateway normalizes)
|
|
334
|
+
const headers: Record<string, string> = {};
|
|
335
|
+
Object.entries(event.headers || {}).forEach(([key, value]) => {
|
|
336
|
+
headers[key.toLowerCase()] = value || '';
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
// Validate webhook
|
|
340
|
+
const result = await validator.validateWebhook(
|
|
341
|
+
rawBody,
|
|
342
|
+
headers,
|
|
343
|
+
publicKey
|
|
344
|
+
);
|
|
345
|
+
|
|
346
|
+
if (!result.isValid) {
|
|
347
|
+
logger.warn('Validation failed', { error: result.error });
|
|
348
|
+
return {
|
|
349
|
+
statusCode: 401,
|
|
350
|
+
body: JSON.stringify({ error: 'Invalid signature' })
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Process webhook
|
|
355
|
+
const data = JSON.parse(rawBody);
|
|
356
|
+
await processWebhook(data);
|
|
357
|
+
|
|
358
|
+
return {
|
|
359
|
+
statusCode: 200,
|
|
360
|
+
body: JSON.stringify({ success: true, eventId: data.eventId })
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
} catch (error) {
|
|
364
|
+
console.error('Lambda error:', error);
|
|
365
|
+
return {
|
|
366
|
+
statusCode: 500,
|
|
367
|
+
body: JSON.stringify({ error: 'Internal server error' })
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
async function processWebhook(data: any): Promise<void> {
|
|
373
|
+
console.log('Processing webhook:', data);
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## Fastify
|
|
380
|
+
|
|
381
|
+
### Fastify Webhook Route
|
|
382
|
+
|
|
383
|
+
```typescript
|
|
384
|
+
import Fastify from 'fastify';
|
|
385
|
+
import {
|
|
386
|
+
WebhookValidationService,
|
|
387
|
+
createConsoleLogger,
|
|
388
|
+
toStructuredLogger
|
|
389
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
390
|
+
|
|
391
|
+
const fastify = Fastify({ logger: true });
|
|
392
|
+
|
|
393
|
+
// Register raw body parser
|
|
394
|
+
fastify.addContentTypeParser(
|
|
395
|
+
'application/json',
|
|
396
|
+
{ parseAs: 'string' },
|
|
397
|
+
(req, body, done) => {
|
|
398
|
+
done(null, body);
|
|
399
|
+
}
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
fastify.post('/webhooks/fluent', async (request, reply) => {
|
|
403
|
+
try {
|
|
404
|
+
// Initialize validator
|
|
405
|
+
const logger = toStructuredLogger(createConsoleLogger(), { service: 'webhook' });
|
|
406
|
+
const validator = new WebhookValidationService({
|
|
407
|
+
strictValidation: true
|
|
408
|
+
}, logger);
|
|
409
|
+
|
|
410
|
+
const publicKey = process.env.FLUENT_WEBHOOK_PUBLIC_KEY;
|
|
411
|
+
if (!publicKey) {
|
|
412
|
+
return reply.code(500).send({ error: 'Configuration error' });
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Raw body is preserved by custom content type parser
|
|
416
|
+
const rawBody = request.body as string;
|
|
417
|
+
|
|
418
|
+
// Validate
|
|
419
|
+
const result = await validator.validateWebhook(
|
|
420
|
+
rawBody,
|
|
421
|
+
request.headers as Record<string, string>,
|
|
422
|
+
publicKey
|
|
423
|
+
);
|
|
424
|
+
|
|
425
|
+
if (!result.isValid) {
|
|
426
|
+
return reply.code(401).send({ error: 'Invalid signature' });
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Process
|
|
430
|
+
const data = JSON.parse(rawBody);
|
|
431
|
+
await processWebhook(data);
|
|
432
|
+
|
|
433
|
+
return reply.code(200).send({ success: true });
|
|
434
|
+
|
|
435
|
+
} catch (error) {
|
|
436
|
+
request.log.error(error);
|
|
437
|
+
return reply.code(500).send({ error: 'Internal error' });
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
fastify.listen({ port: 3000 }, (err) => {
|
|
442
|
+
if (err) throw err;
|
|
443
|
+
console.log('Server listening on port 3000');
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
async function processWebhook(data: any): Promise<void> {
|
|
447
|
+
console.log('Processing:', data);
|
|
448
|
+
}
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
---
|
|
452
|
+
|
|
453
|
+
## Node.js HTTP Server
|
|
454
|
+
|
|
455
|
+
### Raw Node.js Webhook Server
|
|
456
|
+
|
|
457
|
+
```typescript
|
|
458
|
+
import http from 'http';
|
|
459
|
+
import {
|
|
460
|
+
WebhookValidationService,
|
|
461
|
+
createConsoleLogger,
|
|
462
|
+
toStructuredLogger
|
|
463
|
+
} from '@fluentcommerce/fc-connect-sdk';
|
|
464
|
+
|
|
465
|
+
const logger = toStructuredLogger(createConsoleLogger(), { service: 'webhook' });
|
|
466
|
+
const validator = new WebhookValidationService({
|
|
467
|
+
strictValidation: true
|
|
468
|
+
}, logger);
|
|
469
|
+
|
|
470
|
+
const server = http.createServer(async (req, res) => {
|
|
471
|
+
if (req.method !== 'POST' || req.url !== '/webhooks/fluent') {
|
|
472
|
+
res.writeHead(404);
|
|
473
|
+
res.end('Not found');
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
try {
|
|
478
|
+
// Read raw body
|
|
479
|
+
const rawBody = await new Promise<string>((resolve, reject) => {
|
|
480
|
+
let data = '';
|
|
481
|
+
req.setEncoding('utf8');
|
|
482
|
+
req.on('data', (chunk) => (data += chunk));
|
|
483
|
+
req.on('end', () => resolve(data));
|
|
484
|
+
req.on('error', reject);
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
// Get public key
|
|
488
|
+
const publicKey = process.env.FLUENT_WEBHOOK_PUBLIC_KEY;
|
|
489
|
+
if (!publicKey) {
|
|
490
|
+
res.writeHead(500);
|
|
491
|
+
res.end('Configuration error');
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// Validate
|
|
496
|
+
const result = await validator.validateWebhook(
|
|
497
|
+
rawBody,
|
|
498
|
+
req.headers as Record<string, string>,
|
|
499
|
+
publicKey
|
|
500
|
+
);
|
|
501
|
+
|
|
502
|
+
if (!result.isValid) {
|
|
503
|
+
res.writeHead(401);
|
|
504
|
+
res.end(JSON.stringify({ error: 'Invalid signature' }));
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// Process
|
|
509
|
+
const data = JSON.parse(rawBody);
|
|
510
|
+
await processWebhook(data);
|
|
511
|
+
|
|
512
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
513
|
+
res.end(JSON.stringify({ success: true }));
|
|
514
|
+
|
|
515
|
+
} catch (error) {
|
|
516
|
+
console.error('Error:', error);
|
|
517
|
+
res.writeHead(500);
|
|
518
|
+
res.end('Internal error');
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
server.listen(3000, () => {
|
|
523
|
+
console.log('Server listening on port 3000');
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
async function processWebhook(data: any): Promise<void> {
|
|
527
|
+
console.log('Processing:', data);
|
|
528
|
+
}
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
---
|
|
532
|
+
|
|
533
|
+
## Platform-Specific Considerations
|
|
534
|
+
|
|
535
|
+
### Express.js
|
|
536
|
+
**Raw Body**: Use `express.raw({ type: 'application/json' })`
|
|
537
|
+
**Headers**: `req.headers` (already object)
|
|
538
|
+
**Body**: `req.body.toString('utf8')`
|
|
539
|
+
|
|
540
|
+
### Next.js
|
|
541
|
+
**Raw Body**: `await request.text()` (App Router) or custom parser (Pages Router)
|
|
542
|
+
**Headers**: Convert `request.headers` to object
|
|
543
|
+
**Config**: Disable body parser in Pages Router
|
|
544
|
+
|
|
545
|
+
### AWS Lambda
|
|
546
|
+
**Raw Body**: Handle `isBase64Encoded` flag
|
|
547
|
+
**Headers**: Normalize to lowercase (API Gateway does this)
|
|
548
|
+
**Body**: `event.body` (may be base64)
|
|
549
|
+
|
|
550
|
+
### Fastify
|
|
551
|
+
**Raw Body**: Custom content type parser with `parseAs: 'string'`
|
|
552
|
+
**Headers**: `request.headers`
|
|
553
|
+
**Body**: Cast to string after custom parser
|
|
554
|
+
|
|
555
|
+
### Node.js HTTP
|
|
556
|
+
**Raw Body**: Manual stream reading
|
|
557
|
+
**Headers**: `req.headers`
|
|
558
|
+
**Body**: UTF-8 encoded stream
|
|
559
|
+
|
|
560
|
+
---
|
|
561
|
+
|
|
562
|
+
## Common Patterns Across Platforms
|
|
563
|
+
|
|
564
|
+
### Pattern 1: Environment Variable for Public Key
|
|
565
|
+
|
|
566
|
+
```typescript
|
|
567
|
+
// All platforms
|
|
568
|
+
const publicKey = process.env.FLUENT_WEBHOOK_PUBLIC_KEY;
|
|
569
|
+
if (!publicKey) {
|
|
570
|
+
// Return 500 error
|
|
571
|
+
}
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
### Pattern 2: Structured Error Response
|
|
575
|
+
|
|
576
|
+
```typescript
|
|
577
|
+
// All platforms should return consistent error format
|
|
578
|
+
if (!result.isValid) {
|
|
579
|
+
return errorResponse(401, {
|
|
580
|
+
error: 'Invalid signature',
|
|
581
|
+
details: result.error,
|
|
582
|
+
timestamp: new Date().toISOString()
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
### Pattern 3: Security Logging
|
|
588
|
+
|
|
589
|
+
```typescript
|
|
590
|
+
// Log validation failures across all platforms
|
|
591
|
+
if (!result.isValid) {
|
|
592
|
+
logger.warn('Webhook validation failed', {
|
|
593
|
+
error: result.error,
|
|
594
|
+
algorithm: result.algorithm,
|
|
595
|
+
sourceIp: getClientIp(request),
|
|
596
|
+
timestamp: new Date().toISOString()
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
---
|
|
602
|
+
|
|
603
|
+
## Key Takeaways
|
|
604
|
+
|
|
605
|
+
- **Raw body is critical**: Each platform has different ways to access it
|
|
606
|
+
- **Disable JSON parsing**: Use raw body parsers to preserve original bytes
|
|
607
|
+
- **Headers vary**: Some platforms normalize headers (lowercase, etc.)
|
|
608
|
+
- **Environment variables**: Consistent pattern across all platforms
|
|
609
|
+
- **Error responses**: Return 401 for invalid signatures, 500 for config errors
|
|
610
|
+
- **Logging**: Track validation failures for security monitoring
|
|
611
|
+
|
|
612
|
+
---
|
|
613
|
+
|
|
614
|
+
## Practice Exercise
|
|
615
|
+
|
|
616
|
+
Choose your platform and implement a complete webhook endpoint with validation.
|
|
617
|
+
|
|
618
|
+
---
|
|
619
|
+
|
|
620
|
+
## Next Steps
|
|
621
|
+
|
|
622
|
+
Continue to [Module 5: Configuration](./webhook-validation-05-configuration.md) to learn about advanced configuration options and factory methods.
|
|
623
|
+
|
|
624
|
+
---
|
|
625
|
+
|
|
626
|
+
## Further Reading
|
|
627
|
+
|
|
628
|
+
- [Module 6: Error Handling](./webhook-validation-06-error-handling.md) - Comprehensive error handling
|
|
629
|
+
- [Module 7: API Reference](../../auto-pagination/modules/auto-pagination-07-api-reference.md) - Complete API documentation
|