Merge pull request 'refactor(client): вставка markdown через канонический пакет + удаление md-слоя editor-ext (#347)' (#498) from refactor/347-client-md-paste into develop

Reviewed-on: #498
This commit was merged in pull request #498.
This commit is contained in:
2026-07-11 04:33:37 +03:00
56 changed files with 1081 additions and 2115 deletions
@@ -1,4 +1,5 @@
import { markdownToHtml, encodeHtmlEmbedSource } from '@docmost/editor-ext';
import { markdownToProseMirror } from '@docmost/prosemirror-markdown';
import { encodeHtmlEmbedSource } from '@docmost/editor-ext';
import { htmlToJson } from '../../../collaboration/collaboration.util';
import { hasHtmlEmbedNode, stripHtmlEmbedNodes } from './html-embed.util';
@@ -10,13 +11,12 @@ import { hasHtmlEmbedNode, stripHtmlEmbedNodes } from './html-embed.util';
*
* The block renders inside a sandboxed iframe, so this is not an XSS surface;
* this exercises the REAL server import conversion path that ImportService uses
* (`markdownToHtml` then `htmlToJson`; `processHTML` adds only a cheerio
* link/iframe normalize pass which does not touch htmlEmbed divs) and asserts
* that such a node is DETECTED and STRIPPABLE — so the share read path's
* (`markdownToProseMirror`, the canonical converter — issue #345/#347) and
* asserts that such a node is DETECTED and STRIPPABLE — so the share read path's
* master-toggle strip can remove it when the workspace toggle is OFF.
*/
describe('htmlEmbed smuggled via the raw serialized div in imported markdown/HTML', () => {
it('round-trips through markdownToHtml -> htmlToJson and is DETECTED (base64 data-source)', async () => {
it('round-trips through markdownToProseMirror and is DETECTED (base64 data-source)', async () => {
const source = '<script>steal()</script>';
const encoded = encodeHtmlEmbedSource(source);
const md = [
@@ -27,12 +27,9 @@ describe('htmlEmbed smuggled via the raw serialized div in imported markdown/HTM
'World',
].join('\n');
const html = await markdownToHtml(md);
// marked preserves the raw block-level div verbatim.
expect(html).toContain('data-type="htmlEmbed"');
const json = htmlToJson(html);
// The div parses into a real htmlEmbed node carrying the decoded source.
// The canonical importer parses the raw block-level div into a real
// htmlEmbed node carrying the decoded source.
const json = await markdownToProseMirror(md);
expect(hasHtmlEmbedNode(json)).toBe(true);
// Because it is detected, the share master-toggle strip can remove it.
@@ -59,8 +56,7 @@ describe('htmlEmbed smuggled via the raw serialized div in imported markdown/HTM
// therefore stripping) does not depend on the source being well-formed, so
// the bypass cannot be hidden by sending a malformed data-source.
const md = `<div data-type="htmlEmbed" data-source="&lt;script&gt;x&lt;/script&gt;"></div>`;
const html = await markdownToHtml(md);
const json = htmlToJson(html);
const json = await markdownToProseMirror(md);
expect(hasHtmlEmbedNode(json)).toBe(true);
expect(hasHtmlEmbedNode(stripHtmlEmbedNodes(json))).toBe(false);
});
@@ -238,8 +238,9 @@ function convertReferenceFootnotes(markdown: string): string {
*
* LINE-ANCHORED (the same shape the canonical parser uses in
* prosemirror-markdown/page-file.ts): the block opens only on `---\n` at the
* very start and closes only on a `\n---` line. The retired `markdownToHtml`
* strip closed on the FIRST `---` ANYWHERE (an unanchored close), so a value
* very start and closes only on a `\n---` line. The retired editor-ext
* `markdownToHtml` front-matter strip (removed in #347) closed on the FIRST
* `---` ANYWHERE (an unanchored close), so a value
* containing a triple-dash (e.g. `title: Q1 --- Q2`) truncated the front-matter
* and leaked the rest into the body. An optional leading BOM is tolerated.
*/