feat(import): add deterministic anchor nodeId helper

This commit is contained in:
Philipinho
2026-05-16 18:24:44 +01:00
parent bbb1b5eb26
commit d3bb105366
2 changed files with 60 additions and 0 deletions
@@ -0,0 +1,32 @@
import { nodeIdFromConfluenceAnchor } from './confluence-anchor-id';
describe('nodeIdFromConfluenceAnchor', () => {
it('is deterministic for the same (pageId, anchorName)', () => {
const a = nodeIdFromConfluenceAnchor('page-1', 'My Anchor');
const b = nodeIdFromConfluenceAnchor('page-1', 'My Anchor');
expect(a).toBe(b);
});
it('returns different ids when the anchor name differs', () => {
const a = nodeIdFromConfluenceAnchor('page-1', 'one');
const b = nodeIdFromConfluenceAnchor('page-1', 'two');
expect(a).not.toBe(b);
});
it('returns different ids when the pageId differs', () => {
const a = nodeIdFromConfluenceAnchor('page-1', 'same');
const b = nodeIdFromConfluenceAnchor('page-2', 'same');
expect(a).not.toBe(b);
});
it('returns exactly 12 lowercase a-z characters', () => {
const id = nodeIdFromConfluenceAnchor('page-xyz', 'Section · 1');
expect(id).toHaveLength(12);
expect(id).toMatch(/^[a-z]{12}$/);
});
it('treats an empty anchor name as a valid input', () => {
const id = nodeIdFromConfluenceAnchor('page-1', '');
expect(id).toMatch(/^[a-z]{12}$/);
});
});
@@ -0,0 +1,28 @@
import { createHash } from 'crypto';
// Matches the alphabet used by generateNodeId() in
// packages/editor-ext/src/lib/utils.ts (customAlphabet from nanoid).
const ALPHABET = 'abcdefghijklmnopqrstuvwxyz';
const NODE_ID_LENGTH = 12;
/**
* Returns a deterministic 12-character nodeId for a Confluence anchor.
* The same (pageId, anchorName) pair always produces the same result, so
* cross-page anchor links resolve to the anchor target without a
* precomputed map. The output uses the same alphabet and length as
* generateNodeId() from @docmost/editor-ext, so it is interchangeable
* with editor-generated nodeIds.
*/
export function nodeIdFromConfluenceAnchor(
pageId: string,
anchorName: string,
): string {
const digest = createHash('sha256')
.update(`${pageId}#${anchorName}`)
.digest();
let out = '';
for (let i = 0; i < NODE_ID_LENGTH; i++) {
out += ALPHABET[digest[i] % ALPHABET.length];
}
return out;
}