securemark 0.280.9 → 0.281.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.280.9",
3
+ "version": "0.281.0",
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,31 +9,59 @@ 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 {
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 {
13
21
  assert(!id?.match(/[^0-9a-z/-]/i));
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
+ if (str.length <= MAX || type === '') return `${type}:${id ?? ''}:${str}`;
28
+ const cs = [...str];
29
+ if (cs.length <= MAX) return `${type}:${id ?? ''}:${str}`;
24
30
  switch (type) {
25
31
  case 'index':
26
32
  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}`;
33
+ const s1 = cs.slice(0, PART + REM).join('');
34
+ const s2 = cs.slice(-PART).join('');
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
+ function hash(source: string): number {
40
+ let x = 1;
41
+ assert(x !== 0);
42
+ for (let i = 0; i < source.length; ++i) {
43
+ x ^= x << 13;
44
+ x ^= x >>> 17;
45
+ x ^= x << 15;
46
+ x ^= source.charCodeAt(i) << 11;
47
+ }
48
+ return x >>> 0;
49
+ }
50
+ assert.deepStrictEqual(
51
+ identity(undefined, `${'0'.repeat(MAX - 1)}1`)!.slice(7),
52
+ `${'0'.repeat(MAX - 1)}1`);
53
+ assert.deepStrictEqual(
54
+ identity(undefined, `0${'1'.repeat(MAX / 2)}${'2'.repeat(MAX / 2)}3`)!.slice(7),
55
+ `0${'1'.repeat(PART + REM - 1)}${ELLIPSIS}${'2'.repeat(PART - 1)}3=x8ujbi`);
56
+ assert.deepStrictEqual(
57
+ identity(undefined, `0${'1'.repeat(MAX * 2)}${'2'.repeat(MAX * 2)}3`)!.slice(7),
58
+ `0${'1'.repeat(PART + REM - 1)}${ELLIPSIS}${'2'.repeat(PART - 1)}3=1c1m3g9`);
59
+ assert.deepStrictEqual(
60
+ identity(undefined, ` ${'0 '.repeat(MAX)}`)!.slice(7),
61
+ identity(undefined, ` ${'0 '.repeat(MAX)}`.trim())!.slice(7));
62
+ assert.notDeepStrictEqual(
63
+ identity(undefined, `${'0 '.repeat(MAX)}`)!.slice(7),
64
+ identity(undefined, `${'0_'.repeat(MAX)}`)!.slice(7));
37
65
 
38
66
  export function index(source: Element, optional = false): string {
39
67
  assert(!source.matches('.indexer'));
@@ -41,7 +69,7 @@ export function index(source: Element, optional = false): string {
41
69
  if (!source.firstChild) return '';
42
70
  const indexer = source.querySelector(':scope > .indexer');
43
71
  const index = indexer?.getAttribute('data-index');
44
- if (index) return index;
72
+ if (index) return index.replace(/=\w+$/, '');
45
73
  if (index === '' && optional) return '';
46
74
  return signature(source);
47
75
  }
@@ -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;