toon-formatter 2.0.1 → 2.2.0

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/src/csv.js CHANGED
@@ -4,38 +4,7 @@
4
4
 
5
5
  import Papa from 'papaparse';
6
6
  import { jsonToToonSync, toonToJsonSync } from './json.js';
7
- import { extractCsvFromString } from './utils.js';
8
-
9
- /**
10
- * Internal core function to convert pure CSV string to TOON (Async)
11
- * @param {string} csvString
12
- * @returns {Promise<string>}
13
- */
14
- function parseCsvToToon(csvString) {
15
- return new Promise((resolve, reject) => {
16
- Papa.parse(csvString, {
17
- header: true,
18
- dynamicTyping: true,
19
- complete: function (results) {
20
- try {
21
- const jsonObject = results.data;
22
-
23
- if (typeof jsonObject !== "object" || jsonObject === null) {
24
- throw new Error("CSV parsing failed — cannot convert.");
25
- }
26
-
27
- const toonString = jsonToToonSync(jsonObject);
28
- resolve(toonString);
29
- } catch (error) {
30
- reject(error);
31
- }
32
- },
33
- error: function (error) {
34
- reject(new Error(`CSV parsing error: ${error.message}`));
35
- }
36
- });
37
- });
38
- }
7
+ import { extractCsvFromString, flattenObject } from './utils.js';
39
8
 
40
9
  /**
41
10
  * Internal core function to convert pure CSV string to TOON (Sync)
@@ -46,6 +15,7 @@ function parseCsvToToonSync(csvString) {
46
15
  const results = Papa.parse(csvString, {
47
16
  header: true,
48
17
  dynamicTyping: true,
18
+ skipEmptyLines: true,
49
19
  });
50
20
 
51
21
  if (results.errors && results.errors.length > 0) {
@@ -62,20 +32,9 @@ function parseCsvToToonSync(csvString) {
62
32
  }
63
33
 
64
34
  /**
65
- * Converts CSV (or mixed text with CSV) to TOON format (Async)
66
- * @param {string} csvString - CSV formatted string or mixed text
67
- * @returns {Promise<string>} TOON formatted string
68
- * @throws {Error} If CSV is invalid
69
- */
70
- export async function csvToToon(csvString) {
71
- return csvToToonSync(csvString);
72
- }
73
-
74
- /**
75
- * Converts CSV (or mixed text with CSV) to TOON format (synchronous version)
35
+ * Converts CSV (or mixed text with CSV) to TOON format (Sync)
76
36
  * @param {string} csvString - CSV formatted string or mixed text
77
37
  * @returns {string} TOON formatted string
78
- * @throws {Error} If CSV is invalid
79
38
  */
80
39
  export function csvToToonSync(csvString) {
81
40
  if (!csvString || typeof csvString !== 'string') {
@@ -104,10 +63,18 @@ export function csvToToonSync(csvString) {
104
63
  }
105
64
 
106
65
  /**
107
- * Converts TOON to CSV format (Synchronous)
66
+ * Converts CSV (or mixed text with CSV) to TOON format (Async)
67
+ * @param {string} csvString - CSV formatted string or mixed text
68
+ * @returns {Promise<string>} TOON formatted string
69
+ */
70
+ export async function csvToToon(csvString) {
71
+ return csvToToonSync(csvString);
72
+ }
73
+
74
+ /**
75
+ * Converts TOON to CSV format (Sync)
108
76
  * @param {string} toonString - TOON formatted string
109
77
  * @returns {string} CSV formatted string
110
- * @throws {Error} If TOON is invalid
111
78
  */
112
79
  export function toonToCsvSync(toonString) {
113
80
  if (!toonString || typeof toonString !== 'string') {
@@ -116,12 +83,14 @@ export function toonToCsvSync(toonString) {
116
83
 
117
84
  const jsonObject = toonToJsonSync(toonString);
118
85
 
119
- const csvString = Papa.unparse(jsonObject, {
86
+ // Flatten the object for CSV
87
+ const dataToUnparse = Array.isArray(jsonObject)
88
+ ? jsonObject.map(row => flattenObject(row))
89
+ : [flattenObject(jsonObject)];
90
+
91
+ return Papa.unparse(dataToUnparse, {
120
92
  header: true,
121
- dynamicTyping: true,
122
93
  });
123
-
124
- return csvString;
125
94
  }
126
95
 
127
96
  /**
@@ -0,0 +1,496 @@
1
+ /**
2
+ * CsvConverter Class
3
+ */
4
+
5
+ import { csvToToon, csvToToonSync, toonToCsv, toonToCsvSync } from '../csv.js';
6
+ import { csvToJson, csvToJsonSync, jsonToCsv, jsonToCsvSync } from '../json_formatter/csv.js';
7
+ import { csvToYaml, csvToYamlSync, yamlToCsv, yamlToCsvSync } from '../yaml_formatter/csv.js';
8
+ import { csvToXml, csvToXmlSync, xmlToCsv, xmlToCsvSync } from '../xml_formatter/csv.js';
9
+ import { validateCsvString, validateCsvStringSync } from './validator.js';
10
+
11
+ export class CsvConverter {
12
+ /**
13
+ * Creates a CsvConverter instance
14
+ * @param {Object} [encryptor=null] - Optional Encryptor instance for encryption support
15
+ */
16
+ constructor(encryptor = null) {
17
+ this.encryptor = encryptor;
18
+ }
19
+
20
+ // --- Helper Methods for Encryption ---
21
+
22
+ /**
23
+ * Applies encryption logic based on conversion mode (synchronous)
24
+ * @private
25
+ * @param {Function} fn - The converter function to call
26
+ * @param {*} data - Data to convert
27
+ * @param {string} mode - Conversion mode: 'no_encryption', 'middleware', 'ingestion', 'export'
28
+ * @returns {*} Converted (and possibly encrypted) data
29
+ */
30
+ _convertWithEncryption(fn, data, mode) {
31
+ if (!this.encryptor || mode === 'no_encryption') {
32
+ return fn(data);
33
+ }
34
+
35
+ switch (mode) {
36
+ case 'middleware': // Decrypt -> Convert -> Encrypt
37
+ const decrypted = this.encryptor.decrypt(data);
38
+ const result = fn(decrypted);
39
+ return this.encryptor.encrypt(result);
40
+ case 'ingestion': // Decrypt -> Convert
41
+ const dec = this.encryptor.decrypt(data);
42
+ return fn(dec);
43
+ case 'export': // Convert -> Encrypt
44
+ const res = fn(data);
45
+ return this.encryptor.encrypt(res);
46
+ default:
47
+ return fn(data);
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Applies encryption logic based on conversion mode (asynchronous)
53
+ * @private
54
+ * @param {Function} fn - The async converter function to call
55
+ * @param {*} data - Data to convert
56
+ * @param {string} mode - Conversion mode: 'no_encryption', 'middleware', 'ingestion', 'export'
57
+ * @returns {Promise<*>} Converted (and possibly encrypted) data
58
+ */
59
+ async _convertWithEncryptionAsync(fn, data, mode) {
60
+ if (!this.encryptor || mode === 'no_encryption') {
61
+ return await fn(data);
62
+ }
63
+
64
+ switch (mode) {
65
+ case 'middleware':
66
+ const decrypted = this.encryptor.decrypt(data);
67
+ const result = await fn(decrypted);
68
+ return this.encryptor.encrypt(result);
69
+ case 'ingestion':
70
+ const dec = this.encryptor.decrypt(data);
71
+ return await fn(dec);
72
+ case 'export':
73
+ const res = await fn(data);
74
+ return this.encryptor.encrypt(res);
75
+ default:
76
+ return await fn(data);
77
+ }
78
+ }
79
+
80
+ // --- TOON Conversions ---
81
+
82
+ /**
83
+ * Convert TOON string to CSV (Sync)
84
+ * @param {string} toonString - TOON formatted string
85
+ * @param {Object} [options={}] - Conversion options
86
+ * @param {string} [options.conversionMode='no_encryption'] - Encryption mode
87
+ * @returns {string} CSV formatted string
88
+ */
89
+ fromToon(toonString, options = {}) {
90
+ const { conversionMode = 'no_encryption' } = options;
91
+ return this._convertWithEncryption(
92
+ (data) => toonToCsvSync(data),
93
+ toonString,
94
+ conversionMode
95
+ );
96
+ }
97
+
98
+ /**
99
+ * Convert TOON string to CSV (Async)
100
+ * @param {string} toonString - TOON formatted string
101
+ * @param {Object} [options={}] - Conversion options
102
+ * @param {string} [options.conversionMode='no_encryption'] - Encryption mode
103
+ * @returns {Promise<string>} CSV formatted string
104
+ */
105
+ async fromToonAsync(toonString, options = {}) {
106
+ const { conversionMode = 'no_encryption' } = options;
107
+ return this._convertWithEncryptionAsync(
108
+ async (data) => toonToCsv(data),
109
+ toonString,
110
+ conversionMode
111
+ );
112
+ }
113
+
114
+ /**
115
+ * Convert CSV to TOON string (Sync)
116
+ * @param {string} csvString - CSV formatted string (supports mixed text)
117
+ * @param {Object} [options={}] - Conversion options
118
+ * @param {string} [options.conversionMode='no_encryption'] - Encryption mode
119
+ * @returns {string} TOON formatted string
120
+ */
121
+ toToon(csvString, options = {}) {
122
+ const { conversionMode = 'no_encryption' } = options;
123
+ return this._convertWithEncryption(
124
+ (data) => csvToToonSync(data),
125
+ csvString,
126
+ conversionMode
127
+ );
128
+ }
129
+
130
+ /**
131
+ * Convert CSV to TOON string (Async)
132
+ * @param {string} csvString - CSV formatted string (supports mixed text)
133
+ * @param {Object} [options={}] - Conversion options
134
+ * @param {string} [options.conversionMode='no_encryption'] - Encryption mode
135
+ * @returns {Promise<string>} TOON formatted string
136
+ */
137
+ async toToonAsync(csvString, options = {}) {
138
+ const { conversionMode = 'no_encryption' } = options;
139
+ return this._convertWithEncryptionAsync(
140
+ async (data) => csvToToon(data),
141
+ csvString,
142
+ conversionMode
143
+ );
144
+ }
145
+
146
+ // --- JSON Conversions ---
147
+
148
+ /**
149
+ * Convert JSON to CSV (Sync)
150
+ * @param {Object|string} jsonData - JSON data or string with embedded JSON
151
+ * @param {Object} [options={}] - Conversion options
152
+ * @param {string} [options.conversionMode='no_encryption'] - Encryption mode
153
+ * @returns {string} CSV formatted string
154
+ */
155
+ fromJson(jsonData, options = {}) {
156
+ const { conversionMode = 'no_encryption' } = options;
157
+ return this._convertWithEncryption(
158
+ (data) => jsonToCsvSync(data),
159
+ jsonData,
160
+ conversionMode
161
+ );
162
+ }
163
+
164
+ /**
165
+ * Convert JSON to CSV (Async)
166
+ * @param {Object|string} jsonData - JSON data or string with embedded JSON
167
+ * @param {Object} [options={}] - Conversion options
168
+ * @param {string} [options.conversionMode='no_encryption'] - Encryption mode
169
+ * @returns {Promise<string>} CSV formatted string
170
+ */
171
+ async fromJsonAsync(jsonData, options = {}) {
172
+ const { conversionMode = 'no_encryption' } = options;
173
+ return this._convertWithEncryptionAsync(
174
+ async (data) => jsonToCsv(data),
175
+ jsonData,
176
+ conversionMode
177
+ );
178
+ }
179
+
180
+ /**
181
+ * Convert CSV to JSON (Sync)
182
+ * @param {string} csvString - CSV formatted string (supports mixed text)
183
+ * @param {Object} [options={}] - Conversion options
184
+ * @param {string} [options.conversionMode='no_encryption'] - Encryption mode
185
+ * @returns {Array|string} JSON result
186
+ */
187
+ toJson(csvString, options = {}) {
188
+ const { conversionMode = 'no_encryption' } = options;
189
+ return this._convertWithEncryption(
190
+ (data) => csvToJsonSync(data),
191
+ csvString,
192
+ conversionMode
193
+ );
194
+ }
195
+
196
+ /**
197
+ * Convert CSV to JSON (Async)
198
+ * @param {string} csvString - CSV formatted string (supports mixed text)
199
+ * @param {Object} [options={}] - Conversion options
200
+ * @param {string} [options.conversionMode='no_encryption'] - Encryption mode
201
+ * @returns {Promise<Array|string>} JSON result
202
+ */
203
+ async toJsonAsync(csvString, options = {}) {
204
+ const { conversionMode = 'no_encryption' } = options;
205
+ return this._convertWithEncryptionAsync(
206
+ async (data) => csvToJson(data),
207
+ csvString,
208
+ conversionMode
209
+ );
210
+ }
211
+
212
+ // --- YAML Conversions ---
213
+
214
+ /**
215
+ * Convert YAML string to CSV (Sync)
216
+ * @param {string} yamlString - YAML formatted string
217
+ * @param {Object} [options={}] - Conversion options
218
+ * @param {string} [options.conversionMode='no_encryption'] - Encryption mode
219
+ * @returns {string} CSV formatted string
220
+ */
221
+ fromYaml(yamlString, options = {}) {
222
+ const { conversionMode = 'no_encryption' } = options;
223
+ return this._convertWithEncryption(
224
+ (data) => yamlToCsvSync(data),
225
+ yamlString,
226
+ conversionMode
227
+ );
228
+ }
229
+
230
+ async fromYamlAsync(yamlString, options = {}) {
231
+ const { conversionMode = 'no_encryption' } = options;
232
+ return this._convertWithEncryptionAsync(
233
+ async (data) => yamlToCsv(data),
234
+ yamlString,
235
+ conversionMode
236
+ );
237
+ }
238
+
239
+ /**
240
+ * Convert CSV to YAML string (Sync)
241
+ * @param {string} csvString - CSV formatted string (supports mixed text)
242
+ * @param {Object} [options={}] - Conversion options
243
+ * @param {string} [options.conversionMode='no_encryption'] - Encryption mode
244
+ * @returns {string} YAML formatted string
245
+ */
246
+ toYaml(csvString, options = {}) {
247
+ const { conversionMode = 'no_encryption' } = options;
248
+ return this._convertWithEncryption(
249
+ (data) => csvToYamlSync(data),
250
+ csvString,
251
+ conversionMode
252
+ );
253
+ }
254
+
255
+ async toYamlAsync(csvString, options = {}) {
256
+ const { conversionMode = 'no_encryption' } = options;
257
+ return this._convertWithEncryptionAsync(
258
+ async (data) => csvToYaml(data),
259
+ csvString,
260
+ conversionMode
261
+ );
262
+ }
263
+
264
+ // --- XML Conversions ---
265
+
266
+ /**
267
+ * Convert XML string to CSV (Sync)
268
+ * @param {string} xmlString - XML formatted string (supports mixed text)
269
+ * @param {Object} [options={}] - Conversion options
270
+ * @param {string} [options.conversionMode='no_encryption'] - Encryption mode
271
+ * @returns {string} CSV formatted string
272
+ */
273
+ fromXml(xmlString, options = {}) {
274
+ const { conversionMode = 'no_encryption' } = options;
275
+ return this._convertWithEncryption(
276
+ (data) => xmlToCsvSync(data),
277
+ xmlString,
278
+ conversionMode
279
+ );
280
+ }
281
+
282
+ async fromXmlAsync(xmlString, options = {}) {
283
+ const { conversionMode = 'no_encryption' } = options;
284
+ return this._convertWithEncryptionAsync(
285
+ async (data) => xmlToCsv(data),
286
+ xmlString,
287
+ conversionMode
288
+ );
289
+ }
290
+
291
+ /**
292
+ * Convert CSV to XML string (Sync)
293
+ * @param {string} csvString - CSV formatted string (supports mixed text)
294
+ * @param {Object} [options={}] - Conversion options
295
+ * @param {string} [options.conversionMode='no_encryption'] - Encryption mode
296
+ * @returns {string} XML formatted string
297
+ */
298
+ toXml(csvString, options = {}) {
299
+ const { conversionMode = 'no_encryption' } = options;
300
+ return this._convertWithEncryption(
301
+ (data) => csvToXmlSync(data),
302
+ csvString,
303
+ conversionMode
304
+ );
305
+ }
306
+
307
+ async toXmlAsync(csvString, options = {}) {
308
+ const { conversionMode = 'no_encryption' } = options;
309
+ return this._convertWithEncryptionAsync(
310
+ async (data) => csvToXml(data),
311
+ csvString,
312
+ conversionMode
313
+ );
314
+ }
315
+
316
+ // --- Validation ---
317
+
318
+ /**
319
+ * Validate CSV string (Sync)
320
+ * @param {string} csvString - CSV string to validate
321
+ * @returns {boolean} True if valid
322
+ */
323
+ validate(csvString) {
324
+ return validateCsvStringSync(csvString);
325
+ }
326
+
327
+ async validateAsync(csvString) {
328
+ return validateCsvString(csvString);
329
+ }
330
+
331
+ // ========================================
332
+ // Static Methods (Backward Compatibility)
333
+ // ========================================
334
+
335
+ /**
336
+ * Convert TOON string to CSV (Sync)
337
+ * @param {string} toonString - TOON formatted string
338
+ * @returns {string} CSV formatted string
339
+ */
340
+ static fromToon(toonString) {
341
+ return toonToCsvSync(toonString);
342
+ }
343
+
344
+ /**
345
+ * Convert TOON string to CSV (Async)
346
+ * @param {string} toonString - TOON formatted string
347
+ * @returns {Promise<string>} CSV formatted string
348
+ */
349
+ static async fromToonAsync(toonString) {
350
+ return toonToCsv(toonString);
351
+ }
352
+
353
+ /**
354
+ * Convert CSV to TOON string (Sync)
355
+ * @param {string} csvString - CSV formatted string
356
+ * @returns {string} TOON formatted string
357
+ */
358
+ static toToon(csvString) {
359
+ return csvToToonSync(csvString);
360
+ }
361
+
362
+ /**
363
+ * Convert CSV to TOON string (Async)
364
+ * @param {string} csvString - CSV formatted string
365
+ * @returns {Promise<string>} TOON formatted string
366
+ */
367
+ static async toToonAsync(csvString) {
368
+ return csvToToon(csvString);
369
+ }
370
+
371
+ /**
372
+ * Convert JSON to CSV string (Sync)
373
+ * @param {Object|string} jsonData - JSON data or string with embedded JSON
374
+ * @returns {string} CSV formatted string
375
+ */
376
+ static fromJson(jsonData) {
377
+ return jsonToCsvSync(jsonData);
378
+ }
379
+
380
+ /**
381
+ * Convert JSON to CSV string (Async)
382
+ * @param {Object|string} jsonData - JSON data or string with embedded JSON
383
+ * @returns {Promise<string>} CSV formatted string
384
+ */
385
+ static async fromJsonAsync(jsonData) {
386
+ return jsonToCsv(jsonData);
387
+ }
388
+
389
+ /**
390
+ * Convert CSV to JSON (Sync)
391
+ * @param {string} csvString - CSV formatted string
392
+ * @returns {Array|string} JSON result
393
+ */
394
+ static toJson(csvString) {
395
+ return csvToJsonSync(csvString);
396
+ }
397
+
398
+ /**
399
+ * Convert CSV to JSON (Async)
400
+ * @param {string} csvString - CSV formatted string
401
+ * @returns {Promise<Array|string>} JSON result
402
+ */
403
+ static async toJsonAsync(csvString) {
404
+ return csvToJson(csvString);
405
+ }
406
+
407
+ /**
408
+ * Convert YAML to CSV string (Sync)
409
+ * @param {string} yamlString - YAML formatted string
410
+ * @returns {string} CSV formatted string
411
+ */
412
+ static fromYaml(yamlString) {
413
+ return yamlToCsvSync(yamlString);
414
+ }
415
+
416
+ /**
417
+ * Convert YAML to CSV string (Async)
418
+ * @param {string} yamlString - YAML formatted string
419
+ * @returns {Promise<string>} CSV formatted string
420
+ */
421
+ static async fromYamlAsync(yamlString) {
422
+ return yamlToCsv(yamlString);
423
+ }
424
+
425
+ /**
426
+ * Convert CSV to YAML string (Sync)
427
+ * @param {string} csvString - CSV formatted string
428
+ * @returns {string} YAML formatted string
429
+ */
430
+ static toYaml(csvString) {
431
+ return csvToYamlSync(csvString);
432
+ }
433
+
434
+ /**
435
+ * Convert CSV to YAML string (Async)
436
+ * @param {string} csvString - CSV formatted string
437
+ * @returns {Promise<string>} YAML formatted string
438
+ */
439
+ static async toYamlAsync(csvString) {
440
+ return csvToYaml(csvString);
441
+ }
442
+
443
+ /**
444
+ * Convert XML to CSV string (Sync)
445
+ * @param {string} xmlString - XML formatted string
446
+ * @returns {string} CSV formatted string
447
+ */
448
+ static fromXml(xmlString) {
449
+ return xmlToCsvSync(xmlString);
450
+ }
451
+
452
+ /**
453
+ * Convert XML to CSV string (Async)
454
+ * @param {string} xmlString - XML formatted string
455
+ * @returns {Promise<string>} CSV formatted string
456
+ */
457
+ static async fromXmlAsync(xmlString) {
458
+ return xmlToCsv(xmlString);
459
+ }
460
+
461
+ /**
462
+ * Convert CSV to XML string (Sync)
463
+ * @param {string} csvString - CSV formatted string
464
+ * @returns {string} XML formatted string
465
+ */
466
+ static toXml(csvString) {
467
+ return csvToXmlSync(csvString);
468
+ }
469
+
470
+ /**
471
+ * Convert CSV to XML string (Async)
472
+ * @param {string} csvString - CSV formatted string
473
+ * @returns {Promise<string>} XML formatted string
474
+ */
475
+ static async toXmlAsync(csvString) {
476
+ return csvToXml(csvString);
477
+ }
478
+
479
+ /**
480
+ * Validate CSV string
481
+ * @param {string} csvString - CSV string to validate
482
+ * @returns {boolean} True if valid
483
+ */
484
+ static validate(csvString) {
485
+ return validateCsvStringSync(csvString);
486
+ }
487
+
488
+ /**
489
+ * Validate CSV string (Async)
490
+ * @param {string} csvString - CSV string to validate
491
+ * @returns {Promise<boolean>} True if valid
492
+ */
493
+ static async validateAsync(csvString) {
494
+ return validateCsvString(csvString);
495
+ }
496
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * CSV Validator (for CsvConverter)
3
+ */
4
+
5
+ import Papa from 'papaparse';
6
+
7
+ /**
8
+ * Validate CSV string (Sync)
9
+ * @param {string} csvString
10
+ * @returns {boolean} True if valid, throws error if invalid
11
+ */
12
+ export function validateCsvStringSync(csvString) {
13
+ if (typeof csvString !== 'string') {
14
+ throw new Error("Input must be a string.");
15
+ }
16
+
17
+ const results = Papa.parse(csvString, {
18
+ header: true,
19
+ skipEmptyLines: true,
20
+ });
21
+
22
+ if (results.errors && results.errors.length > 0) {
23
+ throw new Error(`Invalid CSV: ${results.errors[0].message}`);
24
+ }
25
+
26
+ return true;
27
+ }
28
+
29
+ /**
30
+ * Validate CSV string (Async)
31
+ * @param {string} csvString
32
+ * @returns {Promise<boolean>} True if valid
33
+ */
34
+ export async function validateCsvString(csvString) {
35
+ return validateCsvStringSync(csvString);
36
+ }
package/src/index.js CHANGED
@@ -24,6 +24,10 @@ import {
24
24
  extractCsvFromString
25
25
  } from './utils.js';
26
26
  import { Encryptor } from './encryptor.js';
27
+ import { JsonConverter } from './json_formatter/index.js';
28
+ import { YamlConverter } from './yaml_formatter/index.js';
29
+ import { XmlConverter } from './xml_formatter/index.js';
30
+ import { CsvConverter } from './csv_formatter/index.js';
27
31
 
28
32
  // Exports
29
33
  export {
@@ -34,7 +38,11 @@ export {
34
38
  validateToonString, validateToonStringSync,
35
39
  encodeXmlReservedChars, splitByDelimiter, parseValue, formatValue,
36
40
  extractJsonFromString, extractXmlFromString, extractCsvFromString,
37
- Encryptor
41
+ Encryptor,
42
+ JsonConverter,
43
+ YamlConverter,
44
+ XmlConverter,
45
+ CsvConverter
38
46
  };
39
47
 
40
48
  /**