0
# Synchronous API
1
2
Synchronous data transformation for immediate processing of data arrays. The sync API provides blocking execution with direct return values, ideal for small datasets where simplicity and immediate results are prioritized.
3
4
## Capabilities
5
6
### Synchronous Transform Function
7
8
Transform an array of records synchronously with immediate results.
9
10
```javascript { .api }
11
/**
12
* Transform records synchronously
13
* @param records - Array of data to transform
14
* @param handler - Synchronous function to transform each record
15
* @returns Array of transformed results
16
*/
17
function transform<T, U>(records: Array<T>, handler: Handler<T, U>): Array<U>;
18
19
/**
20
* Transform with options synchronously
21
* @param records - Array of data to transform
22
* @param options - Configuration options (limited subset)
23
* @param handler - Synchronous function to transform each record
24
* @returns Array of transformed results
25
*/
26
function transform<T, U>(
27
records: Array<T>,
28
options: Options,
29
handler: Handler<T, U>
30
): Array<U>;
31
32
/**
33
* Synchronous handler type - must return result directly
34
*/
35
type Handler<T = any, U = any> = (record: T, params?: any) => U;
36
```
37
38
**Module Import:**
39
40
```javascript
41
// Import from sync module
42
import { transform } from "stream-transform/sync";
43
44
// CommonJS
45
const { transform } = require("stream-transform/sync");
46
47
// Browser ESM
48
import { transform } from "stream-transform/browser/esm/sync";
49
```
50
51
**Usage Examples:**
52
53
```javascript
54
import { transform } from "stream-transform/sync";
55
56
// Basic synchronous transformation
57
const numbers = [1, 2, 3, 4, 5];
58
const doubled = transform(numbers, (num) => num * 2);
59
console.log(doubled); // [2, 4, 6, 8, 10]
60
61
// Object transformation
62
const users = [
63
{ name: " Alice ", age: "25" },
64
{ name: " Bob ", age: "30" }
65
];
66
67
const cleanUsers = transform(users, (user) => ({
68
name: user.name.trim(),
69
age: parseInt(user.age),
70
processed: true
71
}));
72
73
console.log(cleanUsers);
74
// [
75
// { name: "Alice", age: 25, processed: true },
76
// { name: "Bob", age: 30, processed: true }
77
// ]
78
79
// Array manipulation
80
const records = [
81
["a", "b", "c"],
82
["1", "2", "3"],
83
["x", "y", "z"]
84
];
85
86
const joined = transform(records, (record) => record.join("|"));
87
console.log(joined); // ["a|b|c", "1|2|3", "x|y|z"]
88
```
89
90
### Transform with Options
91
92
Enhanced synchronous transformation with configuration options.
93
94
```javascript { .api }
95
/**
96
* Transform with options synchronously
97
* @param records - Array of data to transform
98
* @param options - Configuration options
99
* @param handler - Synchronous transformation function
100
* @returns Array of transformed results
101
*/
102
function transform<T, U>(
103
records: Array<T>,
104
options: Options,
105
handler: Handler<T, U>
106
): Array<U>;
107
108
interface Options {
109
/** User-defined parameters passed to handler function */
110
params?: any;
111
}
112
```
113
114
**Usage Examples:**
115
116
```javascript
117
import { transform } from "stream-transform/sync";
118
119
// Using custom parameters
120
const data = [
121
{ value: 10, category: "A" },
122
{ value: 20, category: "B" },
123
{ value: 30, category: "A" }
124
];
125
126
const processed = transform(data, {
127
params: {
128
multiplier: 2,
129
prefix: "item_",
130
filter: "A"
131
}
132
}, (record, params) => {
133
if (record.category !== params.filter) {
134
return null; // Skip non-matching records
135
}
136
return {
137
id: params.prefix + record.value,
138
value: record.value * params.multiplier,
139
category: record.category
140
};
141
});
142
143
// Filter out null results
144
const filtered = processed.filter(item => item !== null);
145
console.log(filtered);
146
// [
147
// { id: "item_10", value: 20, category: "A" },
148
// { id: "item_30", value: 60, category: "A" }
149
// ]
150
151
// Configuration-driven transformation
152
const config = {
153
params: {
154
dateFormat: "YYYY-MM-DD",
155
timezone: "UTC",
156
includeMetadata: true
157
}
158
};
159
160
const events = [
161
{ timestamp: 1640995200000, event: "login" },
162
{ timestamp: 1640995260000, event: "logout" }
163
];
164
165
const formattedEvents = transform(events, config, (event, params) => {
166
const date = new Date(event.timestamp);
167
const formatted = {
168
event: event.event,
169
date: date.toISOString().split('T')[0], // Simple YYYY-MM-DD format
170
time: date.toISOString().split('T')[1].split('.')[0] // HH:MM:SS format
171
};
172
173
if (params.includeMetadata) {
174
formatted.metadata = {
175
timezone: params.timezone,
176
processed: new Date().toISOString()
177
};
178
}
179
180
return formatted;
181
});
182
```
183
184
### Handler Function Requirements
185
186
Synchronous API only supports synchronous handlers with specific signature requirements.
187
188
```javascript { .api }
189
/**
190
* Synchronous handler - returns result directly
191
* @param record - Individual record to transform
192
* @param params - Optional user-defined parameters
193
* @returns Transformed result
194
*/
195
type Handler<T, U> = (record: T, params?: any) => U;
196
197
// Handler must be synchronous - these patterns are NOT supported:
198
// ❌ (record, callback) => void // Async callback pattern
199
// ❌ (record) => Promise<result> // Promise-based pattern
200
// ❌ async (record) => result // Async function pattern
201
```
202
203
**Usage Examples:**
204
205
```javascript
206
import { transform } from "stream-transform/sync";
207
208
// ✅ Valid synchronous handlers
209
const result1 = transform([1, 2, 3], (num) => num * 2);
210
211
const result2 = transform(["a", "b"], (str) => str.toUpperCase());
212
213
const result3 = transform([{a: 1}, {a: 2}], (obj) => ({ ...obj, b: obj.a * 2 }));
214
215
// With parameters
216
const result4 = transform([1, 2, 3], { params: { factor: 10 } }, (num, params) => {
217
return num * params.factor;
218
});
219
220
// ❌ Invalid - async patterns will throw errors
221
try {
222
transform([1, 2, 3], (num, callback) => {
223
// This will throw: "Invalid Handler: only synchonous handlers are supported"
224
callback(null, num * 2);
225
});
226
} catch (err) {
227
console.error(err.message);
228
}
229
230
try {
231
transform([1, 2, 3], async (num) => {
232
// This will throw: "Invalid Handler: only synchonous handlers are supported"
233
return num * 2;
234
});
235
} catch (err) {
236
console.error(err.message);
237
}
238
```
239
240
### Record Manipulation Patterns
241
242
Various patterns for manipulating records in synchronous transformations.
243
244
```javascript { .api }
245
// Record transformation patterns:
246
// - Return transformed record: normal transformation
247
// - Return null/undefined/"": skip record (will be filtered out)
248
// - Return array: multiply record (each array element becomes separate result)
249
// - Throw error: halt processing with error
250
```
251
252
**Usage Examples:**
253
254
```javascript
255
import { transform } from "stream-transform/sync";
256
257
// Skip records by returning null/undefined/empty string
258
const numbers = [1, 2, 3, 4, 5, 6];
259
const evenOnly = transform(numbers, (num) => {
260
if (num % 2 !== 0) {
261
return null; // Skip odd numbers
262
}
263
return num * 2;
264
}).filter(result => result !== null); // Filter out nulls
265
console.log(evenOnly); // [4, 8, 12]
266
267
// Multiply records by returning arrays
268
const words = ["hello", "world"];
269
const letters = transform(words, (word) => {
270
return word.split(''); // Each word becomes array of letters
271
}).flat(); // Flatten the results
272
console.log(letters); // ['h','e','l','l','o','w','o','r','l','d']
273
274
// Error handling with synchronous throws
275
const data = [1, 2, "invalid", 4];
276
try {
277
const results = transform(data, (value) => {
278
if (typeof value !== "number") {
279
throw new Error(`Invalid value: ${value}`);
280
}
281
return value * 2;
282
});
283
} catch (err) {
284
console.error("Transformation failed:", err.message);
285
}
286
287
// Conditional transformation
288
const mixed = [1, "2", 3, "4", 5];
289
const processed = transform(mixed, (value) => {
290
if (typeof value === "string") {
291
return parseInt(value); // Convert strings to numbers
292
}
293
if (typeof value === "number") {
294
return value * 2; // Double numbers
295
}
296
return null; // Skip other types
297
}).filter(result => result !== null);
298
console.log(processed); // [2, 2, 6, 4, 10]
299
```
300
301
### Performance Characteristics
302
303
Understanding the performance profile of synchronous transformations.
304
305
```javascript { .api }
306
// Performance characteristics:
307
// - Blocking execution: entire process waits for completion
308
// - Memory efficient: processes one record at a time
309
// - CPU intensive: no I/O concurrency benefits
310
// - Best for: Small datasets, CPU-bound operations, simple transformations
311
// - Avoid for: Large datasets, I/O operations, long-running processes
312
```
313
314
**Usage Examples:**
315
316
```javascript
317
import { transform } from "stream-transform/sync";
318
319
// ✅ Good use cases for sync API
320
// Small datasets
321
const smallData = Array.from({ length: 100 }, (_, i) => i);
322
const processed = transform(smallData, (num) => num * num);
323
324
// Simple CPU operations
325
const texts = ["hello", "world", "sync", "api"];
326
const uppercased = transform(texts, (text) => text.toUpperCase());
327
328
// Data structure transformations
329
const objects = [{ a: 1, b: 2 }, { a: 3, b: 4 }];
330
const flattened = transform(objects, (obj) => [obj.a, obj.b]);
331
332
// ❌ Consider alternatives for these cases
333
// Large datasets - use stream API instead
334
const largeData = Array.from({ length: 1000000 }, (_, i) => i);
335
// const results = transform(largeData, processor); // May block for too long
336
337
// I/O operations - use async API instead
338
const urls = ["http://api1.com", "http://api2.com"];
339
// const responses = transform(urls, (url) => {
340
// return fetch(url); // Blocking I/O - not ideal
341
// });
342
343
// CPU-intensive operations - consider worker threads
344
const complexData = Array.from({ length: 10000 }, (_, i) => ({ data: i }));
345
// const computed = transform(complexData, (item) => {
346
// return heavyComputation(item); // May block UI/other operations
347
// });
348
349
// Better alternatives for problematic cases:
350
import { transform as asyncTransform } from "stream-transform";
351
352
// For large datasets - use streaming
353
const streamProcessor = asyncTransform((record) => processRecord(record));
354
355
// For I/O operations - use async handlers
356
asyncTransform(urls, async (url) => {
357
const response = await fetch(url);
358
return await response.json();
359
}, (err, results) => {
360
console.log("Async results:", results);
361
});
362
```
363
364
### Error Handling
365
366
Synchronous error handling with immediate exception throwing.
367
368
```javascript { .api }
369
// Errors are thrown synchronously and can be caught with try/catch
370
// Processing stops immediately when an error occurs
371
// No partial results are returned - either all succeed or all fail
372
```
373
374
**Usage Examples:**
375
376
```javascript
377
import { transform } from "stream-transform/sync";
378
379
// Basic error handling
380
const data = [1, 2, 3, "invalid", 5];
381
382
try {
383
const results = transform(data, (value) => {
384
if (typeof value !== "number") {
385
throw new Error(`Expected number, got ${typeof value}: ${value}`);
386
}
387
return value * 2;
388
});
389
console.log("All processed:", results);
390
} catch (err) {
391
console.error("Processing failed:", err.message);
392
// No partial results available
393
}
394
395
// Validation with detailed errors
396
const users = [
397
{ name: "Alice", email: "alice@example.com" },
398
{ name: "", email: "bob@example.com" }, // Invalid
399
{ name: "Charlie", email: "charlie@example.com" }
400
];
401
402
try {
403
const validatedUsers = transform(users, (user, index) => {
404
if (!user.name || user.name.trim() === "") {
405
throw new Error(`User at index ${index} has invalid name`);
406
}
407
if (!user.email || !user.email.includes("@")) {
408
throw new Error(`User at index ${index} has invalid email`);
409
}
410
return {
411
...user,
412
name: user.name.trim(),
413
email: user.email.toLowerCase(),
414
validated: true
415
};
416
});
417
console.log("All users validated:", validatedUsers);
418
} catch (err) {
419
console.error("Validation error:", err.message);
420
}
421
422
// Graceful error handling with recovery
423
function safeTransform(data, handler) {
424
const results = [];
425
const errors = [];
426
427
for (let i = 0; i < data.length; i++) {
428
try {
429
const result = handler(data[i], i);
430
results.push(result);
431
} catch (err) {
432
errors.push({ index: i, error: err.message, input: data[i] });
433
}
434
}
435
436
return { results, errors };
437
}
438
439
const mixed = [1, 2, "bad", 4, null, 6];
440
const { results, errors } = safeTransform(mixed, (value) => {
441
if (typeof value !== "number") {
442
throw new Error(`Invalid type: ${typeof value}`);
443
}
444
return value * 2;
445
});
446
447
console.log("Successful results:", results); // [2, 4, 8, 12]
448
console.log("Errors encountered:", errors);
449
// [
450
// { index: 2, error: "Invalid type: string", input: "bad" },
451
// { index: 4, error: "Invalid type: object", input: null }
452
// ]
453
```