confluence-exporter 1.0.4 → 1.0.5

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.
@@ -0,0 +1,5 @@
1
+ <p>
2
+ <ac:structured-macro ac:name="children" ac:schema-version="2" ac:macro-id="7aaf9114-616b-461d-8da7-3ea099b1b543" />
3
+ </p>
4
+ <p><a href="https://confluence.fmr.com/pages/viewpage.action?pageId=560875531">Secondary Reach DevOps On Call</a></p>
5
+ <p><a href="https://confluence.fmr.com/display/AP001330/Batch+job+contacts">Batch Contacts</a></p>
package/agents.md CHANGED
@@ -287,6 +287,9 @@ Displays usage information, options, and examples.
287
287
  ```bash
288
288
  npm run dev -- index -u URL -n USER -p TOKEN -s SPACE -o ./output
289
289
 
290
+ # Index specific page and all children
291
+ npm run dev -- index -i PAGE_ID -u URL -n USER -p TOKEN -s SPACE -o ./output
292
+
290
293
  # With limit (index only first 10 pages)
291
294
  npm run dev -- index -u URL -n USER -p TOKEN -s SPACE -o ./output -l 10
292
295
  ```
@@ -294,13 +297,17 @@ npm run dev -- index -u URL -n USER -p TOKEN -s SPACE -o ./output -l 10
294
297
 
295
298
  **Behavior:**
296
299
  - Creates output directory if missing
297
- - Streams all pages via `api.getAllPages()` (memory-efficient)
298
- - Appends each page to `_index.yaml` as YAML array entry
299
- - **Resume:** Automatically resumes from where it left off by calculating the start position from existing pages (does NOT re-fetch already-indexed pages)
300
+ - **Mode A (No pageId):** Streams all pages via `api.getAllPages()` (memory-efficient)
301
+ - Appends each page to `_index.yaml` as YAML array entry
302
+ - **Resume:** Automatically resumes from where it left off by calculating the start position from existing pages (does NOT re-fetch already-indexed pages)
303
+ - **Logging:** `[N] Indexed: Title (ID) [API Page N]`
304
+ - **Mode B (With pageId):** Fetches specific page and recursively fetches all children
305
+ - Creates fresh `_index.yaml` (no resume for specific page indexing)
306
+ - Uses `api.getPage()` and `api.getChildPages()` recursively
307
+ - **Logging:** `[N] Indexed: Title (ID)` (indented for hierarchy depth)
300
308
  - **Limit:** If `--limit` is specified, stops after indexing that many pages
301
- - **Logging:** `[N] Indexed: Title (ID) [API Page N]`
302
309
 
303
- **Output:** `_index.yaml` with metadata for all pages in space
310
+ **Output:** `_index.yaml` with metadata for all pages in space or page tree
304
311
 
305
312
  #### PlanCommand (`plan.command.ts`)
306
313
  ```bash
@@ -11,4 +11,9 @@ export declare class IndexCommand implements CommandHandler {
11
11
  * Create _index.yaml file with all pages to download
12
12
  */
13
13
  private createIndex;
14
+ /**
15
+ * Create _index.yaml from a specific page and all its children (recursive)
16
+ * Uses parallel fetching for better performance
17
+ */
18
+ private createIndexFromPage;
14
19
  }
@@ -14,11 +14,20 @@ export class IndexCommand {
14
14
  const api = new ConfluenceApi(this.config);
15
15
  // Create output directory if it doesn't exist
16
16
  await fs.mkdir(this.config.outputDir, { recursive: true });
17
- console.log(`Starting indexing of space: ${this.config.spaceKey}`);
18
- console.log(`Output directory: ${this.config.outputDir}\n`);
19
- // Phase 1: Create _index.yaml
20
- console.log('Phase 1: Creating _index.yaml...');
21
- await this.createIndex(api, this.config);
17
+ // Check if pageId is specified for specific page + children indexing
18
+ if (this.config.pageId) {
19
+ console.log(`Starting indexing of page: ${this.config.pageId} and its children`);
20
+ console.log(`Output directory: ${this.config.outputDir}\n`);
21
+ console.log('Creating _index.yaml for specific page tree...');
22
+ await this.createIndexFromPage(api, this.config);
23
+ }
24
+ else {
25
+ console.log(`Starting indexing of space: ${this.config.spaceKey}`);
26
+ console.log(`Output directory: ${this.config.outputDir}\n`);
27
+ // Phase 1: Create _index.yaml
28
+ console.log('Phase 1: Creating _index.yaml...');
29
+ await this.createIndex(api, this.config);
30
+ }
22
31
  console.log(`\nIndexing complete!`);
23
32
  console.log(`Index saved to: ${this.config.outputDir}/_index.yaml`);
24
33
  }
@@ -91,5 +100,106 @@ export class IndexCommand {
91
100
  console.log(`\n✓ Index created: ${indexPath}`);
92
101
  console.log(` Total pages indexed: ${pageCount}`);
93
102
  }
103
+ /**
104
+ * Create _index.yaml from a specific page and all its children (recursive)
105
+ * Uses parallel fetching for better performance
106
+ */
107
+ async createIndexFromPage(api, config) {
108
+ const indexPath = path.join(config.outputDir, '_index.yaml');
109
+ const parallelLimit = config.parallel || 5;
110
+ // Start fresh for specific page indexing
111
+ const header = `# Confluence Export Index
112
+ # Space: ${config.spaceKey}
113
+ # Root Page: ${config.pageId}
114
+ # Export Date: ${new Date().toISOString()}
115
+
116
+ `;
117
+ await fs.writeFile(indexPath, header, 'utf-8');
118
+ console.log(`Creating new index for page tree: ${config.pageId}...`);
119
+ console.log(`Using parallel fetching with concurrency: ${parallelLimit}\n`);
120
+ // Collect all pages first, then write to file
121
+ const allPages = [];
122
+ let limitReached = false;
123
+ // BFS approach with parallel fetching for better performance
124
+ const processLevel = async (pageIds, parentDepth) => {
125
+ if (pageIds.length === 0 || limitReached)
126
+ return [];
127
+ const depth = parentDepth + 1;
128
+ const nextLevelIds = [];
129
+ // Process pages in batches for parallel fetching
130
+ for (let i = 0; i < pageIds.length; i += parallelLimit) {
131
+ if (limitReached)
132
+ break;
133
+ const batch = pageIds.slice(i, i + parallelLimit);
134
+ // Fetch page details and children in parallel
135
+ const results = await Promise.all(batch.map(async (pageId) => {
136
+ const [page, children] = await Promise.all([
137
+ api.getPage(pageId),
138
+ api.getChildPages(pageId)
139
+ ]);
140
+ return { page, children };
141
+ }));
142
+ // Process results
143
+ for (const { page, children } of results) {
144
+ if (limitReached)
145
+ break;
146
+ allPages.push({ page, depth });
147
+ console.log(`[${allPages.length}] Indexed: ${page.title} (${page.id})`);
148
+ // Check limit
149
+ if (config.limit && allPages.length >= config.limit) {
150
+ console.log(`\n⚠ Limit reached: ${config.limit} pages indexed`);
151
+ limitReached = true;
152
+ break;
153
+ }
154
+ // Collect child IDs for next level
155
+ nextLevelIds.push(...children.map(c => c.id));
156
+ }
157
+ }
158
+ return nextLevelIds;
159
+ };
160
+ // Start with the root page
161
+ const rootPage = await api.getPage(config.pageId);
162
+ allPages.push({ page: rootPage, depth: 0 });
163
+ console.log(`[${allPages.length}] Indexed: ${rootPage.title} (${rootPage.id})`);
164
+ if (config.limit && allPages.length >= config.limit) {
165
+ console.log(`\n⚠ Limit reached: ${config.limit} pages indexed`);
166
+ limitReached = true;
167
+ }
168
+ if (!limitReached) {
169
+ // Get children of root and process level by level
170
+ const rootChildren = await api.getChildPages(config.pageId);
171
+ let currentLevelIds = rootChildren.map(c => c.id);
172
+ let currentDepth = 0;
173
+ while (currentLevelIds.length > 0 && !limitReached) {
174
+ currentLevelIds = await processLevel(currentLevelIds, currentDepth);
175
+ currentDepth++;
176
+ }
177
+ }
178
+ // Write all pages to index file
179
+ console.log(`\nWriting ${allPages.length} pages to index...`);
180
+ for (const { page } of allPages) {
181
+ const pageEntry = {
182
+ id: page.id,
183
+ title: page.title,
184
+ version: page.version,
185
+ parentId: page.parentId,
186
+ modifiedDate: page.modifiedDate,
187
+ indexedDate: new Date().toISOString(),
188
+ pageNumber: 0 // Not applicable for specific page indexing
189
+ };
190
+ // Convert to YAML and format as array item
191
+ const yamlDoc = yaml.stringify(pageEntry).trim();
192
+ const lines = yamlDoc.split('\n');
193
+ const arrayItem = lines.map((line, index) => {
194
+ if (index === 0) {
195
+ return `- ${line}`;
196
+ }
197
+ return ` ${line}`;
198
+ }).join('\n');
199
+ await fs.appendFile(indexPath, arrayItem + '\n', 'utf-8');
200
+ }
201
+ console.log(`\n✓ Index created: ${indexPath}`);
202
+ console.log(` Total pages indexed: ${allPages.length}`);
203
+ }
94
204
  }
95
205
  //# sourceMappingURL=index.command.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.command.js","sourceRoot":"","sources":["../../src/commands/index.command.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.command.js","sourceRoot":"","sources":["../../src/commands/index.command.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "confluence-exporter",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "type": "module",
5
5
  "description": "Minimal standalone CLI tool to export Confluence spaces to Markdown",
6
6
  "license": "MIT",
@@ -6,7 +6,7 @@ import { promises as fs } from 'fs';
6
6
  import path from 'path';
7
7
  import yaml from 'yaml';
8
8
  import { ConfluenceApi } from '../api.js';
9
- import type { ConfluenceConfig, PageIndexEntry } from '../types.js';
9
+ import type { ConfluenceConfig, PageIndexEntry, Page } from '../types.js';
10
10
  import type { CommandContext, CommandHandler } from './types.js';
11
11
 
12
12
  export class IndexCommand implements CommandHandler {
@@ -18,12 +18,21 @@ export class IndexCommand implements CommandHandler {
18
18
  // Create output directory if it doesn't exist
19
19
  await fs.mkdir(this.config.outputDir, { recursive: true });
20
20
 
21
- console.log(`Starting indexing of space: ${this.config.spaceKey}`);
22
- console.log(`Output directory: ${this.config.outputDir}\n`);
21
+ // Check if pageId is specified for specific page + children indexing
22
+ if (this.config.pageId) {
23
+ console.log(`Starting indexing of page: ${this.config.pageId} and its children`);
24
+ console.log(`Output directory: ${this.config.outputDir}\n`);
23
25
 
24
- // Phase 1: Create _index.yaml
25
- console.log('Phase 1: Creating _index.yaml...');
26
- await this.createIndex(api, this.config);
26
+ console.log('Creating _index.yaml for specific page tree...');
27
+ await this.createIndexFromPage(api, this.config);
28
+ } else {
29
+ console.log(`Starting indexing of space: ${this.config.spaceKey}`);
30
+ console.log(`Output directory: ${this.config.outputDir}\n`);
31
+
32
+ // Phase 1: Create _index.yaml
33
+ console.log('Phase 1: Creating _index.yaml...');
34
+ await this.createIndex(api, this.config);
35
+ }
27
36
 
28
37
  console.log(`\nIndexing complete!`);
29
38
  console.log(`Index saved to: ${this.config.outputDir}/_index.yaml`);
@@ -108,4 +117,125 @@ export class IndexCommand implements CommandHandler {
108
117
  console.log(`\n✓ Index created: ${indexPath}`);
109
118
  console.log(` Total pages indexed: ${pageCount}`);
110
119
  }
120
+
121
+ /**
122
+ * Create _index.yaml from a specific page and all its children (recursive)
123
+ * Uses parallel fetching for better performance
124
+ */
125
+ private async createIndexFromPage(api: ConfluenceApi, config: CommandContext['config']): Promise<void> {
126
+ const indexPath = path.join(config.outputDir, '_index.yaml');
127
+ const parallelLimit = config.parallel || 5;
128
+
129
+ // Start fresh for specific page indexing
130
+ const header = `# Confluence Export Index
131
+ # Space: ${config.spaceKey}
132
+ # Root Page: ${config.pageId}
133
+ # Export Date: ${new Date().toISOString()}
134
+
135
+ `;
136
+ await fs.writeFile(indexPath, header, 'utf-8');
137
+ console.log(`Creating new index for page tree: ${config.pageId}...`);
138
+ console.log(`Using parallel fetching with concurrency: ${parallelLimit}\n`);
139
+
140
+ // Collect all pages first, then write to file
141
+ const allPages: Array<{ page: Page; depth: number }> = [];
142
+ let limitReached = false;
143
+
144
+ // BFS approach with parallel fetching for better performance
145
+ const processLevel = async (pageIds: string[], parentDepth: number): Promise<string[]> => {
146
+ if (pageIds.length === 0 || limitReached) return [];
147
+
148
+ const depth = parentDepth + 1;
149
+ const nextLevelIds: string[] = [];
150
+
151
+ // Process pages in batches for parallel fetching
152
+ for (let i = 0; i < pageIds.length; i += parallelLimit) {
153
+ if (limitReached) break;
154
+
155
+ const batch = pageIds.slice(i, i + parallelLimit);
156
+
157
+ // Fetch page details and children in parallel
158
+ const results = await Promise.all(
159
+ batch.map(async (pageId) => {
160
+ const [page, children] = await Promise.all([
161
+ api.getPage(pageId),
162
+ api.getChildPages(pageId)
163
+ ]);
164
+ return { page, children };
165
+ })
166
+ );
167
+
168
+ // Process results
169
+ for (const { page, children } of results) {
170
+ if (limitReached) break;
171
+
172
+ allPages.push({ page, depth });
173
+ console.log(`[${allPages.length}] Indexed: ${page.title} (${page.id})`);
174
+
175
+ // Check limit
176
+ if (config.limit && allPages.length >= config.limit) {
177
+ console.log(`\n⚠ Limit reached: ${config.limit} pages indexed`);
178
+ limitReached = true;
179
+ break;
180
+ }
181
+
182
+ // Collect child IDs for next level
183
+ nextLevelIds.push(...children.map(c => c.id));
184
+ }
185
+ }
186
+
187
+ return nextLevelIds;
188
+ };
189
+
190
+ // Start with the root page
191
+ const rootPage = await api.getPage(config.pageId!);
192
+ allPages.push({ page: rootPage, depth: 0 });
193
+ console.log(`[${allPages.length}] Indexed: ${rootPage.title} (${rootPage.id})`);
194
+
195
+ if (config.limit && allPages.length >= config.limit) {
196
+ console.log(`\n⚠ Limit reached: ${config.limit} pages indexed`);
197
+ limitReached = true;
198
+ }
199
+
200
+ if (!limitReached) {
201
+ // Get children of root and process level by level
202
+ const rootChildren = await api.getChildPages(config.pageId!);
203
+ let currentLevelIds = rootChildren.map(c => c.id);
204
+ let currentDepth = 0;
205
+
206
+ while (currentLevelIds.length > 0 && !limitReached) {
207
+ currentLevelIds = await processLevel(currentLevelIds, currentDepth);
208
+ currentDepth++;
209
+ }
210
+ }
211
+
212
+ // Write all pages to index file
213
+ console.log(`\nWriting ${allPages.length} pages to index...`);
214
+ for (const { page } of allPages) {
215
+ const pageEntry: PageIndexEntry = {
216
+ id: page.id,
217
+ title: page.title,
218
+ version: page.version,
219
+ parentId: page.parentId,
220
+ modifiedDate: page.modifiedDate,
221
+ indexedDate: new Date().toISOString(),
222
+ pageNumber: 0 // Not applicable for specific page indexing
223
+ };
224
+
225
+ // Convert to YAML and format as array item
226
+ const yamlDoc = yaml.stringify(pageEntry).trim();
227
+ const lines = yamlDoc.split('\n');
228
+ const arrayItem = lines.map((line, index) => {
229
+ if (index === 0) {
230
+ return `- ${line}`;
231
+ }
232
+ return ` ${line}`;
233
+ }).join('\n');
234
+
235
+ await fs.appendFile(indexPath, arrayItem + '\n', 'utf-8');
236
+ }
237
+
238
+ console.log(`\n✓ Index created: ${indexPath}`);
239
+ console.log(` Total pages indexed: ${allPages.length}`);
240
+ }
111
241
  }