@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
@@ -1,474 +1,476 @@
1
- # Module 2: Quick Start
2
-
3
- **Get started with universal mapping in 15 minutes**
4
-
5
- **Level:** Beginner
6
- **Time:** 15 minutes
7
- **Prerequisites:** [Module 1: Foundations](./mapping-01-foundations.md)
8
-
9
- ---
10
-
11
- ## Learning Objectives
12
-
13
- After completing this module, you will:
14
-
15
- - ✅ Create your first mapping configuration
16
- - ✅ Use basic SDK resolvers
17
- - ✅ Run a complete CSV ingestion example
18
- - ✅ Handle errors and validate results
19
-
20
- ---
21
-
22
- ## Your First Mapping
23
-
24
- Let's create a simple CSV inventory ingestion mapping.
25
-
26
- ### Step 1: Define the Mapping
27
-
28
- Create `inventory-mapping.json`:
29
-
30
- ```json
31
- {
32
- "fields": {
33
- "ref": {
34
- "source": "sku",
35
- "required": true
36
- },
37
- "productRef": {
38
- "source": "sku",
39
- "required": true
40
- },
41
- "locationRef": {
42
- "source": "location",
43
- "required": true
44
- },
45
- "qty": {
46
- "source": "quantity",
47
- "resolver": "sdk.parseInt",
48
- "required": true
49
- },
50
- "status": {
51
- "source": "status",
52
- "resolver": "sdk.uppercase",
53
- "defaultValue": "AVAILABLE"
54
- }
55
- }
56
- }
57
- ```
58
-
59
- ### Step 2: Prepare Sample Data
60
-
61
- Create `inventory.csv`:
62
-
63
- ```csv
64
- sku,location,quantity,status
65
- SKU-WM-001,WH-001,500,available
66
- SKU-WM-002,WH-001,150,available
67
- SKU-KB-001,WH-001,75,reserved
68
- ```
69
-
70
- ### Step 3: Implement the Workflow
71
-
72
- ```typescript
73
- import { UniversalMapper, CSVParserService, createClient } from '@fluentcommerce/fc-connect-sdk';
74
- import * as fs from 'fs';
75
-
76
- // Load mapping configuration
77
- const mappingConfig = JSON.parse(fs.readFileSync('./inventory-mapping.json', 'utf-8'));
78
-
79
- // Load CSV data
80
- const csvContent = fs.readFileSync('./inventory.csv', 'utf-8');
81
- const parser = new CSVParserService();
82
- const records = await parser.parse(csvContent);
83
-
84
- // Create mapper
85
- const mapper = new UniversalMapper(mappingConfig);
86
-
87
- // Process each CSV row
88
- const batchEntities = [];
89
- for (const row of records) {
90
- const result = await mapper.map(row);
91
-
92
- if (result.success) {
93
- batchEntities.push(result.data);
94
- } else {
95
- console.error('Mapping failed for row:', row);
96
- console.error('Errors:', result.errors);
97
- }
98
- }
99
-
100
- // Send to Fluent Commerce
101
- const client = await createClient({
102
- config: {
103
- baseUrl: process.env.FLUENT_BASE_URL!,
104
- clientId: process.env.FLUENT_CLIENT_ID!,
105
- clientSecret: process.env.FLUENT_CLIENT_SECRET!,
106
- username: process.env.FLUENT_USERNAME!,
107
- password: process.env.FLUENT_PASSWORD!,
108
- retailerId: process.env.FLUENT_RETAILER_ID!,
109
- },
110
- });
111
-
112
- // Create a batch job and send entities
113
- const job = await client.createJob({
114
- name: `inventory-import-${Date.now()}`,
115
- retailerId: process.env.FLUENT_RETAILER_ID!,
116
- });
117
-
118
- await client.sendBatch(job.id, {
119
- action: 'UPSERT',
120
- entityType: 'INVENTORY',
121
- source: 'CSV_IMPORT',
122
- event: 'INVENTORY_UPDATE',
123
- entities: batchEntities,
124
- });
125
-
126
- console.log(`✅ Successfully processed ${batchEntities.length} records`);
127
- ```
128
-
129
- ### Step 4: Run and Validate
130
-
131
- ```bash
132
- npm install @fluentcommerce/fc-connect-sdk
133
- node inventory-import.js
134
- ```
135
-
136
- **Expected Output:**
137
-
138
- ```
139
- ✅ Successfully processed 3 records
140
- ```
141
-
142
- ---
143
-
144
- ## Understanding the Mapping
145
-
146
- Let's break down what each field does:
147
-
148
- ### Field: `ref`
149
-
150
- ```json
151
- "ref": {
152
- "source": "sku",
153
- "required": true
154
- }
155
- ```
156
-
157
- - **source:** Read from `sku` column in CSV
158
- - **required:** Must have a value (fails if missing)
159
- - **Result:** Direct copy of SKU value
160
-
161
- ### Field: `qty`
162
-
163
- ```json
164
- "qty": {
165
- "source": "quantity",
166
- "resolver": "sdk.parseInt",
167
- "required": true
168
- }
169
- ```
170
-
171
- - **source:** Read from `quantity` column
172
- - **resolver:** Convert string "500" to integer 500
173
- - **required:** Must have a value
174
- - **Result:** Parsed integer
175
-
176
- ### Field: `status`
177
-
178
- ```json
179
- "status": {
180
- "source": "status",
181
- "resolver": "sdk.uppercase",
182
- "defaultValue": "AVAILABLE"
183
- }
184
- ```
185
-
186
- - **source:** Read from `status` column
187
- - **resolver:** Convert "available" to "AVAILABLE"
188
- - **defaultValue:** Use "AVAILABLE" if missing
189
- - **Result:** Uppercased string or default
190
-
191
- ---
192
-
193
- ## Common SDK Resolvers
194
-
195
- Here are the most commonly used SDK resolvers:
196
-
197
- ### String Transformations
198
-
199
- ```json
200
- // Uppercase
201
- "status": { "source": "status", "resolver": "sdk.uppercase" }
202
- // "active" → "ACTIVE"
203
-
204
- // Lowercase
205
- "email": { "source": "email", "resolver": "sdk.lowercase" }
206
- // "USER@EXAMPLE.COM" → "user@example.com"
207
-
208
- // Trim whitespace
209
- "name": { "source": "name", "resolver": "sdk.trim" }
210
- // " Product " → "Product"
211
- ```
212
-
213
- ### Number Transformations
214
-
215
- ```json
216
- // Parse integer
217
- "quantity": { "source": "qty", "resolver": "sdk.parseInt" }
218
- // "100" → 100
219
-
220
- // Parse float
221
- "price": { "source": "price", "resolver": "sdk.parseFloat" }
222
- // "19.99" → 19.99
223
- ```
224
-
225
- ### Date Transformations
226
-
227
- ```json
228
- // ISO8601 format
229
- "createdAt": { "source": "date", "resolver": "sdk.formatDate" }
230
- // "2024-01-15" → "2024-01-15T00:00:00.000Z"
231
-
232
- // Short format (YYYY-MM-DD)
233
- "expectedOn": { "source": "date", "resolver": "sdk.formatDateShort" }
234
- // "01/15/2024" → "2024-01-15"
235
- ```
236
-
237
- ---
238
-
239
- ## Error Handling
240
-
241
- Always check mapping results:
242
-
243
- ```typescript
244
- const result = await mapper.map(sourceData);
245
-
246
- if (result.success) {
247
- // ✅ Mapping succeeded
248
- console.log('Mapped data:', result.data);
249
- batchEntities.push(result.data);
250
- } else {
251
- // ❌ Mapping failed
252
- console.error('Mapping failed');
253
- console.error('Errors:', result.errors);
254
- console.error('Source data:', sourceData);
255
-
256
- // Log each error
257
- result.errors.forEach(error => {
258
- console.error(`- Field: ${error.field}`);
259
- console.error(` Message: ${error.message}`);
260
- });
261
- }
262
- ```
263
-
264
- ### Common Errors
265
-
266
- **Missing Required Field:**
267
-
268
- ```
269
- Error: Required field 'productRef' is missing
270
- Source: { sku: '', location: 'WH-001', quantity: '100' }
271
- ```
272
-
273
- **Invalid Number:**
274
-
275
- ```
276
- Error: Failed to parse 'qty' as integer
277
- Source: { quantity: 'invalid' }
278
- ```
279
-
280
- **Resolver Error:**
281
-
282
- ```
283
- Error: Resolver 'sdk.parseInt' failed
284
- Message: Cannot parse 'abc' as integer
285
- ```
286
-
287
- ---
288
-
289
- ## Static Values
290
-
291
- Sometimes you need constant values:
292
-
293
- ```json
294
- {
295
- "fields": {
296
- "type": {
297
- "value": "STANDARD"
298
- },
299
- "retailerId": {
300
- "value": 1
301
- },
302
- "source": {
303
- "value": "CSV_IMPORT"
304
- }
305
- }
306
- }
307
- ```
308
-
309
- **Result:**
310
-
311
- ```json
312
- {
313
- "type": "STANDARD",
314
- "retailerId": 1,
315
- "source": "CSV_IMPORT"
316
- }
317
- ```
318
-
319
- ---
320
-
321
- ## Default Values
322
-
323
- Provide fallbacks for optional fields:
324
-
325
- ```json
326
- {
327
- "fields": {
328
- "status": {
329
- "source": "orderStatus",
330
- "defaultValue": "PENDING"
331
- },
332
- "shippingMethod": {
333
- "source": "shipping",
334
- "defaultValue": "STANDARD"
335
- }
336
- }
337
- }
338
- ```
339
-
340
- **Behavior:**
341
-
342
- - If `orderStatus` exists → use its value
343
- - If `orderStatus` is missing/empty → use "PENDING"
344
-
345
- ---
346
-
347
- ## Combining Resolvers and Defaults
348
-
349
- ```json
350
- {
351
- "fields": {
352
- "quantity": {
353
- "source": "qty",
354
- "resolver": "sdk.parseInt",
355
- "defaultValue": 0
356
- }
357
- }
358
- }
359
- ```
360
-
361
- **Processing order:**
362
-
363
- 1. Read from `qty` field
364
- 2. If missing → use default value `0`
365
- 3. Apply resolver `sdk.parseInt`
366
- 4. Return result
367
-
368
- ---
369
-
370
- ## Practice Exercise
371
-
372
- **Task:** Create a mapping for this customer CSV:
373
-
374
- ```csv
375
- email,first_name,last_name,phone,status
376
- john@example.com,John,Doe,555-1234,active
377
- jane@example.com,Jane,Smith,555-5678,
378
- ```
379
-
380
- **Requirements:**
381
-
382
- 1. Map `email` to lowercase
383
- 2. Combine `first_name` and `last_name` into `fullName`
384
- 3. Format `phone` (you'll need a custom resolver)
385
- 4. Map `status` to uppercase with default "ACTIVE"
386
- 5. Mark `email` as required
387
-
388
- <details>
389
- <summary>Click to see solution</summary>
390
-
391
- ```json
392
- {
393
- "fields": {
394
- "email": {
395
- "source": "email",
396
- "resolver": "sdk.lowercase",
397
- "required": true
398
- },
399
- "firstName": {
400
- "source": "first_name",
401
- "required": true
402
- },
403
- "lastName": {
404
- "source": "last_name",
405
- "required": true
406
- },
407
- "phone": {
408
- "source": "phone",
409
- "resolver": "custom.formatPhone"
410
- },
411
- "status": {
412
- "source": "status",
413
- "resolver": "sdk.uppercase",
414
- "defaultValue": "ACTIVE"
415
- }
416
- }
417
- }
418
- ```
419
-
420
- **Custom resolver for phone formatting:**
421
-
422
- ```typescript
423
- const customResolvers = {
424
- 'custom.formatPhone': (value: any) => {
425
- const digits = String(value).replace(/\D/g, '');
426
- return `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`;
427
- },
428
- };
429
-
430
- const mapper = new UniversalMapper(config, { customResolvers });
431
- ```
432
-
433
- **Note:** Combining `first_name` and `last_name` would also require a custom resolver. See [Module 6: Custom Resolvers](./mapping-06-helpers-resolvers.md) for advanced patterns.
434
-
435
- </details>
436
-
437
- ---
438
-
439
- ## Key Takeaways
440
-
441
- 1. **Start simple:** Basic mappings only need `source` and optional `resolver`
442
- 2. **Mark required fields:** Use `required: true` to catch missing data early
443
- 3. **Use SDK resolvers:** Built-in resolvers cover 90% of common transformations
444
- 4. **Always check results:** Validate `result.success` before using data
445
- 5. **Provide defaults:** Use `defaultValue` for optional fields
446
-
447
- ---
448
-
449
- ## Next Steps
450
-
451
- Now that you can create basic mappings, you're ready to ensure they're correct:
452
-
453
- → Continue to [Module 3: Schema Validation](./mapping-03-schema-validation.md)
454
-
455
- **Alternative paths:**
456
-
457
- - Jump to [Use Cases](./mapping-04-use-cases.md) to see real-world examples
458
- - Review [Advanced Patterns](./mapping-05-advanced-patterns.md) for nested objects and arrays
459
-
460
- ---
461
-
462
- [← Back to Foundations](./mapping-01-foundations.md) | [Next: Schema Validation →](./mapping-03-schema-validation.md)
463
-
464
-
465
-
466
-
467
-
468
-
469
-
470
-
471
-
472
-
473
-
474
-
1
+ # Module 2: Quick Start
2
+
3
+ **Get started with universal mapping in 15 minutes**
4
+
5
+ **Level:** Beginner
6
+ **Time:** 15 minutes
7
+ **Prerequisites:** [Module 1: Foundations](./mapping-01-foundations.md)
8
+
9
+ ---
10
+
11
+ ## Learning Objectives
12
+
13
+ After completing this module, you will:
14
+
15
+ - ✅ Create your first mapping configuration
16
+ - ✅ Use basic SDK resolvers
17
+ - ✅ Run a complete CSV ingestion example
18
+ - ✅ Handle errors and validate results
19
+
20
+ ---
21
+
22
+ ## Your First Mapping
23
+
24
+ Let's create a simple CSV inventory ingestion mapping.
25
+
26
+ ### Step 1: Define the Mapping
27
+
28
+ Create `inventory-mapping.json`:
29
+
30
+ ```json
31
+ {
32
+ "fields": {
33
+ "ref": {
34
+ "source": "sku",
35
+ "required": true
36
+ },
37
+ "productRef": {
38
+ "source": "sku",
39
+ "required": true
40
+ },
41
+ "locationRef": {
42
+ "source": "location",
43
+ "required": true
44
+ },
45
+ "qty": {
46
+ "source": "quantity",
47
+ "resolver": "sdk.parseInt",
48
+ "required": true
49
+ },
50
+ "status": {
51
+ "source": "status",
52
+ "resolver": "sdk.uppercase",
53
+ "defaultValue": "AVAILABLE"
54
+ }
55
+ }
56
+ }
57
+ ```
58
+
59
+ ### Step 2: Prepare Sample Data
60
+
61
+ Create `inventory.csv`:
62
+
63
+ ```csv
64
+ sku,location,quantity,status
65
+ SKU-WM-001,WH-001,500,available
66
+ SKU-WM-002,WH-001,150,available
67
+ SKU-KB-001,WH-001,75,reserved
68
+ ```
69
+
70
+ ### Step 3: Implement the Workflow
71
+
72
+ ```typescript
73
+ import { UniversalMapper, CSVParserService, createClient } from '@fluentcommerce/fc-connect-sdk';
74
+ import * as fs from 'fs';
75
+
76
+ // Load mapping configuration
77
+ const mappingConfig = JSON.parse(fs.readFileSync('./inventory-mapping.json', 'utf-8'));
78
+
79
+ // Load CSV data
80
+ const csvContent = fs.readFileSync('./inventory.csv', 'utf-8');
81
+ const parser = new CSVParserService();
82
+ const records = await parser.parse(csvContent);
83
+
84
+ // Create mapper
85
+ const mapper = new UniversalMapper(mappingConfig);
86
+
87
+ // Process each CSV row
88
+ const batchEntities = [];
89
+ for (const row of records) {
90
+ const result = await mapper.map(row);
91
+
92
+ if (result.success) {
93
+ batchEntities.push(result.data);
94
+ } else {
95
+ console.error('Mapping failed for row:', row);
96
+ console.error('Errors:', result.errors);
97
+ }
98
+ }
99
+
100
+ // Send to Fluent Commerce
101
+ const client = await createClient({
102
+ config: {
103
+ baseUrl: process.env.FLUENT_BASE_URL!,
104
+ clientId: process.env.FLUENT_CLIENT_ID!,
105
+ clientSecret: process.env.FLUENT_CLIENT_SECRET!,
106
+ username: process.env.FLUENT_USERNAME!,
107
+ password: process.env.FLUENT_PASSWORD!,
108
+ retailerId: process.env.FLUENT_RETAILER_ID!,
109
+ },
110
+ });
111
+
112
+ // Create a batch job and send entities
113
+ const job = await client.createJob({
114
+ name: `inventory-import-${Date.now()}`,
115
+ retailerId: process.env.FLUENT_RETAILER_ID!,
116
+ });
117
+
118
+ await client.sendBatch(job.id, {
119
+ action: 'UPSERT',
120
+ entityType: 'INVENTORY',
121
+ source: 'CSV_IMPORT',
122
+ event: 'INVENTORY_UPDATE',
123
+ entities: batchEntities,
124
+ });
125
+
126
+ console.log(`✅ Successfully processed ${batchEntities.length} records`);
127
+ ```
128
+
129
+ ### Step 4: Run and Validate
130
+
131
+ ```bash
132
+ npm install @fluentcommerce/fc-connect-sdk
133
+ node inventory-import.js
134
+ ```
135
+
136
+ **Expected Output:**
137
+
138
+ ```
139
+ ✅ Successfully processed 3 records
140
+ ```
141
+
142
+ ---
143
+
144
+ ## Understanding the Mapping
145
+
146
+ Let's break down what each field does:
147
+
148
+ ### Field: `ref`
149
+
150
+ ```json
151
+ "ref": {
152
+ "source": "sku",
153
+ "required": true
154
+ }
155
+ ```
156
+
157
+ - **source:** Read from `sku` column in CSV
158
+ - **required:** Must have a value (fails if missing)
159
+ - **Result:** Direct copy of SKU value
160
+
161
+ ### Field: `qty`
162
+
163
+ ```json
164
+ "qty": {
165
+ "source": "quantity",
166
+ "resolver": "sdk.parseInt",
167
+ "required": true
168
+ }
169
+ ```
170
+
171
+ - **source:** Read from `quantity` column
172
+ - **resolver:** Convert string "500" to integer 500
173
+ - **required:** Must have a value
174
+ - **Result:** Parsed integer
175
+
176
+ ### Field: `status`
177
+
178
+ ```json
179
+ "status": {
180
+ "source": "status",
181
+ "resolver": "sdk.uppercase",
182
+ "defaultValue": "AVAILABLE"
183
+ }
184
+ ```
185
+
186
+ - **source:** Read from `status` column
187
+ - **resolver:** Convert "available" to "AVAILABLE"
188
+ - **defaultValue:** Use "AVAILABLE" if missing
189
+ - **Result:** Uppercased string or default
190
+
191
+ ---
192
+
193
+ ## Common SDK Resolvers
194
+
195
+ Here are the most commonly used SDK resolvers:
196
+
197
+ ### String Transformations
198
+
199
+ ```json
200
+ // Uppercase
201
+ "status": { "source": "status", "resolver": "sdk.uppercase" }
202
+ // "active" → "ACTIVE"
203
+
204
+ // Lowercase
205
+ "email": { "source": "email", "resolver": "sdk.lowercase" }
206
+ // "USER@EXAMPLE.COM" → "user@example.com"
207
+
208
+ // Trim whitespace
209
+ "name": { "source": "name", "resolver": "sdk.trim" }
210
+ // " Product " → "Product"
211
+ ```
212
+
213
+ ### Number Transformations
214
+
215
+ ```json
216
+ // Parse integer
217
+ "quantity": { "source": "qty", "resolver": "sdk.parseInt" }
218
+ // "100" → 100
219
+
220
+ // Parse float
221
+ "price": { "source": "price", "resolver": "sdk.parseFloat" }
222
+ // "19.99" → 19.99
223
+ ```
224
+
225
+ ### Date Transformations
226
+
227
+ ```json
228
+ // ISO8601 format
229
+ "createdAt": { "source": "date", "resolver": "sdk.formatDate" }
230
+ // "2024-01-15" → "2024-01-15T00:00:00.000Z"
231
+
232
+ // Short format (YYYY-MM-DD)
233
+ "expectedOn": { "source": "date", "resolver": "sdk.formatDateShort" }
234
+ // "01/15/2024" → "2024-01-15"
235
+ ```
236
+
237
+ ---
238
+
239
+ ## Error Handling
240
+
241
+ Always check mapping results:
242
+
243
+ ```typescript
244
+ const result = await mapper.map(sourceData);
245
+
246
+ if (result.success) {
247
+ // ✅ Mapping succeeded
248
+ console.log('Mapped data:', result.data);
249
+ batchEntities.push(result.data);
250
+ } else {
251
+ // ❌ Mapping failed
252
+ console.error('Mapping failed');
253
+ console.error('Errors:', result.errors);
254
+ console.error('Source data:', sourceData);
255
+
256
+ // Log each error
257
+ result.errors.forEach(error => {
258
+ console.error(`- Field: ${error.field}`);
259
+ console.error(` Message: ${error.message}`);
260
+ });
261
+ }
262
+ ```
263
+
264
+ ### Common Errors
265
+
266
+ **Missing Required Field:**
267
+
268
+ ```
269
+ Error: Required field 'productRef' is missing
270
+ Source: { sku: '', location: 'WH-001', quantity: '100' }
271
+ ```
272
+
273
+ **Invalid Number:**
274
+
275
+ ```
276
+ Error: Failed to parse 'qty' as integer
277
+ Source: { quantity: 'invalid' }
278
+ ```
279
+
280
+ **Resolver Error:**
281
+
282
+ ```
283
+ Error: Resolver 'sdk.parseInt' failed
284
+ Message: Cannot parse 'abc' as integer
285
+ ```
286
+
287
+ ---
288
+
289
+ ## Static Values
290
+
291
+ Sometimes you need constant values:
292
+
293
+ ```json
294
+ {
295
+ "fields": {
296
+ "type": {
297
+ "value": "STANDARD"
298
+ },
299
+ "retailerId": {
300
+ "value": 1
301
+ },
302
+ "source": {
303
+ "value": "CSV_IMPORT"
304
+ }
305
+ }
306
+ }
307
+ ```
308
+
309
+ **Result:**
310
+
311
+ ```json
312
+ {
313
+ "type": "STANDARD",
314
+ "retailerId": 1,
315
+ "source": "CSV_IMPORT"
316
+ }
317
+ ```
318
+
319
+ ---
320
+
321
+ ## Default Values
322
+
323
+ Provide fallbacks for optional fields:
324
+
325
+ ```json
326
+ {
327
+ "fields": {
328
+ "status": {
329
+ "source": "orderStatus",
330
+ "defaultValue": "PENDING"
331
+ },
332
+ "shippingMethod": {
333
+ "source": "shipping",
334
+ "defaultValue": "STANDARD"
335
+ }
336
+ }
337
+ }
338
+ ```
339
+
340
+ **Behavior:**
341
+
342
+ - If `orderStatus` exists → use its value
343
+ - If `orderStatus` is missing/empty → use "PENDING"
344
+
345
+ ---
346
+
347
+ ## Combining Resolvers and Defaults
348
+
349
+ ```json
350
+ {
351
+ "fields": {
352
+ "quantity": {
353
+ "source": "qty",
354
+ "resolver": "sdk.parseInt",
355
+ "defaultValue": 0
356
+ }
357
+ }
358
+ }
359
+ ```
360
+
361
+ **Processing order:**
362
+
363
+ 1. Read from `qty` field
364
+ 2. If missing → use default value `0`
365
+ 3. Apply resolver `sdk.parseInt`
366
+ 4. Return result
367
+
368
+ ---
369
+
370
+ ## Practice Exercise
371
+
372
+ **Task:** Create a mapping for this customer CSV:
373
+
374
+ ```csv
375
+ email,first_name,last_name,phone,status
376
+ john@example.com,John,Doe,555-1234,active
377
+ jane@example.com,Jane,Smith,555-5678,
378
+ ```
379
+
380
+ **Requirements:**
381
+
382
+ 1. Map `email` to lowercase
383
+ 2. Combine `first_name` and `last_name` into `fullName`
384
+ 3. Format `phone` (you'll need a custom resolver)
385
+ 4. Map `status` to uppercase with default "ACTIVE"
386
+ 5. Mark `email` as required
387
+
388
+ <details>
389
+ <summary>Click to see solution</summary>
390
+
391
+ ```json
392
+ {
393
+ "fields": {
394
+ "email": {
395
+ "source": "email",
396
+ "resolver": "sdk.lowercase",
397
+ "required": true
398
+ },
399
+ "firstName": {
400
+ "source": "first_name",
401
+ "required": true
402
+ },
403
+ "lastName": {
404
+ "source": "last_name",
405
+ "required": true
406
+ },
407
+ "phone": {
408
+ "source": "phone",
409
+ "resolver": "custom.formatPhone"
410
+ },
411
+ "status": {
412
+ "source": "status",
413
+ "resolver": "sdk.uppercase",
414
+ "defaultValue": "ACTIVE"
415
+ }
416
+ }
417
+ }
418
+ ```
419
+
420
+ **Custom resolver for phone formatting:**
421
+
422
+ ```typescript
423
+ const customResolvers = {
424
+ 'custom.formatPhone': (value: any) => {
425
+ const digits = String(value).replace(/\D/g, '');
426
+ return `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`;
427
+ },
428
+ };
429
+
430
+ const mapper = new UniversalMapper(config, { customResolvers });
431
+ ```
432
+
433
+ **Note:** Combining `first_name` and `last_name` would also require a custom resolver. See [Module 6: Custom Resolvers](./mapping-06-helpers-resolvers.md) for advanced patterns.
434
+
435
+ </details>
436
+
437
+ ---
438
+
439
+ ## Key Takeaways
440
+
441
+ 1. **Start simple:** Basic mappings only need `source` and optional `resolver`
442
+ 2. **Mark required fields:** Use `required: true` to catch missing data early
443
+ 3. **Use SDK resolvers:** Built-in resolvers cover 90% of common transformations
444
+ 4. **Always check results:** Validate `result.success` before using data
445
+ 5. **Provide defaults:** Use `defaultValue` for optional fields
446
+
447
+ ---
448
+
449
+ ## Next Steps
450
+
451
+ Now that you can create basic mappings, you're ready to ensure they're correct:
452
+
453
+ → Continue to [Module 3: Schema Validation](./mapping-03-schema-validation.md)
454
+
455
+ **Alternative paths:**
456
+
457
+ - Jump to [Use Cases](./mapping-04-use-cases.md) to see real-world examples
458
+ - Review [Advanced Patterns](./mapping-05-advanced-patterns.md) for nested objects and arrays
459
+
460
+ ---
461
+
462
+ [← Back to Foundations](./mapping-01-foundations.md) | [Next: Schema Validation →](./mapping-03-schema-validation.md)
463
+
464
+
465
+
466
+
467
+
468
+
469
+
470
+
471
+
472
+
473
+
474
+
475
+
476
+