@openleaf-editor/core 0.1.0-beta.0

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/dist/schema.js ADDED
@@ -0,0 +1,268 @@
1
+ /**
2
+ * The OpenLeaf document schema.
3
+ *
4
+ * Scope note: table NODES are here, in the base schema, so that every
5
+ * deployment reads and writes tables faithfully. Table EDITING -- cell
6
+ * selection, column resizing, the row and column commands and toolbar -- is
7
+ * the opt-in @openleaf-editor/plugins-table. See tables.ts for why the split falls
8
+ * there rather than at the package boundary.
9
+ */
10
+ import { Schema } from 'prosemirror-model';
11
+ import { unknownBlock, unknownInline } from './preserve.js';
12
+ import { table, table_cell, table_header, table_row } from './tables.js';
13
+ import { isSafeUrl } from './url.js';
14
+ /** The base node specs. Extensions are appended to these. */
15
+ export const coreNodes = {
16
+ doc: { content: 'block+' },
17
+ paragraph: {
18
+ // `dir` is bidirectional text direction, not styling. Dropping it
19
+ // silently breaks Arabic, Hebrew and Persian content, so it is a
20
+ // first-class schema attribute rather than something the preservation
21
+ // layer has to rescue.
22
+ attrs: { dir: { default: null } },
23
+ content: 'inline*',
24
+ group: 'block',
25
+ parseDOM: [
26
+ {
27
+ tag: 'p',
28
+ getAttrs: (dom) => ({ dir: dom.getAttribute('dir') }),
29
+ },
30
+ ],
31
+ toDOM(node) {
32
+ const dir = node.attrs['dir'];
33
+ return dir ? ['p', { dir }, 0] : ['p', 0];
34
+ },
35
+ },
36
+ heading: {
37
+ attrs: { level: { default: 1 }, dir: { default: null } },
38
+ content: 'inline*',
39
+ group: 'block',
40
+ defining: true,
41
+ parseDOM: [1, 2, 3, 4, 5, 6].map((level) => ({
42
+ tag: `h${level}`,
43
+ getAttrs: (dom) => ({ level, dir: dom.getAttribute('dir') }),
44
+ })),
45
+ toDOM(node) {
46
+ const tag = `h${node.attrs['level']}`;
47
+ const dir = node.attrs['dir'];
48
+ return dir ? [tag, { dir }, 0] : [tag, 0];
49
+ },
50
+ },
51
+ blockquote: {
52
+ content: 'block+',
53
+ group: 'block',
54
+ defining: true,
55
+ parseDOM: [{ tag: 'blockquote' }],
56
+ toDOM: () => ['blockquote', 0],
57
+ },
58
+ code_block: {
59
+ // `language` is content, not decoration. Without it a fenced block loses
60
+ // `class="language-js"` on the first save -- which is both an attribute-loss
61
+ // bug and the reason a highlighter has nothing to work from.
62
+ attrs: { language: { default: null } },
63
+ content: 'text*',
64
+ marks: '',
65
+ group: 'block',
66
+ code: true,
67
+ defining: true,
68
+ parseDOM: [
69
+ {
70
+ tag: 'pre',
71
+ preserveWhitespace: 'full',
72
+ getAttrs(dom) {
73
+ const pre = dom;
74
+ // The class may sit on either element in the wild. CommonMark, Prism
75
+ // and highlight.js all put it on <code>; older CMS output often puts
76
+ // it on <pre>. Read both, write one.
77
+ const code = pre.querySelector('code');
78
+ const source = `${pre.getAttribute('class') ?? ''} ${code?.getAttribute('class') ?? ''}`;
79
+ const match = /(?:^|\s)(?:language|lang)-([a-z0-9+#.-]+)/i.exec(source);
80
+ return { language: match ? match[1].toLowerCase() : null };
81
+ },
82
+ },
83
+ ],
84
+ toDOM(node) {
85
+ const language = node.attrs['language'];
86
+ // Normalized onto <code>, which is where every downstream highlighter
87
+ // looks. A block authored with the class on <pre> moves it; that is a
88
+ // one-time normalization and it is stable thereafter.
89
+ return language ? ['pre', ['code', { class: `language-${language}` }, 0]] : ['pre', ['code', 0]];
90
+ },
91
+ },
92
+ horizontal_rule: {
93
+ group: 'block',
94
+ parseDOM: [{ tag: 'hr' }],
95
+ toDOM: () => ['hr'],
96
+ },
97
+ bullet_list: {
98
+ content: 'list_item+',
99
+ group: 'block',
100
+ parseDOM: [{ tag: 'ul' }],
101
+ toDOM: () => ['ul', 0],
102
+ },
103
+ ordered_list: {
104
+ attrs: { start: { default: 1 } },
105
+ content: 'list_item+',
106
+ group: 'block',
107
+ parseDOM: [
108
+ {
109
+ tag: 'ol',
110
+ getAttrs(dom) {
111
+ const start = dom.getAttribute('start');
112
+ return { start: start ? Number(start) : 1 };
113
+ },
114
+ },
115
+ ],
116
+ toDOM(node) {
117
+ const start = node.attrs['start'];
118
+ return start === 1 ? ['ol', 0] : ['ol', { start: String(start) }, 0];
119
+ },
120
+ },
121
+ list_item: {
122
+ content: 'paragraph block*',
123
+ defining: true,
124
+ parseDOM: [{ tag: 'li' }],
125
+ toDOM: () => ['li', 0],
126
+ },
127
+ image: {
128
+ inline: true,
129
+ group: 'inline',
130
+ draggable: true,
131
+ attrs: {
132
+ src: {},
133
+ alt: { default: null },
134
+ title: { default: null },
135
+ width: { default: null },
136
+ height: { default: null },
137
+ },
138
+ parseDOM: [
139
+ {
140
+ tag: 'img[src]',
141
+ getAttrs(dom) {
142
+ const el = dom;
143
+ // Returning false declines the rule, so an image with a
144
+ // `javascript:` src is dropped rather than carried through.
145
+ if (!isSafeUrl(el.getAttribute('src')))
146
+ return false;
147
+ return {
148
+ src: el.getAttribute('src'),
149
+ // An absent alt and alt="" mean different things to a screen
150
+ // reader: "undescribed" versus "decorative". Never conflate them.
151
+ alt: el.getAttribute('alt'),
152
+ title: el.getAttribute('title'),
153
+ width: el.getAttribute('width'),
154
+ height: el.getAttribute('height'),
155
+ };
156
+ },
157
+ },
158
+ ],
159
+ toDOM(node) {
160
+ const { src, alt, title, width, height } = node.attrs;
161
+ const attrs = { src: src };
162
+ if (alt !== null)
163
+ attrs['alt'] = alt;
164
+ if (title !== null)
165
+ attrs['title'] = title;
166
+ if (width !== null)
167
+ attrs['width'] = width;
168
+ if (height !== null)
169
+ attrs['height'] = height;
170
+ return ['img', attrs];
171
+ },
172
+ },
173
+ hard_break: {
174
+ inline: true,
175
+ group: 'inline',
176
+ selectable: false,
177
+ parseDOM: [{ tag: 'br' }],
178
+ toDOM: () => ['br'],
179
+ },
180
+ text: { group: 'inline' },
181
+ // Tables are in the BASE schema deliberately. Without them a <table> in
182
+ // stored content becomes an opaque preserved atom -- faithful but uneditable,
183
+ // which is not something you can tell a CMS. The heavy part (cell selection,
184
+ // column resizing, the row and column commands) is the opt-in
185
+ // @openleaf-editor/plugins-table.
186
+ table,
187
+ table_row,
188
+ table_cell,
189
+ table_header,
190
+ unknown_block: unknownBlock,
191
+ unknown_inline: unknownInline,
192
+ };
193
+ /** The base mark specs. */
194
+ export const coreMarks = {
195
+ strong: {
196
+ parseDOM: [
197
+ { tag: 'strong' },
198
+ { tag: 'b' },
199
+ { style: 'font-weight=bold' },
200
+ { style: 'font-weight=700' },
201
+ ],
202
+ toDOM: () => ['strong', 0],
203
+ },
204
+ em: {
205
+ parseDOM: [{ tag: 'em' }, { tag: 'i' }, { style: 'font-style=italic' }],
206
+ toDOM: () => ['em', 0],
207
+ },
208
+ underline: {
209
+ parseDOM: [{ tag: 'u' }, { style: 'text-decoration=underline' }],
210
+ toDOM: () => ['u', 0],
211
+ },
212
+ strike: {
213
+ parseDOM: [{ tag: 's' }, { tag: 'del' }, { style: 'text-decoration=line-through' }],
214
+ toDOM: () => ['s', 0],
215
+ },
216
+ code: {
217
+ parseDOM: [{ tag: 'code' }],
218
+ toDOM: () => ['code', 0],
219
+ },
220
+ link: {
221
+ attrs: {
222
+ href: {},
223
+ title: { default: null },
224
+ target: { default: null },
225
+ rel: { default: null },
226
+ },
227
+ inclusive: false,
228
+ parseDOM: [
229
+ {
230
+ tag: 'a[href]',
231
+ getAttrs(dom) {
232
+ const el = dom;
233
+ // A `javascript:` href is the cheapest XSS vector there is: no
234
+ // injected element, no script tag, just a reader who clicks.
235
+ // Declining the rule keeps the link TEXT and drops the mark.
236
+ if (!isSafeUrl(el.getAttribute('href')))
237
+ return false;
238
+ return {
239
+ href: el.getAttribute('href'),
240
+ title: el.getAttribute('title'),
241
+ target: el.getAttribute('target'),
242
+ rel: el.getAttribute('rel'),
243
+ };
244
+ },
245
+ },
246
+ ],
247
+ toDOM(node) {
248
+ const { href, title, target, rel } = node.attrs;
249
+ const attrs = { href: href };
250
+ if (title !== null)
251
+ attrs['title'] = title;
252
+ if (target !== null)
253
+ attrs['target'] = target;
254
+ if (rel !== null)
255
+ attrs['rel'] = rel;
256
+ return ['a', attrs, 0];
257
+ },
258
+ },
259
+ };
260
+ /**
261
+ * The base schema, with no extensions.
262
+ *
263
+ * Kept for the many places that only ever need the built-in types. Anything that
264
+ * must honour plugin-contributed node types uses `createSchema` or reads
265
+ * `state.schema` instead -- see extensions.ts.
266
+ */
267
+ export const baseSchema = new Schema({ nodes: coreNodes, marks: coreMarks });
268
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Table node specifications.
3
+ *
4
+ * ## Why these live in core rather than in the opt-in plugin
5
+ *
6
+ * The obvious design is to put tables entirely in `@openleaf-editor/plugins-table`, so
7
+ * a CMS that forbids tables ships none of the code. That is wrong, and the
8
+ * fidelity harness is what shows why: without these node types, a `<table>` in
9
+ * stored content is claimed by the preservation layer and becomes a single
10
+ * opaque atom. It round-trips faithfully -- but it is *uneditable*. An author
11
+ * opening a decade-old post finds a grey card where their table used to be.
12
+ *
13
+ * "We read your tables but you may not touch them" is not a thing you can tell a
14
+ * CMS. So the schema is always present, costing about a kilobyte, and what is
15
+ * genuinely opt-in is the *editing machinery*: cell selection, column resizing,
16
+ * the row and column commands, the toolbar controls. That is the part with real
17
+ * weight, and it lives in the plugin.
18
+ *
19
+ * ## Compatibility with prosemirror-tables
20
+ *
21
+ * Each spec carries a `tableRole`, which is how `prosemirror-tables` recognises
22
+ * these nodes. Cell attributes are named `colspan`, `rowspan` and `colwidth`
23
+ * exactly because that library requires those names. Deviating would mean
24
+ * forking it.
25
+ *
26
+ * ## Legacy presentational attributes
27
+ *
28
+ * `border`, `cellpadding`, `cellspacing`, `align` and friends are kept, which a
29
+ * clean-slate schema would not do. They are how HTML expressed table styling for
30
+ * fifteen years, they are all over the content this editor is meant to inherit,
31
+ * and dropping them changes how a page renders. Preserving them is the same
32
+ * decision the preservation layer makes, applied to attributes.
33
+ *
34
+ * `scope` on a header cell is kept for a different and more important reason: it
35
+ * is what tells a screen reader which cells a header governs. Dropping it turns
36
+ * a navigable table into a grid of unrelated values.
37
+ */
38
+ import type { NodeSpec } from 'prosemirror-model';
39
+ export declare const table: NodeSpec;
40
+ export declare const table_row: NodeSpec;
41
+ export declare const table_cell: NodeSpec;
42
+ export declare const table_header: NodeSpec;
43
+ //# sourceMappingURL=tables.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../src/tables.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AA2EjD,eAAO,MAAM,KAAK,EAAE,QA0CnB,CAAA;AAED,eAAO,MAAM,SAAS,EAAE,QAQvB,CAAA;AAED,eAAO,MAAM,UAAU,EAAE,QAOxB,CAAA;AAED,eAAO,MAAM,YAAY,EAAE,QAO1B,CAAA"}
package/dist/tables.js ADDED
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Table node specifications.
3
+ *
4
+ * ## Why these live in core rather than in the opt-in plugin
5
+ *
6
+ * The obvious design is to put tables entirely in `@openleaf-editor/plugins-table`, so
7
+ * a CMS that forbids tables ships none of the code. That is wrong, and the
8
+ * fidelity harness is what shows why: without these node types, a `<table>` in
9
+ * stored content is claimed by the preservation layer and becomes a single
10
+ * opaque atom. It round-trips faithfully -- but it is *uneditable*. An author
11
+ * opening a decade-old post finds a grey card where their table used to be.
12
+ *
13
+ * "We read your tables but you may not touch them" is not a thing you can tell a
14
+ * CMS. So the schema is always present, costing about a kilobyte, and what is
15
+ * genuinely opt-in is the *editing machinery*: cell selection, column resizing,
16
+ * the row and column commands, the toolbar controls. That is the part with real
17
+ * weight, and it lives in the plugin.
18
+ *
19
+ * ## Compatibility with prosemirror-tables
20
+ *
21
+ * Each spec carries a `tableRole`, which is how `prosemirror-tables` recognises
22
+ * these nodes. Cell attributes are named `colspan`, `rowspan` and `colwidth`
23
+ * exactly because that library requires those names. Deviating would mean
24
+ * forking it.
25
+ *
26
+ * ## Legacy presentational attributes
27
+ *
28
+ * `border`, `cellpadding`, `cellspacing`, `align` and friends are kept, which a
29
+ * clean-slate schema would not do. They are how HTML expressed table styling for
30
+ * fifteen years, they are all over the content this editor is meant to inherit,
31
+ * and dropping them changes how a page renders. Preserving them is the same
32
+ * decision the preservation layer makes, applied to attributes.
33
+ *
34
+ * `scope` on a header cell is kept for a different and more important reason: it
35
+ * is what tells a screen reader which cells a header governs. Dropping it turns
36
+ * a navigable table into a grid of unrelated values.
37
+ */
38
+ /** Presentational attributes legacy CMS content puts on `<table>`. */
39
+ const TABLE_LEGACY_ATTRS = ['border', 'cellpadding', 'cellspacing', 'width', 'align', 'summary', 'class'];
40
+ /** Attributes legacy content puts on cells, plus the accessibility-critical ones. */
41
+ const CELL_LEGACY_ATTRS = ['align', 'valign', 'width', 'height', 'class', 'scope', 'headers', 'abbr'];
42
+ function readAttrs(el, names) {
43
+ const out = {};
44
+ for (const name of names)
45
+ out[name] = el.getAttribute(name);
46
+ return out;
47
+ }
48
+ function writeAttrs(attrs, names) {
49
+ const out = {};
50
+ for (const name of names) {
51
+ const value = attrs[name];
52
+ if (value !== null && value !== undefined && value !== '')
53
+ out[name] = String(value);
54
+ }
55
+ return out;
56
+ }
57
+ const legacyDefaults = (names) => Object.fromEntries(names.map((name) => [name, { default: null }]));
58
+ /**
59
+ * Parse `colwidth` from an inline width style or attribute.
60
+ *
61
+ * `prosemirror-tables` stores column widths as an array of numbers on the cell
62
+ * that starts the column, which is how its resizing plugin reads them.
63
+ */
64
+ function readColwidth(el) {
65
+ const widthAttr = el.getAttribute('data-colwidth');
66
+ if (widthAttr) {
67
+ const parsed = widthAttr.split(',').map((n) => Number.parseInt(n, 10));
68
+ if (parsed.every((n) => Number.isFinite(n)))
69
+ return parsed;
70
+ }
71
+ return null;
72
+ }
73
+ const cellAttrs = {
74
+ // These three names are required by prosemirror-tables.
75
+ colspan: { default: 1 },
76
+ rowspan: { default: 1 },
77
+ colwidth: { default: null },
78
+ ...legacyDefaults(CELL_LEGACY_ATTRS),
79
+ };
80
+ function cellGetAttrs(dom) {
81
+ const el = dom;
82
+ return {
83
+ colspan: Number.parseInt(el.getAttribute('colspan') ?? '1', 10) || 1,
84
+ rowspan: Number.parseInt(el.getAttribute('rowspan') ?? '1', 10) || 1,
85
+ colwidth: readColwidth(el),
86
+ ...readAttrs(el, CELL_LEGACY_ATTRS),
87
+ };
88
+ }
89
+ function cellToDOM(tag) {
90
+ return (node) => {
91
+ const attrs = writeAttrs(node.attrs, CELL_LEGACY_ATTRS);
92
+ const colspan = node.attrs['colspan'];
93
+ const rowspan = node.attrs['rowspan'];
94
+ if (colspan !== 1)
95
+ attrs['colspan'] = String(colspan);
96
+ if (rowspan !== 1)
97
+ attrs['rowspan'] = String(rowspan);
98
+ const colwidth = node.attrs['colwidth'];
99
+ if (colwidth)
100
+ attrs['data-colwidth'] = colwidth.join(',');
101
+ return [tag, attrs, 0];
102
+ };
103
+ }
104
+ export const table = {
105
+ content: 'table_row+',
106
+ tableRole: 'table',
107
+ isolating: true,
108
+ group: 'block',
109
+ attrs: legacyDefaults(TABLE_LEGACY_ATTRS),
110
+ parseDOM: [
111
+ { tag: 'table', getAttrs: (dom) => readAttrs(dom, TABLE_LEGACY_ATTRS) },
112
+ /*
113
+ * `tbody`, `thead` and `tfoot` are SKIPPED rather than ignored: the wrapper
114
+ * itself carries nothing, but its rows are the entire content. `ignore`
115
+ * would delete the table's contents along with the wrapper.
116
+ *
117
+ * These rules also have to exist because the preservation layer's catch-all
118
+ * would otherwise claim a `<tbody>` as unrecognised markup and produce an
119
+ * opaque atom that `table_row+` refuses to accept.
120
+ */
121
+ { tag: 'tbody', skip: true },
122
+ { tag: 'thead', skip: true },
123
+ { tag: 'tfoot', skip: true },
124
+ /*
125
+ * KNOWN LIMITATION, declared rather than hidden.
126
+ *
127
+ * `<colgroup>`/`<col>` are dropped, and `<caption>` is dropped with its
128
+ * text. Both should be preserved and neither can be today: a caption node
129
+ * would have to be the table's first child, and `prosemirror-tables`
130
+ * computes its cell map by treating every child of a table as a row, so a
131
+ * leading caption breaks its indexing.
132
+ *
133
+ * Dropping a caption is a real accessibility regression -- a caption is a
134
+ * table's accessible name -- so this is tracked as a bug to fix by adding a
135
+ * caption node and contributing the indexing fix upstream, not as a design
136
+ * decision. There is a fixture asserting the current behaviour so it cannot
137
+ * regress further without someone noticing.
138
+ */
139
+ { tag: 'colgroup', ignore: true },
140
+ { tag: 'col', ignore: true },
141
+ { tag: 'caption', ignore: true },
142
+ ],
143
+ toDOM(node) {
144
+ return ['table', writeAttrs(node.attrs, TABLE_LEGACY_ATTRS), ['tbody', 0]];
145
+ },
146
+ };
147
+ export const table_row = {
148
+ content: '(table_cell | table_header)*',
149
+ tableRole: 'row',
150
+ attrs: { class: { default: null }, align: { default: null } },
151
+ parseDOM: [{ tag: 'tr', getAttrs: (dom) => readAttrs(dom, ['class', 'align']) }],
152
+ toDOM(node) {
153
+ return ['tr', writeAttrs(node.attrs, ['class', 'align']), 0];
154
+ },
155
+ };
156
+ export const table_cell = {
157
+ content: 'block+',
158
+ attrs: cellAttrs,
159
+ tableRole: 'cell',
160
+ isolating: true,
161
+ parseDOM: [{ tag: 'td', getAttrs: cellGetAttrs }],
162
+ toDOM: cellToDOM('td'),
163
+ };
164
+ export const table_header = {
165
+ content: 'block+',
166
+ attrs: cellAttrs,
167
+ tableRole: 'header_cell',
168
+ isolating: true,
169
+ parseDOM: [{ tag: 'th', getAttrs: cellGetAttrs }],
170
+ toDOM: cellToDOM('th'),
171
+ };
172
+ //# sourceMappingURL=tables.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tables.js","sourceRoot":"","sources":["../src/tables.ts"],"names":[],"mappings":"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"}
package/dist/url.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ /**
2
+ * URL safety.
3
+ *
4
+ * A link is the cheapest XSS vector there is: `<a href="javascript:...">` needs
5
+ * no injected element, no script tag, and no clever parsing trick -- just a user
6
+ * who clicks. OpenLeaf's schema previously accepted any `href` at all.
7
+ *
8
+ * The checks here are deliberately a small allowlist of schemes rather than a
9
+ * blocklist of dangerous ones. A blocklist is a promise to have thought of every
10
+ * scheme, including the ones a browser will invent later.
11
+ */
12
+ /**
13
+ * Is this URL safe to put in an `href` or `src`?
14
+ *
15
+ * Relative URLs, fragments and query-only URLs are allowed: they cannot carry a
16
+ * scheme, so they cannot execute. Protocol-relative `//host/path` is allowed
17
+ * because it inherits the page's scheme.
18
+ */
19
+ export declare function isSafeUrl(value: string | null | undefined): boolean;
20
+ /** The safe URL, or null when it should be dropped. */
21
+ export declare function safeUrlOrNull(value: string | null | undefined): string | null;
22
+ /** Attributes whose values are URLs and therefore need checking. */
23
+ export declare const URL_ATTRIBUTES: ReadonlySet<string>;
24
+ /** True for `onclick`, `onmouseover` and every other inline event handler. */
25
+ export declare function isEventHandlerAttribute(name: string): boolean;
26
+ //# sourceMappingURL=url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../src/url.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA6BH;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAUnE;AAED,uDAAuD;AACvD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAE7E;AAED,oEAAoE;AACpE,eAAO,MAAM,cAAc,EAAE,WAAW,CAAC,MAAM,CAa7C,CAAA;AAEF,8EAA8E;AAC9E,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE7D"}
package/dist/url.js ADDED
@@ -0,0 +1,78 @@
1
+ /**
2
+ * URL safety.
3
+ *
4
+ * A link is the cheapest XSS vector there is: `<a href="javascript:...">` needs
5
+ * no injected element, no script tag, and no clever parsing trick -- just a user
6
+ * who clicks. OpenLeaf's schema previously accepted any `href` at all.
7
+ *
8
+ * The checks here are deliberately a small allowlist of schemes rather than a
9
+ * blocklist of dangerous ones. A blocklist is a promise to have thought of every
10
+ * scheme, including the ones a browser will invent later.
11
+ */
12
+ /**
13
+ * Schemes an author may link to.
14
+ *
15
+ * `data:` is absent on purpose. `data:text/html` is a full XSS vector, and
16
+ * distinguishing safe data URLs from dangerous ones by sniffing the media type
17
+ * is exactly the kind of parsing that gets defeated. Integrators who genuinely
18
+ * need inline images can permit them in their server-side policy, where the
19
+ * decision is explicit and reviewable.
20
+ */
21
+ const SAFE_SCHEMES = new Set(['http', 'https', 'mailto', 'tel', 'ftp', 'ftps']);
22
+ /** Matches a leading URL scheme, per RFC 3986. */
23
+ const SCHEME = /^([a-z][a-z0-9+.-]*):/i;
24
+ /**
25
+ * Strip the characters browsers ignore when resolving a scheme.
26
+ *
27
+ * `java\tscript:alert(1)` and `java\nscript:alert(1)` both navigate. Browsers
28
+ * discard ASCII whitespace and control characters while parsing the scheme, so
29
+ * any check that does not do the same is trivially bypassed. Attribute values
30
+ * arrive already entity-decoded from `getAttribute`, so `&#106;avascript:` is
31
+ * covered by the same normalization.
32
+ */
33
+ function normalize(value) {
34
+ return value.replace(/[\u0000-\u0020\u007f-\u00a0]/g, '').toLowerCase();
35
+ }
36
+ /**
37
+ * Is this URL safe to put in an `href` or `src`?
38
+ *
39
+ * Relative URLs, fragments and query-only URLs are allowed: they cannot carry a
40
+ * scheme, so they cannot execute. Protocol-relative `//host/path` is allowed
41
+ * because it inherits the page's scheme.
42
+ */
43
+ export function isSafeUrl(value) {
44
+ if (value === null || value === undefined)
45
+ return false;
46
+ const candidate = normalize(value);
47
+ if (candidate === '')
48
+ return false;
49
+ const match = SCHEME.exec(candidate);
50
+ // No scheme means relative, fragment or protocol-relative. All are safe.
51
+ if (!match)
52
+ return true;
53
+ return SAFE_SCHEMES.has(match[1]);
54
+ }
55
+ /** The safe URL, or null when it should be dropped. */
56
+ export function safeUrlOrNull(value) {
57
+ return isSafeUrl(value) ? value : null;
58
+ }
59
+ /** Attributes whose values are URLs and therefore need checking. */
60
+ export const URL_ATTRIBUTES = new Set([
61
+ 'href',
62
+ 'src',
63
+ 'action',
64
+ 'formaction',
65
+ 'data',
66
+ 'poster',
67
+ 'background',
68
+ 'cite',
69
+ 'longdesc',
70
+ 'srcdoc',
71
+ 'xlink:href',
72
+ 'ping',
73
+ ]);
74
+ /** True for `onclick`, `onmouseover` and every other inline event handler. */
75
+ export function isEventHandlerAttribute(name) {
76
+ return /^on/i.test(name);
77
+ }
78
+ //# sourceMappingURL=url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.js","sourceRoot":"","sources":["../src/url.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;;;;;;;GAQG;AACH,MAAM,YAAY,GAAwB,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAA;AAEpG,kDAAkD;AAClD,MAAM,MAAM,GAAG,wBAAwB,CAAA;AAEvC;;;;;;;;GAQG;AACH,SAAS,SAAS,CAAC,KAAa;IAC9B,OAAO,KAAK,CAAC,OAAO,CAAC,+BAA+B,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAA;AACzE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,KAAgC;IACxD,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAA;IACvD,MAAM,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,CAAA;IAClC,IAAI,SAAS,KAAK,EAAE;QAAE,OAAO,KAAK,CAAA;IAElC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IACpC,yEAAyE;IACzE,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IAEvB,OAAO,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAW,CAAC,CAAA;AAC7C,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,aAAa,CAAC,KAAgC;IAC5D,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAE,KAAgB,CAAC,CAAC,CAAC,IAAI,CAAA;AACpD,CAAC;AAED,oEAAoE;AACpE,MAAM,CAAC,MAAM,cAAc,GAAwB,IAAI,GAAG,CAAC;IACzD,MAAM;IACN,KAAK;IACL,QAAQ;IACR,YAAY;IACZ,MAAM;IACN,QAAQ;IACR,YAAY;IACZ,MAAM;IACN,UAAU;IACV,QAAQ;IACR,YAAY;IACZ,MAAM;CACP,CAAC,CAAA;AAEF,8EAA8E;AAC9E,MAAM,UAAU,uBAAuB,CAAC,IAAY;IAClD,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAC1B,CAAC"}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@openleaf-editor/core",
3
+ "version": "0.1.0-beta.0",
4
+ "description": "OpenLeaf editing core: ProseMirror schema, HTML I/O, and the content-preservation layer. No UI, no framework dependencies.",
5
+ "license": "Apache-2.0",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/PeytonNowlin/openleaf.git",
12
+ "directory": "packages/core"
13
+ },
14
+ "homepage": "https://peytonnowlin.github.io/openleaf/",
15
+ "bugs": {
16
+ "url": "https://github.com/PeytonNowlin/openleaf/issues"
17
+ },
18
+ "type": "module",
19
+ "main": "./dist/index.js",
20
+ "types": "./dist/index.d.ts",
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/index.d.ts",
24
+ "import": "./dist/index.js"
25
+ }
26
+ },
27
+ "files": [
28
+ "dist"
29
+ ],
30
+ "sideEffects": false,
31
+ "dependencies": {
32
+ "orderedmap": "^2.1.1",
33
+ "prosemirror-commands": "^1.6.2",
34
+ "prosemirror-history": "^1.4.1",
35
+ "prosemirror-model": "^1.24.1",
36
+ "prosemirror-schema-list": "^1.5.1",
37
+ "prosemirror-state": "^1.4.3",
38
+ "prosemirror-transform": "^1.10.2"
39
+ },
40
+ "scripts": {
41
+ "build": "tsc -p tsconfig.json"
42
+ }
43
+ }