Skip to content

fix: prevent same-title page rewrites by matching targetId (issue #737)#738

Merged
otomatty merged 5 commits into
developfrom
claude/fix-issue-737-JHtkq
Apr 25, 2026
Merged

fix: prevent same-title page rewrites by matching targetId (issue #737)#738
otomatty merged 5 commits into
developfrom
claude/fix-issue-737-JHtkq

Conversation

@otomatty
Copy link
Copy Markdown
Owner

@otomatty otomatty commented Apr 25, 2026

概要

リネーム伝播時に同名ページが複数存在する場合、誤ったページのリンクまで書き換わってしまう問題(issue #737)を修正します。WikiLink と tag マークに targetId 属性を追加し、ID 一致による厳密なマッチングを実装しました。既存データとの後方互換性を保つため、targetId を持たないマークはタイトル文字列でのフォールバック書き換えを継続します。

変更点

コア実装

  • ydocRenameRewrite.ts: マッチング優先順位を実装

  • titleRenamePropagationService.ts: renamedPageIdrewriteTitleRefsInDoc に渡すように修正

クライアント側マーク属性

  • WikiLinkExtension.ts: targetId 属性を追加(既定値 null、HTML ラウンドトリップ対応)
  • TagExtension.ts: targetId 属性を追加(既定値 null、HTML ラウンドトリップ対応)

属性値の埋め込み

  • wikiLinkUtils.ts: updateWikiLinkAttributespageTitleToId マップを追加
  • tagUtils.ts: updateTagAttributespageTitleToId マップを追加
  • useWikiLinkStatusSync.ts: collectWikiLinkUpdatestargetId を埋め込み
  • usePageQueries.ts: checkExistence の戻り値に pageTitleToId マップを追加
  • useBubbleMenuWikiLink.ts: バブルメニューから WikiLink 変換時に targetId を埋め込み

共有定数モジュール

  • packages/shared/: 新規作成

    • tagCharacterClass.ts: タグ名の許容文字集合を定義(クライアント・サーバ間で同期)
    • tagCharacterClass.test.ts: 文字集合の仕様テスト
    • index.ts, package.json, tsconfig.json, vitest.config.ts: モジュール基盤
  • src/lib/tagCharacterClassSync.test.ts: CI で @zedi/shared とサーバ側の文字クラスが一致することを検証

テスト

  • ydocRenameRewrite.test.ts: 新規テストスイート「targetId-based matching (issue タイトル重複時の rename 伝播曖昧性とタグ文字クラス共有 (Phase 2 follow-up) #737)」

    • ID 一致による書き換え
    • 同名ページの誤書き換え防止
    • targetId 無しマークのフォールバック
    • 空文字 targetId の扱い
    • タグマークの ID 一致ルール
    • renamedPageId 省略時の安全な動作
  • titleRenamePropagationService.test.ts: 同名リンク 2 つのシナリオテスト

  • **`WikiLinkExt

https://claude.ai/code/session_01Mc7saJM6qTX4XZx3FzwvuN


Open in Devin Review

Summary by CodeRabbit

  • New Features

    • Wiki links and tags now carry resolved target IDs so links stay tied to specific pages and preserve resolution state.
    • Tag validation rules centralized for consistent parsing and acceptance across editor features.
  • Bug Fixes

    • Title renames now only update links that actually point to the renamed page, preventing unintended bulk renames.
  • Tests

    • Added tests and CI checks to ensure target-ID syncing, tag validation, and rename behaviors remain consistent.

claude added 2 commits April 25, 2026 03:00
)

Implements the two follow-ups tracked by issue #737:

1. Same-title rename ambiguity (approach A — `targetId` on marks)
   - Add `targetId` attribute to `wikiLink` and `tag` Tiptap marks.
   - Populate it during link resolution (`useWikiLinkStatusSync`,
     `useTagStatusSync`, `useBubbleMenuWikiLink`); `useWikiLinkExistsChecker`
     now returns a `pageTitleToId` map.
   - Switch `rewriteTitleRefsInDoc` to id-strict matching when a mark carries
     `targetId`; id-less marks still fall back to title matching for lazy
     migration of pre-existing Y.Docs.
   - Plumb `renamedPageId` through `propagateTitleRename` →
     `rewriteSourcePage` → `rewriteTitleRefsInDoc`.

2. Shared tag character class
   - New `@zedi/shared` workspace package owns
     `TAG_NAME_CHAR_CLASS` (single source of truth for client + admin).
   - Client `TagExtension` builds its regex from the shared constant.
   - `server/api` (intentionally outside the workspace, see AGENTS.md)
     keeps a duplicated `TAG_NAME_CHAR_CLASS_STRING`; a new client-side
     drift-check vitest (`src/lib/tagCharacterClassSync.test.ts`) reads the
     server file and asserts both literals match in CI.
   - AGENTS.md documents the cross-workspace sharing pattern and the
     server-side drift-check workaround.
The previous commit's lint-staged auto-fix added blank `/**\n *\n */`
stubs to every variable declaration in `useBubbleMenuWikiLink.ts` and to
`WikiLinkInfo` in `wikiLinkUtils.ts`. Replace those with real JSDoc
descriptions on the public exports so `jsdoc/require-jsdoc` is satisfied
without the stub noise polluting the file body.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 25, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ef3cbf75-94d4-4498-9340-88ce8104e160

📥 Commits

Reviewing files that changed from the base of the PR and between a8f8560 and 83d21c5.

📒 Files selected for processing (2)
  • server/api/src/__tests__/services/ydocRenameRewrite.test.ts
  • server/api/src/services/ydocRenameRewrite.ts

📝 Walkthrough

Walkthrough

Adds a new workspace package @zedi/shared exporting TAG_NAME_CHAR_CLASS; introduces targetId attribute plumbing for WikiLink and Tag marks across client and server; makes server rename rewriting targetId-aware; and adds tests plus a CI drift-check to keep the character-class constant synchronized.

Changes

Cohort / File(s) Summary
Shared package & infra
packages/shared/package.json, packages/shared/tsconfig.json, packages/shared/vitest.config.ts, packages/shared/src/index.ts, packages/shared/src/tagCharacterClass.ts, packages/shared/src/tagCharacterClass.test.ts, package.json, AGENTS.md
New @zedi/shared workspace package exporting TAG_NAME_CHAR_CLASS; TS and Vitest configs added; root test script updated to run shared tests; docs describe cross-boundary constants and a server/client sync procedure.
Server rename & sync
server/api/src/services/ydocRenameRewrite.ts, server/api/src/services/titleRenamePropagationService.ts, server/api/src/__tests__/services/ydocRenameRewrite.test.ts, server/api/src/__tests__/services/titleRenamePropagationService.test.ts
Rewrite logic now respects targetId (only rewrites marks when targetId === renamedPageId); added TAG_NAME_CHAR_CLASS_STRING; rewriteTitleRefsInDoc now accepts an options object (backwards-compatible); tests cover id-scoped rewriting and legacy fallbacks.
Editor marks & extensions
src/components/editor/extensions/WikiLinkExtension.ts, src/components/editor/extensions/WikiLinkExtension.test.ts, src/components/editor/extensions/TagExtension.ts, src/components/editor/extensions/TagExtension.test.ts
Added targetId mark attribute (persisted as data-target-id, default null) for WikiLink and Tag; Tag parsing uses shared TAG_NAME_CHAR_CLASS; tests pin schema parsing/rendering behavior.
Editor hooks & component tests
src/components/editor/TiptapEditor/useBubbleMenuWikiLink.ts, src/components/editor/TiptapEditor/useBubbleMenuWikiLink.test.ts, src/components/editor/TiptapEditor/useWikiLinkStatusSync.ts, src/components/editor/TiptapEditor/useTagStatusSync.ts, src/components/editor/TiptapEditor/EditorBubbleMenu.test.tsx, src/components/editor/TiptapEditor/useEditorBubbleMenu.test.ts
Hooks consume/return pageTitleToId maps, compute resolved targetId (or null) during convert/update flows; selection delimiter normalized; tests and mocks updated to include pageTitleToId.
Link/tag utilities & tests
src/lib/wikiLinkUtils.ts, src/lib/wikiLinkUtils.test.ts, src/lib/tagUtils.ts, src/lib/tagUtils.test.ts, src/lib/tagCharacterClassSync.test.ts
update* functions accept optional pageTitleToId to set targetId when resolved; change detection includes targetId; tests added for plumbing; drift-detection test compares server-side constant string with shared package.
Page queries
src/hooks/usePageQueries.ts
useWikiLinkExistsChecker.checkExistence now returns pageTitleToId: Map<string,string> alongside pageTitles and referencedTitles; logic refactored to populate that map.
Yjs title propagation tests
server/api/src/__tests__/services/titleRenamePropagationService.test.ts
New/updated helpers and an acceptance test validate targetId-scoped title propagation within Y.Doc state.
Editor tests/mocks
src/components/editor/TiptapEditor/useBubbleMenuWikiLink.test.ts, src/components/editor/TiptapEditor/useEditorBubbleMenu.test.ts, src/components/editor/TiptapEditor/EditorBubbleMenu.test.tsx
Mocks updated to include pageTitleToId; assertions adjusted to expect targetId when resolution occurs.

Sequence Diagram(s)

sequenceDiagram
    participant Editor as Editor (Client)
    participant Checker as ExistsChecker / usePageQueries
    participant Server as API (Rename/Save)
    participant YDoc as Y.Doc / DB

    Editor->>Checker: request existence for titles
    Checker-->>Editor: { pageTitles, referencedTitles, pageTitleToId }
    Editor->>Editor: insert/convert mark (title, exists, targetId?)
    Editor->>Server: save page (page contents with marks)
    Server->>YDoc: load source Y.Doc(s)
    Server->>Server: rewriteTitleRefsInDoc(oldTitle,newTitle,{renamedPageId})
    Server->>YDoc: persist updated Y.Doc(s)
    Server-->>Editor: persist ack
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Possibly related PRs

Poem

🐰 I hopped to share a tiny trait,
TAG chars synced across the gate,
targetIds now point the way,
tests keep drift and bugs at bay,
Hooray—links find home, precise and straight! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 70.83% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: preventing same-title page rewrites by matching targetId to fix issue #737, which aligns with the core purpose documented in the PR objectives.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/fix-issue-737-JHtkq

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a shared workspace package for constants and implements strict ID-based matching for WikiLinks and tags to prevent incorrect renames when duplicate titles exist. The changes span the Tiptap editor extensions, client-side status synchronization hooks, and server-side rename propagation services. Feedback highlights an opportunity to refactor the existence-checking logic in the client hooks to reduce code duplication and improve data processing efficiency.

Comment thread src/hooks/usePageQueries.ts Outdated
Comment on lines 842 to 860
if (pageNoteId !== null) {
if (notePages === undefined) {
return { pageTitles: new Set(), referencedTitles: new Set() };
return {
pageTitles: new Set(),
referencedTitles: new Set(),
pageTitleToId: new Map(),
};
}
pageTitles = new Set(notePages.map((p) => p.title.toLowerCase().trim()));
for (const p of notePages) {
pageTitleToId.set(p.title.toLowerCase().trim(), p.id);
}
} else {
const pages = await repo.getPagesSummary(userId);
pageTitles = new Set(pages.map((p) => p.title.toLowerCase().trim()));
for (const p of pages) {
pageTitleToId.set(p.title.toLowerCase().trim(), p.id);
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for populating pageTitles and pageTitleToId is duplicated across the if/else branches and involves multiple iterations over the same data. This can be refactored into a single pass over a unified source list to improve maintainability and efficiency.

      const sourcePages = pageNoteId !== null ? notePages : await repo.getPagesSummary(userId);
      if (pageNoteId !== null && sourcePages === undefined) {
        return {
          pageTitles: new Set(),
          referencedTitles: new Set(),
          pageTitleToId: new Map(),
        };
      }

      pageTitles = new Set();
      for (const p of sourcePages ?? []) {
        const normalized = p.title.toLowerCase().trim();
        pageTitles.add(normalized);
        pageTitleToId.set(normalized, p.id);
      }

…cker

Address Gemini review on PR #738: collapse the duplicated note / personal
branches into one walk that builds both `pageTitles` and `pageTitleToId`
from a unified `sourcePages` list. The previous shape called `.map()` to
seed the Set and a separate `for` loop to populate the Map for each
branch, walking the candidate list twice. Behaviour is preserved
(unloaded `notePages` in note scope still returns empty sets).
Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 6 additional findings.

Open in Devin Review

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (4)
packages/shared/src/tagCharacterClass.ts (1)

18-20: Minor documentation note: Unicode range comment covers two distinct blocks.

The comment states U+3040..U+309F (぀ヿ の前半) and U+30A0..U+30FF (぀ヿ の後半), but the actual range ぀-ヿ spans U+3040–U+30FF continuously (both hiragana and katakana together). This is functionally correct but the comment structure might be slightly confusing since it describes them as "前半/後半" (first/second half) of the same range rather than two separate Unicode blocks.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/shared/src/tagCharacterClass.ts` around lines 18 - 20, The
Unicode-range comment in tagCharacterClass.ts is misleading: clarify that
hiragana (U+3040..U+309F) and katakana (U+30A0..U+30FF) are two distinct Unicode
blocks even though the visual range "぀-ヿ" spans them contiguously; update the
comment to either list the blocks separately (hiragana: U+3040..U+309F,
katakana: U+30A0..U+30FF) or explicitly state that "぀-ヿ (U+3040..U+30FF) covers
both hiragana and katakana" so readers aren’t confused by the "前半/後半" phrasing.
src/components/editor/TiptapEditor/useBubbleMenuWikiLink.ts (1)

28-28: Add an explicit return type to the exported hook.

The hook’s public contract is currently inferred from the returned object. Making that shape explicit will keep downstream consumers stable as this payload evolves.

♻️ Suggested typing cleanup
+interface UseBubbleMenuWikiLinkResult {
+  isWikiLinkSelection: boolean;
+  convertToWikiLink: () => Promise<void>;
+  unsetWikiLink: () => void;
+  isConverting: boolean;
+}
+
-export function useBubbleMenuWikiLink({ editor, pageId }: UseBubbleMenuWikiLinkOptions) {
+export function useBubbleMenuWikiLink({
+  editor,
+  pageId,
+}: UseBubbleMenuWikiLinkOptions): UseBubbleMenuWikiLinkResult {
As per coding guidelines, "Use TypeScript strict mode; forbid `any` type and require explicit type annotations."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/editor/TiptapEditor/useBubbleMenuWikiLink.ts` at line 28, The
exported hook useBubbleMenuWikiLink lacks an explicit return type; define a
specific return type (e.g., interface or type alias like
UseBubbleMenuWikiLinkReturn) describing the exact shape of the returned object
and annotate the function signature as export function
useBubbleMenuWikiLink(...): UseBubbleMenuWikiLinkReturn { ... }; ensure the
defined type avoids any usage, reflects all returned properties/methods, and is
exported if it is part of the public API so downstream consumers remain stable.
server/api/src/__tests__/services/ydocRenameRewrite.test.ts (1)

335-405: Add tag parity cases for the fallback branches.

The new tag suite covers the strict targetId match, but the backward-compatible branches are only asserted for wikiLink. A tag-specific regression in the “no targetId” / “no renamedPageId” paths could still slip through.

Based on learnings, "Tests serve as a source of truth for specifications alongside implementation code TSDoc/JSDoc."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/api/src/__tests__/services/ydocRenameRewrite.test.ts` around lines 335
- 405, Add parity tests for tag marks mirroring the two wikiLink fallback cases:
call rewriteTitleRefsInDoc with and without the renamedPageId and assert
behavior on tag marks — first, create a tag-marked insert with a targetId (e.g.
targetId: OTHER_PAGE_ID) and call rewriteTitleRefsInDoc("Foo","Bar") without
renamedPageId, then assert result.tagMarksUpdated === 0 and plainText stays
"Foo"; second, create a tag-marked insert without targetId and call
rewriteTitleRefsInDoc("Foo","Bar") without renamedPageId, then assert
result.tagMarksUpdated === 1 (and/or result.tagTextUpdated) and plainText
becomes "Bar". Ensure the tests reference rewriteTitleRefsInDoc and check
result.tagMarksUpdated/result.tagTextUpdated so tag-specific fallback behavior
is covered.
server/api/src/services/ydocRenameRewrite.ts (1)

79-81: Static analysis false positive — regex is safe.

The ast-grep ReDoS warning is a false positive:

  1. TAG_NAME_CHAR_CLASS_STRING is a hardcoded constant, not user input
  2. The pattern ^[...]+$ has no nested quantifiers or alternations that could cause catastrophic backtracking
  3. Time complexity is linear in input length

Consider adding a brief comment to preempt future tooling warnings:

📝 Optional: Add safety comment
 export const TAG_NAME_CHAR_CLASS_STRING = "A-Za-z0-9_\\-぀-ヿ㐀-鿿";

+// ReDoS-safe: constant pattern with no nested quantifiers or alternations.
 const VALID_TAG_NAME_REGEX = new RegExp(`^[${TAG_NAME_CHAR_CLASS_STRING}]+$`);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/api/src/services/ydocRenameRewrite.ts` around lines 79 - 81, The regex
warning is a false positive; add a brief explanatory comment above
TAG_NAME_CHAR_CLASS_STRING and VALID_TAG_NAME_REGEX stating that the character
class is a hardcoded constant (not user input), the pattern is a simple anchored
character-class with a single quantifier and has linear-time complexity, so it
is safe from ReDoS; reference TAG_NAME_CHAR_CLASS_STRING and
VALID_TAG_NAME_REGEX in the comment to make intent explicit for future reviewers
and tooling.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/shared/src/tagCharacterClass.ts`:
- Around line 18-20: The Unicode-range comment in tagCharacterClass.ts is
misleading: clarify that hiragana (U+3040..U+309F) and katakana (U+30A0..U+30FF)
are two distinct Unicode blocks even though the visual range "぀-ヿ" spans them
contiguously; update the comment to either list the blocks separately (hiragana:
U+3040..U+309F, katakana: U+30A0..U+30FF) or explicitly state that "぀-ヿ
(U+3040..U+30FF) covers both hiragana and katakana" so readers aren’t confused
by the "前半/後半" phrasing.

In `@server/api/src/__tests__/services/ydocRenameRewrite.test.ts`:
- Around line 335-405: Add parity tests for tag marks mirroring the two wikiLink
fallback cases: call rewriteTitleRefsInDoc with and without the renamedPageId
and assert behavior on tag marks — first, create a tag-marked insert with a
targetId (e.g. targetId: OTHER_PAGE_ID) and call
rewriteTitleRefsInDoc("Foo","Bar") without renamedPageId, then assert
result.tagMarksUpdated === 0 and plainText stays "Foo"; second, create a
tag-marked insert without targetId and call rewriteTitleRefsInDoc("Foo","Bar")
without renamedPageId, then assert result.tagMarksUpdated === 1 (and/or
result.tagTextUpdated) and plainText becomes "Bar". Ensure the tests reference
rewriteTitleRefsInDoc and check result.tagMarksUpdated/result.tagTextUpdated so
tag-specific fallback behavior is covered.

In `@server/api/src/services/ydocRenameRewrite.ts`:
- Around line 79-81: The regex warning is a false positive; add a brief
explanatory comment above TAG_NAME_CHAR_CLASS_STRING and VALID_TAG_NAME_REGEX
stating that the character class is a hardcoded constant (not user input), the
pattern is a simple anchored character-class with a single quantifier and has
linear-time complexity, so it is safe from ReDoS; reference
TAG_NAME_CHAR_CLASS_STRING and VALID_TAG_NAME_REGEX in the comment to make
intent explicit for future reviewers and tooling.

In `@src/components/editor/TiptapEditor/useBubbleMenuWikiLink.ts`:
- Line 28: The exported hook useBubbleMenuWikiLink lacks an explicit return
type; define a specific return type (e.g., interface or type alias like
UseBubbleMenuWikiLinkReturn) describing the exact shape of the returned object
and annotate the function signature as export function
useBubbleMenuWikiLink(...): UseBubbleMenuWikiLinkReturn { ... }; ensure the
defined type avoids any usage, reflects all returned properties/methods, and is
exported if it is part of the public API so downstream consumers remain stable.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7da982af-845e-421f-a7db-e4f330f77744

📥 Commits

Reviewing files that changed from the base of the PR and between 9e1d66f and 2e9f1ad.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (30)
  • AGENTS.md
  • package.json
  • packages/shared/package.json
  • packages/shared/src/index.ts
  • packages/shared/src/tagCharacterClass.test.ts
  • packages/shared/src/tagCharacterClass.ts
  • packages/shared/tsconfig.json
  • packages/shared/vitest.config.ts
  • server/api/src/__tests__/services/titleRenamePropagationService.test.ts
  • server/api/src/__tests__/services/ydocRenameRewrite.test.ts
  • server/api/src/services/titleRenamePropagationService.ts
  • server/api/src/services/ydocRenameRewrite.ts
  • src/components/editor/TiptapEditor/EditorBubbleMenu.test.tsx
  • src/components/editor/TiptapEditor/useBubbleMenuWikiLink.test.ts
  • src/components/editor/TiptapEditor/useBubbleMenuWikiLink.ts
  • src/components/editor/TiptapEditor/useEditorBubbleMenu.test.ts
  • src/components/editor/TiptapEditor/useTagStatusSync.test.tsx
  • src/components/editor/TiptapEditor/useTagStatusSync.ts
  • src/components/editor/TiptapEditor/useWikiLinkStatusSync.test.tsx
  • src/components/editor/TiptapEditor/useWikiLinkStatusSync.ts
  • src/components/editor/extensions/TagExtension.test.ts
  • src/components/editor/extensions/TagExtension.ts
  • src/components/editor/extensions/WikiLinkExtension.test.ts
  • src/components/editor/extensions/WikiLinkExtension.ts
  • src/hooks/usePageQueries.ts
  • src/lib/tagCharacterClassSync.test.ts
  • src/lib/tagUtils.test.ts
  • src/lib/tagUtils.ts
  • src/lib/wikiLinkUtils.test.ts
  • src/lib/wikiLinkUtils.ts

- packages/shared/src/tagCharacterClass.ts: clarify the hiragana / katakana
  Unicode comment so it no longer reads as "前半/後半" of a single block;
  list the two distinct blocks (U+3040..U+309F / U+30A0..U+30FF) and note
  that `぀-ヿ` covers them contiguously.
- src/components/editor/TiptapEditor/useBubbleMenuWikiLink.ts: add an
  explicit `UseBubbleMenuWikiLinkResult` return type to the exported hook
  so the public contract is no longer inferred (matches project rule on
  explicit type annotations).
- server/api/src/services/ydocRenameRewrite.ts: add a comment justifying
  why `VALID_TAG_NAME_REGEX` is ReDoS-safe (constant char class, single
  quantifier, no nested alternations) to preempt static-analysis false
  positives.
- server/api/src/__tests__/services/ydocRenameRewrite.test.ts: add tag
  parity tests for the two `renamedPageId`-omitted fallback branches that
  previously only covered wikiLink marks (skip-when-targetId-present and
  rewrite-when-id-less).
coderabbitai[bot]

This comment was marked as resolved.

… TSDoc

Address two CodeRabbit review comments on PR #738:

1. `rewriteTitleRefsInDoc` accepted a `fragmentName: string` as its 4th
   argument before issue #737 and now expects an options object. Any
   surviving caller passing a string would silently retarget the default
   fragment because `"foo".fragmentName === undefined`. Add a
   compatibility shim that normalizes a string 4th argument into
   `{ fragmentName }`. Two new tests lock in the legacy call form and
   guard against a regression that would silently rewrite the default
   fragment.

2. The TSDoc on `RewriteTitleRefsOptions.renamedPageId` claimed that
   omitting it falls back to title-only matching for *every* mark, but
   the implementation (and the issue #737 tests) skip marks that already
   carry a `targetId` because they cannot be safely verified. Update both
   the Japanese and English descriptions to match the real contract.
@otomatty otomatty self-assigned this Apr 25, 2026
@otomatty otomatty merged commit e30dd03 into develop Apr 25, 2026
16 checks passed
@otomatty otomatty deleted the claude/fix-issue-737-JHtkq branch April 25, 2026 04:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants