securemark 0.280.9 → 0.281.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.280.9",
3
+ "version": "0.281.1",
4
4
  "description": "Secure markdown renderer working on browsers for user input data.",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/falsandtru/securemark",
@@ -28,38 +28,38 @@
28
28
  "LICENSE"
29
29
  ],
30
30
  "dependencies": {
31
- "spica": "0.0.781"
31
+ "spica": "0.0.804"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/dompurify": "3.0.5",
35
- "@types/jquery": "3.5.29",
35
+ "@types/jquery": "3.5.30",
36
36
  "@types/mathjax": "0.0.40",
37
- "@types/mocha": "10.0.6",
37
+ "@types/mocha": "10.0.7",
38
38
  "@types/power-assert": "1.5.12",
39
- "@types/prismjs": "1.26.3",
40
- "@typescript-eslint/parser": "^6.21.0",
39
+ "@types/prismjs": "1.26.4",
40
+ "@typescript-eslint/parser": "^8.0.0",
41
41
  "babel-loader": "^9.1.3",
42
42
  "babel-plugin-unassert": "^3.2.0",
43
43
  "concurrently": "^8.2.2",
44
- "eslint": "^8.56.0",
44
+ "eslint": "^9.8.0",
45
45
  "eslint-plugin-redos": "^4.4.5",
46
- "eslint-webpack-plugin": "^4.0.1",
47
- "glob": "^10.3.10",
48
- "karma": "^6.4.2",
46
+ "eslint-webpack-plugin": "^4.2.0",
47
+ "glob": "^11.0.0",
48
+ "karma": "^6.4.4",
49
49
  "karma-chrome-launcher": "^3.2.0",
50
50
  "karma-coverage": "^2.2.1",
51
- "karma-firefox-launcher": "^2.1.2",
51
+ "karma-firefox-launcher": "^2.1.3",
52
52
  "karma-mocha": "^2.0.1",
53
53
  "karma-power-assert": "^1.0.0",
54
- "mocha": "^10.3.0",
55
- "npm-check-updates": "^16.14.15",
56
- "semver": "^7.6.0",
54
+ "mocha": "^10.7.0",
55
+ "npm-check-updates": "^17.0.2",
56
+ "semver": "^7.6.3",
57
57
  "ts-loader": "^9.5.1",
58
- "typed-dom": "0.0.348",
59
- "typescript": "5.3.3",
60
- "webpack": "^5.90.1",
58
+ "typed-dom": "0.0.349",
59
+ "typescript": "5.4.5",
60
+ "webpack": "^5.93.0",
61
61
  "webpack-cli": "^5.1.4",
62
- "webpack-merge": "^5.10.0"
62
+ "webpack-merge": "^6.0.1"
63
63
  },
64
64
  "scripts": {
65
65
  "update": "ncu -u && npm i --no-shrinkwrap && bundle update",
@@ -14,6 +14,7 @@ export class Memo {
14
14
  syntax: number,
15
15
  state: number,
16
16
  ): readonly [any[], number] | readonly [] | undefined {
17
+ assert(position > 0);
17
18
  if (this.count === 0) return;
18
19
  //console.log('get', position, syntax, state, this.memory[position - 1]?.[syntax]?.[state]);
19
20
  const cache = this.memory[position - 1]?.[syntax]?.[state];
@@ -28,6 +29,7 @@ export class Memo {
28
29
  nodes: any[] | undefined,
29
30
  offset: number,
30
31
  ): void {
32
+ assert(position > 0);
31
33
  this.count += +!this.memory[position - 1];
32
34
  const record = this.memory[position - 1] ??= {};
33
35
  assert(!record[syntax]?.[state]);
@@ -1,11 +1,11 @@
1
1
  import { Caches } from '../../..';
2
- import { Clock } from 'spica/clock';
2
+ import { TClock } from 'spica/tclock';
3
3
  import { TLRU } from 'spica/tlru';
4
4
 
5
5
  // For rerendering in editing.
6
6
 
7
7
  /*
8
- 同一文書内で複数回使用される可能性が低いデータ: Clock
8
+ 同一文書内で複数回使用される可能性が低いデータ: TClock
9
9
  同一文書内で複数回使用される可能性が高いデータ: TLRU
10
10
 
11
11
  編集時の再描画高速化が主目的であるためブロックを周期とするループおよび
@@ -22,7 +22,7 @@ import { TLRU } from 'spica/tlru';
22
22
  */
23
23
 
24
24
  export const caches: Caches = {
25
- code: new Clock<string, HTMLElement>(1000),
25
+ code: new TClock<string, HTMLElement>(1000),
26
26
  math: new TLRU<string, HTMLElement>(1000),
27
- media: new Clock<string, HTMLElement>(100),
27
+ media: new TClock<string, HTMLElement>(100),
28
28
  } as const;
@@ -34,7 +34,7 @@ describe('Unit: parser/block/heading', () => {
34
34
  it('basic', () => {
35
35
  assert.deepStrictEqual(inspect(parser('# a')), [['<h1 id="index::a">a</h1>'], '']);
36
36
  assert.deepStrictEqual(inspect(parser('# a ')), [['<h1 id="index::a">a</h1>'], '']);
37
- assert.deepStrictEqual(inspect(parser('# a b c \n')), [['<h1 id="index::a_b_c">a b c</h1>'], '']);
37
+ assert.deepStrictEqual(inspect(parser('# a b c \n')), [['<h1 id="index::a_b__c">a b c</h1>'], '']);
38
38
  assert.deepStrictEqual(inspect(parser('# a\n')), [['<h1 id="index::a">a</h1>'], '']);
39
39
  assert.deepStrictEqual(inspect(parser('# *a*`b`${c}$')), [['<h1 id="index::a`b`${c}$"><em>a</em><code data-src="`b`">b</code><span class="math" translate="no" data-src="${c}$">${c}$</span></h1>'], '']);
40
40
  assert.deepStrictEqual(inspect(parser('# a\\')), [['<h1 id="index::a">a</h1>'], '']);
@@ -45,7 +45,7 @@ describe('Unit: parser/inline/extension/index', () => {
45
45
  assert.deepStrictEqual(inspect(parser('[#a ]')), [['<a class="index" href="#index::a">a</a>'], '']);
46
46
  assert.deepStrictEqual(inspect(parser('[#a ]')), [['<a class="index" href="#index::a">a</a>'], '']);
47
47
  assert.deepStrictEqual(inspect(parser('[#a b]')), [['<a class="index" href="#index::a_b">a b</a>'], '']);
48
- assert.deepStrictEqual(inspect(parser('[#a b]')), [['<a class="index" href="#index::a_b">a b</a>'], '']);
48
+ assert.deepStrictEqual(inspect(parser('[#a b]')), [['<a class="index" href="#index::a__b">a b</a>'], '']);
49
49
  assert.deepStrictEqual(inspect(parser('[#a \\ ]')), [['<a class="index" href="#index::a">a</a>'], '']);
50
50
  assert.deepStrictEqual(inspect(parser('[#a &nbsp;]')), [['<a class="index" href="#index::a">a</a>'], '']);
51
51
  assert.deepStrictEqual(inspect(parser('[#a <wbr>]')), [['<a class="index" href="#index::a">a</a>'], '']);
@@ -84,7 +84,7 @@ describe('Unit: parser/inline/extension/index', () => {
84
84
  assert.deepStrictEqual(inspect(parser('[#a|\\b]')), [['<a class="index" href="#index::b">a<span class="indexer" data-index="b"></span></a>'], '']);
85
85
  assert.deepStrictEqual(inspect(parser('[#a|*b*]')), [['<a class="index" href="#index::*b*">a<span class="indexer" data-index="*b*"></span></a>'], '']);
86
86
  assert.deepStrictEqual(inspect(parser('[#a|b c]')), [['<a class="index" href="#index::b_c">a<span class="indexer" data-index="b_c"></span></a>'], '']);
87
- assert.deepStrictEqual(inspect(parser('[#a|b c]')), [['<a class="index" href="#index::b_c">a<span class="indexer" data-index="b_c"></span></a>'], '']);
87
+ assert.deepStrictEqual(inspect(parser('[#a|b c]')), [['<a class="index" href="#index::b__c">a<span class="indexer" data-index="b__c"></span></a>'], '']);
88
88
  assert.deepStrictEqual(inspect(parser('[#a|[]]')), [['<a class="index" href="#index::[]">a<span class="indexer" data-index="[]"></span></a>'], '']);
89
89
  assert.deepStrictEqual(inspect(parser('[#a|&copy;]')), [['<a class="index" href="#index::&amp;copy;">a<span class="indexer" data-index="&amp;copy;"></span></a>'], '']);
90
90
  assert.deepStrictEqual(inspect(parser('[#a |b]')), [['<a class="index" href="#index::b">a<span class="indexer" data-index="b"></span></a>'], '']);
@@ -9,42 +9,82 @@ export function indexee(parser: Parser<HTMLElement, MarkdownParser.Context>, opt
9
9
  return fmap(parser, ([el], _, { id }) => [define(el, { id: identity(id, index(el, optional)) })]);
10
10
  }
11
11
 
12
- export function identity(id: string | undefined, text: string, type: 'index' | 'mark' | '' = 'index'): string | undefined {
13
- assert(!id?.match(/[^0-9a-z/-]/i));
12
+ const MAX = 60;
13
+ const ELLIPSIS = '...';
14
+ const PART = (MAX - ELLIPSIS.length) / 2 | 0;
15
+ const REM = MAX - PART * 2 - ELLIPSIS.length;
16
+ export function identity(
17
+ id: string | undefined,
18
+ text: string,
19
+ type: 'index' | 'mark' | '' = 'index',
20
+ ): string | undefined {
21
+ assert(id?.match(/^[0-9a-z/-]*$/i) ?? true);
14
22
  assert(!text.includes('\n'));
15
23
  if (id === '') return undefined;
16
- text &&= text.trim().replace(/\s\s+/g, ' ');
24
+ text = text.trim();
17
25
  if (text === '') return undefined;
18
- const hash = text.replace(/\s/g, '_');
19
- if (hash.length <= 120 || type === '') return `${type}:${id ?? ''}:${hash}`;
20
- const cs = [...text];
21
- if (cs.length <= 120) return `${type}:${id ?? ''}:${hash}`;
22
- const ellipsis = '...';
23
- const len = (120 - ellipsis.length * 2) / 3 | 0;
26
+ const str = text.replace(/\s/g, '_');
27
+ const cs = [...str];
28
+ if (cs.length <= MAX || type === '') return `${type}:${id ?? ''}:${str}`;
24
29
  switch (type) {
25
30
  case 'index':
26
31
  case 'mark':
27
- const s1 = hash.slice(0, cs.slice(0, len).join('').trimEnd().length);
28
- const s3 = hash.slice(-cs.slice(-len).join('').trimStart().length);
29
- const s2 = cs.slice(cs.length / 2 - len / 2 - (len - s1.length) | 0).slice(0, len + len - s3.length).join('').trim().replace(/\s/g, '_');
30
- return `${type}:${id ?? ''}:${s1}${ellipsis}${s2}${ellipsis}${s3}`;
32
+ const s1 = cs.slice(0, PART + REM).join('');
33
+ const s2 = cs.slice(-PART).join('');
34
+ assert([...`${s1}${ELLIPSIS}${s2}`].length === MAX);
35
+ return `${type}:${id ?? ''}:${s1}${ELLIPSIS}${s2}=${hash(text).toString(36)}`;
31
36
  }
32
37
  assert(false);
33
38
  }
34
- assert(identity(undefined, '0'.repeat(120 - 1) + 1)!.slice(7) === '0'.repeat(120 - 1) + 1);
35
- assert(identity(undefined, '0'.repeat(41) + '1'.repeat(38) + '2'.repeat(41) + 3)!.slice(7) === '0'.repeat(38) + '...' + '1'.repeat(38) + '...' + '2'.repeat(38 - 1) + 3);
36
- assert(identity(undefined, '0'.repeat(81) + '1'.repeat(38) + '2'.repeat(81) + 3)!.slice(7) === '0'.repeat(38) + '...' + '1'.repeat(38) + '...' + '2'.repeat(38 - 1) + 3);
39
+ assert.deepStrictEqual(
40
+ identity(undefined, `${'0'.repeat(MAX - 1)}1`)!.slice(7),
41
+ `${'0'.repeat(MAX - 1)}1`);
42
+ assert.deepStrictEqual(
43
+ identity(undefined, `0${'1'.repeat(MAX / 2)}${'2'.repeat(MAX / 2)}3`)!.slice(7),
44
+ `0${'1'.repeat(PART + REM - 1)}${ELLIPSIS}${'2'.repeat(PART - 1)}3=mhy513`);
45
+ assert.deepStrictEqual(
46
+ identity(undefined, `0${'1'.repeat(MAX * 2)}${'2'.repeat(MAX * 2)}3`)!.slice(7),
47
+ `0${'1'.repeat(PART + REM - 1)}${ELLIPSIS}${'2'.repeat(PART - 1)}3=12jqtiv`);
48
+ assert.deepStrictEqual(
49
+ identity(undefined, ` ${'0 '.repeat(MAX)}`)!.slice(7),
50
+ identity(undefined, ` ${'0 '.repeat(MAX)}`.trim())!.slice(7));
51
+ assert.notDeepStrictEqual(
52
+ identity(undefined, `${'0 '.repeat(MAX)}`)!.slice(7),
53
+ identity(undefined, `${'0_'.repeat(MAX)}`)!.slice(7));
54
+ function hash(source: string): number {
55
+ let x = 0;
56
+ for (let i = 0; i < source.length; ++i) {
57
+ x ^= source.charCodeAt(i) << 1 | 1; // 16+1bit
58
+ x ^= x << 13; // shift <= 32-17bit
59
+ x ^= x >>> 17;
60
+ x ^= x << 15;
61
+ }
62
+ return x >>> 0;
63
+ }
64
+ assert(hash('\x00') !== 0);
65
+ assert(hash('\x01') !== 0);
66
+ assert(hash('\x00') !== hash(String.fromCharCode(1 << 15)));
37
67
 
38
68
  export function index(source: Element, optional = false): string {
39
69
  assert(!source.matches('.indexer'));
40
70
  assert(source.querySelectorAll(':scope > .indexer').length <= 1);
41
71
  if (!source.firstChild) return '';
42
72
  const indexer = source.querySelector(':scope > .indexer');
43
- const index = indexer?.getAttribute('data-index');
44
- if (index) return index;
45
- if (index === '' && optional) return '';
73
+ const text = indexer?.getAttribute('data-index');
74
+ if (text === '' && optional) return '';
75
+ if (text) return [...text].length <= MAX ? text : text.replace(/=\w{1,7}$/, '');
46
76
  return signature(source);
47
77
  }
78
+ assert.deepStrictEqual(
79
+ index(define(document.createElement('b'), [
80
+ define(document.createElement('b'), { class: 'indexer', 'data-index': '0'.repeat(MAX - 2) + '=0' })
81
+ ])),
82
+ '0'.repeat(MAX - 2) + '=0');
83
+ assert.deepStrictEqual(
84
+ index(define(document.createElement('b'), [
85
+ define(document.createElement('b'), { class: 'indexer', 'data-index': '0'.repeat(MAX) + '=0' })
86
+ ])),
87
+ '0'.repeat(MAX));
48
88
 
49
89
  export function signature(source: Element | DocumentFragment): string {
50
90
  assert(!navigator.userAgent.includes('Chrome') || !source.querySelector('br:not(:has(+ :is(ul, ol)))'));
@@ -24,7 +24,7 @@ describe('Unit: parser/inline/extension/indexer', () => {
24
24
  assert.deepStrictEqual(inspect(parser(' [|a ]')), [['<span class="indexer" data-index="a"></span>'], '']);
25
25
  assert.deepStrictEqual(inspect(parser(' [|a ]')), [['<span class="indexer" data-index="a"></span>'], '']);
26
26
  assert.deepStrictEqual(inspect(parser(' [|a b]')), [['<span class="indexer" data-index="a_b"></span>'], '']);
27
- assert.deepStrictEqual(inspect(parser(' [|a b]')), [['<span class="indexer" data-index="a_b"></span>'], '']);
27
+ assert.deepStrictEqual(inspect(parser(' [|a b]')), [['<span class="indexer" data-index="a__b"></span>'], '']);
28
28
  assert.deepStrictEqual(inspect(parser(' [|A]')), [['<span class="indexer" data-index="A"></span>'], '']);
29
29
  assert.deepStrictEqual(inspect(parser(' [|*A*]')), [['<span class="indexer" data-index="*A*"></span>'], '']);
30
30
  assert.deepStrictEqual(inspect(parser(' [|`A`]')), [['<span class="indexer" data-index="`A`"></span>'], '']);
@@ -85,7 +85,7 @@ function build(
85
85
  ++iSplitters) {
86
86
  if (~iSplitters << 32 - 8 === 0) yield;
87
87
  if (!scope && el.parentNode !== target) continue;
88
- if (el.tagName === 'OL' && el.nextElementSibling !== splitters[iSplitters + 1]) {
88
+ if (el.tagName === 'OL' && (el.nextElementSibling !== splitters[iSplitters + 1] || defs.size === 0)) {
89
89
  assert(el.matches(`.${syntax}s`));
90
90
  el.remove();
91
91
  continue;