mirror of
https://github.com/docmost/docmost.git
synced 2026-05-20 00:14:10 +08:00
feat(import): add deterministic anchor nodeId helper
This commit is contained in:
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user