formatferry 1.0.8 → 1.0.10
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/dist/index.js +425 -414
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -17256,8 +17256,8 @@ var require_turndown_cjs = __commonJS({
|
|
|
17256
17256
|
[/_/g, "\\_"],
|
|
17257
17257
|
[/^(\d+)\. /g, "$1\\. "]
|
|
17258
17258
|
];
|
|
17259
|
-
function
|
|
17260
|
-
if (!(this instanceof
|
|
17259
|
+
function TurndownService3(options) {
|
|
17260
|
+
if (!(this instanceof TurndownService3)) return new TurndownService3(options);
|
|
17261
17261
|
var defaults = {
|
|
17262
17262
|
rules,
|
|
17263
17263
|
headingStyle: "setext",
|
|
@@ -17284,7 +17284,7 @@ var require_turndown_cjs = __commonJS({
|
|
|
17284
17284
|
this.options = extend({}, defaults, options);
|
|
17285
17285
|
this.rules = new Rules(this.options);
|
|
17286
17286
|
}
|
|
17287
|
-
|
|
17287
|
+
TurndownService3.prototype = {
|
|
17288
17288
|
/**
|
|
17289
17289
|
* The entry point for converting a string or DOM node to Markdown
|
|
17290
17290
|
* @public
|
|
@@ -17405,7 +17405,7 @@ var require_turndown_cjs = __commonJS({
|
|
|
17405
17405
|
function canConvert(input) {
|
|
17406
17406
|
return input != null && (typeof input === "string" || input.nodeType && (input.nodeType === 1 || input.nodeType === 9 || input.nodeType === 11));
|
|
17407
17407
|
}
|
|
17408
|
-
module.exports =
|
|
17408
|
+
module.exports = TurndownService3;
|
|
17409
17409
|
}
|
|
17410
17410
|
});
|
|
17411
17411
|
|
|
@@ -19490,7 +19490,7 @@ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "
|
|
|
19490
19490
|
import { extname } from "path";
|
|
19491
19491
|
|
|
19492
19492
|
// ../../src/shared/conversion-engine.ts
|
|
19493
|
-
var
|
|
19493
|
+
var import_turndown2 = __toESM(require_turndown_cjs(), 1);
|
|
19494
19494
|
|
|
19495
19495
|
// ../../src/shared/input-sanitizer.ts
|
|
19496
19496
|
import DOMPurifyImport from "dompurify";
|
|
@@ -19978,6 +19978,357 @@ function normalizeAtlassianHTML(html, domParser) {
|
|
|
19978
19978
|
return doc.body.innerHTML;
|
|
19979
19979
|
}
|
|
19980
19980
|
|
|
19981
|
+
// ../../src/shared/confluence-slack-services.ts
|
|
19982
|
+
var import_turndown = __toESM(require_turndown_cjs(), 1);
|
|
19983
|
+
function createSlackService(deps) {
|
|
19984
|
+
const service = new import_turndown.default({
|
|
19985
|
+
headingStyle: "atx",
|
|
19986
|
+
hr: "---",
|
|
19987
|
+
bulletListMarker: "-",
|
|
19988
|
+
codeBlockStyle: "fenced",
|
|
19989
|
+
fence: "```",
|
|
19990
|
+
emDelimiter: "_"
|
|
19991
|
+
});
|
|
19992
|
+
deps.addListRules(service);
|
|
19993
|
+
deps.addPdfPageMarkerRules(service);
|
|
19994
|
+
service.addRule("slackBold", {
|
|
19995
|
+
filter: (node) => {
|
|
19996
|
+
if (node.nodeName === "STRONG" || node.nodeName === "B") return true;
|
|
19997
|
+
if (node.nodeName === "SPAN") {
|
|
19998
|
+
const style = node.getAttribute("style") || "";
|
|
19999
|
+
if (style.includes("font-weight: bold") || style.includes("font-weight:bold")) return true;
|
|
20000
|
+
const fwMatch = style.match(/font-weight:\s*(\d+)/);
|
|
20001
|
+
if (fwMatch && parseInt(fwMatch[1]) >= 600) return true;
|
|
20002
|
+
const ffMatch = style.match(/font-family:\s*['"]*([^'"]+)['"]*[;]?/);
|
|
20003
|
+
if (ffMatch && ffMatch[1].toLowerCase().includes("bold")) return true;
|
|
20004
|
+
}
|
|
20005
|
+
return false;
|
|
20006
|
+
},
|
|
20007
|
+
replacement: (content) => content.trim() ? `*${content}*` : content
|
|
20008
|
+
});
|
|
20009
|
+
service.addRule("slackItalic", {
|
|
20010
|
+
filter: (node) => {
|
|
20011
|
+
if (node.nodeName === "EM" || node.nodeName === "I") return true;
|
|
20012
|
+
if (node.nodeName === "SPAN") {
|
|
20013
|
+
const style = node.getAttribute("style") || "";
|
|
20014
|
+
if (style.includes("font-style: italic") || style.includes("font-style:italic")) return true;
|
|
20015
|
+
const ffMatch = style.match(/font-family:\s*['"]*([^'"]+)['"]*[;]?/);
|
|
20016
|
+
if (ffMatch && ffMatch[1].toLowerCase().includes("italic")) return true;
|
|
20017
|
+
}
|
|
20018
|
+
return false;
|
|
20019
|
+
},
|
|
20020
|
+
replacement: (content) => content.trim() ? `_${content}_` : content
|
|
20021
|
+
});
|
|
20022
|
+
service.addRule("slackStrikethrough", {
|
|
20023
|
+
filter: (node) => {
|
|
20024
|
+
if (["DEL", "S", "STRIKE"].includes(node.nodeName)) return true;
|
|
20025
|
+
if (node.nodeName === "SPAN") {
|
|
20026
|
+
const style = node.getAttribute("style") || "";
|
|
20027
|
+
return style.includes("text-decoration: line-through") || style.includes("text-decoration:line-through") || style.includes("text-decoration-line: line-through") || style.includes("text-decoration-line:line-through");
|
|
20028
|
+
}
|
|
20029
|
+
return false;
|
|
20030
|
+
},
|
|
20031
|
+
replacement: (content) => content.trim() ? `~${content}~` : content
|
|
20032
|
+
});
|
|
20033
|
+
service.addRule("slackMonospaceCode", {
|
|
20034
|
+
filter: (node) => {
|
|
20035
|
+
if (node.nodeName !== "SPAN") return false;
|
|
20036
|
+
const style = node.getAttribute("style") || "";
|
|
20037
|
+
const fontFamily = style.toLowerCase();
|
|
20038
|
+
return fontFamily.includes("monospace") || fontFamily.includes("courier") || fontFamily.includes("consolas") || fontFamily.includes("monaco") || fontFamily.includes("menlo") || fontFamily.includes("source code") || fontFamily.includes("fira code") || fontFamily.includes("roboto mono") || fontFamily.includes("ubuntu mono");
|
|
20039
|
+
},
|
|
20040
|
+
replacement: (content) => {
|
|
20041
|
+
const trimmed = content.trim();
|
|
20042
|
+
if (!trimmed) return content;
|
|
20043
|
+
return trimmed.includes("`") ? `\`\` ${trimmed} \`\`` : `\`${trimmed}\``;
|
|
20044
|
+
}
|
|
20045
|
+
});
|
|
20046
|
+
service.addRule("slackHeading", {
|
|
20047
|
+
filter: ["h1", "h2", "h3", "h4", "h5", "h6"],
|
|
20048
|
+
replacement: (content) => content.trim() ? `*${content.trim()}*
|
|
20049
|
+
|
|
20050
|
+
` : content
|
|
20051
|
+
});
|
|
20052
|
+
service.addRule("slackGoogleDocsHeadings", {
|
|
20053
|
+
filter: (node) => {
|
|
20054
|
+
if (node.nodeName !== "P" && node.nodeName !== "DIV") return false;
|
|
20055
|
+
const style = node.getAttribute("style") || "";
|
|
20056
|
+
const fontSizeMatch = style.match(/font-size:\s*(\d+(?:\.\d+)?)px/);
|
|
20057
|
+
if (fontSizeMatch && parseFloat(fontSizeMatch[1]) > 16) return true;
|
|
20058
|
+
if (style.includes("font-weight: bold") || style.includes("font-weight:bold")) {
|
|
20059
|
+
const textContent = node.textContent || "";
|
|
20060
|
+
return textContent.length < 100 && textContent.trim().length > 0;
|
|
20061
|
+
}
|
|
20062
|
+
return false;
|
|
20063
|
+
},
|
|
20064
|
+
replacement: (content) => content.trim() ? `*${content.trim()}*
|
|
20065
|
+
|
|
20066
|
+
` : content
|
|
20067
|
+
});
|
|
20068
|
+
service.addRule("slackUnderline", {
|
|
20069
|
+
filter: (node) => {
|
|
20070
|
+
if (node.nodeName === "U" || node.nodeName === "INS") return true;
|
|
20071
|
+
if (node.nodeName === "SPAN") {
|
|
20072
|
+
const style = node.getAttribute("style") || "";
|
|
20073
|
+
return style.includes("text-decoration: underline") || style.includes("text-decoration:underline");
|
|
20074
|
+
}
|
|
20075
|
+
return false;
|
|
20076
|
+
},
|
|
20077
|
+
replacement: (content) => content
|
|
20078
|
+
});
|
|
20079
|
+
service.addRule("slackLink", {
|
|
20080
|
+
filter: (node) => node.nodeName === "A" && !!node.getAttribute("href"),
|
|
20081
|
+
replacement: (content, node) => {
|
|
20082
|
+
const href = node.getAttribute("href") || "";
|
|
20083
|
+
const text = content.trim().replace(/\|/g, " ");
|
|
20084
|
+
if (!text || text === href) return `<${href}>`;
|
|
20085
|
+
return `<${href}|${text}>`;
|
|
20086
|
+
}
|
|
20087
|
+
});
|
|
20088
|
+
service.addRule("slackImage", {
|
|
20089
|
+
filter: "img",
|
|
20090
|
+
replacement: (_content, node) => {
|
|
20091
|
+
const src = node.getAttribute("src") || "";
|
|
20092
|
+
const alt = (node.getAttribute("alt") || "image").replace(/\|/g, " ");
|
|
20093
|
+
return `<${src}|${alt}>`;
|
|
20094
|
+
}
|
|
20095
|
+
});
|
|
20096
|
+
service.addRule("slackTable", {
|
|
20097
|
+
filter: "table",
|
|
20098
|
+
replacement: (_content, node) => {
|
|
20099
|
+
const plainText = deps.tableToPlainText(node);
|
|
20100
|
+
return `\`\`\`
|
|
20101
|
+
${plainText}
|
|
20102
|
+
\`\`\`
|
|
20103
|
+
|
|
20104
|
+
`;
|
|
20105
|
+
}
|
|
20106
|
+
});
|
|
20107
|
+
return service;
|
|
20108
|
+
}
|
|
20109
|
+
function processConfluenceList(list, service, depth) {
|
|
20110
|
+
const isOrdered = list.nodeName === "OL";
|
|
20111
|
+
const marker = (isOrdered ? "#" : "*").repeat(depth);
|
|
20112
|
+
const items = Array.from(list.children).filter((c) => c.nodeName === "LI");
|
|
20113
|
+
let result = depth === 1 ? "\n" : "";
|
|
20114
|
+
items.forEach((item) => {
|
|
20115
|
+
const itemContent = processConfluenceListItem(item, service, depth);
|
|
20116
|
+
result += `${marker} ${itemContent}
|
|
20117
|
+
`;
|
|
20118
|
+
});
|
|
20119
|
+
return depth === 1 ? result + "\n" : result;
|
|
20120
|
+
}
|
|
20121
|
+
function processConfluenceListItem(item, service, depth) {
|
|
20122
|
+
let content = "";
|
|
20123
|
+
const TEXT_NODE = 3;
|
|
20124
|
+
const ELEMENT_NODE = 1;
|
|
20125
|
+
Array.from(item.childNodes).forEach((child) => {
|
|
20126
|
+
if (child.nodeType === TEXT_NODE) {
|
|
20127
|
+
content += child.textContent || "";
|
|
20128
|
+
} else if (child.nodeType === ELEMENT_NODE) {
|
|
20129
|
+
const el = child;
|
|
20130
|
+
if (el.nodeName === "UL" || el.nodeName === "OL") {
|
|
20131
|
+
content += "\n" + processConfluenceList(el, service, depth + 1);
|
|
20132
|
+
} else {
|
|
20133
|
+
content += service.turndown(el.outerHTML);
|
|
20134
|
+
}
|
|
20135
|
+
}
|
|
20136
|
+
});
|
|
20137
|
+
return content.trim();
|
|
20138
|
+
}
|
|
20139
|
+
function createConfluenceService(deps) {
|
|
20140
|
+
const service = new import_turndown.default({
|
|
20141
|
+
headingStyle: "atx",
|
|
20142
|
+
hr: "---",
|
|
20143
|
+
bulletListMarker: "-",
|
|
20144
|
+
codeBlockStyle: "fenced",
|
|
20145
|
+
emDelimiter: "_"
|
|
20146
|
+
});
|
|
20147
|
+
deps.addPdfPageMarkerRules(service);
|
|
20148
|
+
service.addRule("confluenceHeading", {
|
|
20149
|
+
filter: ["h1", "h2", "h3", "h4", "h5", "h6"],
|
|
20150
|
+
replacement: (content, node) => {
|
|
20151
|
+
const level = parseInt(node.nodeName[1], 10);
|
|
20152
|
+
return `
|
|
20153
|
+
|
|
20154
|
+
h${level}. ${content.trim()}
|
|
20155
|
+
|
|
20156
|
+
`;
|
|
20157
|
+
}
|
|
20158
|
+
});
|
|
20159
|
+
service.addRule("confluenceCodeBlock", {
|
|
20160
|
+
filter: "pre",
|
|
20161
|
+
replacement: (_content, node) => {
|
|
20162
|
+
const codeEl = node.querySelector("code");
|
|
20163
|
+
const langMatch = codeEl?.className?.match(/language-(\w+)/);
|
|
20164
|
+
const lang = langMatch ? langMatch[1] : "";
|
|
20165
|
+
const codeContent = codeEl?.textContent || node.textContent || "";
|
|
20166
|
+
const langAttr = lang ? `:language=${lang}` : "";
|
|
20167
|
+
return `
|
|
20168
|
+
|
|
20169
|
+
{code${langAttr}}
|
|
20170
|
+
${codeContent}
|
|
20171
|
+
{code}
|
|
20172
|
+
|
|
20173
|
+
`;
|
|
20174
|
+
}
|
|
20175
|
+
});
|
|
20176
|
+
service.addRule("confluenceInlineCode", {
|
|
20177
|
+
filter: (node) => node.nodeName === "CODE" && node.parentNode?.nodeName !== "PRE",
|
|
20178
|
+
replacement: (content) => content ? `{{${content.replace(/}+/g, (m) => m.split("").join(" "))}}}` : ""
|
|
20179
|
+
});
|
|
20180
|
+
service.addRule("confluenceBold", {
|
|
20181
|
+
filter: (node) => {
|
|
20182
|
+
if (node.nodeName === "STRONG" || node.nodeName === "B") return true;
|
|
20183
|
+
if (node.nodeName === "SPAN") {
|
|
20184
|
+
const style = node.getAttribute("style") || "";
|
|
20185
|
+
if (style.includes("font-weight: bold") || style.includes("font-weight:bold")) return true;
|
|
20186
|
+
const fwMatch = style.match(/font-weight:\s*(\d+)/);
|
|
20187
|
+
if (fwMatch && parseInt(fwMatch[1]) >= 600) return true;
|
|
20188
|
+
const ffMatch = style.match(/font-family:\s*['"]*([^'"]+)['"]*[;]?/);
|
|
20189
|
+
if (ffMatch && ffMatch[1].toLowerCase().includes("bold")) return true;
|
|
20190
|
+
}
|
|
20191
|
+
return false;
|
|
20192
|
+
},
|
|
20193
|
+
replacement: (content) => content.trim() ? `*${content}*` : content
|
|
20194
|
+
});
|
|
20195
|
+
service.addRule("confluenceItalic", {
|
|
20196
|
+
filter: (node) => {
|
|
20197
|
+
if (node.nodeName === "EM" || node.nodeName === "I") return true;
|
|
20198
|
+
if (node.nodeName === "SPAN") {
|
|
20199
|
+
const style = node.getAttribute("style") || "";
|
|
20200
|
+
if (style.includes("font-style: italic") || style.includes("font-style:italic")) return true;
|
|
20201
|
+
const ffMatch = style.match(/font-family:\s*['"]*([^'"]+)['"]*[;]?/);
|
|
20202
|
+
if (ffMatch && ffMatch[1].toLowerCase().includes("italic")) return true;
|
|
20203
|
+
}
|
|
20204
|
+
return false;
|
|
20205
|
+
},
|
|
20206
|
+
replacement: (content) => content.trim() ? `_${content}_` : content
|
|
20207
|
+
});
|
|
20208
|
+
service.addRule("confluenceUnderline", {
|
|
20209
|
+
filter: (node) => {
|
|
20210
|
+
if (node.nodeName === "U" || node.nodeName === "INS") return true;
|
|
20211
|
+
if (node.nodeName === "SPAN") {
|
|
20212
|
+
const style = node.getAttribute("style") || "";
|
|
20213
|
+
return style.includes("text-decoration: underline") || style.includes("text-decoration:underline");
|
|
20214
|
+
}
|
|
20215
|
+
return false;
|
|
20216
|
+
},
|
|
20217
|
+
replacement: (content) => content.trim() ? `+${content}+` : content
|
|
20218
|
+
});
|
|
20219
|
+
service.addRule("confluenceStrikethrough", {
|
|
20220
|
+
filter: (node) => {
|
|
20221
|
+
if (["DEL", "S", "STRIKE"].includes(node.nodeName)) return true;
|
|
20222
|
+
if (node.nodeName === "SPAN") {
|
|
20223
|
+
const style = node.getAttribute("style") || "";
|
|
20224
|
+
return style.includes("text-decoration: line-through") || style.includes("text-decoration:line-through") || style.includes("text-decoration-line: line-through") || style.includes("text-decoration-line:line-through");
|
|
20225
|
+
}
|
|
20226
|
+
return false;
|
|
20227
|
+
},
|
|
20228
|
+
replacement: (content) => content.trim() ? `-${content}-` : content
|
|
20229
|
+
});
|
|
20230
|
+
service.addRule("confluenceMonospaceCode", {
|
|
20231
|
+
filter: (node) => {
|
|
20232
|
+
if (node.nodeName !== "SPAN") return false;
|
|
20233
|
+
const style = node.getAttribute("style") || "";
|
|
20234
|
+
const fontFamily = style.toLowerCase();
|
|
20235
|
+
return fontFamily.includes("monospace") || fontFamily.includes("courier") || fontFamily.includes("consolas") || fontFamily.includes("monaco") || fontFamily.includes("menlo") || fontFamily.includes("source code") || fontFamily.includes("fira code") || fontFamily.includes("roboto mono") || fontFamily.includes("ubuntu mono");
|
|
20236
|
+
},
|
|
20237
|
+
replacement: (content) => content.trim() ? `{{${content.trim().replace(/}+/g, (m) => m.split("").join(" "))}}}` : content
|
|
20238
|
+
});
|
|
20239
|
+
service.addRule("confluenceLink", {
|
|
20240
|
+
filter: (node) => node.nodeName === "A" && !!node.getAttribute("href"),
|
|
20241
|
+
replacement: (content, node) => {
|
|
20242
|
+
const href = node.getAttribute("href") || "";
|
|
20243
|
+
const text = content.trim().replace(/\|/g, " ");
|
|
20244
|
+
if (!text || text === href) return `[${href}]`;
|
|
20245
|
+
return `[${text}|${href}]`;
|
|
20246
|
+
}
|
|
20247
|
+
});
|
|
20248
|
+
service.addRule("confluenceImage", {
|
|
20249
|
+
filter: "img",
|
|
20250
|
+
replacement: (_content, node) => {
|
|
20251
|
+
const src = node.getAttribute("src") || "";
|
|
20252
|
+
return src ? `!${src}!` : "";
|
|
20253
|
+
}
|
|
20254
|
+
});
|
|
20255
|
+
service.addRule("confluenceHr", {
|
|
20256
|
+
filter: "hr",
|
|
20257
|
+
replacement: () => "\n\n----\n\n"
|
|
20258
|
+
});
|
|
20259
|
+
service.addRule("confluenceBlockquote", {
|
|
20260
|
+
filter: "blockquote",
|
|
20261
|
+
replacement: (content) => `
|
|
20262
|
+
|
|
20263
|
+
{quote}
|
|
20264
|
+
${content.trim()}
|
|
20265
|
+
{quote}
|
|
20266
|
+
|
|
20267
|
+
`
|
|
20268
|
+
});
|
|
20269
|
+
service.addRule("confluenceGoogleDocsHeadings", {
|
|
20270
|
+
filter: (node) => {
|
|
20271
|
+
if (node.nodeName !== "P" && node.nodeName !== "DIV") return false;
|
|
20272
|
+
const style = node.getAttribute("style") || "";
|
|
20273
|
+
const fontSizeMatch = style.match(/font-size:\s*(\d+(?:\.\d+)?)px/);
|
|
20274
|
+
if (fontSizeMatch && parseFloat(fontSizeMatch[1]) > 16) return true;
|
|
20275
|
+
if (style.includes("font-weight: bold") || style.includes("font-weight:bold")) {
|
|
20276
|
+
const textContent = node.textContent || "";
|
|
20277
|
+
return textContent.length < 100 && textContent.trim().length > 0;
|
|
20278
|
+
}
|
|
20279
|
+
return false;
|
|
20280
|
+
},
|
|
20281
|
+
replacement: (content, node) => {
|
|
20282
|
+
const style = node?.getAttribute("style") || "";
|
|
20283
|
+
const fontSizeMatch = style.match(/font-size:\s*(\d+(?:\.\d+)?)px/);
|
|
20284
|
+
const trimmed = content.trim();
|
|
20285
|
+
if (!trimmed) return content;
|
|
20286
|
+
if (!fontSizeMatch) {
|
|
20287
|
+
return `
|
|
20288
|
+
|
|
20289
|
+
*${trimmed}*
|
|
20290
|
+
|
|
20291
|
+
`;
|
|
20292
|
+
}
|
|
20293
|
+
const fontSize = parseFloat(fontSizeMatch[1]);
|
|
20294
|
+
let level = 5;
|
|
20295
|
+
if (fontSize >= 28) level = 1;
|
|
20296
|
+
else if (fontSize >= 24) level = 2;
|
|
20297
|
+
else if (fontSize >= 20) level = 3;
|
|
20298
|
+
else if (fontSize >= 18) level = 4;
|
|
20299
|
+
return `
|
|
20300
|
+
|
|
20301
|
+
h${level}. ${trimmed}
|
|
20302
|
+
|
|
20303
|
+
`;
|
|
20304
|
+
}
|
|
20305
|
+
});
|
|
20306
|
+
service.addRule("confluenceList", {
|
|
20307
|
+
filter: ["ul", "ol"],
|
|
20308
|
+
replacement: (_content, node) => {
|
|
20309
|
+
return processConfluenceList(node, service, 1);
|
|
20310
|
+
}
|
|
20311
|
+
});
|
|
20312
|
+
service.addRule("confluenceTable", {
|
|
20313
|
+
filter: "table",
|
|
20314
|
+
replacement: (_content, node) => {
|
|
20315
|
+
const table = node;
|
|
20316
|
+
const rows = Array.from(table.querySelectorAll("tr"));
|
|
20317
|
+
if (rows.length === 0) return "";
|
|
20318
|
+
let result = "\n";
|
|
20319
|
+
rows.forEach((row) => {
|
|
20320
|
+
const cells = Array.from(row.querySelectorAll("th, td"));
|
|
20321
|
+
const isHeaderRow = cells.some((c) => c.nodeName === "TH");
|
|
20322
|
+
const delim = isHeaderRow ? "||" : "|";
|
|
20323
|
+
const cellTexts = cells.map((c) => (c.textContent || "").trim());
|
|
20324
|
+
result += delim + cellTexts.join(delim) + delim + "\n";
|
|
20325
|
+
});
|
|
20326
|
+
return result + "\n";
|
|
20327
|
+
}
|
|
20328
|
+
});
|
|
20329
|
+
return service;
|
|
20330
|
+
}
|
|
20331
|
+
|
|
19981
20332
|
// ../../src/shared/conversion-engine.ts
|
|
19982
20333
|
var ConversionEngine = class {
|
|
19983
20334
|
githubService;
|
|
@@ -19992,21 +20343,21 @@ var ConversionEngine = class {
|
|
|
19992
20343
|
this.initializeServices();
|
|
19993
20344
|
}
|
|
19994
20345
|
initializeServices() {
|
|
19995
|
-
this.githubService = new
|
|
20346
|
+
this.githubService = new import_turndown2.default({
|
|
19996
20347
|
headingStyle: "atx",
|
|
19997
20348
|
hr: "---",
|
|
19998
20349
|
bulletListMarker: "-",
|
|
19999
20350
|
codeBlockStyle: "fenced",
|
|
20000
20351
|
emDelimiter: "_"
|
|
20001
20352
|
});
|
|
20002
|
-
this.commonmarkService = new
|
|
20353
|
+
this.commonmarkService = new import_turndown2.default({
|
|
20003
20354
|
headingStyle: "atx",
|
|
20004
20355
|
hr: "***",
|
|
20005
20356
|
bulletListMarker: "*",
|
|
20006
20357
|
codeBlockStyle: "indented",
|
|
20007
20358
|
emDelimiter: "*"
|
|
20008
20359
|
});
|
|
20009
|
-
this.customService = new
|
|
20360
|
+
this.customService = new import_turndown2.default({
|
|
20010
20361
|
headingStyle: "atx",
|
|
20011
20362
|
hr: "---",
|
|
20012
20363
|
bulletListMarker: "-",
|
|
@@ -20028,8 +20379,13 @@ var ConversionEngine = class {
|
|
|
20028
20379
|
this.discordService = this.createDiscordService();
|
|
20029
20380
|
this.redditService = this.createRedditService();
|
|
20030
20381
|
this.rmarkdownService = this.createRMarkdownService();
|
|
20031
|
-
|
|
20032
|
-
|
|
20382
|
+
const deps = {
|
|
20383
|
+
addListRules: this.addListRules.bind(this),
|
|
20384
|
+
addPdfPageMarkerRules: this.addPdfPageMarkerRules.bind(this),
|
|
20385
|
+
tableToPlainText: this.tableToPlainText.bind(this)
|
|
20386
|
+
};
|
|
20387
|
+
this.slackService = createSlackService(deps);
|
|
20388
|
+
this.confluenceService = createConfluenceService(deps);
|
|
20033
20389
|
}
|
|
20034
20390
|
/**
|
|
20035
20391
|
* Create Discord-optimized Turndown service
|
|
@@ -20039,7 +20395,7 @@ var ConversionEngine = class {
|
|
|
20039
20395
|
* - Images converted to links
|
|
20040
20396
|
*/
|
|
20041
20397
|
createDiscordService() {
|
|
20042
|
-
const service = new
|
|
20398
|
+
const service = new import_turndown2.default({
|
|
20043
20399
|
headingStyle: "atx",
|
|
20044
20400
|
hr: "---",
|
|
20045
20401
|
bulletListMarker: "-",
|
|
@@ -20216,7 +20572,7 @@ ${plainText}
|
|
|
20216
20572
|
* - Fenced code blocks (new Reddit)
|
|
20217
20573
|
*/
|
|
20218
20574
|
createRedditService() {
|
|
20219
|
-
const service = new
|
|
20575
|
+
const service = new import_turndown2.default({
|
|
20220
20576
|
headingStyle: "atx",
|
|
20221
20577
|
hr: "---",
|
|
20222
20578
|
bulletListMarker: "-",
|
|
@@ -20274,7 +20630,7 @@ ${plainText}
|
|
|
20274
20630
|
* - Math expression preservation
|
|
20275
20631
|
*/
|
|
20276
20632
|
createRMarkdownService() {
|
|
20277
|
-
const service = new
|
|
20633
|
+
const service = new import_turndown2.default({
|
|
20278
20634
|
headingStyle: "atx",
|
|
20279
20635
|
hr: "---",
|
|
20280
20636
|
bulletListMarker: "-",
|
|
@@ -20337,377 +20693,6 @@ ${plainText}
|
|
|
20337
20693
|
});
|
|
20338
20694
|
return service;
|
|
20339
20695
|
}
|
|
20340
|
-
/**
|
|
20341
|
-
* Create Slack mrkdwn-optimized Turndown service
|
|
20342
|
-
* - Bold uses *text* (single asterisk, not **)
|
|
20343
|
-
* - Italic uses _text_
|
|
20344
|
-
* - Strikethrough uses ~text~ (single tilde)
|
|
20345
|
-
* - Links use <url|text> format
|
|
20346
|
-
* - Headers converted to bold (Slack has no heading support)
|
|
20347
|
-
* - Tables converted to code blocks
|
|
20348
|
-
*/
|
|
20349
|
-
createSlackService() {
|
|
20350
|
-
const service = new import_turndown.default({
|
|
20351
|
-
headingStyle: "atx",
|
|
20352
|
-
hr: "---",
|
|
20353
|
-
bulletListMarker: "-",
|
|
20354
|
-
codeBlockStyle: "fenced",
|
|
20355
|
-
fence: "```",
|
|
20356
|
-
emDelimiter: "_"
|
|
20357
|
-
});
|
|
20358
|
-
this.addListRules(service);
|
|
20359
|
-
this.addPdfPageMarkerRules(service);
|
|
20360
|
-
service.addRule("slackBold", {
|
|
20361
|
-
filter: (node) => {
|
|
20362
|
-
if (node.nodeName === "STRONG" || node.nodeName === "B") return true;
|
|
20363
|
-
if (node.nodeName === "SPAN") {
|
|
20364
|
-
const style = node.getAttribute("style") || "";
|
|
20365
|
-
if (style.includes("font-weight: bold") || style.includes("font-weight:bold")) return true;
|
|
20366
|
-
const fwMatch = style.match(/font-weight:\s*(\d+)/);
|
|
20367
|
-
if (fwMatch && parseInt(fwMatch[1]) >= 600) return true;
|
|
20368
|
-
const ffMatch = style.match(/font-family:\s*['"]*([^'"]+)['"]*[;]?/);
|
|
20369
|
-
if (ffMatch && ffMatch[1].toLowerCase().includes("bold")) return true;
|
|
20370
|
-
}
|
|
20371
|
-
return false;
|
|
20372
|
-
},
|
|
20373
|
-
replacement: (content) => content.trim() ? `*${content}*` : content
|
|
20374
|
-
});
|
|
20375
|
-
service.addRule("slackItalic", {
|
|
20376
|
-
filter: (node) => {
|
|
20377
|
-
if (node.nodeName === "EM" || node.nodeName === "I") return true;
|
|
20378
|
-
if (node.nodeName === "SPAN") {
|
|
20379
|
-
const style = node.getAttribute("style") || "";
|
|
20380
|
-
if (style.includes("font-style: italic") || style.includes("font-style:italic")) return true;
|
|
20381
|
-
const ffMatch = style.match(/font-family:\s*['"]*([^'"]+)['"]*[;]?/);
|
|
20382
|
-
if (ffMatch && ffMatch[1].toLowerCase().includes("italic")) return true;
|
|
20383
|
-
}
|
|
20384
|
-
return false;
|
|
20385
|
-
},
|
|
20386
|
-
replacement: (content) => content.trim() ? `_${content}_` : content
|
|
20387
|
-
});
|
|
20388
|
-
service.addRule("slackStrikethrough", {
|
|
20389
|
-
filter: (node) => {
|
|
20390
|
-
if (["DEL", "S", "STRIKE"].includes(node.nodeName)) return true;
|
|
20391
|
-
if (node.nodeName === "SPAN") {
|
|
20392
|
-
const style = node.getAttribute("style") || "";
|
|
20393
|
-
return style.includes("text-decoration: line-through") || style.includes("text-decoration:line-through") || style.includes("text-decoration-line: line-through") || style.includes("text-decoration-line:line-through");
|
|
20394
|
-
}
|
|
20395
|
-
return false;
|
|
20396
|
-
},
|
|
20397
|
-
replacement: (content) => content.trim() ? `~${content}~` : content
|
|
20398
|
-
});
|
|
20399
|
-
service.addRule("slackMonospaceCode", {
|
|
20400
|
-
filter: (node) => {
|
|
20401
|
-
if (node.nodeName !== "SPAN") return false;
|
|
20402
|
-
const style = node.getAttribute("style") || "";
|
|
20403
|
-
const fontFamily = style.toLowerCase();
|
|
20404
|
-
return fontFamily.includes("monospace") || fontFamily.includes("courier") || fontFamily.includes("consolas") || fontFamily.includes("monaco") || fontFamily.includes("menlo") || fontFamily.includes("source code") || fontFamily.includes("fira code") || fontFamily.includes("roboto mono") || fontFamily.includes("ubuntu mono");
|
|
20405
|
-
},
|
|
20406
|
-
replacement: (content) => {
|
|
20407
|
-
const trimmed = content.trim();
|
|
20408
|
-
if (!trimmed) return content;
|
|
20409
|
-
return trimmed.includes("`") ? `\`\` ${trimmed} \`\`` : `\`${trimmed}\``;
|
|
20410
|
-
}
|
|
20411
|
-
});
|
|
20412
|
-
service.addRule("slackHeading", {
|
|
20413
|
-
filter: ["h1", "h2", "h3", "h4", "h5", "h6"],
|
|
20414
|
-
replacement: (content) => content.trim() ? `*${content.trim()}*
|
|
20415
|
-
|
|
20416
|
-
` : content
|
|
20417
|
-
});
|
|
20418
|
-
service.addRule("slackGoogleDocsHeadings", {
|
|
20419
|
-
filter: (node) => {
|
|
20420
|
-
if (node.nodeName !== "P" && node.nodeName !== "DIV") return false;
|
|
20421
|
-
const style = node.getAttribute("style") || "";
|
|
20422
|
-
const fontSizeMatch = style.match(/font-size:\s*(\d+(?:\.\d+)?)px/);
|
|
20423
|
-
if (fontSizeMatch && parseFloat(fontSizeMatch[1]) > 16) return true;
|
|
20424
|
-
if (style.includes("font-weight: bold") || style.includes("font-weight:bold")) {
|
|
20425
|
-
const textContent = node.textContent || "";
|
|
20426
|
-
return textContent.length < 100 && textContent.trim().length > 0;
|
|
20427
|
-
}
|
|
20428
|
-
return false;
|
|
20429
|
-
},
|
|
20430
|
-
replacement: (content) => content.trim() ? `*${content.trim()}*
|
|
20431
|
-
|
|
20432
|
-
` : content
|
|
20433
|
-
});
|
|
20434
|
-
service.addRule("slackUnderline", {
|
|
20435
|
-
filter: (node) => {
|
|
20436
|
-
if (node.nodeName === "U" || node.nodeName === "INS") return true;
|
|
20437
|
-
if (node.nodeName === "SPAN") {
|
|
20438
|
-
const style = node.getAttribute("style") || "";
|
|
20439
|
-
return style.includes("text-decoration: underline") || style.includes("text-decoration:underline");
|
|
20440
|
-
}
|
|
20441
|
-
return false;
|
|
20442
|
-
},
|
|
20443
|
-
replacement: (content) => content
|
|
20444
|
-
});
|
|
20445
|
-
service.addRule("slackLink", {
|
|
20446
|
-
filter: (node) => node.nodeName === "A" && !!node.getAttribute("href"),
|
|
20447
|
-
replacement: (content, node) => {
|
|
20448
|
-
const href = node.getAttribute("href") || "";
|
|
20449
|
-
const text = content.trim().replace(/\|/g, " ");
|
|
20450
|
-
if (!text || text === href) return `<${href}>`;
|
|
20451
|
-
return `<${href}|${text}>`;
|
|
20452
|
-
}
|
|
20453
|
-
});
|
|
20454
|
-
service.addRule("slackImage", {
|
|
20455
|
-
filter: "img",
|
|
20456
|
-
replacement: (_content, node) => {
|
|
20457
|
-
const src = node.getAttribute("src") || "";
|
|
20458
|
-
const alt = (node.getAttribute("alt") || "image").replace(/\|/g, " ");
|
|
20459
|
-
return `<${src}|${alt}>`;
|
|
20460
|
-
}
|
|
20461
|
-
});
|
|
20462
|
-
service.addRule("slackTable", {
|
|
20463
|
-
filter: "table",
|
|
20464
|
-
replacement: (_content, node) => {
|
|
20465
|
-
const plainText = this.tableToPlainText(node);
|
|
20466
|
-
return `\`\`\`
|
|
20467
|
-
${plainText}
|
|
20468
|
-
\`\`\`
|
|
20469
|
-
|
|
20470
|
-
`;
|
|
20471
|
-
}
|
|
20472
|
-
});
|
|
20473
|
-
return service;
|
|
20474
|
-
}
|
|
20475
|
-
/**
|
|
20476
|
-
* Create Confluence wiki markup Turndown service
|
|
20477
|
-
* - Headings: h1. h2. etc.
|
|
20478
|
-
* - Bold: *text*, Italic: _text_, Underline: +text+, Strikethrough: -text-
|
|
20479
|
-
* - Inline code: {{text}}, Code blocks: {code:language=x}...{code}
|
|
20480
|
-
* - Links: [text|url], Images: !url!
|
|
20481
|
-
* - Tables: ||header|| / |cell| format
|
|
20482
|
-
* - Lists: depth-aware * and # notation
|
|
20483
|
-
* - Blockquote: {quote}...{quote}
|
|
20484
|
-
*/
|
|
20485
|
-
createConfluenceService() {
|
|
20486
|
-
const service = new import_turndown.default({
|
|
20487
|
-
headingStyle: "atx",
|
|
20488
|
-
hr: "---",
|
|
20489
|
-
bulletListMarker: "-",
|
|
20490
|
-
codeBlockStyle: "fenced",
|
|
20491
|
-
emDelimiter: "_"
|
|
20492
|
-
});
|
|
20493
|
-
this.addPdfPageMarkerRules(service);
|
|
20494
|
-
service.addRule("confluenceHeading", {
|
|
20495
|
-
filter: ["h1", "h2", "h3", "h4", "h5", "h6"],
|
|
20496
|
-
replacement: (content, node) => {
|
|
20497
|
-
const level = parseInt(node.nodeName[1], 10);
|
|
20498
|
-
return `
|
|
20499
|
-
|
|
20500
|
-
h${level}. ${content.trim()}
|
|
20501
|
-
|
|
20502
|
-
`;
|
|
20503
|
-
}
|
|
20504
|
-
});
|
|
20505
|
-
service.addRule("confluenceCodeBlock", {
|
|
20506
|
-
filter: "pre",
|
|
20507
|
-
replacement: (_content, node) => {
|
|
20508
|
-
const codeEl = node.querySelector("code");
|
|
20509
|
-
const langMatch = codeEl?.className?.match(/language-(\w+)/);
|
|
20510
|
-
const lang = langMatch ? langMatch[1] : "";
|
|
20511
|
-
const codeContent = codeEl?.textContent || node.textContent || "";
|
|
20512
|
-
const langAttr = lang ? `:language=${lang}` : "";
|
|
20513
|
-
return `
|
|
20514
|
-
|
|
20515
|
-
{code${langAttr}}
|
|
20516
|
-
${codeContent}
|
|
20517
|
-
{code}
|
|
20518
|
-
|
|
20519
|
-
`;
|
|
20520
|
-
}
|
|
20521
|
-
});
|
|
20522
|
-
service.addRule("confluenceInlineCode", {
|
|
20523
|
-
filter: (node) => node.nodeName === "CODE" && node.parentNode?.nodeName !== "PRE",
|
|
20524
|
-
replacement: (content) => content ? `{{${content.replace(/}+/g, (m) => m.split("").join(" "))}}}` : ""
|
|
20525
|
-
});
|
|
20526
|
-
service.addRule("confluenceBold", {
|
|
20527
|
-
filter: (node) => {
|
|
20528
|
-
if (node.nodeName === "STRONG" || node.nodeName === "B") return true;
|
|
20529
|
-
if (node.nodeName === "SPAN") {
|
|
20530
|
-
const style = node.getAttribute("style") || "";
|
|
20531
|
-
if (style.includes("font-weight: bold") || style.includes("font-weight:bold")) return true;
|
|
20532
|
-
const fwMatch = style.match(/font-weight:\s*(\d+)/);
|
|
20533
|
-
if (fwMatch && parseInt(fwMatch[1]) >= 600) return true;
|
|
20534
|
-
const ffMatch = style.match(/font-family:\s*['"]*([^'"]+)['"]*[;]?/);
|
|
20535
|
-
if (ffMatch && ffMatch[1].toLowerCase().includes("bold")) return true;
|
|
20536
|
-
}
|
|
20537
|
-
return false;
|
|
20538
|
-
},
|
|
20539
|
-
replacement: (content) => content.trim() ? `*${content}*` : content
|
|
20540
|
-
});
|
|
20541
|
-
service.addRule("confluenceItalic", {
|
|
20542
|
-
filter: (node) => {
|
|
20543
|
-
if (node.nodeName === "EM" || node.nodeName === "I") return true;
|
|
20544
|
-
if (node.nodeName === "SPAN") {
|
|
20545
|
-
const style = node.getAttribute("style") || "";
|
|
20546
|
-
if (style.includes("font-style: italic") || style.includes("font-style:italic")) return true;
|
|
20547
|
-
const ffMatch = style.match(/font-family:\s*['"]*([^'"]+)['"]*[;]?/);
|
|
20548
|
-
if (ffMatch && ffMatch[1].toLowerCase().includes("italic")) return true;
|
|
20549
|
-
}
|
|
20550
|
-
return false;
|
|
20551
|
-
},
|
|
20552
|
-
replacement: (content) => content.trim() ? `_${content}_` : content
|
|
20553
|
-
});
|
|
20554
|
-
service.addRule("confluenceUnderline", {
|
|
20555
|
-
filter: (node) => {
|
|
20556
|
-
if (node.nodeName === "U" || node.nodeName === "INS") return true;
|
|
20557
|
-
if (node.nodeName === "SPAN") {
|
|
20558
|
-
const style = node.getAttribute("style") || "";
|
|
20559
|
-
return style.includes("text-decoration: underline") || style.includes("text-decoration:underline");
|
|
20560
|
-
}
|
|
20561
|
-
return false;
|
|
20562
|
-
},
|
|
20563
|
-
replacement: (content) => content.trim() ? `+${content}+` : content
|
|
20564
|
-
});
|
|
20565
|
-
service.addRule("confluenceStrikethrough", {
|
|
20566
|
-
filter: (node) => {
|
|
20567
|
-
if (["DEL", "S", "STRIKE"].includes(node.nodeName)) return true;
|
|
20568
|
-
if (node.nodeName === "SPAN") {
|
|
20569
|
-
const style = node.getAttribute("style") || "";
|
|
20570
|
-
return style.includes("text-decoration: line-through") || style.includes("text-decoration:line-through") || style.includes("text-decoration-line: line-through") || style.includes("text-decoration-line:line-through");
|
|
20571
|
-
}
|
|
20572
|
-
return false;
|
|
20573
|
-
},
|
|
20574
|
-
replacement: (content) => content.trim() ? `-${content}-` : content
|
|
20575
|
-
});
|
|
20576
|
-
service.addRule("confluenceMonospaceCode", {
|
|
20577
|
-
filter: (node) => {
|
|
20578
|
-
if (node.nodeName !== "SPAN") return false;
|
|
20579
|
-
const style = node.getAttribute("style") || "";
|
|
20580
|
-
const fontFamily = style.toLowerCase();
|
|
20581
|
-
return fontFamily.includes("monospace") || fontFamily.includes("courier") || fontFamily.includes("consolas") || fontFamily.includes("monaco") || fontFamily.includes("menlo") || fontFamily.includes("source code") || fontFamily.includes("fira code") || fontFamily.includes("roboto mono") || fontFamily.includes("ubuntu mono");
|
|
20582
|
-
},
|
|
20583
|
-
replacement: (content) => content.trim() ? `{{${content.trim().replace(/}+/g, (m) => m.split("").join(" "))}}}` : content
|
|
20584
|
-
});
|
|
20585
|
-
service.addRule("confluenceLink", {
|
|
20586
|
-
filter: (node) => node.nodeName === "A" && !!node.getAttribute("href"),
|
|
20587
|
-
replacement: (content, node) => {
|
|
20588
|
-
const href = node.getAttribute("href") || "";
|
|
20589
|
-
const text = content.trim().replace(/\|/g, " ");
|
|
20590
|
-
if (!text || text === href) return `[${href}]`;
|
|
20591
|
-
return `[${text}|${href}]`;
|
|
20592
|
-
}
|
|
20593
|
-
});
|
|
20594
|
-
service.addRule("confluenceImage", {
|
|
20595
|
-
filter: "img",
|
|
20596
|
-
replacement: (_content, node) => {
|
|
20597
|
-
const src = node.getAttribute("src") || "";
|
|
20598
|
-
return src ? `!${src}!` : "";
|
|
20599
|
-
}
|
|
20600
|
-
});
|
|
20601
|
-
service.addRule("confluenceHr", {
|
|
20602
|
-
filter: "hr",
|
|
20603
|
-
replacement: () => "\n\n----\n\n"
|
|
20604
|
-
});
|
|
20605
|
-
service.addRule("confluenceBlockquote", {
|
|
20606
|
-
filter: "blockquote",
|
|
20607
|
-
replacement: (content) => `
|
|
20608
|
-
|
|
20609
|
-
{quote}
|
|
20610
|
-
${content.trim()}
|
|
20611
|
-
{quote}
|
|
20612
|
-
|
|
20613
|
-
`
|
|
20614
|
-
});
|
|
20615
|
-
service.addRule("confluenceGoogleDocsHeadings", {
|
|
20616
|
-
filter: (node) => {
|
|
20617
|
-
if (node.nodeName !== "P" && node.nodeName !== "DIV") return false;
|
|
20618
|
-
const style = node.getAttribute("style") || "";
|
|
20619
|
-
const fontSizeMatch = style.match(/font-size:\s*(\d+(?:\.\d+)?)px/);
|
|
20620
|
-
if (fontSizeMatch && parseFloat(fontSizeMatch[1]) > 16) return true;
|
|
20621
|
-
if (style.includes("font-weight: bold") || style.includes("font-weight:bold")) {
|
|
20622
|
-
const textContent = node.textContent || "";
|
|
20623
|
-
return textContent.length < 100 && textContent.trim().length > 0;
|
|
20624
|
-
}
|
|
20625
|
-
return false;
|
|
20626
|
-
},
|
|
20627
|
-
replacement: (content, node) => {
|
|
20628
|
-
const style = node?.getAttribute("style") || "";
|
|
20629
|
-
const fontSizeMatch = style.match(/font-size:\s*(\d+(?:\.\d+)?)px/);
|
|
20630
|
-
const trimmed = content.trim();
|
|
20631
|
-
if (!trimmed) return content;
|
|
20632
|
-
if (!fontSizeMatch) {
|
|
20633
|
-
return `
|
|
20634
|
-
|
|
20635
|
-
*${trimmed}*
|
|
20636
|
-
|
|
20637
|
-
`;
|
|
20638
|
-
}
|
|
20639
|
-
const fontSize = parseFloat(fontSizeMatch[1]);
|
|
20640
|
-
let level = 5;
|
|
20641
|
-
if (fontSize >= 28) level = 1;
|
|
20642
|
-
else if (fontSize >= 24) level = 2;
|
|
20643
|
-
else if (fontSize >= 20) level = 3;
|
|
20644
|
-
else if (fontSize >= 18) level = 4;
|
|
20645
|
-
return `
|
|
20646
|
-
|
|
20647
|
-
h${level}. ${trimmed}
|
|
20648
|
-
|
|
20649
|
-
`;
|
|
20650
|
-
}
|
|
20651
|
-
});
|
|
20652
|
-
service.addRule("confluenceList", {
|
|
20653
|
-
filter: ["ul", "ol"],
|
|
20654
|
-
replacement: (_content, node) => {
|
|
20655
|
-
return this.processConfluenceList(node, service, 1);
|
|
20656
|
-
}
|
|
20657
|
-
});
|
|
20658
|
-
service.addRule("confluenceTable", {
|
|
20659
|
-
filter: "table",
|
|
20660
|
-
replacement: (_content, node) => {
|
|
20661
|
-
const table = node;
|
|
20662
|
-
const rows = Array.from(table.querySelectorAll("tr"));
|
|
20663
|
-
if (rows.length === 0) return "";
|
|
20664
|
-
let result = "\n";
|
|
20665
|
-
rows.forEach((row) => {
|
|
20666
|
-
const cells = Array.from(row.querySelectorAll("th, td"));
|
|
20667
|
-
const isHeaderRow = cells.some((c) => c.nodeName === "TH");
|
|
20668
|
-
const delim = isHeaderRow ? "||" : "|";
|
|
20669
|
-
const cellTexts = cells.map((c) => (c.textContent || "").trim());
|
|
20670
|
-
result += delim + cellTexts.join(delim) + delim + "\n";
|
|
20671
|
-
});
|
|
20672
|
-
return result + "\n";
|
|
20673
|
-
}
|
|
20674
|
-
});
|
|
20675
|
-
return service;
|
|
20676
|
-
}
|
|
20677
|
-
/**
|
|
20678
|
-
* Process a Confluence list element into depth-aware wiki markup.
|
|
20679
|
-
* Uses * for unordered and # for ordered, repeated for nesting depth.
|
|
20680
|
-
*/
|
|
20681
|
-
processConfluenceList(list, service, depth) {
|
|
20682
|
-
const isOrdered = list.nodeName === "OL";
|
|
20683
|
-
const marker = (isOrdered ? "#" : "*").repeat(depth);
|
|
20684
|
-
const items = Array.from(list.children).filter((c) => c.nodeName === "LI");
|
|
20685
|
-
let result = depth === 1 ? "\n" : "";
|
|
20686
|
-
items.forEach((item) => {
|
|
20687
|
-
const itemContent = this.processConfluenceListItem(item, service, depth);
|
|
20688
|
-
result += `${marker} ${itemContent}
|
|
20689
|
-
`;
|
|
20690
|
-
});
|
|
20691
|
-
return depth === 1 ? result + "\n" : result;
|
|
20692
|
-
}
|
|
20693
|
-
processConfluenceListItem(item, service, depth) {
|
|
20694
|
-
let content = "";
|
|
20695
|
-
const TEXT_NODE = 3;
|
|
20696
|
-
const ELEMENT_NODE = 1;
|
|
20697
|
-
Array.from(item.childNodes).forEach((child) => {
|
|
20698
|
-
if (child.nodeType === TEXT_NODE) {
|
|
20699
|
-
content += child.textContent || "";
|
|
20700
|
-
} else if (child.nodeType === ELEMENT_NODE) {
|
|
20701
|
-
const el = child;
|
|
20702
|
-
if (el.nodeName === "UL" || el.nodeName === "OL") {
|
|
20703
|
-
content += "\n" + this.processConfluenceList(el, service, depth + 1);
|
|
20704
|
-
} else {
|
|
20705
|
-
content += service.turndown(el.outerHTML);
|
|
20706
|
-
}
|
|
20707
|
-
}
|
|
20708
|
-
});
|
|
20709
|
-
return content.trim();
|
|
20710
|
-
}
|
|
20711
20696
|
/**
|
|
20712
20697
|
* Convert HTML table to plain text for Discord code blocks
|
|
20713
20698
|
*/
|
|
@@ -21122,37 +21107,51 @@ async function loadLibrary(format) {
|
|
|
21122
21107
|
}
|
|
21123
21108
|
}
|
|
21124
21109
|
async function importMammoth() {
|
|
21125
|
-
|
|
21110
|
+
const mod = await import("mammoth");
|
|
21111
|
+
const modDefault = mod.default;
|
|
21112
|
+
if (mod && !mod.convertToHtml && modDefault && modDefault.convertToHtml) {
|
|
21113
|
+
return mod.default;
|
|
21114
|
+
}
|
|
21115
|
+
return mod;
|
|
21126
21116
|
}
|
|
21127
21117
|
async function importXlsx() {
|
|
21128
|
-
|
|
21118
|
+
const mod = await import("xlsx");
|
|
21119
|
+
const modDefault = mod.default;
|
|
21120
|
+
if (mod && !mod.read && modDefault && modDefault.read) {
|
|
21121
|
+
return mod.default;
|
|
21122
|
+
}
|
|
21123
|
+
return mod;
|
|
21129
21124
|
}
|
|
21130
21125
|
async function importPdfjs() {
|
|
21131
21126
|
if (typeof process !== "undefined") {
|
|
21132
|
-
const
|
|
21133
|
-
if (typeof
|
|
21134
|
-
let requireWorker;
|
|
21127
|
+
const p = process;
|
|
21128
|
+
if (typeof p.getBuiltinModule !== "function") {
|
|
21135
21129
|
try {
|
|
21136
|
-
const
|
|
21137
|
-
const
|
|
21138
|
-
|
|
21139
|
-
|
|
21140
|
-
|
|
21141
|
-
|
|
21142
|
-
|
|
21130
|
+
const mod2 = await import("node:module");
|
|
21131
|
+
const createRequire = mod2.createRequire;
|
|
21132
|
+
if (typeof createRequire === "function") {
|
|
21133
|
+
const req = createRequire(process.argv?.[1] || import.meta.url);
|
|
21134
|
+
p.getBuiltinModule = (name) => {
|
|
21135
|
+
try {
|
|
21136
|
+
return req(name);
|
|
21137
|
+
} catch {
|
|
21138
|
+
return void 0;
|
|
21139
|
+
}
|
|
21140
|
+
};
|
|
21141
|
+
} else {
|
|
21142
|
+
p.getBuiltinModule = () => void 0;
|
|
21143
21143
|
}
|
|
21144
21144
|
} catch {
|
|
21145
|
+
p.getBuiltinModule = () => void 0;
|
|
21145
21146
|
}
|
|
21146
|
-
proxyProcess.getBuiltinModule = (name) => {
|
|
21147
|
-
try {
|
|
21148
|
-
return requireWorker ? requireWorker(name) : void 0;
|
|
21149
|
-
} catch {
|
|
21150
|
-
return void 0;
|
|
21151
|
-
}
|
|
21152
|
-
};
|
|
21153
21147
|
}
|
|
21154
21148
|
}
|
|
21155
|
-
|
|
21149
|
+
const mod = await import("pdfjs-dist/legacy/build/pdf.mjs");
|
|
21150
|
+
const modDefault = mod.default;
|
|
21151
|
+
if (mod && !mod.getDocument && modDefault && modDefault.getDocument) {
|
|
21152
|
+
return mod.default;
|
|
21153
|
+
}
|
|
21154
|
+
return mod;
|
|
21156
21155
|
}
|
|
21157
21156
|
async function loadLibraryInternal(format) {
|
|
21158
21157
|
try {
|
|
@@ -21969,6 +21968,9 @@ function readConfig() {
|
|
|
21969
21968
|
return null;
|
|
21970
21969
|
}
|
|
21971
21970
|
}
|
|
21971
|
+
function getApiKey() {
|
|
21972
|
+
return process.env.FORMATFERRY_API_KEY || readConfig()?.apiKey;
|
|
21973
|
+
}
|
|
21972
21974
|
function writeConfig(config) {
|
|
21973
21975
|
mkdirSync(dirname(CONFIG_PATH), { recursive: true });
|
|
21974
21976
|
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), { mode: 384 });
|
|
@@ -22731,11 +22733,10 @@ async function convertCommand(options) {
|
|
|
22731
22733
|
process.stderr.write("Error: --url and --input are mutually exclusive. Use one or the other.\n");
|
|
22732
22734
|
process.exit(1);
|
|
22733
22735
|
}
|
|
22734
|
-
const
|
|
22735
|
-
const apiKey = config?.apiKey;
|
|
22736
|
+
const apiKey = getApiKey();
|
|
22736
22737
|
if (!apiKey) {
|
|
22737
22738
|
process.stderr.write(
|
|
22738
|
-
"Error: No SaaS API key configured. URL extraction requires a subscription.\nSet your API key with: formatferry auth --
|
|
22739
|
+
"Error: No SaaS API key configured. URL extraction requires a subscription.\nSet your API key with: formatferry auth --api-key <KEY>\nOr via the FORMATFERRY_API_KEY environment variable.\n"
|
|
22739
22740
|
);
|
|
22740
22741
|
process.exit(1);
|
|
22741
22742
|
}
|
|
@@ -23074,15 +23075,20 @@ async function handleLicenseKey(key) {
|
|
|
23074
23075
|
}
|
|
23075
23076
|
}
|
|
23076
23077
|
function handleStatus() {
|
|
23078
|
+
const envKey = process.env.FORMATFERRY_API_KEY;
|
|
23077
23079
|
const config = readConfig();
|
|
23078
|
-
if (
|
|
23080
|
+
if (envKey) {
|
|
23081
|
+
process.stdout.write(`Using API Key from FORMATFERRY_API_KEY environment variable.
|
|
23082
|
+
`);
|
|
23083
|
+
}
|
|
23084
|
+
if ((!config || !config.licenseKey && !config.apiKey) && !envKey) {
|
|
23079
23085
|
process.stdout.write(`Tier: free
|
|
23080
23086
|
`);
|
|
23081
23087
|
process.stdout.write(`No license key or API key configured.
|
|
23082
23088
|
`);
|
|
23083
23089
|
return;
|
|
23084
23090
|
}
|
|
23085
|
-
if (config
|
|
23091
|
+
if (config?.apiKey) {
|
|
23086
23092
|
const plan = config.plan ?? "free";
|
|
23087
23093
|
process.stdout.write(`Plan: ${formatPlanName(plan)}
|
|
23088
23094
|
`);
|
|
@@ -23104,9 +23110,14 @@ function handleStatus() {
|
|
|
23104
23110
|
}
|
|
23105
23111
|
return;
|
|
23106
23112
|
}
|
|
23107
|
-
|
|
23108
|
-
|
|
23109
|
-
|
|
23113
|
+
if (!config && envKey) {
|
|
23114
|
+
process.stdout.write(`Plan: API key configured via environment variable
|
|
23115
|
+
`);
|
|
23116
|
+
return;
|
|
23117
|
+
}
|
|
23118
|
+
const tier = config?.tier ?? "free";
|
|
23119
|
+
const features = config?.features ?? [];
|
|
23120
|
+
const expiresAt = config?.expiresAt ?? null;
|
|
23110
23121
|
process.stdout.write(`Tier: ${tier}
|
|
23111
23122
|
`);
|
|
23112
23123
|
if (expiresAt) {
|
|
@@ -23129,8 +23140,8 @@ import { readFileSync as readFileSync4 } from "fs";
|
|
|
23129
23140
|
import { resolve, dirname as dirname3 } from "path";
|
|
23130
23141
|
import { fileURLToPath } from "url";
|
|
23131
23142
|
var getVersion = () => {
|
|
23132
|
-
if ("1.0.
|
|
23133
|
-
return "1.0.
|
|
23143
|
+
if ("1.0.10") {
|
|
23144
|
+
return "1.0.10";
|
|
23134
23145
|
}
|
|
23135
23146
|
try {
|
|
23136
23147
|
const __dirname = dirname3(fileURLToPath(import.meta.url));
|