@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,1319 +1,1319 @@
|
|
|
1
|
-
# Module 7: API Reference
|
|
2
|
-
|
|
3
|
-
**Complete Technical Reference**
|
|
4
|
-
|
|
5
|
-
**Level:** Reference
|
|
6
|
-
**Time:** As needed
|
|
7
|
-
**Prerequisites:** Modules 1-6
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## Table of Contents
|
|
12
|
-
|
|
13
|
-
1. [TypeScript Type Definitions](#typescript-type-definitions)
|
|
14
|
-
2. [Configuration Migration](#configuration-migration)
|
|
15
|
-
3. [Performance Tips](#performance-tips)
|
|
16
|
-
4. [FAQ](#faq)
|
|
17
|
-
5. [Troubleshooting](#troubleshooting)
|
|
18
|
-
6. [Best Practices](#best-practices)
|
|
19
|
-
7. [Cheat Sheet](#cheat-sheet)
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
## TypeScript Type Definitions
|
|
24
|
-
|
|
25
|
-
### MappingConfig
|
|
26
|
-
|
|
27
|
-
Complete mapping configuration structure:
|
|
28
|
-
|
|
29
|
-
```typescript
|
|
30
|
-
interface MappingConfig {
|
|
31
|
-
/** Direction of mapping */
|
|
32
|
-
direction: 'ingest' | 'extract';
|
|
33
|
-
|
|
34
|
-
/** Source data format (for context) */
|
|
35
|
-
sourceFormat?: 'csv' | 'xml' | 'json' | 'parquet';
|
|
36
|
-
|
|
37
|
-
/** GraphQL mutation name (for ingest direction) */
|
|
38
|
-
mutation?: string;
|
|
39
|
-
|
|
40
|
-
/** GraphQL query name (for extract direction) */
|
|
41
|
-
query?: string;
|
|
42
|
-
|
|
43
|
-
/** Nodes configuration for nested/escaped data */
|
|
44
|
-
nodes?: Record<string, NodeConfig>;
|
|
45
|
-
|
|
46
|
-
/** Field mappings */
|
|
47
|
-
fields: Record<string, FieldConfig>;
|
|
48
|
-
|
|
49
|
-
/** Return fields for GraphQL mutation (defaults to ['id', 'ref']) */
|
|
50
|
-
returnFields?: string[];
|
|
51
|
-
}
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### FieldConfig
|
|
55
|
-
|
|
56
|
-
Individual field configuration:
|
|
57
|
-
|
|
58
|
-
```typescript
|
|
59
|
-
interface FieldConfig {
|
|
60
|
-
/** Source field path (supports dot notation, array indices, XML attributes) */
|
|
61
|
-
source?: string;
|
|
62
|
-
|
|
63
|
-
/** Static value (alternative to source) */
|
|
64
|
-
value?: any;
|
|
65
|
-
|
|
66
|
-
/** Default value if source is null/undefined */
|
|
67
|
-
defaultValue?: any;
|
|
68
|
-
|
|
69
|
-
/** Resolver function name (sdk.* or custom.*) */
|
|
70
|
-
resolver?: string;
|
|
71
|
-
|
|
72
|
-
/** Whether field is required (throws if missing).
|
|
73
|
-
*
|
|
74
|
-
* **Validation Timing:**
|
|
75
|
-
* - Fields with `source`: Validated BEFORE resolver execution
|
|
76
|
-
* - Resolver-only fields (no source): Validated AFTER resolver execution
|
|
77
|
-
*
|
|
78
|
-
* **Behavior:**
|
|
79
|
-
* - `required: true`: Mapping stops immediately if field is missing/empty, returns `success: false`
|
|
80
|
-
* - `required: false` or omitted: Mapping continues with warning, field omitted from output
|
|
81
|
-
*
|
|
82
|
-
* **Important:** Resolvers can return `null`, `undefined`, or empty strings.
|
|
83
|
-
* `required: true` validates AFTER resolver execution to catch these cases.
|
|
84
|
-
*
|
|
85
|
-
* **Examples:**
|
|
86
|
-
* ```json
|
|
87
|
-
* // Source field - validates before resolver
|
|
88
|
-
* { "source": "sku", "resolver": "sdk.trim", "required": true }
|
|
89
|
-
*
|
|
90
|
-
* // Resolver-only - validates after resolver
|
|
91
|
-
* { "resolver": "custom.generateUUID", "required": true }
|
|
92
|
-
* ```
|
|
93
|
-
*/
|
|
94
|
-
required?: boolean;
|
|
95
|
-
|
|
96
|
-
/** Nested object fields */
|
|
97
|
-
fields?: Record<string, FieldConfig>;
|
|
98
|
-
|
|
99
|
-
/** Array configuration */
|
|
100
|
-
isArray?: boolean;
|
|
101
|
-
|
|
102
|
-
/** Comment for documentation */
|
|
103
|
-
comment?: string;
|
|
104
|
-
}
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### NodeConfig
|
|
108
|
-
|
|
109
|
-
Configuration for extracting nested/escaped data:
|
|
110
|
-
|
|
111
|
-
```typescript
|
|
112
|
-
interface NodeConfig {
|
|
113
|
-
/** Path to extract data from source */
|
|
114
|
-
extract: string;
|
|
115
|
-
|
|
116
|
-
/** How to parse extracted data */
|
|
117
|
-
parse: 'xml' | 'json';
|
|
118
|
-
|
|
119
|
-
/** Optional schema validation */
|
|
120
|
-
schema?: string;
|
|
121
|
-
}
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
### ResolverFunction
|
|
125
|
-
|
|
126
|
-
Custom resolver function signature:
|
|
127
|
-
|
|
128
|
-
```typescript
|
|
129
|
-
type ResolverFunction = (
|
|
130
|
-
value: any, // Extracted field value
|
|
131
|
-
sourceData: any, // Complete source object
|
|
132
|
-
config: any, // Resolver configuration
|
|
133
|
-
helpers: ResolverHelpers // 54 helper functions
|
|
134
|
-
) => any | Promise<any>; // Transformed value (can be async)
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### ResolverHelpers
|
|
138
|
-
|
|
139
|
-
Helper functions available in custom resolvers:
|
|
140
|
-
|
|
141
|
-
```typescript
|
|
142
|
-
interface ResolverHelpers {
|
|
143
|
-
/** Fluent client instance (if available) */
|
|
144
|
-
fluentClient?: any;
|
|
145
|
-
|
|
146
|
-
/** Logger instance */
|
|
147
|
-
logger?: any;
|
|
148
|
-
|
|
149
|
-
/** Path resolution helper */
|
|
150
|
-
get: (obj: any, path: string, defaultValue?: any) => any;
|
|
151
|
-
|
|
152
|
-
/** Set value at path */
|
|
153
|
-
set: (obj: any, path: string, value: any) => void;
|
|
154
|
-
|
|
155
|
-
/** Array normalization helper */
|
|
156
|
-
ensureArray: <T>(val: T | T[] | null | undefined) => T[];
|
|
157
|
-
|
|
158
|
-
/** Safe number parsing */
|
|
159
|
-
parseIntSafe: (value: any, defaultValue?: number) => number;
|
|
160
|
-
parseFloatSafe: (value: any, defaultValue?: number) => number;
|
|
161
|
-
|
|
162
|
-
/** Safe JSON parsing */
|
|
163
|
-
safeJsonParse: (json: string, defaultValue?: any) => any;
|
|
164
|
-
|
|
165
|
-
/** Date formatting */
|
|
166
|
-
formatDate: (date: string | Date) => string;
|
|
167
|
-
parseDate: (value: any) => Date | null;
|
|
168
|
-
|
|
169
|
-
/** String utilities */
|
|
170
|
-
toCamelCase: (str: string) => string;
|
|
171
|
-
toSnakeCase: (str: string) => string;
|
|
172
|
-
truncate: (str: string, maxLength: number) => string;
|
|
173
|
-
normalizeWhitespace: (str: string) => string;
|
|
174
|
-
fullName: (firstName?: string, lastName?: string) => string;
|
|
175
|
-
|
|
176
|
-
/** Validation */
|
|
177
|
-
isValidEmail: (email: string) => boolean;
|
|
178
|
-
requireField: (value: any, fieldName: string) => void;
|
|
179
|
-
isEmpty: (value: any) => boolean;
|
|
180
|
-
isUrl: (url: string) => boolean;
|
|
181
|
-
|
|
182
|
-
/** Object utilities */
|
|
183
|
-
deepClone: <T>(obj: T) => T;
|
|
184
|
-
deepMerge: (target: any, source: any) => any;
|
|
185
|
-
pick: <T extends object, K extends keyof T>(obj: T, keys: K[]) => Pick<T, K>;
|
|
186
|
-
omit: <T extends object, K extends keyof T>(obj: T, keys: K[]) => Omit<T, K>;
|
|
187
|
-
flatten: (obj: any, prefix?: string) => Record<string, any>;
|
|
188
|
-
|
|
189
|
-
/** Array utilities */
|
|
190
|
-
unique: <T>(arr: T[]) => T[];
|
|
191
|
-
groupBy: <T>(arr: T[], key: string) => Record<string, T[]>;
|
|
192
|
-
sortBy: <T>(arr: T[], key: string) => T[];
|
|
193
|
-
chunk: <T>(arr: T[], size: number) => T[][];
|
|
194
|
-
findBy: <T>(arr: T[], key: string, value: any) => T | undefined;
|
|
195
|
-
|
|
196
|
-
/** Async utilities */
|
|
197
|
-
retry: <T>(fn: () => Promise<T>, maxRetries?: number, delayMs?: number) => Promise<T>;
|
|
198
|
-
batchProcess: <T, R>(
|
|
199
|
-
items: T[],
|
|
200
|
-
processor: (item: T) => Promise<R>,
|
|
201
|
-
batchSize?: number
|
|
202
|
-
) => Promise<R[]>;
|
|
203
|
-
sleep: (ms: number) => Promise<void>;
|
|
204
|
-
timeout: <T>(promise: Promise<T>, ms: number) => Promise<T>;
|
|
205
|
-
|
|
206
|
-
/** Performance */
|
|
207
|
-
memoize: <T extends (...args: any[]) => any>(fn: T) => T;
|
|
208
|
-
hash: (data: string) => string;
|
|
209
|
-
uuid: () => string;
|
|
210
|
-
|
|
211
|
-
/** Context */
|
|
212
|
-
context: Record<string, any>;
|
|
213
|
-
|
|
214
|
-
/** Additional custom helpers */
|
|
215
|
-
[key: string]: any;
|
|
216
|
-
}
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
### MapResult
|
|
220
|
-
|
|
221
|
-
Result from UniversalMapper.map():
|
|
222
|
-
|
|
223
|
-
```typescript
|
|
224
|
-
interface MapResult {
|
|
225
|
-
/** Whether mapping succeeded */
|
|
226
|
-
success: boolean;
|
|
227
|
-
|
|
228
|
-
/** Mapped data */
|
|
229
|
-
data: Record<string, any>;
|
|
230
|
-
|
|
231
|
-
/** Error messages (if any) */
|
|
232
|
-
errors?: string[];
|
|
233
|
-
|
|
234
|
-
/** Warning messages (non-blocking) */
|
|
235
|
-
warnings?: string[];
|
|
236
|
-
}
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
---
|
|
240
|
-
|
|
241
|
-
## Configuration Format
|
|
242
|
-
|
|
243
|
-
**Field mapping format:**
|
|
244
|
-
|
|
245
|
-
```json
|
|
246
|
-
{
|
|
247
|
-
"fields": {
|
|
248
|
-
"productRef": {
|
|
249
|
-
"source": "sku",
|
|
250
|
-
"resolver": "sdk.uppercase"
|
|
251
|
-
},
|
|
252
|
-
"quantity": {
|
|
253
|
-
"source": "qty",
|
|
254
|
-
"resolver": "sdk.parseInt"
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
---
|
|
261
|
-
|
|
262
|
-
## Field Configuration Edge Cases & Best Practices
|
|
263
|
-
|
|
264
|
-
### ⚠️ Important Field Property Combinations
|
|
265
|
-
|
|
266
|
-
Understanding how field properties interact is critical for correct mapping behavior.
|
|
267
|
-
|
|
268
|
-
---
|
|
269
|
-
|
|
270
|
-
#### ✅ Resolver-Only Fields with `required: true`
|
|
271
|
-
|
|
272
|
-
**New in SDK v0.1.37:** Resolver-only fields can now be required.
|
|
273
|
-
|
|
274
|
-
**Pattern:**
|
|
275
|
-
```json
|
|
276
|
-
{
|
|
277
|
-
"id": {
|
|
278
|
-
"resolver": "custom.generateUUID",
|
|
279
|
-
"required": true
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
**When to use:**
|
|
285
|
-
- Generate unique IDs (UUID, composite keys)
|
|
286
|
-
- Look up values from external APIs
|
|
287
|
-
- Derive values from multiple source fields
|
|
288
|
-
- Apply complex business logic
|
|
289
|
-
|
|
290
|
-
**Validation behavior:**
|
|
291
|
-
- Required validation runs AFTER resolver execution
|
|
292
|
-
- Resolver must return a non-empty value
|
|
293
|
-
- If resolver returns null/undefined/'', mapping fails
|
|
294
|
-
|
|
295
|
-
**Example:**
|
|
296
|
-
```typescript
|
|
297
|
-
const mapper = new UniversalMapper(
|
|
298
|
-
{
|
|
299
|
-
fields: {
|
|
300
|
-
transactionId: {
|
|
301
|
-
resolver: 'custom.generateTransactionId',
|
|
302
|
-
required: true,
|
|
303
|
-
},
|
|
304
|
-
},
|
|
305
|
-
},
|
|
306
|
-
{
|
|
307
|
-
customResolvers: {
|
|
308
|
-
'custom.generateTransactionId': (value, sourceData, config, helpers) => {
|
|
309
|
-
return `TXN-${Date.now()}-${helpers.uuid()}`;
|
|
310
|
-
},
|
|
311
|
-
},
|
|
312
|
-
}
|
|
313
|
-
);
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
📖 **Learn more:** [Resolver-Only Fields Guide](../resolvers/mapping-resolvers-resolver-guide.md#resolver-only-fields--required-validation)
|
|
317
|
-
|
|
318
|
-
---
|
|
319
|
-
|
|
320
|
-
#### ⚠️ `required: true` + `defaultValue` (Contradictory)
|
|
321
|
-
|
|
322
|
-
**Status:** ⚠️ This combination is semantically contradictory for source fields.
|
|
323
|
-
|
|
324
|
-
**Problem:**
|
|
325
|
-
```json
|
|
326
|
-
{
|
|
327
|
-
"status": {
|
|
328
|
-
"source": "orderStatus",
|
|
329
|
-
"required": true,
|
|
330
|
-
"defaultValue": "PENDING" // ❌ Never used!
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
**Behavior:**
|
|
336
|
-
1. Extract from source → `undefined` (missing)
|
|
337
|
-
2. Validate required → ❌ Throws error immediately
|
|
338
|
-
3. Apply defaultValue → ❌ Never reached
|
|
339
|
-
|
|
340
|
-
**Why this doesn't work:**
|
|
341
|
-
- Required validation happens BEFORE defaultValue application for source fields
|
|
342
|
-
- DefaultValue is never applied because required fails first
|
|
343
|
-
|
|
344
|
-
**Important:** The order for `source + resolver + defaultValue + required` is:
|
|
345
|
-
1. Extract from source → `undefined`
|
|
346
|
-
2. Validate required → ❌ Fails immediately (defaultValue never applied)
|
|
347
|
-
3. Apply defaultValue → ❌ Never reached
|
|
348
|
-
4. Run resolver → ❌ Never reached
|
|
349
|
-
|
|
350
|
-
The code includes a warning when this combination is detected (see console output).
|
|
351
|
-
|
|
352
|
-
**Solution:**
|
|
353
|
-
```json
|
|
354
|
-
// ✅ CORRECT - Optional field with default
|
|
355
|
-
{
|
|
356
|
-
"status": {
|
|
357
|
-
"source": "orderStatus",
|
|
358
|
-
"required": false, // or omit required
|
|
359
|
-
"defaultValue": "PENDING"
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// ✅ CORRECT - Required field without default
|
|
364
|
-
{
|
|
365
|
-
"status": {
|
|
366
|
-
"source": "orderStatus",
|
|
367
|
-
"required": true
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
```
|
|
371
|
-
|
|
372
|
-
---
|
|
373
|
-
|
|
374
|
-
#### ✅ `defaultValue` + `resolver` (Works, but order matters)
|
|
375
|
-
|
|
376
|
-
**Pattern:**
|
|
377
|
-
```json
|
|
378
|
-
{
|
|
379
|
-
"status": {
|
|
380
|
-
"source": "orderStatus",
|
|
381
|
-
"defaultValue": "pending",
|
|
382
|
-
"resolver": "sdk.uppercase"
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
**Behavior:**
|
|
388
|
-
1. Extract from source → `undefined` (missing)
|
|
389
|
-
2. Apply defaultValue → `"pending"`
|
|
390
|
-
3. Apply resolver → `"PENDING"`
|
|
391
|
-
|
|
392
|
-
**Key insight:** Resolver receives the defaultValue, not the missing source value.
|
|
393
|
-
|
|
394
|
-
**Real-world example:**
|
|
395
|
-
```json
|
|
396
|
-
{
|
|
397
|
-
"attributes.condition": {
|
|
398
|
-
"source": "condition",
|
|
399
|
-
"defaultValue": "NEW",
|
|
400
|
-
"resolver": "sdk.uppercase",
|
|
401
|
-
"required": false
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
// Missing source → "NEW" → "NEW" (already uppercase)
|
|
405
|
-
// Empty source → "NEW" → "NEW"
|
|
406
|
-
// Source "used" → "USED" → "USED"
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
---
|
|
410
|
-
|
|
411
|
-
#### ✅ `required: true` + `value` (Redundant but harmless)
|
|
412
|
-
|
|
413
|
-
**Pattern:**
|
|
414
|
-
```json
|
|
415
|
-
{
|
|
416
|
-
"type": {
|
|
417
|
-
"value": "STANDARD",
|
|
418
|
-
"required": true // ⚠️ Redundant
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
**Behavior:**
|
|
424
|
-
- Static values are always present
|
|
425
|
-
- Required validation always passes
|
|
426
|
-
- `required: true` has no effect but doesn't hurt
|
|
427
|
-
|
|
428
|
-
**Recommendation:**
|
|
429
|
-
```json
|
|
430
|
-
// ✅ BETTER - Omit redundant required
|
|
431
|
-
{
|
|
432
|
-
"type": {
|
|
433
|
-
"value": "STANDARD"
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
```
|
|
437
|
-
|
|
438
|
-
---
|
|
439
|
-
|
|
440
|
-
#### ✅ Empty String Handling
|
|
441
|
-
|
|
442
|
-
**Behavior:** Empty string (`''`) is treated as "missing" for:
|
|
443
|
-
- Required validation
|
|
444
|
-
- DefaultValue application
|
|
445
|
-
|
|
446
|
-
**Example:**
|
|
447
|
-
```json
|
|
448
|
-
{
|
|
449
|
-
"status": {
|
|
450
|
-
"source": "orderStatus", // = ""
|
|
451
|
-
"defaultValue": "PENDING",
|
|
452
|
-
"required": false
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
// Result: "PENDING" (empty string triggers default)
|
|
456
|
-
```
|
|
457
|
-
|
|
458
|
-
**Validation:**
|
|
459
|
-
```typescript
|
|
460
|
-
// Empty string fails required validation
|
|
461
|
-
if (value === null || value === undefined || value === '')
|
|
462
|
-
|
|
463
|
-
// Empty string triggers defaultValue
|
|
464
|
-
if ((value === null || value === undefined || value === '') && config.defaultValue !== undefined)
|
|
465
|
-
```
|
|
466
|
-
|
|
467
|
-
---
|
|
468
|
-
|
|
469
|
-
### 📋 Field Property Combinations Summary
|
|
470
|
-
|
|
471
|
-
| Combination | Status | Behavior |
|
|
472
|
-
|-------------|--------|----------|
|
|
473
|
-
| `source` + `required: true` | ✅ Correct | Validates before resolver, fails if missing |
|
|
474
|
-
| `resolver` + `required: true` (no source) | ✅ Correct | Validates after resolver, fails if empty |
|
|
475
|
-
| `source` + `resolver` + `required: true` | ✅ Correct | Validates source, then applies resolver |
|
|
476
|
-
| `required: true` + `defaultValue` (source only) | ❌ Contradictory | Required fails before default applied |
|
|
477
|
-
| `source` + `resolver` + `defaultValue` + `required: true` | ❌ Contradictory | Required fails before default applied (same as above) |
|
|
478
|
-
| `defaultValue` + `resolver` | ✅ Works | Resolver receives default if source missing |
|
|
479
|
-
| `value` + `required: true` | ⚠️ Redundant | Works but required is unnecessary |
|
|
480
|
-
| `value` + `source` | ❌ Invalid | value takes precedence, source ignored |
|
|
481
|
-
| `value` + `resolver` | ✅ Works | Resolver transforms static value |
|
|
482
|
-
|
|
483
|
-
---
|
|
484
|
-
|
|
485
|
-
### 🎯 Best Practices
|
|
486
|
-
|
|
487
|
-
**1. ✅ DO: Use `required: true` for mandatory source fields**
|
|
488
|
-
```json
|
|
489
|
-
{ "source": "sku", "required": true }
|
|
490
|
-
```
|
|
491
|
-
|
|
492
|
-
**2. ✅ DO: Use `defaultValue` for optional fields**
|
|
493
|
-
```json
|
|
494
|
-
{ "source": "status", "defaultValue": "ACTIVE", "required": false }
|
|
495
|
-
```
|
|
496
|
-
|
|
497
|
-
**3. ✅ DO: Use resolver-only for generated values**
|
|
498
|
-
```json
|
|
499
|
-
{ "resolver": "custom.generateId", "required": true }
|
|
500
|
-
```
|
|
501
|
-
|
|
502
|
-
**4. ❌ DON'T: Combine `required: true` with `defaultValue`**
|
|
503
|
-
```json
|
|
504
|
-
// ❌ Contradictory - default will never be used
|
|
505
|
-
{ "source": "field", "required": true, "defaultValue": "FALLBACK" }
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
**5. ⚠️ CAUTION: `defaultValue` + `resolver` - default is passed to resolver**
|
|
509
|
-
```json
|
|
510
|
-
// Resolver receives "NEW" if source missing
|
|
511
|
-
{ "source": "condition", "defaultValue": "NEW", "resolver": "sdk.uppercase" }
|
|
512
|
-
```
|
|
513
|
-
|
|
514
|
-
---
|
|
515
|
-
|
|
516
|
-
### From Old Transformer System
|
|
517
|
-
|
|
518
|
-
**Old transformers → New resolvers:**
|
|
519
|
-
|
|
520
|
-
| Old Transformer | New Resolver |
|
|
521
|
-
| --------------- | ---------------- |
|
|
522
|
-
| `uppercase` | `sdk.uppercase` |
|
|
523
|
-
| `lowercase` | `sdk.lowercase` |
|
|
524
|
-
| `parseInt` | `sdk.parseInt` |
|
|
525
|
-
| `parseFloat` | `sdk.parseFloat` |
|
|
526
|
-
| `formatDate` | `sdk.formatDate` |
|
|
527
|
-
| `boolean` | `sdk.boolean` |
|
|
528
|
-
| `toJson` | `sdk.toJson` |
|
|
529
|
-
| `parseJson` | `sdk.parseJson` |
|
|
530
|
-
|
|
531
|
-
### Migration Steps
|
|
532
|
-
|
|
533
|
-
1. **Identify mapping format**:
|
|
534
|
-
- Check for `fieldMappings` array
|
|
535
|
-
- Check for `transformer` property
|
|
536
|
-
- Check for field names
|
|
537
|
-
|
|
538
|
-
2. **Convert to universal mapping format**:
|
|
539
|
-
- Replace `fieldMappings` with `fields` object
|
|
540
|
-
- Replace `sourceField` + `targetField` with nested structure
|
|
541
|
-
- Replace `transformer` with `resolver`
|
|
542
|
-
- Add `sdk.` prefix to resolver names
|
|
543
|
-
|
|
544
|
-
3. **Validate against schema**:
|
|
545
|
-
|
|
546
|
-
```bash
|
|
547
|
-
fc-connect-validate-schema --mapping config/new-mapping.json --schema schema.json
|
|
548
|
-
```
|
|
549
|
-
|
|
550
|
-
4. **Test with sample data**:
|
|
551
|
-
```typescript
|
|
552
|
-
const mapper = new UniversalMapper(newConfig);
|
|
553
|
-
const result = await mapper.map(sampleData);
|
|
554
|
-
console.log('Migration result:', result);
|
|
555
|
-
```
|
|
556
|
-
|
|
557
|
-
### Example Migration
|
|
558
|
-
|
|
559
|
-
**Before** (old format):
|
|
560
|
-
|
|
561
|
-
```json
|
|
562
|
-
{
|
|
563
|
-
"fieldMappings": [
|
|
564
|
-
{
|
|
565
|
-
"sourceField": "customer_email",
|
|
566
|
-
"targetField": "customer.email",
|
|
567
|
-
"transformer": "lowercase",
|
|
568
|
-
"required": true
|
|
569
|
-
},
|
|
570
|
-
{
|
|
571
|
-
"sourceField": "order_total",
|
|
572
|
-
"targetField": "totalPrice",
|
|
573
|
-
"transformer": "parseFloat"
|
|
574
|
-
},
|
|
575
|
-
{
|
|
576
|
-
"sourceField": "status",
|
|
577
|
-
"targetField": "status",
|
|
578
|
-
"transformer": "uppercase",
|
|
579
|
-
"defaultValue": "PENDING"
|
|
580
|
-
}
|
|
581
|
-
]
|
|
582
|
-
}
|
|
583
|
-
```
|
|
584
|
-
|
|
585
|
-
**After** (new format):
|
|
586
|
-
|
|
587
|
-
```json
|
|
588
|
-
{
|
|
589
|
-
"direction": "ingest",
|
|
590
|
-
"fields": {
|
|
591
|
-
"customer.email": {
|
|
592
|
-
"source": "customer_email",
|
|
593
|
-
"resolver": "sdk.lowercase",
|
|
594
|
-
"required": true
|
|
595
|
-
},
|
|
596
|
-
"totalPrice": {
|
|
597
|
-
"source": "order_total",
|
|
598
|
-
"resolver": "sdk.parseFloat"
|
|
599
|
-
},
|
|
600
|
-
"status": {
|
|
601
|
-
"source": "status",
|
|
602
|
-
"resolver": "sdk.uppercase",
|
|
603
|
-
"defaultValue": "PENDING"
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
```
|
|
608
|
-
|
|
609
|
-
---
|
|
610
|
-
|
|
611
|
-
## Performance Tips
|
|
612
|
-
|
|
613
|
-
### 1. Minimize Resolver Calls
|
|
614
|
-
|
|
615
|
-
```typescript
|
|
616
|
-
// ❌ BAD: Multiple resolvers for same transformation
|
|
617
|
-
{
|
|
618
|
-
"fields": {
|
|
619
|
-
"email": {
|
|
620
|
-
"source": "customer_email",
|
|
621
|
-
"resolver": "sdk.lowercase"
|
|
622
|
-
},
|
|
623
|
-
"emailNormalized": {
|
|
624
|
-
"source": "customer_email",
|
|
625
|
-
"resolver": "sdk.trim" // Processes same field again
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
// ✅ GOOD: Single custom resolver
|
|
631
|
-
{
|
|
632
|
-
"fields": {
|
|
633
|
-
"email": {
|
|
634
|
-
"source": "customer_email",
|
|
635
|
-
"resolver": "custom.normalizeEmail" // Does lowercase + trim in one pass
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
```
|
|
640
|
-
|
|
641
|
-
### 2. Cache Expensive Lookups
|
|
642
|
-
|
|
643
|
-
```typescript
|
|
644
|
-
const customerCache = new Map();
|
|
645
|
-
|
|
646
|
-
const customResolvers = {
|
|
647
|
-
'custom.lookupCustomer': async (value, data, config, helpers) => {
|
|
648
|
-
if (customerCache.has(value)) {
|
|
649
|
-
return customerCache.get(value); // Cache hit - no API call
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
const result = await helpers.fluentClient.graphql({
|
|
653
|
-
/* query */
|
|
654
|
-
});
|
|
655
|
-
customerCache.set(value, result.id);
|
|
656
|
-
return result.id;
|
|
657
|
-
},
|
|
658
|
-
};
|
|
659
|
-
```
|
|
660
|
-
|
|
661
|
-
### 3. Use Memoization
|
|
662
|
-
|
|
663
|
-
```typescript
|
|
664
|
-
const expensiveCalculation = helpers.memoize((value) => {
|
|
665
|
-
// Complex calculation here
|
|
666
|
-
return result;
|
|
667
|
-
});
|
|
668
|
-
|
|
669
|
-
'custom.calculate': (value) => expensiveCalculation(value)
|
|
670
|
-
```
|
|
671
|
-
|
|
672
|
-
### 4. Batch Process Arrays
|
|
673
|
-
|
|
674
|
-
```typescript
|
|
675
|
-
// ❌ BAD: Sequential processing (slow for large arrays)
|
|
676
|
-
for (const item of items) {
|
|
677
|
-
await processItem(item);
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
// ✅ GOOD: Batch processing (parallel chunks)
|
|
681
|
-
await helpers.batchProcess(items, processItem, 100);
|
|
682
|
-
```
|
|
683
|
-
|
|
684
|
-
### 5. Avoid Deep Nesting
|
|
685
|
-
|
|
686
|
-
```typescript
|
|
687
|
-
// ❌ BAD: Deep nesting causes performance overhead
|
|
688
|
-
{
|
|
689
|
-
"fields": {
|
|
690
|
-
"customer": {
|
|
691
|
-
"fields": {
|
|
692
|
-
"address": {
|
|
693
|
-
"fields": {
|
|
694
|
-
"location": {
|
|
695
|
-
"fields": {
|
|
696
|
-
"city": { "source": "city" }
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
// ✅ GOOD: Flat dot notation (faster)
|
|
707
|
-
{
|
|
708
|
-
"fields": {
|
|
709
|
-
"customer.address.location.city": {
|
|
710
|
-
"source": "city"
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
```
|
|
715
|
-
|
|
716
|
-
---
|
|
717
|
-
|
|
718
|
-
## FAQ
|
|
719
|
-
|
|
720
|
-
### Q: What's the difference between UniversalMapper and GraphQLMutationMapper?
|
|
721
|
-
|
|
722
|
-
**A**: Direction determines which to use:
|
|
723
|
-
|
|
724
|
-
| Mapper | Direction | Purpose | GraphQL Execution |
|
|
725
|
-
| ----------------------- | --------- | --------------------------------------- | ----------------- |
|
|
726
|
-
| `UniversalMapper` | `extract` | Fluent → External (CSV, Parquet, JSON) | ❌ No |
|
|
727
|
-
| `GraphQLMutationMapper` | `ingest` | External → Fluent (XML, JSON → GraphQL) | ✅ Yes |
|
|
728
|
-
|
|
729
|
-
Both use the **same mapping configuration format**.
|
|
730
|
-
|
|
731
|
-
---
|
|
732
|
-
|
|
733
|
-
### Q: Can I use the same mapping config for both ingestion and extraction?
|
|
734
|
-
|
|
735
|
-
**A**: No, because:
|
|
736
|
-
|
|
737
|
-
- **Ingestion**: Source = External data, Target = Fluent schema
|
|
738
|
-
- **Extraction**: Source = Fluent data, Target = External format
|
|
739
|
-
|
|
740
|
-
Field names and structures are different.
|
|
741
|
-
|
|
742
|
-
---
|
|
743
|
-
|
|
744
|
-
### Q: How do I map arrays?
|
|
745
|
-
|
|
746
|
-
**A**: Use `isArray: true` property:
|
|
747
|
-
|
|
748
|
-
```json
|
|
749
|
-
{
|
|
750
|
-
"fields": {
|
|
751
|
-
"items": {
|
|
752
|
-
"source": "line_items",
|
|
753
|
-
"isArray": true,
|
|
754
|
-
"fields": {
|
|
755
|
-
"ref": { "source": "$.id" },
|
|
756
|
-
"quantity": { "source": "$.qty" }
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
```
|
|
762
|
-
|
|
763
|
-
See [Module 5: Arrays](./mapping-05-advanced-patterns.md#arrays) for complete guide.
|
|
764
|
-
|
|
765
|
-
---
|
|
766
|
-
|
|
767
|
-
### Q: How do I handle nested/escaped XML?
|
|
768
|
-
|
|
769
|
-
**A**: Use `nodes` configuration:
|
|
770
|
-
|
|
771
|
-
```json
|
|
772
|
-
{
|
|
773
|
-
"nodes": {
|
|
774
|
-
"radial": {
|
|
775
|
-
"extract": "orders.order.custom-attribute[attribute-id=CreateOrderServiceRequest]",
|
|
776
|
-
"parse": "xml"
|
|
777
|
-
}
|
|
778
|
-
},
|
|
779
|
-
"fields": {
|
|
780
|
-
"ref": {
|
|
781
|
-
"source": "$radial.Order@customerOrderId"
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
```
|
|
786
|
-
|
|
787
|
-
See [Module 4: XML to GraphQL](./mapping-04-use-cases.md#use-case-2-xml-graphql-mutation) for complete example.
|
|
788
|
-
|
|
789
|
-
---
|
|
790
|
-
|
|
791
|
-
### Q: Can resolvers be async?
|
|
792
|
-
|
|
793
|
-
**A**: Yes! Custom resolvers can be async for API lookups:
|
|
794
|
-
|
|
795
|
-
```typescript
|
|
796
|
-
'custom.lookupCustomer': async (value, data, config, helpers) => {
|
|
797
|
-
const result = await helpers.fluentClient.graphql({ /* query */ });
|
|
798
|
-
return result.id;
|
|
799
|
-
}
|
|
800
|
-
```
|
|
801
|
-
|
|
802
|
-
You must `await` the mapping when using async resolvers:
|
|
803
|
-
|
|
804
|
-
```typescript
|
|
805
|
-
const result = await mapper.map(data);
|
|
806
|
-
```
|
|
807
|
-
|
|
808
|
-
---
|
|
809
|
-
|
|
810
|
-
### Q: How do I validate my mapping config?
|
|
811
|
-
|
|
812
|
-
**A**: Use the CLI validation tool:
|
|
813
|
-
|
|
814
|
-
```bash
|
|
815
|
-
fc-connect-validate-schema \
|
|
816
|
-
--mapping config/field-mappings.json \
|
|
817
|
-
--schema schema.json
|
|
818
|
-
```
|
|
819
|
-
|
|
820
|
-
See [Module 3: Schema Validation](../graphql-mutation-mapping/modules/graphql-mutation-mapping-03-schema-validation.md) for complete guide.
|
|
821
|
-
|
|
822
|
-
---
|
|
823
|
-
|
|
824
|
-
### Q: What's the performance of UniversalMapper?
|
|
825
|
-
|
|
826
|
-
**A**: Very fast for typical use cases:
|
|
827
|
-
|
|
828
|
-
- **Small records** (< 100 fields): < 1ms per record
|
|
829
|
-
- **Large records** (100-1000 fields): 1-5ms per record
|
|
830
|
-
- **Batch processing** (10K records): 10-50 seconds
|
|
831
|
-
|
|
832
|
-
Bottlenecks are usually:
|
|
833
|
-
|
|
834
|
-
- Async resolvers making API calls
|
|
835
|
-
- Complex nested transformations
|
|
836
|
-
- JSON.parse/stringify operations
|
|
837
|
-
|
|
838
|
-
---
|
|
839
|
-
|
|
840
|
-
### Q: Can I chain multiple resolvers?
|
|
841
|
-
|
|
842
|
-
**A**: Not directly, but you can:
|
|
843
|
-
|
|
844
|
-
1. **Use custom resolver** that calls SDK resolvers:
|
|
845
|
-
|
|
846
|
-
```typescript
|
|
847
|
-
'custom.normalize': (value, data, config, helpers) => {
|
|
848
|
-
let result = value;
|
|
849
|
-
if (typeof result === 'string') {
|
|
850
|
-
result = result.toLowerCase().trim();
|
|
851
|
-
}
|
|
852
|
-
return result;
|
|
853
|
-
}
|
|
854
|
-
```
|
|
855
|
-
|
|
856
|
-
2. **Use multiple field configs** (less efficient):
|
|
857
|
-
```json
|
|
858
|
-
{
|
|
859
|
-
"fields": {
|
|
860
|
-
"temp": { "source": "email", "resolver": "sdk.lowercase" },
|
|
861
|
-
"email": { "source": "temp", "resolver": "sdk.trim" }
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
```
|
|
865
|
-
|
|
866
|
-
---
|
|
867
|
-
|
|
868
|
-
### Q: How do I debug mapping issues?
|
|
869
|
-
|
|
870
|
-
**A**: Enable debug logging:
|
|
871
|
-
|
|
872
|
-
```typescript
|
|
873
|
-
const mapper = new UniversalMapper(config);
|
|
874
|
-
|
|
875
|
-
const result = await mapper.map(data);
|
|
876
|
-
|
|
877
|
-
console.log('Mapping result:', JSON.stringify(result, null, 2));
|
|
878
|
-
|
|
879
|
-
if (!result.success) {
|
|
880
|
-
console.error('Errors:', result.errors);
|
|
881
|
-
}
|
|
882
|
-
```
|
|
883
|
-
|
|
884
|
-
See [Troubleshooting](#troubleshooting) section below.
|
|
885
|
-
|
|
886
|
-
---
|
|
887
|
-
|
|
888
|
-
## Troubleshooting
|
|
889
|
-
|
|
890
|
-
### Issue: "Missing required field"
|
|
891
|
-
|
|
892
|
-
**Cause**: Required field is null/undefined in source data or resolver returned empty value.
|
|
893
|
-
|
|
894
|
-
**Solution 1: Provide defaultValue**
|
|
895
|
-
|
|
896
|
-
```json
|
|
897
|
-
{
|
|
898
|
-
"fields": {
|
|
899
|
-
"productRef": {
|
|
900
|
-
"source": "sku",
|
|
901
|
-
"required": true,
|
|
902
|
-
"defaultValue": "UNKNOWN" // Provide fallback
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
```
|
|
907
|
-
|
|
908
|
-
**Solution 2: Make field optional**
|
|
909
|
-
|
|
910
|
-
```json
|
|
911
|
-
{
|
|
912
|
-
"fields": {
|
|
913
|
-
"productRef": {
|
|
914
|
-
"source": "sku"
|
|
915
|
-
// Remove "required: true"
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
```
|
|
920
|
-
|
|
921
|
-
**Solution 3: Fix resolver that returns empty value**
|
|
922
|
-
|
|
923
|
-
If you have a resolver-only field that's required but returning empty:
|
|
924
|
-
|
|
925
|
-
```json
|
|
926
|
-
{
|
|
927
|
-
"fields": {
|
|
928
|
-
"customerId": {
|
|
929
|
-
"resolver": "custom.lookupCustomer",
|
|
930
|
-
"required": true
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
```
|
|
935
|
-
|
|
936
|
-
**Problem:** Resolver returns `undefined` when customer not found:
|
|
937
|
-
|
|
938
|
-
```typescript
|
|
939
|
-
'custom.lookupCustomer': async (value, data, config, helpers) => {
|
|
940
|
-
const email = helpers.get(data, 'customer.email');
|
|
941
|
-
const customer = await lookupCustomerByEmail(email);
|
|
942
|
-
return customer?.id; // ❌ Returns undefined if customer not found
|
|
943
|
-
}
|
|
944
|
-
```
|
|
945
|
-
|
|
946
|
-
**Fix:** Ensure resolver always returns a value or throw error:
|
|
947
|
-
|
|
948
|
-
```typescript
|
|
949
|
-
'custom.lookupCustomer': async (value, data, config, helpers) => {
|
|
950
|
-
const email = helpers.get(data, 'customer.email');
|
|
951
|
-
if (!email) {
|
|
952
|
-
throw new Error('Email required for customer lookup');
|
|
953
|
-
}
|
|
954
|
-
const customer = await lookupCustomerByEmail(email);
|
|
955
|
-
if (!customer) {
|
|
956
|
-
throw new Error(`Customer not found for email: ${email}`);
|
|
957
|
-
}
|
|
958
|
-
return customer.id; // ✅ Always returns a value or throws
|
|
959
|
-
}
|
|
960
|
-
```
|
|
961
|
-
|
|
962
|
-
**Solution 4: Use defaultValue with resolver (but ensure resolver always returns value)**
|
|
963
|
-
|
|
964
|
-
```json
|
|
965
|
-
{
|
|
966
|
-
"fields": {
|
|
967
|
-
"discount": {
|
|
968
|
-
"source": "order.subtotal",
|
|
969
|
-
"resolver": "custom.calculateDiscount",
|
|
970
|
-
"required": true,
|
|
971
|
-
"defaultValue": 0 // Applied before resolver, but resolver must return non-empty
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
```
|
|
976
|
-
|
|
977
|
-
**Important:** If `custom.calculateDiscount` returns `null`, `undefined`, or `''`, the mapping will still fail because required validation happens after resolver execution. The defaultValue only helps if the source is missing (it's applied before the resolver runs).
|
|
978
|
-
|
|
979
|
-
---
|
|
980
|
-
|
|
981
|
-
### Issue: "Resolver not found"
|
|
982
|
-
|
|
983
|
-
**Cause**: Resolver name is misspelled or not registered.
|
|
984
|
-
|
|
985
|
-
**Solution**:
|
|
986
|
-
|
|
987
|
-
- Check resolver name: `sdk.uppercase` (NOT `uppercase`)
|
|
988
|
-
- Register custom resolver:
|
|
989
|
-
```typescript
|
|
990
|
-
const mapper = new UniversalMapper(config, {
|
|
991
|
-
customResolvers: {
|
|
992
|
-
'custom.myResolver': value => transformValue(value),
|
|
993
|
-
},
|
|
994
|
-
});
|
|
995
|
-
```
|
|
996
|
-
|
|
997
|
-
---
|
|
998
|
-
|
|
999
|
-
### Issue: "Cannot read property of undefined"
|
|
1000
|
-
|
|
1001
|
-
**Cause**: Path doesn't exist in source data.
|
|
1002
|
-
|
|
1003
|
-
**Solution**: Use `defaultValue` or make path optional:
|
|
1004
|
-
|
|
1005
|
-
```json
|
|
1006
|
-
{
|
|
1007
|
-
"fields": {
|
|
1008
|
-
"city": {
|
|
1009
|
-
"source": "customer.address.city",
|
|
1010
|
-
"defaultValue": "Unknown"
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
```
|
|
1015
|
-
|
|
1016
|
-
Or use `helpers.get` in custom resolver:
|
|
1017
|
-
|
|
1018
|
-
```typescript
|
|
1019
|
-
'custom.getCity': (value, data, config, helpers) => {
|
|
1020
|
-
return helpers.get(data, 'customer.address.city', 'Unknown');
|
|
1021
|
-
}
|
|
1022
|
-
```
|
|
1023
|
-
|
|
1024
|
-
---
|
|
1025
|
-
|
|
1026
|
-
### Issue: "Invalid JSON"
|
|
1027
|
-
|
|
1028
|
-
**Cause**: Using `sdk.parseJson` on non-JSON string.
|
|
1029
|
-
|
|
1030
|
-
**Solution**: `sdk.parseJson` is null-safe and returns original value if parse fails:
|
|
1031
|
-
|
|
1032
|
-
```typescript
|
|
1033
|
-
// This won't throw - it returns "not json" unchanged
|
|
1034
|
-
{ "resolver": "sdk.parseJson" }
|
|
1035
|
-
```
|
|
1036
|
-
|
|
1037
|
-
If you need strict validation, use custom resolver:
|
|
1038
|
-
|
|
1039
|
-
```typescript
|
|
1040
|
-
'custom.strictParseJson': (value, data, config, helpers) => {
|
|
1041
|
-
const result = helpers.safeJsonParse(value, null);
|
|
1042
|
-
if (result === null) {
|
|
1043
|
-
throw new Error(`Invalid JSON: ${value}`);
|
|
1044
|
-
}
|
|
1045
|
-
return result;
|
|
1046
|
-
}
|
|
1047
|
-
```
|
|
1048
|
-
|
|
1049
|
-
---
|
|
1050
|
-
|
|
1051
|
-
### Issue: "Array expected but got object"
|
|
1052
|
-
|
|
1053
|
-
**Cause**: XML parser doesn't create arrays for single items.
|
|
1054
|
-
|
|
1055
|
-
**Solution**: Use `helpers.ensureArray`:
|
|
1056
|
-
|
|
1057
|
-
```typescript
|
|
1058
|
-
'custom.processItems': (value, data, config, helpers) => {
|
|
1059
|
-
const items = helpers.ensureArray(data.items);
|
|
1060
|
-
// Now always an array, even if single item
|
|
1061
|
-
}
|
|
1062
|
-
```
|
|
1063
|
-
|
|
1064
|
-
---
|
|
1065
|
-
|
|
1066
|
-
### Issue: "NaN in Fluent API response"
|
|
1067
|
-
|
|
1068
|
-
**Cause**: Number resolver returned `NaN` instead of valid number.
|
|
1069
|
-
|
|
1070
|
-
**Solution**: SDK resolvers (`sdk.parseInt`, `sdk.parseFloat`) return `0` by default (NOT `NaN`):
|
|
1071
|
-
|
|
1072
|
-
```json
|
|
1073
|
-
{
|
|
1074
|
-
"fields": {
|
|
1075
|
-
"quantity": {
|
|
1076
|
-
"source": "qty",
|
|
1077
|
-
"resolver": "sdk.parseInt" // Returns 0 for invalid, NOT NaN
|
|
1078
|
-
}
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1081
|
-
```
|
|
1082
|
-
|
|
1083
|
-
If using custom resolver, ensure safe parsing:
|
|
1084
|
-
|
|
1085
|
-
```typescript
|
|
1086
|
-
'custom.parseQty': (value, data, config, helpers) => {
|
|
1087
|
-
return helpers.parseIntSafe(value, 0); // Safe default
|
|
1088
|
-
}
|
|
1089
|
-
```
|
|
1090
|
-
|
|
1091
|
-
---
|
|
1092
|
-
|
|
1093
|
-
### Issue: "Circular reference in JSON"
|
|
1094
|
-
|
|
1095
|
-
**Cause**: `sdk.toJson` on object with circular references.
|
|
1096
|
-
|
|
1097
|
-
**Solution**: `sdk.toJson` is null-safe and returns original value if stringify fails:
|
|
1098
|
-
|
|
1099
|
-
```typescript
|
|
1100
|
-
// Won't throw on circular references
|
|
1101
|
-
{ "resolver": "sdk.toJson" }
|
|
1102
|
-
```
|
|
1103
|
-
|
|
1104
|
-
If you need custom handling:
|
|
1105
|
-
|
|
1106
|
-
```typescript
|
|
1107
|
-
'custom.safeStringify': (value) => {
|
|
1108
|
-
try {
|
|
1109
|
-
return JSON.stringify(value, (key, val) => {
|
|
1110
|
-
// Custom replacer logic
|
|
1111
|
-
return val;
|
|
1112
|
-
});
|
|
1113
|
-
} catch {
|
|
1114
|
-
return String(value);
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
```
|
|
1118
|
-
|
|
1119
|
-
---
|
|
1120
|
-
|
|
1121
|
-
## Best Practices
|
|
1122
|
-
|
|
1123
|
-
### 1. Always Validate Against Schema
|
|
1124
|
-
|
|
1125
|
-
Before deployment:
|
|
1126
|
-
|
|
1127
|
-
```bash
|
|
1128
|
-
fc-connect-validate-schema --mapping config/mappings.json --schema schema.json
|
|
1129
|
-
```
|
|
1130
|
-
|
|
1131
|
-
---
|
|
1132
|
-
|
|
1133
|
-
### 2. Use SDK Resolvers First
|
|
1134
|
-
|
|
1135
|
-
Don't write custom resolvers for common transformations:
|
|
1136
|
-
|
|
1137
|
-
```typescript
|
|
1138
|
-
// ❌ NOT NEEDED
|
|
1139
|
-
'custom.uppercase': (value) => value?.toUpperCase()
|
|
1140
|
-
|
|
1141
|
-
// ✅ USE SDK
|
|
1142
|
-
{ "resolver": "sdk.uppercase" }
|
|
1143
|
-
```
|
|
1144
|
-
|
|
1145
|
-
---
|
|
1146
|
-
|
|
1147
|
-
### 3. Provide Default Values for Required Fields
|
|
1148
|
-
|
|
1149
|
-
```json
|
|
1150
|
-
{
|
|
1151
|
-
"fields": {
|
|
1152
|
-
"status": {
|
|
1153
|
-
"source": "order_status",
|
|
1154
|
-
"required": true,
|
|
1155
|
-
"defaultValue": "PENDING"
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
```
|
|
1160
|
-
|
|
1161
|
-
---
|
|
1162
|
-
|
|
1163
|
-
### 4. Use Comments for Complex Mappings
|
|
1164
|
-
|
|
1165
|
-
```json
|
|
1166
|
-
{
|
|
1167
|
-
"fields": {
|
|
1168
|
-
"totalPrice": {
|
|
1169
|
-
"resolver": "custom.calculateTotal",
|
|
1170
|
-
"comment": "Sum of (price * qty) + tax + shipping for all items"
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
}
|
|
1174
|
-
```
|
|
1175
|
-
|
|
1176
|
-
---
|
|
1177
|
-
|
|
1178
|
-
### 5. Keep Resolvers Pure and Testable
|
|
1179
|
-
|
|
1180
|
-
```typescript
|
|
1181
|
-
// ✅ GOOD: Pure function, easy to test
|
|
1182
|
-
const customResolvers = {
|
|
1183
|
-
'custom.buildRef': (value, data) => `${data.sku}-${data.location}`,
|
|
1184
|
-
};
|
|
1185
|
-
|
|
1186
|
-
// Test
|
|
1187
|
-
const result = customResolvers`'custom.buildRef'`;
|
|
1188
|
-
console.assert(result === 'SKU001-WH01');
|
|
1189
|
-
```
|
|
1190
|
-
|
|
1191
|
-
---
|
|
1192
|
-
|
|
1193
|
-
### 6. Log Errors and Warnings
|
|
1194
|
-
|
|
1195
|
-
```typescript
|
|
1196
|
-
'custom.lookup': async (value, data, config, helpers) => {
|
|
1197
|
-
try {
|
|
1198
|
-
return await apiCall(value);
|
|
1199
|
-
} catch (error) {
|
|
1200
|
-
helpers.logger?.error('Lookup failed', { value, error });
|
|
1201
|
-
return null; // Graceful degradation
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
```
|
|
1205
|
-
|
|
1206
|
-
---
|
|
1207
|
-
|
|
1208
|
-
### 7. Use Nested Objects for Clarity
|
|
1209
|
-
|
|
1210
|
-
```json
|
|
1211
|
-
{
|
|
1212
|
-
"fields": {
|
|
1213
|
-
"customer": {
|
|
1214
|
-
"fields": {
|
|
1215
|
-
"id": { "source": "customer_id" },
|
|
1216
|
-
"email": { "source": "customer_email" }
|
|
1217
|
-
}
|
|
1218
|
-
}
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
```
|
|
1222
|
-
|
|
1223
|
-
More readable than:
|
|
1224
|
-
|
|
1225
|
-
```json
|
|
1226
|
-
{
|
|
1227
|
-
"fields": {
|
|
1228
|
-
"customer.id": { "source": "customer_id" },
|
|
1229
|
-
"customer.email": { "source": "customer_email" }
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1232
|
-
```
|
|
1233
|
-
|
|
1234
|
-
---
|
|
1235
|
-
|
|
1236
|
-
## Cheat Sheet
|
|
1237
|
-
|
|
1238
|
-
### Quick Mapping Patterns
|
|
1239
|
-
|
|
1240
|
-
```json
|
|
1241
|
-
// Static value
|
|
1242
|
-
{ "value": "ACTIVE" }
|
|
1243
|
-
|
|
1244
|
-
// Simple mapping
|
|
1245
|
-
{ "source": "sku" }
|
|
1246
|
-
|
|
1247
|
-
// With transformation
|
|
1248
|
-
{ "source": "email", "resolver": "sdk.lowercase" }
|
|
1249
|
-
|
|
1250
|
-
// With default
|
|
1251
|
-
{ "source": "status", "defaultValue": "PENDING" }
|
|
1252
|
-
|
|
1253
|
-
// Required field
|
|
1254
|
-
{ "source": "productRef", "required": true }
|
|
1255
|
-
|
|
1256
|
-
// Custom resolver
|
|
1257
|
-
{ "source": "data", "resolver": "custom.transform" }
|
|
1258
|
-
|
|
1259
|
-
// Nested object (dot notation)
|
|
1260
|
-
{ "customer.email": { "source": "email" } }
|
|
1261
|
-
|
|
1262
|
-
// Nested object (fields)
|
|
1263
|
-
{
|
|
1264
|
-
"customer": {
|
|
1265
|
-
"fields": {
|
|
1266
|
-
"email": { "source": "email" }
|
|
1267
|
-
}
|
|
1268
|
-
}
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1271
|
-
// Array
|
|
1272
|
-
{
|
|
1273
|
-
"items": {
|
|
1274
|
-
"source": "line_items",
|
|
1275
|
-
"isArray": true,
|
|
1276
|
-
"fields": {
|
|
1277
|
-
"ref": { "source": "$.id" }
|
|
1278
|
-
}
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
```
|
|
1282
|
-
|
|
1283
|
-
### Common SDK Resolvers
|
|
1284
|
-
|
|
1285
|
-
```json
|
|
1286
|
-
"sdk.uppercase" // String to UPPER
|
|
1287
|
-
"sdk.lowercase" // String to lower
|
|
1288
|
-
"sdk.trim" // Remove whitespace
|
|
1289
|
-
"sdk.parseInt" // String → int
|
|
1290
|
-
"sdk.parseFloat" // String → float
|
|
1291
|
-
"sdk.formatDate" // Date → ISO8601
|
|
1292
|
-
"sdk.boolean" // "true"/"1"/"yes" → true
|
|
1293
|
-
"sdk.parseJson" // JSON string → object
|
|
1294
|
-
"sdk.toJson" // Object → JSON string
|
|
1295
|
-
```
|
|
1296
|
-
|
|
1297
|
-
---
|
|
1298
|
-
|
|
1299
|
-
## Next Steps
|
|
1300
|
-
|
|
1301
|
-
You've completed the Universal Mapping Guide! Next steps:
|
|
1302
|
-
|
|
1303
|
-
- **[GraphQL Mutation Mapping Quick Reference](../graphql-mutation-mapping/graphql-mutation-mapping-quick-reference.md)** - Comprehensive cheat sheet
|
|
1304
|
-
- **[Examples](../examples/)** - Complete working examples
|
|
1305
|
-
- **[Use Cases](../../../01-TEMPLATES/readme.md)** - Real-world implementations
|
|
1306
|
-
|
|
1307
|
-
---
|
|
1308
|
-
|
|
1309
|
-
## Additional Resources
|
|
1310
|
-
|
|
1311
|
-
- [UniversalMapper Source Code](../../../../src/services/mapping/universal-mapper.ts)
|
|
1312
|
-
- [GraphQLMutationMapper Source](../../../../src/services/mapping/graphql-mutation-mapper.ts)
|
|
1313
|
-
- [SDK Resolvers Source](../../../../src/services/resolvers/sdk-resolvers.ts)
|
|
1314
|
-
- [Fluent Commerce API Documentation](https://docs.fluentcommerce.com/)
|
|
1315
|
-
|
|
1316
|
-
---
|
|
1317
|
-
|
|
1318
|
-
[← Back to Index](../mapping-readme.md) | [Previous: Helpers & Resolvers](./mapping-06-helpers-resolvers.md)
|
|
1319
|
-
|
|
1
|
+
# Module 7: API Reference
|
|
2
|
+
|
|
3
|
+
**Complete Technical Reference**
|
|
4
|
+
|
|
5
|
+
**Level:** Reference
|
|
6
|
+
**Time:** As needed
|
|
7
|
+
**Prerequisites:** Modules 1-6
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Table of Contents
|
|
12
|
+
|
|
13
|
+
1. [TypeScript Type Definitions](#typescript-type-definitions)
|
|
14
|
+
2. [Configuration Migration](#configuration-migration)
|
|
15
|
+
3. [Performance Tips](#performance-tips)
|
|
16
|
+
4. [FAQ](#faq)
|
|
17
|
+
5. [Troubleshooting](#troubleshooting)
|
|
18
|
+
6. [Best Practices](#best-practices)
|
|
19
|
+
7. [Cheat Sheet](#cheat-sheet)
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## TypeScript Type Definitions
|
|
24
|
+
|
|
25
|
+
### MappingConfig
|
|
26
|
+
|
|
27
|
+
Complete mapping configuration structure:
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
interface MappingConfig {
|
|
31
|
+
/** Direction of mapping */
|
|
32
|
+
direction: 'ingest' | 'extract';
|
|
33
|
+
|
|
34
|
+
/** Source data format (for context) */
|
|
35
|
+
sourceFormat?: 'csv' | 'xml' | 'json' | 'parquet';
|
|
36
|
+
|
|
37
|
+
/** GraphQL mutation name (for ingest direction) */
|
|
38
|
+
mutation?: string;
|
|
39
|
+
|
|
40
|
+
/** GraphQL query name (for extract direction) */
|
|
41
|
+
query?: string;
|
|
42
|
+
|
|
43
|
+
/** Nodes configuration for nested/escaped data */
|
|
44
|
+
nodes?: Record<string, NodeConfig>;
|
|
45
|
+
|
|
46
|
+
/** Field mappings */
|
|
47
|
+
fields: Record<string, FieldConfig>;
|
|
48
|
+
|
|
49
|
+
/** Return fields for GraphQL mutation (defaults to ['id', 'ref']) */
|
|
50
|
+
returnFields?: string[];
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### FieldConfig
|
|
55
|
+
|
|
56
|
+
Individual field configuration:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
interface FieldConfig {
|
|
60
|
+
/** Source field path (supports dot notation, array indices, XML attributes) */
|
|
61
|
+
source?: string;
|
|
62
|
+
|
|
63
|
+
/** Static value (alternative to source) */
|
|
64
|
+
value?: any;
|
|
65
|
+
|
|
66
|
+
/** Default value if source is null/undefined */
|
|
67
|
+
defaultValue?: any;
|
|
68
|
+
|
|
69
|
+
/** Resolver function name (sdk.* or custom.*) */
|
|
70
|
+
resolver?: string;
|
|
71
|
+
|
|
72
|
+
/** Whether field is required (throws if missing).
|
|
73
|
+
*
|
|
74
|
+
* **Validation Timing:**
|
|
75
|
+
* - Fields with `source`: Validated BEFORE resolver execution
|
|
76
|
+
* - Resolver-only fields (no source): Validated AFTER resolver execution
|
|
77
|
+
*
|
|
78
|
+
* **Behavior:**
|
|
79
|
+
* - `required: true`: Mapping stops immediately if field is missing/empty, returns `success: false`
|
|
80
|
+
* - `required: false` or omitted: Mapping continues with warning, field omitted from output
|
|
81
|
+
*
|
|
82
|
+
* **Important:** Resolvers can return `null`, `undefined`, or empty strings.
|
|
83
|
+
* `required: true` validates AFTER resolver execution to catch these cases.
|
|
84
|
+
*
|
|
85
|
+
* **Examples:**
|
|
86
|
+
* ```json
|
|
87
|
+
* // Source field - validates before resolver
|
|
88
|
+
* { "source": "sku", "resolver": "sdk.trim", "required": true }
|
|
89
|
+
*
|
|
90
|
+
* // Resolver-only - validates after resolver
|
|
91
|
+
* { "resolver": "custom.generateUUID", "required": true }
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
required?: boolean;
|
|
95
|
+
|
|
96
|
+
/** Nested object fields */
|
|
97
|
+
fields?: Record<string, FieldConfig>;
|
|
98
|
+
|
|
99
|
+
/** Array configuration */
|
|
100
|
+
isArray?: boolean;
|
|
101
|
+
|
|
102
|
+
/** Comment for documentation */
|
|
103
|
+
comment?: string;
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### NodeConfig
|
|
108
|
+
|
|
109
|
+
Configuration for extracting nested/escaped data:
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
interface NodeConfig {
|
|
113
|
+
/** Path to extract data from source */
|
|
114
|
+
extract: string;
|
|
115
|
+
|
|
116
|
+
/** How to parse extracted data */
|
|
117
|
+
parse: 'xml' | 'json';
|
|
118
|
+
|
|
119
|
+
/** Optional schema validation */
|
|
120
|
+
schema?: string;
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### ResolverFunction
|
|
125
|
+
|
|
126
|
+
Custom resolver function signature:
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
type ResolverFunction = (
|
|
130
|
+
value: any, // Extracted field value
|
|
131
|
+
sourceData: any, // Complete source object
|
|
132
|
+
config: any, // Resolver configuration
|
|
133
|
+
helpers: ResolverHelpers // 54 helper functions
|
|
134
|
+
) => any | Promise<any>; // Transformed value (can be async)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### ResolverHelpers
|
|
138
|
+
|
|
139
|
+
Helper functions available in custom resolvers:
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
interface ResolverHelpers {
|
|
143
|
+
/** Fluent client instance (if available) */
|
|
144
|
+
fluentClient?: any;
|
|
145
|
+
|
|
146
|
+
/** Logger instance */
|
|
147
|
+
logger?: any;
|
|
148
|
+
|
|
149
|
+
/** Path resolution helper */
|
|
150
|
+
get: (obj: any, path: string, defaultValue?: any) => any;
|
|
151
|
+
|
|
152
|
+
/** Set value at path */
|
|
153
|
+
set: (obj: any, path: string, value: any) => void;
|
|
154
|
+
|
|
155
|
+
/** Array normalization helper */
|
|
156
|
+
ensureArray: <T>(val: T | T[] | null | undefined) => T[];
|
|
157
|
+
|
|
158
|
+
/** Safe number parsing */
|
|
159
|
+
parseIntSafe: (value: any, defaultValue?: number) => number;
|
|
160
|
+
parseFloatSafe: (value: any, defaultValue?: number) => number;
|
|
161
|
+
|
|
162
|
+
/** Safe JSON parsing */
|
|
163
|
+
safeJsonParse: (json: string, defaultValue?: any) => any;
|
|
164
|
+
|
|
165
|
+
/** Date formatting */
|
|
166
|
+
formatDate: (date: string | Date) => string;
|
|
167
|
+
parseDate: (value: any) => Date | null;
|
|
168
|
+
|
|
169
|
+
/** String utilities */
|
|
170
|
+
toCamelCase: (str: string) => string;
|
|
171
|
+
toSnakeCase: (str: string) => string;
|
|
172
|
+
truncate: (str: string, maxLength: number) => string;
|
|
173
|
+
normalizeWhitespace: (str: string) => string;
|
|
174
|
+
fullName: (firstName?: string, lastName?: string) => string;
|
|
175
|
+
|
|
176
|
+
/** Validation */
|
|
177
|
+
isValidEmail: (email: string) => boolean;
|
|
178
|
+
requireField: (value: any, fieldName: string) => void;
|
|
179
|
+
isEmpty: (value: any) => boolean;
|
|
180
|
+
isUrl: (url: string) => boolean;
|
|
181
|
+
|
|
182
|
+
/** Object utilities */
|
|
183
|
+
deepClone: <T>(obj: T) => T;
|
|
184
|
+
deepMerge: (target: any, source: any) => any;
|
|
185
|
+
pick: <T extends object, K extends keyof T>(obj: T, keys: K[]) => Pick<T, K>;
|
|
186
|
+
omit: <T extends object, K extends keyof T>(obj: T, keys: K[]) => Omit<T, K>;
|
|
187
|
+
flatten: (obj: any, prefix?: string) => Record<string, any>;
|
|
188
|
+
|
|
189
|
+
/** Array utilities */
|
|
190
|
+
unique: <T>(arr: T[]) => T[];
|
|
191
|
+
groupBy: <T>(arr: T[], key: string) => Record<string, T[]>;
|
|
192
|
+
sortBy: <T>(arr: T[], key: string) => T[];
|
|
193
|
+
chunk: <T>(arr: T[], size: number) => T[][];
|
|
194
|
+
findBy: <T>(arr: T[], key: string, value: any) => T | undefined;
|
|
195
|
+
|
|
196
|
+
/** Async utilities */
|
|
197
|
+
retry: <T>(fn: () => Promise<T>, maxRetries?: number, delayMs?: number) => Promise<T>;
|
|
198
|
+
batchProcess: <T, R>(
|
|
199
|
+
items: T[],
|
|
200
|
+
processor: (item: T) => Promise<R>,
|
|
201
|
+
batchSize?: number
|
|
202
|
+
) => Promise<R[]>;
|
|
203
|
+
sleep: (ms: number) => Promise<void>;
|
|
204
|
+
timeout: <T>(promise: Promise<T>, ms: number) => Promise<T>;
|
|
205
|
+
|
|
206
|
+
/** Performance */
|
|
207
|
+
memoize: <T extends (...args: any[]) => any>(fn: T) => T;
|
|
208
|
+
hash: (data: string) => string;
|
|
209
|
+
uuid: () => string;
|
|
210
|
+
|
|
211
|
+
/** Context */
|
|
212
|
+
context: Record<string, any>;
|
|
213
|
+
|
|
214
|
+
/** Additional custom helpers */
|
|
215
|
+
[key: string]: any;
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### MapResult
|
|
220
|
+
|
|
221
|
+
Result from UniversalMapper.map():
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
interface MapResult {
|
|
225
|
+
/** Whether mapping succeeded */
|
|
226
|
+
success: boolean;
|
|
227
|
+
|
|
228
|
+
/** Mapped data */
|
|
229
|
+
data: Record<string, any>;
|
|
230
|
+
|
|
231
|
+
/** Error messages (if any) */
|
|
232
|
+
errors?: string[];
|
|
233
|
+
|
|
234
|
+
/** Warning messages (non-blocking) */
|
|
235
|
+
warnings?: string[];
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Configuration Format
|
|
242
|
+
|
|
243
|
+
**Field mapping format:**
|
|
244
|
+
|
|
245
|
+
```json
|
|
246
|
+
{
|
|
247
|
+
"fields": {
|
|
248
|
+
"productRef": {
|
|
249
|
+
"source": "sku",
|
|
250
|
+
"resolver": "sdk.uppercase"
|
|
251
|
+
},
|
|
252
|
+
"quantity": {
|
|
253
|
+
"source": "qty",
|
|
254
|
+
"resolver": "sdk.parseInt"
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## Field Configuration Edge Cases & Best Practices
|
|
263
|
+
|
|
264
|
+
### ⚠️ Important Field Property Combinations
|
|
265
|
+
|
|
266
|
+
Understanding how field properties interact is critical for correct mapping behavior.
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
#### ✅ Resolver-Only Fields with `required: true`
|
|
271
|
+
|
|
272
|
+
**New in SDK v0.1.37:** Resolver-only fields can now be required.
|
|
273
|
+
|
|
274
|
+
**Pattern:**
|
|
275
|
+
```json
|
|
276
|
+
{
|
|
277
|
+
"id": {
|
|
278
|
+
"resolver": "custom.generateUUID",
|
|
279
|
+
"required": true
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**When to use:**
|
|
285
|
+
- Generate unique IDs (UUID, composite keys)
|
|
286
|
+
- Look up values from external APIs
|
|
287
|
+
- Derive values from multiple source fields
|
|
288
|
+
- Apply complex business logic
|
|
289
|
+
|
|
290
|
+
**Validation behavior:**
|
|
291
|
+
- Required validation runs AFTER resolver execution
|
|
292
|
+
- Resolver must return a non-empty value
|
|
293
|
+
- If resolver returns null/undefined/'', mapping fails
|
|
294
|
+
|
|
295
|
+
**Example:**
|
|
296
|
+
```typescript
|
|
297
|
+
const mapper = new UniversalMapper(
|
|
298
|
+
{
|
|
299
|
+
fields: {
|
|
300
|
+
transactionId: {
|
|
301
|
+
resolver: 'custom.generateTransactionId',
|
|
302
|
+
required: true,
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
customResolvers: {
|
|
308
|
+
'custom.generateTransactionId': (value, sourceData, config, helpers) => {
|
|
309
|
+
return `TXN-${Date.now()}-${helpers.uuid()}`;
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
}
|
|
313
|
+
);
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
📖 **Learn more:** [Resolver-Only Fields Guide](../resolvers/mapping-resolvers-resolver-guide.md#resolver-only-fields--required-validation)
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
#### ⚠️ `required: true` + `defaultValue` (Contradictory)
|
|
321
|
+
|
|
322
|
+
**Status:** ⚠️ This combination is semantically contradictory for source fields.
|
|
323
|
+
|
|
324
|
+
**Problem:**
|
|
325
|
+
```json
|
|
326
|
+
{
|
|
327
|
+
"status": {
|
|
328
|
+
"source": "orderStatus",
|
|
329
|
+
"required": true,
|
|
330
|
+
"defaultValue": "PENDING" // ❌ Never used!
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
**Behavior:**
|
|
336
|
+
1. Extract from source → `undefined` (missing)
|
|
337
|
+
2. Validate required → ❌ Throws error immediately
|
|
338
|
+
3. Apply defaultValue → ❌ Never reached
|
|
339
|
+
|
|
340
|
+
**Why this doesn't work:**
|
|
341
|
+
- Required validation happens BEFORE defaultValue application for source fields
|
|
342
|
+
- DefaultValue is never applied because required fails first
|
|
343
|
+
|
|
344
|
+
**Important:** The order for `source + resolver + defaultValue + required` is:
|
|
345
|
+
1. Extract from source → `undefined`
|
|
346
|
+
2. Validate required → ❌ Fails immediately (defaultValue never applied)
|
|
347
|
+
3. Apply defaultValue → ❌ Never reached
|
|
348
|
+
4. Run resolver → ❌ Never reached
|
|
349
|
+
|
|
350
|
+
The code includes a warning when this combination is detected (see console output).
|
|
351
|
+
|
|
352
|
+
**Solution:**
|
|
353
|
+
```json
|
|
354
|
+
// ✅ CORRECT - Optional field with default
|
|
355
|
+
{
|
|
356
|
+
"status": {
|
|
357
|
+
"source": "orderStatus",
|
|
358
|
+
"required": false, // or omit required
|
|
359
|
+
"defaultValue": "PENDING"
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// ✅ CORRECT - Required field without default
|
|
364
|
+
{
|
|
365
|
+
"status": {
|
|
366
|
+
"source": "orderStatus",
|
|
367
|
+
"required": true
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
#### ✅ `defaultValue` + `resolver` (Works, but order matters)
|
|
375
|
+
|
|
376
|
+
**Pattern:**
|
|
377
|
+
```json
|
|
378
|
+
{
|
|
379
|
+
"status": {
|
|
380
|
+
"source": "orderStatus",
|
|
381
|
+
"defaultValue": "pending",
|
|
382
|
+
"resolver": "sdk.uppercase"
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
**Behavior:**
|
|
388
|
+
1. Extract from source → `undefined` (missing)
|
|
389
|
+
2. Apply defaultValue → `"pending"`
|
|
390
|
+
3. Apply resolver → `"PENDING"`
|
|
391
|
+
|
|
392
|
+
**Key insight:** Resolver receives the defaultValue, not the missing source value.
|
|
393
|
+
|
|
394
|
+
**Real-world example:**
|
|
395
|
+
```json
|
|
396
|
+
{
|
|
397
|
+
"attributes.condition": {
|
|
398
|
+
"source": "condition",
|
|
399
|
+
"defaultValue": "NEW",
|
|
400
|
+
"resolver": "sdk.uppercase",
|
|
401
|
+
"required": false
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
// Missing source → "NEW" → "NEW" (already uppercase)
|
|
405
|
+
// Empty source → "NEW" → "NEW"
|
|
406
|
+
// Source "used" → "USED" → "USED"
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
---
|
|
410
|
+
|
|
411
|
+
#### ✅ `required: true` + `value` (Redundant but harmless)
|
|
412
|
+
|
|
413
|
+
**Pattern:**
|
|
414
|
+
```json
|
|
415
|
+
{
|
|
416
|
+
"type": {
|
|
417
|
+
"value": "STANDARD",
|
|
418
|
+
"required": true // ⚠️ Redundant
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
**Behavior:**
|
|
424
|
+
- Static values are always present
|
|
425
|
+
- Required validation always passes
|
|
426
|
+
- `required: true` has no effect but doesn't hurt
|
|
427
|
+
|
|
428
|
+
**Recommendation:**
|
|
429
|
+
```json
|
|
430
|
+
// ✅ BETTER - Omit redundant required
|
|
431
|
+
{
|
|
432
|
+
"type": {
|
|
433
|
+
"value": "STANDARD"
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
---
|
|
439
|
+
|
|
440
|
+
#### ✅ Empty String Handling
|
|
441
|
+
|
|
442
|
+
**Behavior:** Empty string (`''`) is treated as "missing" for:
|
|
443
|
+
- Required validation
|
|
444
|
+
- DefaultValue application
|
|
445
|
+
|
|
446
|
+
**Example:**
|
|
447
|
+
```json
|
|
448
|
+
{
|
|
449
|
+
"status": {
|
|
450
|
+
"source": "orderStatus", // = ""
|
|
451
|
+
"defaultValue": "PENDING",
|
|
452
|
+
"required": false
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
// Result: "PENDING" (empty string triggers default)
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
**Validation:**
|
|
459
|
+
```typescript
|
|
460
|
+
// Empty string fails required validation
|
|
461
|
+
if (value === null || value === undefined || value === '')
|
|
462
|
+
|
|
463
|
+
// Empty string triggers defaultValue
|
|
464
|
+
if ((value === null || value === undefined || value === '') && config.defaultValue !== undefined)
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
---
|
|
468
|
+
|
|
469
|
+
### 📋 Field Property Combinations Summary
|
|
470
|
+
|
|
471
|
+
| Combination | Status | Behavior |
|
|
472
|
+
|-------------|--------|----------|
|
|
473
|
+
| `source` + `required: true` | ✅ Correct | Validates before resolver, fails if missing |
|
|
474
|
+
| `resolver` + `required: true` (no source) | ✅ Correct | Validates after resolver, fails if empty |
|
|
475
|
+
| `source` + `resolver` + `required: true` | ✅ Correct | Validates source, then applies resolver |
|
|
476
|
+
| `required: true` + `defaultValue` (source only) | ❌ Contradictory | Required fails before default applied |
|
|
477
|
+
| `source` + `resolver` + `defaultValue` + `required: true` | ❌ Contradictory | Required fails before default applied (same as above) |
|
|
478
|
+
| `defaultValue` + `resolver` | ✅ Works | Resolver receives default if source missing |
|
|
479
|
+
| `value` + `required: true` | ⚠️ Redundant | Works but required is unnecessary |
|
|
480
|
+
| `value` + `source` | ❌ Invalid | value takes precedence, source ignored |
|
|
481
|
+
| `value` + `resolver` | ✅ Works | Resolver transforms static value |
|
|
482
|
+
|
|
483
|
+
---
|
|
484
|
+
|
|
485
|
+
### 🎯 Best Practices
|
|
486
|
+
|
|
487
|
+
**1. ✅ DO: Use `required: true` for mandatory source fields**
|
|
488
|
+
```json
|
|
489
|
+
{ "source": "sku", "required": true }
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
**2. ✅ DO: Use `defaultValue` for optional fields**
|
|
493
|
+
```json
|
|
494
|
+
{ "source": "status", "defaultValue": "ACTIVE", "required": false }
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
**3. ✅ DO: Use resolver-only for generated values**
|
|
498
|
+
```json
|
|
499
|
+
{ "resolver": "custom.generateId", "required": true }
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
**4. ❌ DON'T: Combine `required: true` with `defaultValue`**
|
|
503
|
+
```json
|
|
504
|
+
// ❌ Contradictory - default will never be used
|
|
505
|
+
{ "source": "field", "required": true, "defaultValue": "FALLBACK" }
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
**5. ⚠️ CAUTION: `defaultValue` + `resolver` - default is passed to resolver**
|
|
509
|
+
```json
|
|
510
|
+
// Resolver receives "NEW" if source missing
|
|
511
|
+
{ "source": "condition", "defaultValue": "NEW", "resolver": "sdk.uppercase" }
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
---
|
|
515
|
+
|
|
516
|
+
### From Old Transformer System
|
|
517
|
+
|
|
518
|
+
**Old transformers → New resolvers:**
|
|
519
|
+
|
|
520
|
+
| Old Transformer | New Resolver |
|
|
521
|
+
| --------------- | ---------------- |
|
|
522
|
+
| `uppercase` | `sdk.uppercase` |
|
|
523
|
+
| `lowercase` | `sdk.lowercase` |
|
|
524
|
+
| `parseInt` | `sdk.parseInt` |
|
|
525
|
+
| `parseFloat` | `sdk.parseFloat` |
|
|
526
|
+
| `formatDate` | `sdk.formatDate` |
|
|
527
|
+
| `boolean` | `sdk.boolean` |
|
|
528
|
+
| `toJson` | `sdk.toJson` |
|
|
529
|
+
| `parseJson` | `sdk.parseJson` |
|
|
530
|
+
|
|
531
|
+
### Migration Steps
|
|
532
|
+
|
|
533
|
+
1. **Identify mapping format**:
|
|
534
|
+
- Check for `fieldMappings` array
|
|
535
|
+
- Check for `transformer` property
|
|
536
|
+
- Check for field names
|
|
537
|
+
|
|
538
|
+
2. **Convert to universal mapping format**:
|
|
539
|
+
- Replace `fieldMappings` with `fields` object
|
|
540
|
+
- Replace `sourceField` + `targetField` with nested structure
|
|
541
|
+
- Replace `transformer` with `resolver`
|
|
542
|
+
- Add `sdk.` prefix to resolver names
|
|
543
|
+
|
|
544
|
+
3. **Validate against schema**:
|
|
545
|
+
|
|
546
|
+
```bash
|
|
547
|
+
fc-connect-validate-schema --mapping config/new-mapping.json --schema schema.json
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
4. **Test with sample data**:
|
|
551
|
+
```typescript
|
|
552
|
+
const mapper = new UniversalMapper(newConfig);
|
|
553
|
+
const result = await mapper.map(sampleData);
|
|
554
|
+
console.log('Migration result:', result);
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
### Example Migration
|
|
558
|
+
|
|
559
|
+
**Before** (old format):
|
|
560
|
+
|
|
561
|
+
```json
|
|
562
|
+
{
|
|
563
|
+
"fieldMappings": [
|
|
564
|
+
{
|
|
565
|
+
"sourceField": "customer_email",
|
|
566
|
+
"targetField": "customer.email",
|
|
567
|
+
"transformer": "lowercase",
|
|
568
|
+
"required": true
|
|
569
|
+
},
|
|
570
|
+
{
|
|
571
|
+
"sourceField": "order_total",
|
|
572
|
+
"targetField": "totalPrice",
|
|
573
|
+
"transformer": "parseFloat"
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
"sourceField": "status",
|
|
577
|
+
"targetField": "status",
|
|
578
|
+
"transformer": "uppercase",
|
|
579
|
+
"defaultValue": "PENDING"
|
|
580
|
+
}
|
|
581
|
+
]
|
|
582
|
+
}
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
**After** (new format):
|
|
586
|
+
|
|
587
|
+
```json
|
|
588
|
+
{
|
|
589
|
+
"direction": "ingest",
|
|
590
|
+
"fields": {
|
|
591
|
+
"customer.email": {
|
|
592
|
+
"source": "customer_email",
|
|
593
|
+
"resolver": "sdk.lowercase",
|
|
594
|
+
"required": true
|
|
595
|
+
},
|
|
596
|
+
"totalPrice": {
|
|
597
|
+
"source": "order_total",
|
|
598
|
+
"resolver": "sdk.parseFloat"
|
|
599
|
+
},
|
|
600
|
+
"status": {
|
|
601
|
+
"source": "status",
|
|
602
|
+
"resolver": "sdk.uppercase",
|
|
603
|
+
"defaultValue": "PENDING"
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
---
|
|
610
|
+
|
|
611
|
+
## Performance Tips
|
|
612
|
+
|
|
613
|
+
### 1. Minimize Resolver Calls
|
|
614
|
+
|
|
615
|
+
```typescript
|
|
616
|
+
// ❌ BAD: Multiple resolvers for same transformation
|
|
617
|
+
{
|
|
618
|
+
"fields": {
|
|
619
|
+
"email": {
|
|
620
|
+
"source": "customer_email",
|
|
621
|
+
"resolver": "sdk.lowercase"
|
|
622
|
+
},
|
|
623
|
+
"emailNormalized": {
|
|
624
|
+
"source": "customer_email",
|
|
625
|
+
"resolver": "sdk.trim" // Processes same field again
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// ✅ GOOD: Single custom resolver
|
|
631
|
+
{
|
|
632
|
+
"fields": {
|
|
633
|
+
"email": {
|
|
634
|
+
"source": "customer_email",
|
|
635
|
+
"resolver": "custom.normalizeEmail" // Does lowercase + trim in one pass
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
### 2. Cache Expensive Lookups
|
|
642
|
+
|
|
643
|
+
```typescript
|
|
644
|
+
const customerCache = new Map();
|
|
645
|
+
|
|
646
|
+
const customResolvers = {
|
|
647
|
+
'custom.lookupCustomer': async (value, data, config, helpers) => {
|
|
648
|
+
if (customerCache.has(value)) {
|
|
649
|
+
return customerCache.get(value); // Cache hit - no API call
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
const result = await helpers.fluentClient.graphql({
|
|
653
|
+
/* query */
|
|
654
|
+
});
|
|
655
|
+
customerCache.set(value, result.id);
|
|
656
|
+
return result.id;
|
|
657
|
+
},
|
|
658
|
+
};
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
### 3. Use Memoization
|
|
662
|
+
|
|
663
|
+
```typescript
|
|
664
|
+
const expensiveCalculation = helpers.memoize((value) => {
|
|
665
|
+
// Complex calculation here
|
|
666
|
+
return result;
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
'custom.calculate': (value) => expensiveCalculation(value)
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
### 4. Batch Process Arrays
|
|
673
|
+
|
|
674
|
+
```typescript
|
|
675
|
+
// ❌ BAD: Sequential processing (slow for large arrays)
|
|
676
|
+
for (const item of items) {
|
|
677
|
+
await processItem(item);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// ✅ GOOD: Batch processing (parallel chunks)
|
|
681
|
+
await helpers.batchProcess(items, processItem, 100);
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
### 5. Avoid Deep Nesting
|
|
685
|
+
|
|
686
|
+
```typescript
|
|
687
|
+
// ❌ BAD: Deep nesting causes performance overhead
|
|
688
|
+
{
|
|
689
|
+
"fields": {
|
|
690
|
+
"customer": {
|
|
691
|
+
"fields": {
|
|
692
|
+
"address": {
|
|
693
|
+
"fields": {
|
|
694
|
+
"location": {
|
|
695
|
+
"fields": {
|
|
696
|
+
"city": { "source": "city" }
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// ✅ GOOD: Flat dot notation (faster)
|
|
707
|
+
{
|
|
708
|
+
"fields": {
|
|
709
|
+
"customer.address.location.city": {
|
|
710
|
+
"source": "city"
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
---
|
|
717
|
+
|
|
718
|
+
## FAQ
|
|
719
|
+
|
|
720
|
+
### Q: What's the difference between UniversalMapper and GraphQLMutationMapper?
|
|
721
|
+
|
|
722
|
+
**A**: Direction determines which to use:
|
|
723
|
+
|
|
724
|
+
| Mapper | Direction | Purpose | GraphQL Execution |
|
|
725
|
+
| ----------------------- | --------- | --------------------------------------- | ----------------- |
|
|
726
|
+
| `UniversalMapper` | `extract` | Fluent → External (CSV, Parquet, JSON) | ❌ No |
|
|
727
|
+
| `GraphQLMutationMapper` | `ingest` | External → Fluent (XML, JSON → GraphQL) | ✅ Yes |
|
|
728
|
+
|
|
729
|
+
Both use the **same mapping configuration format**.
|
|
730
|
+
|
|
731
|
+
---
|
|
732
|
+
|
|
733
|
+
### Q: Can I use the same mapping config for both ingestion and extraction?
|
|
734
|
+
|
|
735
|
+
**A**: No, because:
|
|
736
|
+
|
|
737
|
+
- **Ingestion**: Source = External data, Target = Fluent schema
|
|
738
|
+
- **Extraction**: Source = Fluent data, Target = External format
|
|
739
|
+
|
|
740
|
+
Field names and structures are different.
|
|
741
|
+
|
|
742
|
+
---
|
|
743
|
+
|
|
744
|
+
### Q: How do I map arrays?
|
|
745
|
+
|
|
746
|
+
**A**: Use `isArray: true` property:
|
|
747
|
+
|
|
748
|
+
```json
|
|
749
|
+
{
|
|
750
|
+
"fields": {
|
|
751
|
+
"items": {
|
|
752
|
+
"source": "line_items",
|
|
753
|
+
"isArray": true,
|
|
754
|
+
"fields": {
|
|
755
|
+
"ref": { "source": "$.id" },
|
|
756
|
+
"quantity": { "source": "$.qty" }
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
See [Module 5: Arrays](./mapping-05-advanced-patterns.md#arrays) for complete guide.
|
|
764
|
+
|
|
765
|
+
---
|
|
766
|
+
|
|
767
|
+
### Q: How do I handle nested/escaped XML?
|
|
768
|
+
|
|
769
|
+
**A**: Use `nodes` configuration:
|
|
770
|
+
|
|
771
|
+
```json
|
|
772
|
+
{
|
|
773
|
+
"nodes": {
|
|
774
|
+
"radial": {
|
|
775
|
+
"extract": "orders.order.custom-attribute[attribute-id=CreateOrderServiceRequest]",
|
|
776
|
+
"parse": "xml"
|
|
777
|
+
}
|
|
778
|
+
},
|
|
779
|
+
"fields": {
|
|
780
|
+
"ref": {
|
|
781
|
+
"source": "$radial.Order@customerOrderId"
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
See [Module 4: XML to GraphQL](./mapping-04-use-cases.md#use-case-2-xml-graphql-mutation) for complete example.
|
|
788
|
+
|
|
789
|
+
---
|
|
790
|
+
|
|
791
|
+
### Q: Can resolvers be async?
|
|
792
|
+
|
|
793
|
+
**A**: Yes! Custom resolvers can be async for API lookups:
|
|
794
|
+
|
|
795
|
+
```typescript
|
|
796
|
+
'custom.lookupCustomer': async (value, data, config, helpers) => {
|
|
797
|
+
const result = await helpers.fluentClient.graphql({ /* query */ });
|
|
798
|
+
return result.id;
|
|
799
|
+
}
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
You must `await` the mapping when using async resolvers:
|
|
803
|
+
|
|
804
|
+
```typescript
|
|
805
|
+
const result = await mapper.map(data);
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
---
|
|
809
|
+
|
|
810
|
+
### Q: How do I validate my mapping config?
|
|
811
|
+
|
|
812
|
+
**A**: Use the CLI validation tool:
|
|
813
|
+
|
|
814
|
+
```bash
|
|
815
|
+
fc-connect-validate-schema \
|
|
816
|
+
--mapping config/field-mappings.json \
|
|
817
|
+
--schema schema.json
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
See [Module 3: Schema Validation](../graphql-mutation-mapping/modules/graphql-mutation-mapping-03-schema-validation.md) for complete guide.
|
|
821
|
+
|
|
822
|
+
---
|
|
823
|
+
|
|
824
|
+
### Q: What's the performance of UniversalMapper?
|
|
825
|
+
|
|
826
|
+
**A**: Very fast for typical use cases:
|
|
827
|
+
|
|
828
|
+
- **Small records** (< 100 fields): < 1ms per record
|
|
829
|
+
- **Large records** (100-1000 fields): 1-5ms per record
|
|
830
|
+
- **Batch processing** (10K records): 10-50 seconds
|
|
831
|
+
|
|
832
|
+
Bottlenecks are usually:
|
|
833
|
+
|
|
834
|
+
- Async resolvers making API calls
|
|
835
|
+
- Complex nested transformations
|
|
836
|
+
- JSON.parse/stringify operations
|
|
837
|
+
|
|
838
|
+
---
|
|
839
|
+
|
|
840
|
+
### Q: Can I chain multiple resolvers?
|
|
841
|
+
|
|
842
|
+
**A**: Not directly, but you can:
|
|
843
|
+
|
|
844
|
+
1. **Use custom resolver** that calls SDK resolvers:
|
|
845
|
+
|
|
846
|
+
```typescript
|
|
847
|
+
'custom.normalize': (value, data, config, helpers) => {
|
|
848
|
+
let result = value;
|
|
849
|
+
if (typeof result === 'string') {
|
|
850
|
+
result = result.toLowerCase().trim();
|
|
851
|
+
}
|
|
852
|
+
return result;
|
|
853
|
+
}
|
|
854
|
+
```
|
|
855
|
+
|
|
856
|
+
2. **Use multiple field configs** (less efficient):
|
|
857
|
+
```json
|
|
858
|
+
{
|
|
859
|
+
"fields": {
|
|
860
|
+
"temp": { "source": "email", "resolver": "sdk.lowercase" },
|
|
861
|
+
"email": { "source": "temp", "resolver": "sdk.trim" }
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
```
|
|
865
|
+
|
|
866
|
+
---
|
|
867
|
+
|
|
868
|
+
### Q: How do I debug mapping issues?
|
|
869
|
+
|
|
870
|
+
**A**: Enable debug logging:
|
|
871
|
+
|
|
872
|
+
```typescript
|
|
873
|
+
const mapper = new UniversalMapper(config);
|
|
874
|
+
|
|
875
|
+
const result = await mapper.map(data);
|
|
876
|
+
|
|
877
|
+
console.log('Mapping result:', JSON.stringify(result, null, 2));
|
|
878
|
+
|
|
879
|
+
if (!result.success) {
|
|
880
|
+
console.error('Errors:', result.errors);
|
|
881
|
+
}
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
See [Troubleshooting](#troubleshooting) section below.
|
|
885
|
+
|
|
886
|
+
---
|
|
887
|
+
|
|
888
|
+
## Troubleshooting
|
|
889
|
+
|
|
890
|
+
### Issue: "Missing required field"
|
|
891
|
+
|
|
892
|
+
**Cause**: Required field is null/undefined in source data or resolver returned empty value.
|
|
893
|
+
|
|
894
|
+
**Solution 1: Provide defaultValue**
|
|
895
|
+
|
|
896
|
+
```json
|
|
897
|
+
{
|
|
898
|
+
"fields": {
|
|
899
|
+
"productRef": {
|
|
900
|
+
"source": "sku",
|
|
901
|
+
"required": true,
|
|
902
|
+
"defaultValue": "UNKNOWN" // Provide fallback
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
```
|
|
907
|
+
|
|
908
|
+
**Solution 2: Make field optional**
|
|
909
|
+
|
|
910
|
+
```json
|
|
911
|
+
{
|
|
912
|
+
"fields": {
|
|
913
|
+
"productRef": {
|
|
914
|
+
"source": "sku"
|
|
915
|
+
// Remove "required: true"
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
```
|
|
920
|
+
|
|
921
|
+
**Solution 3: Fix resolver that returns empty value**
|
|
922
|
+
|
|
923
|
+
If you have a resolver-only field that's required but returning empty:
|
|
924
|
+
|
|
925
|
+
```json
|
|
926
|
+
{
|
|
927
|
+
"fields": {
|
|
928
|
+
"customerId": {
|
|
929
|
+
"resolver": "custom.lookupCustomer",
|
|
930
|
+
"required": true
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
```
|
|
935
|
+
|
|
936
|
+
**Problem:** Resolver returns `undefined` when customer not found:
|
|
937
|
+
|
|
938
|
+
```typescript
|
|
939
|
+
'custom.lookupCustomer': async (value, data, config, helpers) => {
|
|
940
|
+
const email = helpers.get(data, 'customer.email');
|
|
941
|
+
const customer = await lookupCustomerByEmail(email);
|
|
942
|
+
return customer?.id; // ❌ Returns undefined if customer not found
|
|
943
|
+
}
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
**Fix:** Ensure resolver always returns a value or throw error:
|
|
947
|
+
|
|
948
|
+
```typescript
|
|
949
|
+
'custom.lookupCustomer': async (value, data, config, helpers) => {
|
|
950
|
+
const email = helpers.get(data, 'customer.email');
|
|
951
|
+
if (!email) {
|
|
952
|
+
throw new Error('Email required for customer lookup');
|
|
953
|
+
}
|
|
954
|
+
const customer = await lookupCustomerByEmail(email);
|
|
955
|
+
if (!customer) {
|
|
956
|
+
throw new Error(`Customer not found for email: ${email}`);
|
|
957
|
+
}
|
|
958
|
+
return customer.id; // ✅ Always returns a value or throws
|
|
959
|
+
}
|
|
960
|
+
```
|
|
961
|
+
|
|
962
|
+
**Solution 4: Use defaultValue with resolver (but ensure resolver always returns value)**
|
|
963
|
+
|
|
964
|
+
```json
|
|
965
|
+
{
|
|
966
|
+
"fields": {
|
|
967
|
+
"discount": {
|
|
968
|
+
"source": "order.subtotal",
|
|
969
|
+
"resolver": "custom.calculateDiscount",
|
|
970
|
+
"required": true,
|
|
971
|
+
"defaultValue": 0 // Applied before resolver, but resolver must return non-empty
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
```
|
|
976
|
+
|
|
977
|
+
**Important:** If `custom.calculateDiscount` returns `null`, `undefined`, or `''`, the mapping will still fail because required validation happens after resolver execution. The defaultValue only helps if the source is missing (it's applied before the resolver runs).
|
|
978
|
+
|
|
979
|
+
---
|
|
980
|
+
|
|
981
|
+
### Issue: "Resolver not found"
|
|
982
|
+
|
|
983
|
+
**Cause**: Resolver name is misspelled or not registered.
|
|
984
|
+
|
|
985
|
+
**Solution**:
|
|
986
|
+
|
|
987
|
+
- Check resolver name: `sdk.uppercase` (NOT `uppercase`)
|
|
988
|
+
- Register custom resolver:
|
|
989
|
+
```typescript
|
|
990
|
+
const mapper = new UniversalMapper(config, {
|
|
991
|
+
customResolvers: {
|
|
992
|
+
'custom.myResolver': value => transformValue(value),
|
|
993
|
+
},
|
|
994
|
+
});
|
|
995
|
+
```
|
|
996
|
+
|
|
997
|
+
---
|
|
998
|
+
|
|
999
|
+
### Issue: "Cannot read property of undefined"
|
|
1000
|
+
|
|
1001
|
+
**Cause**: Path doesn't exist in source data.
|
|
1002
|
+
|
|
1003
|
+
**Solution**: Use `defaultValue` or make path optional:
|
|
1004
|
+
|
|
1005
|
+
```json
|
|
1006
|
+
{
|
|
1007
|
+
"fields": {
|
|
1008
|
+
"city": {
|
|
1009
|
+
"source": "customer.address.city",
|
|
1010
|
+
"defaultValue": "Unknown"
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
```
|
|
1015
|
+
|
|
1016
|
+
Or use `helpers.get` in custom resolver:
|
|
1017
|
+
|
|
1018
|
+
```typescript
|
|
1019
|
+
'custom.getCity': (value, data, config, helpers) => {
|
|
1020
|
+
return helpers.get(data, 'customer.address.city', 'Unknown');
|
|
1021
|
+
}
|
|
1022
|
+
```
|
|
1023
|
+
|
|
1024
|
+
---
|
|
1025
|
+
|
|
1026
|
+
### Issue: "Invalid JSON"
|
|
1027
|
+
|
|
1028
|
+
**Cause**: Using `sdk.parseJson` on non-JSON string.
|
|
1029
|
+
|
|
1030
|
+
**Solution**: `sdk.parseJson` is null-safe and returns original value if parse fails:
|
|
1031
|
+
|
|
1032
|
+
```typescript
|
|
1033
|
+
// This won't throw - it returns "not json" unchanged
|
|
1034
|
+
{ "resolver": "sdk.parseJson" }
|
|
1035
|
+
```
|
|
1036
|
+
|
|
1037
|
+
If you need strict validation, use custom resolver:
|
|
1038
|
+
|
|
1039
|
+
```typescript
|
|
1040
|
+
'custom.strictParseJson': (value, data, config, helpers) => {
|
|
1041
|
+
const result = helpers.safeJsonParse(value, null);
|
|
1042
|
+
if (result === null) {
|
|
1043
|
+
throw new Error(`Invalid JSON: ${value}`);
|
|
1044
|
+
}
|
|
1045
|
+
return result;
|
|
1046
|
+
}
|
|
1047
|
+
```
|
|
1048
|
+
|
|
1049
|
+
---
|
|
1050
|
+
|
|
1051
|
+
### Issue: "Array expected but got object"
|
|
1052
|
+
|
|
1053
|
+
**Cause**: XML parser doesn't create arrays for single items.
|
|
1054
|
+
|
|
1055
|
+
**Solution**: Use `helpers.ensureArray`:
|
|
1056
|
+
|
|
1057
|
+
```typescript
|
|
1058
|
+
'custom.processItems': (value, data, config, helpers) => {
|
|
1059
|
+
const items = helpers.ensureArray(data.items);
|
|
1060
|
+
// Now always an array, even if single item
|
|
1061
|
+
}
|
|
1062
|
+
```
|
|
1063
|
+
|
|
1064
|
+
---
|
|
1065
|
+
|
|
1066
|
+
### Issue: "NaN in Fluent API response"
|
|
1067
|
+
|
|
1068
|
+
**Cause**: Number resolver returned `NaN` instead of valid number.
|
|
1069
|
+
|
|
1070
|
+
**Solution**: SDK resolvers (`sdk.parseInt`, `sdk.parseFloat`) return `0` by default (NOT `NaN`):
|
|
1071
|
+
|
|
1072
|
+
```json
|
|
1073
|
+
{
|
|
1074
|
+
"fields": {
|
|
1075
|
+
"quantity": {
|
|
1076
|
+
"source": "qty",
|
|
1077
|
+
"resolver": "sdk.parseInt" // Returns 0 for invalid, NOT NaN
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
```
|
|
1082
|
+
|
|
1083
|
+
If using custom resolver, ensure safe parsing:
|
|
1084
|
+
|
|
1085
|
+
```typescript
|
|
1086
|
+
'custom.parseQty': (value, data, config, helpers) => {
|
|
1087
|
+
return helpers.parseIntSafe(value, 0); // Safe default
|
|
1088
|
+
}
|
|
1089
|
+
```
|
|
1090
|
+
|
|
1091
|
+
---
|
|
1092
|
+
|
|
1093
|
+
### Issue: "Circular reference in JSON"
|
|
1094
|
+
|
|
1095
|
+
**Cause**: `sdk.toJson` on object with circular references.
|
|
1096
|
+
|
|
1097
|
+
**Solution**: `sdk.toJson` is null-safe and returns original value if stringify fails:
|
|
1098
|
+
|
|
1099
|
+
```typescript
|
|
1100
|
+
// Won't throw on circular references
|
|
1101
|
+
{ "resolver": "sdk.toJson" }
|
|
1102
|
+
```
|
|
1103
|
+
|
|
1104
|
+
If you need custom handling:
|
|
1105
|
+
|
|
1106
|
+
```typescript
|
|
1107
|
+
'custom.safeStringify': (value) => {
|
|
1108
|
+
try {
|
|
1109
|
+
return JSON.stringify(value, (key, val) => {
|
|
1110
|
+
// Custom replacer logic
|
|
1111
|
+
return val;
|
|
1112
|
+
});
|
|
1113
|
+
} catch {
|
|
1114
|
+
return String(value);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
```
|
|
1118
|
+
|
|
1119
|
+
---
|
|
1120
|
+
|
|
1121
|
+
## Best Practices
|
|
1122
|
+
|
|
1123
|
+
### 1. Always Validate Against Schema
|
|
1124
|
+
|
|
1125
|
+
Before deployment:
|
|
1126
|
+
|
|
1127
|
+
```bash
|
|
1128
|
+
fc-connect-validate-schema --mapping config/mappings.json --schema schema.json
|
|
1129
|
+
```
|
|
1130
|
+
|
|
1131
|
+
---
|
|
1132
|
+
|
|
1133
|
+
### 2. Use SDK Resolvers First
|
|
1134
|
+
|
|
1135
|
+
Don't write custom resolvers for common transformations:
|
|
1136
|
+
|
|
1137
|
+
```typescript
|
|
1138
|
+
// ❌ NOT NEEDED
|
|
1139
|
+
'custom.uppercase': (value) => value?.toUpperCase()
|
|
1140
|
+
|
|
1141
|
+
// ✅ USE SDK
|
|
1142
|
+
{ "resolver": "sdk.uppercase" }
|
|
1143
|
+
```
|
|
1144
|
+
|
|
1145
|
+
---
|
|
1146
|
+
|
|
1147
|
+
### 3. Provide Default Values for Required Fields
|
|
1148
|
+
|
|
1149
|
+
```json
|
|
1150
|
+
{
|
|
1151
|
+
"fields": {
|
|
1152
|
+
"status": {
|
|
1153
|
+
"source": "order_status",
|
|
1154
|
+
"required": true,
|
|
1155
|
+
"defaultValue": "PENDING"
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
```
|
|
1160
|
+
|
|
1161
|
+
---
|
|
1162
|
+
|
|
1163
|
+
### 4. Use Comments for Complex Mappings
|
|
1164
|
+
|
|
1165
|
+
```json
|
|
1166
|
+
{
|
|
1167
|
+
"fields": {
|
|
1168
|
+
"totalPrice": {
|
|
1169
|
+
"resolver": "custom.calculateTotal",
|
|
1170
|
+
"comment": "Sum of (price * qty) + tax + shipping for all items"
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
```
|
|
1175
|
+
|
|
1176
|
+
---
|
|
1177
|
+
|
|
1178
|
+
### 5. Keep Resolvers Pure and Testable
|
|
1179
|
+
|
|
1180
|
+
```typescript
|
|
1181
|
+
// ✅ GOOD: Pure function, easy to test
|
|
1182
|
+
const customResolvers = {
|
|
1183
|
+
'custom.buildRef': (value, data) => `${data.sku}-${data.location}`,
|
|
1184
|
+
};
|
|
1185
|
+
|
|
1186
|
+
// Test
|
|
1187
|
+
const result = customResolvers`'custom.buildRef'`;
|
|
1188
|
+
console.assert(result === 'SKU001-WH01');
|
|
1189
|
+
```
|
|
1190
|
+
|
|
1191
|
+
---
|
|
1192
|
+
|
|
1193
|
+
### 6. Log Errors and Warnings
|
|
1194
|
+
|
|
1195
|
+
```typescript
|
|
1196
|
+
'custom.lookup': async (value, data, config, helpers) => {
|
|
1197
|
+
try {
|
|
1198
|
+
return await apiCall(value);
|
|
1199
|
+
} catch (error) {
|
|
1200
|
+
helpers.logger?.error('Lookup failed', { value, error });
|
|
1201
|
+
return null; // Graceful degradation
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
```
|
|
1205
|
+
|
|
1206
|
+
---
|
|
1207
|
+
|
|
1208
|
+
### 7. Use Nested Objects for Clarity
|
|
1209
|
+
|
|
1210
|
+
```json
|
|
1211
|
+
{
|
|
1212
|
+
"fields": {
|
|
1213
|
+
"customer": {
|
|
1214
|
+
"fields": {
|
|
1215
|
+
"id": { "source": "customer_id" },
|
|
1216
|
+
"email": { "source": "customer_email" }
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
```
|
|
1222
|
+
|
|
1223
|
+
More readable than:
|
|
1224
|
+
|
|
1225
|
+
```json
|
|
1226
|
+
{
|
|
1227
|
+
"fields": {
|
|
1228
|
+
"customer.id": { "source": "customer_id" },
|
|
1229
|
+
"customer.email": { "source": "customer_email" }
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
```
|
|
1233
|
+
|
|
1234
|
+
---
|
|
1235
|
+
|
|
1236
|
+
## Cheat Sheet
|
|
1237
|
+
|
|
1238
|
+
### Quick Mapping Patterns
|
|
1239
|
+
|
|
1240
|
+
```json
|
|
1241
|
+
// Static value
|
|
1242
|
+
{ "value": "ACTIVE" }
|
|
1243
|
+
|
|
1244
|
+
// Simple mapping
|
|
1245
|
+
{ "source": "sku" }
|
|
1246
|
+
|
|
1247
|
+
// With transformation
|
|
1248
|
+
{ "source": "email", "resolver": "sdk.lowercase" }
|
|
1249
|
+
|
|
1250
|
+
// With default
|
|
1251
|
+
{ "source": "status", "defaultValue": "PENDING" }
|
|
1252
|
+
|
|
1253
|
+
// Required field
|
|
1254
|
+
{ "source": "productRef", "required": true }
|
|
1255
|
+
|
|
1256
|
+
// Custom resolver
|
|
1257
|
+
{ "source": "data", "resolver": "custom.transform" }
|
|
1258
|
+
|
|
1259
|
+
// Nested object (dot notation)
|
|
1260
|
+
{ "customer.email": { "source": "email" } }
|
|
1261
|
+
|
|
1262
|
+
// Nested object (fields)
|
|
1263
|
+
{
|
|
1264
|
+
"customer": {
|
|
1265
|
+
"fields": {
|
|
1266
|
+
"email": { "source": "email" }
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
// Array
|
|
1272
|
+
{
|
|
1273
|
+
"items": {
|
|
1274
|
+
"source": "line_items",
|
|
1275
|
+
"isArray": true,
|
|
1276
|
+
"fields": {
|
|
1277
|
+
"ref": { "source": "$.id" }
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
```
|
|
1282
|
+
|
|
1283
|
+
### Common SDK Resolvers
|
|
1284
|
+
|
|
1285
|
+
```json
|
|
1286
|
+
"sdk.uppercase" // String to UPPER
|
|
1287
|
+
"sdk.lowercase" // String to lower
|
|
1288
|
+
"sdk.trim" // Remove whitespace
|
|
1289
|
+
"sdk.parseInt" // String → int
|
|
1290
|
+
"sdk.parseFloat" // String → float
|
|
1291
|
+
"sdk.formatDate" // Date → ISO8601
|
|
1292
|
+
"sdk.boolean" // "true"/"1"/"yes" → true
|
|
1293
|
+
"sdk.parseJson" // JSON string → object
|
|
1294
|
+
"sdk.toJson" // Object → JSON string
|
|
1295
|
+
```
|
|
1296
|
+
|
|
1297
|
+
---
|
|
1298
|
+
|
|
1299
|
+
## Next Steps
|
|
1300
|
+
|
|
1301
|
+
You've completed the Universal Mapping Guide! Next steps:
|
|
1302
|
+
|
|
1303
|
+
- **[GraphQL Mutation Mapping Quick Reference](../graphql-mutation-mapping/graphql-mutation-mapping-quick-reference.md)** - Comprehensive cheat sheet
|
|
1304
|
+
- **[Examples](../examples/)** - Complete working examples
|
|
1305
|
+
- **[Use Cases](../../../01-TEMPLATES/readme.md)** - Real-world implementations
|
|
1306
|
+
|
|
1307
|
+
---
|
|
1308
|
+
|
|
1309
|
+
## Additional Resources
|
|
1310
|
+
|
|
1311
|
+
- [UniversalMapper Source Code](../../../../src/services/mapping/universal-mapper.ts)
|
|
1312
|
+
- [GraphQLMutationMapper Source](../../../../src/services/mapping/graphql-mutation-mapper.ts)
|
|
1313
|
+
- [SDK Resolvers Source](../../../../src/services/resolvers/sdk-resolvers.ts)
|
|
1314
|
+
- [Fluent Commerce API Documentation](https://docs.fluentcommerce.com/)
|
|
1315
|
+
|
|
1316
|
+
---
|
|
1317
|
+
|
|
1318
|
+
[← Back to Index](../mapping-readme.md) | [Previous: Helpers & Resolvers](./mapping-06-helpers-resolvers.md)
|
|
1319
|
+
|