@fluentcommerce/fc-connect-sdk 0.1.48 → 0.1.52

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +506 -379
  2. package/README.md +343 -0
  3. package/dist/cjs/clients/fluent-client.js +110 -14
  4. package/dist/cjs/data-sources/s3-data-source.js +1 -1
  5. package/dist/cjs/data-sources/sftp-data-source.js +1 -1
  6. package/dist/cjs/index.d.ts +1 -1
  7. package/dist/cjs/services/extraction/extraction-orchestrator.d.ts +4 -1
  8. package/dist/cjs/services/extraction/extraction-orchestrator.js +84 -11
  9. package/dist/cjs/types/index.d.ts +79 -10
  10. package/dist/cjs/versori/fluent-versori-client.d.ts +4 -1
  11. package/dist/cjs/versori/fluent-versori-client.js +131 -13
  12. package/dist/esm/clients/fluent-client.js +110 -14
  13. package/dist/esm/data-sources/s3-data-source.js +1 -1
  14. package/dist/esm/data-sources/sftp-data-source.js +1 -1
  15. package/dist/esm/index.d.ts +1 -1
  16. package/dist/esm/services/extraction/extraction-orchestrator.d.ts +4 -1
  17. package/dist/esm/services/extraction/extraction-orchestrator.js +84 -11
  18. package/dist/esm/types/index.d.ts +79 -10
  19. package/dist/esm/versori/fluent-versori-client.d.ts +4 -1
  20. package/dist/esm/versori/fluent-versori-client.js +131 -13
  21. package/dist/tsconfig.esm.tsbuildinfo +1 -1
  22. package/dist/tsconfig.tsbuildinfo +1 -1
  23. package/dist/tsconfig.types.tsbuildinfo +1 -1
  24. package/dist/types/index.d.ts +1 -1
  25. package/dist/types/services/extraction/extraction-orchestrator.d.ts +4 -1
  26. package/dist/types/types/index.d.ts +79 -10
  27. package/dist/types/versori/fluent-versori-client.d.ts +4 -1
  28. package/docs/02-CORE-GUIDES/api-reference/event-api-input-output-reference.md +478 -18
  29. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-01-client-api.md +83 -0
  30. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-08-types.md +52 -0
  31. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-partial-responses.md +212 -0
  32. package/docs/02-CORE-GUIDES/api-reference/readme.md +1 -1
  33. package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-08-extraction-orchestrator.md +68 -4
  34. package/docs/02-CORE-GUIDES/mapping/modules/mapping-01-foundations.md +450 -448
  35. package/docs/02-CORE-GUIDES/mapping/modules/mapping-02-quick-start.md +476 -474
  36. package/docs/02-CORE-GUIDES/mapping/modules/mapping-03-schema-validation.md +464 -462
  37. package/docs/02-CORE-GUIDES/mapping/modules/mapping-05-advanced-patterns.md +1366 -1364
  38. package/docs/readme.md +245 -245
  39. package/package.json +17 -6
  40. package/docs/versori-apis/ACTIVATIONS-AND-VARIABLES-GUIDE.md +0 -60
  41. package/docs/versori-apis/JWT-GENERATION-GUIDE.md +0 -94
  42. package/docs/versori-apis/QUICK-WORKFLOW.md +0 -293
  43. package/docs/versori-apis/README.md +0 -73
  44. package/docs/versori-apis/VERSORI-PLATFORM-ARCHITECTURE.md +0 -880
  45. package/docs/versori-apis/Versori-Platform-API.postman_collection.json +0 -2925
  46. package/docs/versori-apis/Versori-Platform-API.postman_environment.example.json +0 -62
  47. package/docs/versori-apis/Versori-Platform-API.postman_environment.json +0 -178
@@ -77,6 +77,58 @@ if (result.errors) {
77
77
  }
78
78
  ```
79
79
 
80
+ ### FluentEventQueryParams
81
+
82
+ Query params for Event Log/Audit search (`GET /api/v4.1/event`).
83
+
84
+ ```typescript
85
+ interface FluentEventQueryParams {
86
+ start?: number;
87
+ count?: number;
88
+ from?: string;
89
+ to?: string;
90
+ name?: string;
91
+ category?: string;
92
+ retailerId?: string | number;
93
+ eventType?: string;
94
+ eventStatus?: string;
95
+ 'context.rootEntityType'?: string;
96
+ 'context.rootEntityId'?: string | number;
97
+ 'context.rootEntityRef'?: string;
98
+ 'context.entityType'?: string;
99
+ 'context.entityId'?: string | number;
100
+ 'context.entityRef'?: string;
101
+ }
102
+ ```
103
+
104
+ ### FluentEventLogResponse
105
+
106
+ Response from Event Log search (`client.getEvents()`).
107
+
108
+ ```typescript
109
+ interface FluentEventLogResponse {
110
+ start: number;
111
+ count: number;
112
+ hasMore: boolean;
113
+ results: FluentEventLogItem[];
114
+ }
115
+
116
+ interface FluentEventLogItem {
117
+ id: string;
118
+ name: string;
119
+ type?: string;
120
+ accountId?: string;
121
+ retailerId?: string;
122
+ category?: string;
123
+ context?: FluentEventLogContext;
124
+ eventStatus?: string;
125
+ attributes?: FluentEventLogAttribute[] | Record<string, AttributeValue> | null;
126
+ source?: string | null;
127
+ generatedBy?: string;
128
+ generatedOn?: string;
129
+ }
130
+ ```
131
+
80
132
  ## Enums
81
133
 
82
134
  ### BatchAction
@@ -0,0 +1,212 @@
1
+ # GraphQL Partial Response Support
2
+
3
+ > **Version:** 0.1.51+
4
+ > **Status:** Stable
5
+
6
+ ## Overview
7
+
8
+ By default, the SDK throws a `GraphQLExecutionError` when a GraphQL response contains any errors. This "fail-fast" behavior is intentional for data integration scenarios where partial data could lead to inconsistencies.
9
+
10
+ However, GraphQL allows responses to contain **both** `data` AND `errors` simultaneously (partial responses). This feature allows you to opt-in to receiving partial data when appropriate for your use case.
11
+
12
+ ## Default Behavior (Backward Compatible)
13
+
14
+ ```typescript
15
+ // Default: throws on ANY GraphQL errors
16
+ const result = await client.graphql({
17
+ query: `
18
+ mutation {
19
+ product1: createProduct(input: { ref: "SKU1" }) { id }
20
+ product2: createProduct(input: { ref: "SKU2" }) { id }
21
+ }
22
+ `
23
+ });
24
+ // If product2 fails, throws GraphQLExecutionError
25
+ // Data from product1 is lost
26
+ ```
27
+
28
+ ## Opt-In Partial Response Mode
29
+
30
+ ```typescript
31
+ // Partial mode: returns both data and errors
32
+ const result = await client.graphql({
33
+ query: `
34
+ mutation {
35
+ product1: createProduct(input: { ref: "SKU1" }) { id }
36
+ product2: createProduct(input: { ref: "SKU2" }) { id }
37
+ }
38
+ `,
39
+ errorHandling: 'partial' // <-- Opt-in
40
+ });
41
+
42
+ // Check for partial success
43
+ if (result.hasPartialData) {
44
+ console.log('Partial success:', {
45
+ data: result.data, // { product1: { id: '123' }, product2: null }
46
+ errors: result.errors, // [{ message: 'SKU2 already exists', path: ['product2'] }]
47
+ errorCount: result.errors?.length,
48
+ });
49
+
50
+ // Process successful items
51
+ if (result.data?.product1) {
52
+ console.log('Product 1 created:', result.data.product1.id);
53
+ }
54
+
55
+ // Log failed items for retry or investigation
56
+ for (const error of result.errors || []) {
57
+ console.warn('Failed mutation:', error.path?.[0], error.message);
58
+ }
59
+ }
60
+ ```
61
+
62
+ ## API Reference
63
+
64
+ ### GraphQLPayload Options
65
+
66
+ ```typescript
67
+ interface GraphQLPayload {
68
+ query: string;
69
+ variables?: Record<string, any>;
70
+ operationName?: string;
71
+ pagination?: PaginationConfig;
72
+
73
+ /**
74
+ * How to handle GraphQL errors in the response.
75
+ * @default 'throw' - Throws GraphQLExecutionError on any errors
76
+ * @since 0.1.51 Set to 'partial' to receive both data and errors
77
+ */
78
+ errorHandling?: 'throw' | 'partial';
79
+ }
80
+ ```
81
+
82
+ ### GraphQLResponse Extensions
83
+
84
+ ```typescript
85
+ interface GraphQLResponse<T> {
86
+ data?: T;
87
+ errors?: GraphQLError[];
88
+ extensions?: Record<string, unknown>;
89
+
90
+ /**
91
+ * True when response contains both data AND errors.
92
+ * Only present when errorHandling: 'partial' is used and errors occurred.
93
+ * @since 0.1.51
94
+ */
95
+ hasPartialData?: boolean;
96
+ }
97
+ ```
98
+
99
+ ## With Pagination
100
+
101
+ Partial response mode works with auto-pagination. Errors from all pages are accumulated:
102
+
103
+ ```typescript
104
+ const result = await client.graphql({
105
+ query: PAGINATED_QUERY,
106
+ variables: { first: 100 },
107
+ errorHandling: 'partial'
108
+ });
109
+
110
+ // Errors from all pages are collected
111
+ if (result.hasPartialData) {
112
+ console.log(`Fetched data with ${result.errors?.length} errors across pages`);
113
+ }
114
+ ```
115
+
116
+ ## With ExtractionOrchestrator
117
+
118
+ The ExtractionOrchestrator also supports partial responses:
119
+
120
+ ```typescript
121
+ const orchestrator = new ExtractionOrchestrator(client, logger);
122
+
123
+ const result = await orchestrator.extract({
124
+ query: PRODUCTS_QUERY,
125
+ resultPath: 'products.edges.node',
126
+ errorHandling: 'partial', // Continue extraction even with errors
127
+ });
128
+
129
+ // Check for partial errors in stats
130
+ if (result.stats.partialErrors) {
131
+ logger.warn('Extraction completed with errors', {
132
+ recordsExtracted: result.data.length,
133
+ errorCount: result.stats.partialErrors.length,
134
+ });
135
+ }
136
+ ```
137
+
138
+ ### Edge Case: Null Data with Errors
139
+
140
+ When GraphQL returns errors with `data: null` or `data: undefined`, the ExtractionOrchestrator handles this gracefully in partial mode:
141
+
142
+ ```typescript
143
+ // GraphQL response: { data: null, errors: [{ message: 'Order item not found' }] }
144
+ const result = await orchestrator.extract({
145
+ query: ORDERS_QUERY,
146
+ resultPath: 'orders.edges.node',
147
+ errorHandling: 'partial',
148
+ });
149
+
150
+ // Returns empty array with errors in stats
151
+ expect(result.data).toEqual([]);
152
+ expect(result.stats.partialErrors).toHaveLength(1);
153
+ ```
154
+
155
+ **Behavior:**
156
+ - **Partial mode**: Returns empty array `[]`, accumulates errors in `stats.partialErrors`, stops pagination if no data
157
+ - **Throw mode** (default): Throws `GraphQLExecutionError` immediately (backward compatible)
158
+
159
+ This ensures that partial mode never throws due to null data - it always returns available data (even if empty) along with accumulated errors.
160
+
161
+ ## When to Use Partial Responses
162
+
163
+ | Scenario | Recommendation |
164
+ |----------|----------------|
165
+ | Single record operations | Use default `'throw'` |
166
+ | Batch mutations where failures are independent | Consider `'partial'` |
167
+ | Read-only queries where partial data is useful | Consider `'partial'` |
168
+ | Critical data where consistency matters | Use default `'throw'` |
169
+ | Reporting/analytics where some data is better than none | Consider `'partial'` |
170
+
171
+ ## Error Classification
172
+
173
+ When using partial mode, you can classify errors for better handling:
174
+
175
+ ```typescript
176
+ import { classifyErrors } from '@fluentcommerce/fc-connect-sdk';
177
+
178
+ const result = await client.graphql({
179
+ query: batchMutation,
180
+ errorHandling: 'partial'
181
+ });
182
+
183
+ if (result.hasPartialData && result.errors) {
184
+ const classified = classifyErrors(result.errors);
185
+
186
+ // Handle retryable errors
187
+ if (classified.retryable.length > 0) {
188
+ console.log('Retryable errors:', classified.retryable);
189
+ }
190
+
191
+ // Handle permanent failures
192
+ if (classified.permanent.length > 0) {
193
+ console.log('Permanent failures:', classified.permanent);
194
+ }
195
+ }
196
+ ```
197
+
198
+ ## Migration Guide
199
+
200
+ No migration needed - the default behavior remains unchanged. To adopt partial responses:
201
+
202
+ 1. Add `errorHandling: 'partial'` to your GraphQL calls
203
+ 2. Check `result.hasPartialData` to detect partial success
204
+ 3. Handle `result.errors` array for failed operations
205
+ 4. Update your logging and monitoring to track partial successes
206
+
207
+ ## Best Practices
208
+
209
+ 1. **Always check `hasPartialData`**: Don't assume success just because no exception was thrown
210
+ 2. **Log partial errors**: Even in partial mode, errors should be logged for investigation
211
+ 3. **Consider retry logic**: Partial failures may benefit from retrying failed items
212
+ 4. **Monitor partial success rates**: Track how often partial responses occur in your workflows
@@ -68,7 +68,7 @@ import {
68
68
 
69
69
  The main entry point for SDK operations. Learn how to create clients, execute GraphQL operations, send events, and manage batch jobs.
70
70
 
71
- **Key APIs:** `createClient()`, `FluentClient`, `graphql(payload)`, `sendEvent()`, `createJob()`, `sendBatch()`
71
+ **Key APIs:** `createClient()`, `FluentClient`, `graphql(payload)`, `sendEvent()`, `getEvents()`, `getEventById()`, `createJob()`, `sendBatch()`
72
72
 
73
73
  [View Module →](./modules/api-reference-01-client-api.md)
74
74
 
@@ -290,13 +290,14 @@ interface ExtractionOptions<T = any> {
290
290
  timeout?: number; // Query timeout ms (default: 60000)
291
291
  direction?: 'forward' | 'backward'; // Pagination direction (default: 'forward')
292
292
  validateItem?: (item: T) => boolean; // Optional per-item validation
293
+ errorHandling?: 'throw' | 'partial'; // How to handle GraphQL errors (default: 'throw')
294
+ operationName?: string; // Optional operation name for logging
293
295
  }
294
296
  ```
295
297
 
296
- **Note on `direction`:**
297
- - `'forward'` (default): Uses `first/after` params, requires `pageInfo.hasNextPage`
298
- - `'backward'`: Uses `last/before` params, requires `pageInfo.hasPreviousPage`
299
- - Your GraphQL query must match the direction (don't mix `first/after` with `direction: 'backward'`)
298
+ **Notes:**
299
+ - `direction`: `'forward'` (default) uses `first/after` params, requires `pageInfo.hasNextPage`. `'backward'` uses `last/before` params, requires `pageInfo.hasPreviousPage`. Your GraphQL query must match the direction (don't mix `first/after` with `direction: 'backward'`)
300
+ - `errorHandling`: `'throw'` (default) throws `GraphQLExecutionError` on any errors. `'partial'` continues extraction and accumulates errors in `stats.partialErrors`. See [Partial Response Support](../../api-reference/modules/api-reference-12-partial-responses.md) for details.
300
301
 
301
302
  ### ExtractionResult
302
303
 
@@ -316,6 +317,7 @@ interface ExtractionStats {
316
317
  validRecords?: number; // Valid records (if validation used)
317
318
  invalidRecords?: number; // Invalid records (if validation used)
318
319
  direction?: 'forward' | 'backward'; // Pagination direction used
320
+ partialErrors?: GraphQLError[]; // GraphQL errors (only when errorHandling: 'partial')
319
321
  }
320
322
 
321
323
  interface ExtractionError {
@@ -715,6 +717,68 @@ try {
715
717
  }
716
718
  ```
717
719
 
720
+ ### Partial Response Mode (errorHandling: 'partial')
721
+
722
+ When GraphQL returns errors but some data is available, you can use partial mode to continue extraction:
723
+
724
+ ```typescript
725
+ const result = await orchestrator.extract({
726
+ query: ORDERS_QUERY,
727
+ resultPath: 'orders.edges.node',
728
+ errorHandling: 'partial', // Continue extraction even with errors
729
+ });
730
+
731
+ // Check for partial errors in stats
732
+ if (result.stats.partialErrors) {
733
+ logger.warn('Extraction completed with partial errors', {
734
+ recordsExtracted: result.data.length,
735
+ errorCount: result.stats.partialErrors.length,
736
+ });
737
+
738
+ // Process available data
739
+ console.log(`Extracted ${result.data.length} records despite errors`);
740
+
741
+ // Log errors for investigation
742
+ result.stats.partialErrors.forEach(err => {
743
+ logger.warn('GraphQL error during extraction', {
744
+ message: err.message,
745
+ path: err.path,
746
+ });
747
+ });
748
+ }
749
+ ```
750
+
751
+ **Edge Case: Null Data with Errors**
752
+
753
+ When GraphQL returns errors with `data: null` or `data: undefined`:
754
+
755
+ ```typescript
756
+ // GraphQL response: { data: null, errors: [{ message: 'Order item not found' }] }
757
+ const result = await orchestrator.extract({
758
+ query: ORDERS_QUERY,
759
+ resultPath: 'orders.edges.node',
760
+ errorHandling: 'partial',
761
+ });
762
+
763
+ // Returns empty array with errors in stats
764
+ expect(result.data).toEqual([]);
765
+ expect(result.stats.partialErrors).toHaveLength(1);
766
+ ```
767
+
768
+ **Behavior:**
769
+ - **Partial mode**: Returns empty array `[]`, accumulates errors in `stats.partialErrors`, stops pagination if no data
770
+ - **Throw mode** (default): Throws `GraphQLExecutionError` immediately (backward compatible)
771
+
772
+ **When to Use Partial Mode:**
773
+ - ✅ Batch queries where some items may fail independently
774
+ - ✅ Reporting/analytics where partial data is better than none
775
+ - ✅ Non-critical extractions where you want to process available data
776
+
777
+ **When to Use Throw Mode (default):**
778
+ - ✅ Critical data where consistency matters
779
+ - ✅ Single record operations
780
+ - ✅ When you need to fail fast on any errors
781
+
718
782
  ### Common Error Scenarios
719
783
 
720
784
  | Error | Cause | Solution |