tdecollab 0.2.1 → 0.2.3
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 +91 -9
- package/dist/{chunk-T73I3OT6.js → chunk-2HLUO2TQ.js} +54 -21
- package/dist/chunk-2HLUO2TQ.js.map +1 -0
- package/dist/{chunk-SJ7KPK6Q.js → chunk-IFYMZLQI.js} +21 -6
- package/dist/chunk-IFYMZLQI.js.map +1 -0
- package/dist/{chunk-2IQ4QMK3.js → chunk-UH2YGKTB.js} +280 -125
- package/dist/chunk-UH2YGKTB.js.map +1 -0
- package/dist/cli.js +126 -12
- package/dist/cli.js.map +1 -1
- package/dist/{image-downloader-D57KFAIQ.js → image-downloader-IY2A3R5N.js} +7 -6
- package/dist/image-downloader-IY2A3R5N.js.map +1 -0
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/server-GLKCDDKS.js +9 -0
- package/package.json +13 -7
- package/dist/chunk-2IQ4QMK3.js.map +0 -1
- package/dist/chunk-SJ7KPK6Q.js.map +0 -1
- package/dist/chunk-T73I3OT6.js.map +0 -1
- package/dist/image-downloader-D57KFAIQ.js.map +0 -1
- package/dist/server-HS774DWY.js +0 -9
- /package/dist/{server-HS774DWY.js.map → server-GLKCDDKS.js.map} +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
logger
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-IFYMZLQI.js";
|
|
4
4
|
|
|
5
|
-
//
|
|
5
|
+
// tools/confluence/api/content.ts
|
|
6
6
|
var ConfluenceContentApi = class {
|
|
7
7
|
constructor(client) {
|
|
8
8
|
this.client = client;
|
|
@@ -85,7 +85,40 @@ var ConfluenceContentApi = class {
|
|
|
85
85
|
const data = labels.map((name) => ({ prefix: "global", name }));
|
|
86
86
|
await this.client.post(`/rest/api/content/${id}/label`, data);
|
|
87
87
|
}
|
|
88
|
-
// Attachment 관련 메서드
|
|
88
|
+
// Attachment 관련 메서드 (upsert: 기존 파일이 있으면 업데이트, 없으면 신규 업로드)
|
|
89
|
+
async uploadAttachment(pageId, filename, fileContent, contentType) {
|
|
90
|
+
const FormData = (await import("form-data")).default;
|
|
91
|
+
const existingAttachments = await this.getAttachments(pageId, filename);
|
|
92
|
+
const existing = existingAttachments.find((a) => a.title === filename);
|
|
93
|
+
const form = new FormData();
|
|
94
|
+
form.append("file", fileContent, {
|
|
95
|
+
filename,
|
|
96
|
+
contentType: contentType || "application/octet-stream"
|
|
97
|
+
});
|
|
98
|
+
const headers = {
|
|
99
|
+
...form.getHeaders(),
|
|
100
|
+
"X-Atlassian-Token": "nocheck",
|
|
101
|
+
"Accept": "application/json"
|
|
102
|
+
};
|
|
103
|
+
let response;
|
|
104
|
+
if (existing) {
|
|
105
|
+
response = await this.client.post(
|
|
106
|
+
`/rest/api/content/${pageId}/child/attachment/${existing.id}/data`,
|
|
107
|
+
form,
|
|
108
|
+
{ headers }
|
|
109
|
+
);
|
|
110
|
+
} else {
|
|
111
|
+
response = await this.client.post(
|
|
112
|
+
`/rest/api/content/${pageId}/child/attachment`,
|
|
113
|
+
form,
|
|
114
|
+
{ headers }
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
if (response.data && response.data.results && response.data.results.length > 0) {
|
|
118
|
+
return response.data.results[0];
|
|
119
|
+
}
|
|
120
|
+
return response.data;
|
|
121
|
+
}
|
|
89
122
|
async getAttachments(pageId, filename) {
|
|
90
123
|
const response = await this.client.get(`/rest/api/content/${pageId}/child/attachment`, {
|
|
91
124
|
params: {
|
|
@@ -103,7 +136,7 @@ var ConfluenceContentApi = class {
|
|
|
103
136
|
}
|
|
104
137
|
};
|
|
105
138
|
|
|
106
|
-
//
|
|
139
|
+
// tools/confluence/api/space.ts
|
|
107
140
|
var ConfluenceSpaceApi = class {
|
|
108
141
|
constructor(client) {
|
|
109
142
|
this.client = client;
|
|
@@ -120,7 +153,7 @@ var ConfluenceSpaceApi = class {
|
|
|
120
153
|
}
|
|
121
154
|
};
|
|
122
155
|
|
|
123
|
-
//
|
|
156
|
+
// tools/confluence/api/search.ts
|
|
124
157
|
var ConfluenceSearchApi = class {
|
|
125
158
|
constructor(client) {
|
|
126
159
|
this.client = client;
|
|
@@ -139,10 +172,10 @@ var ConfluenceSearchApi = class {
|
|
|
139
172
|
}
|
|
140
173
|
};
|
|
141
174
|
|
|
142
|
-
//
|
|
175
|
+
// tools/common/http-client.ts
|
|
143
176
|
import axios from "axios";
|
|
144
177
|
|
|
145
|
-
//
|
|
178
|
+
// tools/common/errors.ts
|
|
146
179
|
var TdeCollabError = class extends Error {
|
|
147
180
|
constructor(message) {
|
|
148
181
|
super(message);
|
|
@@ -173,7 +206,7 @@ var ConflictError = class extends TdeCollabError {
|
|
|
173
206
|
}
|
|
174
207
|
};
|
|
175
208
|
|
|
176
|
-
//
|
|
209
|
+
// tools/common/http-client.ts
|
|
177
210
|
function createHttpClient(config) {
|
|
178
211
|
const client = axios.create({
|
|
179
212
|
baseURL: config.baseUrl,
|
|
@@ -184,19 +217,25 @@ function createHttpClient(config) {
|
|
|
184
217
|
}
|
|
185
218
|
});
|
|
186
219
|
client.interceptors.request.use((reqConfig) => {
|
|
187
|
-
if (config.auth.
|
|
220
|
+
if (config.auth.token && !reqConfig.headers.Authorization && !reqConfig.headers["PRIVATE-TOKEN"]) {
|
|
221
|
+
reqConfig.headers.Authorization = `Bearer ${config.auth.token}`;
|
|
222
|
+
} else if (config.auth.username && config.auth.token && !reqConfig.headers.Authorization) {
|
|
188
223
|
const token = Buffer.from(`${config.auth.username}:${config.auth.token}`).toString("base64");
|
|
189
224
|
reqConfig.headers.Authorization = `Basic ${token}`;
|
|
190
|
-
} else if (config.auth.token) {
|
|
191
|
-
if (!reqConfig.headers.Authorization && !reqConfig.headers["PRIVATE-TOKEN"]) {
|
|
192
|
-
reqConfig.headers.Authorization = `Bearer ${config.auth.token}`;
|
|
193
|
-
}
|
|
194
225
|
}
|
|
226
|
+
logger.debug(`[HTTP Request] ${reqConfig.method?.toUpperCase()} ${reqConfig.url}`, {
|
|
227
|
+
headers: reqConfig.headers,
|
|
228
|
+
params: reqConfig.params,
|
|
229
|
+
data: reqConfig.data
|
|
230
|
+
});
|
|
195
231
|
return reqConfig;
|
|
196
232
|
});
|
|
197
233
|
client.interceptors.response.use(
|
|
198
234
|
(response) => {
|
|
199
|
-
logger.debug(`[HTTP] ${response.status} ${response.config.method?.toUpperCase()} ${response.config.url}
|
|
235
|
+
logger.debug(`[HTTP Response] ${response.status} ${response.config.method?.toUpperCase()} ${response.config.url}`, {
|
|
236
|
+
headers: response.headers,
|
|
237
|
+
data: response.data
|
|
238
|
+
});
|
|
200
239
|
return response;
|
|
201
240
|
},
|
|
202
241
|
(error) => {
|
|
@@ -205,7 +244,11 @@ function createHttpClient(config) {
|
|
|
205
244
|
const method = error.config?.method?.toUpperCase();
|
|
206
245
|
const url = error.config?.url;
|
|
207
246
|
const message = error.response.data?.message || error.message;
|
|
208
|
-
logger.error(`[HTTP] ${status} ${method} ${url}
|
|
247
|
+
logger.error(`[HTTP Error] ${status} ${method} ${url}`, {
|
|
248
|
+
message,
|
|
249
|
+
responseData: error.response.data,
|
|
250
|
+
requestHeaders: error.config?.headers
|
|
251
|
+
});
|
|
209
252
|
if (status === 401 || status === 403) {
|
|
210
253
|
throw new AuthError(`\uC778\uC99D \uC2E4\uD328: ${message}`);
|
|
211
254
|
}
|
|
@@ -228,101 +271,12 @@ function createHttpClient(config) {
|
|
|
228
271
|
return client;
|
|
229
272
|
}
|
|
230
273
|
|
|
231
|
-
//
|
|
274
|
+
// tools/confluence/api/client.ts
|
|
232
275
|
function createConfluenceClient(config) {
|
|
233
276
|
return createHttpClient(config);
|
|
234
277
|
}
|
|
235
278
|
|
|
236
|
-
//
|
|
237
|
-
import MarkdownIt from "markdown-it";
|
|
238
|
-
var MarkdownToStorageConverter = class {
|
|
239
|
-
md;
|
|
240
|
-
constructor() {
|
|
241
|
-
this.md = new MarkdownIt({
|
|
242
|
-
html: true,
|
|
243
|
-
linkify: true,
|
|
244
|
-
breaks: true
|
|
245
|
-
});
|
|
246
|
-
this.md.renderer.rules.fence = (tokens, idx) => {
|
|
247
|
-
const token = tokens[idx];
|
|
248
|
-
const code = token.content.trim();
|
|
249
|
-
const lang = token.info.trim();
|
|
250
|
-
return `<ac:structured-macro ac:name="code" ac:schema-version="1">
|
|
251
|
-
<ac:parameter ac:name="language">${lang || "text"}</ac:parameter>
|
|
252
|
-
<ac:plain-text-body><![CDATA[${code}]]></ac:plain-text-body>
|
|
253
|
-
</ac:structured-macro>`;
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
convert(markdown) {
|
|
257
|
-
return this.md.render(markdown);
|
|
258
|
-
}
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
// src/confluence/converters/storage-to-md.ts
|
|
262
|
-
var StorageToMarkdownConverter = class {
|
|
263
|
-
convert(storageHtml, imageUrlMap) {
|
|
264
|
-
let md = storageHtml;
|
|
265
|
-
if (imageUrlMap && imageUrlMap.size > 0) {
|
|
266
|
-
imageUrlMap.forEach((localPath, originalTag) => {
|
|
267
|
-
const filename = localPath.split("/").pop() || "image";
|
|
268
|
-
const altText = filename.replace(/\.[^.]+$/, "");
|
|
269
|
-
const markdownImage = ``;
|
|
270
|
-
md = md.replace(originalTag, markdownImage);
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
md = md.replace(
|
|
274
|
-
/<ac:image[^>]*>[\s\S]*?<ri:attachment\s+ri:filename="([^"]+)"[\s\S]*?<\/ac:image>/g,
|
|
275
|
-
(match, filename) => {
|
|
276
|
-
return ``;
|
|
277
|
-
}
|
|
278
|
-
);
|
|
279
|
-
md = md.replace(
|
|
280
|
-
/<ac:image[^>]*>[\s\S]*?<ri:url\s+ri:value="([^"]+)"[\s\S]*?<\/ac:image>/g,
|
|
281
|
-
(match, url) => {
|
|
282
|
-
const filename = url.split("/").pop() || "image";
|
|
283
|
-
return ``;
|
|
284
|
-
}
|
|
285
|
-
);
|
|
286
|
-
md = md.replace(
|
|
287
|
-
/<img\s+[^>]*\/?>/g,
|
|
288
|
-
(match) => {
|
|
289
|
-
const srcMatch = /src="([^"]+)"/.exec(match);
|
|
290
|
-
const altMatch = /alt="([^"]*)"/.exec(match);
|
|
291
|
-
if (srcMatch) {
|
|
292
|
-
const src = srcMatch[1];
|
|
293
|
-
const altText = altMatch ? altMatch[1] : src.split("/").pop() || "image";
|
|
294
|
-
return ``;
|
|
295
|
-
}
|
|
296
|
-
return match;
|
|
297
|
-
}
|
|
298
|
-
);
|
|
299
|
-
md = md.replace(/<ac:structured-macro[^>]*ac:name="code"[^>]*>[\s\S]*?<ac:parameter[^>]*ac:name="language">([^<]*)<\/ac:parameter>[\s\S]*?<ac:plain-text-body><!\[CDATA\[([\s\S]*?)\]\]><\/ac:plain-text-body>[\s\S]*?<\/ac:structured-macro>/g, (match, lang, code) => {
|
|
300
|
-
return `\`\`\`${lang}
|
|
301
|
-
${code}
|
|
302
|
-
\`\`\``;
|
|
303
|
-
});
|
|
304
|
-
md = md.replace(/<h1>(.*?)<\/h1>/g, "# $1\n");
|
|
305
|
-
md = md.replace(/<h2>(.*?)<\/h2>/g, "## $1\n");
|
|
306
|
-
md = md.replace(/<h3>(.*?)<\/h3>/g, "### $1\n");
|
|
307
|
-
md = md.replace(/<p>(.*?)<\/p>/g, "$1\n\n");
|
|
308
|
-
md = md.replace(/<strong>(.*?)<\/strong>/g, "**$1**");
|
|
309
|
-
md = md.replace(/<b>(.*?)<\/b>/g, "**$1**");
|
|
310
|
-
md = md.replace(/<em>(.*?)<\/em>/g, "*$1*");
|
|
311
|
-
md = md.replace(/<i>(.*?)<\/i>/g, "*$1*");
|
|
312
|
-
md = md.replace(/<ul>([\s\S]*?)<\/ul>/g, (match, content) => {
|
|
313
|
-
return content.replace(/<li>(.*?)<\/li>/g, "- $1\n");
|
|
314
|
-
});
|
|
315
|
-
md = md.replace(/<ol>([\s\S]*?)<\/ol>/g, (match, content) => {
|
|
316
|
-
let i = 1;
|
|
317
|
-
return content.replace(/<li>(.*?)<\/li>/g, () => `${i++}. $1
|
|
318
|
-
`);
|
|
319
|
-
});
|
|
320
|
-
md = md.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"');
|
|
321
|
-
return md.trim();
|
|
322
|
-
}
|
|
323
|
-
};
|
|
324
|
-
|
|
325
|
-
// src/common/config.ts
|
|
279
|
+
// tools/common/config.ts
|
|
326
280
|
import dotenv from "dotenv";
|
|
327
281
|
dotenv.config();
|
|
328
282
|
function getEnvOrThrow(key, description) {
|
|
@@ -337,20 +291,23 @@ function getEnvOrThrow(key, description) {
|
|
|
337
291
|
function loadConfluenceConfig() {
|
|
338
292
|
const baseUrl = getEnvOrThrow("CONFLUENCE_BASE_URL", "Confluence \uAE30\uBCF8 URL");
|
|
339
293
|
const username = process.env.CONFLUENCE_USERNAME;
|
|
340
|
-
const token = getEnvOrThrow("CONFLUENCE_API_TOKEN", "Confluence
|
|
294
|
+
const token = getEnvOrThrow("CONFLUENCE_API_TOKEN", "Confluence PAT \uD1A0\uD070");
|
|
295
|
+
const mermaidMacroName = process.env.CONFLUENCE_MERMAID_MACRO_NAME || "mermaiddiagram";
|
|
296
|
+
const inlineCodeStyle = process.env.CONFLUENCE_INLINE_CODE_STYLE || "color: #d04437; font-weight: bold;";
|
|
341
297
|
return {
|
|
342
298
|
baseUrl,
|
|
343
299
|
auth: {
|
|
344
300
|
username,
|
|
345
301
|
token
|
|
346
|
-
|
|
347
|
-
|
|
302
|
+
},
|
|
303
|
+
mermaidMacroName,
|
|
304
|
+
inlineCodeStyle
|
|
348
305
|
};
|
|
349
306
|
}
|
|
350
307
|
function loadJiraConfig() {
|
|
351
308
|
const baseUrl = getEnvOrThrow("JIRA_BASE_URL", "JIRA \uAE30\uBCF8 URL");
|
|
352
309
|
const username = process.env.JIRA_USERNAME;
|
|
353
|
-
const token = getEnvOrThrow("JIRA_API_TOKEN", "JIRA
|
|
310
|
+
const token = getEnvOrThrow("JIRA_API_TOKEN", "JIRA PAT \uD1A0\uD070");
|
|
354
311
|
return {
|
|
355
312
|
baseUrl,
|
|
356
313
|
auth: {
|
|
@@ -370,7 +327,205 @@ function loadGitlabConfig() {
|
|
|
370
327
|
};
|
|
371
328
|
}
|
|
372
329
|
|
|
373
|
-
//
|
|
330
|
+
// tools/confluence/converters/md-to-storage.ts
|
|
331
|
+
import MarkdownIt from "markdown-it";
|
|
332
|
+
var MarkdownToStorageConverter = class {
|
|
333
|
+
md;
|
|
334
|
+
mermaidMacroName;
|
|
335
|
+
inlineCodeStyle;
|
|
336
|
+
constructor() {
|
|
337
|
+
const config = loadConfluenceConfig();
|
|
338
|
+
this.mermaidMacroName = config.mermaidMacroName;
|
|
339
|
+
this.inlineCodeStyle = config.inlineCodeStyle;
|
|
340
|
+
this.md = new MarkdownIt({
|
|
341
|
+
html: true,
|
|
342
|
+
linkify: true,
|
|
343
|
+
breaks: true,
|
|
344
|
+
xhtmlOut: true
|
|
345
|
+
// Confluence XML 파서와의 호환성을 위해 XHTML 출력 활성화
|
|
346
|
+
});
|
|
347
|
+
this.md.renderer.rules.fence = (tokens, idx) => {
|
|
348
|
+
const token = tokens[idx];
|
|
349
|
+
const code = token.content.trim();
|
|
350
|
+
const lang = token.info.trim().toLowerCase();
|
|
351
|
+
if (lang === "mermaid") {
|
|
352
|
+
return `<ac:structured-macro ac:name="${this.mermaidMacroName}" ac:schema-version="1">
|
|
353
|
+
<ac:plain-text-body><![CDATA[${code}]]></ac:plain-text-body>
|
|
354
|
+
</ac:structured-macro>`;
|
|
355
|
+
}
|
|
356
|
+
if (lang === "plantuml") {
|
|
357
|
+
return `<ac:structured-macro ac:name="plantuml" ac:schema-version="1">
|
|
358
|
+
<ac:parameter ac:name="atlassian-macro-output-type">INLINE</ac:parameter>
|
|
359
|
+
<ac:plain-text-body><![CDATA[${code}]]></ac:plain-text-body>
|
|
360
|
+
</ac:structured-macro>`;
|
|
361
|
+
}
|
|
362
|
+
return `<ac:structured-macro ac:name="code" ac:schema-version="1">
|
|
363
|
+
<ac:parameter ac:name="language">${lang || "text"}</ac:parameter>
|
|
364
|
+
<ac:plain-text-body><![CDATA[${code}]]></ac:plain-text-body>
|
|
365
|
+
</ac:structured-macro>`;
|
|
366
|
+
};
|
|
367
|
+
this.md.renderer.rules.image = (tokens, idx, options, env, self) => {
|
|
368
|
+
const token = tokens[idx];
|
|
369
|
+
const src = token.attrGet("src") || "";
|
|
370
|
+
const alt = token.content || "";
|
|
371
|
+
const isExternal = src.startsWith("http://") || src.startsWith("https://");
|
|
372
|
+
const altAttr = alt ? ` ac:alt="${this.md.utils.escapeHtml(alt)}"` : "";
|
|
373
|
+
if (isExternal) {
|
|
374
|
+
return `<ac:image${altAttr}><ri:url ri:value="${src}" /></ac:image>`;
|
|
375
|
+
} else {
|
|
376
|
+
const filename = src.split("/").pop() || src;
|
|
377
|
+
return `<ac:image${altAttr}><ri:attachment ri:filename="${filename}" /></ac:image>`;
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
this.md.renderer.rules.code_inline = (tokens, idx) => {
|
|
381
|
+
const token = tokens[idx];
|
|
382
|
+
const content = this.md.utils.escapeHtml(token.content);
|
|
383
|
+
return `<code style="${this.inlineCodeStyle}">${content}</code>`;
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
convert(markdown) {
|
|
387
|
+
return this.md.render(markdown);
|
|
388
|
+
}
|
|
389
|
+
extractLocalImages(markdown) {
|
|
390
|
+
const tokens = this.md.parse(markdown, {});
|
|
391
|
+
const localImages = /* @__PURE__ */ new Set();
|
|
392
|
+
const walk = (tokens2) => {
|
|
393
|
+
for (const token of tokens2) {
|
|
394
|
+
if (token.type === "image") {
|
|
395
|
+
const src = token.attrGet("src") || "";
|
|
396
|
+
if (!src.startsWith("http://") && !src.startsWith("https://")) {
|
|
397
|
+
localImages.add(src);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
if (token.children) {
|
|
401
|
+
walk(token.children);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
walk(tokens);
|
|
406
|
+
return Array.from(localImages);
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
// tools/confluence/converters/storage-to-md.ts
|
|
411
|
+
import TurndownService from "turndown";
|
|
412
|
+
import { gfm } from "turndown-plugin-gfm";
|
|
413
|
+
import { JSDOM } from "jsdom";
|
|
414
|
+
var StorageToMarkdownConverter = class {
|
|
415
|
+
turndown;
|
|
416
|
+
constructor() {
|
|
417
|
+
this.turndown = new TurndownService({
|
|
418
|
+
headingStyle: "atx",
|
|
419
|
+
hr: "---",
|
|
420
|
+
bulletListMarker: "-",
|
|
421
|
+
codeBlockStyle: "fenced"
|
|
422
|
+
});
|
|
423
|
+
this.turndown.use(gfm);
|
|
424
|
+
this.setupRules();
|
|
425
|
+
}
|
|
426
|
+
setupRules() {
|
|
427
|
+
this.turndown.addRule("tables", {
|
|
428
|
+
filter: ["table"],
|
|
429
|
+
replacement: (content, node) => {
|
|
430
|
+
const element = node;
|
|
431
|
+
const rows = Array.from(element.rows);
|
|
432
|
+
if (rows.length === 0) return "";
|
|
433
|
+
let mdTable = "\n\n";
|
|
434
|
+
rows.forEach((row, index) => {
|
|
435
|
+
const cells = Array.from(row.cells);
|
|
436
|
+
const cellContents = cells.map((cell) => {
|
|
437
|
+
return this.turndown.turndown(cell.innerHTML).replace(/\n/g, " ").trim();
|
|
438
|
+
});
|
|
439
|
+
mdTable += `| ${cellContents.join(" | ")} |
|
|
440
|
+
`;
|
|
441
|
+
if (index === 0) {
|
|
442
|
+
const separators = cells.map((cell) => {
|
|
443
|
+
const style = cell.getAttribute("style") || "";
|
|
444
|
+
const align = style.match(/text-align:\s*(\w+)/i)?.[1]?.toLowerCase();
|
|
445
|
+
if (align === "center") return ":---:";
|
|
446
|
+
if (align === "right") return "---:";
|
|
447
|
+
if (align === "left") return ":---";
|
|
448
|
+
return "---";
|
|
449
|
+
});
|
|
450
|
+
mdTable += `| ${separators.join(" | ")} |
|
|
451
|
+
`;
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
return mdTable + "\n";
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
this.turndown.addRule("confluenceMacro", {
|
|
458
|
+
filter: (node) => {
|
|
459
|
+
return node.nodeName === "DIV" && node.getAttribute("data-macro-name-tag") !== null;
|
|
460
|
+
},
|
|
461
|
+
replacement: (content, node) => {
|
|
462
|
+
const element = node;
|
|
463
|
+
const macroName = element.getAttribute("data-macro-name");
|
|
464
|
+
if (macroName === "code") {
|
|
465
|
+
const lang = element.querySelector('[data-macro-param-name="language"]')?.textContent || "text";
|
|
466
|
+
let body = element.querySelector("[data-macro-body]")?.textContent || "";
|
|
467
|
+
body = body.replace(/__CDATA_START__/g, "").replace(/__CDATA_END__/g, "");
|
|
468
|
+
return `
|
|
469
|
+
\`\`\`${lang}
|
|
470
|
+
${body.trim()}
|
|
471
|
+
\`\`\`
|
|
472
|
+
`;
|
|
473
|
+
}
|
|
474
|
+
if (macroName === "mermaid" || macroName === "mermaiddiagram" || macroName === "capable-mermaid" || macroName === "mermaid-macro") {
|
|
475
|
+
let body = element.querySelector("[data-macro-body]")?.textContent || "";
|
|
476
|
+
body = body.replace(/__CDATA_START__/g, "").replace(/__CDATA_END__/g, "");
|
|
477
|
+
return `
|
|
478
|
+
\`\`\`mermaid
|
|
479
|
+
${body.trim()}
|
|
480
|
+
\`\`\`
|
|
481
|
+
`;
|
|
482
|
+
}
|
|
483
|
+
if (macroName === "plantuml") {
|
|
484
|
+
let body = element.querySelector("[data-macro-body]")?.textContent || "";
|
|
485
|
+
body = body.replace(/__CDATA_START__/g, "").replace(/__CDATA_END__/g, "");
|
|
486
|
+
return `
|
|
487
|
+
\`\`\`plantuml
|
|
488
|
+
${body.trim()}
|
|
489
|
+
\`\`\`
|
|
490
|
+
`;
|
|
491
|
+
}
|
|
492
|
+
return `
|
|
493
|
+
<!-- Macro: ${macroName} -->
|
|
494
|
+
`;
|
|
495
|
+
}
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
convert(storageHtml, imageUrlMap) {
|
|
499
|
+
if (!storageHtml) return "";
|
|
500
|
+
let processedHtml = storageHtml.replace(/<!\[CDATA\[([\s\S]*?)\]\]>/gi, (match, p1) => {
|
|
501
|
+
return `__CDATA_START__${p1}__CDATA_END__`;
|
|
502
|
+
}).replace(/<ac:structured-macro\s+ac:name="([^"]*)"/gi, '<div data-macro-name-tag data-macro-name="$1"').replace(/<\/ac:structured-macro>/gi, "</div>").replace(/<ac:parameter\s+ac:name="([^"]*)"/gi, '<div data-macro-param-tag data-macro-param-name="$1"').replace(/<\/ac:parameter>/gi, "</div>").replace(/<ac:plain-text-body>/gi, "<pre data-macro-body>").replace(/<\/ac:plain-text-body>/gi, "</pre>").replace(/<ac:image([^>]*)>[\s\S]*?<ri:attachment\s+ri:filename="([^"]*)"\s*\/?>[\s\S]*?<\/ac:image>/gi, (match, attrs, filename) => {
|
|
503
|
+
const altMatch = attrs.match(/ac:alt="([^"]*)"/i);
|
|
504
|
+
const alt = altMatch ? altMatch[1] : filename;
|
|
505
|
+
return `<img src="${filename}" alt="${alt}" />`;
|
|
506
|
+
}).replace(/<ac:image([^>]*)>[\s\S]*?<ri:url\s+ri:value="([^"]*)"\s*\/?>[\s\S]*?<\/ac:image>/gi, (match, attrs, url) => {
|
|
507
|
+
const altMatch = attrs.match(/ac:alt="([^"]*)"/i);
|
|
508
|
+
const alt = altMatch ? altMatch[1] : "";
|
|
509
|
+
return `<img src="${url}" alt="${alt}" />`;
|
|
510
|
+
});
|
|
511
|
+
const dom = new JSDOM(processedHtml);
|
|
512
|
+
const document = dom.window.document;
|
|
513
|
+
if (imageUrlMap && imageUrlMap.size > 0) {
|
|
514
|
+
const images = document.querySelectorAll("img");
|
|
515
|
+
images.forEach((img) => {
|
|
516
|
+
const src = img.getAttribute("src");
|
|
517
|
+
if (src && imageUrlMap.has(src)) {
|
|
518
|
+
img.setAttribute("src", imageUrlMap.get(src));
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
let markdown = this.turndown.turndown(document.body.innerHTML).trim();
|
|
523
|
+
markdown = markdown.replace(/^(#{1,6}\s.*?)\\\./gm, "$1.");
|
|
524
|
+
return markdown + "\n";
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
// tools/jira/api/issue.ts
|
|
374
529
|
var JiraIssueApi = class {
|
|
375
530
|
constructor(client) {
|
|
376
531
|
this.client = client;
|
|
@@ -430,7 +585,7 @@ var JiraIssueApi = class {
|
|
|
430
585
|
}
|
|
431
586
|
};
|
|
432
587
|
|
|
433
|
-
//
|
|
588
|
+
// tools/jira/api/search.ts
|
|
434
589
|
var JiraSearchApi = class {
|
|
435
590
|
constructor(client) {
|
|
436
591
|
this.client = client;
|
|
@@ -449,7 +604,7 @@ var JiraSearchApi = class {
|
|
|
449
604
|
}
|
|
450
605
|
};
|
|
451
606
|
|
|
452
|
-
//
|
|
607
|
+
// tools/jira/api/transition.ts
|
|
453
608
|
var JiraTransitionApi = class {
|
|
454
609
|
constructor(client) {
|
|
455
610
|
this.client = client;
|
|
@@ -469,7 +624,7 @@ var JiraTransitionApi = class {
|
|
|
469
624
|
}
|
|
470
625
|
};
|
|
471
626
|
|
|
472
|
-
//
|
|
627
|
+
// tools/jira/api/comment.ts
|
|
473
628
|
var JiraCommentApi = class {
|
|
474
629
|
constructor(client) {
|
|
475
630
|
this.client = client;
|
|
@@ -496,7 +651,7 @@ var JiraCommentApi = class {
|
|
|
496
651
|
}
|
|
497
652
|
};
|
|
498
653
|
|
|
499
|
-
//
|
|
654
|
+
// tools/jira/api/project.ts
|
|
500
655
|
var JiraProjectApi = class {
|
|
501
656
|
constructor(client) {
|
|
502
657
|
this.client = client;
|
|
@@ -526,12 +681,12 @@ var JiraProjectApi = class {
|
|
|
526
681
|
}
|
|
527
682
|
};
|
|
528
683
|
|
|
529
|
-
//
|
|
684
|
+
// tools/jira/api/client.ts
|
|
530
685
|
function createJiraClient(config) {
|
|
531
686
|
return createHttpClient(config);
|
|
532
687
|
}
|
|
533
688
|
|
|
534
|
-
//
|
|
689
|
+
// tools/gitlab/api/project.ts
|
|
535
690
|
var GitlabProjectApi = class {
|
|
536
691
|
constructor(client) {
|
|
537
692
|
this.client = client;
|
|
@@ -553,7 +708,7 @@ var GitlabProjectApi = class {
|
|
|
553
708
|
}
|
|
554
709
|
};
|
|
555
710
|
|
|
556
|
-
//
|
|
711
|
+
// tools/gitlab/api/merge-request.ts
|
|
557
712
|
var GitlabMergeRequestApi = class {
|
|
558
713
|
constructor(client) {
|
|
559
714
|
this.client = client;
|
|
@@ -614,7 +769,7 @@ var GitlabMergeRequestApi = class {
|
|
|
614
769
|
}
|
|
615
770
|
};
|
|
616
771
|
|
|
617
|
-
//
|
|
772
|
+
// tools/gitlab/api/pipeline.ts
|
|
618
773
|
var GitlabPipelineApi = class {
|
|
619
774
|
constructor(client) {
|
|
620
775
|
this.client = client;
|
|
@@ -649,7 +804,7 @@ var GitlabPipelineApi = class {
|
|
|
649
804
|
}
|
|
650
805
|
};
|
|
651
806
|
|
|
652
|
-
//
|
|
807
|
+
// tools/gitlab/api/branch.ts
|
|
653
808
|
var GitlabBranchApi = class {
|
|
654
809
|
constructor(client) {
|
|
655
810
|
this.client = client;
|
|
@@ -683,7 +838,7 @@ var GitlabBranchApi = class {
|
|
|
683
838
|
}
|
|
684
839
|
};
|
|
685
840
|
|
|
686
|
-
//
|
|
841
|
+
// tools/gitlab/api/repository.ts
|
|
687
842
|
var GitlabRepositoryApi = class {
|
|
688
843
|
constructor(client) {
|
|
689
844
|
this.client = client;
|
|
@@ -713,7 +868,7 @@ var GitlabRepositoryApi = class {
|
|
|
713
868
|
}
|
|
714
869
|
};
|
|
715
870
|
|
|
716
|
-
//
|
|
871
|
+
// tools/gitlab/api/client.ts
|
|
717
872
|
function createGitlabClient(config) {
|
|
718
873
|
const client = createHttpClient({
|
|
719
874
|
...config,
|
|
@@ -728,11 +883,11 @@ export {
|
|
|
728
883
|
ConfluenceSpaceApi,
|
|
729
884
|
ConfluenceSearchApi,
|
|
730
885
|
createConfluenceClient,
|
|
731
|
-
MarkdownToStorageConverter,
|
|
732
|
-
StorageToMarkdownConverter,
|
|
733
886
|
loadConfluenceConfig,
|
|
734
887
|
loadJiraConfig,
|
|
735
888
|
loadGitlabConfig,
|
|
889
|
+
MarkdownToStorageConverter,
|
|
890
|
+
StorageToMarkdownConverter,
|
|
736
891
|
JiraIssueApi,
|
|
737
892
|
JiraSearchApi,
|
|
738
893
|
JiraTransitionApi,
|
|
@@ -746,4 +901,4 @@ export {
|
|
|
746
901
|
GitlabRepositoryApi,
|
|
747
902
|
createGitlabClient
|
|
748
903
|
};
|
|
749
|
-
//# sourceMappingURL=chunk-
|
|
904
|
+
//# sourceMappingURL=chunk-UH2YGKTB.js.map
|