confluence-md-sync 0.1.1 → 0.2.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.
- package/README.md +43 -0
- package/dist/bpmn/convert.d.ts +46 -0
- package/dist/bpmn/convert.js +109 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +3 -1
- package/dist/markdown/render.d.ts +6 -0
- package/dist/markdown/render.js +13 -0
- package/dist/publish/publish.d.ts +6 -0
- package/dist/publish/publish.js +31 -9
- package/package.json +14 -1
package/README.md
CHANGED
|
@@ -95,6 +95,49 @@ const { storage } = await publishPage({ pageId, markdownPath, dryRun: true }, cf
|
|
|
95
95
|
await publishPage({ pageId, markdown: '# Generated\n\ntext' }, cfg);
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
+
## BPMN diagrams out of the box
|
|
99
|
+
|
|
100
|
+
Pass a `.bpmn` file as an image — it is rendered to PNG at publish time
|
|
101
|
+
(headless Chromium via [bpmn-to-image](https://npmjs.com/package/bpmn-to-image),
|
|
102
|
+
an optional peer dependency):
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npm install -D bpmn-to-image
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
> bpmn-to-image pins puppeteer 21, whose bundled Chromium fails to launch on
|
|
109
|
+
> recent OSes ("socket hang up"). Force a current puppeteer in your
|
|
110
|
+
> `package.json`:
|
|
111
|
+
>
|
|
112
|
+
> ```json
|
|
113
|
+
> "overrides": { "puppeteer": "^24.0.0" }
|
|
114
|
+
> ```
|
|
115
|
+
|
|
116
|
+
```markdown
|
|
117
|
+
Процесс выпуска релиза:
|
|
118
|
+
|
|
119
|
+
{{img:release-flow.bpmn}}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
await publishPage({
|
|
124
|
+
pageId: '123456789',
|
|
125
|
+
markdownPath: 'docs/process.md',
|
|
126
|
+
images: ['docs/release-flow.bpmn'], // converted to release-flow.png automatically
|
|
127
|
+
bpmnOutDir: 'build', // optional; default: temp dir per run
|
|
128
|
+
}, cfg);
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Chromium PNG output is not byte-stable between runs, so dedup is pinned to
|
|
132
|
+
the SHA-256 of the *source* `.bpmn` via a `.src-sha256` sidecar — unchanged
|
|
133
|
+
diagrams never create new attachment versions. Batch pre-conversion is also
|
|
134
|
+
available:
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
import { convertBpmnFolder } from 'confluence-md-sync';
|
|
138
|
+
await convertBpmnFolder({ srcDir: 'docs/diagrams', outDir: 'build' });
|
|
139
|
+
```
|
|
140
|
+
|
|
98
141
|
## Read pages and tables
|
|
99
142
|
|
|
100
143
|
```ts
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BPMN → image conversion (out-of-the-box BPMN support).
|
|
3
|
+
*
|
|
4
|
+
* Тяжёлые зависимости (`bpmn-to-image` + puppeteer/Chromium) — опциональный
|
|
5
|
+
* peer dependency: импортируются динамически только когда конвертация
|
|
6
|
+
* реально запрошена. Ядро библиотеки остаётся лёгким.
|
|
7
|
+
*
|
|
8
|
+
* npm install -D bpmn-to-image
|
|
9
|
+
*/
|
|
10
|
+
export type BpmnImageFormat = 'png' | 'svg' | 'pdf';
|
|
11
|
+
export declare const BPMN_FILE_RE: RegExp;
|
|
12
|
+
export declare function isBpmnFile(path: string): boolean;
|
|
13
|
+
/** `p1.bpmn` → `p1.png` (имя выходного файла для формата). */
|
|
14
|
+
export declare function bpmnOutputName(file: string, format?: BpmnImageFormat): string;
|
|
15
|
+
export interface BpmnConversion {
|
|
16
|
+
input: string;
|
|
17
|
+
output: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Конвертирует BPMN-файлы в изображения по явному списку input → output.
|
|
21
|
+
*
|
|
22
|
+
* Рядом с каждым результатом пишется sidecar `<output>.src-sha256` с SHA-256
|
|
23
|
+
* исходного BPMN — AttachmentService использует его как dedup-tag вместо SHA
|
|
24
|
+
* результата (PNG от Chromium байт-нестабилен между запусками, BPMN —
|
|
25
|
+
* стабилен). Так Confluence не плодит лишние версии аттача, когда диаграмма
|
|
26
|
+
* не менялась.
|
|
27
|
+
*/
|
|
28
|
+
export declare function convertBpmn(conversions: BpmnConversion[]): Promise<void>;
|
|
29
|
+
export interface ConvertBpmnFolderOptions {
|
|
30
|
+
/** Папка-источник с `*.bpmn`. */
|
|
31
|
+
srcDir: string;
|
|
32
|
+
/** Папка-приёмник для сгенерированных изображений. Будет создана, если её нет. */
|
|
33
|
+
outDir: string;
|
|
34
|
+
/** Формат выхода. Дефолт — `png`. */
|
|
35
|
+
format?: BpmnImageFormat;
|
|
36
|
+
/** Регулярка для фильтра входов. Дефолт — `/\.bpmn$/i`. */
|
|
37
|
+
pattern?: RegExp;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Конвертирует все BPMN-файлы из `srcDir` в изображения в `outDir`.
|
|
41
|
+
* Файлы получают то же базовое имя с расширением формата (`p1.bpmn` → `p1.png`).
|
|
42
|
+
*
|
|
43
|
+
* Сгенерированные файлы — артефакты сборки: коммитить их не нужно.
|
|
44
|
+
* В Confluence они улетают как аттачи на этапе публикации.
|
|
45
|
+
*/
|
|
46
|
+
export declare function convertBpmnFolder(opts: ConvertBpmnFolderOptions): Promise<BpmnConversion[]>;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BPMN → image conversion (out-of-the-box BPMN support).
|
|
3
|
+
*
|
|
4
|
+
* Тяжёлые зависимости (`bpmn-to-image` + puppeteer/Chromium) — опциональный
|
|
5
|
+
* peer dependency: импортируются динамически только когда конвертация
|
|
6
|
+
* реально запрошена. Ядро библиотеки остаётся лёгким.
|
|
7
|
+
*
|
|
8
|
+
* npm install -D bpmn-to-image
|
|
9
|
+
*/
|
|
10
|
+
import { createHash } from 'node:crypto';
|
|
11
|
+
import { mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
12
|
+
import { basename, dirname, extname, join } from 'node:path';
|
|
13
|
+
import { SRC_SHA_SIDECAR_SUFFIX } from '../attachments/attachment.js';
|
|
14
|
+
export const BPMN_FILE_RE = /\.bpmn$/i;
|
|
15
|
+
export function isBpmnFile(path) {
|
|
16
|
+
return BPMN_FILE_RE.test(path);
|
|
17
|
+
}
|
|
18
|
+
/** `p1.bpmn` → `p1.png` (имя выходного файла для формата). */
|
|
19
|
+
export function bpmnOutputName(file, format = 'png') {
|
|
20
|
+
return `${basename(file, extname(file))}.${format}`;
|
|
21
|
+
}
|
|
22
|
+
let puppeteerPatched = false;
|
|
23
|
+
/**
|
|
24
|
+
* bpmn-to-image вызывает puppeteer.launch() без флагов. В CI Chromium
|
|
25
|
+
* запускается из-под root и без --no-sandbox просто не стартует. Патчим
|
|
26
|
+
* глобальный puppeteer.launch один раз — best-effort: если puppeteer
|
|
27
|
+
* не резолвится из нашего контекста, bpmn-to-image разберётся сам.
|
|
28
|
+
*/
|
|
29
|
+
async function patchPuppeteerForCI() {
|
|
30
|
+
if (puppeteerPatched)
|
|
31
|
+
return;
|
|
32
|
+
puppeteerPatched = true;
|
|
33
|
+
try {
|
|
34
|
+
const { default: puppeteer } = await import('puppeteer');
|
|
35
|
+
const origLaunch = puppeteer.launch.bind(puppeteer);
|
|
36
|
+
puppeteer.launch = ((options = {}) => {
|
|
37
|
+
const existing = options?.args ?? [];
|
|
38
|
+
const patched = [
|
|
39
|
+
...existing,
|
|
40
|
+
'--no-sandbox',
|
|
41
|
+
'--disable-setuid-sandbox',
|
|
42
|
+
'--disable-dev-shm-usage',
|
|
43
|
+
];
|
|
44
|
+
return origLaunch({ ...options, args: patched });
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// puppeteer не установлен отдельно — положимся на bpmn-to-image
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async function loadConverter() {
|
|
52
|
+
try {
|
|
53
|
+
const mod = await import('bpmn-to-image');
|
|
54
|
+
await patchPuppeteerForCI();
|
|
55
|
+
return mod;
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
if (err.code === 'ERR_MODULE_NOT_FOUND') {
|
|
59
|
+
throw new Error("BPMN conversion requires the optional peer dependency 'bpmn-to-image'. " +
|
|
60
|
+
'Install it with: npm install -D bpmn-to-image');
|
|
61
|
+
}
|
|
62
|
+
throw err;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Конвертирует BPMN-файлы в изображения по явному списку input → output.
|
|
67
|
+
*
|
|
68
|
+
* Рядом с каждым результатом пишется sidecar `<output>.src-sha256` с SHA-256
|
|
69
|
+
* исходного BPMN — AttachmentService использует его как dedup-tag вместо SHA
|
|
70
|
+
* результата (PNG от Chromium байт-нестабилен между запусками, BPMN —
|
|
71
|
+
* стабилен). Так Confluence не плодит лишние версии аттача, когда диаграмма
|
|
72
|
+
* не менялась.
|
|
73
|
+
*/
|
|
74
|
+
export async function convertBpmn(conversions) {
|
|
75
|
+
if (conversions.length === 0)
|
|
76
|
+
return;
|
|
77
|
+
const { convertAll } = await loadConverter();
|
|
78
|
+
for (const c of conversions) {
|
|
79
|
+
mkdirSync(dirname(c.output), { recursive: true });
|
|
80
|
+
}
|
|
81
|
+
console.log(`[bpmn] converting ${conversions.length} diagram(s)`);
|
|
82
|
+
await convertAll(conversions.map((c) => ({ input: c.input, outputs: [c.output] })));
|
|
83
|
+
for (const c of conversions) {
|
|
84
|
+
const sha = createHash('sha256').update(readFileSync(c.input)).digest('hex');
|
|
85
|
+
writeFileSync(c.output + SRC_SHA_SIDECAR_SUFFIX, sha);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Конвертирует все BPMN-файлы из `srcDir` в изображения в `outDir`.
|
|
90
|
+
* Файлы получают то же базовое имя с расширением формата (`p1.bpmn` → `p1.png`).
|
|
91
|
+
*
|
|
92
|
+
* Сгенерированные файлы — артефакты сборки: коммитить их не нужно.
|
|
93
|
+
* В Confluence они улетают как аттачи на этапе публикации.
|
|
94
|
+
*/
|
|
95
|
+
export async function convertBpmnFolder(opts) {
|
|
96
|
+
const format = opts.format ?? 'png';
|
|
97
|
+
const pattern = opts.pattern ?? BPMN_FILE_RE;
|
|
98
|
+
const files = readdirSync(opts.srcDir).filter((f) => pattern.test(f));
|
|
99
|
+
if (files.length === 0) {
|
|
100
|
+
console.log(`[bpmn] no files matching ${pattern} in ${opts.srcDir}`);
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
const conversions = files.map((f) => ({
|
|
104
|
+
input: join(opts.srcDir, f),
|
|
105
|
+
output: join(opts.outDir, bpmnOutputName(f, format)),
|
|
106
|
+
}));
|
|
107
|
+
await convertBpmn(conversions);
|
|
108
|
+
return conversions;
|
|
109
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export { ConfluenceClient, ConfluenceApiError, type ConfluencePage, type ConfluencePageStorage, type ConfluenceAttachment, type ConfluenceLabel, type AttachmentVersionData, type CreatePageOptions, } from './client/client.js';
|
|
2
2
|
export { authHeader, loadConfigFromEnv, type ConfluenceAuthType, type ConfluenceConfig, type LoadConfigOptions, } from './client/config.js';
|
|
3
3
|
export { Markdown } from './markdown/markdown.js';
|
|
4
|
-
export { renderToStorage, extractPlaceholders, MissingAttachmentUrlError, PLACEHOLDER_RE, type AttachmentUrls, type ExtractedPlaceholders, } from './markdown/render.js';
|
|
4
|
+
export { renderToStorage, extractPlaceholders, renameImagePlaceholders, MissingAttachmentUrlError, PLACEHOLDER_RE, type AttachmentUrls, type ExtractedPlaceholders, } from './markdown/render.js';
|
|
5
5
|
export { validateMarkdown, MarkdownValidationError, type ValidateOptions } from './markdown/validate.js';
|
|
6
6
|
export * from './macros/index.js';
|
|
7
|
+
export { convertBpmn, convertBpmnFolder, isBpmnFile, bpmnOutputName, BPMN_FILE_RE, type BpmnConversion, type BpmnImageFormat, type ConvertBpmnFolderOptions, } from './bpmn/convert.js';
|
|
7
8
|
export { fileSha256, HASH_TAG_PREFIX } from './attachments/hash.js';
|
|
8
9
|
export { Attachment, AttachmentService, toAttachmentVersion, SRC_SHA_SIDECAR_SUFFIX, type AttachmentVersion, type EnsuredAttachment, } from './attachments/attachment.js';
|
|
9
10
|
export { Page, Table } from './pages/page.js';
|
package/dist/index.js
CHANGED
|
@@ -3,10 +3,12 @@ export { ConfluenceClient, ConfluenceApiError, } from './client/client.js';
|
|
|
3
3
|
export { authHeader, loadConfigFromEnv, } from './client/config.js';
|
|
4
4
|
// Markdown
|
|
5
5
|
export { Markdown } from './markdown/markdown.js';
|
|
6
|
-
export { renderToStorage, extractPlaceholders, MissingAttachmentUrlError, PLACEHOLDER_RE, } from './markdown/render.js';
|
|
6
|
+
export { renderToStorage, extractPlaceholders, renameImagePlaceholders, MissingAttachmentUrlError, PLACEHOLDER_RE, } from './markdown/render.js';
|
|
7
7
|
export { validateMarkdown, MarkdownValidationError } from './markdown/validate.js';
|
|
8
8
|
// Macros (pluggable)
|
|
9
9
|
export * from './macros/index.js';
|
|
10
|
+
// BPMN (optional peer dep 'bpmn-to-image' is loaded lazily on use)
|
|
11
|
+
export { convertBpmn, convertBpmnFolder, isBpmnFile, bpmnOutputName, BPMN_FILE_RE, } from './bpmn/convert.js';
|
|
10
12
|
// Attachments
|
|
11
13
|
export { fileSha256, HASH_TAG_PREFIX } from './attachments/hash.js';
|
|
12
14
|
export { Attachment, AttachmentService, toAttachmentVersion, SRC_SHA_SIDECAR_SUFFIX, } from './attachments/attachment.js';
|
|
@@ -4,6 +4,12 @@ export interface ExtractedPlaceholders {
|
|
|
4
4
|
files: string[];
|
|
5
5
|
}
|
|
6
6
|
export declare function extractPlaceholders(markdown: string): ExtractedPlaceholders;
|
|
7
|
+
/**
|
|
8
|
+
* Переименовывает `{{img:...}}`-плейсхолдеры по карте старое→новое имя.
|
|
9
|
+
* Используется BPMN-конвейером: автор пишет `{{img:p1.bpmn}}`, публикация
|
|
10
|
+
* подменяет на `{{img:p1.png}}` после конвертации диаграммы.
|
|
11
|
+
*/
|
|
12
|
+
export declare function renameImagePlaceholders(markdown: string, renames: Map<string, string>): string;
|
|
7
13
|
export interface AttachmentUrls {
|
|
8
14
|
/** filename → абсолютный URL аттача, полученный из Confluence после аплоада. */
|
|
9
15
|
images: Map<string, string>;
|
package/dist/markdown/render.js
CHANGED
|
@@ -34,6 +34,19 @@ function escapeXmlAttr(s) {
|
|
|
34
34
|
}
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Переименовывает `{{img:...}}`-плейсхолдеры по карте старое→новое имя.
|
|
39
|
+
* Используется BPMN-конвейером: автор пишет `{{img:p1.bpmn}}`, публикация
|
|
40
|
+
* подменяет на `{{img:p1.png}}` после конвертации диаграммы.
|
|
41
|
+
*/
|
|
42
|
+
export function renameImagePlaceholders(markdown, renames) {
|
|
43
|
+
if (renames.size === 0)
|
|
44
|
+
return markdown;
|
|
45
|
+
return markdown.replace(/\{\{img:([^}]+)\}\}/g, (full, rawName) => {
|
|
46
|
+
const renamed = renames.get(rawName.trim());
|
|
47
|
+
return renamed ? `{{img:${renamed}}}` : full;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
37
50
|
export class MissingAttachmentUrlError extends Error {
|
|
38
51
|
constructor(message) {
|
|
39
52
|
super(message);
|
|
@@ -37,6 +37,12 @@ export interface PublishPageOptions {
|
|
|
37
37
|
versionMessage?: string;
|
|
38
38
|
/** Свой реестр макросов (default: встроенные core + table-filter). */
|
|
39
39
|
registry?: MacroRegistry;
|
|
40
|
+
/**
|
|
41
|
+
* Каталог для PNG, сгенерированных из `*.bpmn` в images[].
|
|
42
|
+
* Default: временный каталог на каждый запуск — дедуп аттачей всё равно
|
|
43
|
+
* идёт по SHA-256 исходного BPMN через sidecar, лишних версий не будет.
|
|
44
|
+
*/
|
|
45
|
+
bpmnOutDir?: string;
|
|
40
46
|
/**
|
|
41
47
|
* Ключ content property для хранения content-hash.
|
|
42
48
|
* Default: 'confluence-md-sync-content-hash'.
|
package/dist/publish/publish.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
1
|
+
import { mkdtempSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { basename, join } from 'node:path';
|
|
3
5
|
import { ConfluenceClient } from '../client/client.js';
|
|
4
6
|
import { AttachmentService } from '../attachments/attachment.js';
|
|
5
|
-
import {
|
|
7
|
+
import { bpmnOutputName, convertBpmn, isBpmnFile } from '../bpmn/convert.js';
|
|
8
|
+
import { renameImagePlaceholders, renderToStorage } from '../markdown/render.js';
|
|
6
9
|
import { validateMarkdown } from '../markdown/validate.js';
|
|
7
10
|
import { processMacros } from '../macros/registry.js';
|
|
8
11
|
import { defaultMacroRegistry } from '../macros/index.js';
|
|
@@ -60,11 +63,29 @@ export async function publishPage(opts, cfg) {
|
|
|
60
63
|
else {
|
|
61
64
|
throw new Error('publishPage: either markdownPath or markdown must be provided');
|
|
62
65
|
}
|
|
63
|
-
|
|
66
|
+
let images = opts.images ?? [];
|
|
64
67
|
const files = opts.files ?? [];
|
|
65
68
|
const tables = opts.tables ?? [];
|
|
66
69
|
const registry = opts.registry ?? defaultMacroRegistry;
|
|
67
70
|
const hashKey = opts.hashPropertyKey ?? DEFAULT_HASH_PROPERTY_KEY;
|
|
71
|
+
// 0. BPMN из коробки: *.bpmn в images[] конвертируются в PNG, а
|
|
72
|
+
// {{img:p1.bpmn}} в markdown подменяется на {{img:p1.png}} ДО валидации.
|
|
73
|
+
// Сама конвертация (puppeteer) — после валидации, чтобы падать быстро.
|
|
74
|
+
const bpmnConversions = [];
|
|
75
|
+
const bpmnInputs = images.filter(isBpmnFile);
|
|
76
|
+
if (bpmnInputs.length > 0) {
|
|
77
|
+
const outDir = opts.bpmnOutDir ?? mkdtempSync(join(tmpdir(), 'confluence-md-sync-bpmn-'));
|
|
78
|
+
const renames = new Map();
|
|
79
|
+
const outputByInput = new Map();
|
|
80
|
+
for (const input of bpmnInputs) {
|
|
81
|
+
const output = join(outDir, bpmnOutputName(input));
|
|
82
|
+
bpmnConversions.push({ input, output });
|
|
83
|
+
outputByInput.set(input, output);
|
|
84
|
+
renames.set(basename(input), basename(output));
|
|
85
|
+
}
|
|
86
|
+
images = images.map((p) => outputByInput.get(p) ?? p);
|
|
87
|
+
markdown = renameImagePlaceholders(markdown, renames);
|
|
88
|
+
}
|
|
68
89
|
validateMarkdown({
|
|
69
90
|
markdown,
|
|
70
91
|
imagePaths: images,
|
|
@@ -77,6 +98,11 @@ export async function publishPage(opts, cfg) {
|
|
|
77
98
|
const tableMarkdown = table.markdown instanceof Markdown ? table.markdown.toString() : table.markdown;
|
|
78
99
|
markdown = markdown.replace(new RegExp(`\\{\\{table:${escapeRegex(table.name)}\\}\\}`, 'g'), () => tableMarkdown);
|
|
79
100
|
}
|
|
101
|
+
// Конвертация диаграмм — до аплоада; в dry-run пропускается (аттачи всё
|
|
102
|
+
// равно не загружаются, URL подставляются фиктивные).
|
|
103
|
+
if (bpmnConversions.length > 0 && !opts.dryRun) {
|
|
104
|
+
await convertBpmn(bpmnConversions);
|
|
105
|
+
}
|
|
80
106
|
const client = new ConfluenceClient(cfg);
|
|
81
107
|
const attachmentSvc = new AttachmentService(client);
|
|
82
108
|
const { pageId, created } = await resolvePage(client, opts);
|
|
@@ -89,9 +115,9 @@ export async function publishPage(opts, cfg) {
|
|
|
89
115
|
// В dry-run не трогаем Confluence — подставляем фиктивные URL, чтобы
|
|
90
116
|
// рендер и валидация плейсхолдеров отработали полностью.
|
|
91
117
|
for (const p of images)
|
|
92
|
-
urls.images.set(
|
|
118
|
+
urls.images.set(basename(p), `dry-run://img/${basename(p)}`);
|
|
93
119
|
for (const p of files)
|
|
94
|
-
urls.files.set(
|
|
120
|
+
urls.files.set(basename(p), `dry-run://file/${basename(p)}`);
|
|
95
121
|
}
|
|
96
122
|
else {
|
|
97
123
|
for (const p of images) {
|
|
@@ -155,10 +181,6 @@ export async function publishPage(opts, cfg) {
|
|
|
155
181
|
console.log(`[publish] ${pageId} "${title}" → v${nextVersion} (hash ${newHash.slice(0, 12)})`);
|
|
156
182
|
return { pageId, title, version: nextVersion, attachments, updated: true, created, storage };
|
|
157
183
|
}
|
|
158
|
-
function basenameOf(p) {
|
|
159
|
-
const idx = Math.max(p.lastIndexOf('/'), p.lastIndexOf('\\'));
|
|
160
|
-
return idx === -1 ? p : p.slice(idx + 1);
|
|
161
|
-
}
|
|
162
184
|
function escapeRegex(s) {
|
|
163
185
|
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
164
186
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "confluence-md-sync",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Publish Markdown to Confluence (Data Center & Cloud): idempotent page sync, attachment dedup, tables and a pluggable macro system",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"confluence",
|
|
@@ -63,7 +63,20 @@
|
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"@types/markdown-it": "^14.1.2",
|
|
65
65
|
"@types/node": "^20.14.0",
|
|
66
|
+
"bpmn-to-image": "^0.7.0",
|
|
67
|
+
"puppeteer": "^24.43.1",
|
|
66
68
|
"typescript": "^5.6.0",
|
|
67
69
|
"vitest": "^3.0.0"
|
|
70
|
+
},
|
|
71
|
+
"peerDependencies": {
|
|
72
|
+
"bpmn-to-image": ">=0.7.0"
|
|
73
|
+
},
|
|
74
|
+
"peerDependenciesMeta": {
|
|
75
|
+
"bpmn-to-image": {
|
|
76
|
+
"optional": true
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"overrides": {
|
|
80
|
+
"puppeteer": "$puppeteer"
|
|
68
81
|
}
|
|
69
82
|
}
|