@zengjing/xmind-mcp 1.0.0

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 (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +574 -0
  3. package/dist/cli.d.ts +7 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +170 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/core/legacy-parser.d.ts +63 -0
  8. package/dist/core/legacy-parser.d.ts.map +1 -0
  9. package/dist/core/legacy-parser.js +139 -0
  10. package/dist/core/legacy-parser.js.map +1 -0
  11. package/dist/core/parser.d.ts +19 -0
  12. package/dist/core/parser.d.ts.map +1 -0
  13. package/dist/core/parser.js +111 -0
  14. package/dist/core/parser.js.map +1 -0
  15. package/dist/core/zen-parser.d.ts +15 -0
  16. package/dist/core/zen-parser.d.ts.map +1 -0
  17. package/dist/core/zen-parser.js +90 -0
  18. package/dist/core/zen-parser.js.map +1 -0
  19. package/dist/formatters/json-formatter.d.ts +69 -0
  20. package/dist/formatters/json-formatter.d.ts.map +1 -0
  21. package/dist/formatters/json-formatter.js +131 -0
  22. package/dist/formatters/json-formatter.js.map +1 -0
  23. package/dist/formatters/markdown-formatter.d.ts +40 -0
  24. package/dist/formatters/markdown-formatter.d.ts.map +1 -0
  25. package/dist/formatters/markdown-formatter.js +136 -0
  26. package/dist/formatters/markdown-formatter.js.map +1 -0
  27. package/dist/index.d.ts +6 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +117 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/model/schemas.d.ts +54 -0
  32. package/dist/model/schemas.d.ts.map +1 -0
  33. package/dist/model/schemas.js +64 -0
  34. package/dist/model/schemas.js.map +1 -0
  35. package/dist/model/types.d.ts +79 -0
  36. package/dist/model/types.d.ts.map +1 -0
  37. package/dist/model/types.js +6 -0
  38. package/dist/model/types.js.map +1 -0
  39. package/dist/tools/branch-tool.d.ts +20 -0
  40. package/dist/tools/branch-tool.d.ts.map +1 -0
  41. package/dist/tools/branch-tool.js +150 -0
  42. package/dist/tools/branch-tool.js.map +1 -0
  43. package/dist/tools/parse-tool.d.ts +27 -0
  44. package/dist/tools/parse-tool.d.ts.map +1 -0
  45. package/dist/tools/parse-tool.js +126 -0
  46. package/dist/tools/parse-tool.js.map +1 -0
  47. package/dist/tools/search-tool.d.ts +27 -0
  48. package/dist/tools/search-tool.d.ts.map +1 -0
  49. package/dist/tools/search-tool.js +179 -0
  50. package/dist/tools/search-tool.js.map +1 -0
  51. package/dist/utils/errors.d.ts +26 -0
  52. package/dist/utils/errors.d.ts.map +1 -0
  53. package/dist/utils/errors.js +41 -0
  54. package/dist/utils/errors.js.map +1 -0
  55. package/dist/utils/file-utils.d.ts +25 -0
  56. package/dist/utils/file-utils.d.ts.map +1 -0
  57. package/dist/utils/file-utils.js +58 -0
  58. package/dist/utils/file-utils.js.map +1 -0
  59. package/dist/utils/logger.d.ts +8 -0
  60. package/dist/utils/logger.d.ts.map +1 -0
  61. package/dist/utils/logger.js +23 -0
  62. package/dist/utils/logger.js.map +1 -0
  63. package/package.json +67 -0
package/dist/cli.js ADDED
@@ -0,0 +1,170 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI tool for extracting and analyzing XMind files.
4
+ * Usage: node dist/cli.js <file-path> [options]
5
+ */
6
+ import * as fs from 'fs/promises';
7
+ import * as path from 'path';
8
+ import { parseXMindFile } from './core/parser.js';
9
+ import { xmindToMarkdown } from './formatters/markdown-formatter.js';
10
+ import { xmindToJSON } from './formatters/json-formatter.js';
11
+ import { isXMindError } from './utils/errors.js';
12
+ async function main() {
13
+ const args = process.argv.slice(2);
14
+ if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
15
+ printHelp();
16
+ process.exit(0);
17
+ }
18
+ const filePath = args[0];
19
+ const options = parseArgs(args.slice(1));
20
+ try {
21
+ // Resolve file path (support ~ expansion)
22
+ const resolvedPath = filePath.startsWith('~')
23
+ ? path.join(process.env.HOME || '/', filePath.slice(1))
24
+ : path.resolve(filePath);
25
+ // Check file exists
26
+ try {
27
+ await fs.access(resolvedPath);
28
+ }
29
+ catch {
30
+ console.error(`āŒ File not found: ${resolvedPath}`);
31
+ process.exit(1);
32
+ }
33
+ if (options.verbose) {
34
+ console.log(`šŸ“– Parsing: ${resolvedPath}`);
35
+ }
36
+ // Parse the file
37
+ const result = await parseXMindFile(resolvedPath);
38
+ // Display metadata
39
+ console.log('\n═══════════════════════════════════════');
40
+ console.log('šŸ“Š XMind File Information');
41
+ console.log('═══════════════════════════════════════');
42
+ console.log(`Format: ${result.meta?.formatVersion}`);
43
+ console.log(`Sheets: ${result.sheets.length}`);
44
+ console.log(`Total Topics: ${result.meta?.topicCount}`);
45
+ console.log(`Parsed at: ${result.meta?.parsedAt}`);
46
+ if (options.verbose) {
47
+ result.sheets.forEach((sheet, idx) => {
48
+ console.log(` Sheet ${idx + 1}: "${sheet.title}"`);
49
+ });
50
+ }
51
+ // Format content
52
+ let content;
53
+ if (options.format === 'json') {
54
+ content = xmindToJSON(result);
55
+ }
56
+ else {
57
+ content = xmindToMarkdown(result);
58
+ }
59
+ // Handle search
60
+ if (options.search) {
61
+ content = filterContentBySearch(content, options.search, options.format);
62
+ }
63
+ // Output
64
+ console.log('\n═══════════════════════════════════════');
65
+ console.log(`šŸ“„ Content (${options.format.toUpperCase()} format)`);
66
+ console.log('═══════════════════════════════════════\n');
67
+ console.log(content);
68
+ // Save to file if specified
69
+ if (options.output) {
70
+ await fs.writeFile(options.output, content, 'utf-8');
71
+ console.log(`\nāœ… Content saved to: ${options.output}`);
72
+ }
73
+ process.exit(0);
74
+ }
75
+ catch (error) {
76
+ if (isXMindError(error)) {
77
+ console.error(`\nāŒ Error: ${error.message}`);
78
+ }
79
+ else if (error instanceof Error) {
80
+ console.error(`\nāŒ Unexpected error: ${error.message}`);
81
+ }
82
+ else {
83
+ console.error('\nāŒ An unknown error occurred');
84
+ }
85
+ process.exit(1);
86
+ }
87
+ }
88
+ function parseArgs(args) {
89
+ const options = {
90
+ format: 'markdown',
91
+ verbose: false,
92
+ };
93
+ for (let i = 0; i < args.length; i++) {
94
+ switch (args[i]) {
95
+ case '--format':
96
+ case '-f':
97
+ if (args[i + 1] && (args[i + 1] === 'json' || args[i + 1] === 'markdown')) {
98
+ options.format = args[i + 1];
99
+ i++;
100
+ }
101
+ break;
102
+ case '--output':
103
+ case '-o':
104
+ if (args[i + 1]) {
105
+ options.output = args[i + 1];
106
+ i++;
107
+ }
108
+ break;
109
+ case '--search':
110
+ case '-s':
111
+ if (args[i + 1]) {
112
+ options.search = args[i + 1];
113
+ i++;
114
+ }
115
+ break;
116
+ case '--verbose':
117
+ case '-v':
118
+ options.verbose = true;
119
+ break;
120
+ }
121
+ }
122
+ return options;
123
+ }
124
+ function filterContentBySearch(content, query, format) {
125
+ const lines = content.split('\n');
126
+ const filtered = lines.filter(line => line.toLowerCase().includes(query.toLowerCase()));
127
+ if (filtered.length === 0) {
128
+ return `No results found for: "${query}"`;
129
+ }
130
+ return filtered.join('\n');
131
+ }
132
+ function printHelp() {
133
+ console.log(`
134
+ ╔════════════════════════════════════════════════════════════╗
135
+ ā•‘ XMind Parser CLI - Extract Mind Map Content ā•‘
136
+ ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•
137
+
138
+ USAGE:
139
+ node dist/cli.js <file-path> [options]
140
+
141
+ ARGUMENTS:
142
+ <file-path> Path to the .xmind file (supports ~ for home directory)
143
+
144
+ OPTIONS:
145
+ -f, --format Output format: 'markdown' (default) or 'json'
146
+ -o, --output Save output to file (optional)
147
+ -s, --search Filter results by keyword (optional)
148
+ -v, --verbose Show detailed information
149
+ -h, --help Show this help message
150
+
151
+ EXAMPLES:
152
+ # Basic usage - print to console
153
+ node dist/cli.js ~/Documents/my-mindmap.xmind
154
+
155
+ # JSON format
156
+ node dist/cli.js ~/Documents/my-mindmap.xmind --format json
157
+
158
+ # Save to file
159
+ node dist/cli.js ~/Documents/my-mindmap.xmind -o output.md
160
+
161
+ # Search and filter
162
+ node dist/cli.js ~/Documents/my-mindmap.xmind --search "important"
163
+
164
+ # Verbose with output file
165
+ node dist/cli.js ~/Documents/my-mindmap.xmind -v -o result.json --format json
166
+
167
+ `);
168
+ }
169
+ main();
170
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Parser for legacy XMind format (XML-based, versions 8 and earlier).
3
+ * Legacy XMind files use XML structure within the ZIP container.
4
+ */
5
+ import type { XMindNode, XMindSheet } from '../model/types.js';
6
+ interface LegacyTopic {
7
+ '@_id': string;
8
+ '@_href'?: string;
9
+ title?: string | {
10
+ '#text': string;
11
+ };
12
+ 'notes'?: string | {
13
+ '#text': string;
14
+ };
15
+ markers?: {
16
+ marker?: Array<{
17
+ '@_markerId': string;
18
+ }> | {
19
+ '@_markerId': string;
20
+ };
21
+ };
22
+ labels?: {
23
+ label?: Array<{
24
+ '#text': string;
25
+ }> | {
26
+ '#text': string;
27
+ };
28
+ };
29
+ topic?: LegacyTopic[] | LegacyTopic;
30
+ children?: {
31
+ topics?: Array<{
32
+ topic?: LegacyTopic[] | LegacyTopic;
33
+ '@_type'?: string;
34
+ }> | {
35
+ topic?: LegacyTopic[] | LegacyTopic;
36
+ '@_type'?: string;
37
+ };
38
+ };
39
+ 'relationship'?: Array<{
40
+ '@_id': string;
41
+ '@_end': string;
42
+ '@_sourceId': string;
43
+ '@_targetId': string;
44
+ '@_title'?: string;
45
+ }> | {
46
+ '@_id': string;
47
+ '@_end': string;
48
+ '@_sourceId': string;
49
+ '@_targetId': string;
50
+ '@_title'?: string;
51
+ };
52
+ }
53
+ /**
54
+ * Transform raw Legacy XML topic to normalized XMindNode.
55
+ * Recursively processes child topics.
56
+ */
57
+ export declare function transformLegacyTopic(legacyTopic: LegacyTopic): XMindNode;
58
+ /**
59
+ * Parse Legacy XML format content into XMindSheet objects.
60
+ */
61
+ export declare function parseLegacyContent(xmlText: string): Promise<XMindSheet[]>;
62
+ export {};
63
+ //# sourceMappingURL=legacy-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"legacy-parser.d.ts","sourceRoot":"","sources":["../../src/core/legacy-parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAI/D,UAAU,WAAW;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC,OAAO,CAAC,EAAE;QACR,MAAM,CAAC,EAAE,KAAK,CAAC;YAAE,YAAY,EAAE,MAAM,CAAA;SAAE,CAAC,GAAG;YAAE,YAAY,EAAE,MAAM,CAAA;SAAE,CAAC;KACrE,CAAC;IACF,MAAM,CAAC,EAAE;QACP,KAAK,CAAC,EAAE,KAAK,CAAC;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,GAAG;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC;KAC1D,CAAC;IACF,KAAK,CAAC,EAAE,WAAW,EAAE,GAAG,WAAW,CAAC;IAEpC,QAAQ,CAAC,EAAE;QACT,MAAM,CAAC,EAAE,KAAK,CAAC;YAAE,KAAK,CAAC,EAAE,WAAW,EAAE,GAAG,WAAW,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,GAAG;YAAE,KAAK,CAAC,EAAE,WAAW,EAAE,GAAG,WAAW,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;KACzI,CAAC;IACF,cAAc,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACnO;AAqCD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,WAAW,GAAG,SAAS,CAoDxE;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CA+D/E"}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Parser for legacy XMind format (XML-based, versions 8 and earlier).
3
+ * Legacy XMind files use XML structure within the ZIP container.
4
+ */
5
+ import { XMLParser } from 'fast-xml-parser';
6
+ import { ParseError } from '../utils/errors.js';
7
+ import { debug } from '../utils/logger.js';
8
+ /**
9
+ * Normalize a single item or array to always be an array.
10
+ * fast-xml-parser returns a single object if there's one item,
11
+ * and an array if there are multiple items.
12
+ */
13
+ function normalizeArray(value) {
14
+ if (!value)
15
+ return [];
16
+ return Array.isArray(value) ? value : [value];
17
+ }
18
+ /**
19
+ * Extract text from a string or object with #text property.
20
+ */
21
+ function extractText(value) {
22
+ if (!value)
23
+ return undefined;
24
+ if (typeof value === 'string')
25
+ return value;
26
+ if (typeof value === 'object' && '#text' in value)
27
+ return value['#text'];
28
+ return undefined;
29
+ }
30
+ /**
31
+ * Transform raw Legacy XML topic to normalized XMindNode.
32
+ * Recursively processes child topics.
33
+ */
34
+ export function transformLegacyTopic(legacyTopic) {
35
+ const node = {
36
+ id: legacyTopic['@_id'],
37
+ title: extractText(legacyTopic.title) || '',
38
+ children: [],
39
+ href: legacyTopic['@_href'],
40
+ };
41
+ // Extract note
42
+ const noteValue = legacyTopic.notes;
43
+ if (noteValue) {
44
+ node.note = extractText(noteValue);
45
+ }
46
+ // Extract markers
47
+ if (legacyTopic.markers && legacyTopic.markers.marker) {
48
+ const markerArray = normalizeArray(legacyTopic.markers.marker);
49
+ node.markers = markerArray.map(m => m['@_markerId']);
50
+ }
51
+ // Extract labels
52
+ if (legacyTopic.labels && legacyTopic.labels.label) {
53
+ const labelArray = normalizeArray(legacyTopic.labels.label);
54
+ node.labels = labelArray.map(l => extractText(l) || '').filter(l => l.length > 0);
55
+ }
56
+ // Recursively process child topics
57
+ // Support two formats:
58
+ // 1. Old format: <topic><topic>...</topic></topic> (direct topic children)
59
+ // 2. New format: <children><topics type="attached"><topic>...</topic></topics></children>
60
+ let childTopics = [];
61
+ if (legacyTopic.topic) {
62
+ // Old format: direct topic children
63
+ childTopics = normalizeArray(legacyTopic.topic);
64
+ }
65
+ else if (legacyTopic.children?.topics) {
66
+ // New format: children > topics array
67
+ const topicsArray = normalizeArray(legacyTopic.children.topics);
68
+ // Each topics element can have topic children
69
+ topicsArray.forEach(topicsElement => {
70
+ if (topicsElement.topic) {
71
+ const topicChildren = normalizeArray(topicsElement.topic);
72
+ childTopics.push(...topicChildren);
73
+ }
74
+ });
75
+ }
76
+ if (childTopics.length > 0) {
77
+ node.children = childTopics.map(child => transformLegacyTopic(child));
78
+ }
79
+ return node;
80
+ }
81
+ /**
82
+ * Parse Legacy XML format content into XMindSheet objects.
83
+ */
84
+ export async function parseLegacyContent(xmlText) {
85
+ try {
86
+ const parser = new XMLParser({
87
+ ignoreAttributes: false,
88
+ attributeNamePrefix: '@_',
89
+ textNodeName: '#text',
90
+ });
91
+ const parsed = parser.parse(xmlText);
92
+ if (!parsed['xmap-content']) {
93
+ throw new ParseError('Invalid Legacy XML format: missing xmap-content root element');
94
+ }
95
+ const xcontent = parsed['xmap-content'];
96
+ if (!xcontent.sheet) {
97
+ throw new ParseError('Invalid Legacy XML format: missing sheet element');
98
+ }
99
+ const sheets = normalizeArray(xcontent.sheet);
100
+ // Extract relationships from relationship-map (optional)
101
+ const relationshipsMap = new Map();
102
+ if (xcontent['relationship-map'] && xcontent['relationship-map'].relationship) {
103
+ const relArray = normalizeArray(xcontent['relationship-map'].relationship);
104
+ // Group relationships by sheet ID if available (relationships are usually global)
105
+ relArray.forEach(rel => {
106
+ if (!relationshipsMap.has('global')) {
107
+ relationshipsMap.set('global', []);
108
+ }
109
+ relationshipsMap.get('global').push(rel);
110
+ });
111
+ }
112
+ return sheets.map(sheet => {
113
+ if (!sheet['topic']) {
114
+ throw new ParseError(`Sheet "${sheet.title}" is missing root topic`);
115
+ }
116
+ debug('Transforming Legacy XML sheet', { sheetId: sheet['@_id'], title: sheet.title });
117
+ const rootTopic = transformLegacyTopic(sheet['topic']);
118
+ // Transform relationships for this sheet
119
+ const relationships = (relationshipsMap.get('global') || []).map(rel => ({
120
+ id: rel['@_id'],
121
+ title: rel['@_title'],
122
+ sourceId: rel['@_sourceId'],
123
+ targetId: rel['@_targetId'],
124
+ }));
125
+ return {
126
+ id: sheet['@_id'],
127
+ title: sheet.title,
128
+ rootTopic,
129
+ relationships: relationships.length > 0 ? relationships : undefined,
130
+ };
131
+ });
132
+ }
133
+ catch (err) {
134
+ if (err instanceof ParseError)
135
+ throw err;
136
+ throw new ParseError('Failed to parse Legacy XML', err.message);
137
+ }
138
+ }
139
+ //# sourceMappingURL=legacy-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"legacy-parser.js","sourceRoot":"","sources":["../../src/core/legacy-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Main parser dispatcher for XMind files.
3
+ * Detects file format (Zen or Legacy) and routes to appropriate parser.
4
+ * Handles ZIP extraction, format detection, and unified result generation.
5
+ */
6
+ import type { XMindParsedResult } from '../model/types.js';
7
+ /**
8
+ * Parse an XMind file (.xmind is a ZIP container).
9
+ * Detects format (Zen JSON or Legacy XML) and routes to appropriate parser.
10
+ * Returns a unified XMindParsedResult with metadata.
11
+ *
12
+ * @param filePath - Absolute path to the .xmind file
13
+ * @returns Parsed XMind document with sheets and metadata
14
+ * @throws FileNotFoundError if file does not exist
15
+ * @throws InvalidFormatError if ZIP is corrupted or format cannot be detected
16
+ * @throws ParseError if content parsing fails
17
+ */
18
+ export declare function parseXMindFile(filePath: string): Promise<XMindParsedResult>;
19
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/core/parser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAa,iBAAiB,EAAc,MAAM,mBAAmB,CAAC;AAMlF;;;;;;;;;;GAUG;AACH,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA+FjF"}
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Main parser dispatcher for XMind files.
3
+ * Detects file format (Zen or Legacy) and routes to appropriate parser.
4
+ * Handles ZIP extraction, format detection, and unified result generation.
5
+ */
6
+ import * as fs from 'fs/promises';
7
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
8
+ import JSZip from 'jszip';
9
+ import { FileNotFoundError, InvalidFormatError, ParseError } from '../utils/errors.js';
10
+ import { debug, info } from '../utils/logger.js';
11
+ import { parseZenContent, countTopics } from './zen-parser.js';
12
+ import { parseLegacyContent } from './legacy-parser.js';
13
+ /**
14
+ * Parse an XMind file (.xmind is a ZIP container).
15
+ * Detects format (Zen JSON or Legacy XML) and routes to appropriate parser.
16
+ * Returns a unified XMindParsedResult with metadata.
17
+ *
18
+ * @param filePath - Absolute path to the .xmind file
19
+ * @returns Parsed XMind document with sheets and metadata
20
+ * @throws FileNotFoundError if file does not exist
21
+ * @throws InvalidFormatError if ZIP is corrupted or format cannot be detected
22
+ * @throws ParseError if content parsing fails
23
+ */
24
+ export async function parseXMindFile(filePath) {
25
+ info('Starting XMind file parsing', { filePath });
26
+ // Step 1: Verify file exists
27
+ try {
28
+ await fs.access(filePath);
29
+ }
30
+ catch (err) {
31
+ throw new FileNotFoundError(filePath);
32
+ }
33
+ // Step 2: Read file and extract ZIP
34
+ let zipData;
35
+ try {
36
+ zipData = await fs.readFile(filePath);
37
+ }
38
+ catch (err) {
39
+ throw new FileNotFoundError(filePath);
40
+ }
41
+ let zip;
42
+ try {
43
+ zip = new JSZip();
44
+ await zip.loadAsync(zipData);
45
+ }
46
+ catch (err) {
47
+ throw new InvalidFormatError('ZIP container is corrupted or invalid');
48
+ }
49
+ debug('ZIP loaded successfully', { fileSize: zipData.length });
50
+ // Step 3: Detect format (Zen or Legacy)
51
+ let sheets;
52
+ let formatVersion;
53
+ // Try Zen format first (content.json)
54
+ if (zip.file('content.json')) {
55
+ debug('Detected Zen format (content.json)');
56
+ formatVersion = 'zen';
57
+ try {
58
+ const jsonData = await zip.file('content.json').async('string');
59
+ sheets = await parseZenContent(jsonData);
60
+ }
61
+ catch (err) {
62
+ throw new ParseError(`Failed to parse Zen format: ${err.message}`);
63
+ }
64
+ }
65
+ else if (zip.file('content.xml')) {
66
+ // Fall back to Legacy format (content.xml)
67
+ debug('Detected Legacy format (content.xml)');
68
+ formatVersion = 'legacy';
69
+ try {
70
+ const xmlData = await zip.file('content.xml').async('string');
71
+ sheets = await parseLegacyContent(xmlData);
72
+ }
73
+ catch (err) {
74
+ throw new ParseError(`Failed to parse Legacy format: ${err.message}`);
75
+ }
76
+ }
77
+ else {
78
+ // Neither format detected
79
+ throw new InvalidFormatError('Neither content.json (Zen) nor content.xml (Legacy) found in ZIP. File may be corrupted or created with an unsupported XMind version.');
80
+ }
81
+ // Step 4: Validate sheets
82
+ if (!sheets || sheets.length === 0) {
83
+ throw new InvalidFormatError('No sheets found in parsed document');
84
+ }
85
+ debug('Sheets parsed successfully', { sheetCount: sheets.length });
86
+ // Step 5: Calculate total topic count across all sheets
87
+ let totalTopicCount = 0;
88
+ for (const sheet of sheets) {
89
+ const sheetTopicCount = countTopics(sheet.rootTopic);
90
+ totalTopicCount += sheetTopicCount;
91
+ debug('Sheet topic count', { sheetId: sheet.id, topicCount: sheetTopicCount });
92
+ }
93
+ // Step 6: Construct and return unified result
94
+ const result = {
95
+ filePath,
96
+ sheets,
97
+ meta: {
98
+ formatVersion,
99
+ topicCount: totalTopicCount,
100
+ parsedAt: new Date().toISOString(),
101
+ },
102
+ };
103
+ info('XMind file parsed successfully', {
104
+ filePath,
105
+ formatVersion,
106
+ sheetCount: sheets.length,
107
+ topicCount: totalTopicCount,
108
+ });
109
+ return result;
110
+ }
111
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/core/parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Parser for modern XMind Zen format (content.json).
3
+ * Zen files use a straightforward JSON structure within the ZIP container.
4
+ */
5
+ import type { XMindNode, XMindSheet } from '../model/types.js';
6
+ /**
7
+ * Parse Zen format JSON content into XMindSheet objects.
8
+ * Supports two formats: direct array of sheets or object with sheets property.
9
+ */
10
+ export declare function parseZenContent(jsonText: string): Promise<XMindSheet[]>;
11
+ /**
12
+ * Count total nodes in a parsed sheet (for metadata).
13
+ */
14
+ export declare function countTopics(node: XMindNode): number;
15
+ //# sourceMappingURL=zen-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zen-parser.d.ts","sourceRoot":"","sources":["../../src/core/zen-parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAuE/D;;;GAGG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAmC7E;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAMnD"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Parser for modern XMind Zen format (content.json).
3
+ * Zen files use a straightforward JSON structure within the ZIP container.
4
+ */
5
+ import { ParseError } from '../utils/errors.js';
6
+ import { debug } from '../utils/logger.js';
7
+ /**
8
+ * Transform raw Zen JSON topic to normalized XMindNode.
9
+ * Recursively processes children (both attached and detached).
10
+ */
11
+ function transformZenTopic(zenTopic) {
12
+ const node = {
13
+ id: zenTopic.id,
14
+ title: zenTopic.title,
15
+ children: [],
16
+ labels: zenTopic.labels,
17
+ href: zenTopic.href,
18
+ };
19
+ // Extract note (can be string or object with format)
20
+ if (zenTopic.notes) {
21
+ if (typeof zenTopic.notes === 'string') {
22
+ node.note = zenTopic.notes;
23
+ }
24
+ else if (typeof zenTopic.notes === 'object' && 'content' in zenTopic.notes) {
25
+ node.note = zenTopic.notes.content;
26
+ }
27
+ }
28
+ // Transform markers (extract marker IDs)
29
+ if (zenTopic.markers && zenTopic.markers.length > 0) {
30
+ node.markers = zenTopic.markers.map(m => m.markerId);
31
+ }
32
+ // Recursively process children (attached and detached)
33
+ if (zenTopic.children) {
34
+ const attached = zenTopic.children.attached || [];
35
+ const detached = zenTopic.children.detached || [];
36
+ const allChildren = [...attached, ...detached];
37
+ node.children = allChildren.map(child => transformZenTopic(child));
38
+ }
39
+ return node;
40
+ }
41
+ /**
42
+ * Parse Zen format JSON content into XMindSheet objects.
43
+ * Supports two formats: direct array of sheets or object with sheets property.
44
+ */
45
+ export async function parseZenContent(jsonText) {
46
+ try {
47
+ const parsed = JSON.parse(jsonText);
48
+ let sheets;
49
+ // Handle both formats:
50
+ // 1. Direct array: [{ id, title, rootTopic }, ...]
51
+ // 2. Object with sheets: { sheets: [{ id, title, rootTopic }, ...] }
52
+ if (Array.isArray(parsed)) {
53
+ sheets = parsed;
54
+ }
55
+ else if (parsed.sheets && Array.isArray(parsed.sheets)) {
56
+ sheets = parsed.sheets;
57
+ }
58
+ else {
59
+ throw new ParseError('Invalid Zen format: missing or malformed sheets array');
60
+ }
61
+ return sheets.map(sheet => {
62
+ if (!sheet.rootTopic) {
63
+ throw new ParseError(`Sheet "${sheet.title}" is missing rootTopic`);
64
+ }
65
+ debug('Transforming Zen sheet', { sheetId: sheet.id, title: sheet.title });
66
+ return {
67
+ id: sheet.id,
68
+ title: sheet.title,
69
+ rootTopic: transformZenTopic(sheet.rootTopic),
70
+ relationships: sheet.relationships,
71
+ };
72
+ });
73
+ }
74
+ catch (err) {
75
+ if (err instanceof ParseError)
76
+ throw err;
77
+ throw new ParseError('Failed to parse Zen JSON', err.message);
78
+ }
79
+ }
80
+ /**
81
+ * Count total nodes in a parsed sheet (for metadata).
82
+ */
83
+ export function countTopics(node) {
84
+ let count = 1;
85
+ for (const child of node.children) {
86
+ count += countTopics(child);
87
+ }
88
+ return count;
89
+ }
90
+ //# sourceMappingURL=zen-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zen-parser.js","sourceRoot":"","sources":["../../src/core/zen-parser.ts"],"names":[],"mappings":"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"}