@bian-womp/spark-graph 0.3.55 → 0.3.57

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/lib/cjs/index.cjs CHANGED
@@ -1546,6 +1546,70 @@ class LevelLogger {
1546
1546
  error(message, context, overrideLevel) {
1547
1547
  this.log("error", message, context, overrideLevel);
1548
1548
  }
1549
+ /**
1550
+ * Tries to parse a string as JSON and format it nicely if it's fully JSON
1551
+ */
1552
+ parseJsonStringIfFull(str) {
1553
+ const trimmed = str.trim();
1554
+ // Check if the string starts with { or [ and ends with } or ]
1555
+ if ((trimmed.startsWith("{") && trimmed.endsWith("}")) ||
1556
+ (trimmed.startsWith("[") && trimmed.endsWith("]"))) {
1557
+ try {
1558
+ const parsed = JSON.parse(trimmed);
1559
+ return JSON.stringify(parsed, null, 2);
1560
+ }
1561
+ catch {
1562
+ // If parsing fails, return original string
1563
+ return str;
1564
+ }
1565
+ }
1566
+ return str;
1567
+ }
1568
+ /**
1569
+ * Tries to parse a string as JSON and return the parsed object/array if it's fully JSON
1570
+ * Used for parsing JSON strings inside arrays so they can be formatted by stringifySceneAndOps
1571
+ */
1572
+ parseJsonStringToObject(str) {
1573
+ const trimmed = str.trim();
1574
+ // Check if the string starts with { or [ and ends with } or ]
1575
+ if ((trimmed.startsWith("{") && trimmed.endsWith("}")) ||
1576
+ (trimmed.startsWith("[") && trimmed.endsWith("]"))) {
1577
+ try {
1578
+ return JSON.parse(trimmed);
1579
+ }
1580
+ catch {
1581
+ // If parsing fails, return original string
1582
+ return str;
1583
+ }
1584
+ }
1585
+ return str;
1586
+ }
1587
+ /**
1588
+ * Parses JSON strings in context values if they are fully JSON
1589
+ * Also handles arrays containing JSON strings
1590
+ */
1591
+ parseJsonStringsInContext(context) {
1592
+ const parsed = {};
1593
+ for (const [key, value] of Object.entries(context)) {
1594
+ if (typeof value === "string") {
1595
+ parsed[key] = this.parseJsonStringIfFull(value);
1596
+ }
1597
+ else if (Array.isArray(value)) {
1598
+ // Process arrays: parse JSON strings within the array to objects/arrays
1599
+ // so they can be formatted correctly by stringifySceneAndOps
1600
+ parsed[key] = value.map((item) => {
1601
+ if (typeof item === "string") {
1602
+ return this.parseJsonStringToObject(item);
1603
+ }
1604
+ return item;
1605
+ });
1606
+ }
1607
+ else {
1608
+ parsed[key] = value;
1609
+ }
1610
+ }
1611
+ return parsed;
1612
+ }
1549
1613
  /**
1550
1614
  * Core logging method that respects the log level and applies prefix
1551
1615
  */
@@ -1559,12 +1623,73 @@ class LevelLogger {
1559
1623
  const effectiveValue = LevelLogger.levelValues[effectiveLevel] ?? 1;
1560
1624
  // Only log if the requested level is >= effective level
1561
1625
  if (requestedValue >= effectiveValue) {
1562
- const { formatJson, ...rest } = context ?? {};
1563
- const contextStr = rest && Object.keys(rest).length > 0
1564
- ? `${formatJson ? "\n" : " "}${Object.entries(rest)
1565
- .map(([k, v]) => formatJson
1566
- ? `${k}=${stringifySceneAndOps(v)}`
1567
- : `${k}=${JSON.stringify(v)}`)
1626
+ const { logFormat = "plain", ...rest } = context ?? {};
1627
+ const formatJson = logFormat === "json" || logFormat === "json-deep";
1628
+ const parseJsonString = logFormat === "json-deep";
1629
+ // Parse JSON strings in context values if requested
1630
+ let processedContext = rest;
1631
+ const parsedKeys = new Set(); // Track keys that had JSON strings parsed
1632
+ if (parseJsonString && rest && Object.keys(rest).length > 0) {
1633
+ // Check which keys had JSON strings before parsing
1634
+ for (const [key, value] of Object.entries(rest)) {
1635
+ if (typeof value === "string") {
1636
+ const trimmed = value.trim();
1637
+ if ((trimmed.startsWith("{") && trimmed.endsWith("}")) ||
1638
+ (trimmed.startsWith("[") && trimmed.endsWith("]"))) {
1639
+ try {
1640
+ JSON.parse(trimmed);
1641
+ parsedKeys.add(key);
1642
+ }
1643
+ catch {
1644
+ // Not valid JSON, skip
1645
+ }
1646
+ }
1647
+ }
1648
+ else if (Array.isArray(value)) {
1649
+ // Check if array contains JSON strings
1650
+ for (const item of value) {
1651
+ if (typeof item === "string") {
1652
+ const trimmed = item.trim();
1653
+ if ((trimmed.startsWith("{") && trimmed.endsWith("}")) ||
1654
+ (trimmed.startsWith("[") && trimmed.endsWith("]"))) {
1655
+ try {
1656
+ JSON.parse(trimmed);
1657
+ parsedKeys.add(key);
1658
+ break; // Found at least one JSON string in array
1659
+ }
1660
+ catch {
1661
+ // Not valid JSON, continue
1662
+ }
1663
+ }
1664
+ }
1665
+ }
1666
+ }
1667
+ }
1668
+ processedContext = this.parseJsonStringsInContext(rest);
1669
+ }
1670
+ const contextStr = processedContext && Object.keys(processedContext).length > 0
1671
+ ? `${formatJson ? "\n" : " "}${Object.entries(processedContext)
1672
+ .map(([k, v]) => {
1673
+ // If parseJsonString is enabled and value is a formatted JSON string
1674
+ // (starts with { or [ and contains newlines indicating it was formatted),
1675
+ // output it directly without stringifySceneAndOps to preserve formatting
1676
+ // Wrap it with json`...` to distinguish it as formatted JSON
1677
+ if (parseJsonString &&
1678
+ typeof v === "string" &&
1679
+ v.trim().match(/^[{\[]/) &&
1680
+ v.includes("\n")) {
1681
+ return `${k}=json\`${v}\``;
1682
+ }
1683
+ // If this key had a JSON string parsed and we're formatting JSON,
1684
+ // wrap the formatted output with json`...`
1685
+ if (formatJson && parseJsonString && parsedKeys.has(k)) {
1686
+ return `${k}=json\`${stringifySceneAndOps(v)}\``;
1687
+ }
1688
+ if (formatJson) {
1689
+ return `${k}=${stringifySceneAndOps(v)}`;
1690
+ }
1691
+ return `${k}=${JSON.stringify(v)}`;
1692
+ })
1568
1693
  .join(formatJson ? "\n" : " ")}`
1569
1694
  : "";
1570
1695
  const prefixedMessage = this.prefix