confluence-md-sync 0.8.8 → 0.8.9

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.
@@ -1,6 +1,6 @@
1
1
  import { Markdown } from '../markdown/markdown.js';
2
2
  import { unescapeParamValue } from './builder.js';
3
- import { generateMacroId, structuredMacro } from './xml.js';
3
+ import { stableMacroId, structuredMacro } from './xml.js';
4
4
  /**
5
5
  * Реестр макросов. Пустой при создании — наполняется плагинами через
6
6
  * {@link use} или точечной регистрацией через {@link register}.
@@ -126,7 +126,9 @@ function processMacroType(storage, macroName, renderer) {
126
126
  }
127
127
  const bodyStart = startIdx + match[0].length;
128
128
  const body = storage.substring(bodyStart, endIdx).trim();
129
- const macroId = generateMacroId();
129
+ // Стабильный id от содержимого маркера — иначе он менялся бы на КАЖДЫЙ
130
+ // рендер (см. stableMacroId), ломая content-hash идемпотентность publish.
131
+ const macroId = stableMacroId(`${macroName}\u0000${paramStr}\u0000${body}`);
130
132
  const macroXhtml = renderer({ params, body, macroId });
131
133
  const before = storage.substring(0, startIdx);
132
134
  const after = storage.substring(endIdx + endMarker.length);
@@ -2,8 +2,21 @@
2
2
  import type { MacroParam } from './types.js';
3
3
  /** Экранирует строку для XML-атрибутов и текстовых узлов. */
4
4
  export declare function escapeXmlAttr(s: string): string;
5
- /** Генерирует UUID для ac:macro-id. */
5
+ /** Генерирует случайный UUID для ac:macro-id (когда стабильность не нужна). */
6
6
  export declare function generateMacroId(): string;
7
+ /**
8
+ * Детерминированный macro-id (UUID-формы) от seed — как правило, от исходного
9
+ * содержимого маркера (имя+параметры+тело). ВАЖНО для идемпотентности
10
+ * content-hash: если id генерировать случайно на каждый рендер (см.
11
+ * {@link generateMacroId}), storage у страницы с любым макросом отличается
12
+ * при каждой публикации, даже когда исходный markdown не менялся — hash
13
+ * никогда не совпадает, и publish принудительно обновляет страницу заново
14
+ * при каждом прогоне (замечено на проде: 1 изменённый файл → 250+ страниц
15
+ * «обновились», потому что ~250 из них содержат макрос details/properties).
16
+ * Стабильный id по содержимому убирает этот псевдо-дрейф: одинаковый маркер
17
+ * даёт одинаковый storage → hash совпадает → UNCHANGED.
18
+ */
19
+ export declare function stableMacroId(seed: string): string;
7
20
  export interface StructuredMacroOptions {
8
21
  /**
9
22
  * Macro parameters. `undefined` values are skipped. A key rendered as
@@ -1,4 +1,5 @@
1
1
  /** XML helpers shared by macro renderers. */
2
+ import { createHash } from 'node:crypto';
2
3
  /** Экранирует строку для XML-атрибутов и текстовых узлов. */
3
4
  export function escapeXmlAttr(s) {
4
5
  return s.replace(/[<>&"']/g, (c) => {
@@ -12,7 +13,7 @@ export function escapeXmlAttr(s) {
12
13
  }
13
14
  });
14
15
  }
15
- /** Генерирует UUID для ac:macro-id. */
16
+ /** Генерирует случайный UUID для ac:macro-id (когда стабильность не нужна). */
16
17
  export function generateMacroId() {
17
18
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
18
19
  const r = (Math.random() * 16) | 0;
@@ -20,6 +21,24 @@ export function generateMacroId() {
20
21
  return v.toString(16);
21
22
  });
22
23
  }
24
+ /**
25
+ * Детерминированный macro-id (UUID-формы) от seed — как правило, от исходного
26
+ * содержимого маркера (имя+параметры+тело). ВАЖНО для идемпотентности
27
+ * content-hash: если id генерировать случайно на каждый рендер (см.
28
+ * {@link generateMacroId}), storage у страницы с любым макросом отличается
29
+ * при каждой публикации, даже когда исходный markdown не менялся — hash
30
+ * никогда не совпадает, и publish принудительно обновляет страницу заново
31
+ * при каждом прогоне (замечено на проде: 1 изменённый файл → 250+ страниц
32
+ * «обновились», потому что ~250 из них содержат макрос details/properties).
33
+ * Стабильный id по содержимому убирает этот псевдо-дрейф: одинаковый маркер
34
+ * даёт одинаковый storage → hash совпадает → UNCHANGED.
35
+ */
36
+ export function stableMacroId(seed) {
37
+ const hex = createHash('sha256').update(seed, 'utf-8').digest('hex');
38
+ const bytes = hex.slice(0, 32);
39
+ return (`${bytes.slice(0, 8)}-${bytes.slice(8, 12)}-4${bytes.slice(13, 16)}-` +
40
+ `${'89ab'[parseInt(bytes[16], 16) % 4]}${bytes.slice(17, 20)}-${bytes.slice(20, 32)}`);
41
+ }
23
42
  /**
24
43
  * Assembles an `<ac:structured-macro>` element. Takes care of parameter
25
44
  * escaping, rich vs plain bodies and CDATA safety.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "confluence-md-sync",
3
- "version": "0.8.8",
3
+ "version": "0.8.9",
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",