@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,1142 +1,1142 @@
|
|
|
1
|
-
# UniversalMapper Error Handling
|
|
2
|
-
|
|
3
|
-
[← Back to Mapping Guide](../mapping-readme.md)
|
|
4
|
-
|
|
5
|
-
**Module 8** | **Level**: Intermediate | **Time**: 25 minutes
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Overview
|
|
10
|
-
|
|
11
|
-
Understanding error handling is critical for building robust integrations. The UniversalMapper provides comprehensive error handling with clear error messages and fail-fast behavior for required fields.
|
|
12
|
-
|
|
13
|
-
**Key Concepts:**
|
|
14
|
-
- ✅ **Individual record error handling** - Each record processed independently
|
|
15
|
-
- ✅ **Fail-fast for required fields** - Prevents partial/corrupt data
|
|
16
|
-
- ✅ **Error aggregation** - Collects all errors for arrays
|
|
17
|
-
- ✅ **Rich error context** - Field name, source path, input value, timestamps
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## Table of Contents
|
|
22
|
-
|
|
23
|
-
1. [Error Types Overview](#error-types-overview)
|
|
24
|
-
2. [Fatal vs Non-Fatal Errors](#fatal-vs-non-fatal-errors)
|
|
25
|
-
3. [Array Error Handling](#array-error-handling)
|
|
26
|
-
4. [Error Result Structure](#error-result-structure)
|
|
27
|
-
5. [Common Error Scenarios](#common-error-scenarios)
|
|
28
|
-
6. [Production Patterns](#production-patterns)
|
|
29
|
-
7. [Troubleshooting Guide](#troubleshooting-guide)
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
## Error Types Overview
|
|
34
|
-
|
|
35
|
-
The UniversalMapper produces two main types of errors:
|
|
36
|
-
|
|
37
|
-
| Error Type | When Thrown | Result | Retryable |
|
|
38
|
-
|------------|-------------|--------|-----------|
|
|
39
|
-
| **Required Field Error** | Required field missing/null/empty | `success: false`, record skipped | ❌ No - Fix data or config |
|
|
40
|
-
| **Resolver Error** | Resolver execution fails | `success: false` (required) or warning (optional) | ❌ No - Fix resolver or data |
|
|
41
|
-
| **Resolver Not Found** | Referenced resolver doesn't exist | `success: false`, record skipped | ❌ No - Fix config |
|
|
42
|
-
| **Configuration Error** | Invalid mapping config | Throws exception | ❌ No - Fix config |
|
|
43
|
-
|
|
44
|
-
---
|
|
45
|
-
|
|
46
|
-
## Fatal vs Non-Fatal Errors
|
|
47
|
-
|
|
48
|
-
### ❌ Fatal Errors (Stops Record Processing)
|
|
49
|
-
|
|
50
|
-
**Result:** `{ success: false, data: null, errors: [...] }`
|
|
51
|
-
|
|
52
|
-
The mapper **immediately stops** processing and returns failure for:
|
|
53
|
-
|
|
54
|
-
#### 1. Required Field Missing
|
|
55
|
-
|
|
56
|
-
```typescript
|
|
57
|
-
const mapper = new UniversalMapper({
|
|
58
|
-
fields: {
|
|
59
|
-
sku: { source: 'product.sku', required: true },
|
|
60
|
-
quantity: { source: 'product.qty', required: true }
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
const result = await mapper.map({ product: { sku: 'ABC' } }); // Missing qty
|
|
65
|
-
|
|
66
|
-
// Result:
|
|
67
|
-
{
|
|
68
|
-
success: false,
|
|
69
|
-
data: null,
|
|
70
|
-
errors: [
|
|
71
|
-
"Failed to map field 'quantity': Required field 'quantity' is missing or empty (source: product.qty)"
|
|
72
|
-
]
|
|
73
|
-
}
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
**Error includes:**
|
|
77
|
-
- Field name: `quantity`
|
|
78
|
-
- Source path: `product.qty`
|
|
79
|
-
- Clear reason: "missing or empty"
|
|
80
|
-
|
|
81
|
-
#### 2. Required Field is Null/Empty
|
|
82
|
-
|
|
83
|
-
```typescript
|
|
84
|
-
const result = await mapper.map({
|
|
85
|
-
product: {
|
|
86
|
-
sku: '', // ❌ Empty string
|
|
87
|
-
qty: null // ❌ Null
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
// Both fail validation
|
|
92
|
-
{
|
|
93
|
-
success: false,
|
|
94
|
-
data: null,
|
|
95
|
-
errors: [
|
|
96
|
-
"Failed to map field 'sku': Required field 'sku' is missing or empty (source: product.sku)"
|
|
97
|
-
]
|
|
98
|
-
}
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
**Validation checks:**
|
|
102
|
-
- `value === null` ❌ Fails
|
|
103
|
-
- `value === undefined` ❌ Fails
|
|
104
|
-
- `value === ''` ❌ Fails (empty string)
|
|
105
|
-
- `value === 0` ✅ Passes (valid number)
|
|
106
|
-
- `value === false` ✅ Passes (valid boolean)
|
|
107
|
-
|
|
108
|
-
#### 3. Required Field Resolver Fails
|
|
109
|
-
|
|
110
|
-
```typescript
|
|
111
|
-
const mapper = new UniversalMapper({
|
|
112
|
-
fields: {
|
|
113
|
-
quantity: {
|
|
114
|
-
source: 'qty',
|
|
115
|
-
resolver: 'sdk.parseInt',
|
|
116
|
-
required: true
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
const result = await mapper.map({ qty: 'ABC' }); // Not a number
|
|
122
|
-
|
|
123
|
-
// Result:
|
|
124
|
-
{
|
|
125
|
-
success: false,
|
|
126
|
-
data: null,
|
|
127
|
-
errors: [
|
|
128
|
-
"Failed to map field 'quantity': Resolver 'sdk.parseInt' failed: Cannot parse 'ABC' as integer"
|
|
129
|
-
]
|
|
130
|
-
}
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
**Error includes:**
|
|
134
|
-
- Resolver name: `sdk.parseInt`
|
|
135
|
-
- Input value: `ABC`
|
|
136
|
-
- Original error: "Cannot parse 'ABC' as integer"
|
|
137
|
-
|
|
138
|
-
#### 4. Nested Required Field Fails
|
|
139
|
-
|
|
140
|
-
```typescript
|
|
141
|
-
const mapper = new UniversalMapper({
|
|
142
|
-
fields: {
|
|
143
|
-
customer: {
|
|
144
|
-
fields: {
|
|
145
|
-
email: { source: 'email', required: true },
|
|
146
|
-
firstName: { source: 'firstName', required: true }
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
const result = await mapper.map({
|
|
153
|
-
customer: { firstName: 'John' } // Missing email
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
// Result:
|
|
157
|
-
{
|
|
158
|
-
success: false,
|
|
159
|
-
data: null,
|
|
160
|
-
errors: [
|
|
161
|
-
"Failed to map field 'email': Required field 'email' is missing or empty"
|
|
162
|
-
]
|
|
163
|
-
}
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
**Nested errors propagate up** - Parent field also fails if any child required field fails.
|
|
167
|
-
|
|
168
|
-
#### 5. Resolver Not Found
|
|
169
|
-
|
|
170
|
-
```typescript
|
|
171
|
-
const mapper = new UniversalMapper({
|
|
172
|
-
fields: {
|
|
173
|
-
status: {
|
|
174
|
-
value: 'ACTIVE',
|
|
175
|
-
resolver: 'sdk.unknownResolver' // ❌ Doesn't exist
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
const result = await mapper.map({});
|
|
181
|
-
|
|
182
|
-
// Result:
|
|
183
|
-
{
|
|
184
|
-
success: false,
|
|
185
|
-
data: null,
|
|
186
|
-
errors: [
|
|
187
|
-
"Failed to map field 'status': Resolver 'sdk.unknownResolver' not found"
|
|
188
|
-
]
|
|
189
|
-
}
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
**Common cause:** Typo in resolver name or forgot to register custom resolver.
|
|
193
|
-
|
|
194
|
-
---
|
|
195
|
-
|
|
196
|
-
### ⚠️ Non-Fatal Errors (Processing Continues)
|
|
197
|
-
|
|
198
|
-
**Result:** `{ success: true, data: {...}, errors: [...] }`
|
|
199
|
-
|
|
200
|
-
The mapper **continues processing** but collects warnings for:
|
|
201
|
-
|
|
202
|
-
#### 1. Optional Field Missing (Skipped)
|
|
203
|
-
|
|
204
|
-
```typescript
|
|
205
|
-
const mapper = new UniversalMapper({
|
|
206
|
-
fields: {
|
|
207
|
-
sku: { source: 'sku', required: true },
|
|
208
|
-
description: { source: 'description' } // Optional, no defaultValue
|
|
209
|
-
}
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
const result = await mapper.map({ sku: 'ABC' }); // No description
|
|
213
|
-
|
|
214
|
-
// Result:
|
|
215
|
-
{
|
|
216
|
-
success: true, // ✅ Succeeds because required field present
|
|
217
|
-
data: { sku: 'ABC' }, // description not included (undefined fields are skipped)
|
|
218
|
-
errors: undefined,
|
|
219
|
-
skippedFields: ['description'] // Field names that were skipped
|
|
220
|
-
}
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
**Important:** Undefined optional fields are **automatically skipped** from the output (correct JSON behavior). Field names are tracked in `skippedFields` for reference.
|
|
224
|
-
|
|
225
|
-
**To always include a field:** Add `defaultValue` to the field configuration:
|
|
226
|
-
```typescript
|
|
227
|
-
{
|
|
228
|
-
description: {
|
|
229
|
-
source: 'description',
|
|
230
|
-
defaultValue: '' // Empty string if missing
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
#### 2. Optional Field Uses Default Value
|
|
236
|
-
|
|
237
|
-
```typescript
|
|
238
|
-
const mapper = new UniversalMapper({
|
|
239
|
-
fields: {
|
|
240
|
-
sku: { source: 'sku', required: true },
|
|
241
|
-
status: { source: 'status', defaultValue: 'ACTIVE' }
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
const result = await mapper.map({ sku: 'ABC' }); // No status
|
|
246
|
-
|
|
247
|
-
// Result:
|
|
248
|
-
{
|
|
249
|
-
success: true,
|
|
250
|
-
data: {
|
|
251
|
-
sku: 'ABC',
|
|
252
|
-
status: 'ACTIVE' // ✅ Default value used
|
|
253
|
-
},
|
|
254
|
-
errors: undefined
|
|
255
|
-
}
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
#### 3. Optional Field Resolver Fails
|
|
259
|
-
|
|
260
|
-
```typescript
|
|
261
|
-
const mapper = new UniversalMapper({
|
|
262
|
-
fields: {
|
|
263
|
-
sku: { source: 'sku', required: true },
|
|
264
|
-
quantity: {
|
|
265
|
-
source: 'qty',
|
|
266
|
-
resolver: 'sdk.parseInt' // Optional field, resolver fails
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
const result = await mapper.map({ sku: 'ABC', qty: 'invalid' });
|
|
272
|
-
|
|
273
|
-
// Result:
|
|
274
|
-
{
|
|
275
|
-
success: true, // ✅ Required field succeeded
|
|
276
|
-
data: { sku: 'ABC' },
|
|
277
|
-
errors: [
|
|
278
|
-
"Failed to map field 'quantity': Resolver 'sdk.parseInt' failed: Cannot parse 'invalid' as integer"
|
|
279
|
-
]
|
|
280
|
-
}
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
**Note:** Error is logged but doesn't stop processing since field is optional.
|
|
284
|
-
|
|
285
|
-
---
|
|
286
|
-
|
|
287
|
-
## Array Error Handling
|
|
288
|
-
|
|
289
|
-
The UniversalMapper has **built-in array error handling** that processes each item individually and aggregates results.
|
|
290
|
-
|
|
291
|
-
### Automatic Array Processing
|
|
292
|
-
|
|
293
|
-
```typescript
|
|
294
|
-
const mapper = new UniversalMapper({
|
|
295
|
-
fields: {
|
|
296
|
-
sku: { source: 'sku', required: true },
|
|
297
|
-
quantity: { source: 'qty', resolver: 'sdk.parseInt', required: true }
|
|
298
|
-
}
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
// Pass array directly to mapper
|
|
302
|
-
const records = [
|
|
303
|
-
{ sku: 'ABC', qty: '10' }, // ✅ Valid
|
|
304
|
-
{ sku: 'DEF' }, // ❌ Missing qty
|
|
305
|
-
{ sku: 'GHI', qty: '20' }, // ✅ Valid
|
|
306
|
-
{ qty: '30' } // ❌ Missing sku
|
|
307
|
-
];
|
|
308
|
-
|
|
309
|
-
const result = await mapper.map(records);
|
|
310
|
-
|
|
311
|
-
// Result:
|
|
312
|
-
{
|
|
313
|
-
success: true, // ✅ At least one item succeeded
|
|
314
|
-
data: [
|
|
315
|
-
{ sku: 'ABC', quantity: 10 },
|
|
316
|
-
{ sku: 'GHI', quantity: 20 }
|
|
317
|
-
],
|
|
318
|
-
errors: [
|
|
319
|
-
"Failed to map field 'quantity': Required field 'quantity' is missing or empty (source: qty)",
|
|
320
|
-
"Failed to map field 'sku': Required field 'sku' is missing or empty (source: sku)"
|
|
321
|
-
]
|
|
322
|
-
}
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
**Behavior:**
|
|
326
|
-
- ✅ Successful items collected in `data` array
|
|
327
|
-
- ❌ Failed items logged in `errors` array
|
|
328
|
-
- Result is `success: true` if **at least one item** succeeds
|
|
329
|
-
- Result is `success: false` if **all items** fail
|
|
330
|
-
|
|
331
|
-
### Processing Individual Records
|
|
332
|
-
|
|
333
|
-
```typescript
|
|
334
|
-
// Production pattern: Process records one-by-one for detailed error tracking
|
|
335
|
-
const mappedRecords = [];
|
|
336
|
-
const failedRecords = [];
|
|
337
|
-
|
|
338
|
-
for (let i = 0; i < records.length; i++) {
|
|
339
|
-
const record = records[i];
|
|
340
|
-
const recordId = record.sku || record.id || `index-${i}`;
|
|
341
|
-
|
|
342
|
-
const mappingResult = await mapper.map(record);
|
|
343
|
-
|
|
344
|
-
if (mappingResult.success) {
|
|
345
|
-
mappedRecords.push(mappingResult.data);
|
|
346
|
-
log.info(`✅ Record ${recordId} mapped successfully`);
|
|
347
|
-
} else {
|
|
348
|
-
failedRecords.push({
|
|
349
|
-
recordId,
|
|
350
|
-
index: i,
|
|
351
|
-
record,
|
|
352
|
-
errors: mappingResult.errors
|
|
353
|
-
});
|
|
354
|
-
log.error(`❌ Record ${recordId} mapping failed`, {
|
|
355
|
-
errors: mappingResult.errors,
|
|
356
|
-
record: JSON.stringify(record)
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
log.info('Mapping complete', {
|
|
362
|
-
total: records.length,
|
|
363
|
-
successful: mappedRecords.length,
|
|
364
|
-
failed: failedRecords.length,
|
|
365
|
-
successRate: `${((mappedRecords.length / records.length) * 100).toFixed(2)}%`
|
|
366
|
-
});
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
### Nested Array Error Handling
|
|
370
|
-
|
|
371
|
-
```typescript
|
|
372
|
-
const mapper = new UniversalMapper({
|
|
373
|
-
fields: {
|
|
374
|
-
orderId: { source: 'id', required: true },
|
|
375
|
-
items: {
|
|
376
|
-
source: 'items',
|
|
377
|
-
isArray: true,
|
|
378
|
-
fields: {
|
|
379
|
-
sku: { source: 'sku', required: true },
|
|
380
|
-
quantity: { source: 'qty', required: true }
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
});
|
|
385
|
-
|
|
386
|
-
const result = await mapper.map({
|
|
387
|
-
id: 'ORDER-123',
|
|
388
|
-
items: [
|
|
389
|
-
{ sku: 'ABC', qty: 10 }, // ✅ Valid
|
|
390
|
-
{ qty: 5 } // ❌ Missing sku
|
|
391
|
-
]
|
|
392
|
-
});
|
|
393
|
-
|
|
394
|
-
// Result:
|
|
395
|
-
{
|
|
396
|
-
success: false, // ❌ Required nested field failed
|
|
397
|
-
data: null,
|
|
398
|
-
errors: [
|
|
399
|
-
"Failed to map field 'items': Required field 'sku' is missing or empty"
|
|
400
|
-
]
|
|
401
|
-
}
|
|
402
|
-
```
|
|
403
|
-
|
|
404
|
-
**Nested array errors propagate up** - Parent field fails if any array item has required field errors.
|
|
405
|
-
|
|
406
|
-
---
|
|
407
|
-
|
|
408
|
-
## Error Result Structure
|
|
409
|
-
|
|
410
|
-
Every mapping operation returns a `MappingResult`:
|
|
411
|
-
|
|
412
|
-
```typescript
|
|
413
|
-
interface MappingResult {
|
|
414
|
-
/** Whether mapping succeeded */
|
|
415
|
-
success: boolean;
|
|
416
|
-
|
|
417
|
-
/** Mapped data (null if failed) */
|
|
418
|
-
data: any;
|
|
419
|
-
|
|
420
|
-
/** Errors encountered during mapping */
|
|
421
|
-
errors?: string[];
|
|
422
|
-
|
|
423
|
-
/** Fields that were skipped because they were undefined (field names only) */
|
|
424
|
-
skippedFields?: string[];
|
|
425
|
-
}
|
|
426
|
-
```
|
|
427
|
-
|
|
428
|
-
### Success Cases
|
|
429
|
-
|
|
430
|
-
#### 1. Complete Success
|
|
431
|
-
|
|
432
|
-
```typescript
|
|
433
|
-
{
|
|
434
|
-
success: true,
|
|
435
|
-
data: { sku: 'ABC', quantity: 10 },
|
|
436
|
-
errors: undefined // No errors
|
|
437
|
-
}
|
|
438
|
-
```
|
|
439
|
-
|
|
440
|
-
#### 2. Success with Skipped Fields
|
|
441
|
-
|
|
442
|
-
```typescript
|
|
443
|
-
{
|
|
444
|
-
success: true,
|
|
445
|
-
data: { sku: 'ABC' }, // description field skipped (was undefined)
|
|
446
|
-
errors: undefined,
|
|
447
|
-
skippedFields: ['description'] // Field names that were skipped
|
|
448
|
-
}
|
|
449
|
-
```
|
|
450
|
-
|
|
451
|
-
#### 3. Success with Resolver Errors (Optional Fields)
|
|
452
|
-
|
|
453
|
-
```typescript
|
|
454
|
-
{
|
|
455
|
-
success: true,
|
|
456
|
-
data: { sku: 'ABC' },
|
|
457
|
-
errors: [
|
|
458
|
-
"Failed to map field 'description': Resolver 'sdk.uppercase' failed: Cannot uppercase null"
|
|
459
|
-
]
|
|
460
|
-
}
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
### Failure Cases
|
|
464
|
-
|
|
465
|
-
#### 1. Required Field Missing
|
|
466
|
-
|
|
467
|
-
```typescript
|
|
468
|
-
{
|
|
469
|
-
success: false,
|
|
470
|
-
data: null,
|
|
471
|
-
errors: [
|
|
472
|
-
"Failed to map field 'sku': Required field 'sku' is missing or empty (source: product.sku)"
|
|
473
|
-
]
|
|
474
|
-
}
|
|
475
|
-
```
|
|
476
|
-
|
|
477
|
-
#### 2. Multiple Errors (Array Processing)
|
|
478
|
-
|
|
479
|
-
```typescript
|
|
480
|
-
{
|
|
481
|
-
success: true, // Some items succeeded
|
|
482
|
-
data: [
|
|
483
|
-
{ sku: 'ABC', quantity: 10 },
|
|
484
|
-
{ sku: 'DEF', quantity: 20 }
|
|
485
|
-
],
|
|
486
|
-
errors: [
|
|
487
|
-
"Failed to map field 'sku': Required field 'sku' is missing or empty",
|
|
488
|
-
"Failed to map field 'quantity': Resolver 'sdk.parseInt' failed: Cannot parse 'XYZ' as integer"
|
|
489
|
-
]
|
|
490
|
-
}
|
|
491
|
-
```
|
|
492
|
-
|
|
493
|
-
---
|
|
494
|
-
|
|
495
|
-
## Common Error Scenarios
|
|
496
|
-
|
|
497
|
-
### Scenario 1: Required Field Missing
|
|
498
|
-
|
|
499
|
-
**Problem:**
|
|
500
|
-
```typescript
|
|
501
|
-
const mapper = new UniversalMapper({
|
|
502
|
-
fields: {
|
|
503
|
-
sku: { source: 'product.sku', required: true }
|
|
504
|
-
}
|
|
505
|
-
});
|
|
506
|
-
|
|
507
|
-
const result = await mapper.map({ product: {} });
|
|
508
|
-
```
|
|
509
|
-
|
|
510
|
-
**Error:**
|
|
511
|
-
```
|
|
512
|
-
Failed to map field 'sku': Required field 'sku' is missing or empty (source: product.sku)
|
|
513
|
-
```
|
|
514
|
-
|
|
515
|
-
**Solutions:**
|
|
516
|
-
|
|
517
|
-
✅ **Option 1: Fix source data**
|
|
518
|
-
```typescript
|
|
519
|
-
await mapper.map({ product: { sku: 'ABC123' } });
|
|
520
|
-
```
|
|
521
|
-
|
|
522
|
-
✅ **Option 2: Make field optional with default**
|
|
523
|
-
```typescript
|
|
524
|
-
const mapper = new UniversalMapper({
|
|
525
|
-
fields: {
|
|
526
|
-
sku: {
|
|
527
|
-
source: 'product.sku',
|
|
528
|
-
defaultValue: 'UNKNOWN' // No longer required
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
});
|
|
532
|
-
```
|
|
533
|
-
|
|
534
|
-
✅ **Option 3: Use resolver to generate value**
|
|
535
|
-
```typescript
|
|
536
|
-
const mapper = new UniversalMapper({
|
|
537
|
-
fields: {
|
|
538
|
-
sku: {
|
|
539
|
-
resolver: 'sdk.uuid', // Generates UUID if source missing
|
|
540
|
-
required: true
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
});
|
|
544
|
-
```
|
|
545
|
-
|
|
546
|
-
---
|
|
547
|
-
|
|
548
|
-
### Scenario 2: Resolver Fails on Invalid Data
|
|
549
|
-
|
|
550
|
-
**Problem:**
|
|
551
|
-
```typescript
|
|
552
|
-
const mapper = new UniversalMapper({
|
|
553
|
-
fields: {
|
|
554
|
-
quantity: {
|
|
555
|
-
source: 'qty',
|
|
556
|
-
resolver: 'sdk.parseInt',
|
|
557
|
-
required: true
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
});
|
|
561
|
-
|
|
562
|
-
const result = await mapper.map({ qty: 'ABC' });
|
|
563
|
-
```
|
|
564
|
-
|
|
565
|
-
**Error:**
|
|
566
|
-
```
|
|
567
|
-
Failed to map field 'quantity': Resolver 'sdk.parseInt' failed: Cannot parse 'ABC' as integer
|
|
568
|
-
```
|
|
569
|
-
|
|
570
|
-
**Solutions:**
|
|
571
|
-
|
|
572
|
-
✅ **Option 1: Fix source data**
|
|
573
|
-
```typescript
|
|
574
|
-
await mapper.map({ qty: '123' }); // Valid number string
|
|
575
|
-
```
|
|
576
|
-
|
|
577
|
-
✅ **Option 2: Use parseInt with optional field and default**
|
|
578
|
-
```typescript
|
|
579
|
-
const mapper = new UniversalMapper({
|
|
580
|
-
fields: {
|
|
581
|
-
quantity: {
|
|
582
|
-
source: 'qty',
|
|
583
|
-
resolver: 'sdk.parseInt', // Already safe, returns 0 for invalid input
|
|
584
|
-
required: false, // Make optional to allow default
|
|
585
|
-
defaultValue: 0 // Fallback for missing/invalid
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
});
|
|
589
|
-
```
|
|
590
|
-
|
|
591
|
-
✅ **Option 3: Validate before mapping**
|
|
592
|
-
```typescript
|
|
593
|
-
const sourceData = { qty: 'ABC' };
|
|
594
|
-
|
|
595
|
-
// Pre-validation
|
|
596
|
-
if (!/^\d+$/.test(sourceData.qty)) {
|
|
597
|
-
throw new Error(`Invalid quantity: ${sourceData.qty}`);
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
await mapper.map(sourceData);
|
|
601
|
-
```
|
|
602
|
-
|
|
603
|
-
---
|
|
604
|
-
|
|
605
|
-
### Scenario 3: Resolver Not Found
|
|
606
|
-
|
|
607
|
-
**Problem:**
|
|
608
|
-
```typescript
|
|
609
|
-
const mapper = new UniversalMapper({
|
|
610
|
-
fields: {
|
|
611
|
-
status: {
|
|
612
|
-
value: 'active',
|
|
613
|
-
resolver: 'sdk.uppercas' // ❌ Typo: should be 'sdk.uppercase'
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
});
|
|
617
|
-
```
|
|
618
|
-
|
|
619
|
-
**Error:**
|
|
620
|
-
```
|
|
621
|
-
Failed to map field 'status': Resolver 'sdk.uppercas' not found
|
|
622
|
-
```
|
|
623
|
-
|
|
624
|
-
**Solutions:**
|
|
625
|
-
|
|
626
|
-
✅ **Option 1: Fix typo**
|
|
627
|
-
```typescript
|
|
628
|
-
resolver: 'sdk.uppercase' // ✅ Correct spelling
|
|
629
|
-
```
|
|
630
|
-
|
|
631
|
-
✅ **Option 2: Check available resolvers**
|
|
632
|
-
```typescript
|
|
633
|
-
console.log(mapper.getResolverNames());
|
|
634
|
-
// ['sdk.uppercase', 'sdk.lowercase', 'sdk.parseInt', ...]
|
|
635
|
-
```
|
|
636
|
-
|
|
637
|
-
✅ **Option 3: Register custom resolver if needed**
|
|
638
|
-
```typescript
|
|
639
|
-
mapper.registerResolver('custom.uppercas', (value) => {
|
|
640
|
-
return String(value).toUpperCase();
|
|
641
|
-
});
|
|
642
|
-
```
|
|
643
|
-
|
|
644
|
-
---
|
|
645
|
-
|
|
646
|
-
### Scenario 4: Contradictory Configuration
|
|
647
|
-
|
|
648
|
-
**Problem:**
|
|
649
|
-
```typescript
|
|
650
|
-
const mapper = new UniversalMapper({
|
|
651
|
-
fields: {
|
|
652
|
-
status: {
|
|
653
|
-
source: 'status',
|
|
654
|
-
required: true,
|
|
655
|
-
defaultValue: 'ACTIVE' // ❌ Contradictory: required + defaultValue
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
});
|
|
659
|
-
```
|
|
660
|
-
|
|
661
|
-
**Warning (logged to console):**
|
|
662
|
-
```
|
|
663
|
-
⚠️ Field 'status' has both required=true and defaultValue.
|
|
664
|
-
This is contradictory - the defaultValue will never be used because required validation
|
|
665
|
-
fails before defaultValue is applied. Consider using required=false if you want a fallback value.
|
|
666
|
-
```
|
|
667
|
-
|
|
668
|
-
**Solution:**
|
|
669
|
-
```typescript
|
|
670
|
-
const mapper = new UniversalMapper({
|
|
671
|
-
fields: {
|
|
672
|
-
status: {
|
|
673
|
-
source: 'status',
|
|
674
|
-
required: false, // ✅ Allow missing with default
|
|
675
|
-
defaultValue: 'ACTIVE'
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
});
|
|
679
|
-
```
|
|
680
|
-
|
|
681
|
-
---
|
|
682
|
-
|
|
683
|
-
### Scenario 5: Nested Required Field Missing
|
|
684
|
-
|
|
685
|
-
**Problem:**
|
|
686
|
-
```typescript
|
|
687
|
-
const mapper = new UniversalMapper({
|
|
688
|
-
fields: {
|
|
689
|
-
customer: {
|
|
690
|
-
fields: {
|
|
691
|
-
email: { source: 'email', required: true },
|
|
692
|
-
phone: { source: 'phone', required: true }
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
});
|
|
697
|
-
|
|
698
|
-
const result = await mapper.map({
|
|
699
|
-
customer: { email: 'test@example.com' } // Missing phone
|
|
700
|
-
});
|
|
701
|
-
```
|
|
702
|
-
|
|
703
|
-
**Error:**
|
|
704
|
-
```
|
|
705
|
-
Failed to map field 'phone': Required field 'phone' is missing or empty
|
|
706
|
-
```
|
|
707
|
-
|
|
708
|
-
**Solutions:**
|
|
709
|
-
|
|
710
|
-
✅ **Option 1: Provide required field**
|
|
711
|
-
```typescript
|
|
712
|
-
await mapper.map({
|
|
713
|
-
customer: {
|
|
714
|
-
email: 'test@example.com',
|
|
715
|
-
phone: '555-1234' // ✅ Provided
|
|
716
|
-
}
|
|
717
|
-
});
|
|
718
|
-
```
|
|
719
|
-
|
|
720
|
-
✅ **Option 2: Make field optional**
|
|
721
|
-
```typescript
|
|
722
|
-
const mapper = new UniversalMapper({
|
|
723
|
-
fields: {
|
|
724
|
-
customer: {
|
|
725
|
-
fields: {
|
|
726
|
-
email: { source: 'email', required: true },
|
|
727
|
-
phone: { source: 'phone', required: false } // ✅ Optional
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
});
|
|
732
|
-
```
|
|
733
|
-
|
|
734
|
-
---
|
|
735
|
-
|
|
736
|
-
## Production Patterns
|
|
737
|
-
|
|
738
|
-
### Pattern 1: Individual Record Error Tracking
|
|
739
|
-
|
|
740
|
-
```typescript
|
|
741
|
-
import { UniversalMapper } from '@fluentcommerce/fc-connect-sdk';
|
|
742
|
-
|
|
743
|
-
interface RecordError {
|
|
744
|
-
recordId: string;
|
|
745
|
-
index: number;
|
|
746
|
-
error: string[];
|
|
747
|
-
record: any;
|
|
748
|
-
timestamp: string;
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
async function processRecords(
|
|
752
|
-
records: any[],
|
|
753
|
-
mapper: UniversalMapper,
|
|
754
|
-
log: any
|
|
755
|
-
): Promise<{
|
|
756
|
-
successful: any[];
|
|
757
|
-
failed: RecordError[];
|
|
758
|
-
summary: string;
|
|
759
|
-
}> {
|
|
760
|
-
const successful = [];
|
|
761
|
-
const failed: RecordError[] = [];
|
|
762
|
-
|
|
763
|
-
for (let i = 0; i < records.length; i++) {
|
|
764
|
-
const record = records[i];
|
|
765
|
-
const recordId = record.id || record.sku || `index-${i}`;
|
|
766
|
-
|
|
767
|
-
try {
|
|
768
|
-
const mappingResult = await mapper.map(record);
|
|
769
|
-
|
|
770
|
-
if (mappingResult.success) {
|
|
771
|
-
successful.push(mappingResult.data);
|
|
772
|
-
log.info(`✅ Record ${recordId} processed`, { index: i });
|
|
773
|
-
} else {
|
|
774
|
-
failed.push({
|
|
775
|
-
recordId,
|
|
776
|
-
index: i,
|
|
777
|
-
error: mappingResult.errors || ['Unknown error'],
|
|
778
|
-
record,
|
|
779
|
-
timestamp: new Date().toISOString()
|
|
780
|
-
});
|
|
781
|
-
log.error(`❌ Record ${recordId} failed`, {
|
|
782
|
-
index: i,
|
|
783
|
-
errors: mappingResult.errors
|
|
784
|
-
});
|
|
785
|
-
}
|
|
786
|
-
} catch (error: any) {
|
|
787
|
-
// Unexpected error (config issue, etc.)
|
|
788
|
-
failed.push({
|
|
789
|
-
recordId,
|
|
790
|
-
index: i,
|
|
791
|
-
error: [error.message],
|
|
792
|
-
record,
|
|
793
|
-
timestamp: new Date().toISOString()
|
|
794
|
-
});
|
|
795
|
-
log.error(`💥 Record ${recordId} threw exception`, {
|
|
796
|
-
error: error.message,
|
|
797
|
-
stack: error.stack
|
|
798
|
-
});
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
return {
|
|
803
|
-
successful,
|
|
804
|
-
failed,
|
|
805
|
-
summary: `Processed ${records.length} records: ${successful.length} succeeded, ${failed.length} failed`
|
|
806
|
-
};
|
|
807
|
-
}
|
|
808
|
-
```
|
|
809
|
-
|
|
810
|
-
### Pattern 2: Error Categorization
|
|
811
|
-
|
|
812
|
-
```typescript
|
|
813
|
-
enum ErrorCategory {
|
|
814
|
-
VALIDATION = 'validation',
|
|
815
|
-
MAPPING = 'mapping',
|
|
816
|
-
RESOLVER = 'resolver',
|
|
817
|
-
UNKNOWN = 'unknown'
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
interface CategorizedError {
|
|
821
|
-
category: ErrorCategory;
|
|
822
|
-
recordId: string;
|
|
823
|
-
message: string;
|
|
824
|
-
record: any;
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
function categorizeError(error: string, record: any, recordId: string): CategorizedError {
|
|
828
|
-
if (error.includes('Required field')) {
|
|
829
|
-
return {
|
|
830
|
-
category: ErrorCategory.VALIDATION,
|
|
831
|
-
recordId,
|
|
832
|
-
message: error,
|
|
833
|
-
record
|
|
834
|
-
};
|
|
835
|
-
} else if (error.includes('Resolver')) {
|
|
836
|
-
return {
|
|
837
|
-
category: ErrorCategory.RESOLVER,
|
|
838
|
-
recordId,
|
|
839
|
-
message: error,
|
|
840
|
-
record
|
|
841
|
-
};
|
|
842
|
-
} else if (error.includes('Failed to map')) {
|
|
843
|
-
return {
|
|
844
|
-
category: ErrorCategory.MAPPING,
|
|
845
|
-
recordId,
|
|
846
|
-
message: error,
|
|
847
|
-
record
|
|
848
|
-
};
|
|
849
|
-
} else {
|
|
850
|
-
return {
|
|
851
|
-
category: ErrorCategory.UNKNOWN,
|
|
852
|
-
recordId,
|
|
853
|
-
message: error,
|
|
854
|
-
record
|
|
855
|
-
};
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
|
|
859
|
-
async function processWithCategorization(
|
|
860
|
-
records: any[],
|
|
861
|
-
mapper: UniversalMapper,
|
|
862
|
-
log: any
|
|
863
|
-
) {
|
|
864
|
-
const results = {
|
|
865
|
-
successful: [],
|
|
866
|
-
errors: {
|
|
867
|
-
[ErrorCategory.VALIDATION]: [] as CategorizedError[],
|
|
868
|
-
[ErrorCategory.MAPPING]: [] as CategorizedError[],
|
|
869
|
-
[ErrorCategory.RESOLVER]: [] as CategorizedError[],
|
|
870
|
-
[ErrorCategory.UNKNOWN]: [] as CategorizedError[]
|
|
871
|
-
}
|
|
872
|
-
};
|
|
873
|
-
|
|
874
|
-
for (let i = 0; i < records.length; i++) {
|
|
875
|
-
const record = records[i];
|
|
876
|
-
const recordId = record.id || `index-${i}`;
|
|
877
|
-
|
|
878
|
-
const mappingResult = await mapper.map(record);
|
|
879
|
-
|
|
880
|
-
if (mappingResult.success) {
|
|
881
|
-
results.successful.push(mappingResult.data);
|
|
882
|
-
} else {
|
|
883
|
-
// Categorize each error
|
|
884
|
-
(mappingResult.errors || []).forEach(errorMsg => {
|
|
885
|
-
const categorized = categorizeError(errorMsg, record, recordId);
|
|
886
|
-
results.errors[categorized.category].push(categorized);
|
|
887
|
-
});
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
// Log summary by category
|
|
892
|
-
log.info('Processing complete', {
|
|
893
|
-
successful: results.successful.length,
|
|
894
|
-
validationErrors: results.errors[ErrorCategory.VALIDATION].length,
|
|
895
|
-
mappingErrors: results.errors[ErrorCategory.MAPPING].length,
|
|
896
|
-
resolverErrors: results.errors[ErrorCategory.RESOLVER].length,
|
|
897
|
-
unknownErrors: results.errors[ErrorCategory.UNKNOWN].length
|
|
898
|
-
});
|
|
899
|
-
|
|
900
|
-
return results;
|
|
901
|
-
}
|
|
902
|
-
```
|
|
903
|
-
|
|
904
|
-
### Pattern 3: Using PartialBatchRecovery for Retries
|
|
905
|
-
|
|
906
|
-
```typescript
|
|
907
|
-
import { PartialBatchRecovery } from '@fluentcommerce/fc-connect-sdk';
|
|
908
|
-
|
|
909
|
-
async function processWithRecovery(
|
|
910
|
-
records: any[],
|
|
911
|
-
mapper: UniversalMapper,
|
|
912
|
-
client: FluentClient,
|
|
913
|
-
jobId: string,
|
|
914
|
-
log: any
|
|
915
|
-
) {
|
|
916
|
-
const recovery = new PartialBatchRecovery(log);
|
|
917
|
-
|
|
918
|
-
const result = await recovery.processBatchWithRecovery(
|
|
919
|
-
records,
|
|
920
|
-
async (batch) => {
|
|
921
|
-
// Map records
|
|
922
|
-
const mapped = [];
|
|
923
|
-
for (const rec of batch) {
|
|
924
|
-
const mappingResult = await mapper.map(rec);
|
|
925
|
-
if (mappingResult.success) {
|
|
926
|
-
mapped.push(mappingResult.data);
|
|
927
|
-
} else {
|
|
928
|
-
throw new Error(`Mapping failed: ${mappingResult.errors?.join(', ')}`);
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
// Send to Fluent
|
|
933
|
-
return client.sendBatch(jobId, mapped);
|
|
934
|
-
},
|
|
935
|
-
{
|
|
936
|
-
maxRetries: 3,
|
|
937
|
-
retryOnlyFailed: true,
|
|
938
|
-
checkpointKey: `job-${jobId}`,
|
|
939
|
-
retryDelayMs: 1000
|
|
940
|
-
}
|
|
941
|
-
);
|
|
942
|
-
|
|
943
|
-
log.info('Batch processing complete', {
|
|
944
|
-
total: result.totalRecords,
|
|
945
|
-
successful: result.successCount,
|
|
946
|
-
failed: result.failedCount,
|
|
947
|
-
failedRecords: result.failedRecords
|
|
948
|
-
});
|
|
949
|
-
|
|
950
|
-
return result;
|
|
951
|
-
}
|
|
952
|
-
```
|
|
953
|
-
|
|
954
|
-
---
|
|
955
|
-
|
|
956
|
-
## Troubleshooting Guide
|
|
957
|
-
|
|
958
|
-
### Quick Diagnostic Checklist
|
|
959
|
-
|
|
960
|
-
When you encounter mapping errors, check:
|
|
961
|
-
|
|
962
|
-
1. **✅ Is the field required?**
|
|
963
|
-
- Check `required: true` in field config
|
|
964
|
-
- Required fields must have non-null/non-empty values
|
|
965
|
-
|
|
966
|
-
2. **✅ Is the source path correct?**
|
|
967
|
-
- Verify path matches actual data structure
|
|
968
|
-
- Check for typos in field names
|
|
969
|
-
- Use `console.log(JSON.stringify(sourceData))` to inspect
|
|
970
|
-
|
|
971
|
-
3. **✅ Is the resolver name correct?**
|
|
972
|
-
- Check spelling: `sdk.parseInt` not `sdk.parseInt`
|
|
973
|
-
- Verify with `mapper.getResolverNames()`
|
|
974
|
-
- Ensure custom resolvers are registered
|
|
975
|
-
|
|
976
|
-
4. **✅ Is the data type compatible with resolver?**
|
|
977
|
-
- `sdk.parseInt` expects string/number (returns 0 for invalid, already safe)
|
|
978
|
-
- `sdk.parseFloat` expects string/number (returns 0 for invalid, already safe)
|
|
979
|
-
- `sdk.uppercase` expects string
|
|
980
|
-
- Make fields optional with `defaultValue` for fallback behavior
|
|
981
|
-
|
|
982
|
-
5. **✅ Are there nested required fields?**
|
|
983
|
-
- Check all nested field configs
|
|
984
|
-
- Nested required fields propagate errors up
|
|
985
|
-
|
|
986
|
-
### Common Error Messages
|
|
987
|
-
|
|
988
|
-
| Error Message | Cause | Solution |
|
|
989
|
-
|---------------|-------|----------|
|
|
990
|
-
| `Required field 'X' is missing or empty` | Field is null/undefined/'' | Provide value or make optional |
|
|
991
|
-
| `Resolver 'X' not found` | Typo or unregistered resolver | Fix spelling or register resolver |
|
|
992
|
-
| `Resolver 'X' failed: ...` | Resolver threw during execution | Fix input data or use safe resolver |
|
|
993
|
-
| `Failed to map field 'X': ...` | Generic mapping failure | Check field config and data |
|
|
994
|
-
|
|
995
|
-
### Debug Logging
|
|
996
|
-
|
|
997
|
-
```typescript
|
|
998
|
-
const mockLogger = {
|
|
999
|
-
info: (...args: any[]) => console.log('[INFO]', ...args),
|
|
1000
|
-
debug: (...args: any[]) => console.log('[DEBUG]', ...args),
|
|
1001
|
-
warn: (...args: any[]) => console.warn('[WARN]', ...args),
|
|
1002
|
-
error: (...args: any[]) => console.error('[ERROR]', ...args)
|
|
1003
|
-
};
|
|
1004
|
-
|
|
1005
|
-
const mapper = new UniversalMapper(config, { logger: mockLogger });
|
|
1006
|
-
|
|
1007
|
-
// Now see detailed logs during mapping
|
|
1008
|
-
const result = await mapper.map(data);
|
|
1009
|
-
```
|
|
1010
|
-
|
|
1011
|
-
---
|
|
1012
|
-
|
|
1013
|
-
## Best Practices
|
|
1014
|
-
|
|
1015
|
-
### ✅ DO:
|
|
1016
|
-
|
|
1017
|
-
1. **Always check `mappingResult.success`**
|
|
1018
|
-
```typescript
|
|
1019
|
-
if (mappingResult.success) {
|
|
1020
|
-
await client.sendBatch(jobId, mappingResult.data);
|
|
1021
|
-
} else {
|
|
1022
|
-
log.error('Mapping failed', mappingResult.errors);
|
|
1023
|
-
}
|
|
1024
|
-
```
|
|
1025
|
-
|
|
1026
|
-
2. **Log detailed error context**
|
|
1027
|
-
```typescript
|
|
1028
|
-
log.error('Record mapping failed', {
|
|
1029
|
-
recordId,
|
|
1030
|
-
errors: mappingResult.errors,
|
|
1031
|
-
record: JSON.stringify(record)
|
|
1032
|
-
});
|
|
1033
|
-
```
|
|
1034
|
-
|
|
1035
|
-
3. **Use optional fields with defaults for non-critical data**
|
|
1036
|
-
```typescript
|
|
1037
|
-
description: { source: 'desc', defaultValue: '' }
|
|
1038
|
-
```
|
|
1039
|
-
|
|
1040
|
-
4. **Process records individually for better error tracking**
|
|
1041
|
-
```typescript
|
|
1042
|
-
for (const record of records) {
|
|
1043
|
-
const result = await mapper.map(record);
|
|
1044
|
-
// Handle individually
|
|
1045
|
-
}
|
|
1046
|
-
```
|
|
1047
|
-
|
|
1048
|
-
5. **Categorize errors for monitoring**
|
|
1049
|
-
```typescript
|
|
1050
|
-
if (error.includes('Resolver')) {
|
|
1051
|
-
metrics.incrementResolverErrors();
|
|
1052
|
-
}
|
|
1053
|
-
```
|
|
1054
|
-
|
|
1055
|
-
### ❌ DON'T:
|
|
1056
|
-
|
|
1057
|
-
1. **Don't ignore `mappingResult.errors`**
|
|
1058
|
-
```typescript
|
|
1059
|
-
// ❌ BAD
|
|
1060
|
-
const result = await mapper.map(data);
|
|
1061
|
-
await client.sendBatch(jobId, result.data); // Might be null!
|
|
1062
|
-
|
|
1063
|
-
// ✅ GOOD
|
|
1064
|
-
if (result.success) {
|
|
1065
|
-
await client.sendBatch(jobId, result.data);
|
|
1066
|
-
}
|
|
1067
|
-
```
|
|
1068
|
-
|
|
1069
|
-
2. **Don't use required + defaultValue together**
|
|
1070
|
-
```typescript
|
|
1071
|
-
// ❌ BAD - defaultValue never used
|
|
1072
|
-
{ source: 'field', required: true, defaultValue: 'X' }
|
|
1073
|
-
|
|
1074
|
-
// ✅ GOOD - Optional with default
|
|
1075
|
-
{ source: 'field', required: false, defaultValue: 'X' }
|
|
1076
|
-
```
|
|
1077
|
-
|
|
1078
|
-
3. **Don't catch and ignore errors silently**
|
|
1079
|
-
```typescript
|
|
1080
|
-
// ❌ BAD
|
|
1081
|
-
try {
|
|
1082
|
-
await mapper.map(record);
|
|
1083
|
-
} catch (error) {
|
|
1084
|
-
// Silently ignored
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
// ✅ GOOD
|
|
1088
|
-
try {
|
|
1089
|
-
const result = await mapper.map(record);
|
|
1090
|
-
if (!result.success) {
|
|
1091
|
-
log.error('Mapping failed', result.errors);
|
|
1092
|
-
}
|
|
1093
|
-
} catch (error) {
|
|
1094
|
-
log.error('Unexpected error', error);
|
|
1095
|
-
}
|
|
1096
|
-
```
|
|
1097
|
-
|
|
1098
|
-
4. **Don't assume all array items succeed**
|
|
1099
|
-
```typescript
|
|
1100
|
-
// ❌ BAD
|
|
1101
|
-
const result = await mapper.map(records);
|
|
1102
|
-
console.log(`Processed ${records.length} records`);
|
|
1103
|
-
|
|
1104
|
-
// ✅ GOOD
|
|
1105
|
-
const result = await mapper.map(records);
|
|
1106
|
-
console.log(`Processed ${result.data.length}/${records.length} records`);
|
|
1107
|
-
if (result.errors) {
|
|
1108
|
-
console.log(`Errors: ${result.errors.length}`);
|
|
1109
|
-
}
|
|
1110
|
-
```
|
|
1111
|
-
|
|
1112
|
-
---
|
|
1113
|
-
|
|
1114
|
-
## Related Documentation
|
|
1115
|
-
|
|
1116
|
-
- [Mapping Foundations](./mapping-01-foundations.md) - Core mapping concepts
|
|
1117
|
-
- [Custom Resolvers](../resolvers/mapping-resolvers-resolver-guide.md) - Building custom resolvers
|
|
1118
|
-
- [Error Handling Pattern Guide](../../../03-PATTERN-GUIDES/error-handling/error-handling-readme.md) - General error handling
|
|
1119
|
-
- [GraphQL Mutation Mapper Error Handling](../graphql-mutation-mapping/modules/graphql-mutation-mapping-11-error-handling.md) - GraphQL-specific errors
|
|
1120
|
-
|
|
1121
|
-
---
|
|
1122
|
-
|
|
1123
|
-
## Summary
|
|
1124
|
-
|
|
1125
|
-
**Key Takeaways:**
|
|
1126
|
-
|
|
1127
|
-
1. ✅ **UniversalMapper provides built-in error handling** - No need to wrap each record in try-catch
|
|
1128
|
-
2. ✅ **Required fields fail-fast** - Prevents partial/corrupt data
|
|
1129
|
-
3. ✅ **Optional fields continue with warnings** - Flexible error handling
|
|
1130
|
-
4. ✅ **Array processing is automatic** - Each item processed individually
|
|
1131
|
-
5. ✅ **Rich error context** - Field name, source path, resolver name, input value
|
|
1132
|
-
6. ✅ **MappingResult tells you everything** - `success`, `data`, `errors`
|
|
1133
|
-
|
|
1134
|
-
**Next Steps:**
|
|
1135
|
-
|
|
1136
|
-
- Try the [Error Scenarios Test Suite](../../../../tests/unit/services/mapping/universal-mapper.test.ts)
|
|
1137
|
-
- Review [Production Templates](../../../01-TEMPLATES/) for real-world examples
|
|
1138
|
-
- Check [Resolver Troubleshooting](../resolvers/mapping-resolvers-resolver-troubleshooting.md) for resolver-specific issues
|
|
1139
|
-
|
|
1140
|
-
---
|
|
1141
|
-
|
|
1142
|
-
[← Back to Mapping Guide](../mapping-readme.md)
|
|
1
|
+
# UniversalMapper Error Handling
|
|
2
|
+
|
|
3
|
+
[← Back to Mapping Guide](../mapping-readme.md)
|
|
4
|
+
|
|
5
|
+
**Module 8** | **Level**: Intermediate | **Time**: 25 minutes
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
Understanding error handling is critical for building robust integrations. The UniversalMapper provides comprehensive error handling with clear error messages and fail-fast behavior for required fields.
|
|
12
|
+
|
|
13
|
+
**Key Concepts:**
|
|
14
|
+
- ✅ **Individual record error handling** - Each record processed independently
|
|
15
|
+
- ✅ **Fail-fast for required fields** - Prevents partial/corrupt data
|
|
16
|
+
- ✅ **Error aggregation** - Collects all errors for arrays
|
|
17
|
+
- ✅ **Rich error context** - Field name, source path, input value, timestamps
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Table of Contents
|
|
22
|
+
|
|
23
|
+
1. [Error Types Overview](#error-types-overview)
|
|
24
|
+
2. [Fatal vs Non-Fatal Errors](#fatal-vs-non-fatal-errors)
|
|
25
|
+
3. [Array Error Handling](#array-error-handling)
|
|
26
|
+
4. [Error Result Structure](#error-result-structure)
|
|
27
|
+
5. [Common Error Scenarios](#common-error-scenarios)
|
|
28
|
+
6. [Production Patterns](#production-patterns)
|
|
29
|
+
7. [Troubleshooting Guide](#troubleshooting-guide)
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Error Types Overview
|
|
34
|
+
|
|
35
|
+
The UniversalMapper produces two main types of errors:
|
|
36
|
+
|
|
37
|
+
| Error Type | When Thrown | Result | Retryable |
|
|
38
|
+
|------------|-------------|--------|-----------|
|
|
39
|
+
| **Required Field Error** | Required field missing/null/empty | `success: false`, record skipped | ❌ No - Fix data or config |
|
|
40
|
+
| **Resolver Error** | Resolver execution fails | `success: false` (required) or warning (optional) | ❌ No - Fix resolver or data |
|
|
41
|
+
| **Resolver Not Found** | Referenced resolver doesn't exist | `success: false`, record skipped | ❌ No - Fix config |
|
|
42
|
+
| **Configuration Error** | Invalid mapping config | Throws exception | ❌ No - Fix config |
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Fatal vs Non-Fatal Errors
|
|
47
|
+
|
|
48
|
+
### ❌ Fatal Errors (Stops Record Processing)
|
|
49
|
+
|
|
50
|
+
**Result:** `{ success: false, data: null, errors: [...] }`
|
|
51
|
+
|
|
52
|
+
The mapper **immediately stops** processing and returns failure for:
|
|
53
|
+
|
|
54
|
+
#### 1. Required Field Missing
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
const mapper = new UniversalMapper({
|
|
58
|
+
fields: {
|
|
59
|
+
sku: { source: 'product.sku', required: true },
|
|
60
|
+
quantity: { source: 'product.qty', required: true }
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const result = await mapper.map({ product: { sku: 'ABC' } }); // Missing qty
|
|
65
|
+
|
|
66
|
+
// Result:
|
|
67
|
+
{
|
|
68
|
+
success: false,
|
|
69
|
+
data: null,
|
|
70
|
+
errors: [
|
|
71
|
+
"Failed to map field 'quantity': Required field 'quantity' is missing or empty (source: product.qty)"
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Error includes:**
|
|
77
|
+
- Field name: `quantity`
|
|
78
|
+
- Source path: `product.qty`
|
|
79
|
+
- Clear reason: "missing or empty"
|
|
80
|
+
|
|
81
|
+
#### 2. Required Field is Null/Empty
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
const result = await mapper.map({
|
|
85
|
+
product: {
|
|
86
|
+
sku: '', // ❌ Empty string
|
|
87
|
+
qty: null // ❌ Null
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Both fail validation
|
|
92
|
+
{
|
|
93
|
+
success: false,
|
|
94
|
+
data: null,
|
|
95
|
+
errors: [
|
|
96
|
+
"Failed to map field 'sku': Required field 'sku' is missing or empty (source: product.sku)"
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Validation checks:**
|
|
102
|
+
- `value === null` ❌ Fails
|
|
103
|
+
- `value === undefined` ❌ Fails
|
|
104
|
+
- `value === ''` ❌ Fails (empty string)
|
|
105
|
+
- `value === 0` ✅ Passes (valid number)
|
|
106
|
+
- `value === false` ✅ Passes (valid boolean)
|
|
107
|
+
|
|
108
|
+
#### 3. Required Field Resolver Fails
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
const mapper = new UniversalMapper({
|
|
112
|
+
fields: {
|
|
113
|
+
quantity: {
|
|
114
|
+
source: 'qty',
|
|
115
|
+
resolver: 'sdk.parseInt',
|
|
116
|
+
required: true
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const result = await mapper.map({ qty: 'ABC' }); // Not a number
|
|
122
|
+
|
|
123
|
+
// Result:
|
|
124
|
+
{
|
|
125
|
+
success: false,
|
|
126
|
+
data: null,
|
|
127
|
+
errors: [
|
|
128
|
+
"Failed to map field 'quantity': Resolver 'sdk.parseInt' failed: Cannot parse 'ABC' as integer"
|
|
129
|
+
]
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Error includes:**
|
|
134
|
+
- Resolver name: `sdk.parseInt`
|
|
135
|
+
- Input value: `ABC`
|
|
136
|
+
- Original error: "Cannot parse 'ABC' as integer"
|
|
137
|
+
|
|
138
|
+
#### 4. Nested Required Field Fails
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
const mapper = new UniversalMapper({
|
|
142
|
+
fields: {
|
|
143
|
+
customer: {
|
|
144
|
+
fields: {
|
|
145
|
+
email: { source: 'email', required: true },
|
|
146
|
+
firstName: { source: 'firstName', required: true }
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const result = await mapper.map({
|
|
153
|
+
customer: { firstName: 'John' } // Missing email
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Result:
|
|
157
|
+
{
|
|
158
|
+
success: false,
|
|
159
|
+
data: null,
|
|
160
|
+
errors: [
|
|
161
|
+
"Failed to map field 'email': Required field 'email' is missing or empty"
|
|
162
|
+
]
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Nested errors propagate up** - Parent field also fails if any child required field fails.
|
|
167
|
+
|
|
168
|
+
#### 5. Resolver Not Found
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
const mapper = new UniversalMapper({
|
|
172
|
+
fields: {
|
|
173
|
+
status: {
|
|
174
|
+
value: 'ACTIVE',
|
|
175
|
+
resolver: 'sdk.unknownResolver' // ❌ Doesn't exist
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const result = await mapper.map({});
|
|
181
|
+
|
|
182
|
+
// Result:
|
|
183
|
+
{
|
|
184
|
+
success: false,
|
|
185
|
+
data: null,
|
|
186
|
+
errors: [
|
|
187
|
+
"Failed to map field 'status': Resolver 'sdk.unknownResolver' not found"
|
|
188
|
+
]
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Common cause:** Typo in resolver name or forgot to register custom resolver.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
### ⚠️ Non-Fatal Errors (Processing Continues)
|
|
197
|
+
|
|
198
|
+
**Result:** `{ success: true, data: {...}, errors: [...] }`
|
|
199
|
+
|
|
200
|
+
The mapper **continues processing** but collects warnings for:
|
|
201
|
+
|
|
202
|
+
#### 1. Optional Field Missing (Skipped)
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
const mapper = new UniversalMapper({
|
|
206
|
+
fields: {
|
|
207
|
+
sku: { source: 'sku', required: true },
|
|
208
|
+
description: { source: 'description' } // Optional, no defaultValue
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const result = await mapper.map({ sku: 'ABC' }); // No description
|
|
213
|
+
|
|
214
|
+
// Result:
|
|
215
|
+
{
|
|
216
|
+
success: true, // ✅ Succeeds because required field present
|
|
217
|
+
data: { sku: 'ABC' }, // description not included (undefined fields are skipped)
|
|
218
|
+
errors: undefined,
|
|
219
|
+
skippedFields: ['description'] // Field names that were skipped
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**Important:** Undefined optional fields are **automatically skipped** from the output (correct JSON behavior). Field names are tracked in `skippedFields` for reference.
|
|
224
|
+
|
|
225
|
+
**To always include a field:** Add `defaultValue` to the field configuration:
|
|
226
|
+
```typescript
|
|
227
|
+
{
|
|
228
|
+
description: {
|
|
229
|
+
source: 'description',
|
|
230
|
+
defaultValue: '' // Empty string if missing
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
#### 2. Optional Field Uses Default Value
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
const mapper = new UniversalMapper({
|
|
239
|
+
fields: {
|
|
240
|
+
sku: { source: 'sku', required: true },
|
|
241
|
+
status: { source: 'status', defaultValue: 'ACTIVE' }
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
const result = await mapper.map({ sku: 'ABC' }); // No status
|
|
246
|
+
|
|
247
|
+
// Result:
|
|
248
|
+
{
|
|
249
|
+
success: true,
|
|
250
|
+
data: {
|
|
251
|
+
sku: 'ABC',
|
|
252
|
+
status: 'ACTIVE' // ✅ Default value used
|
|
253
|
+
},
|
|
254
|
+
errors: undefined
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
#### 3. Optional Field Resolver Fails
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
const mapper = new UniversalMapper({
|
|
262
|
+
fields: {
|
|
263
|
+
sku: { source: 'sku', required: true },
|
|
264
|
+
quantity: {
|
|
265
|
+
source: 'qty',
|
|
266
|
+
resolver: 'sdk.parseInt' // Optional field, resolver fails
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const result = await mapper.map({ sku: 'ABC', qty: 'invalid' });
|
|
272
|
+
|
|
273
|
+
// Result:
|
|
274
|
+
{
|
|
275
|
+
success: true, // ✅ Required field succeeded
|
|
276
|
+
data: { sku: 'ABC' },
|
|
277
|
+
errors: [
|
|
278
|
+
"Failed to map field 'quantity': Resolver 'sdk.parseInt' failed: Cannot parse 'invalid' as integer"
|
|
279
|
+
]
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**Note:** Error is logged but doesn't stop processing since field is optional.
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Array Error Handling
|
|
288
|
+
|
|
289
|
+
The UniversalMapper has **built-in array error handling** that processes each item individually and aggregates results.
|
|
290
|
+
|
|
291
|
+
### Automatic Array Processing
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
const mapper = new UniversalMapper({
|
|
295
|
+
fields: {
|
|
296
|
+
sku: { source: 'sku', required: true },
|
|
297
|
+
quantity: { source: 'qty', resolver: 'sdk.parseInt', required: true }
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
// Pass array directly to mapper
|
|
302
|
+
const records = [
|
|
303
|
+
{ sku: 'ABC', qty: '10' }, // ✅ Valid
|
|
304
|
+
{ sku: 'DEF' }, // ❌ Missing qty
|
|
305
|
+
{ sku: 'GHI', qty: '20' }, // ✅ Valid
|
|
306
|
+
{ qty: '30' } // ❌ Missing sku
|
|
307
|
+
];
|
|
308
|
+
|
|
309
|
+
const result = await mapper.map(records);
|
|
310
|
+
|
|
311
|
+
// Result:
|
|
312
|
+
{
|
|
313
|
+
success: true, // ✅ At least one item succeeded
|
|
314
|
+
data: [
|
|
315
|
+
{ sku: 'ABC', quantity: 10 },
|
|
316
|
+
{ sku: 'GHI', quantity: 20 }
|
|
317
|
+
],
|
|
318
|
+
errors: [
|
|
319
|
+
"Failed to map field 'quantity': Required field 'quantity' is missing or empty (source: qty)",
|
|
320
|
+
"Failed to map field 'sku': Required field 'sku' is missing or empty (source: sku)"
|
|
321
|
+
]
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**Behavior:**
|
|
326
|
+
- ✅ Successful items collected in `data` array
|
|
327
|
+
- ❌ Failed items logged in `errors` array
|
|
328
|
+
- Result is `success: true` if **at least one item** succeeds
|
|
329
|
+
- Result is `success: false` if **all items** fail
|
|
330
|
+
|
|
331
|
+
### Processing Individual Records
|
|
332
|
+
|
|
333
|
+
```typescript
|
|
334
|
+
// Production pattern: Process records one-by-one for detailed error tracking
|
|
335
|
+
const mappedRecords = [];
|
|
336
|
+
const failedRecords = [];
|
|
337
|
+
|
|
338
|
+
for (let i = 0; i < records.length; i++) {
|
|
339
|
+
const record = records[i];
|
|
340
|
+
const recordId = record.sku || record.id || `index-${i}`;
|
|
341
|
+
|
|
342
|
+
const mappingResult = await mapper.map(record);
|
|
343
|
+
|
|
344
|
+
if (mappingResult.success) {
|
|
345
|
+
mappedRecords.push(mappingResult.data);
|
|
346
|
+
log.info(`✅ Record ${recordId} mapped successfully`);
|
|
347
|
+
} else {
|
|
348
|
+
failedRecords.push({
|
|
349
|
+
recordId,
|
|
350
|
+
index: i,
|
|
351
|
+
record,
|
|
352
|
+
errors: mappingResult.errors
|
|
353
|
+
});
|
|
354
|
+
log.error(`❌ Record ${recordId} mapping failed`, {
|
|
355
|
+
errors: mappingResult.errors,
|
|
356
|
+
record: JSON.stringify(record)
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
log.info('Mapping complete', {
|
|
362
|
+
total: records.length,
|
|
363
|
+
successful: mappedRecords.length,
|
|
364
|
+
failed: failedRecords.length,
|
|
365
|
+
successRate: `${((mappedRecords.length / records.length) * 100).toFixed(2)}%`
|
|
366
|
+
});
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Nested Array Error Handling
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
const mapper = new UniversalMapper({
|
|
373
|
+
fields: {
|
|
374
|
+
orderId: { source: 'id', required: true },
|
|
375
|
+
items: {
|
|
376
|
+
source: 'items',
|
|
377
|
+
isArray: true,
|
|
378
|
+
fields: {
|
|
379
|
+
sku: { source: 'sku', required: true },
|
|
380
|
+
quantity: { source: 'qty', required: true }
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
const result = await mapper.map({
|
|
387
|
+
id: 'ORDER-123',
|
|
388
|
+
items: [
|
|
389
|
+
{ sku: 'ABC', qty: 10 }, // ✅ Valid
|
|
390
|
+
{ qty: 5 } // ❌ Missing sku
|
|
391
|
+
]
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
// Result:
|
|
395
|
+
{
|
|
396
|
+
success: false, // ❌ Required nested field failed
|
|
397
|
+
data: null,
|
|
398
|
+
errors: [
|
|
399
|
+
"Failed to map field 'items': Required field 'sku' is missing or empty"
|
|
400
|
+
]
|
|
401
|
+
}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
**Nested array errors propagate up** - Parent field fails if any array item has required field errors.
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
## Error Result Structure
|
|
409
|
+
|
|
410
|
+
Every mapping operation returns a `MappingResult`:
|
|
411
|
+
|
|
412
|
+
```typescript
|
|
413
|
+
interface MappingResult {
|
|
414
|
+
/** Whether mapping succeeded */
|
|
415
|
+
success: boolean;
|
|
416
|
+
|
|
417
|
+
/** Mapped data (null if failed) */
|
|
418
|
+
data: any;
|
|
419
|
+
|
|
420
|
+
/** Errors encountered during mapping */
|
|
421
|
+
errors?: string[];
|
|
422
|
+
|
|
423
|
+
/** Fields that were skipped because they were undefined (field names only) */
|
|
424
|
+
skippedFields?: string[];
|
|
425
|
+
}
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### Success Cases
|
|
429
|
+
|
|
430
|
+
#### 1. Complete Success
|
|
431
|
+
|
|
432
|
+
```typescript
|
|
433
|
+
{
|
|
434
|
+
success: true,
|
|
435
|
+
data: { sku: 'ABC', quantity: 10 },
|
|
436
|
+
errors: undefined // No errors
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
#### 2. Success with Skipped Fields
|
|
441
|
+
|
|
442
|
+
```typescript
|
|
443
|
+
{
|
|
444
|
+
success: true,
|
|
445
|
+
data: { sku: 'ABC' }, // description field skipped (was undefined)
|
|
446
|
+
errors: undefined,
|
|
447
|
+
skippedFields: ['description'] // Field names that were skipped
|
|
448
|
+
}
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
#### 3. Success with Resolver Errors (Optional Fields)
|
|
452
|
+
|
|
453
|
+
```typescript
|
|
454
|
+
{
|
|
455
|
+
success: true,
|
|
456
|
+
data: { sku: 'ABC' },
|
|
457
|
+
errors: [
|
|
458
|
+
"Failed to map field 'description': Resolver 'sdk.uppercase' failed: Cannot uppercase null"
|
|
459
|
+
]
|
|
460
|
+
}
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### Failure Cases
|
|
464
|
+
|
|
465
|
+
#### 1. Required Field Missing
|
|
466
|
+
|
|
467
|
+
```typescript
|
|
468
|
+
{
|
|
469
|
+
success: false,
|
|
470
|
+
data: null,
|
|
471
|
+
errors: [
|
|
472
|
+
"Failed to map field 'sku': Required field 'sku' is missing or empty (source: product.sku)"
|
|
473
|
+
]
|
|
474
|
+
}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
#### 2. Multiple Errors (Array Processing)
|
|
478
|
+
|
|
479
|
+
```typescript
|
|
480
|
+
{
|
|
481
|
+
success: true, // Some items succeeded
|
|
482
|
+
data: [
|
|
483
|
+
{ sku: 'ABC', quantity: 10 },
|
|
484
|
+
{ sku: 'DEF', quantity: 20 }
|
|
485
|
+
],
|
|
486
|
+
errors: [
|
|
487
|
+
"Failed to map field 'sku': Required field 'sku' is missing or empty",
|
|
488
|
+
"Failed to map field 'quantity': Resolver 'sdk.parseInt' failed: Cannot parse 'XYZ' as integer"
|
|
489
|
+
]
|
|
490
|
+
}
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
---
|
|
494
|
+
|
|
495
|
+
## Common Error Scenarios
|
|
496
|
+
|
|
497
|
+
### Scenario 1: Required Field Missing
|
|
498
|
+
|
|
499
|
+
**Problem:**
|
|
500
|
+
```typescript
|
|
501
|
+
const mapper = new UniversalMapper({
|
|
502
|
+
fields: {
|
|
503
|
+
sku: { source: 'product.sku', required: true }
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
const result = await mapper.map({ product: {} });
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
**Error:**
|
|
511
|
+
```
|
|
512
|
+
Failed to map field 'sku': Required field 'sku' is missing or empty (source: product.sku)
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
**Solutions:**
|
|
516
|
+
|
|
517
|
+
✅ **Option 1: Fix source data**
|
|
518
|
+
```typescript
|
|
519
|
+
await mapper.map({ product: { sku: 'ABC123' } });
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
✅ **Option 2: Make field optional with default**
|
|
523
|
+
```typescript
|
|
524
|
+
const mapper = new UniversalMapper({
|
|
525
|
+
fields: {
|
|
526
|
+
sku: {
|
|
527
|
+
source: 'product.sku',
|
|
528
|
+
defaultValue: 'UNKNOWN' // No longer required
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
✅ **Option 3: Use resolver to generate value**
|
|
535
|
+
```typescript
|
|
536
|
+
const mapper = new UniversalMapper({
|
|
537
|
+
fields: {
|
|
538
|
+
sku: {
|
|
539
|
+
resolver: 'sdk.uuid', // Generates UUID if source missing
|
|
540
|
+
required: true
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
---
|
|
547
|
+
|
|
548
|
+
### Scenario 2: Resolver Fails on Invalid Data
|
|
549
|
+
|
|
550
|
+
**Problem:**
|
|
551
|
+
```typescript
|
|
552
|
+
const mapper = new UniversalMapper({
|
|
553
|
+
fields: {
|
|
554
|
+
quantity: {
|
|
555
|
+
source: 'qty',
|
|
556
|
+
resolver: 'sdk.parseInt',
|
|
557
|
+
required: true
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
const result = await mapper.map({ qty: 'ABC' });
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
**Error:**
|
|
566
|
+
```
|
|
567
|
+
Failed to map field 'quantity': Resolver 'sdk.parseInt' failed: Cannot parse 'ABC' as integer
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
**Solutions:**
|
|
571
|
+
|
|
572
|
+
✅ **Option 1: Fix source data**
|
|
573
|
+
```typescript
|
|
574
|
+
await mapper.map({ qty: '123' }); // Valid number string
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
✅ **Option 2: Use parseInt with optional field and default**
|
|
578
|
+
```typescript
|
|
579
|
+
const mapper = new UniversalMapper({
|
|
580
|
+
fields: {
|
|
581
|
+
quantity: {
|
|
582
|
+
source: 'qty',
|
|
583
|
+
resolver: 'sdk.parseInt', // Already safe, returns 0 for invalid input
|
|
584
|
+
required: false, // Make optional to allow default
|
|
585
|
+
defaultValue: 0 // Fallback for missing/invalid
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
});
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
✅ **Option 3: Validate before mapping**
|
|
592
|
+
```typescript
|
|
593
|
+
const sourceData = { qty: 'ABC' };
|
|
594
|
+
|
|
595
|
+
// Pre-validation
|
|
596
|
+
if (!/^\d+$/.test(sourceData.qty)) {
|
|
597
|
+
throw new Error(`Invalid quantity: ${sourceData.qty}`);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
await mapper.map(sourceData);
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
---
|
|
604
|
+
|
|
605
|
+
### Scenario 3: Resolver Not Found
|
|
606
|
+
|
|
607
|
+
**Problem:**
|
|
608
|
+
```typescript
|
|
609
|
+
const mapper = new UniversalMapper({
|
|
610
|
+
fields: {
|
|
611
|
+
status: {
|
|
612
|
+
value: 'active',
|
|
613
|
+
resolver: 'sdk.uppercas' // ❌ Typo: should be 'sdk.uppercase'
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
});
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
**Error:**
|
|
620
|
+
```
|
|
621
|
+
Failed to map field 'status': Resolver 'sdk.uppercas' not found
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
**Solutions:**
|
|
625
|
+
|
|
626
|
+
✅ **Option 1: Fix typo**
|
|
627
|
+
```typescript
|
|
628
|
+
resolver: 'sdk.uppercase' // ✅ Correct spelling
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
✅ **Option 2: Check available resolvers**
|
|
632
|
+
```typescript
|
|
633
|
+
console.log(mapper.getResolverNames());
|
|
634
|
+
// ['sdk.uppercase', 'sdk.lowercase', 'sdk.parseInt', ...]
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
✅ **Option 3: Register custom resolver if needed**
|
|
638
|
+
```typescript
|
|
639
|
+
mapper.registerResolver('custom.uppercas', (value) => {
|
|
640
|
+
return String(value).toUpperCase();
|
|
641
|
+
});
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
---
|
|
645
|
+
|
|
646
|
+
### Scenario 4: Contradictory Configuration
|
|
647
|
+
|
|
648
|
+
**Problem:**
|
|
649
|
+
```typescript
|
|
650
|
+
const mapper = new UniversalMapper({
|
|
651
|
+
fields: {
|
|
652
|
+
status: {
|
|
653
|
+
source: 'status',
|
|
654
|
+
required: true,
|
|
655
|
+
defaultValue: 'ACTIVE' // ❌ Contradictory: required + defaultValue
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
**Warning (logged to console):**
|
|
662
|
+
```
|
|
663
|
+
⚠️ Field 'status' has both required=true and defaultValue.
|
|
664
|
+
This is contradictory - the defaultValue will never be used because required validation
|
|
665
|
+
fails before defaultValue is applied. Consider using required=false if you want a fallback value.
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
**Solution:**
|
|
669
|
+
```typescript
|
|
670
|
+
const mapper = new UniversalMapper({
|
|
671
|
+
fields: {
|
|
672
|
+
status: {
|
|
673
|
+
source: 'status',
|
|
674
|
+
required: false, // ✅ Allow missing with default
|
|
675
|
+
defaultValue: 'ACTIVE'
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
});
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
---
|
|
682
|
+
|
|
683
|
+
### Scenario 5: Nested Required Field Missing
|
|
684
|
+
|
|
685
|
+
**Problem:**
|
|
686
|
+
```typescript
|
|
687
|
+
const mapper = new UniversalMapper({
|
|
688
|
+
fields: {
|
|
689
|
+
customer: {
|
|
690
|
+
fields: {
|
|
691
|
+
email: { source: 'email', required: true },
|
|
692
|
+
phone: { source: 'phone', required: true }
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
});
|
|
697
|
+
|
|
698
|
+
const result = await mapper.map({
|
|
699
|
+
customer: { email: 'test@example.com' } // Missing phone
|
|
700
|
+
});
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
**Error:**
|
|
704
|
+
```
|
|
705
|
+
Failed to map field 'phone': Required field 'phone' is missing or empty
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
**Solutions:**
|
|
709
|
+
|
|
710
|
+
✅ **Option 1: Provide required field**
|
|
711
|
+
```typescript
|
|
712
|
+
await mapper.map({
|
|
713
|
+
customer: {
|
|
714
|
+
email: 'test@example.com',
|
|
715
|
+
phone: '555-1234' // ✅ Provided
|
|
716
|
+
}
|
|
717
|
+
});
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
✅ **Option 2: Make field optional**
|
|
721
|
+
```typescript
|
|
722
|
+
const mapper = new UniversalMapper({
|
|
723
|
+
fields: {
|
|
724
|
+
customer: {
|
|
725
|
+
fields: {
|
|
726
|
+
email: { source: 'email', required: true },
|
|
727
|
+
phone: { source: 'phone', required: false } // ✅ Optional
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
});
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
---
|
|
735
|
+
|
|
736
|
+
## Production Patterns
|
|
737
|
+
|
|
738
|
+
### Pattern 1: Individual Record Error Tracking
|
|
739
|
+
|
|
740
|
+
```typescript
|
|
741
|
+
import { UniversalMapper } from '@fluentcommerce/fc-connect-sdk';
|
|
742
|
+
|
|
743
|
+
interface RecordError {
|
|
744
|
+
recordId: string;
|
|
745
|
+
index: number;
|
|
746
|
+
error: string[];
|
|
747
|
+
record: any;
|
|
748
|
+
timestamp: string;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
async function processRecords(
|
|
752
|
+
records: any[],
|
|
753
|
+
mapper: UniversalMapper,
|
|
754
|
+
log: any
|
|
755
|
+
): Promise<{
|
|
756
|
+
successful: any[];
|
|
757
|
+
failed: RecordError[];
|
|
758
|
+
summary: string;
|
|
759
|
+
}> {
|
|
760
|
+
const successful = [];
|
|
761
|
+
const failed: RecordError[] = [];
|
|
762
|
+
|
|
763
|
+
for (let i = 0; i < records.length; i++) {
|
|
764
|
+
const record = records[i];
|
|
765
|
+
const recordId = record.id || record.sku || `index-${i}`;
|
|
766
|
+
|
|
767
|
+
try {
|
|
768
|
+
const mappingResult = await mapper.map(record);
|
|
769
|
+
|
|
770
|
+
if (mappingResult.success) {
|
|
771
|
+
successful.push(mappingResult.data);
|
|
772
|
+
log.info(`✅ Record ${recordId} processed`, { index: i });
|
|
773
|
+
} else {
|
|
774
|
+
failed.push({
|
|
775
|
+
recordId,
|
|
776
|
+
index: i,
|
|
777
|
+
error: mappingResult.errors || ['Unknown error'],
|
|
778
|
+
record,
|
|
779
|
+
timestamp: new Date().toISOString()
|
|
780
|
+
});
|
|
781
|
+
log.error(`❌ Record ${recordId} failed`, {
|
|
782
|
+
index: i,
|
|
783
|
+
errors: mappingResult.errors
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
} catch (error: any) {
|
|
787
|
+
// Unexpected error (config issue, etc.)
|
|
788
|
+
failed.push({
|
|
789
|
+
recordId,
|
|
790
|
+
index: i,
|
|
791
|
+
error: [error.message],
|
|
792
|
+
record,
|
|
793
|
+
timestamp: new Date().toISOString()
|
|
794
|
+
});
|
|
795
|
+
log.error(`💥 Record ${recordId} threw exception`, {
|
|
796
|
+
error: error.message,
|
|
797
|
+
stack: error.stack
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
return {
|
|
803
|
+
successful,
|
|
804
|
+
failed,
|
|
805
|
+
summary: `Processed ${records.length} records: ${successful.length} succeeded, ${failed.length} failed`
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
```
|
|
809
|
+
|
|
810
|
+
### Pattern 2: Error Categorization
|
|
811
|
+
|
|
812
|
+
```typescript
|
|
813
|
+
enum ErrorCategory {
|
|
814
|
+
VALIDATION = 'validation',
|
|
815
|
+
MAPPING = 'mapping',
|
|
816
|
+
RESOLVER = 'resolver',
|
|
817
|
+
UNKNOWN = 'unknown'
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
interface CategorizedError {
|
|
821
|
+
category: ErrorCategory;
|
|
822
|
+
recordId: string;
|
|
823
|
+
message: string;
|
|
824
|
+
record: any;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
function categorizeError(error: string, record: any, recordId: string): CategorizedError {
|
|
828
|
+
if (error.includes('Required field')) {
|
|
829
|
+
return {
|
|
830
|
+
category: ErrorCategory.VALIDATION,
|
|
831
|
+
recordId,
|
|
832
|
+
message: error,
|
|
833
|
+
record
|
|
834
|
+
};
|
|
835
|
+
} else if (error.includes('Resolver')) {
|
|
836
|
+
return {
|
|
837
|
+
category: ErrorCategory.RESOLVER,
|
|
838
|
+
recordId,
|
|
839
|
+
message: error,
|
|
840
|
+
record
|
|
841
|
+
};
|
|
842
|
+
} else if (error.includes('Failed to map')) {
|
|
843
|
+
return {
|
|
844
|
+
category: ErrorCategory.MAPPING,
|
|
845
|
+
recordId,
|
|
846
|
+
message: error,
|
|
847
|
+
record
|
|
848
|
+
};
|
|
849
|
+
} else {
|
|
850
|
+
return {
|
|
851
|
+
category: ErrorCategory.UNKNOWN,
|
|
852
|
+
recordId,
|
|
853
|
+
message: error,
|
|
854
|
+
record
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
async function processWithCategorization(
|
|
860
|
+
records: any[],
|
|
861
|
+
mapper: UniversalMapper,
|
|
862
|
+
log: any
|
|
863
|
+
) {
|
|
864
|
+
const results = {
|
|
865
|
+
successful: [],
|
|
866
|
+
errors: {
|
|
867
|
+
[ErrorCategory.VALIDATION]: [] as CategorizedError[],
|
|
868
|
+
[ErrorCategory.MAPPING]: [] as CategorizedError[],
|
|
869
|
+
[ErrorCategory.RESOLVER]: [] as CategorizedError[],
|
|
870
|
+
[ErrorCategory.UNKNOWN]: [] as CategorizedError[]
|
|
871
|
+
}
|
|
872
|
+
};
|
|
873
|
+
|
|
874
|
+
for (let i = 0; i < records.length; i++) {
|
|
875
|
+
const record = records[i];
|
|
876
|
+
const recordId = record.id || `index-${i}`;
|
|
877
|
+
|
|
878
|
+
const mappingResult = await mapper.map(record);
|
|
879
|
+
|
|
880
|
+
if (mappingResult.success) {
|
|
881
|
+
results.successful.push(mappingResult.data);
|
|
882
|
+
} else {
|
|
883
|
+
// Categorize each error
|
|
884
|
+
(mappingResult.errors || []).forEach(errorMsg => {
|
|
885
|
+
const categorized = categorizeError(errorMsg, record, recordId);
|
|
886
|
+
results.errors[categorized.category].push(categorized);
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
// Log summary by category
|
|
892
|
+
log.info('Processing complete', {
|
|
893
|
+
successful: results.successful.length,
|
|
894
|
+
validationErrors: results.errors[ErrorCategory.VALIDATION].length,
|
|
895
|
+
mappingErrors: results.errors[ErrorCategory.MAPPING].length,
|
|
896
|
+
resolverErrors: results.errors[ErrorCategory.RESOLVER].length,
|
|
897
|
+
unknownErrors: results.errors[ErrorCategory.UNKNOWN].length
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
return results;
|
|
901
|
+
}
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
### Pattern 3: Using PartialBatchRecovery for Retries
|
|
905
|
+
|
|
906
|
+
```typescript
|
|
907
|
+
import { PartialBatchRecovery } from '@fluentcommerce/fc-connect-sdk';
|
|
908
|
+
|
|
909
|
+
async function processWithRecovery(
|
|
910
|
+
records: any[],
|
|
911
|
+
mapper: UniversalMapper,
|
|
912
|
+
client: FluentClient,
|
|
913
|
+
jobId: string,
|
|
914
|
+
log: any
|
|
915
|
+
) {
|
|
916
|
+
const recovery = new PartialBatchRecovery(log);
|
|
917
|
+
|
|
918
|
+
const result = await recovery.processBatchWithRecovery(
|
|
919
|
+
records,
|
|
920
|
+
async (batch) => {
|
|
921
|
+
// Map records
|
|
922
|
+
const mapped = [];
|
|
923
|
+
for (const rec of batch) {
|
|
924
|
+
const mappingResult = await mapper.map(rec);
|
|
925
|
+
if (mappingResult.success) {
|
|
926
|
+
mapped.push(mappingResult.data);
|
|
927
|
+
} else {
|
|
928
|
+
throw new Error(`Mapping failed: ${mappingResult.errors?.join(', ')}`);
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// Send to Fluent
|
|
933
|
+
return client.sendBatch(jobId, mapped);
|
|
934
|
+
},
|
|
935
|
+
{
|
|
936
|
+
maxRetries: 3,
|
|
937
|
+
retryOnlyFailed: true,
|
|
938
|
+
checkpointKey: `job-${jobId}`,
|
|
939
|
+
retryDelayMs: 1000
|
|
940
|
+
}
|
|
941
|
+
);
|
|
942
|
+
|
|
943
|
+
log.info('Batch processing complete', {
|
|
944
|
+
total: result.totalRecords,
|
|
945
|
+
successful: result.successCount,
|
|
946
|
+
failed: result.failedCount,
|
|
947
|
+
failedRecords: result.failedRecords
|
|
948
|
+
});
|
|
949
|
+
|
|
950
|
+
return result;
|
|
951
|
+
}
|
|
952
|
+
```
|
|
953
|
+
|
|
954
|
+
---
|
|
955
|
+
|
|
956
|
+
## Troubleshooting Guide
|
|
957
|
+
|
|
958
|
+
### Quick Diagnostic Checklist
|
|
959
|
+
|
|
960
|
+
When you encounter mapping errors, check:
|
|
961
|
+
|
|
962
|
+
1. **✅ Is the field required?**
|
|
963
|
+
- Check `required: true` in field config
|
|
964
|
+
- Required fields must have non-null/non-empty values
|
|
965
|
+
|
|
966
|
+
2. **✅ Is the source path correct?**
|
|
967
|
+
- Verify path matches actual data structure
|
|
968
|
+
- Check for typos in field names
|
|
969
|
+
- Use `console.log(JSON.stringify(sourceData))` to inspect
|
|
970
|
+
|
|
971
|
+
3. **✅ Is the resolver name correct?**
|
|
972
|
+
- Check spelling: `sdk.parseInt` not `sdk.parseInt`
|
|
973
|
+
- Verify with `mapper.getResolverNames()`
|
|
974
|
+
- Ensure custom resolvers are registered
|
|
975
|
+
|
|
976
|
+
4. **✅ Is the data type compatible with resolver?**
|
|
977
|
+
- `sdk.parseInt` expects string/number (returns 0 for invalid, already safe)
|
|
978
|
+
- `sdk.parseFloat` expects string/number (returns 0 for invalid, already safe)
|
|
979
|
+
- `sdk.uppercase` expects string
|
|
980
|
+
- Make fields optional with `defaultValue` for fallback behavior
|
|
981
|
+
|
|
982
|
+
5. **✅ Are there nested required fields?**
|
|
983
|
+
- Check all nested field configs
|
|
984
|
+
- Nested required fields propagate errors up
|
|
985
|
+
|
|
986
|
+
### Common Error Messages
|
|
987
|
+
|
|
988
|
+
| Error Message | Cause | Solution |
|
|
989
|
+
|---------------|-------|----------|
|
|
990
|
+
| `Required field 'X' is missing or empty` | Field is null/undefined/'' | Provide value or make optional |
|
|
991
|
+
| `Resolver 'X' not found` | Typo or unregistered resolver | Fix spelling or register resolver |
|
|
992
|
+
| `Resolver 'X' failed: ...` | Resolver threw during execution | Fix input data or use safe resolver |
|
|
993
|
+
| `Failed to map field 'X': ...` | Generic mapping failure | Check field config and data |
|
|
994
|
+
|
|
995
|
+
### Debug Logging
|
|
996
|
+
|
|
997
|
+
```typescript
|
|
998
|
+
const mockLogger = {
|
|
999
|
+
info: (...args: any[]) => console.log('[INFO]', ...args),
|
|
1000
|
+
debug: (...args: any[]) => console.log('[DEBUG]', ...args),
|
|
1001
|
+
warn: (...args: any[]) => console.warn('[WARN]', ...args),
|
|
1002
|
+
error: (...args: any[]) => console.error('[ERROR]', ...args)
|
|
1003
|
+
};
|
|
1004
|
+
|
|
1005
|
+
const mapper = new UniversalMapper(config, { logger: mockLogger });
|
|
1006
|
+
|
|
1007
|
+
// Now see detailed logs during mapping
|
|
1008
|
+
const result = await mapper.map(data);
|
|
1009
|
+
```
|
|
1010
|
+
|
|
1011
|
+
---
|
|
1012
|
+
|
|
1013
|
+
## Best Practices
|
|
1014
|
+
|
|
1015
|
+
### ✅ DO:
|
|
1016
|
+
|
|
1017
|
+
1. **Always check `mappingResult.success`**
|
|
1018
|
+
```typescript
|
|
1019
|
+
if (mappingResult.success) {
|
|
1020
|
+
await client.sendBatch(jobId, mappingResult.data);
|
|
1021
|
+
} else {
|
|
1022
|
+
log.error('Mapping failed', mappingResult.errors);
|
|
1023
|
+
}
|
|
1024
|
+
```
|
|
1025
|
+
|
|
1026
|
+
2. **Log detailed error context**
|
|
1027
|
+
```typescript
|
|
1028
|
+
log.error('Record mapping failed', {
|
|
1029
|
+
recordId,
|
|
1030
|
+
errors: mappingResult.errors,
|
|
1031
|
+
record: JSON.stringify(record)
|
|
1032
|
+
});
|
|
1033
|
+
```
|
|
1034
|
+
|
|
1035
|
+
3. **Use optional fields with defaults for non-critical data**
|
|
1036
|
+
```typescript
|
|
1037
|
+
description: { source: 'desc', defaultValue: '' }
|
|
1038
|
+
```
|
|
1039
|
+
|
|
1040
|
+
4. **Process records individually for better error tracking**
|
|
1041
|
+
```typescript
|
|
1042
|
+
for (const record of records) {
|
|
1043
|
+
const result = await mapper.map(record);
|
|
1044
|
+
// Handle individually
|
|
1045
|
+
}
|
|
1046
|
+
```
|
|
1047
|
+
|
|
1048
|
+
5. **Categorize errors for monitoring**
|
|
1049
|
+
```typescript
|
|
1050
|
+
if (error.includes('Resolver')) {
|
|
1051
|
+
metrics.incrementResolverErrors();
|
|
1052
|
+
}
|
|
1053
|
+
```
|
|
1054
|
+
|
|
1055
|
+
### ❌ DON'T:
|
|
1056
|
+
|
|
1057
|
+
1. **Don't ignore `mappingResult.errors`**
|
|
1058
|
+
```typescript
|
|
1059
|
+
// ❌ BAD
|
|
1060
|
+
const result = await mapper.map(data);
|
|
1061
|
+
await client.sendBatch(jobId, result.data); // Might be null!
|
|
1062
|
+
|
|
1063
|
+
// ✅ GOOD
|
|
1064
|
+
if (result.success) {
|
|
1065
|
+
await client.sendBatch(jobId, result.data);
|
|
1066
|
+
}
|
|
1067
|
+
```
|
|
1068
|
+
|
|
1069
|
+
2. **Don't use required + defaultValue together**
|
|
1070
|
+
```typescript
|
|
1071
|
+
// ❌ BAD - defaultValue never used
|
|
1072
|
+
{ source: 'field', required: true, defaultValue: 'X' }
|
|
1073
|
+
|
|
1074
|
+
// ✅ GOOD - Optional with default
|
|
1075
|
+
{ source: 'field', required: false, defaultValue: 'X' }
|
|
1076
|
+
```
|
|
1077
|
+
|
|
1078
|
+
3. **Don't catch and ignore errors silently**
|
|
1079
|
+
```typescript
|
|
1080
|
+
// ❌ BAD
|
|
1081
|
+
try {
|
|
1082
|
+
await mapper.map(record);
|
|
1083
|
+
} catch (error) {
|
|
1084
|
+
// Silently ignored
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
// ✅ GOOD
|
|
1088
|
+
try {
|
|
1089
|
+
const result = await mapper.map(record);
|
|
1090
|
+
if (!result.success) {
|
|
1091
|
+
log.error('Mapping failed', result.errors);
|
|
1092
|
+
}
|
|
1093
|
+
} catch (error) {
|
|
1094
|
+
log.error('Unexpected error', error);
|
|
1095
|
+
}
|
|
1096
|
+
```
|
|
1097
|
+
|
|
1098
|
+
4. **Don't assume all array items succeed**
|
|
1099
|
+
```typescript
|
|
1100
|
+
// ❌ BAD
|
|
1101
|
+
const result = await mapper.map(records);
|
|
1102
|
+
console.log(`Processed ${records.length} records`);
|
|
1103
|
+
|
|
1104
|
+
// ✅ GOOD
|
|
1105
|
+
const result = await mapper.map(records);
|
|
1106
|
+
console.log(`Processed ${result.data.length}/${records.length} records`);
|
|
1107
|
+
if (result.errors) {
|
|
1108
|
+
console.log(`Errors: ${result.errors.length}`);
|
|
1109
|
+
}
|
|
1110
|
+
```
|
|
1111
|
+
|
|
1112
|
+
---
|
|
1113
|
+
|
|
1114
|
+
## Related Documentation
|
|
1115
|
+
|
|
1116
|
+
- [Mapping Foundations](./mapping-01-foundations.md) - Core mapping concepts
|
|
1117
|
+
- [Custom Resolvers](../resolvers/mapping-resolvers-resolver-guide.md) - Building custom resolvers
|
|
1118
|
+
- [Error Handling Pattern Guide](../../../03-PATTERN-GUIDES/error-handling/error-handling-readme.md) - General error handling
|
|
1119
|
+
- [GraphQL Mutation Mapper Error Handling](../graphql-mutation-mapping/modules/graphql-mutation-mapping-11-error-handling.md) - GraphQL-specific errors
|
|
1120
|
+
|
|
1121
|
+
---
|
|
1122
|
+
|
|
1123
|
+
## Summary
|
|
1124
|
+
|
|
1125
|
+
**Key Takeaways:**
|
|
1126
|
+
|
|
1127
|
+
1. ✅ **UniversalMapper provides built-in error handling** - No need to wrap each record in try-catch
|
|
1128
|
+
2. ✅ **Required fields fail-fast** - Prevents partial/corrupt data
|
|
1129
|
+
3. ✅ **Optional fields continue with warnings** - Flexible error handling
|
|
1130
|
+
4. ✅ **Array processing is automatic** - Each item processed individually
|
|
1131
|
+
5. ✅ **Rich error context** - Field name, source path, resolver name, input value
|
|
1132
|
+
6. ✅ **MappingResult tells you everything** - `success`, `data`, `errors`
|
|
1133
|
+
|
|
1134
|
+
**Next Steps:**
|
|
1135
|
+
|
|
1136
|
+
- Try the [Error Scenarios Test Suite](../../../../tests/unit/services/mapping/universal-mapper.test.ts)
|
|
1137
|
+
- Review [Production Templates](../../../01-TEMPLATES/) for real-world examples
|
|
1138
|
+
- Check [Resolver Troubleshooting](../resolvers/mapping-resolvers-resolver-troubleshooting.md) for resolver-specific issues
|
|
1139
|
+
|
|
1140
|
+
---
|
|
1141
|
+
|
|
1142
|
+
[← Back to Mapping Guide](../mapping-readme.md)
|