@rathkit/rte-core 0.1.0-alpha.4 → 0.1.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/autoformat/autoformat.js +18 -3
- package/dist/base64.js +5 -1
- package/dist/dom/index.d.ts +1 -1
- package/dist/dom/render.js +4 -4
- package/dist/dom/view.d.ts +54 -3
- package/dist/dom/view.js +163 -24
- package/dist/edit.d.ts +6 -0
- package/dist/edit.js +96 -9
- package/dist/engine.d.ts +19 -3
- package/dist/engine.js +96 -22
- package/dist/export.js +1 -1
- package/dist/html/parse.d.ts +8 -2
- package/dist/html/parse.js +6 -2
- package/dist/images/plugin.js +2 -1
- package/dist/images/view.d.ts +1 -1
- package/dist/images/view.js +4 -4
- package/dist/import.d.ts +18 -2
- package/dist/import.js +35 -27
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/lists/lists.js +14 -4
- package/dist/markdown/export.js +1 -1
- package/dist/markdown/inline.d.ts +2 -0
- package/dist/markdown/inline.js +1 -1
- package/dist/markdown/plugin.js +4 -1
- package/dist/model.d.ts +5 -0
- package/dist/paste.js +9 -2
- package/dist/path.d.ts +7 -0
- package/dist/path.js +20 -7
- package/dist/plugin.d.ts +1 -1
- package/dist/policy.d.ts +41 -0
- package/dist/policy.js +40 -0
- package/dist/standard/index.d.ts +0 -1
- package/dist/standard/index.js +0 -1
- package/dist/stats.js +1 -1
- package/dist/svg/svg.js +9 -1
- package/dist/tables/cells.d.ts +1 -1
- package/dist/tables/cells.js +3 -3
- package/dist/tables/grips.js +2 -2
- package/dist/warning.d.ts +16 -0
- package/dist/warning.js +5 -0
- package/package.json +1 -1
package/dist/import.js
CHANGED
|
@@ -1,24 +1,16 @@
|
|
|
1
1
|
import { decodeEntities } from "./html/entities.js";
|
|
2
2
|
import { parseHtml } from "./html/parse.js";
|
|
3
|
+
import { CONTENT_PROFILES, DATA_IMAGE_SIGNATURES, DEFAULT_LINK_SCHEMES, DROPPED_ELEMENTS, GROUPING_ELEMENTS, IMAGE_SCHEMES } from "./policy.js";
|
|
3
4
|
import { parseLine, SIDES, withSides } from "./cellborder.js";
|
|
4
5
|
import { COLOR_VALUE, COLUMN_WIDTH, MAX_INDENT, NOTE_ID, ROW_HEIGHT, inlineText, normalizeInline, } from "./model.js";
|
|
5
6
|
import { asChildren } from "./path.js";
|
|
6
7
|
import { DEFAULT_MARKER } from "./model.js";
|
|
7
|
-
const DROP =
|
|
8
|
-
|
|
9
|
-
"head", "textarea", "input", "select", "option", "button", "canvas", "audio", "video", "applet", "xml",
|
|
10
|
-
]);
|
|
11
|
-
const CONTAINERS = new Set([
|
|
12
|
-
"div", "section", "article", "header", "footer", "aside", "nav", "figure", "figcaption", "address", "main", "form",
|
|
13
|
-
"body", "html", "center", "details", "summary", "dd", "dt", "fieldset", "hgroup",
|
|
14
|
-
]);
|
|
8
|
+
const DROP = DROPPED_ELEMENTS;
|
|
9
|
+
const CONTAINERS = GROUPING_ELEMENTS;
|
|
15
10
|
const MARK_TAGS = {
|
|
16
11
|
strong: "bold", b: "bold", em: "italic", i: "italic", cite: "italic", dfn: "italic", var: "italic", u: "underline", ins: "underline",
|
|
17
12
|
s: "strike", strike: "strike", del: "strike", code: "code", tt: "code", kbd: "code", samp: "code", sup: "sup", sub: "sub",
|
|
18
13
|
};
|
|
19
|
-
const RASTER = [
|
|
20
|
-
["image/png", "iVBORw0KGgo"], ["image/jpeg", "/9j/"], ["image/gif", "R0lGOD"], ["image/webp", "UklGR"], ["image/bmp", "Qk"],
|
|
21
|
-
];
|
|
22
14
|
const COLOR = COLOR_VALUE;
|
|
23
15
|
export function parseStyle(style) {
|
|
24
16
|
const out = new Map();
|
|
@@ -78,9 +70,11 @@ function safeImageSrc(raw, settings) {
|
|
|
78
70
|
if (!match)
|
|
79
71
|
return undefined;
|
|
80
72
|
const body = match[2];
|
|
81
|
-
return
|
|
73
|
+
return DATA_IMAGE_SIGNATURES.some(([, magic]) => body.startsWith(magic)) ? value : undefined;
|
|
82
74
|
}
|
|
83
|
-
|
|
75
|
+
if (!settings.relative && !/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i.test(value.replace(INVISIBLE, "")))
|
|
76
|
+
return undefined;
|
|
77
|
+
return safeUrl(value, new Set(IMAGE_SCHEMES));
|
|
84
78
|
}
|
|
85
79
|
class Sink {
|
|
86
80
|
blocks = [];
|
|
@@ -508,7 +502,7 @@ function convertList(el, ctx, settings, notes = false) {
|
|
|
508
502
|
continue;
|
|
509
503
|
if (child.tag === "ul" || child.tag === "ol") {
|
|
510
504
|
const nested = convertList(child, ctx, settings);
|
|
511
|
-
const last = list.items[list.items.length - 1] ?? list.items[list.items.push({ content: [], children: [] }) - 1];
|
|
505
|
+
const last = list.items[list.items.length - 1] ?? list.items[list.items.push({ content: [], children: [], bare: true }) - 1];
|
|
512
506
|
last.children.push(nested);
|
|
513
507
|
continue;
|
|
514
508
|
}
|
|
@@ -548,6 +542,8 @@ function convertList(el, ctx, settings, notes = false) {
|
|
|
548
542
|
if (id !== undefined)
|
|
549
543
|
item.id = id;
|
|
550
544
|
}
|
|
545
|
+
if (item.content.length === 0 && !task && item.children.length > 0 && /list-style(-type)?\s*:\s*none/i.test(child.attrs.style ?? ""))
|
|
546
|
+
item.bare = true;
|
|
551
547
|
const level = levelOf(child.attrs["data-aria-level"] ?? child.attrs["aria-level"]);
|
|
552
548
|
if (level !== undefined)
|
|
553
549
|
item.level = level;
|
|
@@ -675,7 +671,7 @@ function rowHeights(rowEls, kept) {
|
|
|
675
671
|
return heights.some((h) => h !== null) ? heights : undefined;
|
|
676
672
|
}
|
|
677
673
|
function columnWidths(table, rows) {
|
|
678
|
-
const cols =
|
|
674
|
+
const cols = rows.reduce((most, row) => Math.max(most, row.reduce((sum, cell) => sum + cell.colspan, 0)), 0);
|
|
679
675
|
const widths = [];
|
|
680
676
|
const visit = (nodes) => {
|
|
681
677
|
for (const n of nodes) {
|
|
@@ -696,7 +692,7 @@ function columnWidths(table, rows) {
|
|
|
696
692
|
if (firstRow)
|
|
697
693
|
for (const cell of firstRow.children)
|
|
698
694
|
if (cell.type === "el" && (cell.tag === "td" || cell.tag === "th")) {
|
|
699
|
-
const span = Math.max(Number.parseInt(cell.attrs.colspan ?? "1", 10) || 1, 1);
|
|
695
|
+
const span = Math.min(Math.max(Number.parseInt(cell.attrs.colspan ?? "1", 10) || 1, 1), 100);
|
|
700
696
|
if (span === 1)
|
|
701
697
|
widths.push(widthOf(cell));
|
|
702
698
|
else
|
|
@@ -731,7 +727,7 @@ function padRows(rows) {
|
|
|
731
727
|
reach[k] = reach[k] - 1;
|
|
732
728
|
return used;
|
|
733
729
|
});
|
|
734
|
-
const widest = Math.max(
|
|
730
|
+
const widest = widths.reduce((most, w) => Math.max(most, w), 0);
|
|
735
731
|
return rows.map((cells, i) => {
|
|
736
732
|
const missing = widest - widths[i];
|
|
737
733
|
return missing > 0 ? [...cells, ...Array.from({ length: missing }, () => ({ header: false, colspan: 1, rowspan: 1, blocks: [{ type: "p", content: [] }] }))] : cells;
|
|
@@ -781,7 +777,7 @@ function nestByLevel(lists) {
|
|
|
781
777
|
const fresh = { type: list.type, items: [] };
|
|
782
778
|
const parent = stack[stack.length - 1];
|
|
783
779
|
if (parent) {
|
|
784
|
-
const host = parent.items[parent.items.length - 1] ?? parent.items[parent.items.push({ content: [], children: [] }) - 1];
|
|
780
|
+
const host = parent.items[parent.items.length - 1] ?? parent.items[parent.items.push({ content: [], children: [], bare: true }) - 1];
|
|
785
781
|
host.children.push(fresh);
|
|
786
782
|
}
|
|
787
783
|
else {
|
|
@@ -800,7 +796,7 @@ function nestByLevel(lists) {
|
|
|
800
796
|
return result.map((l) => freezeList(l, 0));
|
|
801
797
|
}
|
|
802
798
|
function freezeList(list, depth) {
|
|
803
|
-
const items = list.items.map((item) => ({ content: item.content, children: item.children.map((child) => (child.type === "ul" || child.type === "ol" ? freezeList(child, depth + 1) : child)), ...(item.checked !== undefined ? { checked: item.checked } : {}), ...(item.id !== undefined ? { id: item.id } : {}) }));
|
|
799
|
+
const items = list.items.map((item) => ({ content: item.content, children: item.children.map((child) => (child.type === "ul" || child.type === "ol" ? freezeList(child, depth + 1) : child)), ...(item.checked !== undefined ? { checked: item.checked } : {}), ...(item.id !== undefined ? { id: item.id } : {}), ...(item.bare ? { bare: true } : {}) }));
|
|
804
800
|
const style = list.style !== undefined && list.style !== DEFAULT_MARKER(list.type, depth) ? list.style : undefined;
|
|
805
801
|
return { type: list.type, ...(list.notes ? { notes: true } : {}), ...(list.start !== undefined ? { start: list.start } : {}), ...(style !== undefined ? { style } : {}), items };
|
|
806
802
|
}
|
|
@@ -821,17 +817,29 @@ function repairRewrittenSpaces(nodes) {
|
|
|
821
817
|
export function importBlocks(html, options = {}) {
|
|
822
818
|
const settings = {
|
|
823
819
|
allowStyles: (options.styles ?? "allowlist") === "allowlist",
|
|
824
|
-
schemes: new Set(options.linkSchemes ??
|
|
820
|
+
schemes: new Set(options.linkSchemes ?? DEFAULT_LINK_SCHEMES),
|
|
825
821
|
keepEmpty: options.keepEmptyBlocks ?? false,
|
|
826
|
-
maxData: options.maxDataImageLength ??
|
|
822
|
+
maxData: options.maxDataImageLength ?? CONTENT_PROFILES.standard.maxDataImageLength,
|
|
823
|
+
relative: options.relativeImages ?? true,
|
|
827
824
|
svg: options.sanitizeSvg,
|
|
828
825
|
recognizers: options.recognizers ?? [],
|
|
829
826
|
};
|
|
830
|
-
const
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
827
|
+
const maxInput = options.maxInputLength ?? CONTENT_PROFILES.standard.maxInputLength;
|
|
828
|
+
if (html.length > maxInput) {
|
|
829
|
+
options.onLimit?.({ limit: "maxInputLength", max: maxInput, length: html.length });
|
|
830
|
+
return [];
|
|
831
|
+
}
|
|
832
|
+
try {
|
|
833
|
+
const sink = new Sink(settings);
|
|
834
|
+
const tree = parseHtml(html, options.maxDepth ?? CONTENT_PROFILES.standard.maxDepth);
|
|
835
|
+
convertNodes(/-webkit-text-stroke-width/.test(html) ? repairRewrittenSpaces(tree) : tree, sink, { marks: [], inLink: false, pre: false }, settings);
|
|
836
|
+
sink.flush();
|
|
837
|
+
return finishBlocks(sink.blocks);
|
|
838
|
+
}
|
|
839
|
+
catch (error) {
|
|
840
|
+
options.onError?.(error);
|
|
841
|
+
return [];
|
|
842
|
+
}
|
|
835
843
|
}
|
|
836
844
|
export function importHtml(html, options = {}) {
|
|
837
845
|
const blocks = importBlocks(html, { keepEmptyBlocks: true, ...options });
|
|
@@ -854,4 +862,4 @@ export function importText(value) {
|
|
|
854
862
|
}
|
|
855
863
|
const decodeLineBreaks = (value) => value.replace(/\r\n?|\u2028|\u2029/g, "\n").replace(/^\n+|\n+$/g, "");
|
|
856
864
|
export { decodeEntities };
|
|
857
|
-
export const checkImageSrc = (src, maxDataLength = 12_000_000, sanitizeSvg) => safeImageSrc(src, { maxData: maxDataLength, svg: sanitizeSvg });
|
|
865
|
+
export const checkImageSrc = (src, maxDataLength = 12_000_000, sanitizeSvg, relative = true) => safeImageSrc(src, { maxData: maxDataLength, svg: sanitizeSvg, relative });
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
export declare const PACKAGE_NAME: "@rathkit/rte-core";
|
|
2
1
|
export * from "./model.ts";
|
|
3
2
|
export { checkImageSrc, importBlocks, importHtml, importText, safeUrl, parseStyle, type ImportOptions } from "./import.ts";
|
|
3
|
+
export { WARNING_CODES, type Warning, type WarningCode } from "./warning.ts";
|
|
4
|
+
export { CONTENT_PROFILES, contentPolicy, type ContentLimits, type ContentOptions, type ContentPolicy, type ContentProfile } from "./policy.ts";
|
|
4
5
|
export { exportBlock, exportHtml, exportInline } from "./export.ts";
|
|
5
6
|
export * from "./path.ts";
|
|
6
7
|
export * from "./edit.ts";
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
export const PACKAGE_NAME = "@rathkit/rte-core";
|
|
2
1
|
export * from "./model.js";
|
|
3
2
|
export { checkImageSrc, importBlocks, importHtml, importText, safeUrl, parseStyle } from "./import.js";
|
|
3
|
+
export { WARNING_CODES } from "./warning.js";
|
|
4
|
+
export { CONTENT_PROFILES, contentPolicy } from "./policy.js";
|
|
4
5
|
export { exportBlock, exportHtml, exportInline } from "./export.js";
|
|
5
6
|
export * from "./path.js";
|
|
6
7
|
export * from "./edit.js";
|
package/dist/lists/lists.js
CHANGED
|
@@ -68,7 +68,7 @@ function splitList(list, path, liftChildren) {
|
|
|
68
68
|
const item = list.items[i];
|
|
69
69
|
const numbering = (count) => (list.type === "ol" ? { start: (list.start ?? 1) + count } : {});
|
|
70
70
|
if (k === undefined) {
|
|
71
|
-
const placeholder = item.children.length > 0 && !liftChildren ? [{ content: [], children: item.children }] : [];
|
|
71
|
+
const placeholder = item.children.length > 0 && !liftChildren ? [{ content: [], children: item.children, bare: true }] : [];
|
|
72
72
|
const rest = [...placeholder, ...list.items.slice(i + 1)];
|
|
73
73
|
return {
|
|
74
74
|
before: i > 0 ? { ...list, items: list.items.slice(0, i) } : undefined,
|
|
@@ -80,7 +80,7 @@ function splitList(list, path, liftChildren) {
|
|
|
80
80
|
const inner = splitList(item.children[k], path.slice(2), liftChildren);
|
|
81
81
|
const keptSublists = [...item.children.slice(0, k), ...(inner.before ? [inner.before] : [])];
|
|
82
82
|
const afterSublists = [...(inner.after ? [inner.after] : []), ...item.children.slice(k + 1)];
|
|
83
|
-
const rest = [...(afterSublists.length > 0 ? [{ content: [], children: afterSublists }] : []), ...list.items.slice(i + 1)];
|
|
83
|
+
const rest = [...(afterSublists.length > 0 ? [{ content: [], children: afterSublists, bare: true }] : []), ...list.items.slice(i + 1)];
|
|
84
84
|
return {
|
|
85
85
|
before: { ...list, items: [...list.items.slice(0, i), { content: item.content, children: keptSublists }] },
|
|
86
86
|
content: inner.content,
|
|
@@ -271,7 +271,7 @@ export function indentItems(doc, sel, maxDepth = Number.POSITIVE_INFINITY) {
|
|
|
271
271
|
const rest = list.items.slice(index + group.length);
|
|
272
272
|
if (previous)
|
|
273
273
|
return { ...list, items: [...list.items.slice(0, index - 1), nested(previous), ...rest] };
|
|
274
|
-
return { ...list, items: [nested({ content: [], children: [] }), ...rest] };
|
|
274
|
+
return { ...list, items: [nested({ content: [], children: [], bare: true }), ...rest] };
|
|
275
275
|
});
|
|
276
276
|
}
|
|
277
277
|
return { doc: next, selection: carry(doc, next, sel) };
|
|
@@ -286,6 +286,10 @@ export function outdentItems(doc, sel) {
|
|
|
286
286
|
if (!loc)
|
|
287
287
|
continue;
|
|
288
288
|
if (loc.item.length === 1) {
|
|
289
|
+
const here = listOfItem(next, path);
|
|
290
|
+
const held = here?.list.items[here.index];
|
|
291
|
+
if (held && held.content.length === 0 && held.children.length > 0)
|
|
292
|
+
continue;
|
|
289
293
|
next = takeOutOfList(next, path, true);
|
|
290
294
|
continue;
|
|
291
295
|
}
|
|
@@ -300,6 +304,7 @@ export function outdentItems(doc, sel) {
|
|
|
300
304
|
const keptSub = itemIndex > 0 ? [{ ...sub, items: sub.items.slice(0, itemIndex) }] : [];
|
|
301
305
|
const lifted = {
|
|
302
306
|
content: moved.content,
|
|
307
|
+
...(moved.bare ? { bare: true } : {}),
|
|
303
308
|
children: [...moved.children, ...(followers.length > 0 ? [{ type: sub.type, ...(sub.style !== undefined ? { style: sub.style } : {}), items: followers }] : [])],
|
|
304
309
|
};
|
|
305
310
|
const children = [...parent.children.slice(0, sublistIndex), ...keptSub, ...parent.children.slice(sublistIndex + 1)];
|
|
@@ -355,6 +360,11 @@ export function backspaceAtItemStart(doc, pos) {
|
|
|
355
360
|
const held = listOfItem(doc, pos.path);
|
|
356
361
|
if (held?.list.items[held.index]?.checked !== undefined)
|
|
357
362
|
return { doc: setChecked(doc, pos.path, undefined), selection: caret(pos) };
|
|
363
|
+
const only = held?.list.items[held.index];
|
|
364
|
+
if (only && only.content.length === 0 && only.bare !== true && (only.children[0]?.type === "ul" || only.children[0]?.type === "ol")) {
|
|
365
|
+
const next = mapListAt(doc, pos.path, (list, index) => ({ ...list, items: list.items.map((item, i) => (i === index ? { ...item, bare: true } : item)) }));
|
|
366
|
+
return { doc: next, selection: caret(pos) };
|
|
367
|
+
}
|
|
358
368
|
if (!held || held.index !== 0)
|
|
359
369
|
return undefined;
|
|
360
370
|
return leaveListItem(doc, pos);
|
|
@@ -375,7 +385,7 @@ export function splitListItem(doc, pos) {
|
|
|
375
385
|
...list,
|
|
376
386
|
items: list.items.flatMap((candidate, i) => (i !== index ? [candidate] : [
|
|
377
387
|
{ content: before, children: [], ...(item.checked !== undefined ? { checked: item.checked } : {}), ...(item.id !== undefined ? { id: item.id } : {}) },
|
|
378
|
-
{ content: after, children: item.children, ...(item.checked !== undefined ? { checked: false } : {}) },
|
|
388
|
+
{ content: after, children: item.children, ...(item.checked !== undefined ? { checked: false } : {}), ...(item.bare && after.length === 0 ? { bare: true } : {}) },
|
|
379
389
|
])),
|
|
380
390
|
}));
|
|
381
391
|
return { doc: next, selection: caret({ path: containerPaths(next)[ordinal + 1] ?? pos.path, offset: 0 }) };
|
package/dist/markdown/export.js
CHANGED
|
@@ -66,7 +66,7 @@ function inline(content, flat, table) {
|
|
|
66
66
|
return;
|
|
67
67
|
}
|
|
68
68
|
const lead = /^\s*/.exec(text)?.[0] ?? "";
|
|
69
|
-
const trail = lead.length === text.length ? "" :
|
|
69
|
+
const trail = lead.length === text.length ? "" : text.slice(text.trimEnd().length);
|
|
70
70
|
const core = text.slice(lead.length, text.length - trail.length);
|
|
71
71
|
const prev = runs[r - 1];
|
|
72
72
|
const next = runs[r + 1];
|
|
@@ -6,6 +6,8 @@ export interface MarkdownOptions {
|
|
|
6
6
|
readonly sanitizeSvg?: ((dataUrl: string) => string | undefined) | undefined;
|
|
7
7
|
/** Largest `data:` image address, in characters. */
|
|
8
8
|
readonly maxDataImageLength?: number;
|
|
9
|
+
/** Keep an image whose address is relative. Default true. */
|
|
10
|
+
readonly relativeImages?: boolean;
|
|
9
11
|
}
|
|
10
12
|
/** Link reference definitions found in the document: `[label]: address "title"`. Keys come from `refLabel`. */
|
|
11
13
|
export type Refs = ReadonlyMap<string, {
|
package/dist/markdown/inline.js
CHANGED
|
@@ -331,7 +331,7 @@ export function parseInline(src, options = {}) {
|
|
|
331
331
|
}
|
|
332
332
|
p.dead = true;
|
|
333
333
|
}
|
|
334
|
-
const safe = checkImageSrc(decodeEntities(tailInfo.dest), options.maxDataImageLength, options.sanitizeSvg);
|
|
334
|
+
const safe = checkImageSrc(decodeEntities(tailInfo.dest), options.maxDataImageLength, options.sanitizeSvg, options.relativeImages ?? true);
|
|
335
335
|
const img = top;
|
|
336
336
|
if (safe) {
|
|
337
337
|
Object.assign(img, { k: "img", src: safe, alt });
|
package/dist/markdown/plugin.js
CHANGED
|
@@ -23,7 +23,7 @@ export function markdown(options = {}) {
|
|
|
23
23
|
return false;
|
|
24
24
|
const { doc, selection } = ctx.snapshot;
|
|
25
25
|
const blocks = host.ops.isCollapsed(selection) ? doc.blocks : host.ops.extractSelection(doc, selection);
|
|
26
|
-
clipboard.writeText(exportMarkdown({ blocks }, { inlineHtml: options.inlineHtml === true })).catch((error) => host.editor.
|
|
26
|
+
clipboard.writeText(exportMarkdown({ blocks }, { inlineHtml: options.inlineHtml === true })).catch((error) => host.editor.report({ code: "copy-failed", plugin: "rathkit.markdown", message: `Copy as Markdown failed: ${error instanceof Error ? error.message : String(error)}`, error }));
|
|
27
27
|
return true;
|
|
28
28
|
},
|
|
29
29
|
});
|
|
@@ -39,12 +39,15 @@ export function markdown(options = {}) {
|
|
|
39
39
|
const path = selection.focus.path;
|
|
40
40
|
if (kindAt(doc, path) === "pre" || getBlock(doc, path)?.type === "pre")
|
|
41
41
|
return undefined;
|
|
42
|
+
if (text.length > (host.editor.importOptions.maxInputLength ?? Infinity))
|
|
43
|
+
return undefined;
|
|
42
44
|
if (mode === "detect" && !looksLikeMarkdown(text))
|
|
43
45
|
return undefined;
|
|
44
46
|
const blocks = parseMarkdown(text, {
|
|
45
47
|
...(options.linkSchemes ? { linkSchemes: options.linkSchemes } : {}),
|
|
46
48
|
...(options.maxDataImageLength !== undefined ? { maxDataImageLength: options.maxDataImageLength } : {}),
|
|
47
49
|
sanitizeSvg: host.editor.importOptions.sanitizeSvg,
|
|
50
|
+
relativeImages: host.editor.importOptions.pasteRelativeImages ?? false,
|
|
48
51
|
});
|
|
49
52
|
return blocks.length > 0 ? blocks : undefined;
|
|
50
53
|
});
|
package/dist/model.d.ts
CHANGED
|
@@ -76,6 +76,11 @@ export interface ListItem {
|
|
|
76
76
|
readonly checked?: boolean;
|
|
77
77
|
/** In a notes list: the id of the reference this note belongs to. */
|
|
78
78
|
readonly id?: string;
|
|
79
|
+
/**
|
|
80
|
+
* Set on an item that has no text and holds only a nested list, and shows no marker of its own: the nested list sits under it
|
|
81
|
+
* without a bullet or number above it. Without this an item with no text still shows its marker. Ignored once the item has text.
|
|
82
|
+
*/
|
|
83
|
+
readonly bare?: true;
|
|
79
84
|
}
|
|
80
85
|
/** What a list item may hold besides its own text. Tables and definition lists are not allowed in an item. */
|
|
81
86
|
export type ItemChild = List | Paragraph | Heading | CodeBlock | Quote | Rule;
|
package/dist/paste.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { importBlocks, importText } from "./import.js";
|
|
2
|
+
import { CONTENT_PROFILES } from "./policy.js";
|
|
2
3
|
const START = "<!--StartFragment-->";
|
|
3
4
|
const END = "<!--EndFragment-->";
|
|
4
5
|
export function extractFragment(html) {
|
|
@@ -17,11 +18,17 @@ function unwrapSingleCell(blocks) {
|
|
|
17
18
|
}
|
|
18
19
|
export function defaultPaste(data, importOptions = {}) {
|
|
19
20
|
if (data.html && data.html.trim() !== "") {
|
|
20
|
-
const blocks = importBlocks(data.html, { ...importOptions, styles: "semantic" });
|
|
21
|
+
const blocks = importBlocks(data.html, { ...importOptions, styles: "semantic", relativeImages: importOptions.pasteRelativeImages ?? false });
|
|
21
22
|
if (blocks.length > 0)
|
|
22
23
|
return unwrapSingleCell(blocks);
|
|
23
24
|
}
|
|
24
|
-
|
|
25
|
+
const text = data.text ?? "";
|
|
26
|
+
const max = importOptions.maxInputLength ?? CONTENT_PROFILES.standard.maxInputLength;
|
|
27
|
+
if (text.length > max) {
|
|
28
|
+
importOptions.onLimit?.({ limit: "maxInputLength", max, length: text.length });
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
return importText(text);
|
|
25
32
|
}
|
|
26
33
|
export function pasteBlocks(data, handlers = [], importOptions = {}) {
|
|
27
34
|
for (const handler of handlers) {
|
package/dist/path.d.ts
CHANGED
|
@@ -21,11 +21,18 @@ export declare const pathKey: (path: Path) => string;
|
|
|
21
21
|
* nested under it, rows run left to right then top to bottom, and so on. Negative when `a` comes first.
|
|
22
22
|
*/
|
|
23
23
|
export declare function comparePath(a: Path, b: Path): number;
|
|
24
|
+
/** A horizontal rule: where it is, and how many containers come before it in reading order. */
|
|
25
|
+
export interface RuleAt {
|
|
26
|
+
readonly path: Path;
|
|
27
|
+
readonly before: number;
|
|
28
|
+
}
|
|
24
29
|
/**
|
|
25
30
|
* Every container in the document, in reading order. Documents are never changed once made, so the answer is remembered
|
|
26
31
|
* per document. The array is shared: do not change it.
|
|
27
32
|
*/
|
|
28
33
|
export declare function containerPaths(doc: Doc): Path[];
|
|
34
|
+
/** Every horizontal rule, in reading order, with the number of containers before it. A rule between container n-1 and n has `before` n. The array is shared. */
|
|
35
|
+
export declare function rulePaths(doc: Doc): RuleAt[];
|
|
29
36
|
export declare function blockContainers(block: Block, prefix: readonly number[]): Path[];
|
|
30
37
|
/** True when the path leads to a container that exists. Cheap: it follows the path instead of listing every container. */
|
|
31
38
|
export declare function hasContainer(doc: Doc, path: Path): boolean;
|
package/dist/path.js
CHANGED
|
@@ -11,13 +11,13 @@ export function comparePath(a, b) {
|
|
|
11
11
|
}
|
|
12
12
|
return a.length - b.length;
|
|
13
13
|
}
|
|
14
|
-
function listPaths(list, prefix, out) {
|
|
14
|
+
function listPaths(list, prefix, out, rules) {
|
|
15
15
|
list.items.forEach((item, i) => {
|
|
16
16
|
out.push([...prefix, i]);
|
|
17
|
-
item.children.forEach((child, k) => blockPaths(child, [...prefix, i, k], out));
|
|
17
|
+
item.children.forEach((child, k) => blockPaths(child, [...prefix, i, k], out, rules));
|
|
18
18
|
});
|
|
19
19
|
}
|
|
20
|
-
function blockPaths(block, prefix, out) {
|
|
20
|
+
function blockPaths(block, prefix, out, rules) {
|
|
21
21
|
switch (block.type) {
|
|
22
22
|
case "p":
|
|
23
23
|
case "h":
|
|
@@ -26,18 +26,20 @@ function blockPaths(block, prefix, out) {
|
|
|
26
26
|
break;
|
|
27
27
|
case "ul":
|
|
28
28
|
case "ol":
|
|
29
|
-
listPaths(block, prefix, out);
|
|
29
|
+
listPaths(block, prefix, out, rules);
|
|
30
30
|
break;
|
|
31
31
|
case "dl":
|
|
32
32
|
block.entries.forEach((_, i) => out.push([...prefix, i]));
|
|
33
33
|
break;
|
|
34
34
|
case "table":
|
|
35
|
-
block.rows.forEach((row, r) => row.forEach((cell, c) => cell.blocks.forEach((inner, n) => blockPaths(inner, [...prefix, r, c, n], out))));
|
|
35
|
+
block.rows.forEach((row, r) => row.forEach((cell, c) => cell.blocks.forEach((inner, n) => blockPaths(inner, [...prefix, r, c, n], out, rules))));
|
|
36
36
|
break;
|
|
37
37
|
case "quote":
|
|
38
|
-
block.blocks.forEach((inner, n) => blockPaths(inner, [...prefix, n], out));
|
|
38
|
+
block.blocks.forEach((inner, n) => blockPaths(inner, [...prefix, n], out, rules));
|
|
39
|
+
break;
|
|
40
|
+
case "hr":
|
|
41
|
+
rules?.push({ path: prefix, before: out.length });
|
|
39
42
|
break;
|
|
40
|
-
case "hr": break;
|
|
41
43
|
}
|
|
42
44
|
}
|
|
43
45
|
const containerCache = new WeakMap();
|
|
@@ -50,6 +52,17 @@ export function containerPaths(doc) {
|
|
|
50
52
|
containerCache.set(doc, out);
|
|
51
53
|
return out;
|
|
52
54
|
}
|
|
55
|
+
const ruleCache = new WeakMap();
|
|
56
|
+
export function rulePaths(doc) {
|
|
57
|
+
const hit = ruleCache.get(doc);
|
|
58
|
+
if (hit)
|
|
59
|
+
return hit;
|
|
60
|
+
const out = [];
|
|
61
|
+
const containers = [];
|
|
62
|
+
doc.blocks.forEach((block, b) => blockPaths(block, [b], containers, out));
|
|
63
|
+
ruleCache.set(doc, out);
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
53
66
|
export function blockContainers(block, prefix) {
|
|
54
67
|
const out = [];
|
|
55
68
|
blockPaths(block, prefix, out);
|
package/dist/plugin.d.ts
CHANGED
|
@@ -69,7 +69,7 @@ export interface FileLike extends BlobLike {
|
|
|
69
69
|
/** Turns pasted or dropped files into blocks, or returns undefined if it does not handle them. */
|
|
70
70
|
export type FileHandler = (files: readonly FileLike[]) => readonly Block[] | undefined | Promise<readonly Block[] | undefined>;
|
|
71
71
|
/** What a plugin sees of the editor: the same object a host gets, nothing more. */
|
|
72
|
-
export type EditorHandle = Pick<Engine, "snapshot" | "apply" | "commands" | "subscribe" | "select" | "keymap" | "warn" | "importOptions" | "ui">;
|
|
72
|
+
export type EditorHandle = Pick<Engine, "snapshot" | "apply" | "commands" | "subscribe" | "select" | "keymap" | "warn" | "report" | "importOptions" | "ui">;
|
|
73
73
|
export interface PluginHost {
|
|
74
74
|
readonly editor: EditorHandle;
|
|
75
75
|
/** The editing operations, so a plugin does not import the editor to use them. */
|
package/dist/policy.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** Elements that are dropped together with everything inside them. */
|
|
2
|
+
export declare const DROPPED_ELEMENTS: ReadonlySet<string>;
|
|
3
|
+
/** Elements that only group blocks: they end the paragraph before and after, and what is inside is kept. */
|
|
4
|
+
export declare const GROUPING_ELEMENTS: ReadonlySet<string>;
|
|
5
|
+
export declare const DEFAULT_LINK_SCHEMES: readonly string[];
|
|
6
|
+
/** Schemes an image address may use. Not configurable: a `data:` address is checked separately. */
|
|
7
|
+
export declare const IMAGE_SCHEMES: readonly string[];
|
|
8
|
+
/** The `data:` image types kept, with the first bytes (base64) their data must start with: the bytes are trusted, not the declared type. */
|
|
9
|
+
export declare const DATA_IMAGE_SIGNATURES: readonly (readonly [string, string])[];
|
|
10
|
+
/** The limits on what is read. */
|
|
11
|
+
export interface ContentLimits {
|
|
12
|
+
/** Longest markup read, in characters. Longer is refused whole: nothing is read and `onLimit` is told. */
|
|
13
|
+
readonly maxInputLength: number;
|
|
14
|
+
/** Deepest nesting of elements. An element nested deeper is dropped and what is inside it stays at the deepest allowed level. */
|
|
15
|
+
readonly maxDepth: number;
|
|
16
|
+
/** Longest `data:` image address, in characters. 0 refuses every `data:` image. */
|
|
17
|
+
readonly maxDataImageLength: number;
|
|
18
|
+
}
|
|
19
|
+
/** A profile: the limits and the choices a host makes. */
|
|
20
|
+
export interface ContentPolicy extends ContentLimits {
|
|
21
|
+
/** URL schemes a link may use besides relative and fragment addresses. */
|
|
22
|
+
readonly linkSchemes: readonly string[];
|
|
23
|
+
/** `semantic` keeps meaning only; `allowlist` also keeps text color, background and alignment. (Pasting is always `semantic`.) */
|
|
24
|
+
readonly styles: "semantic" | "allowlist";
|
|
25
|
+
/** Keep relative image addresses (`a.png`, `/a.png`) when pasting. Loading always keeps them. Default false in every profile. */
|
|
26
|
+
readonly pasteRelativeImages: boolean;
|
|
27
|
+
}
|
|
28
|
+
/** The name of a profile in `CONTENT_PROFILES`. */
|
|
29
|
+
export type ContentProfile = "strict" | "standard" | "loose";
|
|
30
|
+
/** The three profiles. `standard` is the default; `strict` is for hosts that take content from people they do not trust; `loose` for hosts that trust theirs. */
|
|
31
|
+
export declare const CONTENT_PROFILES: Readonly<Record<ContentProfile, ContentPolicy>>;
|
|
32
|
+
/** What a host may say: a profile to start from, then single choices on top of it. */
|
|
33
|
+
export type ContentOptions = {
|
|
34
|
+
readonly profile?: ContentProfile;
|
|
35
|
+
} & Partial<ContentPolicy>;
|
|
36
|
+
/**
|
|
37
|
+
* The policy for the options: the profile (`standard` when none is named), with each given choice laid over it. A choice that
|
|
38
|
+
* cannot be used (an unknown profile, a limit that is not a whole number of at least 0, a list that is not strings) is left out, the
|
|
39
|
+
* profile's value is used, and `complain` is told.
|
|
40
|
+
*/
|
|
41
|
+
export declare function contentPolicy(options?: ContentOptions, complain?: (message: string) => void): ContentPolicy;
|
package/dist/policy.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export const DROPPED_ELEMENTS = new Set([
|
|
2
|
+
"script", "style", "link", "meta", "base", "object", "embed", "iframe", "frame", "frameset", "noscript", "template",
|
|
3
|
+
"head", "textarea", "input", "select", "option", "button", "canvas", "audio", "video", "applet", "xml",
|
|
4
|
+
]);
|
|
5
|
+
export const GROUPING_ELEMENTS = new Set([
|
|
6
|
+
"div", "section", "article", "header", "footer", "aside", "nav", "figure", "figcaption", "address", "main", "form",
|
|
7
|
+
"body", "html", "center", "details", "summary", "dd", "dt", "fieldset", "hgroup",
|
|
8
|
+
]);
|
|
9
|
+
export const DEFAULT_LINK_SCHEMES = ["http", "https", "mailto", "tel"];
|
|
10
|
+
export const IMAGE_SCHEMES = ["http", "https"];
|
|
11
|
+
export const DATA_IMAGE_SIGNATURES = [
|
|
12
|
+
["image/png", "iVBORw0KGgo"], ["image/jpeg", "/9j/"], ["image/gif", "R0lGOD"], ["image/webp", "UklGR"], ["image/bmp", "Qk"],
|
|
13
|
+
];
|
|
14
|
+
export const CONTENT_PROFILES = {
|
|
15
|
+
strict: { pasteRelativeImages: false, linkSchemes: ["http", "https", "mailto"], styles: "semantic", maxDataImageLength: 0, maxDepth: 32, maxInputLength: 1_000_000 },
|
|
16
|
+
standard: { pasteRelativeImages: false, linkSchemes: DEFAULT_LINK_SCHEMES, styles: "allowlist", maxDataImageLength: 12_000_000, maxDepth: 100, maxInputLength: 50_000_000 },
|
|
17
|
+
loose: { pasteRelativeImages: false, linkSchemes: ["http", "https", "mailto", "tel", "sms", "ftp", "ftps"], styles: "allowlist", maxDataImageLength: 25_000_000, maxDepth: 200, maxInputLength: 200_000_000 },
|
|
18
|
+
};
|
|
19
|
+
export function contentPolicy(options = {}, complain = () => undefined) {
|
|
20
|
+
const { profile = "standard", ...rest } = options;
|
|
21
|
+
if (!Object.hasOwn(CONTENT_PROFILES, profile))
|
|
22
|
+
complain(`content.profile "${String(profile)}" is not a profile: using standard (strict, standard or loose).`);
|
|
23
|
+
const base = Object.hasOwn(CONTENT_PROFILES, profile) ? CONTENT_PROFILES[profile] : CONTENT_PROFILES.standard;
|
|
24
|
+
const given = {};
|
|
25
|
+
for (const [key, value] of Object.entries(rest)) {
|
|
26
|
+
if (value === undefined)
|
|
27
|
+
continue;
|
|
28
|
+
const ok = key === "linkSchemes" ? Array.isArray(value) && value.every((v) => typeof v === "string" && /^[a-z][a-z0-9+.-]*$/i.test(v))
|
|
29
|
+
: key === "styles" ? value === "semantic" || value === "allowlist"
|
|
30
|
+
: key === "pasteRelativeImages" ? typeof value === "boolean"
|
|
31
|
+
: key === "maxDepth" ? Number.isInteger(value) && value >= 1
|
|
32
|
+
: (key === "maxInputLength" || key === "maxDataImageLength") ? Number.isInteger(value) && value >= 0
|
|
33
|
+
: false;
|
|
34
|
+
if (ok)
|
|
35
|
+
given[key] = value;
|
|
36
|
+
else
|
|
37
|
+
complain(`content.${key} cannot be ${JSON.stringify(value)}: using the ${profile in CONTENT_PROFILES ? profile : "standard"} profile's value.`);
|
|
38
|
+
}
|
|
39
|
+
return { ...base, ...given };
|
|
40
|
+
}
|
package/dist/standard/index.d.ts
CHANGED
|
@@ -25,4 +25,3 @@ export interface StandardOptions extends Omit<EditorOptions, "plugins">, Standar
|
|
|
25
25
|
/** The standard plugins, for hosts that build their own editor but want the usual parts. */
|
|
26
26
|
export declare function standardPlugins(options?: StandardPluginOptions): Plugin[];
|
|
27
27
|
export declare function createEditor(options: StandardOptions): Editor;
|
|
28
|
-
export { Editor, type EditorOptions };
|
package/dist/standard/index.js
CHANGED
package/dist/stats.js
CHANGED
|
@@ -4,7 +4,7 @@ import { selectedText } from "./edit.js";
|
|
|
4
4
|
const WORD = /[\p{L}\p{M}\p{N}]+(?:['’\-‑][\p{L}\p{M}\p{N}]+)*/gu;
|
|
5
5
|
export function countText(text) {
|
|
6
6
|
const clean = text.replace(//g, "").replace(/\n/g, " ");
|
|
7
|
-
return { words: (clean.match(WORD) ?? []).length, characters: [...clean.
|
|
7
|
+
return { words: (clean.match(WORD) ?? []).length, characters: [...clean.trim()].length };
|
|
8
8
|
}
|
|
9
9
|
export function countDocument(doc) {
|
|
10
10
|
let words = 0;
|
package/dist/svg/svg.js
CHANGED
|
@@ -44,7 +44,15 @@ const decodeXml = (value) => value.replace(ENTITY, (_, dec, hex, name) => {
|
|
|
44
44
|
function parseXml(source, limits) {
|
|
45
45
|
if (/<!ENTITY|<!ATTLIST|<!NOTATION/i.test(source) || /<!\[CDATA\[/i.test(source))
|
|
46
46
|
return undefined;
|
|
47
|
-
|
|
47
|
+
let stripped = source.replace(/^/, "").replace(/<\?xml[^>]*\?>/i, "").replace(/<!DOCTYPE[^>[]*(?:\[[^\]]*\])?[^>]*>/gi, "");
|
|
48
|
+
for (let pass = 0; pass < 4; pass++) {
|
|
49
|
+
const next = stripped.replace(/<!--[\s\S]*?-->/g, "");
|
|
50
|
+
if (next === stripped)
|
|
51
|
+
break;
|
|
52
|
+
stripped = next;
|
|
53
|
+
}
|
|
54
|
+
if (stripped.includes("<!--"))
|
|
55
|
+
return undefined;
|
|
48
56
|
const stack = [];
|
|
49
57
|
let root;
|
|
50
58
|
let count = 0;
|
package/dist/tables/cells.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ViewHost } from "../plugin.ts";
|
|
2
2
|
/** The attribute on a cell that a selection covers, for styling. */
|
|
3
|
-
export declare const SELECTED_CELL = "data-
|
|
3
|
+
export declare const SELECTED_CELL = "data-rte-cell-selected";
|
|
4
4
|
/**
|
|
5
5
|
* Marks the cells of the rectangle a selection runs across (when it starts and ends in different cells of one table). The marks are
|
|
6
6
|
* attributes on the drawn cells, not part of the model, and they are put on again after every change.
|
package/dist/tables/cells.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { pathKey } from "../path.js";
|
|
2
2
|
import { inRow, locateCell, selectedCells } from "../tablegrid.js";
|
|
3
|
-
export const SELECTED_CELL = "data-
|
|
3
|
+
export const SELECTED_CELL = "data-rte-cell-selected";
|
|
4
4
|
const GRAB = 5;
|
|
5
5
|
const MIN_WIDTH = 24;
|
|
6
6
|
const MIN_HEIGHT = 16;
|
|
@@ -96,7 +96,7 @@ export function installColumnResize(host) {
|
|
|
96
96
|
const rowBox = row.getBoundingClientRect();
|
|
97
97
|
const rowTable = rowCell.closest("table")?.getBoundingClientRect() ?? rowBox;
|
|
98
98
|
const rowLine = document.createElement("div");
|
|
99
|
-
rowLine.setAttribute("data-
|
|
99
|
+
rowLine.setAttribute("data-rte-resize-line", "");
|
|
100
100
|
rowLine.style.cssText = `position:fixed;z-index:2147483647;pointer-events:none;height:2px;background:#17408b;top:${rowBox.bottom - 1}px;left:${rowTable.left}px;width:${rowTable.width}px`;
|
|
101
101
|
document.body.appendChild(rowLine);
|
|
102
102
|
rowDrag = { path: rowPath, startY: event.clientY, startHeight: rowBox.height, top: rowBox.top, line: rowLine, height: rowBox.height };
|
|
@@ -106,7 +106,7 @@ export function installColumnResize(host) {
|
|
|
106
106
|
const box = cell.getBoundingClientRect();
|
|
107
107
|
const table = cell.closest("table")?.getBoundingClientRect() ?? box;
|
|
108
108
|
const line = document.createElement("div");
|
|
109
|
-
line.setAttribute("data-
|
|
109
|
+
line.setAttribute("data-rte-resize-line", "");
|
|
110
110
|
line.style.cssText = `position:fixed;z-index:2147483647;pointer-events:none;width:2px;background:#17408b;left:${box.right - 1}px;top:${table.top}px;height:${table.height}px`;
|
|
111
111
|
document.body.appendChild(line);
|
|
112
112
|
drag = { cell, path, startX: event.clientX, startWidth: box.width, line, width: box.width };
|
package/dist/tables/grips.js
CHANGED
|
@@ -82,7 +82,7 @@ export class TableGrips {
|
|
|
82
82
|
}
|
|
83
83
|
make(kind) {
|
|
84
84
|
const el = document.createElement("div");
|
|
85
|
-
el.setAttribute("data-
|
|
85
|
+
el.setAttribute("data-rte-grip", kind);
|
|
86
86
|
el.setAttribute("role", "presentation");
|
|
87
87
|
const cursor = kind === "row" ? "ns-resize" : kind === "column" ? "ew-resize" : kind === "width" ? "col-resize" : "row-resize";
|
|
88
88
|
el.style.cssText = `position:fixed;z-index:2147483646;width:${SIZE}px;height:${SIZE}px;box-sizing:border-box;background:${COLOR};border:2px solid #fff;border-radius:${kind === "row" || kind === "column" ? "4px" : "50%"};cursor:${cursor};touch-action:none;opacity:0.9`;
|
|
@@ -120,7 +120,7 @@ export class TableGrips {
|
|
|
120
120
|
const t = table.getBoundingClientRect();
|
|
121
121
|
const horizontal = kind === "column" || kind === "width";
|
|
122
122
|
const line = document.createElement("div");
|
|
123
|
-
line.setAttribute("data-
|
|
123
|
+
line.setAttribute("data-rte-resize-line", "");
|
|
124
124
|
line.style.cssText = `position:fixed;z-index:2147483645;pointer-events:none;background:${COLOR};${horizontal ? `width:2px;top:${t.top}px;height:${t.height}px` : `height:2px;left:${t.left}px;width:${t.width}px`}`;
|
|
125
125
|
document.body.appendChild(line);
|
|
126
126
|
this.guide = line;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Why a warning was made. The codes are stable: a host can switch on them. */
|
|
2
|
+
export declare const WARNING_CODES: readonly ["plugin-failed", "plugin-rejected", "plugin-message", "key-conflict", "content-too-long", "content-unreadable", "copy-failed", "upload-failed", "bad-option", "missing-label", "late-options", "listener-failed"];
|
|
3
|
+
/** One of the codes in `WARNING_CODES`. */
|
|
4
|
+
export type WarningCode = (typeof WARNING_CODES)[number];
|
|
5
|
+
/** A problem the editor carried on from, reported to `onWarning`. Never thrown. */
|
|
6
|
+
export interface Warning {
|
|
7
|
+
readonly code: WarningCode;
|
|
8
|
+
/** For a person: what happened and what was done about it. */
|
|
9
|
+
readonly message: string;
|
|
10
|
+
/** The plugin it concerns, when there is one. */
|
|
11
|
+
readonly plugin?: string;
|
|
12
|
+
/** The error that was caught, when one was. */
|
|
13
|
+
readonly error?: unknown;
|
|
14
|
+
}
|
|
15
|
+
/** What an error says, whatever was thrown. */
|
|
16
|
+
export declare const describeError: (error: unknown) => string;
|
package/dist/warning.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export const WARNING_CODES = [
|
|
2
|
+
"plugin-failed", "plugin-rejected", "plugin-message", "key-conflict", "content-too-long", "content-unreadable",
|
|
3
|
+
"copy-failed", "upload-failed", "bad-option", "missing-label", "late-options", "listener-failed",
|
|
4
|
+
];
|
|
5
|
+
export const describeError = (error) => (error instanceof Error ? error.message : String(error));
|