@jotx-labs/core 2.4.82 → 2.4.183
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 +2 -2
- package/dist/NodeManager.d.ts +140 -0
- package/dist/NodeManager.d.ts.map +1 -0
- package/dist/NodeManager.js +521 -0
- package/dist/NodeManager.js.map +1 -0
- package/dist/__tests__/events.test 2.d.ts +2 -0
- package/dist/__tests__/events.test 2.d.ts.map +1 -0
- package/dist/__tests__/events.test 2.js +63 -0
- package/dist/__tests__/events.test 2.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/parser/serializer.d.ts.map +1 -1
- package/dist/parser/serializer.js +8 -0
- package/dist/parser/serializer.js.map +1 -1
- package/dist/parser/tokenizer.new 2.d.ts +32 -0
- package/dist/parser/tokenizer.new 2.d.ts.map +1 -0
- package/dist/parser/tokenizer.new 2.js +195 -0
- package/dist/parser/tokenizer.new 2.js.map +1 -0
- package/dist/parser/tokenizer.new.d.ts.map +1 -1
- package/dist/parser/tokenizer.new.js +5 -4
- package/dist/parser/tokenizer.new.js.map +1 -1
- package/dist/platform/NullPlatformAdapter 2.d.ts +18 -0
- package/dist/platform/NullPlatformAdapter 2.d.ts.map +1 -0
- package/dist/platform/NullPlatformAdapter 2.js +41 -0
- package/dist/platform/NullPlatformAdapter 2.js.map +1 -0
- package/dist/platform/PlatformAdapter 2.d.ts +22 -0
- package/dist/platform/PlatformAdapter 2.d.ts.map +1 -0
- package/dist/platform/PlatformAdapter 2.js +7 -0
- package/dist/platform/PlatformAdapter 2.js.map +1 -0
- package/dist/platform/index 2.d.ts +3 -0
- package/dist/platform/index 2.d.ts.map +1 -0
- package/dist/{links/index.js → platform/index 2.js } +3 -12
- package/dist/platform/index 2.js.map +1 -0
- package/dist/plugin/PluginSystem 2.d.ts +46 -0
- package/dist/plugin/PluginSystem 2.d.ts.map +1 -0
- package/dist/plugin/PluginSystem 2.js +58 -0
- package/dist/plugin/PluginSystem 2.js.map +1 -0
- package/dist/plugin/index 2.d.ts +2 -0
- package/dist/plugin/index 2.d.ts.map +1 -0
- package/dist/plugin/index 2.js +18 -0
- package/dist/plugin/index 2.js.map +1 -0
- package/dist/types/ast.d.ts.map +1 -1
- package/dist/types/ast.js +3 -0
- package/dist/types/ast.js.map +1 -1
- package/dist/types/enums.d.ts +1 -1
- package/dist/types/enums.d.ts.map +1 -1
- package/dist/types/enums.js +3 -2
- package/dist/types/enums.js.map +1 -1
- package/dist/types/nodeManager 2.d.ts +86 -0
- package/dist/types/nodeManager 2.d.ts.map +1 -0
- package/dist/types/nodeManager 2.js +7 -0
- package/dist/types/nodeManager 2.js.map +1 -0
- package/dist/types/nodeManager.d.ts +2 -0
- package/dist/types/nodeManager.d.ts.map +1 -1
- package/dist/utils/idGenerator 2.d.ts +34 -0
- package/dist/utils/idGenerator 2.d.ts.map +1 -0
- package/dist/utils/idGenerator 2.js +105 -0
- package/dist/utils/idGenerator 2.js.map +1 -0
- package/package.json +3 -3
- package/dist/links/LinkIndexer.d.ts +0 -85
- package/dist/links/LinkIndexer.d.ts.map +0 -1
- package/dist/links/LinkIndexer.js +0 -238
- package/dist/links/LinkIndexer.js.map +0 -1
- package/dist/links/LinkParser.d.ts +0 -41
- package/dist/links/LinkParser.d.ts.map +0 -1
- package/dist/links/LinkParser.js +0 -162
- package/dist/links/LinkParser.js.map +0 -1
- package/dist/links/index.d.ts +0 -12
- package/dist/links/index.d.ts.map +0 -1
- package/dist/links/index.js.map +0 -1
- package/dist/links/types.d.ts +0 -66
- package/dist/links/types.d.ts.map +0 -1
- package/dist/links/types.js +0 -7
- package/dist/links/types.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @jotx/core
|
|
1
|
+
# @jotx-labs/core
|
|
2
2
|
|
|
3
3
|
Platform-agnostic core package for jotx note parsing and serialization.
|
|
4
4
|
|
|
@@ -12,7 +12,7 @@ npm test
|
|
|
12
12
|
## Usage
|
|
13
13
|
|
|
14
14
|
```typescript
|
|
15
|
-
import { Tokenizer } from '@jotx/core';
|
|
15
|
+
import { Tokenizer } from '@jotx-labs/core';
|
|
16
16
|
|
|
17
17
|
const tokenizer = new Tokenizer('hdef page MyNote');
|
|
18
18
|
const tokens = tokenizer.tokenize();
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NodeManager - Platform-agnostic node registry and graph management
|
|
3
|
+
* Shared implementation for VS Code and web applications
|
|
4
|
+
/**
|
|
5
|
+
* INDEXED BLOCK TYPES:
|
|
6
|
+
* - jotx: The document node (parent of all blocks in a file)
|
|
7
|
+
*
|
|
8
|
+
* Visualization & Data:
|
|
9
|
+
* - table: Rich data tables with title
|
|
10
|
+
* - mermaid: Mermaid diagrams with title
|
|
11
|
+
* - chart: Chart.js visualizations with title
|
|
12
|
+
* - math: LaTeX equations with title
|
|
13
|
+
* - properties: Property tables with title
|
|
14
|
+
*
|
|
15
|
+
* Navigational (for knowledge graph):
|
|
16
|
+
* - jotxlink: Reference links (can link to blocks + documents)
|
|
17
|
+
* - link: Hyperlinks (document-level)
|
|
18
|
+
* - button: Navigation buttons (document-level)
|
|
19
|
+
* - card: Dashboard cards (document-level)
|
|
20
|
+
*/
|
|
21
|
+
import type { NodeEntry, DefTreeNode } from './types/nodeManager';
|
|
22
|
+
/**
|
|
23
|
+
* Block types that are indexed by NodeManager for graph visualization.
|
|
24
|
+
*/
|
|
25
|
+
export declare const INDEXED_BLOCK_TYPES: readonly ["table", "mermaid", "chart", "math", "properties", "jotxlink", "link", "button", "card"];
|
|
26
|
+
export type IndexedBlockType = typeof INDEXED_BLOCK_TYPES[number];
|
|
27
|
+
/**
|
|
28
|
+
* Edge representing a link between two nodes
|
|
29
|
+
*/
|
|
30
|
+
export interface NodeEdge {
|
|
31
|
+
sourceId: string;
|
|
32
|
+
targetId: string;
|
|
33
|
+
type: 'jotxlink' | 'link' | 'button' | 'card' | 'parent-child';
|
|
34
|
+
metadata?: {
|
|
35
|
+
label?: string;
|
|
36
|
+
style?: string;
|
|
37
|
+
icon?: string;
|
|
38
|
+
color?: string;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Options for NodeManager initialization
|
|
43
|
+
*/
|
|
44
|
+
export interface NodeManagerOptions {
|
|
45
|
+
/** Optional error handler */
|
|
46
|
+
onError?: (message: string, error: any) => void;
|
|
47
|
+
/** Optional logger */
|
|
48
|
+
log?: (message: string) => void;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Base NodeManager with shared logic across platforms
|
|
52
|
+
*/
|
|
53
|
+
export declare class NodeManager {
|
|
54
|
+
protected nodeRegistry: Map<string, NodeEntry>;
|
|
55
|
+
protected fileNodeIndex: Map<string, Set<string>>;
|
|
56
|
+
protected typeIndex: Map<string, Set<string>>;
|
|
57
|
+
protected defHierarchy: Map<string, DefTreeNode>;
|
|
58
|
+
protected linkGraph: Map<string, Set<string>>;
|
|
59
|
+
protected options: NodeManagerOptions;
|
|
60
|
+
constructor(options?: NodeManagerOptions);
|
|
61
|
+
/**
|
|
62
|
+
* Extract nodes from AST - ONLY indexes document (jotx) and INDEXED_BLOCK_TYPES
|
|
63
|
+
*/
|
|
64
|
+
protected extractNodes(ast: any, filePath: string, fileName: string): void;
|
|
65
|
+
/**
|
|
66
|
+
* Extract ONLY the indexed block types
|
|
67
|
+
*/
|
|
68
|
+
protected extractBlockNodes(blocks: any[], filePath: string, fileNodes: Set<string>, parentId: string): void;
|
|
69
|
+
/**
|
|
70
|
+
* Check if block type should be indexed
|
|
71
|
+
*/
|
|
72
|
+
protected isIndexedBlockType(type: string): boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Extract content preview from block
|
|
75
|
+
*/
|
|
76
|
+
protected extractBlockContent(block: any): string | undefined;
|
|
77
|
+
/**
|
|
78
|
+
* Process JotxLink block - can reference documents OR blocks
|
|
79
|
+
*/
|
|
80
|
+
protected processJotxLink(sourceId: string, properties: any): void;
|
|
81
|
+
/**
|
|
82
|
+
* Process link block - document references only
|
|
83
|
+
*/
|
|
84
|
+
protected processLink(sourceId: string, properties: any): void;
|
|
85
|
+
/**
|
|
86
|
+
* Process button - navigation links
|
|
87
|
+
*/
|
|
88
|
+
protected processButton(sourceId: string, properties: any): void;
|
|
89
|
+
/**
|
|
90
|
+
* Process card - dashboard cards from gridcard
|
|
91
|
+
*/
|
|
92
|
+
protected processCard(sourceId: string, properties: any): void;
|
|
93
|
+
/**
|
|
94
|
+
* Helper: Extract document ID from file path
|
|
95
|
+
*/
|
|
96
|
+
protected extractDocumentId(filePath: string): string;
|
|
97
|
+
/**
|
|
98
|
+
* Add edge to linkGraph
|
|
99
|
+
*/
|
|
100
|
+
protected addEdge(edge: NodeEdge): void;
|
|
101
|
+
/**
|
|
102
|
+
* Add node to registry and indices
|
|
103
|
+
*/
|
|
104
|
+
protected addNode(node: NodeEntry, fileNodes: Set<string>): void;
|
|
105
|
+
/**
|
|
106
|
+
* Build def tree for a document
|
|
107
|
+
*/
|
|
108
|
+
protected buildDefTree(ast: any, filePath: string): void;
|
|
109
|
+
/**
|
|
110
|
+
* Remove all nodes from a file
|
|
111
|
+
*/
|
|
112
|
+
protected removeFileNodes(filePath: string): void;
|
|
113
|
+
getNode(id: string): NodeEntry | undefined;
|
|
114
|
+
getNodeByShortId(shortId: string): NodeEntry | undefined;
|
|
115
|
+
getNodesByFile(filePath: string): NodeEntry[];
|
|
116
|
+
getNodesByType(type: string): NodeEntry[];
|
|
117
|
+
getAllNodes(): NodeEntry[];
|
|
118
|
+
getAllDocuments(): NodeEntry[];
|
|
119
|
+
getAllBlocks(): NodeEntry[];
|
|
120
|
+
/**
|
|
121
|
+
* Get all edges for graph visualization (all link types)
|
|
122
|
+
*/
|
|
123
|
+
getAllEdges(): NodeEdge[];
|
|
124
|
+
/**
|
|
125
|
+
* Legacy method for backward compatibility
|
|
126
|
+
* @deprecated Use getAllEdges() instead
|
|
127
|
+
*/
|
|
128
|
+
getJotxLinkEdges(): NodeEdge[];
|
|
129
|
+
getLinkedNodes(nodeId: string): NodeEntry[];
|
|
130
|
+
getBacklinks(nodeId: string): NodeEntry[];
|
|
131
|
+
getDefTree(filePath: string): DefTreeNode | undefined;
|
|
132
|
+
validateUniqueIds(): Map<string, string[]>;
|
|
133
|
+
clear(): void;
|
|
134
|
+
getStats(): {
|
|
135
|
+
documents: number;
|
|
136
|
+
blocks: number;
|
|
137
|
+
edges: number;
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=NodeManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NodeManager.d.ts","sourceRoot":"","sources":["../src/NodeManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* NodeManager - Platform-agnostic node registry and graph management
|
|
4
|
+
* Shared implementation for VS Code and web applications
|
|
5
|
+
/**
|
|
6
|
+
* INDEXED BLOCK TYPES:
|
|
7
|
+
* - jotx: The document node (parent of all blocks in a file)
|
|
8
|
+
*
|
|
9
|
+
* Visualization & Data:
|
|
10
|
+
* - table: Rich data tables with title
|
|
11
|
+
* - mermaid: Mermaid diagrams with title
|
|
12
|
+
* - chart: Chart.js visualizations with title
|
|
13
|
+
* - math: LaTeX equations with title
|
|
14
|
+
* - properties: Property tables with title
|
|
15
|
+
*
|
|
16
|
+
* Navigational (for knowledge graph):
|
|
17
|
+
* - jotxlink: Reference links (can link to blocks + documents)
|
|
18
|
+
* - link: Hyperlinks (document-level)
|
|
19
|
+
* - button: Navigation buttons (document-level)
|
|
20
|
+
* - card: Dashboard cards (document-level)
|
|
21
|
+
*/
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.NodeManager = exports.INDEXED_BLOCK_TYPES = void 0;
|
|
24
|
+
/**
|
|
25
|
+
* Block types that are indexed by NodeManager for graph visualization.
|
|
26
|
+
*/
|
|
27
|
+
exports.INDEXED_BLOCK_TYPES = [
|
|
28
|
+
// Visualization & Data
|
|
29
|
+
'table',
|
|
30
|
+
'mermaid',
|
|
31
|
+
'chart',
|
|
32
|
+
'math',
|
|
33
|
+
'properties',
|
|
34
|
+
// Navigational (for knowledge graph)
|
|
35
|
+
'jotxlink',
|
|
36
|
+
'link',
|
|
37
|
+
'button',
|
|
38
|
+
'card'
|
|
39
|
+
];
|
|
40
|
+
/**
|
|
41
|
+
* Base NodeManager with shared logic across platforms
|
|
42
|
+
*/
|
|
43
|
+
class NodeManager {
|
|
44
|
+
constructor(options = {}) {
|
|
45
|
+
// Fast O(1) lookups
|
|
46
|
+
this.nodeRegistry = new Map();
|
|
47
|
+
this.fileNodeIndex = new Map();
|
|
48
|
+
this.typeIndex = new Map();
|
|
49
|
+
this.defHierarchy = new Map();
|
|
50
|
+
this.linkGraph = new Map();
|
|
51
|
+
this.options = {
|
|
52
|
+
onError: options.onError || ((msg, err) => console.error(msg, err)),
|
|
53
|
+
log: options.log || (() => { })
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Extract nodes from AST - ONLY indexes document (jotx) and INDEXED_BLOCK_TYPES
|
|
58
|
+
*/
|
|
59
|
+
extractNodes(ast, filePath, fileName) {
|
|
60
|
+
const fileNodes = new Set();
|
|
61
|
+
// 1. Add document node (jotx - the parent node)
|
|
62
|
+
const docTitle = ast.document.metadata?.title || fileName;
|
|
63
|
+
const docId = ast.document.id || fileName;
|
|
64
|
+
const docType = ast.document.type || 'jotx';
|
|
65
|
+
const docMetadataType = ast.document.metadata?.type || undefined;
|
|
66
|
+
const docNode = {
|
|
67
|
+
id: docId,
|
|
68
|
+
type: 'jotx',
|
|
69
|
+
filePath,
|
|
70
|
+
label: docTitle,
|
|
71
|
+
properties: {
|
|
72
|
+
...ast.document.metadata,
|
|
73
|
+
documentType: docMetadataType
|
|
74
|
+
},
|
|
75
|
+
content: docTitle,
|
|
76
|
+
parentId: undefined,
|
|
77
|
+
children: []
|
|
78
|
+
};
|
|
79
|
+
this.addNode(docNode, fileNodes);
|
|
80
|
+
// 2. Extract indexed block nodes
|
|
81
|
+
this.extractBlockNodes(ast.document.blocks || [], filePath, fileNodes, docId);
|
|
82
|
+
// Update file index
|
|
83
|
+
this.fileNodeIndex.set(filePath, fileNodes);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Extract ONLY the indexed block types
|
|
87
|
+
*/
|
|
88
|
+
extractBlockNodes(blocks, filePath, fileNodes, parentId) {
|
|
89
|
+
if (!blocks)
|
|
90
|
+
return;
|
|
91
|
+
for (const block of blocks) {
|
|
92
|
+
const blockType = block.type;
|
|
93
|
+
// FILTER: Only index specified block types
|
|
94
|
+
if (!this.isIndexedBlockType(blockType)) {
|
|
95
|
+
// Check children of non-indexed blocks
|
|
96
|
+
if (block.children) {
|
|
97
|
+
this.extractBlockNodes(block.children, filePath, fileNodes, parentId);
|
|
98
|
+
}
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
const nodeId = `${parentId}#${block.id}`;
|
|
102
|
+
// Determine label based on block type
|
|
103
|
+
let label = block.properties?.title || block.id;
|
|
104
|
+
if (blockType === 'jotxlink') {
|
|
105
|
+
label = 'JotxLink';
|
|
106
|
+
}
|
|
107
|
+
// Create node entry with ALL properties and children preserved
|
|
108
|
+
const node = {
|
|
109
|
+
id: nodeId,
|
|
110
|
+
type: blockType,
|
|
111
|
+
filePath,
|
|
112
|
+
label,
|
|
113
|
+
properties: { ...block.properties },
|
|
114
|
+
content: this.extractBlockContent(block),
|
|
115
|
+
parentId,
|
|
116
|
+
children: [],
|
|
117
|
+
// Preserve original AST children for content rendering (tables, callouts, etc.)
|
|
118
|
+
astChildren: block.children || undefined
|
|
119
|
+
};
|
|
120
|
+
this.addNode(node, fileNodes);
|
|
121
|
+
// Update parent children
|
|
122
|
+
const parent = this.nodeRegistry.get(parentId);
|
|
123
|
+
if (parent) {
|
|
124
|
+
parent.children.push(nodeId);
|
|
125
|
+
}
|
|
126
|
+
// Process links: populate linkGraph based on block type
|
|
127
|
+
if (blockType === 'jotxlink') {
|
|
128
|
+
this.processJotxLink(nodeId, block.properties);
|
|
129
|
+
}
|
|
130
|
+
else if (blockType === 'link') {
|
|
131
|
+
this.processLink(nodeId, block.properties);
|
|
132
|
+
}
|
|
133
|
+
else if (blockType === 'button') {
|
|
134
|
+
this.processButton(nodeId, block.properties);
|
|
135
|
+
}
|
|
136
|
+
else if (blockType === 'card') {
|
|
137
|
+
this.processCard(nodeId, block.properties);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Check if block type should be indexed
|
|
143
|
+
*/
|
|
144
|
+
isIndexedBlockType(type) {
|
|
145
|
+
return exports.INDEXED_BLOCK_TYPES.includes(type);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Extract content preview from block
|
|
149
|
+
*/
|
|
150
|
+
extractBlockContent(block) {
|
|
151
|
+
const props = block.properties || {};
|
|
152
|
+
if (props.text)
|
|
153
|
+
return props.text;
|
|
154
|
+
if (props.src)
|
|
155
|
+
return props.src;
|
|
156
|
+
if (props.json)
|
|
157
|
+
return typeof props.json === 'string' ? props.json : JSON.stringify(props.json);
|
|
158
|
+
if (props.title)
|
|
159
|
+
return props.title;
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Process JotxLink block - can reference documents OR blocks
|
|
164
|
+
*/
|
|
165
|
+
processJotxLink(sourceId, properties) {
|
|
166
|
+
const refId = properties?.refid || properties?.refId;
|
|
167
|
+
if (!refId)
|
|
168
|
+
return;
|
|
169
|
+
this.addEdge({
|
|
170
|
+
sourceId,
|
|
171
|
+
targetId: refId,
|
|
172
|
+
type: 'jotxlink',
|
|
173
|
+
metadata: { label: properties?.title }
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Process link block - document references only
|
|
178
|
+
*/
|
|
179
|
+
processLink(sourceId, properties) {
|
|
180
|
+
const url = properties?.url || properties?.href;
|
|
181
|
+
if (!url)
|
|
182
|
+
return;
|
|
183
|
+
// Only track internal .jot file links
|
|
184
|
+
if (url.endsWith('.jot') || url.endsWith('.jotx')) {
|
|
185
|
+
this.addEdge({
|
|
186
|
+
sourceId,
|
|
187
|
+
targetId: this.extractDocumentId(url),
|
|
188
|
+
type: 'link',
|
|
189
|
+
metadata: { label: properties?.text }
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Process button - navigation links
|
|
195
|
+
*/
|
|
196
|
+
processButton(sourceId, properties) {
|
|
197
|
+
const link = properties?.link;
|
|
198
|
+
if (!link || !(link.endsWith('.jot') || link.endsWith('.jotx')))
|
|
199
|
+
return;
|
|
200
|
+
this.addEdge({
|
|
201
|
+
sourceId,
|
|
202
|
+
targetId: this.extractDocumentId(link),
|
|
203
|
+
type: 'button',
|
|
204
|
+
metadata: {
|
|
205
|
+
label: properties?.label,
|
|
206
|
+
style: properties?.style
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Process card - dashboard cards from gridcard
|
|
212
|
+
*/
|
|
213
|
+
processCard(sourceId, properties) {
|
|
214
|
+
const link = properties?.link;
|
|
215
|
+
if (!link || !(link.endsWith('.jot') || link.endsWith('.jotx')))
|
|
216
|
+
return;
|
|
217
|
+
this.addEdge({
|
|
218
|
+
sourceId,
|
|
219
|
+
targetId: this.extractDocumentId(link),
|
|
220
|
+
type: 'card',
|
|
221
|
+
metadata: {
|
|
222
|
+
label: properties?.title,
|
|
223
|
+
icon: properties?.icon,
|
|
224
|
+
color: properties?.color
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Helper: Extract document ID from file path
|
|
230
|
+
*/
|
|
231
|
+
extractDocumentId(filePath) {
|
|
232
|
+
// "./docs/example.jot" → "example"
|
|
233
|
+
// "../other.jot" → "other"
|
|
234
|
+
// "folder/file.jot" → "file"
|
|
235
|
+
const fileName = filePath.split('/').pop();
|
|
236
|
+
return fileName?.replace(/\.(jot|jotx)$/, '') || filePath;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Add edge to linkGraph
|
|
240
|
+
*/
|
|
241
|
+
addEdge(edge) {
|
|
242
|
+
const key = `${edge.sourceId}:${edge.targetId}:${edge.type}`;
|
|
243
|
+
if (!this.linkGraph.has(edge.sourceId)) {
|
|
244
|
+
this.linkGraph.set(edge.sourceId, new Set());
|
|
245
|
+
}
|
|
246
|
+
// Store target ID (we'll reconstruct edges with metadata when needed)
|
|
247
|
+
this.linkGraph.get(edge.sourceId).add(edge.targetId);
|
|
248
|
+
this.options.log?.(`[NodeManager] ${edge.type}: ${edge.sourceId} -> ${edge.targetId}`);
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Add node to registry and indices
|
|
252
|
+
*/
|
|
253
|
+
addNode(node, fileNodes) {
|
|
254
|
+
this.nodeRegistry.set(node.id, node);
|
|
255
|
+
fileNodes.add(node.id);
|
|
256
|
+
// Add to type index
|
|
257
|
+
if (!this.typeIndex.has(node.type)) {
|
|
258
|
+
this.typeIndex.set(node.type, new Set());
|
|
259
|
+
}
|
|
260
|
+
this.typeIndex.get(node.type).add(node.id);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Build def tree for a document
|
|
264
|
+
*/
|
|
265
|
+
buildDefTree(ast, filePath) {
|
|
266
|
+
const buildTree = (blocks, parentId) => {
|
|
267
|
+
const children = [];
|
|
268
|
+
for (const block of blocks) {
|
|
269
|
+
if (this.isIndexedBlockType(block.type)) {
|
|
270
|
+
const nodeId = `${filePath}#${block.id}`;
|
|
271
|
+
const defNode = {
|
|
272
|
+
nodeId,
|
|
273
|
+
children: block.children ? buildTree(block.children, nodeId) : []
|
|
274
|
+
};
|
|
275
|
+
children.push(defNode);
|
|
276
|
+
}
|
|
277
|
+
else if (block.children) {
|
|
278
|
+
// Continue searching in nested blocks
|
|
279
|
+
children.push(...buildTree(block.children, parentId));
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return children;
|
|
283
|
+
};
|
|
284
|
+
const tree = {
|
|
285
|
+
nodeId: filePath,
|
|
286
|
+
children: buildTree(ast.document.blocks, filePath)
|
|
287
|
+
};
|
|
288
|
+
this.defHierarchy.set(filePath, tree);
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Remove all nodes from a file
|
|
292
|
+
*/
|
|
293
|
+
removeFileNodes(filePath) {
|
|
294
|
+
const nodeIds = this.fileNodeIndex.get(filePath);
|
|
295
|
+
if (!nodeIds)
|
|
296
|
+
return;
|
|
297
|
+
for (const nodeId of nodeIds) {
|
|
298
|
+
const node = this.nodeRegistry.get(nodeId);
|
|
299
|
+
if (node) {
|
|
300
|
+
// Remove from type index
|
|
301
|
+
this.typeIndex.get(node.type)?.delete(nodeId);
|
|
302
|
+
}
|
|
303
|
+
// Remove from registry
|
|
304
|
+
this.nodeRegistry.delete(nodeId);
|
|
305
|
+
// Remove from linkGraph (as source)
|
|
306
|
+
this.linkGraph.delete(nodeId);
|
|
307
|
+
}
|
|
308
|
+
// Remove file index
|
|
309
|
+
this.fileNodeIndex.delete(filePath);
|
|
310
|
+
this.defHierarchy.delete(filePath);
|
|
311
|
+
}
|
|
312
|
+
// ========== Public API ==========
|
|
313
|
+
getNode(id) {
|
|
314
|
+
this.options.log?.(`[NodeManager] getNode called with: "${id}", registry size: ${this.nodeRegistry.size}`);
|
|
315
|
+
// First try direct lookup
|
|
316
|
+
let node = this.nodeRegistry.get(id);
|
|
317
|
+
if (node) {
|
|
318
|
+
this.options.log?.(`[NodeManager] getNode DIRECT HIT: ${id}`);
|
|
319
|
+
return node;
|
|
320
|
+
}
|
|
321
|
+
this.options.log?.(`[NodeManager] getNode direct lookup MISS for: "${id}"`);
|
|
322
|
+
// If ID contains #, try parsing as documentId#blockId
|
|
323
|
+
if (id.includes('#')) {
|
|
324
|
+
const [docPart, blockPart] = id.split('#');
|
|
325
|
+
this.options.log?.(`[NodeManager] getNode parsing: docPart="${docPart}", blockPart="${blockPart}"`);
|
|
326
|
+
// Search for nodes where the ID ends with #blockPart
|
|
327
|
+
for (const [nodeId, nodeEntry] of this.nodeRegistry) {
|
|
328
|
+
if (nodeId.endsWith(`#${blockPart}`)) {
|
|
329
|
+
this.options.log?.(`[NodeManager] getNode found via blockPart match: ${nodeId}`);
|
|
330
|
+
return nodeEntry;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
this.options.log?.(`[NodeManager] getNode no blockPart match found for #${blockPart}`);
|
|
334
|
+
}
|
|
335
|
+
this.options.log?.(`[NodeManager] getNode returning undefined for: "${id}"`);
|
|
336
|
+
return undefined;
|
|
337
|
+
}
|
|
338
|
+
getNodeByShortId(shortId) {
|
|
339
|
+
this.options.log?.(`[NodeManager] getNodeByShortId called with: "${shortId}"`);
|
|
340
|
+
// First try direct lookup
|
|
341
|
+
const direct = this.nodeRegistry.get(shortId);
|
|
342
|
+
if (direct) {
|
|
343
|
+
this.options.log?.(`[NodeManager] Direct match found`);
|
|
344
|
+
return direct;
|
|
345
|
+
}
|
|
346
|
+
// Try looking up by filePath (for document nodes)
|
|
347
|
+
if (shortId.endsWith('.jot') || shortId.endsWith('.jotx')) {
|
|
348
|
+
for (const [nodeId, node] of this.nodeRegistry) {
|
|
349
|
+
if (node.filePath === shortId && node.type === 'jotx') {
|
|
350
|
+
this.options.log?.(`[NodeManager] Found document by filePath: ${nodeId}`);
|
|
351
|
+
return node;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
// Search for blocks: their ID is "filePath#shortId"
|
|
356
|
+
for (const [nodeId, node] of this.nodeRegistry) {
|
|
357
|
+
if (nodeId.endsWith(`#${shortId}`)) {
|
|
358
|
+
this.options.log?.(`[NodeManager] Found via #suffix match: ${nodeId}`);
|
|
359
|
+
return node;
|
|
360
|
+
}
|
|
361
|
+
const idPart = nodeId.split('#').pop();
|
|
362
|
+
if (idPart === shortId) {
|
|
363
|
+
this.options.log?.(`[NodeManager] Found via split match: ${nodeId}`);
|
|
364
|
+
return node;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
this.options.log?.(`[NodeManager] No node found for "${shortId}"`);
|
|
368
|
+
return undefined;
|
|
369
|
+
}
|
|
370
|
+
getNodesByFile(filePath) {
|
|
371
|
+
const nodeIds = this.fileNodeIndex.get(filePath);
|
|
372
|
+
if (!nodeIds)
|
|
373
|
+
return [];
|
|
374
|
+
return Array.from(nodeIds)
|
|
375
|
+
.map(id => this.nodeRegistry.get(id))
|
|
376
|
+
.filter((node) => node !== undefined);
|
|
377
|
+
}
|
|
378
|
+
getNodesByType(type) {
|
|
379
|
+
const nodeIds = this.typeIndex.get(type);
|
|
380
|
+
if (!nodeIds)
|
|
381
|
+
return [];
|
|
382
|
+
return Array.from(nodeIds)
|
|
383
|
+
.map(id => this.nodeRegistry.get(id))
|
|
384
|
+
.filter((node) => node !== undefined);
|
|
385
|
+
}
|
|
386
|
+
getAllNodes() {
|
|
387
|
+
return Array.from(this.nodeRegistry.values());
|
|
388
|
+
}
|
|
389
|
+
getAllDocuments() {
|
|
390
|
+
return Array.from(this.nodeRegistry.values()).filter(node => !node.parentId);
|
|
391
|
+
}
|
|
392
|
+
getAllBlocks() {
|
|
393
|
+
const blocks = [];
|
|
394
|
+
for (const type of exports.INDEXED_BLOCK_TYPES) {
|
|
395
|
+
blocks.push(...this.getNodesByType(type));
|
|
396
|
+
}
|
|
397
|
+
return blocks;
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Get all edges for graph visualization (all link types)
|
|
401
|
+
*/
|
|
402
|
+
getAllEdges() {
|
|
403
|
+
const edges = [];
|
|
404
|
+
for (const [sourceId, targets] of this.linkGraph) {
|
|
405
|
+
const sourceNode = this.nodeRegistry.get(sourceId);
|
|
406
|
+
if (!sourceNode)
|
|
407
|
+
continue;
|
|
408
|
+
for (const targetId of targets) {
|
|
409
|
+
// Determine edge type from source node type
|
|
410
|
+
let edgeType = 'jotxlink';
|
|
411
|
+
let metadata = {};
|
|
412
|
+
if (sourceNode.type === 'jotxlink') {
|
|
413
|
+
edgeType = 'jotxlink';
|
|
414
|
+
metadata = { label: sourceNode.properties?.title };
|
|
415
|
+
}
|
|
416
|
+
else if (sourceNode.type === 'link') {
|
|
417
|
+
edgeType = 'link';
|
|
418
|
+
metadata = { label: sourceNode.properties?.text };
|
|
419
|
+
}
|
|
420
|
+
else if (sourceNode.type === 'button') {
|
|
421
|
+
edgeType = 'button';
|
|
422
|
+
metadata = {
|
|
423
|
+
label: sourceNode.properties?.label,
|
|
424
|
+
style: sourceNode.properties?.style
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
else if (sourceNode.type === 'card') {
|
|
428
|
+
edgeType = 'card';
|
|
429
|
+
metadata = {
|
|
430
|
+
label: sourceNode.properties?.title,
|
|
431
|
+
icon: sourceNode.properties?.icon,
|
|
432
|
+
color: sourceNode.properties?.color
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
edges.push({
|
|
436
|
+
sourceId,
|
|
437
|
+
targetId,
|
|
438
|
+
type: edgeType,
|
|
439
|
+
metadata
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
return edges;
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Legacy method for backward compatibility
|
|
447
|
+
* @deprecated Use getAllEdges() instead
|
|
448
|
+
*/
|
|
449
|
+
getJotxLinkEdges() {
|
|
450
|
+
return this.getAllEdges().filter(edge => edge.type === 'jotxlink');
|
|
451
|
+
}
|
|
452
|
+
getLinkedNodes(nodeId) {
|
|
453
|
+
const targetIds = this.linkGraph.get(nodeId);
|
|
454
|
+
if (!targetIds)
|
|
455
|
+
return [];
|
|
456
|
+
return Array.from(targetIds)
|
|
457
|
+
.map(id => this.getNodeByShortId(id) || this.nodeRegistry.get(id))
|
|
458
|
+
.filter((node) => node !== undefined);
|
|
459
|
+
}
|
|
460
|
+
getBacklinks(nodeId) {
|
|
461
|
+
const backlinks = [];
|
|
462
|
+
// Extract shortId from full nodeId for comparison
|
|
463
|
+
const targetShortId = nodeId.includes('#') ? nodeId.split('#').pop() : nodeId;
|
|
464
|
+
for (const [sourceId, targets] of this.linkGraph) {
|
|
465
|
+
for (const targetId of targets) {
|
|
466
|
+
// Match by full ID or short ID
|
|
467
|
+
if (targetId === nodeId || targetId === targetShortId) {
|
|
468
|
+
const node = this.nodeRegistry.get(sourceId);
|
|
469
|
+
if (node)
|
|
470
|
+
backlinks.push(node);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
return backlinks;
|
|
475
|
+
}
|
|
476
|
+
getDefTree(filePath) {
|
|
477
|
+
return this.defHierarchy.get(filePath);
|
|
478
|
+
}
|
|
479
|
+
validateUniqueIds() {
|
|
480
|
+
const duplicates = new Map();
|
|
481
|
+
const idToFiles = new Map();
|
|
482
|
+
for (const [nodeId, node] of this.nodeRegistry) {
|
|
483
|
+
// Skip document nodes
|
|
484
|
+
if (node.type === 'jotx')
|
|
485
|
+
continue;
|
|
486
|
+
// Extract shortUUID from nodeId
|
|
487
|
+
const parts = nodeId.split('#');
|
|
488
|
+
if (parts.length !== 2)
|
|
489
|
+
continue;
|
|
490
|
+
const shortId = parts[1];
|
|
491
|
+
const filePath = node.filePath;
|
|
492
|
+
if (!idToFiles.has(shortId)) {
|
|
493
|
+
idToFiles.set(shortId, []);
|
|
494
|
+
}
|
|
495
|
+
idToFiles.get(shortId).push(filePath);
|
|
496
|
+
}
|
|
497
|
+
// Find duplicates
|
|
498
|
+
for (const [id, files] of idToFiles) {
|
|
499
|
+
if (files.length > 1) {
|
|
500
|
+
duplicates.set(id, files);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
return duplicates;
|
|
504
|
+
}
|
|
505
|
+
clear() {
|
|
506
|
+
this.nodeRegistry.clear();
|
|
507
|
+
this.fileNodeIndex.clear();
|
|
508
|
+
this.typeIndex.clear();
|
|
509
|
+
this.defHierarchy.clear();
|
|
510
|
+
this.linkGraph.clear();
|
|
511
|
+
}
|
|
512
|
+
getStats() {
|
|
513
|
+
return {
|
|
514
|
+
documents: this.getAllDocuments().length,
|
|
515
|
+
blocks: this.getAllBlocks().length,
|
|
516
|
+
edges: this.getAllEdges().length
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
exports.NodeManager = NodeManager;
|
|
521
|
+
//# sourceMappingURL=NodeManager.js.map
|