@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.
Files changed (76) hide show
  1. package/README.md +2 -2
  2. package/dist/NodeManager.d.ts +140 -0
  3. package/dist/NodeManager.d.ts.map +1 -0
  4. package/dist/NodeManager.js +521 -0
  5. package/dist/NodeManager.js.map +1 -0
  6. package/dist/__tests__/events.test 2.d.ts +2 -0
  7. package/dist/__tests__/events.test 2.d.ts.map +1 -0
  8. package/dist/__tests__/events.test 2.js +63 -0
  9. package/dist/__tests__/events.test 2.js.map +1 -0
  10. package/dist/index.d.ts +1 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +5 -1
  13. package/dist/index.js.map +1 -1
  14. package/dist/parser/serializer.d.ts.map +1 -1
  15. package/dist/parser/serializer.js +8 -0
  16. package/dist/parser/serializer.js.map +1 -1
  17. package/dist/parser/tokenizer.new 2.d.ts +32 -0
  18. package/dist/parser/tokenizer.new 2.d.ts.map +1 -0
  19. package/dist/parser/tokenizer.new 2.js +195 -0
  20. package/dist/parser/tokenizer.new 2.js.map +1 -0
  21. package/dist/parser/tokenizer.new.d.ts.map +1 -1
  22. package/dist/parser/tokenizer.new.js +5 -4
  23. package/dist/parser/tokenizer.new.js.map +1 -1
  24. package/dist/platform/NullPlatformAdapter 2.d.ts +18 -0
  25. package/dist/platform/NullPlatformAdapter 2.d.ts.map +1 -0
  26. package/dist/platform/NullPlatformAdapter 2.js +41 -0
  27. package/dist/platform/NullPlatformAdapter 2.js.map +1 -0
  28. package/dist/platform/PlatformAdapter 2.d.ts +22 -0
  29. package/dist/platform/PlatformAdapter 2.d.ts.map +1 -0
  30. package/dist/platform/PlatformAdapter 2.js +7 -0
  31. package/dist/platform/PlatformAdapter 2.js.map +1 -0
  32. package/dist/platform/index 2.d.ts +3 -0
  33. package/dist/platform/index 2.d.ts.map +1 -0
  34. package/dist/{links/index.js → platform/index 2.js } +3 -12
  35. package/dist/platform/index 2.js.map +1 -0
  36. package/dist/plugin/PluginSystem 2.d.ts +46 -0
  37. package/dist/plugin/PluginSystem 2.d.ts.map +1 -0
  38. package/dist/plugin/PluginSystem 2.js +58 -0
  39. package/dist/plugin/PluginSystem 2.js.map +1 -0
  40. package/dist/plugin/index 2.d.ts +2 -0
  41. package/dist/plugin/index 2.d.ts.map +1 -0
  42. package/dist/plugin/index 2.js +18 -0
  43. package/dist/plugin/index 2.js.map +1 -0
  44. package/dist/types/ast.d.ts.map +1 -1
  45. package/dist/types/ast.js +3 -0
  46. package/dist/types/ast.js.map +1 -1
  47. package/dist/types/enums.d.ts +1 -1
  48. package/dist/types/enums.d.ts.map +1 -1
  49. package/dist/types/enums.js +3 -2
  50. package/dist/types/enums.js.map +1 -1
  51. package/dist/types/nodeManager 2.d.ts +86 -0
  52. package/dist/types/nodeManager 2.d.ts.map +1 -0
  53. package/dist/types/nodeManager 2.js +7 -0
  54. package/dist/types/nodeManager 2.js.map +1 -0
  55. package/dist/types/nodeManager.d.ts +2 -0
  56. package/dist/types/nodeManager.d.ts.map +1 -1
  57. package/dist/utils/idGenerator 2.d.ts +34 -0
  58. package/dist/utils/idGenerator 2.d.ts.map +1 -0
  59. package/dist/utils/idGenerator 2.js +105 -0
  60. package/dist/utils/idGenerator 2.js.map +1 -0
  61. package/package.json +3 -3
  62. package/dist/links/LinkIndexer.d.ts +0 -85
  63. package/dist/links/LinkIndexer.d.ts.map +0 -1
  64. package/dist/links/LinkIndexer.js +0 -238
  65. package/dist/links/LinkIndexer.js.map +0 -1
  66. package/dist/links/LinkParser.d.ts +0 -41
  67. package/dist/links/LinkParser.d.ts.map +0 -1
  68. package/dist/links/LinkParser.js +0 -162
  69. package/dist/links/LinkParser.js.map +0 -1
  70. package/dist/links/index.d.ts +0 -12
  71. package/dist/links/index.d.ts.map +0 -1
  72. package/dist/links/index.js.map +0 -1
  73. package/dist/links/types.d.ts +0 -66
  74. package/dist/links/types.d.ts.map +0 -1
  75. package/dist/links/types.js +0 -7
  76. 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