fix: 修復 Substack/Cloudinary CDN 圖片 URL 的編碼問題#383
Conversation
問題: - mangmang.run (Substack) 等網站的圖片保存到 Notion 後無法顯示 - Substack CDN URL 在路徑中嵌入 percent-encoded URL,例如: https://substackcdn.com/image/fetch/.../https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com/... - _normalizeUrlInternal 的 decodeURI() 會把 %3A%2F%2F 解碼為 :// - encodeURI() 不會重新編碼 : 和 /(URI-safe 字符) - 結果:CDN 路徑結構被永久破壞,導致圖片無法載入 修復: - 在 _normalizeUrlInternal 中偵測路徑含嵌入式 percent-encoded URL (https?%3A%2F%2F),直接返回跳過 decode/encode - 影響範圍:Substack、Cloudinary、imgix 等 CDN 代理 URL 測試: - 新增 3 個測試案例覆蓋 Substack 和 Cloudinary URL 格式 - 全部 245 個現有測試通過,無回歸
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! 此 PR 主要修復了圖片 URL 在標準化過程中,因錯誤解碼嵌入式 percent-encoded URL 而導致路徑結構損壞的問題。透過在處理流程中加入特定 CDN 格式的偵測邏輯,確保這類 URL 能被正確保留,並透過新增單元測試驗證了修復的有效性與穩定性。 Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 2 minutes and 41 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Repository YAML (base), Organization UI (inherited) Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthrough在 scripts/utils/imageUtils.js 中增强了 URL 规范化与提取逻辑:新增从 scripts/config/extraction.js 导入的常量 Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@scripts/utils/imageUtils.js`:
- Line 168: Extract the inline regex /https?%3A%2F%2F/i into a named constant
(e.g., EMBEDDED_URL_ENCODED_REGEX) defined and exported from your dedicated
constants module, import that constant into scripts/utils/imageUtils.js and
replace the inline usage in the conditional that tests normalized with the
imported constant; ensure the constant name clearly indicates it matches
URL-encoded "http(s)://" so future maintenance and reuse are straightforward.
In `@tests/unit/imageUtils.utils.test.js`:
- Around line 88-92: The test title says it should verify isValidCleanedImageUrl
but the assertion calls isValidImageUrl; update the test so the name and the
assertion match — either rename the test to reference isValidImageUrl or change
the assertion to call isValidCleanedImageUrl (and keep the URL and expectation
the same); locate this in the test containing cleanImageUrl,
isValidCleanedImageUrl, and isValidImageUrl to make the alignment consistent.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: 1c7e54ea-9867-40c2-bc25-463159183b89
📒 Files selected for processing (2)
scripts/utils/imageUtils.jstests/unit/imageUtils.utils.test.js
📜 Review details
🧰 Additional context used
📓 Path-based instructions (11)
**/*.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
此專案為 Chrome Extension (Manifest V3),必須使用 Vanilla JavaScript (ES6+ Modules) 作為核心語言,CommonJS 用於 Node 腳本。
Files:
scripts/utils/imageUtils.jstests/unit/imageUtils.utils.test.js
**/*.{js,ts,tsx,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
禁止使用 TypeScript(除非明確要求)、React、Vue、Webpack。
Do not use console.log() statements in production code
Files:
scripts/utils/imageUtils.jstests/unit/imageUtils.utils.test.js
**/*.{js,ts,tsx,jsx,py,java,cs,rb,go,swift,kt,php,md}
📄 CodeRabbit inference engine (.agents/.shared/knowledge/linter_rules.json)
Use Traditional Chinese (zh-TW) for all code comments, documentation, and string content, with strict enforcement except for technical terms and variable names
Files:
scripts/utils/imageUtils.jstests/unit/imageUtils.utils.test.js
**/*.{js,ts,tsx,jsx,py,java,cs,rb,go,swift,kt,php}
📄 CodeRabbit inference engine (.agents/.shared/knowledge/linter_rules.json)
**/*.{js,ts,tsx,jsx,py,java,cs,rb,go,swift,kt,php}: Do not use magic numbers in code; extract them to constants defined in a dedicated constants module
Use a dedicated constants module for defining and exporting all constant values
Files:
scripts/utils/imageUtils.jstests/unit/imageUtils.utils.test.js
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.agents/.shared/knowledge/security_rules.json)
**/*.{js,ts,jsx,tsx}: Force strict whitelisting for file downloads: appended files must be restricted to '.json' or '.txt' extensions with explicitly designated MIME types. Never use arbitrary user input for filenames to prevent Reflected File Download (RFD) attacks.
Always prefertextContentfor DOM operations. If SVG mixed content requires text updates, find the TEXT_NODE and update itstextContentrather than usinginnerHTMLon the parent.
Rely onLogSanitizerto strip sensitive properties (likesender,tab, PII) before emitting logs in production.Follow the project's ESLint configuration and do not emit DeepSource (skipcq) comments.
**/*.{js,ts,jsx,tsx}: Reject PR if attempting to log complete sender, tab, request objects or raw HTML content. Require using IDs only (e.g., sender?.id, tab?.id), and wrapping URLs with sanitizeUrlForLogging() and API errors with sanitizeApiError().
Reject PR if attempting to store large HTML content, Base64 images, or heavy payloads into chrome.storage.sync. Require storing large payloads in chrome.storage.local or IndexedDB.
Reject PR if introducing new core business logic without corresponding unit or E2E tests. Require TDD and ensure Codecov checks do not fail (< 50%).
Avoid exposing internal-only, non-actionable, non-blocking cleanup/reconciliation/retry failures directly as user-facing errors. Keep the canonical user-facing state, log the internal failure, and handle retry/recovery inside the relevant service or canonical path unless the failure truly blocks the user's next action.
Verify all Logger.info/error calls correctly sanitize data with no PII and no raw HTML.
Verify cache and large data usage strictly targets chrome.storage.local rather than chrome.storage.sync.
Files:
scripts/utils/imageUtils.jstests/unit/imageUtils.utils.test.js
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.agents/.shared/knowledge/debugging_rules.json)
**/*.{ts,tsx,js,jsx}: Use Logger.success(msg, ctx?) for operation success confirmation in Notion Chrome Extension. Output prefix: [INFO] ✅. The Logger class auto-manages the emoji icon.
Use Logger.start(msg, ctx?) for process launch notifications in Notion Chrome Extension. Output prefix: [INFO] 🚀. The Logger class auto-manages the emoji icon.
Use Logger.ready(msg, ctx?) for module or resource readiness notifications in Notion Chrome Extension. Output prefix: [INFO] 📦. The Logger class auto-manages the emoji icon.
Use Logger.info(msg, ctx?) for general informational messages in Notion Chrome Extension. Output prefix: [INFO]. No auto-managed emoji icon.
Use Logger.debug(msg, ctx?) for development debugging in Notion Chrome Extension. Output prefix: [DEBUG]. Controlled by enableDebugLogs switch.
Use Logger.warn(msg, ctx?) for non-fatal warnings in Notion Chrome Extension. Output prefix: [WARN]⚠️ . The Logger class auto-manages the emoji icon.
Use Logger.error(msg, ctx?) for error recording in Notion Chrome Extension. Output prefix: [ERROR] ❌. The Logger class auto-manages the emoji icon. Always displayed regardless of debug switch.
Do NOT manually insert emoji icons (✅, 🚀, 📦,⚠️ , ❌) into log messages. The Logger class auto-manages these icons. Only manually use special semantic emoji like 🔍 (search) or 🗑️ (delete) when needed for semantic clarity.
Use 'action' field in Logger context to record public API function names (e.g., 'loadHighlights', 'savePage') for structured logging in Notion Chrome Extension.
Use 'operation' field in Logger context to record internal operation steps (e.g., 'parseLocalStorage', 'buildUrl') for structured logging in Notion Chrome Extension.
Use 'phase' field in Logger context (optional) to record execution stages (e.g., 'validation', 'apiCall') for structured logging in Notion Chrome Extension.
Use 'result' field in Logger context to record operation results (e.g., 'blocked', 'success', 'failed') for structured logging in Notion Chr...
Files:
scripts/utils/imageUtils.jstests/unit/imageUtils.utils.test.js
**/*.{js,ts,jsx,tsx,md}
📄 CodeRabbit inference engine (.agents/.shared/knowledge/code_review_rules.json)
Maintain code comments and documentation in Traditional Chinese.
Files:
scripts/utils/imageUtils.jstests/unit/imageUtils.utils.test.js
**/*
⚙️ CodeRabbit configuration file
**/*: # Project Specific Guidelines
- Language: All user-facing UI text, strings, and messages MUST be in Traditional Chinese (zh-TW).
- Architecture: Follow the defined storage structures and specs. Do not introduce unauthorized storage fields.
- Code Quality: Avoid N+1 issues, blocking operations, and ensure high code quality. Suggest optimizations that follow modern JavaScript/Chrome Extension best practices.
Files:
scripts/utils/imageUtils.jstests/unit/imageUtils.utils.test.js
**/*.test.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
單元測試邏輯使用 Jest,需要使用 Mocking patterns。
Files:
tests/unit/imageUtils.utils.test.js
**/*.{test,spec}.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.agents/.shared/knowledge/debugging_rules.json)
**/*.{test,spec}.{ts,tsx,js,jsx}: Run failing tests with command 'npx jest <path/to/test.js> --no-coverage' instead of full test suites in Notion Chrome Extension for faster feedback loops during logic fixes.
When mocking objects in Notion Chrome Extension tests, ensure mock object depth is sufficient. For example, mocking an img tag requires including 'dataset: {}' property.
When mocking Logger in Notion Chrome Extension tests, ensure all methods (success, start, ready, info, debug, warn, error) are defined as jest.fn() in the mock setup.
In Notion Chrome Extension tests using ErrorHandler.formatUserMessage, target assertions against Chinese strings (user-facing messages) rather than original English API messages for I18N awareness.
In Notion Chrome Extension tests, avoid vague string matching for Logger assertions. Use 'expect.objectContaining' to assert exact expected metadata objects for Logger calls.
Files:
tests/unit/imageUtils.utils.test.js
**/*.{test,spec}.{js,ts}
📄 CodeRabbit inference engine (.agents/.shared/knowledge/testing_rules.json)
**/*.{test,spec}.{js,ts}: Always reference data structures (HTML snippets, Notion JSON responses) from .agents/.shared/references/ instead of hardcoding large payloads in test files.
Use .agents/.shared/references/notion_api/ for Notion block structures and database properties in test fixtures.
Do not mutate window.chrome directly. Override global.chrome for mocking Chrome API in tests.
Declare mock variables and spies at the top level and initialize them in beforeEach for Chrome API mocking setup.
Execute jest.clearAllMocks() and clean up global.chrome in afterEach to prevent test pollution.
Files:
tests/unit/imageUtils.utils.test.js
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: cowcfj/save-to-notion PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-26T20:59:55.454Z
Learning: Commit Message 必須使用繁體中文,若變更的檔案僅包含測試程式碼(如 .test.js, .spec.js, tests/ 目錄),則必須使用 `test` 類型,絕不可以使用 `feat` 或 `fix`。
📚 Learning: 2026-03-06T18:35:38.641Z
Learnt from: CR
Repo: cowcfj/save-to-notion PR: 0
File: .agents/.shared/knowledge/debugging_rules.json:0-0
Timestamp: 2026-03-06T18:35:38.641Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js,jsx} : When mocking objects in Notion Chrome Extension tests, ensure mock object depth is sufficient. For example, mocking an img tag requires including 'dataset: {}' property.
Applied to files:
tests/unit/imageUtils.utils.test.js
📚 Learning: 2026-03-06T18:35:38.641Z
Learnt from: CR
Repo: cowcfj/save-to-notion PR: 0
File: .agents/.shared/knowledge/debugging_rules.json:0-0
Timestamp: 2026-03-06T18:35:38.641Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js,jsx} : In Notion Chrome Extension tests using ErrorHandler.formatUserMessage, target assertions against Chinese strings (user-facing messages) rather than original English API messages for I18N awareness.
Applied to files:
tests/unit/imageUtils.utils.test.js
🔇 Additional comments (2)
scripts/utils/imageUtils.js (1)
162-170: 這段早退判斷修復方向正確。保留路徑中的嵌入式 percent-encoded URL,能避免
decodeURI將%3A%2F%2F還原成://後破壞 CDN 路徑語義。tests/unit/imageUtils.utils.test.js (1)
70-93: 新增的 CDN 代理 URL 測試覆蓋很到位。這批案例直接鎖定
%3A%2F%2F路徑保留行為,能有效防止同類回歸。
在 cleanImageUrl 函數中,新增對 CDN 代理 URL 的處理,確保 Markdown 敏感字元能正確編碼,避免解析錯誤。
There was a problem hiding this comment.
♻️ Duplicate comments (1)
tests/unit/imageUtils.utils.test.js (1)
97-101:⚠️ Potential issue | 🟡 Minor測試名稱與實際斷言目標不一致。
Line 97 名稱寫的是
isValidCleanedImageUrl,但 Line 101 斷言實際呼叫isValidImageUrl。請擇一對齊,避免誤讀。✏️ 建議調整
- test('cleanImageUrl 對 Substack URL 應通過 isValidCleanedImageUrl 驗證', () => { + test('Substack URL 應通過 isValidImageUrl 驗證', () => {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/unit/imageUtils.utils.test.js` around lines 97 - 101, Test title and assertion disagree: the test description mentions isValidCleanedImageUrl but the assertion calls isValidImageUrl. Fix by making the assertion and title consistent—either change the expect(...) call to use isValidCleanedImageUrl or rename the test string to reference isValidImageUrl; update the test description (the string passed to test(...)) and/or the expectation call (isValidImageUrl → isValidCleanedImageUrl) so the test name and the function under test match.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@tests/unit/imageUtils.utils.test.js`:
- Around line 97-101: Test title and assertion disagree: the test description
mentions isValidCleanedImageUrl but the assertion calls isValidImageUrl. Fix by
making the assertion and title consistent—either change the expect(...) call to
use isValidCleanedImageUrl or rename the test string to reference
isValidImageUrl; update the test description (the string passed to test(...))
and/or the expectation call (isValidImageUrl → isValidCleanedImageUrl) so the
test name and the function under test match.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: fb0c5b0b-a676-4f66-9ad4-088f676a0fee
📒 Files selected for processing (3)
scripts/config/extraction.jsscripts/utils/imageUtils.jstests/unit/imageUtils.utils.test.js
📜 Review details
🧰 Additional context used
📓 Path-based instructions (11)
**/*.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
此專案為 Chrome Extension (Manifest V3),必須使用 Vanilla JavaScript (ES6+ Modules) 作為核心語言,CommonJS 用於 Node 腳本。
Files:
scripts/config/extraction.jstests/unit/imageUtils.utils.test.jsscripts/utils/imageUtils.js
**/*.{js,ts,tsx,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
禁止使用 TypeScript(除非明確要求)、React、Vue、Webpack。
Do not use console.log() statements in production code
Files:
scripts/config/extraction.jstests/unit/imageUtils.utils.test.jsscripts/utils/imageUtils.js
**/*.{js,ts,tsx,jsx,py,java,cs,rb,go,swift,kt,php,md}
📄 CodeRabbit inference engine (.agents/.shared/knowledge/linter_rules.json)
Use Traditional Chinese (zh-TW) for all code comments, documentation, and string content, with strict enforcement except for technical terms and variable names
Files:
scripts/config/extraction.jstests/unit/imageUtils.utils.test.jsscripts/utils/imageUtils.js
**/*.{js,ts,tsx,jsx,py,java,cs,rb,go,swift,kt,php}
📄 CodeRabbit inference engine (.agents/.shared/knowledge/linter_rules.json)
**/*.{js,ts,tsx,jsx,py,java,cs,rb,go,swift,kt,php}: Do not use magic numbers in code; extract them to constants defined in a dedicated constants module
Use a dedicated constants module for defining and exporting all constant values
Files:
scripts/config/extraction.jstests/unit/imageUtils.utils.test.jsscripts/utils/imageUtils.js
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.agents/.shared/knowledge/security_rules.json)
**/*.{js,ts,jsx,tsx}: Force strict whitelisting for file downloads: appended files must be restricted to '.json' or '.txt' extensions with explicitly designated MIME types. Never use arbitrary user input for filenames to prevent Reflected File Download (RFD) attacks.
Always prefertextContentfor DOM operations. If SVG mixed content requires text updates, find the TEXT_NODE and update itstextContentrather than usinginnerHTMLon the parent.
Rely onLogSanitizerto strip sensitive properties (likesender,tab, PII) before emitting logs in production.Follow the project's ESLint configuration and do not emit DeepSource (skipcq) comments.
**/*.{js,ts,jsx,tsx}: Reject PR if attempting to log complete sender, tab, request objects or raw HTML content. Require using IDs only (e.g., sender?.id, tab?.id), and wrapping URLs with sanitizeUrlForLogging() and API errors with sanitizeApiError().
Reject PR if attempting to store large HTML content, Base64 images, or heavy payloads into chrome.storage.sync. Require storing large payloads in chrome.storage.local or IndexedDB.
Reject PR if introducing new core business logic without corresponding unit or E2E tests. Require TDD and ensure Codecov checks do not fail (< 50%).
Avoid exposing internal-only, non-actionable, non-blocking cleanup/reconciliation/retry failures directly as user-facing errors. Keep the canonical user-facing state, log the internal failure, and handle retry/recovery inside the relevant service or canonical path unless the failure truly blocks the user's next action.
Verify all Logger.info/error calls correctly sanitize data with no PII and no raw HTML.
Verify cache and large data usage strictly targets chrome.storage.local rather than chrome.storage.sync.
Files:
scripts/config/extraction.jstests/unit/imageUtils.utils.test.jsscripts/utils/imageUtils.js
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.agents/.shared/knowledge/debugging_rules.json)
**/*.{ts,tsx,js,jsx}: Use Logger.success(msg, ctx?) for operation success confirmation in Notion Chrome Extension. Output prefix: [INFO] ✅. The Logger class auto-manages the emoji icon.
Use Logger.start(msg, ctx?) for process launch notifications in Notion Chrome Extension. Output prefix: [INFO] 🚀. The Logger class auto-manages the emoji icon.
Use Logger.ready(msg, ctx?) for module or resource readiness notifications in Notion Chrome Extension. Output prefix: [INFO] 📦. The Logger class auto-manages the emoji icon.
Use Logger.info(msg, ctx?) for general informational messages in Notion Chrome Extension. Output prefix: [INFO]. No auto-managed emoji icon.
Use Logger.debug(msg, ctx?) for development debugging in Notion Chrome Extension. Output prefix: [DEBUG]. Controlled by enableDebugLogs switch.
Use Logger.warn(msg, ctx?) for non-fatal warnings in Notion Chrome Extension. Output prefix: [WARN]⚠️ . The Logger class auto-manages the emoji icon.
Use Logger.error(msg, ctx?) for error recording in Notion Chrome Extension. Output prefix: [ERROR] ❌. The Logger class auto-manages the emoji icon. Always displayed regardless of debug switch.
Do NOT manually insert emoji icons (✅, 🚀, 📦,⚠️ , ❌) into log messages. The Logger class auto-manages these icons. Only manually use special semantic emoji like 🔍 (search) or 🗑️ (delete) when needed for semantic clarity.
Use 'action' field in Logger context to record public API function names (e.g., 'loadHighlights', 'savePage') for structured logging in Notion Chrome Extension.
Use 'operation' field in Logger context to record internal operation steps (e.g., 'parseLocalStorage', 'buildUrl') for structured logging in Notion Chrome Extension.
Use 'phase' field in Logger context (optional) to record execution stages (e.g., 'validation', 'apiCall') for structured logging in Notion Chrome Extension.
Use 'result' field in Logger context to record operation results (e.g., 'blocked', 'success', 'failed') for structured logging in Notion Chr...
Files:
scripts/config/extraction.jstests/unit/imageUtils.utils.test.jsscripts/utils/imageUtils.js
**/*.{js,ts,jsx,tsx,md}
📄 CodeRabbit inference engine (.agents/.shared/knowledge/code_review_rules.json)
Maintain code comments and documentation in Traditional Chinese.
Files:
scripts/config/extraction.jstests/unit/imageUtils.utils.test.jsscripts/utils/imageUtils.js
**/*
⚙️ CodeRabbit configuration file
**/*: # Project Specific Guidelines
- Language: All user-facing UI text, strings, and messages MUST be in Traditional Chinese (zh-TW).
- Architecture: Follow the defined storage structures and specs. Do not introduce unauthorized storage fields.
- Code Quality: Avoid N+1 issues, blocking operations, and ensure high code quality. Suggest optimizations that follow modern JavaScript/Chrome Extension best practices.
Files:
scripts/config/extraction.jstests/unit/imageUtils.utils.test.jsscripts/utils/imageUtils.js
**/*.test.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
單元測試邏輯使用 Jest,需要使用 Mocking patterns。
Files:
tests/unit/imageUtils.utils.test.js
**/*.{test,spec}.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.agents/.shared/knowledge/debugging_rules.json)
**/*.{test,spec}.{ts,tsx,js,jsx}: Run failing tests with command 'npx jest <path/to/test.js> --no-coverage' instead of full test suites in Notion Chrome Extension for faster feedback loops during logic fixes.
When mocking objects in Notion Chrome Extension tests, ensure mock object depth is sufficient. For example, mocking an img tag requires including 'dataset: {}' property.
When mocking Logger in Notion Chrome Extension tests, ensure all methods (success, start, ready, info, debug, warn, error) are defined as jest.fn() in the mock setup.
In Notion Chrome Extension tests using ErrorHandler.formatUserMessage, target assertions against Chinese strings (user-facing messages) rather than original English API messages for I18N awareness.
In Notion Chrome Extension tests, avoid vague string matching for Logger assertions. Use 'expect.objectContaining' to assert exact expected metadata objects for Logger calls.
Files:
tests/unit/imageUtils.utils.test.js
**/*.{test,spec}.{js,ts}
📄 CodeRabbit inference engine (.agents/.shared/knowledge/testing_rules.json)
**/*.{test,spec}.{js,ts}: Always reference data structures (HTML snippets, Notion JSON responses) from .agents/.shared/references/ instead of hardcoding large payloads in test files.
Use .agents/.shared/references/notion_api/ for Notion block structures and database properties in test fixtures.
Do not mutate window.chrome directly. Override global.chrome for mocking Chrome API in tests.
Declare mock variables and spies at the top level and initialize them in beforeEach for Chrome API mocking setup.
Execute jest.clearAllMocks() and clean up global.chrome in afterEach to prevent test pollution.
Files:
tests/unit/imageUtils.utils.test.js
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: cowcfj/save-to-notion PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-26T20:59:55.454Z
Learning: Commit Message 必須使用繁體中文,若變更的檔案僅包含測試程式碼(如 .test.js, .spec.js, tests/ 目錄),則必須使用 `test` 類型,絕不可以使用 `feat` 或 `fix`。
Learnt from: CR
Repo: cowcfj/save-to-notion PR: 0
File: .agents/.shared/knowledge/debugging_rules.json:0-0
Timestamp: 2026-03-06T18:35:38.641Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js,jsx} : In Notion Chrome Extension tests using ErrorHandler.formatUserMessage, target assertions against Chinese strings (user-facing messages) rather than original English API messages for I18N awareness.
📚 Learning: 2026-03-06T18:35:38.641Z
Learnt from: CR
Repo: cowcfj/save-to-notion PR: 0
File: .agents/.shared/knowledge/debugging_rules.json:0-0
Timestamp: 2026-03-06T18:35:38.641Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js,jsx} : In Notion Chrome Extension tests using ErrorHandler.formatUserMessage, target assertions against Chinese strings (user-facing messages) rather than original English API messages for I18N awareness.
Applied to files:
tests/unit/imageUtils.utils.test.js
📚 Learning: 2026-03-06T18:35:38.641Z
Learnt from: CR
Repo: cowcfj/save-to-notion PR: 0
File: .agents/.shared/knowledge/debugging_rules.json:0-0
Timestamp: 2026-03-06T18:35:38.641Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js,jsx} : When mocking objects in Notion Chrome Extension tests, ensure mock object depth is sufficient. For example, mocking an img tag requires including 'dataset: {}' property.
Applied to files:
tests/unit/imageUtils.utils.test.js
📚 Learning: 2026-03-06T18:35:38.641Z
Learnt from: CR
Repo: cowcfj/save-to-notion PR: 0
File: .agents/.shared/knowledge/debugging_rules.json:0-0
Timestamp: 2026-03-06T18:35:38.641Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js,jsx} : In Notion Chrome Extension tests, avoid vague string matching for Logger assertions. Use 'expect.objectContaining' to assert exact expected metadata objects for Logger calls.
Applied to files:
tests/unit/imageUtils.utils.test.js
📚 Learning: 2026-03-23T04:16:46.480Z
Learnt from: CR
Repo: cowcfj/save-to-notion PR: 0
File: .agents/.shared/knowledge/code_review_rules.json:0-0
Timestamp: 2026-03-23T04:16:46.480Z
Learning: Applies to **/*.{js,ts,jsx,tsx} : Verify all Logger.info/error calls correctly sanitize data with no PII and no raw HTML.
Applied to files:
scripts/utils/imageUtils.js
📚 Learning: 2026-03-23T04:16:46.480Z
Learnt from: CR
Repo: cowcfj/save-to-notion PR: 0
File: .agents/.shared/knowledge/code_review_rules.json:0-0
Timestamp: 2026-03-23T04:16:46.480Z
Learning: Applies to **/*.{js,ts,jsx,tsx} : Reject PR if attempting to log complete sender, tab, request objects or raw HTML content. Require using IDs only (e.g., sender?.id, tab?.id), and wrapping URLs with sanitizeUrlForLogging() and API errors with sanitizeApiError().
Applied to files:
scripts/utils/imageUtils.js
🔇 Additional comments (4)
scripts/config/extraction.js (1)
98-98: 嵌入式協議偵測 regex 抽成常數做得很好。此做法讓後續在 URL 正規化流程中重用更一致,也降低維護成本。
tests/unit/imageUtils.utils.test.js (1)
70-95: CDN 代理 URL 回歸覆蓋補得很完整。Substack、Cloudinary 與 Markdown 敏感字元情境都有覆蓋,對這次修復很有保護力。
scripts/utils/imageUtils.js (2)
10-13: 常數改由配置模組匯入,方向正確。避免在工具函式中散落規則,後續維護與重用性更好。
165-174: CDN 嵌入式 encoded URL 的保護分支實作正確。這段能有效避免
decodeURI()破壞 Substack/Cloudinary 類型路徑,同時仍保留 Markdown/Notion 必要編碼。
問題: - Substack CDN URL 在 transform 參數中含逗號 - srcset 條目以逗號分割時,URL 被截斷為無效片段 - 截斷片段被 new URL(src, baseURI) 拼接為錯誤域名 URL - Notion 無法載入,返回 404 修復: - 在 extractImageSrc 中對 srcset 結果增加 _isPlausibleImageUrl 驗證 - 偵測含 %3A%2F%2F 但不以 http(s):// 開頭的截斷片段 - 驗證失敗時回退到 src 屬性取得完整 URL 測試: - 新增 Substack CDN srcset 截斷場景的測試案例 - 全部 247 個測試通過,零回歸
將對 Substack CDN 圖片 URL 的驗證從 isValidImageUrl 更改為 isValidCleanedImageUrl,以確保清理後的 URL 能正確通過驗證。
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
scripts/utils/imageUtils.js (1)
865-865: 🛠️ Refactor suggestion | 🟠 Major避免重複內嵌 regex,改用既有常數以防規則漂移。
此處直接寫
/%3A%2F%2F/i,建議重用EMBEDDED_URL_ENCODED_HTTP_PROTOCOL_REGEX。As per coding guidelines:
Do not use magic numbers in code; extract them to constants defined in a dedicated constants module與Use a dedicated constants module for defining and exporting all constant values.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/utils/imageUtils.js` at line 865, Replace the inline regex /%3A%2F%2F/i in the conditional with the shared constant EMBEDDED_URL_ENCODED_HTTP_PROTOCOL_REGEX to avoid duplicated regex definitions; import that constant from the module that exports it (where other regex constants live) and use EMBEDDED_URL_ENCODED_HTTP_PROTOCOL_REGEX.test(url) in the if condition (keeping the existing negated /^https?:\/\//i check), ensuring you remove the hardcoded literal so the code consistently uses the centralized constant.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@scripts/utils/imageUtils.js`:
- Around line 857-866: The function _isPlausibleImageUrl currently rejects URLs
containing percent-encoded "://” if they don’t start with http(s)://, which
incorrectly excludes protocol-relative URLs like //substackcdn.com/...; update
the conditional that checks for embedded %3A%2F%2F so it treats
protocol-relative URLs as valid by also allowing strings that start with "//"
(i.e. change the check from only /^https?:\/\//i to allow /^\/\// as well),
keeping the existing rejection for truly truncated fragments that neither start
with http(s):// nor //.
In `@tests/unit/imageUtils.utils.test.js`:
- Around line 354-370: Add a second unit test that mirrors the existing Substack
CDN case but uses a protocol-relative srcset (e.g., beginning with
//substackcdn.com/.../https%3A%2F%2F...) and a matching protocol-relative or
full src, to ensure extractImageSrc still falls back to the full src when srcset
is broken; locate the existing test using createMockImg and extractImageSrc as
references and duplicate the scenario with the protocol-relative URLs, asserting
the returned value equals the full src and contains "substackcdn.com".
---
Duplicate comments:
In `@scripts/utils/imageUtils.js`:
- Line 865: Replace the inline regex /%3A%2F%2F/i in the conditional with the
shared constant EMBEDDED_URL_ENCODED_HTTP_PROTOCOL_REGEX to avoid duplicated
regex definitions; import that constant from the module that exports it (where
other regex constants live) and use
EMBEDDED_URL_ENCODED_HTTP_PROTOCOL_REGEX.test(url) in the if condition (keeping
the existing negated /^https?:\/\//i check), ensuring you remove the hardcoded
literal so the code consistently uses the centralized constant.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: 3a2285dd-6d16-4f5b-b3fb-6b8f4523dc6d
📒 Files selected for processing (2)
scripts/utils/imageUtils.jstests/unit/imageUtils.utils.test.js
📜 Review details
🧰 Additional context used
📓 Path-based instructions (11)
**/*.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
此專案為 Chrome Extension (Manifest V3),必須使用 Vanilla JavaScript (ES6+ Modules) 作為核心語言,CommonJS 用於 Node 腳本。
Files:
tests/unit/imageUtils.utils.test.jsscripts/utils/imageUtils.js
**/*.test.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
單元測試邏輯使用 Jest,需要使用 Mocking patterns。
Files:
tests/unit/imageUtils.utils.test.js
**/*.{js,ts,tsx,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
禁止使用 TypeScript(除非明確要求)、React、Vue、Webpack。
Do not use console.log() statements in production code
Files:
tests/unit/imageUtils.utils.test.jsscripts/utils/imageUtils.js
**/*.{js,ts,tsx,jsx,py,java,cs,rb,go,swift,kt,php,md}
📄 CodeRabbit inference engine (.agents/.shared/knowledge/linter_rules.json)
Use Traditional Chinese (zh-TW) for all code comments, documentation, and string content, with strict enforcement except for technical terms and variable names
Files:
tests/unit/imageUtils.utils.test.jsscripts/utils/imageUtils.js
**/*.{js,ts,tsx,jsx,py,java,cs,rb,go,swift,kt,php}
📄 CodeRabbit inference engine (.agents/.shared/knowledge/linter_rules.json)
**/*.{js,ts,tsx,jsx,py,java,cs,rb,go,swift,kt,php}: Do not use magic numbers in code; extract them to constants defined in a dedicated constants module
Use a dedicated constants module for defining and exporting all constant values
Files:
tests/unit/imageUtils.utils.test.jsscripts/utils/imageUtils.js
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.agents/.shared/knowledge/security_rules.json)
**/*.{js,ts,jsx,tsx}: Force strict whitelisting for file downloads: appended files must be restricted to '.json' or '.txt' extensions with explicitly designated MIME types. Never use arbitrary user input for filenames to prevent Reflected File Download (RFD) attacks.
Always prefertextContentfor DOM operations. If SVG mixed content requires text updates, find the TEXT_NODE and update itstextContentrather than usinginnerHTMLon the parent.
Rely onLogSanitizerto strip sensitive properties (likesender,tab, PII) before emitting logs in production.Follow the project's ESLint configuration and do not emit DeepSource (skipcq) comments.
**/*.{js,ts,jsx,tsx}: Reject PR if attempting to log complete sender, tab, request objects or raw HTML content. Require using IDs only (e.g., sender?.id, tab?.id), and wrapping URLs with sanitizeUrlForLogging() and API errors with sanitizeApiError().
Reject PR if attempting to store large HTML content, Base64 images, or heavy payloads into chrome.storage.sync. Require storing large payloads in chrome.storage.local or IndexedDB.
Reject PR if introducing new core business logic without corresponding unit or E2E tests. Require TDD and ensure Codecov checks do not fail (< 50%).
Avoid exposing internal-only, non-actionable, non-blocking cleanup/reconciliation/retry failures directly as user-facing errors. Keep the canonical user-facing state, log the internal failure, and handle retry/recovery inside the relevant service or canonical path unless the failure truly blocks the user's next action.
Verify all Logger.info/error calls correctly sanitize data with no PII and no raw HTML.
Verify cache and large data usage strictly targets chrome.storage.local rather than chrome.storage.sync.
Files:
tests/unit/imageUtils.utils.test.jsscripts/utils/imageUtils.js
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.agents/.shared/knowledge/debugging_rules.json)
**/*.{ts,tsx,js,jsx}: Use Logger.success(msg, ctx?) for operation success confirmation in Notion Chrome Extension. Output prefix: [INFO] ✅. The Logger class auto-manages the emoji icon.
Use Logger.start(msg, ctx?) for process launch notifications in Notion Chrome Extension. Output prefix: [INFO] 🚀. The Logger class auto-manages the emoji icon.
Use Logger.ready(msg, ctx?) for module or resource readiness notifications in Notion Chrome Extension. Output prefix: [INFO] 📦. The Logger class auto-manages the emoji icon.
Use Logger.info(msg, ctx?) for general informational messages in Notion Chrome Extension. Output prefix: [INFO]. No auto-managed emoji icon.
Use Logger.debug(msg, ctx?) for development debugging in Notion Chrome Extension. Output prefix: [DEBUG]. Controlled by enableDebugLogs switch.
Use Logger.warn(msg, ctx?) for non-fatal warnings in Notion Chrome Extension. Output prefix: [WARN]⚠️ . The Logger class auto-manages the emoji icon.
Use Logger.error(msg, ctx?) for error recording in Notion Chrome Extension. Output prefix: [ERROR] ❌. The Logger class auto-manages the emoji icon. Always displayed regardless of debug switch.
Do NOT manually insert emoji icons (✅, 🚀, 📦,⚠️ , ❌) into log messages. The Logger class auto-manages these icons. Only manually use special semantic emoji like 🔍 (search) or 🗑️ (delete) when needed for semantic clarity.
Use 'action' field in Logger context to record public API function names (e.g., 'loadHighlights', 'savePage') for structured logging in Notion Chrome Extension.
Use 'operation' field in Logger context to record internal operation steps (e.g., 'parseLocalStorage', 'buildUrl') for structured logging in Notion Chrome Extension.
Use 'phase' field in Logger context (optional) to record execution stages (e.g., 'validation', 'apiCall') for structured logging in Notion Chrome Extension.
Use 'result' field in Logger context to record operation results (e.g., 'blocked', 'success', 'failed') for structured logging in Notion Chr...
Files:
tests/unit/imageUtils.utils.test.jsscripts/utils/imageUtils.js
**/*.{test,spec}.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.agents/.shared/knowledge/debugging_rules.json)
**/*.{test,spec}.{ts,tsx,js,jsx}: Run failing tests with command 'npx jest <path/to/test.js> --no-coverage' instead of full test suites in Notion Chrome Extension for faster feedback loops during logic fixes.
When mocking objects in Notion Chrome Extension tests, ensure mock object depth is sufficient. For example, mocking an img tag requires including 'dataset: {}' property.
When mocking Logger in Notion Chrome Extension tests, ensure all methods (success, start, ready, info, debug, warn, error) are defined as jest.fn() in the mock setup.
In Notion Chrome Extension tests using ErrorHandler.formatUserMessage, target assertions against Chinese strings (user-facing messages) rather than original English API messages for I18N awareness.
In Notion Chrome Extension tests, avoid vague string matching for Logger assertions. Use 'expect.objectContaining' to assert exact expected metadata objects for Logger calls.
Files:
tests/unit/imageUtils.utils.test.js
**/*.{test,spec}.{js,ts}
📄 CodeRabbit inference engine (.agents/.shared/knowledge/testing_rules.json)
**/*.{test,spec}.{js,ts}: Always reference data structures (HTML snippets, Notion JSON responses) from .agents/.shared/references/ instead of hardcoding large payloads in test files.
Use .agents/.shared/references/notion_api/ for Notion block structures and database properties in test fixtures.
Do not mutate window.chrome directly. Override global.chrome for mocking Chrome API in tests.
Declare mock variables and spies at the top level and initialize them in beforeEach for Chrome API mocking setup.
Execute jest.clearAllMocks() and clean up global.chrome in afterEach to prevent test pollution.
Files:
tests/unit/imageUtils.utils.test.js
**/*.{js,ts,jsx,tsx,md}
📄 CodeRabbit inference engine (.agents/.shared/knowledge/code_review_rules.json)
Maintain code comments and documentation in Traditional Chinese.
Files:
tests/unit/imageUtils.utils.test.jsscripts/utils/imageUtils.js
**/*
⚙️ CodeRabbit configuration file
**/*: # Project Specific Guidelines
- Language: All user-facing UI text, strings, and messages MUST be in Traditional Chinese (zh-TW).
- Architecture: Follow the defined storage structures and specs. Do not introduce unauthorized storage fields.
- Code Quality: Avoid N+1 issues, blocking operations, and ensure high code quality. Suggest optimizations that follow modern JavaScript/Chrome Extension best practices.
Files:
tests/unit/imageUtils.utils.test.jsscripts/utils/imageUtils.js
🧠 Learnings (10)
📓 Common learnings
Learnt from: CR
Repo: cowcfj/save-to-notion PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-26T20:59:55.454Z
Learning: Commit Message 必須使用繁體中文,若變更的檔案僅包含測試程式碼(如 .test.js, .spec.js, tests/ 目錄),則必須使用 `test` 類型,絕不可以使用 `feat` 或 `fix`。
📚 Learning: 2026-03-06T18:35:38.641Z
Learnt from: CR
Repo: cowcfj/save-to-notion PR: 0
File: .agents/.shared/knowledge/debugging_rules.json:0-0
Timestamp: 2026-03-06T18:35:38.641Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js,jsx} : When mocking objects in Notion Chrome Extension tests, ensure mock object depth is sufficient. For example, mocking an img tag requires including 'dataset: {}' property.
Applied to files:
tests/unit/imageUtils.utils.test.js
📚 Learning: 2026-03-06T18:35:38.641Z
Learnt from: CR
Repo: cowcfj/save-to-notion PR: 0
File: .agents/.shared/knowledge/debugging_rules.json:0-0
Timestamp: 2026-03-06T18:35:38.641Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js,jsx} : Run failing tests with command 'npx jest <path/to/test.js> --no-coverage' instead of full test suites in Notion Chrome Extension for faster feedback loops during logic fixes.
Applied to files:
tests/unit/imageUtils.utils.test.js
📚 Learning: 2026-03-10T08:21:13.819Z
Learnt from: CR
Repo: cowcfj/save-to-notion PR: 0
File: .agents/.shared/knowledge/testing_rules.json:0-0
Timestamp: 2026-03-10T08:21:13.819Z
Learning: Applies to **/*.{test,spec}.{js,ts} : Execute jest.clearAllMocks() and clean up global.chrome in afterEach to prevent test pollution.
Applied to files:
tests/unit/imageUtils.utils.test.js
📚 Learning: 2026-03-06T18:35:38.641Z
Learnt from: CR
Repo: cowcfj/save-to-notion PR: 0
File: .agents/.shared/knowledge/debugging_rules.json:0-0
Timestamp: 2026-03-06T18:35:38.641Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js,jsx} : In Notion Chrome Extension tests using ErrorHandler.formatUserMessage, target assertions against Chinese strings (user-facing messages) rather than original English API messages for I18N awareness.
Applied to files:
tests/unit/imageUtils.utils.test.js
📚 Learning: 2026-03-10T08:21:13.819Z
Learnt from: CR
Repo: cowcfj/save-to-notion PR: 0
File: .agents/.shared/knowledge/testing_rules.json:0-0
Timestamp: 2026-03-10T08:21:13.819Z
Learning: Applies to **/{storage,migration,highlight}/**/*.{js,ts} : Achieve 90%+ code coverage for Storage, Migration, and Highlighting algorithms in unit tests.
Applied to files:
tests/unit/imageUtils.utils.test.js
📚 Learning: 2026-03-06T18:35:38.641Z
Learnt from: CR
Repo: cowcfj/save-to-notion PR: 0
File: .agents/.shared/knowledge/debugging_rules.json:0-0
Timestamp: 2026-03-06T18:35:38.641Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js,jsx} : In Notion Chrome Extension tests, avoid vague string matching for Logger assertions. Use 'expect.objectContaining' to assert exact expected metadata objects for Logger calls.
Applied to files:
tests/unit/imageUtils.utils.test.js
📚 Learning: 2026-02-26T20:59:55.454Z
Learnt from: CR
Repo: cowcfj/save-to-notion PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-26T20:59:55.454Z
Learning: Applies to **/*.test.js : 單元測試邏輯使用 Jest,需要使用 Mocking patterns。
Applied to files:
tests/unit/imageUtils.utils.test.js
📚 Learning: 2026-03-23T04:16:46.480Z
Learnt from: CR
Repo: cowcfj/save-to-notion PR: 0
File: .agents/.shared/knowledge/code_review_rules.json:0-0
Timestamp: 2026-03-23T04:16:46.480Z
Learning: Applies to **/*.{js,ts,jsx,tsx} : Verify all Logger.info/error calls correctly sanitize data with no PII and no raw HTML.
Applied to files:
scripts/utils/imageUtils.js
📚 Learning: 2026-03-23T04:16:46.480Z
Learnt from: CR
Repo: cowcfj/save-to-notion PR: 0
File: .agents/.shared/knowledge/code_review_rules.json:0-0
Timestamp: 2026-03-23T04:16:46.480Z
Learning: Applies to **/*.{js,ts,jsx,tsx} : Reject PR if attempting to log complete sender, tab, request objects or raw HTML content. Require using IDs only (e.g., sender?.id, tab?.id), and wrapping URLs with sanitizeUrlForLogging() and API errors with sanitizeApiError().
Applied to files:
scripts/utils/imageUtils.js
🔇 Additional comments (3)
scripts/utils/imageUtils.js (2)
165-174: CDN 嵌入式編碼 URL 的保留策略正確。這段 bypass 能避免
decodeURI/encodeURI破壞%3A%2F%2F路徑語義,且仍保留 Notion/Markdown 敏感字元編碼,方向正確。
896-904:srcset先驗證再回退的控制流合理。這能直接攔截 Substack/Cloudinary 逗號截斷片段,降低錯誤 URL 被優先採用的機率。
tests/unit/imageUtils.utils.test.js (1)
70-102: CDN 嵌入式 percent-encoded URL 的測試覆蓋完整且到位。這組案例有對齊實際回歸風險(保留
%3A%2F%2F、Markdown 敏感字元編碼、clean 後有效性檢查)。
當 srcset 為 protocol-relative URL 且含逗號導致截斷時,修復 _isPlausibleImageUrl 函數以識別合法的 protocol-relative URL(以 // 開頭),並回退到完整的 src 屬性。
|



Pull Request
📋 變更描述
修復 Substack 和 Cloudinary CDN 圖片 URL 在處理過程中因 percent-encoded URL 而導致的顯示問題。
🎯 變更類型
🤖 提交者聲明
✅ 提交前檢查清單
feat:,fix:,refactor:),以確保release-please正常分析版本號。PR_WORKFLOW.md中的測試規範、代碼規範與安全性指引。🔗 相關 Issue
Closes #
變更
_normalizeUrlInternal函數以處理特定的 CDN URL 格式。測試