@fluentcommerce/fc-connect-sdk 0.1.53 → 0.1.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -2
- package/README.md +39 -0
- package/dist/cjs/auth/index.d.ts +3 -0
- package/dist/cjs/auth/index.js +13 -0
- package/dist/cjs/auth/profile-loader.d.ts +18 -0
- package/dist/cjs/auth/profile-loader.js +208 -0
- package/dist/cjs/client-factory.d.ts +4 -0
- package/dist/cjs/client-factory.js +10 -0
- package/dist/cjs/clients/fluent-client.js +13 -6
- package/dist/cjs/index.d.ts +3 -1
- package/dist/cjs/index.js +8 -2
- package/dist/cjs/utils/pagination-helpers.js +38 -2
- package/dist/cjs/versori/fluent-versori-client.js +11 -5
- package/dist/esm/auth/index.d.ts +3 -0
- package/dist/esm/auth/index.js +2 -0
- package/dist/esm/auth/profile-loader.d.ts +18 -0
- package/dist/esm/auth/profile-loader.js +169 -0
- package/dist/esm/client-factory.d.ts +4 -0
- package/dist/esm/client-factory.js +9 -0
- package/dist/esm/clients/fluent-client.js +13 -6
- package/dist/esm/index.d.ts +3 -1
- package/dist/esm/index.js +2 -1
- package/dist/esm/utils/pagination-helpers.js +38 -2
- package/dist/esm/versori/fluent-versori-client.js +11 -5
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/tsconfig.types.tsbuildinfo +1 -1
- package/dist/types/auth/index.d.ts +3 -0
- package/dist/types/auth/profile-loader.d.ts +18 -0
- package/dist/types/client-factory.d.ts +4 -0
- package/dist/types/index.d.ts +3 -1
- package/docs/00-START-HERE/EXPORT-VALIDATION.md +158 -158
- package/docs/00-START-HERE/cli-analyze-source-structure-guide.md +655 -655
- package/docs/00-START-HERE/cli-documentation-index.md +202 -202
- package/docs/00-START-HERE/cli-quick-reference.md +252 -252
- package/docs/00-START-HERE/decision-tree.md +552 -552
- package/docs/00-START-HERE/getting-started.md +1070 -1070
- package/docs/00-START-HERE/mapper-quick-decision-guide.md +235 -235
- package/docs/00-START-HERE/readme.md +237 -237
- package/docs/00-START-HERE/retailerid-configuration.md +404 -404
- package/docs/00-START-HERE/sdk-philosophy.md +794 -794
- package/docs/00-START-HERE/troubleshooting-quick-reference.md +1086 -1086
- package/docs/01-TEMPLATES/faq.md +686 -686
- package/docs/01-TEMPLATES/patterns/pattern-templates-guide.md +68 -68
- package/docs/01-TEMPLATES/patterns/patterns-csv-schema-validation-and-rejection-report.md +233 -233
- package/docs/01-TEMPLATES/patterns/patterns-custom-resolvers.md +407 -407
- package/docs/01-TEMPLATES/patterns/patterns-error-handling-retry.md +511 -511
- package/docs/01-TEMPLATES/patterns/patterns-field-mapping-universal.md +701 -701
- package/docs/01-TEMPLATES/patterns/patterns-large-file-splitting.md +1430 -1430
- package/docs/01-TEMPLATES/patterns/patterns-master-data-etl.md +2399 -2399
- package/docs/01-TEMPLATES/patterns/patterns-pagination-streaming.md +447 -447
- package/docs/01-TEMPLATES/patterns/patterns-state-duplicate-prevention.md +385 -385
- package/docs/01-TEMPLATES/readme.md +957 -957
- package/docs/01-TEMPLATES/standalone/standalone-asn-inbound-processing.md +1209 -1209
- package/docs/01-TEMPLATES/standalone/standalone-graphql-query-export.md +1140 -1140
- package/docs/01-TEMPLATES/standalone/standalone-graphql-to-parquet-partitioned-s3.md +432 -432
- package/docs/01-TEMPLATES/standalone/standalone-multi-channel-inventory-sync.md +1185 -1185
- package/docs/01-TEMPLATES/standalone/standalone-multi-source-aggregation.md +1462 -1462
- package/docs/01-TEMPLATES/standalone/standalone-s3-csv-batch-api.md +1390 -1390
- package/docs/01-TEMPLATES/standalone/standalone-s3-csv-inventory-to-batch.md +330 -330
- package/docs/01-TEMPLATES/standalone/standalone-scripts-guide.md +87 -87
- package/docs/01-TEMPLATES/standalone/standalone-sftp-xml-graphql.md +1444 -1444
- package/docs/01-TEMPLATES/standalone/standalone-webhook-payload-processing.md +688 -688
- package/docs/01-TEMPLATES/versori/business-examples/business-examples-dropship-order-routing.md +193 -193
- package/docs/01-TEMPLATES/versori/business-examples/business-examples-graphql-parquet-extraction.md +518 -518
- package/docs/01-TEMPLATES/versori/business-examples/business-examples-inter-location-transfers.md +2162 -2162
- package/docs/01-TEMPLATES/versori/business-examples/business-examples-pre-order-allocation.md +2226 -2226
- package/docs/01-TEMPLATES/versori/business-examples/business-scenarios-guide.md +87 -87
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-connection-validation-pattern.md +656 -656
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-dual-workflow-connector.md +835 -835
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-guide.md +108 -108
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-kv-state-management.md +1533 -1533
- package/docs/01-TEMPLATES/versori/patterns/versori-patterns-xml-response-patterns.md +1160 -1160
- package/docs/01-TEMPLATES/versori/versori-platform-guide.md +201 -201
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-asn-purchase-order.md +1906 -1906
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-dropship-routing.md +1074 -1074
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-flash-sale-reserve.md +1395 -1395
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-generic-xml-order.md +888 -888
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-payment-gateway-integration.md +2478 -2478
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-rma-returns-comprehensive.md +2240 -2240
- package/docs/01-TEMPLATES/versori/webhooks/template-webhook-xml-order-ingestion.md +2029 -2029
- package/docs/01-TEMPLATES/versori/webhooks/webhook-templates-guide.md +140 -140
- package/docs/01-TEMPLATES/versori/workflows/_examples/sample-data/inventory-mapping.json +20 -20
- package/docs/01-TEMPLATES/versori/workflows/_examples/sample-data/products_2025-01-22.csv +11 -11
- package/docs/01-TEMPLATES/versori/workflows/_examples/sample-data/sample-data-guide.md +34 -34
- package/docs/01-TEMPLATES/versori/workflows/_examples/workflow-examples-guide.md +36 -36
- package/docs/01-TEMPLATES/versori/workflows/extraction/extraction-modes-guide.md +1038 -1038
- package/docs/01-TEMPLATES/versori/workflows/extraction/extraction-workflows-guide.md +138 -138
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/graphql-extraction-guide.md +63 -63
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-fulfillments-to-sftp-csv.md +2062 -2062
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-fulfillments-to-sftp-xml.md +2294 -2294
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-positions-to-s3-csv.md +2461 -2461
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-positions-to-sftp-xml.md +2529 -2529
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-quantities-to-s3-csv.md +2464 -2464
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-inventory-quantities-to-s3-json.md +1959 -1959
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-orders-to-s3-csv.md +1953 -1953
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-orders-to-sftp-xml.md +2541 -2541
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-products-to-s3-json.md +2384 -2384
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-products-to-sftp-xml.md +2445 -2445
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-virtual-positions-to-s3-csv.md +2355 -2355
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-virtual-positions-to-s3-json.md +2042 -2042
- package/docs/01-TEMPLATES/versori/workflows/extraction/graphql-queries/template-extraction-virtual-positions-to-sftp-xml.md +2726 -2726
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/batch-api-guide.md +206 -206
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-cycle-count-reconciliation.md +2030 -2030
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-multi-channel-inventory-sync.md +1882 -1882
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-s3-csv-inventory-batch.md +2827 -2827
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-s3-json-inventory-batch.md +1952 -1952
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-s3-xml-inventory-batch.md +3289 -3289
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-sftp-csv-inventory-batch.md +3064 -3064
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-sftp-json-inventory-batch.md +3238 -3238
- package/docs/01-TEMPLATES/versori/workflows/ingestion/batch-api/template-ingestion-sftp-xml-inventory-batch.md +2977 -2977
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/event-api-guide.md +321 -321
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-payload-json-order-cancel-event.md +959 -959
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-payload-xml-order-cancel-event.md +1170 -1170
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-csv-product-event.md +2312 -2312
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-json-product-event.md +2999 -2999
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-parquet-product-event.md +2836 -2836
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-s3-xml-product-event.md +2395 -2395
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-csv-product-event.md +2295 -2295
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-json-product-event.md +2602 -2602
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-parquet-product-event.md +2589 -2589
- package/docs/01-TEMPLATES/versori/workflows/ingestion/event-api/template-ingestion-sftp-xml-product-event.md +3578 -3578
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/graphql-mutations-guide.md +93 -93
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-payload-json-order-update-graphql.md +1260 -1260
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-payload-xml-order-update-graphql.md +1472 -1472
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-csv-control-graphql.md +2417 -2417
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-csv-location-graphql.md +2811 -2811
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-csv-price-graphql.md +2619 -2619
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-json-location-graphql.md +2807 -2807
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-s3-xml-location-graphql.md +2373 -2373
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-sftp-csv-control-graphql.md +2740 -2740
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-sftp-csv-location-graphql.md +2760 -2760
- package/docs/01-TEMPLATES/versori/workflows/ingestion/graphql-mutations/template-ingestion-sftp-json-location-graphql.md +1710 -1710
- package/docs/01-TEMPLATES/versori/workflows/ingestion/ingestion-workflows-guide.md +136 -136
- package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/rubix-webhooks-guide.md +520 -520
- package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/template-webhook-rubix-fulfilment-to-sftp-xml-inline.md +1418 -1418
- package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/template-webhook-rubix-fulfilment-to-sftp-xml-universal-mapper.md +1785 -1785
- package/docs/01-TEMPLATES/versori/workflows/rubix-webhooks/template-webhook-rubix-order-attribute-update.md +824 -824
- package/docs/01-TEMPLATES/versori/workflows/workflows-overview-guide.md +646 -646
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-batch-archival.md +724 -724
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-job-tracker.md +627 -627
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-partial-batch-recovery.md +561 -561
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-quick-reference.md +367 -367
- package/docs/02-CORE-GUIDES/advanced-services/advanced-services-readme.md +407 -407
- package/docs/02-CORE-GUIDES/advanced-services/readme.md +49 -49
- package/docs/02-CORE-GUIDES/api-reference/api-reference-quick-reference.md +548 -548
- package/docs/02-CORE-GUIDES/api-reference/event-api-input-output-reference.md +702 -1171
- package/docs/02-CORE-GUIDES/api-reference/examples/client-initialization.ts +286 -286
- package/docs/02-CORE-GUIDES/api-reference/graphql-error-classification.md +337 -337
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-01-client-api.md +399 -482
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-03-authentication.md +199 -199
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-04-graphql-mapping.md +925 -925
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-05-services.md +1198 -1198
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-06-data-sources.md +1083 -1083
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-07-parsers.md +1097 -1097
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-08-pagination.md +513 -513
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-08-types.md +545 -597
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-09-error-handling.md +527 -527
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-09-webhook-validation.md +514 -514
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-10-extraction.md +557 -557
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-10-utilities.md +412 -412
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-11-cli-tools.md +423 -423
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-11-error-handling.md +716 -716
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-analyze-source-structure.md +518 -518
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-partial-responses.md +212 -212
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-testing.md +300 -300
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-13-resolver-builder.md +322 -322
- package/docs/02-CORE-GUIDES/api-reference/readme.md +279 -279
- package/docs/02-CORE-GUIDES/auto-pagination/auto-pagination-quick-reference.md +351 -351
- package/docs/02-CORE-GUIDES/auto-pagination/auto-pagination-readme.md +277 -277
- package/docs/02-CORE-GUIDES/auto-pagination/examples/auto-pagination-readme.md +178 -178
- package/docs/02-CORE-GUIDES/auto-pagination/examples/common-patterns.ts +351 -351
- package/docs/02-CORE-GUIDES/auto-pagination/examples/paginate-products.ts +384 -384
- package/docs/02-CORE-GUIDES/auto-pagination/examples/paginate-virtual-positions.ts +308 -308
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-01-foundations.md +470 -470
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-02-quick-start.md +713 -713
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-03-configuration.md +754 -754
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-04-advanced-patterns.md +732 -732
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-05-sdk-integration.md +847 -847
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-06-troubleshooting.md +359 -359
- package/docs/02-CORE-GUIDES/auto-pagination/modules/auto-pagination-07-api-reference.md +462 -462
- package/docs/02-CORE-GUIDES/auto-pagination/readme.md +54 -54
- package/docs/02-CORE-GUIDES/data-sources/data-sources-file-operations-error-handling.md +1487 -1487
- package/docs/02-CORE-GUIDES/data-sources/data-sources-quick-reference.md +836 -836
- package/docs/02-CORE-GUIDES/data-sources/data-sources-readme.md +276 -276
- package/docs/02-CORE-GUIDES/data-sources/data-sources-sftp-credential-access-security.md +553 -553
- package/docs/02-CORE-GUIDES/data-sources/examples/common-patterns.ts +409 -409
- package/docs/02-CORE-GUIDES/data-sources/examples/data-sources-readme.md +178 -178
- package/docs/02-CORE-GUIDES/data-sources/examples/s3-operations.ts +308 -308
- package/docs/02-CORE-GUIDES/data-sources/examples/sftp-operations.ts +371 -371
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-01-foundations.md +735 -735
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-02-s3-operations.md +1302 -1302
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-03-sftp-operations.md +1379 -1379
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-04-file-patterns.md +941 -941
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-05-advanced-topics.md +813 -813
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-06-integration-patterns.md +486 -486
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-07-troubleshooting.md +387 -387
- package/docs/02-CORE-GUIDES/data-sources/modules/data-sources-08-api-reference.md +417 -417
- package/docs/02-CORE-GUIDES/data-sources/readme.md +77 -77
- package/docs/02-CORE-GUIDES/error-handling-guide.md +936 -936
- package/docs/02-CORE-GUIDES/extraction/examples/02-core-guides-extraction-readme.md +116 -116
- package/docs/02-CORE-GUIDES/extraction/examples/common-patterns.ts +428 -428
- package/docs/02-CORE-GUIDES/extraction/examples/extract-inventory-basic.ts +187 -187
- package/docs/02-CORE-GUIDES/extraction/extraction-quick-reference.md +596 -596
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-01-foundations.md +514 -514
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-02-basic-extraction.md +823 -823
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-03-parquet-processing.md +507 -507
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-04-data-enrichment.md +546 -546
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-05-transformation.md +494 -494
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-06-export-formats.md +458 -458
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-06-performance.md +138 -138
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-07-api-reference.md +148 -148
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-07-optimization.md +692 -692
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-08-extraction-orchestrator.md +1008 -1008
- package/docs/02-CORE-GUIDES/extraction/readme.md +151 -151
- package/docs/02-CORE-GUIDES/ingestion/examples/_simple-kv-store.ts +40 -40
- package/docs/02-CORE-GUIDES/ingestion/examples/error-recovery.ts +728 -728
- package/docs/02-CORE-GUIDES/ingestion/examples/event-driven.ts +501 -501
- package/docs/02-CORE-GUIDES/ingestion/examples/local-file-ingestion.ts +88 -88
- package/docs/02-CORE-GUIDES/ingestion/examples/parquet-ingestion.ts +117 -117
- package/docs/02-CORE-GUIDES/ingestion/examples/performance-optimized.ts +647 -647
- package/docs/02-CORE-GUIDES/ingestion/examples/s3-csv-ingestion.ts +169 -169
- package/docs/02-CORE-GUIDES/ingestion/examples/sftp-csv-ingestion.ts +134 -134
- package/docs/02-CORE-GUIDES/ingestion/ingestion-quick-reference.md +546 -546
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-01-introduction.md +626 -626
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-02-quick-start.md +658 -658
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-03-data-sources.md +1052 -1052
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-04-field-mapping.md +763 -763
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-05-advanced-parsers.md +676 -676
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-06-batch-api.md +1295 -1295
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-07-api-reference.md +138 -138
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-07-state-management.md +1037 -1037
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-08-performance-optimization.md +1349 -1349
- package/docs/02-CORE-GUIDES/ingestion/modules/02-core-guides-ingestion-09-best-practices.md +1893 -1893
- package/docs/02-CORE-GUIDES/ingestion/readme.md +160 -160
- package/docs/02-CORE-GUIDES/logging-guide.md +585 -585
- package/docs/02-CORE-GUIDES/mapping/error-handling-patterns.md +401 -401
- package/docs/02-CORE-GUIDES/mapping/examples/02-core-guides-mapping-readme.md +128 -128
- package/docs/02-CORE-GUIDES/mapping/examples/common-patterns.ts +273 -273
- package/docs/02-CORE-GUIDES/mapping/examples/csv-location-ingestion.json +36 -36
- package/docs/02-CORE-GUIDES/mapping/examples/csv-mapping.ts +242 -242
- package/docs/02-CORE-GUIDES/mapping/examples/graphql-to-parquet-extraction.json +36 -36
- package/docs/02-CORE-GUIDES/mapping/examples/json-mapping.ts +213 -213
- package/docs/02-CORE-GUIDES/mapping/examples/json-product-to-mutation.json +48 -48
- package/docs/02-CORE-GUIDES/mapping/examples/xml-mapping.ts +291 -291
- package/docs/02-CORE-GUIDES/mapping/examples/xml-order-to-mutation.json +45 -45
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/graphql-mutation-mapping-quick-reference.md +463 -463
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/graphql-mutation-mapping-readme.md +227 -227
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-01-introduction.md +222 -222
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-02-quick-start.md +351 -351
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-03-schema-validation.md +569 -569
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-04-mapping-patterns.md +471 -471
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-05-configuration-reference.md +611 -611
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-06-advanced-xpath.md +148 -148
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-06-path-syntax.md +464 -464
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-07-api-reference.md +94 -94
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-07-array-handling.md +307 -307
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-08-custom-resolvers.md +544 -544
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-09-advanced-patterns.md +427 -427
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-10-hooks-and-variables.md +336 -336
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-11-error-handling.md +488 -488
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-12-arguments-vs-nodes.md +383 -383
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/modules/graphql-mutation-mapping-13-best-practices.md +477 -477
- package/docs/02-CORE-GUIDES/mapping/graphql-mutation-mapping/readme.md +62 -62
- package/docs/02-CORE-GUIDES/mapping/mapping-format-decision-tree.md +480 -480
- package/docs/02-CORE-GUIDES/mapping/mapping-graphql-alias-batching-guide.md +820 -820
- package/docs/02-CORE-GUIDES/mapping/mapping-javascript-objects.md +2369 -2369
- package/docs/02-CORE-GUIDES/mapping/mapping-mapper-comparison-guide.md +682 -682
- package/docs/02-CORE-GUIDES/mapping/modules/02-core-guides-mapping-07-api-reference.md +1327 -1327
- package/docs/02-CORE-GUIDES/mapping/modules/02-core-guides-mapping-08-error-handling.md +1142 -1142
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-04-use-cases.md +891 -891
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-06-helpers-resolvers.md +1126 -1126
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-06-sdk-resolvers.md +199 -199
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-07-api-reference.md +1319 -1319
- package/docs/02-CORE-GUIDES/mapping/readme.md +178 -178
- package/docs/02-CORE-GUIDES/mapping/resolver-registration.md +410 -410
- package/docs/02-CORE-GUIDES/mapping/resolvers/examples/common-patterns.ts +226 -226
- package/docs/02-CORE-GUIDES/mapping/resolvers/examples/custom-resolvers.ts +227 -227
- package/docs/02-CORE-GUIDES/mapping/resolvers/examples/sdk-resolvers-usage.ts +203 -203
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-readme.md +274 -274
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-api-reference.md +679 -679
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-cookbook.md +826 -826
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-guide.md +1330 -1330
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-helpers-reference.md +1437 -1437
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-parameters-reference.md +553 -553
- package/docs/02-CORE-GUIDES/mapping/resolvers/mapping-resolvers-resolver-troubleshooting.md +854 -854
- package/docs/02-CORE-GUIDES/mapping/resolvers/readme.md +75 -75
- package/docs/02-CORE-GUIDES/parsers/examples/02-core-guides-parsers-readme.md +161 -161
- package/docs/02-CORE-GUIDES/parsers/examples/csv-parser-examples.ts +110 -110
- package/docs/02-CORE-GUIDES/parsers/examples/json-parser-examples.ts +33 -33
- package/docs/02-CORE-GUIDES/parsers/examples/parquet-parser-examples.ts +47 -47
- package/docs/02-CORE-GUIDES/parsers/examples/xml-parser-examples.ts +38 -38
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-01-foundations.md +355 -355
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-02-csv-parser.md +772 -772
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-03-json-parser.md +789 -789
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-04-xml-parser.md +857 -857
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-05-parquet-parser.md +603 -603
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-06-integration-patterns.md +702 -702
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-06-streaming.md +121 -121
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-07-api-reference.md +89 -89
- package/docs/02-CORE-GUIDES/parsers/modules/02-core-guides-parsers-07-troubleshooting.md +727 -727
- package/docs/02-CORE-GUIDES/parsers/parsers-quick-reference.md +482 -482
- package/docs/02-CORE-GUIDES/parsers/parsers-readme.md +258 -258
- package/docs/02-CORE-GUIDES/parsers/readme.md +65 -65
- package/docs/02-CORE-GUIDES/readme.md +194 -194
- package/docs/02-CORE-GUIDES/webhook-validation/examples/basic-validation.ts +108 -108
- package/docs/02-CORE-GUIDES/webhook-validation/examples/common-patterns.ts +316 -316
- package/docs/02-CORE-GUIDES/webhook-validation/examples/webhook-validation-readme.md +61 -61
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-01-foundations.md +440 -440
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-02-quick-start.md +525 -525
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-03-versori-integration.md +741 -741
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-04-platform-integration.md +629 -629
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-05-configuration.md +535 -535
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-06-error-handling.md +611 -611
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-06-troubleshooting.md +124 -124
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-07-api-reference.md +511 -511
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-08-rubix-webhooks.md +590 -590
- package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-09-rubix-event-vs-http-call.md +432 -432
- package/docs/02-CORE-GUIDES/webhook-validation/readme.md +239 -239
- package/docs/02-CORE-GUIDES/webhook-validation/webhook-validation-quick-reference.md +392 -392
- package/docs/03-PATTERN-GUIDES/connector-scenarios/connector-scenarios-quick-reference.md +498 -498
- package/docs/03-PATTERN-GUIDES/connector-scenarios/connector-scenarios-readme.md +313 -313
- package/docs/03-PATTERN-GUIDES/connector-scenarios/examples/common-patterns.ts +612 -612
- package/docs/03-PATTERN-GUIDES/connector-scenarios/examples/connector-scenarios-readme.md +253 -253
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-01-foundations.md +452 -452
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-02-simple-scenarios.md +681 -681
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-03-intermediate-scenarios.md +637 -637
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-04-advanced-scenarios.md +650 -650
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-05-bidirectional-sync.md +233 -233
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-06-production-patterns.md +442 -442
- package/docs/03-PATTERN-GUIDES/connector-scenarios/modules/connector-scenarios-07-reference.md +445 -445
- package/docs/03-PATTERN-GUIDES/connector-scenarios/readme.md +31 -31
- package/docs/03-PATTERN-GUIDES/enterprise-integration-patterns.md +1528 -1528
- package/docs/03-PATTERN-GUIDES/error-handling/comprehensive-error-handling-guide.md +1437 -1437
- package/docs/03-PATTERN-GUIDES/error-handling/error-handling-quick-reference.md +390 -390
- package/docs/03-PATTERN-GUIDES/error-handling/examples/common-patterns.ts +438 -438
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-01-foundations.md +362 -362
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-02-error-types.md +850 -850
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-03-utf8-handling.md +456 -456
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-04-error-scenarios.md +658 -658
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-05-calling-patterns.md +671 -671
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-06-retry-strategies.md +1034 -1034
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-07-monitoring.md +653 -653
- package/docs/03-PATTERN-GUIDES/error-handling/modules/error-handling-08-api-reference.md +847 -847
- package/docs/03-PATTERN-GUIDES/error-handling/readme.md +36 -36
- package/docs/03-PATTERN-GUIDES/examples/__tests__/readme.md +40 -40
- package/docs/03-PATTERN-GUIDES/examples/__tests__/resolver-examples.test.js +282 -282
- package/docs/03-PATTERN-GUIDES/examples/test-data/03-pattern-guides-readme.md +110 -110
- package/docs/03-PATTERN-GUIDES/examples/test-data/canonical-inventory.json +123 -123
- package/docs/03-PATTERN-GUIDES/examples/test-data/canonical-order.json +171 -171
- package/docs/03-PATTERN-GUIDES/examples/test-data/readme.md +28 -28
- package/docs/03-PATTERN-GUIDES/extraction/extraction-readme.md +15 -15
- package/docs/03-PATTERN-GUIDES/extraction/readme.md +25 -25
- package/docs/03-PATTERN-GUIDES/file-operations/examples/common-patterns.ts +407 -407
- package/docs/03-PATTERN-GUIDES/file-operations/examples/file-operations-readme.md +142 -142
- package/docs/03-PATTERN-GUIDES/file-operations/file-operations-quick-reference.md +462 -462
- package/docs/03-PATTERN-GUIDES/file-operations/file-operations-readme.md +379 -379
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-01-foundations.md +430 -430
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-02-quick-start.md +484 -484
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-03-s3-operations.md +507 -507
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-04-sftp-operations.md +963 -963
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-05-streaming-performance.md +503 -503
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-06-archive-patterns.md +386 -386
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-06-error-handling.md +117 -117
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-07-api-reference.md +78 -78
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-07-testing-troubleshooting.md +567 -567
- package/docs/03-PATTERN-GUIDES/file-operations/modules/file-operations-08-api-reference.md +1055 -1055
- package/docs/03-PATTERN-GUIDES/file-operations/readme.md +32 -32
- package/docs/03-PATTERN-GUIDES/ingestion/ingestion-readme.md +15 -15
- package/docs/03-PATTERN-GUIDES/ingestion/readme.md +25 -25
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/batch-processing.ts +130 -130
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/common-patterns.ts +360 -360
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/delta-sync.ts +130 -130
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/integration-patterns-readme.md +100 -100
- package/docs/03-PATTERN-GUIDES/integration-patterns/examples/real-time-webhook.ts +398 -398
- package/docs/03-PATTERN-GUIDES/integration-patterns/integration-patterns-quick-reference.md +962 -962
- package/docs/03-PATTERN-GUIDES/integration-patterns/integration-patterns-readme.md +134 -134
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-01-real-time-processing.md +991 -991
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-02-batch-processing.md +1547 -1547
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-03-delta-sync.md +1108 -1108
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-04-webhook-patterns.md +1181 -1181
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-05-error-handling.md +1061 -1061
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-06-advanced-integration-services.md +1547 -1547
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-06-performance.md +109 -109
- package/docs/03-PATTERN-GUIDES/integration-patterns/modules/integration-patterns-07-api-reference.md +34 -34
- package/docs/03-PATTERN-GUIDES/integration-patterns/readme.md +30 -30
- package/docs/03-PATTERN-GUIDES/logging-minimal-mode.md +128 -128
- package/docs/03-PATTERN-GUIDES/multiple-connections/examples/common-patterns.ts +380 -380
- package/docs/03-PATTERN-GUIDES/multiple-connections/examples/multiple-connections-readme.md +139 -139
- package/docs/03-PATTERN-GUIDES/multiple-connections/examples/parallel-root-connections.ts +149 -149
- package/docs/03-PATTERN-GUIDES/multiple-connections/examples/real-world-scenarios.ts +405 -405
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-01-foundations.md +378 -378
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-02-quick-start.md +566 -566
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-03-targeting-connections.md +659 -659
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-04-parallel-queries.md +656 -656
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-05-best-practices.md +624 -624
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-06-api-reference.md +824 -824
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-06-versori.md +119 -119
- package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-07-api-reference.md +87 -87
- package/docs/03-PATTERN-GUIDES/multiple-connections/multiple-connections-quick-reference.md +353 -353
- package/docs/03-PATTERN-GUIDES/multiple-connections/multiple-connections-readme.md +270 -270
- package/docs/03-PATTERN-GUIDES/multiple-connections/readme.md +30 -30
- package/docs/03-PATTERN-GUIDES/pagination/pagination-readme.md +14 -14
- package/docs/03-PATTERN-GUIDES/pagination/readme.md +24 -24
- package/docs/03-PATTERN-GUIDES/parquet/examples/common-patterns.ts +180 -180
- package/docs/03-PATTERN-GUIDES/parquet/examples/read-parquet.ts +48 -48
- package/docs/03-PATTERN-GUIDES/parquet/examples/write-parquet.ts +65 -65
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-01-introduction.md +393 -393
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-02-quick-start.md +572 -572
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-03-reading-parquet.md +525 -525
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-04-writing-parquet.md +554 -554
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-05-graphql-extraction.md +405 -405
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-06-performance.md +104 -104
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-06-s3-integration.md +511 -511
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-07-api-reference.md +90 -90
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-07-performance-optimization.md +525 -525
- package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-08-best-practices.md +712 -712
- package/docs/03-PATTERN-GUIDES/parquet/parquet-quick-reference.md +683 -683
- package/docs/03-PATTERN-GUIDES/parquet/parquet-readme.md +248 -248
- package/docs/03-PATTERN-GUIDES/parquet/readme.md +32 -32
- package/docs/03-PATTERN-GUIDES/parsers/parsers-readme.md +12 -12
- package/docs/03-PATTERN-GUIDES/parsers/readme.md +24 -24
- package/docs/03-PATTERN-GUIDES/readme.md +159 -159
- package/docs/03-PATTERN-GUIDES/webhooks/readme.md +24 -24
- package/docs/03-PATTERN-GUIDES/webhooks/webhooks-readme.md +8 -8
- package/docs/04-REFERENCE/architecture/architecture-01-overview.md +427 -427
- package/docs/04-REFERENCE/architecture/architecture-02-client-architecture.md +424 -424
- package/docs/04-REFERENCE/architecture/architecture-03-data-flow.md +690 -690
- package/docs/04-REFERENCE/architecture/architecture-04-service-layer.md +834 -834
- package/docs/04-REFERENCE/architecture/architecture-05-integration-architecture.md +655 -655
- package/docs/04-REFERENCE/architecture/architecture-06-state-management.md +653 -653
- package/docs/04-REFERENCE/architecture/architecture-adding-new-data-sources.md +686 -686
- package/docs/04-REFERENCE/architecture/readme.md +279 -279
- package/docs/04-REFERENCE/platforms/deno/readme.md +117 -117
- package/docs/04-REFERENCE/platforms/nodejs/readme.md +146 -146
- package/docs/04-REFERENCE/platforms/readme.md +135 -135
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-01-introduction.md +398 -398
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-02-quick-start.md +560 -560
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-03-authentication.md +757 -757
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-04-workflows.md +2476 -2476
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-05-connections.md +1167 -1167
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-06-kv-storage.md +990 -990
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-06-state-management.md +121 -121
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-07-api-reference.md +68 -68
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-07-deployment.md +731 -731
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-08-best-practices.md +1111 -1111
- package/docs/04-REFERENCE/platforms/versori/modules/platforms-versori-09-signature-reference.md +766 -766
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-readme.md +299 -299
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-s3-sftp-configuration-guide.md +1425 -1425
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-webhook-api-key-security.md +816 -816
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-webhook-connection-security.md +681 -681
- package/docs/04-REFERENCE/platforms/versori/platforms-versori-workflow-task-types.md +708 -708
- package/docs/04-REFERENCE/platforms/versori/readme.md +108 -108
- package/docs/04-REFERENCE/readme.md +148 -148
- package/docs/04-REFERENCE/resolver-signature/examples/advanced-resolvers.ts +482 -482
- package/docs/04-REFERENCE/resolver-signature/examples/async-resolvers.ts +496 -496
- package/docs/04-REFERENCE/resolver-signature/examples/basic-resolvers.ts +343 -343
- package/docs/04-REFERENCE/resolver-signature/examples/resolver-signature-readme.md +188 -188
- package/docs/04-REFERENCE/resolver-signature/examples/testing-resolvers.ts +463 -463
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-01-foundations.md +286 -286
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-02-parameter-reference.md +643 -643
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-03-basic-examples.md +521 -521
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-04-advanced-patterns.md +739 -739
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-05-sdk-resolvers.md +531 -531
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-06-migration-guide.md +650 -650
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-06-testing.md +125 -125
- package/docs/04-REFERENCE/resolver-signature/modules/resolver-signature-07-api-reference.md +794 -794
- package/docs/04-REFERENCE/resolver-signature/readme.md +64 -64
- package/docs/04-REFERENCE/resolver-signature/resolver-signature-quick-reference.md +270 -270
- package/docs/04-REFERENCE/resolver-signature/resolver-signature-readme.md +351 -351
- package/docs/04-REFERENCE/schema/fluent-commerce-schema.json +764 -764
- package/docs/04-REFERENCE/schema/readme.md +141 -141
- package/docs/04-REFERENCE/testing/examples/04-reference-testing-readme.md +158 -158
- package/docs/04-REFERENCE/testing/examples/fluent-testing.ts +62 -62
- package/docs/04-REFERENCE/testing/examples/health-check.ts +155 -155
- package/docs/04-REFERENCE/testing/examples/integration-test.ts +119 -119
- package/docs/04-REFERENCE/testing/examples/performance-test.ts +183 -183
- package/docs/04-REFERENCE/testing/examples/s3-testing.ts +127 -127
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-01-foundations.md +267 -267
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-02-s3-testing.md +599 -599
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-03-fluent-testing.md +589 -589
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-04-integration-testing.md +699 -699
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-05-debugging.md +478 -478
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-06-cicd-integration.md +463 -463
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-06-preflight-validation.md +131 -131
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-07-best-practices.md +499 -499
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-07-coverage-ci.md +165 -165
- package/docs/04-REFERENCE/testing/modules/04-reference-testing-08-api-reference.md +634 -634
- package/docs/04-REFERENCE/testing/readme.md +86 -86
- package/docs/04-REFERENCE/testing/testing-quick-reference.md +667 -667
- package/docs/04-REFERENCE/testing/testing-readme.md +286 -286
- package/docs/04-REFERENCE/troubleshooting/readme.md +144 -144
- package/docs/04-REFERENCE/troubleshooting/troubleshooting-deno-sftp-compatibility.md +392 -392
- package/docs/template-loading-matrix.md +242 -242
- package/package.json +5 -3
package/docs/03-PATTERN-GUIDES/parquet/modules/03-pattern-guides-parquet-08-best-practices.md
CHANGED
|
@@ -1,712 +1,712 @@
|
|
|
1
|
-
# Module 8: Best Practices
|
|
2
|
-
|
|
3
|
-
[← Back to Parquet Guide](../parquet-readme.md)
|
|
4
|
-
|
|
5
|
-
**Module 8 of 8** | **Level**: All Levels | **Time**: 20 minutes
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Overview
|
|
10
|
-
|
|
11
|
-
Learn production-ready patterns, error handling strategies, troubleshooting techniques, and best practices for reliable Parquet processing.
|
|
12
|
-
|
|
13
|
-
## Learning Objectives
|
|
14
|
-
|
|
15
|
-
By the end of this module, you will:
|
|
16
|
-
|
|
17
|
-
- ✅ Implement robust error handling
|
|
18
|
-
- ✅ Apply production-ready patterns
|
|
19
|
-
- ✅ Troubleshoot common issues
|
|
20
|
-
- ✅ Monitor and maintain Parquet workflows
|
|
21
|
-
- ✅ Follow security and data quality best practices
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## Error Handling
|
|
26
|
-
|
|
27
|
-
### Robust Error Recovery
|
|
28
|
-
|
|
29
|
-
```typescript
|
|
30
|
-
import { Buffer } from 'node:buffer'; // Required for Deno/Versori runtime
|
|
31
|
-
|
|
32
|
-
class RobustParquetProcessor {
|
|
33
|
-
private parser: ParquetParserService;
|
|
34
|
-
private maxRetries = 3;
|
|
35
|
-
|
|
36
|
-
constructor() {
|
|
37
|
-
this.parser = new ParquetParserService(logger, {
|
|
38
|
-
batchSize: 2000,
|
|
39
|
-
enableStreaming: true,
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Parse with automatic retry logic
|
|
45
|
-
*/
|
|
46
|
-
async parseWithRetry(arrayBuffer: ArrayBuffer, fileName: string, retryCount = 0): Promise<any> {
|
|
47
|
-
try {
|
|
48
|
-
return await this.parser.parse(arrayBuffer, fileName);
|
|
49
|
-
} catch (error: any) {
|
|
50
|
-
// Retry on parsing errors
|
|
51
|
-
if (error.code === 'PARSING_ERROR' && retryCount < this.maxRetries) {
|
|
52
|
-
console.warn(`⚠️ Retry ${retryCount + 1}/${this.maxRetries} for ${fileName}`);
|
|
53
|
-
|
|
54
|
-
// Exponential backoff
|
|
55
|
-
await this.sleep(Math.pow(2, retryCount) * 1000);
|
|
56
|
-
|
|
57
|
-
return this.parseWithRetry(arrayBuffer, fileName, retryCount + 1);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Log detailed error
|
|
61
|
-
console.error('❌ Parquet parsing failed:', {
|
|
62
|
-
fileName,
|
|
63
|
-
error: error.message,
|
|
64
|
-
code: error.code,
|
|
65
|
-
retryCount,
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
throw error;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Safe parsing with error containment
|
|
74
|
-
*/
|
|
75
|
-
async safeParse(
|
|
76
|
-
arrayBuffer: ArrayBuffer,
|
|
77
|
-
fileName: string
|
|
78
|
-
): Promise<{ success: boolean; data?: any; error?: any }> {
|
|
79
|
-
try {
|
|
80
|
-
const result = await this.parseWithRetry(arrayBuffer, fileName);
|
|
81
|
-
return { success: true, data: result };
|
|
82
|
-
} catch (error: any) {
|
|
83
|
-
return {
|
|
84
|
-
success: false,
|
|
85
|
-
error: {
|
|
86
|
-
message: error.message,
|
|
87
|
-
code: error.code,
|
|
88
|
-
fileName,
|
|
89
|
-
},
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
private sleep(ms: number): Promise<void> {
|
|
95
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Usage
|
|
100
|
-
const processor = new RobustParquetProcessor();
|
|
101
|
-
|
|
102
|
-
const result = await processor.safeParse(arrayBuffer, 'inventory.parquet');
|
|
103
|
-
|
|
104
|
-
if (result.success) {
|
|
105
|
-
console.log(`✅ Parsed ${result.data.stats.totalRecords} records`);
|
|
106
|
-
// Process records
|
|
107
|
-
} else {
|
|
108
|
-
console.error(`❌ Failed to parse:`, result.error);
|
|
109
|
-
// Handle error, notify monitoring
|
|
110
|
-
}
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
---
|
|
114
|
-
|
|
115
|
-
## Validation Best Practices
|
|
116
|
-
|
|
117
|
-
### Pre-Flight Validation
|
|
118
|
-
|
|
119
|
-
```typescript
|
|
120
|
-
/**
|
|
121
|
-
* Validate before processing
|
|
122
|
-
*/
|
|
123
|
-
async function validateAndProcess(arrayBuffer: ArrayBuffer, fileName: string): Promise<void> {
|
|
124
|
-
const parser = new ParquetParserService(logger);
|
|
125
|
-
|
|
126
|
-
// 1. Validate file
|
|
127
|
-
const validationResult = await parser.validateFile(arrayBuffer, fileName);
|
|
128
|
-
|
|
129
|
-
if (!validationResult.isValid) {
|
|
130
|
-
console.error('❌ File validation failed:');
|
|
131
|
-
validationResult.errors.forEach(err => {
|
|
132
|
-
console.error(` ${err.field}: ${err.message}`);
|
|
133
|
-
});
|
|
134
|
-
throw new Error('Invalid Parquet file');
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// 2. Log warnings
|
|
138
|
-
if (validationResult.warnings.length > 0) {
|
|
139
|
-
console.warn('⚠️ Validation warnings:');
|
|
140
|
-
validationResult.warnings.forEach(warn => {
|
|
141
|
-
console.warn(` ${warn.field}: ${warn.message}`);
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// 3. Parse with schema validation
|
|
146
|
-
const { records, stats } = await parser.parse(arrayBuffer, fileName, {
|
|
147
|
-
validation: {
|
|
148
|
-
enabled: true,
|
|
149
|
-
requiredColumns: ['ref', 'productRef', 'locationRef', 'qty'],
|
|
150
|
-
columnTypes: {
|
|
151
|
-
ref: 'string',
|
|
152
|
-
productRef: 'string',
|
|
153
|
-
locationRef: 'string',
|
|
154
|
-
qty: 'int32',
|
|
155
|
-
},
|
|
156
|
-
},
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
// 4. Data quality checks
|
|
160
|
-
if (stats.validation && !stats.validation.isValid) {
|
|
161
|
-
console.error('❌ Schema validation failed:');
|
|
162
|
-
stats.validation.errors.forEach(err => {
|
|
163
|
-
console.error(` ${err.field}: ${err.message}`);
|
|
164
|
-
});
|
|
165
|
-
throw new Error('Schema validation failed');
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// 5. Business logic validation
|
|
169
|
-
const invalidRecords = records.filter(r => r.qty < 0);
|
|
170
|
-
if (invalidRecords.length > 0) {
|
|
171
|
-
console.warn(`⚠️ Found ${invalidRecords.length} records with negative quantities`);
|
|
172
|
-
// Decide how to handle (skip, fix, fail)
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
console.log(`✅ Validation passed: ${stats.totalRecords} records`);
|
|
176
|
-
}
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
---
|
|
180
|
-
|
|
181
|
-
## Production Patterns
|
|
182
|
-
|
|
183
|
-
### Pattern 1: Idempotent Processing
|
|
184
|
-
|
|
185
|
-
```typescript
|
|
186
|
-
import { StateService, VersoriKVAdapter } from '@fluentcommerce/fc-connect-sdk';
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Process files only once (idempotent)
|
|
190
|
-
* Business use case: Prevent duplicate processing
|
|
191
|
-
*/
|
|
192
|
-
class IdempotentParquetProcessor {
|
|
193
|
-
private parser: ParquetParserService;
|
|
194
|
-
private stateService: StateService;
|
|
195
|
-
private kvAdapter: VersoriKVAdapter;
|
|
196
|
-
private s3: S3DataSource;
|
|
197
|
-
|
|
198
|
-
constructor(kvAdapter: VersoriKVAdapter, s3Config: any, logger: Logger) {
|
|
199
|
-
this.parser = new ParquetParserService(logger);
|
|
200
|
-
this.stateService = new StateService(logger);
|
|
201
|
-
this.kvAdapter = kvAdapter;
|
|
202
|
-
this.s3 = new S3DataSource({ type: 'S3_CSV', s3Config }, logger);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
async processNewFiles(prefix: string): Promise<void> {
|
|
206
|
-
// 1. List files
|
|
207
|
-
const files = await this.s3.listFiles({ prefix });
|
|
208
|
-
const parquetFiles = files.filter(f => f.path.endsWith('.parquet'));
|
|
209
|
-
|
|
210
|
-
console.log(`Found ${parquetFiles.length} Parquet files`);
|
|
211
|
-
|
|
212
|
-
// 2. Process only unprocessed files
|
|
213
|
-
for (const file of parquetFiles) {
|
|
214
|
-
const fileKey = `processed_${file.path}`;
|
|
215
|
-
|
|
216
|
-
// Check if already processed
|
|
217
|
-
const isProcessed = await this.stateService.isFileProcessed(this.kvAdapter, fileKey);
|
|
218
|
-
|
|
219
|
-
if (isProcessed) {
|
|
220
|
-
console.log(`⏭️ Skipping ${file.key} (already processed)`);
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
try {
|
|
225
|
-
console.log(`🔄 Processing ${file.key}...`);
|
|
226
|
-
|
|
227
|
-
// Process file
|
|
228
|
-
await this.processFile(file.path);
|
|
229
|
-
|
|
230
|
-
// Mark as processed
|
|
231
|
-
await this.stateService.markFileProcessed(this.kvAdapter, fileKey, {
|
|
232
|
-
timestamp: new Date().toISOString(),
|
|
233
|
-
fileKey: file.path,
|
|
234
|
-
bucket: (this.s3 as any).s3Config.bucket,
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
console.log(`✅ Completed ${file.key}`);
|
|
238
|
-
} catch (error) {
|
|
239
|
-
console.error(`❌ Failed ${file.key}:`, error);
|
|
240
|
-
// Don't mark as processed - will retry next run
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
private async processFile(key: string): Promise<void> {
|
|
246
|
-
const buffer = await this.s3.downloadFile(key, { encoding: 'binary' });
|
|
247
|
-
const arrayBuffer = buffer.buffer.slice(
|
|
248
|
-
buffer.byteOffset,
|
|
249
|
-
buffer.byteOffset + buffer.byteLength
|
|
250
|
-
);
|
|
251
|
-
|
|
252
|
-
const streamResult = await this.parser.parseStreaming(arrayBuffer, key);
|
|
253
|
-
|
|
254
|
-
for await (const batch of streamResult.records) {
|
|
255
|
-
await this.processBatch(batch);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
const stats = await streamResult.stats;
|
|
259
|
-
console.log(` ${stats.totalRecords} records processed`);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
private async processBatch(batch: any[]): Promise<void> {
|
|
263
|
-
// Your business logic
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// Usage example
|
|
268
|
-
const logger = toStructuredLogger(createConsoleLogger(), { logLevel: 'info' });
|
|
269
|
-
const processor = new IdempotentParquetProcessor(kvAdapter, s3Config, logger);
|
|
270
|
-
await processor.processNewFiles('parquet/');
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
### Pattern 2: Dead Letter Queue
|
|
274
|
-
|
|
275
|
-
```typescript
|
|
276
|
-
/**
|
|
277
|
-
* Failed file handling with DLQ pattern
|
|
278
|
-
*/
|
|
279
|
-
class ParquetProcessorWithDLQ {
|
|
280
|
-
private parser: ParquetParserService;
|
|
281
|
-
private s3: S3DataSource;
|
|
282
|
-
private dlqBucket: string;
|
|
283
|
-
|
|
284
|
-
constructor(s3Config: any, dlqBucket: string, logger: Logger) {
|
|
285
|
-
this.parser = new ParquetParserService(logger);
|
|
286
|
-
this.s3 = new S3DataSource(
|
|
287
|
-
{
|
|
288
|
-
type: 'S3_CSV',
|
|
289
|
-
s3Config: {
|
|
290
|
-
bucket: process.env.S3_BUCKET!,
|
|
291
|
-
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
292
|
-
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
293
|
-
region: process.env.AWS_REGION!,
|
|
294
|
-
},
|
|
295
|
-
},
|
|
296
|
-
logger
|
|
297
|
-
);
|
|
298
|
-
this.dlqBucket = dlqBucket;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
async processWithDLQ(key: string): Promise<{ success: boolean; recordsProcessed?: number }> {
|
|
302
|
-
try {
|
|
303
|
-
const buffer = await this.s3.downloadFile(key, { encoding: 'binary' });
|
|
304
|
-
const arrayBuffer = buffer.buffer.slice(
|
|
305
|
-
buffer.byteOffset,
|
|
306
|
-
buffer.byteOffset + buffer.byteLength
|
|
307
|
-
);
|
|
308
|
-
|
|
309
|
-
const { records, stats } = await this.parser.parse(arrayBuffer, key);
|
|
310
|
-
|
|
311
|
-
// Process records
|
|
312
|
-
await this.processRecords(records);
|
|
313
|
-
|
|
314
|
-
return { success: true, recordsProcessed: stats.totalRecords };
|
|
315
|
-
} catch (error: any) {
|
|
316
|
-
console.error(`❌ Processing failed for ${key}:`, error.message);
|
|
317
|
-
|
|
318
|
-
// Move to DLQ
|
|
319
|
-
await this.moveToDLQ(key, error);
|
|
320
|
-
|
|
321
|
-
return { success: false };
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
private async moveToDLQ(sourceKey: string, error: Error): Promise<void> {
|
|
326
|
-
const dlqKey = `failed/${sourceKey}_${Date.now()}.parquet`;
|
|
327
|
-
|
|
328
|
-
console.log(`📦 Moving to DLQ: ${dlqKey}`);
|
|
329
|
-
|
|
330
|
-
// Create separate S3 instance for DLQ bucket
|
|
331
|
-
const dlqS3 = new S3DataSource(
|
|
332
|
-
{
|
|
333
|
-
type: 'S3_CSV',
|
|
334
|
-
s3Config: {
|
|
335
|
-
bucket: this.dlqBucket,
|
|
336
|
-
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
337
|
-
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
338
|
-
region: process.env.AWS_REGION!,
|
|
339
|
-
},
|
|
340
|
-
},
|
|
341
|
-
toStructuredLogger(createConsoleLogger(), {
|
|
342
|
-
logLevel: 'info',
|
|
343
|
-
})
|
|
344
|
-
);
|
|
345
|
-
|
|
346
|
-
try {
|
|
347
|
-
// Copy file to DLQ
|
|
348
|
-
const buffer = await this.s3.downloadFile(sourceKey, { encoding: 'binary' });
|
|
349
|
-
await dlqS3.uploadFile(dlqKey, buffer, {
|
|
350
|
-
contentType: 'application/octet-stream',
|
|
351
|
-
});
|
|
352
|
-
|
|
353
|
-
// Write error metadata
|
|
354
|
-
const errorMetadata = {
|
|
355
|
-
originalKey: sourceKey,
|
|
356
|
-
error: error.message,
|
|
357
|
-
timestamp: new Date().toISOString(),
|
|
358
|
-
};
|
|
359
|
-
|
|
360
|
-
await dlqS3.uploadFile(
|
|
361
|
-
`${dlqKey}.error.json`,
|
|
362
|
-
Buffer.from(JSON.stringify(errorMetadata, null, 2)),
|
|
363
|
-
{ contentType: 'application/json' }
|
|
364
|
-
);
|
|
365
|
-
|
|
366
|
-
console.log(`✅ Moved to DLQ with metadata`);
|
|
367
|
-
} catch (dlqError) {
|
|
368
|
-
console.error(`❌ Failed to move to DLQ:`, dlqError);
|
|
369
|
-
// Alert monitoring system
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
private async processRecords(records: any[]): Promise<void> {
|
|
374
|
-
// Your processing logic
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
```
|
|
378
|
-
|
|
379
|
-
---
|
|
380
|
-
|
|
381
|
-
## Troubleshooting
|
|
382
|
-
|
|
383
|
-
### Common Issues and Solutions
|
|
384
|
-
|
|
385
|
-
#### Issue 1: Out of Memory
|
|
386
|
-
|
|
387
|
-
**Symptoms**: Node.js crashes with `FATAL ERROR: Reached heap limit`
|
|
388
|
-
|
|
389
|
-
**Solutions**:
|
|
390
|
-
|
|
391
|
-
```typescript
|
|
392
|
-
// 1. Enable streaming
|
|
393
|
-
const parser = new ParquetParserService(logger, {
|
|
394
|
-
enableStreaming: true, // ✅ Always enable for large files
|
|
395
|
-
batchSize: 500, // ✅ Smaller batches
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
// 2. Process in smaller chunks
|
|
399
|
-
const streamResult = await parser.parseStreaming(arrayBuffer, fileName);
|
|
400
|
-
|
|
401
|
-
for await (const batch of streamResult.records) {
|
|
402
|
-
await processBatch(batch);
|
|
403
|
-
// Batch is released after processing
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// 3. Increase Node.js heap size
|
|
407
|
-
// node --max-old-space-size=4096 script.js
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
#### Issue 2: Slow Parsing
|
|
411
|
-
|
|
412
|
-
**Symptoms**: Processing takes much longer than expected
|
|
413
|
-
|
|
414
|
-
**Diagnostics**:
|
|
415
|
-
|
|
416
|
-
```typescript
|
|
417
|
-
// Check performance metrics
|
|
418
|
-
const { records, stats } = await parser.parse(arrayBuffer, fileName);
|
|
419
|
-
|
|
420
|
-
console.log(`Records/sec: ${stats.recordsPerSecond}`);
|
|
421
|
-
console.log(`Processing time: ${stats.processingTimeMs}ms`);
|
|
422
|
-
console.log(`File size: ${(stats.fileSizeBytes / 1024 / 1024).toFixed(2)}MB`);
|
|
423
|
-
|
|
424
|
-
// Expected: 50,000-70,000 records/sec for typical files
|
|
425
|
-
// If much slower, check:
|
|
426
|
-
// - File compression (GZIP/BROTLI slower than SNAPPY)
|
|
427
|
-
// - Validation enabled (disable for speed)
|
|
428
|
-
// - Batch size too small (increase to 2000-5000)
|
|
429
|
-
```
|
|
430
|
-
|
|
431
|
-
**Solutions**:
|
|
432
|
-
|
|
433
|
-
```typescript
|
|
434
|
-
// 1. Disable validation for trusted sources
|
|
435
|
-
const { records } = await parser.parse(arrayBuffer, fileName, {
|
|
436
|
-
validation: { enabled: false }, // ✅ Faster
|
|
437
|
-
});
|
|
438
|
-
|
|
439
|
-
// 2. Increase batch size
|
|
440
|
-
const parser = new ParquetParserService(logger, {
|
|
441
|
-
batchSize: 5000, // ✅ Larger batches for throughput
|
|
442
|
-
});
|
|
443
|
-
|
|
444
|
-
// 3. Use parallel processing for multiple files
|
|
445
|
-
// See Module 7: Performance Optimization
|
|
446
|
-
```
|
|
447
|
-
|
|
448
|
-
#### Issue 3: Schema Mismatch
|
|
449
|
-
|
|
450
|
-
**Symptoms**: `VALIDATION_ERROR: Required column 'xyz' not found`
|
|
451
|
-
|
|
452
|
-
**Diagnosis**:
|
|
453
|
-
|
|
454
|
-
```typescript
|
|
455
|
-
// Check column statistics
|
|
456
|
-
const { stats } = await parser.parse(arrayBuffer, fileName);
|
|
457
|
-
|
|
458
|
-
console.log('Available columns:');
|
|
459
|
-
Object.keys(stats.columnStats).forEach(col => {
|
|
460
|
-
console.log(` - ${col} (${stats.columnStats[col].type})`);
|
|
461
|
-
});
|
|
462
|
-
```
|
|
463
|
-
|
|
464
|
-
**Solution**:
|
|
465
|
-
|
|
466
|
-
```typescript
|
|
467
|
-
// 1. Update validation config to match actual schema
|
|
468
|
-
const { records } = await parser.parse(arrayBuffer, fileName, {
|
|
469
|
-
validation: {
|
|
470
|
-
enabled: true,
|
|
471
|
-
requiredColumns: ['ref', 'productRef', 'locationRef', 'qty'], // ✅ Correct columns
|
|
472
|
-
allowAdditionalColumns: true, // ✅ Allow extra columns
|
|
473
|
-
},
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
// 2. Or disable validation temporarily
|
|
477
|
-
```
|
|
478
|
-
|
|
479
|
-
#### Issue 4: Corrupted Parquet File
|
|
480
|
-
|
|
481
|
-
**Symptoms**: `PARSING_ERROR: Invalid Parquet magic number`
|
|
482
|
-
|
|
483
|
-
**Solutions**:
|
|
484
|
-
|
|
485
|
-
```typescript
|
|
486
|
-
// 1. Validate file before parsing
|
|
487
|
-
const validationResult = await parser.validateFile(arrayBuffer, fileName);
|
|
488
|
-
|
|
489
|
-
if (!validationResult.isValid) {
|
|
490
|
-
console.error('File is corrupted or not Parquet format');
|
|
491
|
-
// Move to DLQ or alert
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
// 2. Check file integrity
|
|
496
|
-
const fileHash = crypto.createHash('md5').update(buffer).digest('hex');
|
|
497
|
-
console.log(`File hash: ${fileHash}`);
|
|
498
|
-
// Compare with expected hash if available
|
|
499
|
-
```
|
|
500
|
-
|
|
501
|
-
---
|
|
502
|
-
|
|
503
|
-
## Monitoring and Logging
|
|
504
|
-
|
|
505
|
-
### Comprehensive Logging
|
|
506
|
-
|
|
507
|
-
```typescript
|
|
508
|
-
class ParquetProcessorWithLogging {
|
|
509
|
-
private parser: ParquetParserService;
|
|
510
|
-
private logger: Logger;
|
|
511
|
-
|
|
512
|
-
constructor() {
|
|
513
|
-
this.logger = toStructuredLogger(createConsoleLogger(), {
|
|
514
|
-
logLevel: 'info',
|
|
515
|
-
});
|
|
516
|
-
this.parser = new ParquetParserService(this.logger, {
|
|
517
|
-
batchSize: 2000,
|
|
518
|
-
enableStreaming: true,
|
|
519
|
-
});
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
async processWithLogging(arrayBuffer: ArrayBuffer, fileName: string): Promise<void> {
|
|
523
|
-
const startTime = Date.now();
|
|
524
|
-
const fileSizeMB = (arrayBuffer.byteLength / 1024 / 1024).toFixed(2);
|
|
525
|
-
|
|
526
|
-
this.logger.info(`📥 Starting: ${fileName} (${fileSizeMB}MB)`);
|
|
527
|
-
|
|
528
|
-
try {
|
|
529
|
-
// Parse
|
|
530
|
-
const { records, stats } = await this.parser.parse(arrayBuffer, fileName);
|
|
531
|
-
|
|
532
|
-
const duration = Date.now() - startTime;
|
|
533
|
-
|
|
534
|
-
// Log success
|
|
535
|
-
this.logger.info(`✅ Success: ${fileName}`, {
|
|
536
|
-
records: stats.totalRecords,
|
|
537
|
-
processingTimeMs: stats.processingTimeMs,
|
|
538
|
-
throughput: stats.recordsPerSecond,
|
|
539
|
-
batches: stats.batchesProcessed,
|
|
540
|
-
totalDuration: duration,
|
|
541
|
-
peakMemoryMB: (stats.peakMemoryUsage / 1024 / 1024).toFixed(2),
|
|
542
|
-
});
|
|
543
|
-
|
|
544
|
-
// Log warnings if any
|
|
545
|
-
if (stats.validation && stats.validation.warnings.length > 0) {
|
|
546
|
-
this.logger.warn(`⚠️ Validation warnings for ${fileName}:`, {
|
|
547
|
-
warnings: stats.validation.warnings,
|
|
548
|
-
});
|
|
549
|
-
}
|
|
550
|
-
} catch (error: any) {
|
|
551
|
-
const duration = Date.now() - startTime;
|
|
552
|
-
|
|
553
|
-
this.logger.error(`❌ Failed: ${fileName}`, {
|
|
554
|
-
error: error.message,
|
|
555
|
-
code: error.code,
|
|
556
|
-
duration,
|
|
557
|
-
});
|
|
558
|
-
|
|
559
|
-
throw error;
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
```
|
|
564
|
-
|
|
565
|
-
---
|
|
566
|
-
|
|
567
|
-
## Security Best Practices
|
|
568
|
-
|
|
569
|
-
### 1. Validate File Sources
|
|
570
|
-
|
|
571
|
-
```typescript
|
|
572
|
-
// Only process files from trusted S3 buckets
|
|
573
|
-
const ALLOWED_BUCKETS = ['data-lake-prod', 'data-lake-staging'];
|
|
574
|
-
|
|
575
|
-
async function processSecure(bucket: string, key: string): Promise<void> {
|
|
576
|
-
if (!ALLOWED_BUCKETS.includes(bucket)) {
|
|
577
|
-
throw new Error(`Unauthorized bucket: ${bucket}`);
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
// Process file
|
|
581
|
-
}
|
|
582
|
-
```
|
|
583
|
-
|
|
584
|
-
### 2. Sanitize File Paths
|
|
585
|
-
|
|
586
|
-
```typescript
|
|
587
|
-
import * as path from 'path';
|
|
588
|
-
|
|
589
|
-
function sanitizePath(filePath: string): string {
|
|
590
|
-
// Prevent directory traversal
|
|
591
|
-
const normalized = path.normalize(filePath);
|
|
592
|
-
|
|
593
|
-
if (normalized.includes('..')) {
|
|
594
|
-
throw new Error('Invalid file path: directory traversal detected');
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
return normalized;
|
|
598
|
-
}
|
|
599
|
-
```
|
|
600
|
-
|
|
601
|
-
### 3. Limit File Size
|
|
602
|
-
|
|
603
|
-
```typescript
|
|
604
|
-
const MAX_FILE_SIZE = 500 * 1024 * 1024; // 500 MB
|
|
605
|
-
|
|
606
|
-
async function processWithSizeLimit(arrayBuffer: ArrayBuffer, fileName: string): Promise<void> {
|
|
607
|
-
if (arrayBuffer.byteLength > MAX_FILE_SIZE) {
|
|
608
|
-
throw new Error(
|
|
609
|
-
`File too large: ${(arrayBuffer.byteLength / 1024 / 1024).toFixed(2)}MB > ${MAX_FILE_SIZE / 1024 / 1024}MB`
|
|
610
|
-
);
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
// Process file
|
|
614
|
-
}
|
|
615
|
-
```
|
|
616
|
-
|
|
617
|
-
---
|
|
618
|
-
|
|
619
|
-
## Data Quality Checks
|
|
620
|
-
|
|
621
|
-
```typescript
|
|
622
|
-
/**
|
|
623
|
-
* Validate data quality after parsing
|
|
624
|
-
*/
|
|
625
|
-
function validateDataQuality(
|
|
626
|
-
records: any[],
|
|
627
|
-
stats: ParquetParsingStats
|
|
628
|
-
): { isValid: boolean; issues: string[] } {
|
|
629
|
-
const issues: string[] = [];
|
|
630
|
-
|
|
631
|
-
// Check for sufficient records
|
|
632
|
-
if (records.length === 0) {
|
|
633
|
-
issues.push('No records found in file');
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
// Check column completeness
|
|
637
|
-
for (const [colName, colStats] of Object.entries(stats.columnStats)) {
|
|
638
|
-
const completeness = 1 - colStats.nullCount / stats.totalRecords;
|
|
639
|
-
|
|
640
|
-
if (completeness < 0.9) {
|
|
641
|
-
issues.push(`Low completeness for ${colName}: ${(completeness * 100).toFixed(1)}%`);
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
// Check for duplicate keys
|
|
646
|
-
const refs = records.map(r => r.ref).filter(Boolean);
|
|
647
|
-
const uniqueRefs = new Set(refs);
|
|
648
|
-
|
|
649
|
-
if (refs.length !== uniqueRefs.size) {
|
|
650
|
-
issues.push(`Duplicate refs found: ${refs.length - uniqueRefs.size} duplicates`);
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
// Business logic checks
|
|
654
|
-
const negativeQty = records.filter(r => r.qty < 0).length;
|
|
655
|
-
if (negativeQty > 0) {
|
|
656
|
-
issues.push(`${negativeQty} records with negative quantities`);
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
return {
|
|
660
|
-
isValid: issues.length === 0,
|
|
661
|
-
issues,
|
|
662
|
-
};
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
// Usage
|
|
666
|
-
const { records, stats } = await parser.parse(arrayBuffer, fileName);
|
|
667
|
-
const qualityCheck = validateDataQuality(records, stats);
|
|
668
|
-
|
|
669
|
-
if (!qualityCheck.isValid) {
|
|
670
|
-
console.warn('⚠️ Data quality issues:');
|
|
671
|
-
qualityCheck.issues.forEach(issue => console.warn(` - ${issue}`));
|
|
672
|
-
// Decide whether to proceed or reject
|
|
673
|
-
}
|
|
674
|
-
```
|
|
675
|
-
|
|
676
|
-
---
|
|
677
|
-
|
|
678
|
-
## Key Takeaways
|
|
679
|
-
|
|
680
|
-
- 🎯 **Retry logic** - Handle transient failures gracefully
|
|
681
|
-
- 🎯 **Validation first** - Check schema and data quality before processing
|
|
682
|
-
- 🎯 **Idempotent processing** - Use state management to prevent duplicates
|
|
683
|
-
- 🎯 **Dead letter queue** - Capture failed files for manual review
|
|
684
|
-
- 🎯 **Comprehensive logging** - Track success, failures, and performance
|
|
685
|
-
- 🎯 **Security checks** - Validate sources, sanitize paths, limit sizes
|
|
686
|
-
- 🎯 **Monitor metrics** - Track throughput, errors, memory usage
|
|
687
|
-
|
|
688
|
-
---
|
|
689
|
-
|
|
690
|
-
## Summary
|
|
691
|
-
|
|
692
|
-
You've completed the Parquet Processing guide! You now know how to:
|
|
693
|
-
|
|
694
|
-
✅ Understand Parquet format and when to use it
|
|
695
|
-
✅ Parse Parquet files with ParquetParserService
|
|
696
|
-
✅ Handle large files with streaming
|
|
697
|
-
✅ Write Parquet files using parquetjs
|
|
698
|
-
✅ Extract from Fluent Commerce GraphQL to Parquet
|
|
699
|
-
✅ Integrate with S3 for data lake operations
|
|
700
|
-
✅ Optimize performance for large-scale processing
|
|
701
|
-
✅ Apply production-ready error handling and monitoring
|
|
702
|
-
|
|
703
|
-
---
|
|
704
|
-
|
|
705
|
-
[← Previous: Module 7 - Performance Optimization](./03-pattern-guides-parquet-07-performance-optimization.md) | [Back to Guide](../parquet-readme.md)
|
|
706
|
-
|
|
707
|
-
## Related Documentation
|
|
708
|
-
|
|
709
|
-
- [Data Extraction Guide](../../extraction/) - Complete extraction workflows
|
|
710
|
-
- [S3 Complete Guide](../../../02-CORE-GUIDES/data-sources/data-sources-readme.md) - S3 operations
|
|
711
|
-
- [Universal Mapping Guide](../../../02-CORE-GUIDES/mapping/) - Data transformations
|
|
712
|
-
- [Auto-Pagination Guide](../../../02-CORE-GUIDES/auto-pagination/) - GraphQL pagination patterns
|
|
1
|
+
# Module 8: Best Practices
|
|
2
|
+
|
|
3
|
+
[← Back to Parquet Guide](../parquet-readme.md)
|
|
4
|
+
|
|
5
|
+
**Module 8 of 8** | **Level**: All Levels | **Time**: 20 minutes
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
Learn production-ready patterns, error handling strategies, troubleshooting techniques, and best practices for reliable Parquet processing.
|
|
12
|
+
|
|
13
|
+
## Learning Objectives
|
|
14
|
+
|
|
15
|
+
By the end of this module, you will:
|
|
16
|
+
|
|
17
|
+
- ✅ Implement robust error handling
|
|
18
|
+
- ✅ Apply production-ready patterns
|
|
19
|
+
- ✅ Troubleshoot common issues
|
|
20
|
+
- ✅ Monitor and maintain Parquet workflows
|
|
21
|
+
- ✅ Follow security and data quality best practices
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Error Handling
|
|
26
|
+
|
|
27
|
+
### Robust Error Recovery
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { Buffer } from 'node:buffer'; // Required for Deno/Versori runtime
|
|
31
|
+
|
|
32
|
+
class RobustParquetProcessor {
|
|
33
|
+
private parser: ParquetParserService;
|
|
34
|
+
private maxRetries = 3;
|
|
35
|
+
|
|
36
|
+
constructor() {
|
|
37
|
+
this.parser = new ParquetParserService(logger, {
|
|
38
|
+
batchSize: 2000,
|
|
39
|
+
enableStreaming: true,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Parse with automatic retry logic
|
|
45
|
+
*/
|
|
46
|
+
async parseWithRetry(arrayBuffer: ArrayBuffer, fileName: string, retryCount = 0): Promise<any> {
|
|
47
|
+
try {
|
|
48
|
+
return await this.parser.parse(arrayBuffer, fileName);
|
|
49
|
+
} catch (error: any) {
|
|
50
|
+
// Retry on parsing errors
|
|
51
|
+
if (error.code === 'PARSING_ERROR' && retryCount < this.maxRetries) {
|
|
52
|
+
console.warn(`⚠️ Retry ${retryCount + 1}/${this.maxRetries} for ${fileName}`);
|
|
53
|
+
|
|
54
|
+
// Exponential backoff
|
|
55
|
+
await this.sleep(Math.pow(2, retryCount) * 1000);
|
|
56
|
+
|
|
57
|
+
return this.parseWithRetry(arrayBuffer, fileName, retryCount + 1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Log detailed error
|
|
61
|
+
console.error('❌ Parquet parsing failed:', {
|
|
62
|
+
fileName,
|
|
63
|
+
error: error.message,
|
|
64
|
+
code: error.code,
|
|
65
|
+
retryCount,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Safe parsing with error containment
|
|
74
|
+
*/
|
|
75
|
+
async safeParse(
|
|
76
|
+
arrayBuffer: ArrayBuffer,
|
|
77
|
+
fileName: string
|
|
78
|
+
): Promise<{ success: boolean; data?: any; error?: any }> {
|
|
79
|
+
try {
|
|
80
|
+
const result = await this.parseWithRetry(arrayBuffer, fileName);
|
|
81
|
+
return { success: true, data: result };
|
|
82
|
+
} catch (error: any) {
|
|
83
|
+
return {
|
|
84
|
+
success: false,
|
|
85
|
+
error: {
|
|
86
|
+
message: error.message,
|
|
87
|
+
code: error.code,
|
|
88
|
+
fileName,
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private sleep(ms: number): Promise<void> {
|
|
95
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Usage
|
|
100
|
+
const processor = new RobustParquetProcessor();
|
|
101
|
+
|
|
102
|
+
const result = await processor.safeParse(arrayBuffer, 'inventory.parquet');
|
|
103
|
+
|
|
104
|
+
if (result.success) {
|
|
105
|
+
console.log(`✅ Parsed ${result.data.stats.totalRecords} records`);
|
|
106
|
+
// Process records
|
|
107
|
+
} else {
|
|
108
|
+
console.error(`❌ Failed to parse:`, result.error);
|
|
109
|
+
// Handle error, notify monitoring
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Validation Best Practices
|
|
116
|
+
|
|
117
|
+
### Pre-Flight Validation
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
/**
|
|
121
|
+
* Validate before processing
|
|
122
|
+
*/
|
|
123
|
+
async function validateAndProcess(arrayBuffer: ArrayBuffer, fileName: string): Promise<void> {
|
|
124
|
+
const parser = new ParquetParserService(logger);
|
|
125
|
+
|
|
126
|
+
// 1. Validate file
|
|
127
|
+
const validationResult = await parser.validateFile(arrayBuffer, fileName);
|
|
128
|
+
|
|
129
|
+
if (!validationResult.isValid) {
|
|
130
|
+
console.error('❌ File validation failed:');
|
|
131
|
+
validationResult.errors.forEach(err => {
|
|
132
|
+
console.error(` ${err.field}: ${err.message}`);
|
|
133
|
+
});
|
|
134
|
+
throw new Error('Invalid Parquet file');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// 2. Log warnings
|
|
138
|
+
if (validationResult.warnings.length > 0) {
|
|
139
|
+
console.warn('⚠️ Validation warnings:');
|
|
140
|
+
validationResult.warnings.forEach(warn => {
|
|
141
|
+
console.warn(` ${warn.field}: ${warn.message}`);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// 3. Parse with schema validation
|
|
146
|
+
const { records, stats } = await parser.parse(arrayBuffer, fileName, {
|
|
147
|
+
validation: {
|
|
148
|
+
enabled: true,
|
|
149
|
+
requiredColumns: ['ref', 'productRef', 'locationRef', 'qty'],
|
|
150
|
+
columnTypes: {
|
|
151
|
+
ref: 'string',
|
|
152
|
+
productRef: 'string',
|
|
153
|
+
locationRef: 'string',
|
|
154
|
+
qty: 'int32',
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// 4. Data quality checks
|
|
160
|
+
if (stats.validation && !stats.validation.isValid) {
|
|
161
|
+
console.error('❌ Schema validation failed:');
|
|
162
|
+
stats.validation.errors.forEach(err => {
|
|
163
|
+
console.error(` ${err.field}: ${err.message}`);
|
|
164
|
+
});
|
|
165
|
+
throw new Error('Schema validation failed');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// 5. Business logic validation
|
|
169
|
+
const invalidRecords = records.filter(r => r.qty < 0);
|
|
170
|
+
if (invalidRecords.length > 0) {
|
|
171
|
+
console.warn(`⚠️ Found ${invalidRecords.length} records with negative quantities`);
|
|
172
|
+
// Decide how to handle (skip, fix, fail)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
console.log(`✅ Validation passed: ${stats.totalRecords} records`);
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Production Patterns
|
|
182
|
+
|
|
183
|
+
### Pattern 1: Idempotent Processing
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
import { StateService, VersoriKVAdapter } from '@fluentcommerce/fc-connect-sdk';
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Process files only once (idempotent)
|
|
190
|
+
* Business use case: Prevent duplicate processing
|
|
191
|
+
*/
|
|
192
|
+
class IdempotentParquetProcessor {
|
|
193
|
+
private parser: ParquetParserService;
|
|
194
|
+
private stateService: StateService;
|
|
195
|
+
private kvAdapter: VersoriKVAdapter;
|
|
196
|
+
private s3: S3DataSource;
|
|
197
|
+
|
|
198
|
+
constructor(kvAdapter: VersoriKVAdapter, s3Config: any, logger: Logger) {
|
|
199
|
+
this.parser = new ParquetParserService(logger);
|
|
200
|
+
this.stateService = new StateService(logger);
|
|
201
|
+
this.kvAdapter = kvAdapter;
|
|
202
|
+
this.s3 = new S3DataSource({ type: 'S3_CSV', s3Config }, logger);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async processNewFiles(prefix: string): Promise<void> {
|
|
206
|
+
// 1. List files
|
|
207
|
+
const files = await this.s3.listFiles({ prefix });
|
|
208
|
+
const parquetFiles = files.filter(f => f.path.endsWith('.parquet'));
|
|
209
|
+
|
|
210
|
+
console.log(`Found ${parquetFiles.length} Parquet files`);
|
|
211
|
+
|
|
212
|
+
// 2. Process only unprocessed files
|
|
213
|
+
for (const file of parquetFiles) {
|
|
214
|
+
const fileKey = `processed_${file.path}`;
|
|
215
|
+
|
|
216
|
+
// Check if already processed
|
|
217
|
+
const isProcessed = await this.stateService.isFileProcessed(this.kvAdapter, fileKey);
|
|
218
|
+
|
|
219
|
+
if (isProcessed) {
|
|
220
|
+
console.log(`⏭️ Skipping ${file.key} (already processed)`);
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
console.log(`🔄 Processing ${file.key}...`);
|
|
226
|
+
|
|
227
|
+
// Process file
|
|
228
|
+
await this.processFile(file.path);
|
|
229
|
+
|
|
230
|
+
// Mark as processed
|
|
231
|
+
await this.stateService.markFileProcessed(this.kvAdapter, fileKey, {
|
|
232
|
+
timestamp: new Date().toISOString(),
|
|
233
|
+
fileKey: file.path,
|
|
234
|
+
bucket: (this.s3 as any).s3Config.bucket,
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
console.log(`✅ Completed ${file.key}`);
|
|
238
|
+
} catch (error) {
|
|
239
|
+
console.error(`❌ Failed ${file.key}:`, error);
|
|
240
|
+
// Don't mark as processed - will retry next run
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
private async processFile(key: string): Promise<void> {
|
|
246
|
+
const buffer = await this.s3.downloadFile(key, { encoding: 'binary' });
|
|
247
|
+
const arrayBuffer = buffer.buffer.slice(
|
|
248
|
+
buffer.byteOffset,
|
|
249
|
+
buffer.byteOffset + buffer.byteLength
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
const streamResult = await this.parser.parseStreaming(arrayBuffer, key);
|
|
253
|
+
|
|
254
|
+
for await (const batch of streamResult.records) {
|
|
255
|
+
await this.processBatch(batch);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const stats = await streamResult.stats;
|
|
259
|
+
console.log(` ${stats.totalRecords} records processed`);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
private async processBatch(batch: any[]): Promise<void> {
|
|
263
|
+
// Your business logic
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Usage example
|
|
268
|
+
const logger = toStructuredLogger(createConsoleLogger(), { logLevel: 'info' });
|
|
269
|
+
const processor = new IdempotentParquetProcessor(kvAdapter, s3Config, logger);
|
|
270
|
+
await processor.processNewFiles('parquet/');
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Pattern 2: Dead Letter Queue
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
/**
|
|
277
|
+
* Failed file handling with DLQ pattern
|
|
278
|
+
*/
|
|
279
|
+
class ParquetProcessorWithDLQ {
|
|
280
|
+
private parser: ParquetParserService;
|
|
281
|
+
private s3: S3DataSource;
|
|
282
|
+
private dlqBucket: string;
|
|
283
|
+
|
|
284
|
+
constructor(s3Config: any, dlqBucket: string, logger: Logger) {
|
|
285
|
+
this.parser = new ParquetParserService(logger);
|
|
286
|
+
this.s3 = new S3DataSource(
|
|
287
|
+
{
|
|
288
|
+
type: 'S3_CSV',
|
|
289
|
+
s3Config: {
|
|
290
|
+
bucket: process.env.S3_BUCKET!,
|
|
291
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
292
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
293
|
+
region: process.env.AWS_REGION!,
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
logger
|
|
297
|
+
);
|
|
298
|
+
this.dlqBucket = dlqBucket;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
async processWithDLQ(key: string): Promise<{ success: boolean; recordsProcessed?: number }> {
|
|
302
|
+
try {
|
|
303
|
+
const buffer = await this.s3.downloadFile(key, { encoding: 'binary' });
|
|
304
|
+
const arrayBuffer = buffer.buffer.slice(
|
|
305
|
+
buffer.byteOffset,
|
|
306
|
+
buffer.byteOffset + buffer.byteLength
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
const { records, stats } = await this.parser.parse(arrayBuffer, key);
|
|
310
|
+
|
|
311
|
+
// Process records
|
|
312
|
+
await this.processRecords(records);
|
|
313
|
+
|
|
314
|
+
return { success: true, recordsProcessed: stats.totalRecords };
|
|
315
|
+
} catch (error: any) {
|
|
316
|
+
console.error(`❌ Processing failed for ${key}:`, error.message);
|
|
317
|
+
|
|
318
|
+
// Move to DLQ
|
|
319
|
+
await this.moveToDLQ(key, error);
|
|
320
|
+
|
|
321
|
+
return { success: false };
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
private async moveToDLQ(sourceKey: string, error: Error): Promise<void> {
|
|
326
|
+
const dlqKey = `failed/${sourceKey}_${Date.now()}.parquet`;
|
|
327
|
+
|
|
328
|
+
console.log(`📦 Moving to DLQ: ${dlqKey}`);
|
|
329
|
+
|
|
330
|
+
// Create separate S3 instance for DLQ bucket
|
|
331
|
+
const dlqS3 = new S3DataSource(
|
|
332
|
+
{
|
|
333
|
+
type: 'S3_CSV',
|
|
334
|
+
s3Config: {
|
|
335
|
+
bucket: this.dlqBucket,
|
|
336
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
337
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
338
|
+
region: process.env.AWS_REGION!,
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
toStructuredLogger(createConsoleLogger(), {
|
|
342
|
+
logLevel: 'info',
|
|
343
|
+
})
|
|
344
|
+
);
|
|
345
|
+
|
|
346
|
+
try {
|
|
347
|
+
// Copy file to DLQ
|
|
348
|
+
const buffer = await this.s3.downloadFile(sourceKey, { encoding: 'binary' });
|
|
349
|
+
await dlqS3.uploadFile(dlqKey, buffer, {
|
|
350
|
+
contentType: 'application/octet-stream',
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
// Write error metadata
|
|
354
|
+
const errorMetadata = {
|
|
355
|
+
originalKey: sourceKey,
|
|
356
|
+
error: error.message,
|
|
357
|
+
timestamp: new Date().toISOString(),
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
await dlqS3.uploadFile(
|
|
361
|
+
`${dlqKey}.error.json`,
|
|
362
|
+
Buffer.from(JSON.stringify(errorMetadata, null, 2)),
|
|
363
|
+
{ contentType: 'application/json' }
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
console.log(`✅ Moved to DLQ with metadata`);
|
|
367
|
+
} catch (dlqError) {
|
|
368
|
+
console.error(`❌ Failed to move to DLQ:`, dlqError);
|
|
369
|
+
// Alert monitoring system
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
private async processRecords(records: any[]): Promise<void> {
|
|
374
|
+
// Your processing logic
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## Troubleshooting
|
|
382
|
+
|
|
383
|
+
### Common Issues and Solutions
|
|
384
|
+
|
|
385
|
+
#### Issue 1: Out of Memory
|
|
386
|
+
|
|
387
|
+
**Symptoms**: Node.js crashes with `FATAL ERROR: Reached heap limit`
|
|
388
|
+
|
|
389
|
+
**Solutions**:
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
// 1. Enable streaming
|
|
393
|
+
const parser = new ParquetParserService(logger, {
|
|
394
|
+
enableStreaming: true, // ✅ Always enable for large files
|
|
395
|
+
batchSize: 500, // ✅ Smaller batches
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// 2. Process in smaller chunks
|
|
399
|
+
const streamResult = await parser.parseStreaming(arrayBuffer, fileName);
|
|
400
|
+
|
|
401
|
+
for await (const batch of streamResult.records) {
|
|
402
|
+
await processBatch(batch);
|
|
403
|
+
// Batch is released after processing
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// 3. Increase Node.js heap size
|
|
407
|
+
// node --max-old-space-size=4096 script.js
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
#### Issue 2: Slow Parsing
|
|
411
|
+
|
|
412
|
+
**Symptoms**: Processing takes much longer than expected
|
|
413
|
+
|
|
414
|
+
**Diagnostics**:
|
|
415
|
+
|
|
416
|
+
```typescript
|
|
417
|
+
// Check performance metrics
|
|
418
|
+
const { records, stats } = await parser.parse(arrayBuffer, fileName);
|
|
419
|
+
|
|
420
|
+
console.log(`Records/sec: ${stats.recordsPerSecond}`);
|
|
421
|
+
console.log(`Processing time: ${stats.processingTimeMs}ms`);
|
|
422
|
+
console.log(`File size: ${(stats.fileSizeBytes / 1024 / 1024).toFixed(2)}MB`);
|
|
423
|
+
|
|
424
|
+
// Expected: 50,000-70,000 records/sec for typical files
|
|
425
|
+
// If much slower, check:
|
|
426
|
+
// - File compression (GZIP/BROTLI slower than SNAPPY)
|
|
427
|
+
// - Validation enabled (disable for speed)
|
|
428
|
+
// - Batch size too small (increase to 2000-5000)
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
**Solutions**:
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
// 1. Disable validation for trusted sources
|
|
435
|
+
const { records } = await parser.parse(arrayBuffer, fileName, {
|
|
436
|
+
validation: { enabled: false }, // ✅ Faster
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
// 2. Increase batch size
|
|
440
|
+
const parser = new ParquetParserService(logger, {
|
|
441
|
+
batchSize: 5000, // ✅ Larger batches for throughput
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
// 3. Use parallel processing for multiple files
|
|
445
|
+
// See Module 7: Performance Optimization
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
#### Issue 3: Schema Mismatch
|
|
449
|
+
|
|
450
|
+
**Symptoms**: `VALIDATION_ERROR: Required column 'xyz' not found`
|
|
451
|
+
|
|
452
|
+
**Diagnosis**:
|
|
453
|
+
|
|
454
|
+
```typescript
|
|
455
|
+
// Check column statistics
|
|
456
|
+
const { stats } = await parser.parse(arrayBuffer, fileName);
|
|
457
|
+
|
|
458
|
+
console.log('Available columns:');
|
|
459
|
+
Object.keys(stats.columnStats).forEach(col => {
|
|
460
|
+
console.log(` - ${col} (${stats.columnStats[col].type})`);
|
|
461
|
+
});
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
**Solution**:
|
|
465
|
+
|
|
466
|
+
```typescript
|
|
467
|
+
// 1. Update validation config to match actual schema
|
|
468
|
+
const { records } = await parser.parse(arrayBuffer, fileName, {
|
|
469
|
+
validation: {
|
|
470
|
+
enabled: true,
|
|
471
|
+
requiredColumns: ['ref', 'productRef', 'locationRef', 'qty'], // ✅ Correct columns
|
|
472
|
+
allowAdditionalColumns: true, // ✅ Allow extra columns
|
|
473
|
+
},
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
// 2. Or disable validation temporarily
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
#### Issue 4: Corrupted Parquet File
|
|
480
|
+
|
|
481
|
+
**Symptoms**: `PARSING_ERROR: Invalid Parquet magic number`
|
|
482
|
+
|
|
483
|
+
**Solutions**:
|
|
484
|
+
|
|
485
|
+
```typescript
|
|
486
|
+
// 1. Validate file before parsing
|
|
487
|
+
const validationResult = await parser.validateFile(arrayBuffer, fileName);
|
|
488
|
+
|
|
489
|
+
if (!validationResult.isValid) {
|
|
490
|
+
console.error('File is corrupted or not Parquet format');
|
|
491
|
+
// Move to DLQ or alert
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// 2. Check file integrity
|
|
496
|
+
const fileHash = crypto.createHash('md5').update(buffer).digest('hex');
|
|
497
|
+
console.log(`File hash: ${fileHash}`);
|
|
498
|
+
// Compare with expected hash if available
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
---
|
|
502
|
+
|
|
503
|
+
## Monitoring and Logging
|
|
504
|
+
|
|
505
|
+
### Comprehensive Logging
|
|
506
|
+
|
|
507
|
+
```typescript
|
|
508
|
+
class ParquetProcessorWithLogging {
|
|
509
|
+
private parser: ParquetParserService;
|
|
510
|
+
private logger: Logger;
|
|
511
|
+
|
|
512
|
+
constructor() {
|
|
513
|
+
this.logger = toStructuredLogger(createConsoleLogger(), {
|
|
514
|
+
logLevel: 'info',
|
|
515
|
+
});
|
|
516
|
+
this.parser = new ParquetParserService(this.logger, {
|
|
517
|
+
batchSize: 2000,
|
|
518
|
+
enableStreaming: true,
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
async processWithLogging(arrayBuffer: ArrayBuffer, fileName: string): Promise<void> {
|
|
523
|
+
const startTime = Date.now();
|
|
524
|
+
const fileSizeMB = (arrayBuffer.byteLength / 1024 / 1024).toFixed(2);
|
|
525
|
+
|
|
526
|
+
this.logger.info(`📥 Starting: ${fileName} (${fileSizeMB}MB)`);
|
|
527
|
+
|
|
528
|
+
try {
|
|
529
|
+
// Parse
|
|
530
|
+
const { records, stats } = await this.parser.parse(arrayBuffer, fileName);
|
|
531
|
+
|
|
532
|
+
const duration = Date.now() - startTime;
|
|
533
|
+
|
|
534
|
+
// Log success
|
|
535
|
+
this.logger.info(`✅ Success: ${fileName}`, {
|
|
536
|
+
records: stats.totalRecords,
|
|
537
|
+
processingTimeMs: stats.processingTimeMs,
|
|
538
|
+
throughput: stats.recordsPerSecond,
|
|
539
|
+
batches: stats.batchesProcessed,
|
|
540
|
+
totalDuration: duration,
|
|
541
|
+
peakMemoryMB: (stats.peakMemoryUsage / 1024 / 1024).toFixed(2),
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
// Log warnings if any
|
|
545
|
+
if (stats.validation && stats.validation.warnings.length > 0) {
|
|
546
|
+
this.logger.warn(`⚠️ Validation warnings for ${fileName}:`, {
|
|
547
|
+
warnings: stats.validation.warnings,
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
} catch (error: any) {
|
|
551
|
+
const duration = Date.now() - startTime;
|
|
552
|
+
|
|
553
|
+
this.logger.error(`❌ Failed: ${fileName}`, {
|
|
554
|
+
error: error.message,
|
|
555
|
+
code: error.code,
|
|
556
|
+
duration,
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
throw error;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
---
|
|
566
|
+
|
|
567
|
+
## Security Best Practices
|
|
568
|
+
|
|
569
|
+
### 1. Validate File Sources
|
|
570
|
+
|
|
571
|
+
```typescript
|
|
572
|
+
// Only process files from trusted S3 buckets
|
|
573
|
+
const ALLOWED_BUCKETS = ['data-lake-prod', 'data-lake-staging'];
|
|
574
|
+
|
|
575
|
+
async function processSecure(bucket: string, key: string): Promise<void> {
|
|
576
|
+
if (!ALLOWED_BUCKETS.includes(bucket)) {
|
|
577
|
+
throw new Error(`Unauthorized bucket: ${bucket}`);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// Process file
|
|
581
|
+
}
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
### 2. Sanitize File Paths
|
|
585
|
+
|
|
586
|
+
```typescript
|
|
587
|
+
import * as path from 'path';
|
|
588
|
+
|
|
589
|
+
function sanitizePath(filePath: string): string {
|
|
590
|
+
// Prevent directory traversal
|
|
591
|
+
const normalized = path.normalize(filePath);
|
|
592
|
+
|
|
593
|
+
if (normalized.includes('..')) {
|
|
594
|
+
throw new Error('Invalid file path: directory traversal detected');
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
return normalized;
|
|
598
|
+
}
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
### 3. Limit File Size
|
|
602
|
+
|
|
603
|
+
```typescript
|
|
604
|
+
const MAX_FILE_SIZE = 500 * 1024 * 1024; // 500 MB
|
|
605
|
+
|
|
606
|
+
async function processWithSizeLimit(arrayBuffer: ArrayBuffer, fileName: string): Promise<void> {
|
|
607
|
+
if (arrayBuffer.byteLength > MAX_FILE_SIZE) {
|
|
608
|
+
throw new Error(
|
|
609
|
+
`File too large: ${(arrayBuffer.byteLength / 1024 / 1024).toFixed(2)}MB > ${MAX_FILE_SIZE / 1024 / 1024}MB`
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// Process file
|
|
614
|
+
}
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
---
|
|
618
|
+
|
|
619
|
+
## Data Quality Checks
|
|
620
|
+
|
|
621
|
+
```typescript
|
|
622
|
+
/**
|
|
623
|
+
* Validate data quality after parsing
|
|
624
|
+
*/
|
|
625
|
+
function validateDataQuality(
|
|
626
|
+
records: any[],
|
|
627
|
+
stats: ParquetParsingStats
|
|
628
|
+
): { isValid: boolean; issues: string[] } {
|
|
629
|
+
const issues: string[] = [];
|
|
630
|
+
|
|
631
|
+
// Check for sufficient records
|
|
632
|
+
if (records.length === 0) {
|
|
633
|
+
issues.push('No records found in file');
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// Check column completeness
|
|
637
|
+
for (const [colName, colStats] of Object.entries(stats.columnStats)) {
|
|
638
|
+
const completeness = 1 - colStats.nullCount / stats.totalRecords;
|
|
639
|
+
|
|
640
|
+
if (completeness < 0.9) {
|
|
641
|
+
issues.push(`Low completeness for ${colName}: ${(completeness * 100).toFixed(1)}%`);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// Check for duplicate keys
|
|
646
|
+
const refs = records.map(r => r.ref).filter(Boolean);
|
|
647
|
+
const uniqueRefs = new Set(refs);
|
|
648
|
+
|
|
649
|
+
if (refs.length !== uniqueRefs.size) {
|
|
650
|
+
issues.push(`Duplicate refs found: ${refs.length - uniqueRefs.size} duplicates`);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// Business logic checks
|
|
654
|
+
const negativeQty = records.filter(r => r.qty < 0).length;
|
|
655
|
+
if (negativeQty > 0) {
|
|
656
|
+
issues.push(`${negativeQty} records with negative quantities`);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
return {
|
|
660
|
+
isValid: issues.length === 0,
|
|
661
|
+
issues,
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// Usage
|
|
666
|
+
const { records, stats } = await parser.parse(arrayBuffer, fileName);
|
|
667
|
+
const qualityCheck = validateDataQuality(records, stats);
|
|
668
|
+
|
|
669
|
+
if (!qualityCheck.isValid) {
|
|
670
|
+
console.warn('⚠️ Data quality issues:');
|
|
671
|
+
qualityCheck.issues.forEach(issue => console.warn(` - ${issue}`));
|
|
672
|
+
// Decide whether to proceed or reject
|
|
673
|
+
}
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
---
|
|
677
|
+
|
|
678
|
+
## Key Takeaways
|
|
679
|
+
|
|
680
|
+
- 🎯 **Retry logic** - Handle transient failures gracefully
|
|
681
|
+
- 🎯 **Validation first** - Check schema and data quality before processing
|
|
682
|
+
- 🎯 **Idempotent processing** - Use state management to prevent duplicates
|
|
683
|
+
- 🎯 **Dead letter queue** - Capture failed files for manual review
|
|
684
|
+
- 🎯 **Comprehensive logging** - Track success, failures, and performance
|
|
685
|
+
- 🎯 **Security checks** - Validate sources, sanitize paths, limit sizes
|
|
686
|
+
- 🎯 **Monitor metrics** - Track throughput, errors, memory usage
|
|
687
|
+
|
|
688
|
+
---
|
|
689
|
+
|
|
690
|
+
## Summary
|
|
691
|
+
|
|
692
|
+
You've completed the Parquet Processing guide! You now know how to:
|
|
693
|
+
|
|
694
|
+
✅ Understand Parquet format and when to use it
|
|
695
|
+
✅ Parse Parquet files with ParquetParserService
|
|
696
|
+
✅ Handle large files with streaming
|
|
697
|
+
✅ Write Parquet files using parquetjs
|
|
698
|
+
✅ Extract from Fluent Commerce GraphQL to Parquet
|
|
699
|
+
✅ Integrate with S3 for data lake operations
|
|
700
|
+
✅ Optimize performance for large-scale processing
|
|
701
|
+
✅ Apply production-ready error handling and monitoring
|
|
702
|
+
|
|
703
|
+
---
|
|
704
|
+
|
|
705
|
+
[← Previous: Module 7 - Performance Optimization](./03-pattern-guides-parquet-07-performance-optimization.md) | [Back to Guide](../parquet-readme.md)
|
|
706
|
+
|
|
707
|
+
## Related Documentation
|
|
708
|
+
|
|
709
|
+
- [Data Extraction Guide](../../extraction/) - Complete extraction workflows
|
|
710
|
+
- [S3 Complete Guide](../../../02-CORE-GUIDES/data-sources/data-sources-readme.md) - S3 operations
|
|
711
|
+
- [Universal Mapping Guide](../../../02-CORE-GUIDES/mapping/) - Data transformations
|
|
712
|
+
- [Auto-Pagination Guide](../../../02-CORE-GUIDES/auto-pagination/) - GraphQL pagination patterns
|