@mdfriday/foundry 25.8.11 → 25.9.2
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/README.md +9 -0
- package/dist/internal/application/ssg.d.ts.map +1 -1
- package/dist/internal/application/ssg.js +29 -6
- package/dist/internal/application/ssg.js.map +1 -1
- package/dist/internal/domain/config/entity/language.d.ts +5 -1
- package/dist/internal/domain/config/entity/language.d.ts.map +1 -1
- package/dist/internal/domain/config/entity/language.js +20 -4
- package/dist/internal/domain/config/entity/language.js.map +1 -1
- package/dist/internal/domain/config/entity/service.d.ts +2 -3
- package/dist/internal/domain/config/entity/service.d.ts.map +1 -1
- package/dist/internal/domain/config/entity/service.js +6 -3
- package/dist/internal/domain/config/entity/service.js.map +1 -1
- package/dist/internal/domain/config/factory/config.js +1 -1
- package/dist/internal/domain/config/factory/config.js.map +1 -1
- package/dist/internal/domain/config/tests/index.test.js +2 -4
- package/dist/internal/domain/config/tests/index.test.js.map +1 -1
- package/dist/internal/domain/config/tests/type.test.js +4 -0
- package/dist/internal/domain/config/tests/type.test.js.map +1 -1
- package/dist/internal/domain/config/tests/vo-service.test.js +95 -145
- package/dist/internal/domain/config/tests/vo-service.test.js.map +1 -1
- package/dist/internal/domain/config/type.d.ts +5 -0
- package/dist/internal/domain/config/type.d.ts.map +1 -1
- package/dist/internal/domain/config/type.js.map +1 -1
- package/dist/internal/domain/config/vo/language.d.ts +0 -3
- package/dist/internal/domain/config/vo/language.d.ts.map +1 -1
- package/dist/internal/domain/config/vo/language.js +22 -26
- package/dist/internal/domain/config/vo/language.js.map +1 -1
- package/dist/internal/domain/config/vo/service.d.ts +1 -1
- package/dist/internal/domain/config/vo/service.d.ts.map +1 -1
- package/dist/internal/domain/config/vo/service.js +15 -36
- package/dist/internal/domain/config/vo/service.js.map +1 -1
- package/dist/internal/domain/content/entity/content.d.ts +21 -6
- package/dist/internal/domain/content/entity/content.d.ts.map +1 -1
- package/dist/internal/domain/content/entity/content.js +51 -55
- package/dist/internal/domain/content/entity/content.js.map +1 -1
- package/dist/internal/domain/content/entity/page.d.ts +4 -2
- package/dist/internal/domain/content/entity/page.d.ts.map +1 -1
- package/dist/internal/domain/content/entity/page.js +65 -12
- package/dist/internal/domain/content/entity/page.js.map +1 -1
- package/dist/internal/domain/content/entity/pagebuilder.d.ts.map +1 -1
- package/dist/internal/domain/content/entity/pagebuilder.js +2 -2
- package/dist/internal/domain/content/entity/pagebuilder.js.map +1 -1
- package/dist/internal/domain/content/entity/pagecollector.d.ts +0 -3
- package/dist/internal/domain/content/entity/pagecollector.d.ts.map +1 -1
- package/dist/internal/domain/content/entity/pagecollector.js +19 -16
- package/dist/internal/domain/content/entity/pagecollector.js.map +1 -1
- package/dist/internal/domain/content/entity/pagemap.d.ts +2 -2
- package/dist/internal/domain/content/entity/pagemap.d.ts.map +1 -1
- package/dist/internal/domain/content/entity/pagemap.js +19 -24
- package/dist/internal/domain/content/entity/pagemap.js.map +1 -1
- package/dist/internal/domain/content/entity/pagetrees.d.ts.map +1 -1
- package/dist/internal/domain/content/entity/pagetrees.js.map +1 -1
- package/dist/internal/domain/content/entity/paginator.d.ts.map +1 -1
- package/dist/internal/domain/content/entity/paginator.js +8 -11
- package/dist/internal/domain/content/entity/paginator.js.map +1 -1
- package/dist/internal/domain/content/entity/taxonomy.d.ts +1 -1
- package/dist/internal/domain/content/entity/taxonomy.d.ts.map +1 -1
- package/dist/internal/domain/content/entity/taxonomy.js +6 -8
- package/dist/internal/domain/content/entity/taxonomy.js.map +1 -1
- package/dist/internal/domain/content/entity/term.d.ts +1 -1
- package/dist/internal/domain/content/entity/term.d.ts.map +1 -1
- package/dist/internal/domain/content/entity/term.js +17 -14
- package/dist/internal/domain/content/entity/term.js.map +1 -1
- package/dist/internal/domain/content/entity/translator.d.ts +42 -0
- package/dist/internal/domain/content/entity/translator.d.ts.map +1 -0
- package/dist/internal/domain/content/entity/translator.js +187 -0
- package/dist/internal/domain/content/entity/translator.js.map +1 -0
- package/dist/internal/domain/content/factory/hub.d.ts.map +1 -1
- package/dist/internal/domain/content/factory/hub.js +20 -1
- package/dist/internal/domain/content/factory/hub.js.map +1 -1
- package/dist/internal/domain/content/index.d.ts +1 -0
- package/dist/internal/domain/content/index.d.ts.map +1 -1
- package/dist/internal/domain/content/index.js +1 -0
- package/dist/internal/domain/content/index.js.map +1 -1
- package/dist/internal/domain/content/type.d.ts +6 -7
- package/dist/internal/domain/content/type.d.ts.map +1 -1
- package/dist/internal/domain/content/type.js.map +1 -1
- package/dist/internal/domain/content/vo/sort.d.ts +0 -1
- package/dist/internal/domain/content/vo/sort.d.ts.map +1 -1
- package/dist/internal/domain/content/vo/sort.js +4 -3
- package/dist/internal/domain/content/vo/sort.js.map +1 -1
- package/dist/internal/domain/fs/entity/fs.d.ts +6 -4
- package/dist/internal/domain/fs/entity/fs.d.ts.map +1 -1
- package/dist/internal/domain/fs/entity/fs.js +7 -3
- package/dist/internal/domain/fs/entity/fs.js.map +1 -1
- package/dist/internal/domain/fs/factory/fs.d.ts.map +1 -1
- package/dist/internal/domain/fs/factory/fs.js +1 -1
- package/dist/internal/domain/fs/factory/fs.js.map +1 -1
- package/dist/internal/domain/fs/tests/entity-fs.test.js +10 -8
- package/dist/internal/domain/fs/tests/entity-fs.test.js.map +1 -1
- package/dist/internal/domain/fs/tests/vo-filesystemscollector.test.js +7 -0
- package/dist/internal/domain/fs/tests/vo-filesystemscollector.test.js.map +1 -1
- package/dist/internal/domain/fs/tests/vo-incremental-file-collector.test.js +23 -23
- package/dist/internal/domain/fs/tests/vo-incremental-file-collector.test.js.map +1 -1
- package/dist/internal/domain/fs/vo/filesystemscollector.d.ts +3 -1
- package/dist/internal/domain/fs/vo/filesystemscollector.d.ts.map +1 -1
- package/dist/internal/domain/fs/vo/filesystemscollector.js +18 -3
- package/dist/internal/domain/fs/vo/filesystemscollector.js.map +1 -1
- package/dist/internal/domain/fs/vo/incremental-file-collector.d.ts +1 -1
- package/dist/internal/domain/fs/vo/incremental-file-collector.d.ts.map +1 -1
- package/dist/internal/domain/fs/vo/incremental-file-collector.js +5 -3
- package/dist/internal/domain/fs/vo/incremental-file-collector.js.map +1 -1
- package/dist/internal/domain/module/entity/lang.d.ts.map +1 -1
- package/dist/internal/domain/module/entity/lang.js +5 -1
- package/dist/internal/domain/module/entity/lang.js.map +1 -1
- package/dist/internal/domain/module/entity/module.d.ts +17 -12
- package/dist/internal/domain/module/entity/module.d.ts.map +1 -1
- package/dist/internal/domain/module/entity/module.js +169 -55
- package/dist/internal/domain/module/entity/module.js.map +1 -1
- package/dist/internal/domain/module/type.d.ts +16 -1
- package/dist/internal/domain/module/type.d.ts.map +1 -1
- package/dist/internal/domain/module/type.js +3 -1
- package/dist/internal/domain/module/type.js.map +1 -1
- package/dist/internal/domain/module/vo/module.d.ts +6 -13
- package/dist/internal/domain/module/vo/module.d.ts.map +1 -1
- package/dist/internal/domain/module/vo/module.js +44 -17
- package/dist/internal/domain/module/vo/module.js.map +1 -1
- package/dist/internal/domain/module/vo/mount.d.ts +4 -0
- package/dist/internal/domain/module/vo/mount.d.ts.map +1 -1
- package/dist/internal/domain/module/vo/mount.js +6 -0
- package/dist/internal/domain/module/vo/mount.js.map +1 -1
- package/dist/internal/domain/module/vo/themes.d.ts +64 -0
- package/dist/internal/domain/module/vo/themes.d.ts.map +1 -0
- package/dist/internal/domain/module/vo/themes.js +92 -0
- package/dist/internal/domain/module/vo/themes.js.map +1 -0
- package/dist/internal/domain/module/vo/themes.test.d.ts +2 -0
- package/dist/internal/domain/module/vo/themes.test.d.ts.map +1 -0
- package/dist/internal/domain/module/vo/themes.test.js +109 -0
- package/dist/internal/domain/module/vo/themes.test.js.map +1 -0
- package/dist/internal/domain/resources/entity/http.d.ts +20 -0
- package/dist/internal/domain/resources/entity/http.d.ts.map +1 -0
- package/dist/internal/domain/resources/entity/http.js +131 -0
- package/dist/internal/domain/resources/entity/http.js.map +1 -0
- package/dist/internal/domain/resources/entity/resources.d.ts +6 -0
- package/dist/internal/domain/resources/entity/resources.d.ts.map +1 -1
- package/dist/internal/domain/resources/entity/resources.js +44 -6
- package/dist/internal/domain/resources/entity/resources.js.map +1 -1
- package/dist/internal/domain/site/entity/language.d.ts +1 -0
- package/dist/internal/domain/site/entity/language.d.ts.map +1 -1
- package/dist/internal/domain/site/entity/language.js +3 -0
- package/dist/internal/domain/site/entity/language.js.map +1 -1
- package/dist/internal/domain/site/entity/page.d.ts +7 -7
- package/dist/internal/domain/site/entity/page.d.ts.map +1 -1
- package/dist/internal/domain/site/entity/page.js +8 -30
- package/dist/internal/domain/site/entity/page.js.map +1 -1
- package/dist/internal/domain/site/entity/pager.d.ts.map +1 -1
- package/dist/internal/domain/site/entity/pager.js.map +1 -1
- package/dist/internal/domain/site/entity/site.d.ts +11 -0
- package/dist/internal/domain/site/entity/site.d.ts.map +1 -1
- package/dist/internal/domain/site/entity/site.js +42 -19
- package/dist/internal/domain/site/entity/site.js.map +1 -1
- package/dist/internal/domain/site/type.d.ts +8 -1
- package/dist/internal/domain/site/type.d.ts.map +1 -1
- package/dist/internal/domain/template/type.d.ts +2 -0
- package/dist/internal/domain/template/type.d.ts.map +1 -1
- package/dist/internal/domain/template/vo/namespace.d.ts.map +1 -1
- package/dist/internal/domain/template/vo/namespace.js +5 -1
- package/dist/internal/domain/template/vo/namespace.js.map +1 -1
- package/dist/internal/domain/template/vo/registry.d.ts +32 -0
- package/dist/internal/domain/template/vo/registry.d.ts.map +1 -1
- package/dist/internal/domain/template/vo/registry.js +401 -15
- package/dist/internal/domain/template/vo/registry.js.map +1 -1
- package/dist/pkg/lang/data.d.ts +28 -0
- package/dist/pkg/lang/data.d.ts.map +1 -0
- package/dist/pkg/lang/data.js +151 -0
- package/dist/pkg/lang/data.js.map +1 -0
- package/dist/pkg/lang/index.d.ts +65 -0
- package/dist/pkg/lang/index.d.ts.map +1 -0
- package/dist/pkg/lang/index.js +79 -0
- package/dist/pkg/lang/index.js.map +1 -0
- package/dist/pkg/lang/service.d.ts +67 -0
- package/dist/pkg/lang/service.d.ts.map +1 -0
- package/dist/pkg/lang/service.js +115 -0
- package/dist/pkg/lang/service.js.map +1 -0
- package/dist/pkg/lang/types.d.ts +51 -0
- package/dist/pkg/lang/types.d.ts.map +1 -0
- package/dist/pkg/lang/types.js +7 -0
- package/dist/pkg/lang/types.js.map +1 -0
- package/dist/pkg/log/manager.js +1 -1
- package/package.json +1 -1
|
@@ -137,6 +137,8 @@ class DefaultTemplateRegistry {
|
|
|
137
137
|
this.registerFmtFunctions(funcMap);
|
|
138
138
|
// Path functions
|
|
139
139
|
this.registerPathFunctions(funcMap);
|
|
140
|
+
// Transform functions (unmarshal, etc.)
|
|
141
|
+
this.registerTransformFunctions(funcMap);
|
|
140
142
|
}
|
|
141
143
|
/**
|
|
142
144
|
* Register cryptographic hash functions
|
|
@@ -367,6 +369,7 @@ class DefaultTemplateRegistry {
|
|
|
367
369
|
registerResourcesFunctions(funcMap, services) {
|
|
368
370
|
funcMap.set('resources', () => ({
|
|
369
371
|
Get: async (filename) => await services.Get(filename),
|
|
372
|
+
GetRemote: async (uri) => await services.GetRemote(uri),
|
|
370
373
|
Minify: async (resource) => await services.Minify(resource),
|
|
371
374
|
Fingerprint: async (resource) => await services.Fingerprint(resource),
|
|
372
375
|
ExecuteAsTemplate: async (targetPath, data, resource) => await services.ExecuteAsTemplate(targetPath, data, resource)
|
|
@@ -388,9 +391,43 @@ class DefaultTemplateRegistry {
|
|
|
388
391
|
* Equivalent to Go's registerLang()
|
|
389
392
|
*/
|
|
390
393
|
registerLanguageFunctions(funcMap, services) {
|
|
394
|
+
// Import language service
|
|
395
|
+
const { lang } = require('../../../../pkg/lang');
|
|
396
|
+
// Language namespace functions
|
|
397
|
+
funcMap.set('lang', () => ({
|
|
398
|
+
// Get all language codes
|
|
399
|
+
getAllCodes: () => lang.getAllCodes(),
|
|
400
|
+
// Get all language names
|
|
401
|
+
getAllNames: () => lang.getAllNames(),
|
|
402
|
+
// Get language codes by direction
|
|
403
|
+
getCodesByDirection: (direction) => lang.getCodesByDirection(direction),
|
|
404
|
+
// Get language names by direction
|
|
405
|
+
getNamesByDirection: (direction) => lang.getNamesByDirection(direction),
|
|
406
|
+
// Get code-name mapping
|
|
407
|
+
getCodeNameMap: () => lang.getCodeNameMap(),
|
|
408
|
+
// Get code-name mapping by direction
|
|
409
|
+
getCodeNameMapByDirection: (direction) => lang.getCodeNameMapByDirection(direction),
|
|
410
|
+
// Get language name by code
|
|
411
|
+
getNameByCode: (code) => lang.getNameByCode(code),
|
|
412
|
+
// Get language info by code
|
|
413
|
+
getLanguageInfo: (code) => lang.getLanguageInfo(code),
|
|
414
|
+
// Check if language is supported
|
|
415
|
+
isSupported: (code) => lang.isSupported(code),
|
|
416
|
+
// Get language direction
|
|
417
|
+
getDirection: (code) => lang.getDirection(code),
|
|
418
|
+
// Check if language is LTR
|
|
419
|
+
isLTR: (code) => lang.isLTR(code),
|
|
420
|
+
// Check if language is RTL
|
|
421
|
+
isRTL: (code) => lang.isRTL(code)
|
|
422
|
+
}));
|
|
423
|
+
// Standalone functions for convenience
|
|
424
|
+
funcMap.set('langName', (code) => lang.getNameByCode(code) || code);
|
|
425
|
+
funcMap.set('langDirection', (code) => lang.getDirection(code) || 'ltr');
|
|
426
|
+
funcMap.set('isLTR', (code) => lang.isLTR(code));
|
|
427
|
+
funcMap.set('isRTL', (code) => lang.isRTL(code));
|
|
428
|
+
// i18n placeholder (can be extended later)
|
|
391
429
|
funcMap.set('i18n', (key) => {
|
|
392
|
-
|
|
393
|
-
return key;
|
|
430
|
+
return services.translate(key);
|
|
394
431
|
});
|
|
395
432
|
}
|
|
396
433
|
/**
|
|
@@ -1281,6 +1318,43 @@ class DefaultTemplateRegistry {
|
|
|
1281
1318
|
}
|
|
1282
1319
|
return set1.filter(item => set2.includes(item));
|
|
1283
1320
|
});
|
|
1321
|
+
// querify function - converts key-value pairs to URL query string
|
|
1322
|
+
// Usage: {{ querify "foo" 1 "bar" 2 "baz" "with spaces" }}
|
|
1323
|
+
// Returns: bar=2&baz=with+spaces&foo=1
|
|
1324
|
+
// Also supports array input: {{ slice "foo" 1 "bar" 2 | querify }}
|
|
1325
|
+
funcMap.set('querify', (...args) => {
|
|
1326
|
+
let params = [];
|
|
1327
|
+
// Handle single array argument (from pipe)
|
|
1328
|
+
if (args.length === 1 && Array.isArray(args[0])) {
|
|
1329
|
+
params = args[0];
|
|
1330
|
+
}
|
|
1331
|
+
else {
|
|
1332
|
+
params = args;
|
|
1333
|
+
}
|
|
1334
|
+
// Must have even number of parameters (key-value pairs)
|
|
1335
|
+
if (params.length % 2 !== 0) {
|
|
1336
|
+
throw new Error('querify requires an even number of arguments (key-value pairs)');
|
|
1337
|
+
}
|
|
1338
|
+
const queryParams = [];
|
|
1339
|
+
// Process key-value pairs
|
|
1340
|
+
for (let i = 0; i < params.length; i += 2) {
|
|
1341
|
+
const key = String(params[i]);
|
|
1342
|
+
const value = String(params[i + 1]);
|
|
1343
|
+
queryParams.push([key, value]);
|
|
1344
|
+
}
|
|
1345
|
+
// Sort by key for consistent output (like Go's implementation)
|
|
1346
|
+
queryParams.sort((a, b) => a[0].localeCompare(b[0]));
|
|
1347
|
+
// Build query string
|
|
1348
|
+
const queryString = queryParams
|
|
1349
|
+
.map(([key, value]) => {
|
|
1350
|
+
// URL encode key and value, replacing %20 with + for spaces (query string convention)
|
|
1351
|
+
const encodedKey = encodeURIComponent(key).replace(/%20/g, '+');
|
|
1352
|
+
const encodedValue = encodeURIComponent(value).replace(/%20/g, '+');
|
|
1353
|
+
return `${encodedKey}=${encodedValue}`;
|
|
1354
|
+
})
|
|
1355
|
+
.join('&');
|
|
1356
|
+
return queryString;
|
|
1357
|
+
});
|
|
1284
1358
|
}
|
|
1285
1359
|
/**
|
|
1286
1360
|
* Core indexing implementation following Go's doIndex logic
|
|
@@ -1560,15 +1634,6 @@ class DefaultTemplateRegistry {
|
|
|
1560
1634
|
funcMap.set('slice', (...args) => {
|
|
1561
1635
|
return args;
|
|
1562
1636
|
});
|
|
1563
|
-
// Printf function for string formatting
|
|
1564
|
-
funcMap.set('printf', (format, ...args) => {
|
|
1565
|
-
// Simple printf implementation
|
|
1566
|
-
let result = format;
|
|
1567
|
-
args.forEach((arg, index) => {
|
|
1568
|
-
result = result.replace(new RegExp(`%[sdf]`, 'g'), String(arg));
|
|
1569
|
-
});
|
|
1570
|
-
return result;
|
|
1571
|
-
});
|
|
1572
1637
|
// Default function for providing default values
|
|
1573
1638
|
// TypeScript equivalent of Go's default function
|
|
1574
1639
|
funcMap.set('default', (defaultValue, value) => {
|
|
@@ -1641,11 +1706,32 @@ class DefaultTemplateRegistry {
|
|
|
1641
1706
|
registerFmtFunctions(funcMap) {
|
|
1642
1707
|
// Basic printf, print, println functions (for backward compatibility)
|
|
1643
1708
|
funcMap.set('printf', (format, ...args) => {
|
|
1644
|
-
//
|
|
1709
|
+
// Enhanced printf implementation supporting %v, %s, %d, %f, %t
|
|
1645
1710
|
let result = format;
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1711
|
+
let argIndex = 0;
|
|
1712
|
+
// Replace format specifiers sequentially
|
|
1713
|
+
result = result.replace(/%[vsdft%]/g, (match) => {
|
|
1714
|
+
if (match === '%%') {
|
|
1715
|
+
return '%'; // Literal %
|
|
1716
|
+
}
|
|
1717
|
+
if (argIndex >= args.length) {
|
|
1718
|
+
return match; // Not enough arguments, keep the format specifier
|
|
1719
|
+
}
|
|
1720
|
+
const arg = args[argIndex++];
|
|
1721
|
+
switch (match) {
|
|
1722
|
+
case '%v': // Go's general format specifier
|
|
1723
|
+
case '%s': // String
|
|
1724
|
+
return String(arg);
|
|
1725
|
+
case '%d': // Integer
|
|
1726
|
+
return String(Math.floor(Number(arg) || 0));
|
|
1727
|
+
case '%f': // Float
|
|
1728
|
+
return String(Number(arg) || 0);
|
|
1729
|
+
case '%t': // Boolean
|
|
1730
|
+
return String(Boolean(arg));
|
|
1731
|
+
default:
|
|
1732
|
+
return String(arg);
|
|
1733
|
+
}
|
|
1734
|
+
});
|
|
1649
1735
|
return result;
|
|
1650
1736
|
});
|
|
1651
1737
|
funcMap.set('print', (...args) => {
|
|
@@ -1884,6 +1970,306 @@ class DefaultTemplateRegistry {
|
|
|
1884
1970
|
}
|
|
1885
1971
|
}));
|
|
1886
1972
|
}
|
|
1973
|
+
/**
|
|
1974
|
+
* Register transform functions (unmarshal, etc.)
|
|
1975
|
+
* Equivalent to Go's transform package functions
|
|
1976
|
+
*/
|
|
1977
|
+
registerTransformFunctions(funcMap) {
|
|
1978
|
+
// Transform namespace functions - following Go's transform package
|
|
1979
|
+
funcMap.set('transform', () => ({
|
|
1980
|
+
// Unmarshal unmarshals data from JSON, YAML, TOML, CSV formats
|
|
1981
|
+
// Usage: {{ $data := "{"key": "value"}" | transform.Unmarshal }}
|
|
1982
|
+
// Usage: {{ $data := (dict "format" "json") $jsonString | transform.Unmarshal }}
|
|
1983
|
+
Unmarshal: (...args) => {
|
|
1984
|
+
if (args.length < 1 || args.length > 2) {
|
|
1985
|
+
throw new Error('unmarshal takes 1 or 2 arguments');
|
|
1986
|
+
}
|
|
1987
|
+
let data;
|
|
1988
|
+
let options = {};
|
|
1989
|
+
if (args.length === 1) {
|
|
1990
|
+
data = args[0];
|
|
1991
|
+
}
|
|
1992
|
+
else {
|
|
1993
|
+
// First argument is options map
|
|
1994
|
+
const opts = args[0];
|
|
1995
|
+
if (!opts || typeof opts !== 'object') {
|
|
1996
|
+
throw new Error('first argument must be a map');
|
|
1997
|
+
}
|
|
1998
|
+
options = opts;
|
|
1999
|
+
data = args[1];
|
|
2000
|
+
}
|
|
2001
|
+
// Handle Resource objects (if data has Content method)
|
|
2002
|
+
if (data && typeof data === 'object' && typeof data.Content === 'function') {
|
|
2003
|
+
try {
|
|
2004
|
+
const content = data.Content();
|
|
2005
|
+
return this.unmarshalContent(content, options);
|
|
2006
|
+
}
|
|
2007
|
+
catch (error) {
|
|
2008
|
+
throw new Error(`failed to get content from resource: ${error.message}`);
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
// Handle string data
|
|
2012
|
+
let dataStr;
|
|
2013
|
+
if (typeof data === 'string') {
|
|
2014
|
+
dataStr = data;
|
|
2015
|
+
}
|
|
2016
|
+
else if (data && typeof data.toString === 'function') {
|
|
2017
|
+
dataStr = data.toString();
|
|
2018
|
+
}
|
|
2019
|
+
else {
|
|
2020
|
+
throw new Error(`type ${typeof data} not supported`);
|
|
2021
|
+
}
|
|
2022
|
+
if (!dataStr || dataStr.trim() === '') {
|
|
2023
|
+
return null;
|
|
2024
|
+
}
|
|
2025
|
+
return this.unmarshalContent(dataStr, options);
|
|
2026
|
+
}
|
|
2027
|
+
}));
|
|
2028
|
+
// Also register unmarshal as a standalone function for backward compatibility
|
|
2029
|
+
funcMap.set('unmarshal', (...args) => {
|
|
2030
|
+
const transform = funcMap.get('transform')();
|
|
2031
|
+
return transform.Unmarshal(...args);
|
|
2032
|
+
});
|
|
2033
|
+
}
|
|
2034
|
+
/**
|
|
2035
|
+
* Unmarshal content from string based on format
|
|
2036
|
+
* Supports JSON, YAML, TOML, CSV formats
|
|
2037
|
+
*/
|
|
2038
|
+
unmarshalContent(content, options = {}) {
|
|
2039
|
+
const trimmedContent = content.trim();
|
|
2040
|
+
if (!trimmedContent) {
|
|
2041
|
+
return null;
|
|
2042
|
+
}
|
|
2043
|
+
// Determine format
|
|
2044
|
+
let format = options.format;
|
|
2045
|
+
if (!format) {
|
|
2046
|
+
format = this.detectFormat(trimmedContent);
|
|
2047
|
+
}
|
|
2048
|
+
try {
|
|
2049
|
+
switch (format.toLowerCase()) {
|
|
2050
|
+
case 'json':
|
|
2051
|
+
return JSON.parse(trimmedContent);
|
|
2052
|
+
case 'yaml':
|
|
2053
|
+
case 'yml':
|
|
2054
|
+
return this.parseYAML(trimmedContent);
|
|
2055
|
+
case 'toml':
|
|
2056
|
+
return this.parseTOML(trimmedContent);
|
|
2057
|
+
case 'csv':
|
|
2058
|
+
return this.parseCSV(trimmedContent);
|
|
2059
|
+
default:
|
|
2060
|
+
throw new Error(`format "${format}" not supported`);
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
catch (error) {
|
|
2064
|
+
throw new Error(`failed to unmarshal ${format}: ${error.message}`);
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
/**
|
|
2068
|
+
* Detect format from content
|
|
2069
|
+
*/
|
|
2070
|
+
detectFormat(content) {
|
|
2071
|
+
const trimmed = content.trim();
|
|
2072
|
+
// JSON detection
|
|
2073
|
+
if ((trimmed.startsWith('{') && trimmed.endsWith('}')) ||
|
|
2074
|
+
(trimmed.startsWith('[') && trimmed.endsWith(']'))) {
|
|
2075
|
+
try {
|
|
2076
|
+
JSON.parse(trimmed);
|
|
2077
|
+
return 'json';
|
|
2078
|
+
}
|
|
2079
|
+
catch {
|
|
2080
|
+
// Not valid JSON, continue checking
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
// YAML detection (starts with --- or has key: value patterns)
|
|
2084
|
+
if (trimmed.startsWith('---') || /^[a-zA-Z_][a-zA-Z0-9_]*\s*:\s*.+/m.test(trimmed)) {
|
|
2085
|
+
return 'yaml';
|
|
2086
|
+
}
|
|
2087
|
+
// TOML detection (has [section] or key = value patterns)
|
|
2088
|
+
if (/^\[.*\]$/m.test(trimmed) || /^[a-zA-Z_][a-zA-Z0-9_]*\s*=\s*.+/m.test(trimmed)) {
|
|
2089
|
+
return 'toml';
|
|
2090
|
+
}
|
|
2091
|
+
// CSV detection (has comma-separated values)
|
|
2092
|
+
if (/^[^,\n]*,[^,\n]*/.test(trimmed)) {
|
|
2093
|
+
return 'csv';
|
|
2094
|
+
}
|
|
2095
|
+
// Default to JSON if uncertain
|
|
2096
|
+
return 'json';
|
|
2097
|
+
}
|
|
2098
|
+
/**
|
|
2099
|
+
* Parse YAML content (basic implementation)
|
|
2100
|
+
* For a full implementation, you'd want to use a proper YAML library
|
|
2101
|
+
*/
|
|
2102
|
+
parseYAML(content) {
|
|
2103
|
+
// This is a very basic YAML parser for simple key-value pairs
|
|
2104
|
+
// For production use, consider integrating a proper YAML library like js-yaml
|
|
2105
|
+
const lines = content.split('\n');
|
|
2106
|
+
const result = {};
|
|
2107
|
+
for (const line of lines) {
|
|
2108
|
+
const trimmedLine = line.trim();
|
|
2109
|
+
if (!trimmedLine || trimmedLine.startsWith('#') || trimmedLine === '---') {
|
|
2110
|
+
continue;
|
|
2111
|
+
}
|
|
2112
|
+
const colonIndex = trimmedLine.indexOf(':');
|
|
2113
|
+
if (colonIndex > 0) {
|
|
2114
|
+
const key = trimmedLine.substring(0, colonIndex).trim();
|
|
2115
|
+
let value = trimmedLine.substring(colonIndex + 1).trim();
|
|
2116
|
+
// Remove quotes if present
|
|
2117
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
2118
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
2119
|
+
value = value.slice(1, -1);
|
|
2120
|
+
}
|
|
2121
|
+
// Try to parse as number or boolean
|
|
2122
|
+
if (value === 'true') {
|
|
2123
|
+
result[key] = true;
|
|
2124
|
+
}
|
|
2125
|
+
else if (value === 'false') {
|
|
2126
|
+
result[key] = false;
|
|
2127
|
+
}
|
|
2128
|
+
else if (/^\d+$/.test(value)) {
|
|
2129
|
+
result[key] = parseInt(value, 10);
|
|
2130
|
+
}
|
|
2131
|
+
else if (/^\d*\.\d+$/.test(value)) {
|
|
2132
|
+
result[key] = parseFloat(value);
|
|
2133
|
+
}
|
|
2134
|
+
else {
|
|
2135
|
+
result[key] = value;
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
return result;
|
|
2140
|
+
}
|
|
2141
|
+
/**
|
|
2142
|
+
* Parse TOML content (basic implementation)
|
|
2143
|
+
* For a full implementation, you'd want to use a proper TOML library
|
|
2144
|
+
*/
|
|
2145
|
+
parseTOML(content) {
|
|
2146
|
+
// This is a very basic TOML parser for simple key-value pairs
|
|
2147
|
+
// For production use, consider integrating a proper TOML library
|
|
2148
|
+
const lines = content.split('\n');
|
|
2149
|
+
const result = {};
|
|
2150
|
+
let currentSection = '';
|
|
2151
|
+
for (const line of lines) {
|
|
2152
|
+
const trimmedLine = line.trim();
|
|
2153
|
+
if (!trimmedLine || trimmedLine.startsWith('#')) {
|
|
2154
|
+
continue;
|
|
2155
|
+
}
|
|
2156
|
+
// Section headers [section]
|
|
2157
|
+
if (trimmedLine.startsWith('[') && trimmedLine.endsWith(']')) {
|
|
2158
|
+
currentSection = trimmedLine.slice(1, -1);
|
|
2159
|
+
if (!result[currentSection]) {
|
|
2160
|
+
result[currentSection] = {};
|
|
2161
|
+
}
|
|
2162
|
+
continue;
|
|
2163
|
+
}
|
|
2164
|
+
// Key-value pairs
|
|
2165
|
+
const equalIndex = trimmedLine.indexOf('=');
|
|
2166
|
+
if (equalIndex > 0) {
|
|
2167
|
+
const key = trimmedLine.substring(0, equalIndex).trim();
|
|
2168
|
+
let value = trimmedLine.substring(equalIndex + 1).trim();
|
|
2169
|
+
// Remove quotes if present
|
|
2170
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
2171
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
2172
|
+
value = value.slice(1, -1);
|
|
2173
|
+
}
|
|
2174
|
+
// Parse value
|
|
2175
|
+
let parsedValue;
|
|
2176
|
+
if (value === 'true') {
|
|
2177
|
+
parsedValue = true;
|
|
2178
|
+
}
|
|
2179
|
+
else if (value === 'false') {
|
|
2180
|
+
parsedValue = false;
|
|
2181
|
+
}
|
|
2182
|
+
else if (/^\d+$/.test(value)) {
|
|
2183
|
+
parsedValue = parseInt(value, 10);
|
|
2184
|
+
}
|
|
2185
|
+
else if (/^\d*\.\d+$/.test(value)) {
|
|
2186
|
+
parsedValue = parseFloat(value);
|
|
2187
|
+
}
|
|
2188
|
+
else {
|
|
2189
|
+
parsedValue = value;
|
|
2190
|
+
}
|
|
2191
|
+
if (currentSection) {
|
|
2192
|
+
result[currentSection][key] = parsedValue;
|
|
2193
|
+
}
|
|
2194
|
+
else {
|
|
2195
|
+
result[key] = parsedValue;
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
return result;
|
|
2200
|
+
}
|
|
2201
|
+
/**
|
|
2202
|
+
* Parse CSV content (basic implementation)
|
|
2203
|
+
*/
|
|
2204
|
+
parseCSV(content) {
|
|
2205
|
+
const lines = content.trim().split('\n');
|
|
2206
|
+
if (lines.length === 0) {
|
|
2207
|
+
return [];
|
|
2208
|
+
}
|
|
2209
|
+
// Parse header row
|
|
2210
|
+
const headers = this.parseCSVLine(lines[0]);
|
|
2211
|
+
const result = [];
|
|
2212
|
+
// Parse data rows
|
|
2213
|
+
for (let i = 1; i < lines.length; i++) {
|
|
2214
|
+
const values = this.parseCSVLine(lines[i]);
|
|
2215
|
+
const row = {};
|
|
2216
|
+
for (let j = 0; j < headers.length; j++) {
|
|
2217
|
+
const header = headers[j];
|
|
2218
|
+
const value = j < values.length ? values[j] : '';
|
|
2219
|
+
// Try to parse as number
|
|
2220
|
+
if (/^\d+$/.test(value)) {
|
|
2221
|
+
row[header] = parseInt(value, 10);
|
|
2222
|
+
}
|
|
2223
|
+
else if (/^\d*\.\d+$/.test(value)) {
|
|
2224
|
+
row[header] = parseFloat(value);
|
|
2225
|
+
}
|
|
2226
|
+
else if (value === 'true') {
|
|
2227
|
+
row[header] = true;
|
|
2228
|
+
}
|
|
2229
|
+
else if (value === 'false') {
|
|
2230
|
+
row[header] = false;
|
|
2231
|
+
}
|
|
2232
|
+
else {
|
|
2233
|
+
row[header] = value;
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
result.push(row);
|
|
2237
|
+
}
|
|
2238
|
+
return result;
|
|
2239
|
+
}
|
|
2240
|
+
/**
|
|
2241
|
+
* Parse a single CSV line, handling quoted values
|
|
2242
|
+
*/
|
|
2243
|
+
parseCSVLine(line) {
|
|
2244
|
+
const result = [];
|
|
2245
|
+
let current = '';
|
|
2246
|
+
let inQuotes = false;
|
|
2247
|
+
for (let i = 0; i < line.length; i++) {
|
|
2248
|
+
const char = line[i];
|
|
2249
|
+
if (char === '"') {
|
|
2250
|
+
if (inQuotes && line[i + 1] === '"') {
|
|
2251
|
+
// Escaped quote
|
|
2252
|
+
current += '"';
|
|
2253
|
+
i++; // Skip next quote
|
|
2254
|
+
}
|
|
2255
|
+
else {
|
|
2256
|
+
// Toggle quote state
|
|
2257
|
+
inQuotes = !inQuotes;
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
else if (char === ',' && !inQuotes) {
|
|
2261
|
+
// Field separator
|
|
2262
|
+
result.push(current.trim());
|
|
2263
|
+
current = '';
|
|
2264
|
+
}
|
|
2265
|
+
else {
|
|
2266
|
+
current += char;
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
// Add the last field
|
|
2270
|
+
result.push(current.trim());
|
|
2271
|
+
return result;
|
|
2272
|
+
}
|
|
1887
2273
|
/**
|
|
1888
2274
|
* Get nested value from object using dot notation path
|
|
1889
2275
|
* Following golang's path resolution logic
|