fix(review): address PR #230 review — payload type, breadcrumb helper, tests

Review follow-ups for the combined QA-UI fixes (#216/#206/#204/#218/#192):

- export/utils: correct the misleading getInternalLinkPageName comment — a
  bare `v1.2` loses its last dot-segment (`v1`); dots survive only in
  multi-segment names like `v1.2.md` -> `v1.2`.
- share: extract toPublicSharePayload(page, share): PublicSharePayload, an
  explicit allowlist type+mapper replacing the inline literal in the
  /shares/page-info anonymous path (#218). Add share.controller.spec.ts that
  stubs getSharedPage returning internal fields and asserts the response key
  set EXACTLY equals the whitelist (page + share), so any `...shareData`
  regression or new leaking field fails. Also key-tests the extracted mapper.
- breadcrumb: extract pure resolveBreadcrumbNodes(treeData, ancestors, pageId)
  (tree-hit -> tree; tree-miss -> map ancestors via canonical pageToTreeNode,
  dropping the as-any casts; else null) and unit-test all three branches.
- share-modal: RTL test asserting enabling a share calls mutateAsync with
  includeSubPages: false (#216 security default).
- share.service: one-line note at getSharedPage on the deferred consolidation
  of the ancestor-aware match into resolveReadableSharePage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
a
2026-06-27 20:09:48 +03:00
parent 2d36641f28
commit c9d252cf2a
9 changed files with 474 additions and 50 deletions

View File

@@ -109,8 +109,10 @@ export function getInternalLinkPageName(path: string, currentFilePath?: string):
// Strip a trailing file extension from the basename, but only when there IS
// one: an extensionless link target (e.g. "My Page") has no extension to drop,
// so `split('.').slice(0,-1)` would otherwise collapse it to an empty string,
// producing an internal link with no visible text (#204 export bug). Dotted
// page names without an extension (e.g. "v1.2") keep their dots.
// producing an internal link with no visible text (#204 export bug). The last
// dot-segment is always treated as an extension and dropped whenever there is
// more than one segment, so dots are preserved only in multi-segment names
// like `v1.2.md` -> `v1.2`; a bare `v1.2` becomes `v1`.
const base = path?.split('/').pop();
const parts = base?.split('.');
const name = parts && parts.length > 1 ? parts.slice(0, -1).join('.') : base;