n8n-nodes-notion-advanced 1.2.31-beta → 1.2.33-beta
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.
@@ -66,6 +66,10 @@ export declare class NotionAITool implements INodeType {
|
|
66
66
|
static processNestedHtmlInListItem(content: string): string;
|
67
67
|
static convertInlineHtmlToMarkdown(content: string): string;
|
68
68
|
static buildListHierarchy(listContent: string, listType: 'bulleted_list_item' | 'numbered_list_item', childHierarchyNodes: HierarchyNode[]): HierarchyNode | null;
|
69
|
+
static getListItemPositions(content: string): Array<{
|
70
|
+
start: number;
|
71
|
+
end: number;
|
72
|
+
}>;
|
69
73
|
static mapChildBlocksToListItems(listContent: string, childBlocks: IDataObject[], childNodes: XMLNode[]): Map<number, IDataObject[]>;
|
70
74
|
static processNestedListWithChildBlocks(listContent: string, listType: 'bulleted_list_item' | 'numbered_list_item', blocks: IDataObject[], listItemChildBlocks: Map<number, IDataObject[]>): void;
|
71
75
|
static processNestedList(listContent: string, listType: 'bulleted_list_item' | 'numbered_list_item', blocks: IDataObject[], childNodes?: XMLNode[]): void;
|
@@ -872,11 +872,12 @@ class NotionAITool {
|
|
872
872
|
}
|
873
873
|
// For list processors, handle them specially
|
874
874
|
if (xmlNode.listProcessor && (xmlNode.tagName === 'ul' || xmlNode.tagName === 'ol')) {
|
875
|
-
// Extract inner content
|
875
|
+
// Extract inner content and calculate offset
|
876
876
|
const tagName = xmlNode.tagName.toLowerCase();
|
877
877
|
const openTagRegex = new RegExp(`^<${tagName}[^>]*>`, 'i');
|
878
878
|
const closeTagRegex = new RegExp(`</${tagName}>$`, 'i');
|
879
879
|
let innerContent = xmlNode.match;
|
880
|
+
let contentStartOffset = 0;
|
880
881
|
const openMatch = xmlNode.match.match(openTagRegex);
|
881
882
|
const closeMatch = xmlNode.match.match(closeTagRegex);
|
882
883
|
if (openMatch && closeMatch) {
|
@@ -885,9 +886,23 @@ class NotionAITool {
|
|
885
886
|
const startIndex = openTag.length;
|
886
887
|
const endIndex = xmlNode.match.length - closeTag.length;
|
887
888
|
innerContent = xmlNode.match.substring(startIndex, endIndex);
|
889
|
+
contentStartOffset = xmlNode.start + startIndex; // Absolute position where list content starts
|
888
890
|
}
|
891
|
+
// Adjust child hierarchy node positions to be relative to list content
|
892
|
+
const adjustedChildNodes = childHierarchyNodes.map(child => {
|
893
|
+
var _a;
|
894
|
+
return ({
|
895
|
+
...child,
|
896
|
+
metadata: {
|
897
|
+
...child.metadata,
|
898
|
+
sourcePosition: ((_a = child.metadata) === null || _a === void 0 ? void 0 : _a.sourcePosition) !== undefined
|
899
|
+
? child.metadata.sourcePosition - contentStartOffset
|
900
|
+
: undefined
|
901
|
+
}
|
902
|
+
});
|
903
|
+
});
|
889
904
|
// Build hierarchy structure for the list
|
890
|
-
const listHierarchy = NotionAITool.buildListHierarchy(innerContent, xmlNode.tagName === 'ul' ? 'bulleted_list_item' : 'numbered_list_item',
|
905
|
+
const listHierarchy = NotionAITool.buildListHierarchy(innerContent, xmlNode.tagName === 'ul' ? 'bulleted_list_item' : 'numbered_list_item', adjustedChildNodes);
|
891
906
|
return listHierarchy;
|
892
907
|
}
|
893
908
|
// For regular nodes, create block and attach children
|
@@ -1472,21 +1487,27 @@ class NotionAITool {
|
|
1472
1487
|
processed = processed.replace(/<\/?li\s*[^>]*>/gi, '');
|
1473
1488
|
// Remove any other common list-related fragments
|
1474
1489
|
processed = processed.replace(/<\/?[uo]l\s*[^>]*>/gi, '');
|
1475
|
-
//
|
1476
|
-
|
1490
|
+
// Convert inline HTML formatting to markdown BEFORE removing tags
|
1491
|
+
processed = NotionAITool.convertInlineHtmlToMarkdown(processed);
|
1492
|
+
// Final cleanup of any remaining unhandled tags
|
1477
1493
|
const result = processed
|
1478
|
-
.replace(/<[^>]*>/g, ' ') // Remove any remaining HTML tags
|
1479
1494
|
.replace(/\s+/g, ' ') // Clean up whitespace
|
1480
1495
|
.trim();
|
1481
1496
|
return result;
|
1482
1497
|
}
|
1483
1498
|
catch (error) {
|
1484
1499
|
console.warn('Error processing nested HTML in list item:', error);
|
1485
|
-
// Fallback:
|
1486
|
-
|
1487
|
-
.
|
1488
|
-
.replace(/\s+/g, ' ')
|
1489
|
-
|
1500
|
+
// Fallback: convert inline HTML then remove any remaining tags
|
1501
|
+
try {
|
1502
|
+
const fallback = NotionAITool.convertInlineHtmlToMarkdown(processed);
|
1503
|
+
return fallback.replace(/\s+/g, ' ').trim();
|
1504
|
+
}
|
1505
|
+
catch (fallbackError) {
|
1506
|
+
return processed
|
1507
|
+
.replace(/<[^>]*>/g, ' ')
|
1508
|
+
.replace(/\s+/g, ' ')
|
1509
|
+
.trim();
|
1510
|
+
}
|
1490
1511
|
}
|
1491
1512
|
}
|
1492
1513
|
// Helper function to convert inline HTML to markdown
|
@@ -1523,12 +1544,13 @@ class NotionAITool {
|
|
1523
1544
|
}
|
1524
1545
|
// Build hierarchy structure for lists using HierarchyNode approach
|
1525
1546
|
static buildListHierarchy(listContent, listType, childHierarchyNodes) {
|
1547
|
+
var _a;
|
1526
1548
|
try {
|
1527
1549
|
// Process each <li> element and build hierarchy
|
1528
1550
|
const listItems = NotionAITool.extractListItemsWithBranching(listContent);
|
1529
1551
|
const listItemHierarchyNodes = [];
|
1530
|
-
// Map child hierarchy nodes to list items based on position
|
1531
|
-
|
1552
|
+
// Map child hierarchy nodes to list items based on actual position in source XML
|
1553
|
+
const listItemPositions = NotionAITool.getListItemPositions(listContent);
|
1532
1554
|
for (let i = 0; i < listItems.length; i++) {
|
1533
1555
|
const item = listItems[i];
|
1534
1556
|
if (!item.text && !item.children.length)
|
@@ -1547,16 +1569,19 @@ class NotionAITool {
|
|
1547
1569
|
listItemBlock[listType].rich_text = NotionAITool.parseBasicMarkdown(cleanText);
|
1548
1570
|
}
|
1549
1571
|
}
|
1550
|
-
// Collect child hierarchy nodes for this list item
|
1572
|
+
// Collect child hierarchy nodes for this list item based on position mapping
|
1551
1573
|
const itemChildNodes = [];
|
1552
|
-
//
|
1553
|
-
|
1554
|
-
|
1555
|
-
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1574
|
+
// Map child hierarchy nodes that belong to this specific list item
|
1575
|
+
if (i < listItemPositions.length) {
|
1576
|
+
const currentItemStart = listItemPositions[i].start;
|
1577
|
+
const currentItemEnd = listItemPositions[i].end;
|
1578
|
+
for (const childNode of childHierarchyNodes) {
|
1579
|
+
const childPosition = (_a = childNode.metadata) === null || _a === void 0 ? void 0 : _a.sourcePosition;
|
1580
|
+
if (childPosition !== undefined &&
|
1581
|
+
childPosition >= currentItemStart &&
|
1582
|
+
childPosition < currentItemEnd) {
|
1583
|
+
itemChildNodes.push(childNode);
|
1584
|
+
}
|
1560
1585
|
}
|
1561
1586
|
}
|
1562
1587
|
// Process nested list children (traditional nested lists)
|
@@ -1608,6 +1633,55 @@ class NotionAITool {
|
|
1608
1633
|
};
|
1609
1634
|
}
|
1610
1635
|
}
|
1636
|
+
// Helper function to get position ranges for each list item in the content
|
1637
|
+
static getListItemPositions(content) {
|
1638
|
+
const positions = [];
|
1639
|
+
let pos = 0;
|
1640
|
+
while (pos < content.length) {
|
1641
|
+
// Find next <li> tag
|
1642
|
+
const liStart = content.indexOf('<li', pos);
|
1643
|
+
if (liStart === -1)
|
1644
|
+
break;
|
1645
|
+
const liOpenEnd = content.indexOf('>', liStart);
|
1646
|
+
if (liOpenEnd === -1)
|
1647
|
+
break;
|
1648
|
+
// Find the matching </li> using proper depth tracking
|
1649
|
+
let depth = 0;
|
1650
|
+
let searchPos = liOpenEnd + 1;
|
1651
|
+
let liEnd = -1;
|
1652
|
+
while (searchPos < content.length) {
|
1653
|
+
const nextLiOpen = content.indexOf('<li', searchPos);
|
1654
|
+
const nextLiClose = content.indexOf('</li>', searchPos);
|
1655
|
+
if (nextLiClose === -1)
|
1656
|
+
break;
|
1657
|
+
if (nextLiOpen !== -1 && nextLiOpen < nextLiClose) {
|
1658
|
+
depth++;
|
1659
|
+
searchPos = nextLiOpen + 3;
|
1660
|
+
}
|
1661
|
+
else {
|
1662
|
+
if (depth === 0) {
|
1663
|
+
liEnd = nextLiClose + 5; // Include the '</li>'
|
1664
|
+
break;
|
1665
|
+
}
|
1666
|
+
else {
|
1667
|
+
depth--;
|
1668
|
+
searchPos = nextLiClose + 5;
|
1669
|
+
}
|
1670
|
+
}
|
1671
|
+
}
|
1672
|
+
if (liEnd !== -1) {
|
1673
|
+
positions.push({
|
1674
|
+
start: liStart,
|
1675
|
+
end: liEnd
|
1676
|
+
});
|
1677
|
+
pos = liEnd;
|
1678
|
+
}
|
1679
|
+
else {
|
1680
|
+
pos = liOpenEnd + 1;
|
1681
|
+
}
|
1682
|
+
}
|
1683
|
+
return positions;
|
1684
|
+
}
|
1611
1685
|
// Helper function to map child blocks to specific list items (legacy support)
|
1612
1686
|
static mapChildBlocksToListItems(listContent, childBlocks, childNodes) {
|
1613
1687
|
const listItemChildBlocks = new Map();
|
@@ -1756,13 +1830,30 @@ class NotionAITool {
|
|
1756
1830
|
continue;
|
1757
1831
|
}
|
1758
1832
|
const item = { text: '', children: [] };
|
1759
|
-
//
|
1833
|
+
// STEP 1: Remove child block XML from content to get clean parent text
|
1834
|
+
let parentTextContent = fullItemContent;
|
1835
|
+
// List of XML tags that create child blocks (these should be removed from parent text)
|
1836
|
+
const childBlockTags = [
|
1837
|
+
'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
1838
|
+
'blockquote', 'quote', 'callout', 'code', 'pre',
|
1839
|
+
'todo', 'toggle', 'image', 'embed', 'bookmark',
|
1840
|
+
'equation', 'divider'
|
1841
|
+
];
|
1842
|
+
// Remove child block XML from parent text
|
1843
|
+
childBlockTags.forEach(tag => {
|
1844
|
+
// Remove both self-closing and paired tags
|
1845
|
+
const selfClosingRegex = new RegExp(`<${tag}[^>]*\\/>`, 'gis');
|
1846
|
+
const pairedRegex = new RegExp(`<${tag}[^>]*>.*?<\\/${tag}>`, 'gis');
|
1847
|
+
parentTextContent = parentTextContent.replace(pairedRegex, '');
|
1848
|
+
parentTextContent = parentTextContent.replace(selfClosingRegex, '');
|
1849
|
+
});
|
1850
|
+
// STEP 2: Process the content to separate remaining text from nested lists
|
1760
1851
|
let contentPos = 0;
|
1761
1852
|
let textParts = [];
|
1762
|
-
while (contentPos <
|
1853
|
+
while (contentPos < parentTextContent.length) {
|
1763
1854
|
// Look for the next nested list (ul or ol)
|
1764
|
-
const nextUlStart =
|
1765
|
-
const nextOlStart =
|
1855
|
+
const nextUlStart = parentTextContent.indexOf('<ul', contentPos);
|
1856
|
+
const nextOlStart = parentTextContent.indexOf('<ol', contentPos);
|
1766
1857
|
let nextListStart = -1;
|
1767
1858
|
let listType = '';
|
1768
1859
|
if (nextUlStart !== -1 && (nextOlStart === -1 || nextUlStart < nextOlStart)) {
|
@@ -1775,22 +1866,22 @@ class NotionAITool {
|
|
1775
1866
|
}
|
1776
1867
|
if (nextListStart === -1) {
|
1777
1868
|
// No more nested lists - add remaining text
|
1778
|
-
const remainingText =
|
1869
|
+
const remainingText = parentTextContent.substring(contentPos);
|
1779
1870
|
if (remainingText.trim()) {
|
1780
1871
|
textParts.push(remainingText);
|
1781
1872
|
}
|
1782
1873
|
break;
|
1783
1874
|
}
|
1784
1875
|
// Add text before the nested list
|
1785
|
-
const textBefore =
|
1876
|
+
const textBefore = parentTextContent.substring(contentPos, nextListStart);
|
1786
1877
|
if (textBefore.trim()) {
|
1787
1878
|
textParts.push(textBefore);
|
1788
1879
|
}
|
1789
1880
|
// Find the end of this nested list
|
1790
|
-
const listOpenEnd =
|
1881
|
+
const listOpenEnd = parentTextContent.indexOf('>', nextListStart);
|
1791
1882
|
if (listOpenEnd === -1) {
|
1792
1883
|
// Malformed list tag
|
1793
|
-
textParts.push(
|
1884
|
+
textParts.push(parentTextContent.substring(contentPos));
|
1794
1885
|
break;
|
1795
1886
|
}
|
1796
1887
|
// Track depth to find the matching closing tag
|
@@ -1799,9 +1890,9 @@ class NotionAITool {
|
|
1799
1890
|
let listEnd = -1;
|
1800
1891
|
const openTag = `<${listType}`;
|
1801
1892
|
const closeTag = `</${listType}>`;
|
1802
|
-
while (listSearchPos <
|
1803
|
-
const nextListOpen =
|
1804
|
-
const nextListClose =
|
1893
|
+
while (listSearchPos < parentTextContent.length && listDepth > 0) {
|
1894
|
+
const nextListOpen = parentTextContent.indexOf(openTag, listSearchPos);
|
1895
|
+
const nextListClose = parentTextContent.indexOf(closeTag, listSearchPos);
|
1805
1896
|
if (nextListClose === -1)
|
1806
1897
|
break;
|
1807
1898
|
if (nextListOpen !== -1 && nextListOpen < nextListClose) {
|
@@ -1818,7 +1909,8 @@ class NotionAITool {
|
|
1818
1909
|
}
|
1819
1910
|
}
|
1820
1911
|
if (listEnd !== -1) {
|
1821
|
-
// Extract the content between <ul>/<ol> and </ul>/<ol>
|
1912
|
+
// Extract the content between <ul>/<ol> and </ul>/<ol> from ORIGINAL content
|
1913
|
+
// (not parentTextContent which has child blocks removed)
|
1822
1914
|
const listContent = fullItemContent.substring(listOpenEnd + 1, listEnd - closeTag.length);
|
1823
1915
|
item.children.push({
|
1824
1916
|
type: listType,
|
@@ -1828,7 +1920,7 @@ class NotionAITool {
|
|
1828
1920
|
}
|
1829
1921
|
else {
|
1830
1922
|
// Malformed nested list - treat remaining as text
|
1831
|
-
textParts.push(
|
1923
|
+
textParts.push(parentTextContent.substring(contentPos));
|
1832
1924
|
break;
|
1833
1925
|
}
|
1834
1926
|
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "n8n-nodes-notion-advanced",
|
3
|
-
"version": "1.2.
|
3
|
+
"version": "1.2.33-beta",
|
4
4
|
"description": "Advanced n8n Notion nodes: Full-featured workflow node + AI Agent Tool for intelligent Notion automation with 25+ block types (BETA)",
|
5
5
|
"scripts": {},
|
6
6
|
"files": [
|