rapier-math 1.0.2 → 1.1.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.
Files changed (64) hide show
  1. package/README.md +187 -85
  2. package/adoption-check.mjs +149 -0
  3. package/check.mjs +21 -0
  4. package/classic.d.ts +4 -0
  5. package/delimiters.d.ts +8 -0
  6. package/delimiters.mjs +39 -0
  7. package/examples/README.md +25 -0
  8. package/examples/browser.html +10 -0
  9. package/examples/browser.mjs +24 -0
  10. package/examples/formula.mjs +12 -0
  11. package/examples/markdown.mjs +9 -0
  12. package/examples/react.tsx +19 -0
  13. package/examples/stream.mjs +19 -0
  14. package/examples/unified.mjs +26 -0
  15. package/examples/worker.mjs +7 -0
  16. package/index.d.ts +43 -0
  17. package/index.mjs +43 -15
  18. package/integration.d.ts +9 -0
  19. package/integration.mjs +28 -0
  20. package/markdown-it.d.ts +6 -0
  21. package/markdown-it.mjs +45 -0
  22. package/markdown-rules.mjs +65 -0
  23. package/math-components.json +6 -6
  24. package/package.json +78 -5
  25. package/{rapier-math-1.0.2.js → rapier-math-1.1.0.js} +43 -15
  26. package/rehype.d.ts +6 -0
  27. package/rehype.mjs +37 -0
  28. package/src/math/README.md +210 -0
  29. package/src/math/adoption-verify.mjs +149 -0
  30. package/src/math/api.js +35 -13
  31. package/src/math/browser.js +7 -1
  32. package/src/math/classic.d.ts +4 -0
  33. package/src/math/delimiters.d.ts +8 -0
  34. package/src/math/delimiters.mjs +39 -0
  35. package/src/math/examples/README.md +25 -0
  36. package/src/math/examples/browser.html +10 -0
  37. package/src/math/examples/browser.mjs +24 -0
  38. package/src/math/examples/formula.mjs +12 -0
  39. package/src/math/examples/markdown.mjs +9 -0
  40. package/src/math/examples/react.tsx +19 -0
  41. package/src/math/examples/stream.mjs +19 -0
  42. package/src/math/examples/unified.mjs +26 -0
  43. package/src/math/examples/worker.mjs +7 -0
  44. package/src/math/index.d.ts +43 -0
  45. package/src/math/integration.d.ts +9 -0
  46. package/src/math/integration.mjs +28 -0
  47. package/src/math/layout.js +1 -1
  48. package/src/math/markdown-it.d.ts +6 -0
  49. package/src/math/markdown-it.mjs +45 -0
  50. package/src/math/markdown-rules.mjs +65 -0
  51. package/src/math/package-build.mjs +4 -4
  52. package/src/math/package-check.mjs +118 -0
  53. package/src/math/package-readme.mjs +8 -0
  54. package/src/math/package-verify.mjs +7 -2
  55. package/src/math/rehype.d.ts +6 -0
  56. package/src/math/rehype.mjs +37 -0
  57. package/src/math/stream.d.ts +14 -0
  58. package/src/math/stream.mjs +46 -0
  59. package/src/package.json +1 -1
  60. package/src/shell/math-resources.json +176 -17
  61. package/src/tools/build-math.mjs +19 -3
  62. package/stream.d.ts +14 -0
  63. package/stream.mjs +46 -0
  64. package/verify.mjs +7 -2
@@ -0,0 +1,6 @@
1
+ // SPDX-License-Identifier: MIT
2
+ import type {Root} from 'hast';
3
+ import type {VFile} from 'vfile';
4
+ import type {IntegrationOptions} from './integration.js';
5
+ /** Install after sanitization. Recognizes remark-rehype math code nodes. */
6
+ export default function rehypeRapierMath(options?: IntegrationOptions): (tree: Root, file: VFile) => void;
@@ -0,0 +1,37 @@
1
+ // SPDX-License-Identifier: MIT
2
+ import {fromHtml} from 'hast-util-from-html';
3
+ import {createFormulaRenderer} from './integration.mjs';
4
+
5
+ // Run AFTER the host sanitizer. Only known math code nodes are replaced, using
6
+ // fresh renderer output parsed into HAST. Authored raw nodes never become HTML here.
7
+ export default function rehypeRapierMath(options = {}) {
8
+ return (tree, file) => {
9
+ const render = createFormulaRenderer(options);
10
+ function math(node) {
11
+ const names = node?.properties?.className;
12
+ if (node?.type !== 'element' || node.tagName !== 'code' || !Array.isArray(names) ||
13
+ !names.includes('language-math') || !names.some(name => name === 'math-inline' || name === 'math-display') ||
14
+ !Array.isArray(node.children) || !node.children.every(child => child.type === 'text' && typeof child.value === 'string')) return null;
15
+ return {source: node.children.map(child => child.value).join(''), display: names.includes('math-display')};
16
+ }
17
+ function visit(parent) {
18
+ for (let index = 0; index < (parent.children?.length || 0); index++) {
19
+ const node = parent.children[index];
20
+ const code = node.type === 'element' && node.tagName === 'pre' && node.children?.length === 1 ? node.children[0] : node;
21
+ const input = math(code);
22
+ if (!input) { visit(node); continue; }
23
+ const result = render(input.source, input.display);
24
+ if (result.errors.length) {
25
+ for (const error of result.errors) {
26
+ const message = file?.message(error.message, {place: code.position, source: 'rapier-math', ruleId: error.code});
27
+ if (message) { message.mathSource = input.source; message.mathOffset = error.offset; }
28
+ }
29
+ continue;
30
+ }
31
+ const svg = fromHtml(result.svg, {fragment: true}).children[0];
32
+ parent.children[index] = input.display ? {type: 'element', tagName: 'div', properties: {className: ['math-display']}, children: [svg]} : svg;
33
+ }
34
+ }
35
+ visit(tree);
36
+ };
37
+ }
@@ -0,0 +1,14 @@
1
+ // SPDX-License-Identifier: MIT
2
+ export interface MarkdownSnapshot {source: string; html: string; revision: number; final: boolean}
3
+ export interface MarkdownStream {
4
+ /** null means keep the previous preview; the chunk was still accepted. */
5
+ write(chunk: string): MarkdownSnapshot | null;
6
+ finish(): MarkdownSnapshot;
7
+ abort(): void;
8
+ /** Replace a message by resetting, then writing the replacement source. */
9
+ reset(): number;
10
+ readonly revision: number;
11
+ }
12
+ export function createMarkdownStream(renderMarkdown: (source: string) => string, options?: {
13
+ maxSourceLength?: number; previewEvery?: number;
14
+ }): MarkdownStream;
@@ -0,0 +1,46 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Full Markdown previews have no append-only promise. Geometric admission bounds
3
+ // the total source passed to a synchronous host, including the final batch pass.
4
+ export function createMarkdownStream(renderMarkdown, {maxSourceLength = 1_000_000, previewEvery = 256} = {}) {
5
+ if (typeof renderMarkdown !== 'function') throw new TypeError('Supply a synchronous Markdown renderer.');
6
+ for (const value of [maxSourceLength, previewEvery])
7
+ if (!Number.isSafeInteger(value) || value < 1) throw new RangeError('Stream limits must be positive finite integers.');
8
+ let chunks = [], length = 0, threshold = previewEvery, closed = false, revision = 0, rendering = false;
9
+ function open() {
10
+ if (closed || rendering) throw new Error(closed ? 'This stream is closed; reset it before writing.' : 'A stream renderer cannot reenter its stream.');
11
+ }
12
+ function snapshot(final) {
13
+ const source = chunks.join('');
14
+ chunks = source ? [source] : [];
15
+ // Failed previews consumed work too. Further chunks must still wait for the
16
+ // next geometric threshold; only an explicit finish retries immediately.
17
+ threshold = Math.max(previewEvery, length * 2);
18
+ rendering = true;
19
+ let html;
20
+ try {
21
+ html = renderMarkdown(source);
22
+ if (typeof html !== 'string') throw new TypeError('The Markdown renderer must return a string synchronously.');
23
+ } finally { rendering = false; }
24
+ if (final) { closed = true; chunks = []; length = 0; }
25
+ return {source, html, revision, final};
26
+ }
27
+ return {
28
+ write(chunk) {
29
+ open();
30
+ if (typeof chunk !== 'string') throw new TypeError('Decode transport bytes to strings before writing.');
31
+ if (chunk.length > maxSourceLength - length)
32
+ throw Object.assign(new RangeError('The message exceeds its source budget; this chunk was not accepted.'), {code: 'resource-limit'});
33
+ if (!chunk.length) return null;
34
+ chunks.push(chunk); length += chunk.length; revision++;
35
+ return length >= threshold ? snapshot(false) : null;
36
+ },
37
+ finish() { open(); revision++; return snapshot(true); },
38
+ abort() {
39
+ if (rendering) throw new Error('A stream renderer cannot abort its stream.');
40
+ if (closed) return;
41
+ chunks = []; length = 0; closed = true; revision++;
42
+ },
43
+ reset() { if (rendering) throw new Error('A stream renderer cannot reset its stream.'); chunks = []; length = 0; threshold = previewEvery; closed = false; return ++revision; },
44
+ get revision() { return revision; },
45
+ };
46
+ }
package/src/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rapier-math-source",
3
3
  "private": true,
4
- "version": "1.0.2",
4
+ "version": "1.1.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "build": "node tools/build-math.mjs"
@@ -1,11 +1,11 @@
1
1
  {
2
- "bundle": "rapier-math-1.0.2.js",
3
- "version": "1.0.2",
2
+ "bundle": "rapier-math-1.1.0.js",
3
+ "version": "1.1.0",
4
4
  "license": "MIT AND LicenseRef-GUST-Font-License",
5
- "bytes": 712164,
6
- "gzipBytes": 441919,
7
- "sha256": "55c326429ed51564670c67fb4e35eddbe4115fb077440146e4fbb5f81a247f9a",
8
- "sha384": "Pg2MvS4Eg4s41yUZiSPn+TCRuA4gM3JHxxbCEkuSFyHgC+t143Iv48n8P4XlkE23",
5
+ "bytes": 714051,
6
+ "gzipBytes": 442525,
7
+ "sha256": "22e5e40abbd1087922335893ad546fb7ffaeaa710338c97e5686e7b7b0b62021",
8
+ "sha384": "dYIPrqCf4rrTcAKw1Zzzo7vujbNtk2C7Zik/tIYOaLVT0U66WOXcGNE6waFbKych",
9
9
  "sources": [
10
10
  {
11
11
  "file": "math/font.js",
@@ -24,8 +24,8 @@
24
24
  },
25
25
  {
26
26
  "file": "math/layout.js",
27
- "bytes": 37583,
28
- "sha256": "04c6d931b4c81495adb8bfdfa70093625cde4faf5aa16a4cb3baf84e9106c4e8"
27
+ "bytes": 37632,
28
+ "sha256": "84f0abeb24907f370754e30e5cf0576cb2346b941815494c9958892f3d1cc939"
29
29
  },
30
30
  {
31
31
  "file": "math/fallbacks.js",
@@ -39,13 +39,13 @@
39
39
  },
40
40
  {
41
41
  "file": "math/browser.js",
42
- "bytes": 9598,
43
- "sha256": "fe55a2ffe969f9f2b30fe20357e85089afbd41ecfa983222131ec06855f1f2e8"
42
+ "bytes": 10048,
43
+ "sha256": "5a9b1ee1b8c55a5560058b0fcfb5d6623f7de74e49bcb4281acdf7c97b235101"
44
44
  },
45
45
  {
46
46
  "file": "math/api.js",
47
- "bytes": 5857,
48
- "sha256": "52b34c348f4cd30bf3a7f2805a37d347ea9aa668a8abb2dc0b799fe749bf8ee1"
47
+ "bytes": 7245,
48
+ "sha256": "66a2c6911aeba9d8f01003b37c132d25da06e9eaffddf0a785b35033aea5d50b"
49
49
  }
50
50
  ],
51
51
  "recipe": [
@@ -66,18 +66,43 @@
66
66
  },
67
67
  {
68
68
  "file": "math/package-verify.mjs",
69
- "bytes": 2691,
70
- "sha256": "6a53081729c020b7840363812138264195cb851a297c5542ef7aca007fdfc61f"
69
+ "bytes": 3314,
70
+ "sha256": "1ceddafe4f989313d4e0b733bb47572c9c6d62fcb06966048fc64402f0960dcb"
71
71
  },
72
72
  {
73
73
  "file": "tools/build-math.mjs",
74
- "bytes": 6012,
75
- "sha256": "5698ec3e5aaa6a3ad833ceb240a851c0447d23ebf80455e95ef7003aa6287342"
74
+ "bytes": 7105,
75
+ "sha256": "d9959884cb2d1e0cc3c09e98b165a2e25f0cec55d7a963a5d810a4167f223cd2"
76
76
  },
77
77
  {
78
78
  "file": "math/LICENSE-MIT",
79
79
  "bytes": 1071,
80
80
  "sha256": "a3bc5d619ab9159f4eef2924e48dfb935556f4bc163f5ea61610b66a5915de70"
81
+ },
82
+ {
83
+ "file": "math/adoption-verify.mjs",
84
+ "bytes": 8253,
85
+ "sha256": "f497b40c03cb9ba2a053b91832ca7889e1c60b5eaf422a10d06f7a6ce7068446"
86
+ },
87
+ {
88
+ "file": "math/package-build.mjs",
89
+ "bytes": 1659,
90
+ "sha256": "b2a72f64a11b7bc58472e373080e9bb4356eda32bc7afcef72556b0c106b2225"
91
+ },
92
+ {
93
+ "file": "math/package-check.mjs",
94
+ "bytes": 7408,
95
+ "sha256": "0c7ba3d257ad1fba3e22b1dde2dd3cce281d3d7143cce726b36f89ac4c413e8b"
96
+ },
97
+ {
98
+ "file": "math/package-readme.mjs",
99
+ "bytes": 427,
100
+ "sha256": "1d3bc00108614b72978c4209d6b718f473e2ce3ab2d0f3008a803fcf68235616"
101
+ },
102
+ {
103
+ "file": "math/README.md",
104
+ "bytes": 14092,
105
+ "sha256": "480cec1bd6557d959dcae8aeb5e07405768dcbd780be2ff967042cfc9422318d"
81
106
  }
82
107
  ],
83
108
  "components": [
@@ -101,5 +126,139 @@
101
126
  "licenseFile": "math/FONT-LICENSE.txt",
102
127
  "licenseSha256": "91ded9a2a371cbeff028bfaf3bd3e97fddfb68af6f186aa261bfaff35baf800c",
103
128
  "licenseBytes": 21246
104
- }
129
+ },
130
+ "adoption": [
131
+ {
132
+ "file": "math/delimiters.mjs",
133
+ "packageFile": "delimiters.mjs",
134
+ "bytes": 1945,
135
+ "sha256": "2847d23ba78534b32348d5e642ded601e4f6fd9c41e10d80c46955d440d24eba"
136
+ },
137
+ {
138
+ "file": "math/markdown-rules.mjs",
139
+ "packageFile": "markdown-rules.mjs",
140
+ "bytes": 3255,
141
+ "sha256": "3c2c1660f669eac88fbee4259e83f9227ed651e2e4bb228a861c5a1d8518f6a7"
142
+ },
143
+ {
144
+ "file": "math/integration.mjs",
145
+ "packageFile": "integration.mjs",
146
+ "bytes": 1493,
147
+ "sha256": "5bd61aa59e78cbab37219b9a569f3b5ad9c5a7723800abc7f3fe7af6b82ae7a6"
148
+ },
149
+ {
150
+ "file": "math/markdown-it.mjs",
151
+ "packageFile": "markdown-it.mjs",
152
+ "bytes": 2113,
153
+ "sha256": "d57abc5dc3e5a31b6be4507b2f5c57df8431763b8d88feaadbf242cb09af6c9d"
154
+ },
155
+ {
156
+ "file": "math/rehype.mjs",
157
+ "packageFile": "rehype.mjs",
158
+ "bytes": 2017,
159
+ "sha256": "7cc9c9d2e7c9b9667368cfc1a5a4f5f86d218b7c8a1acb8d2e12334d41e83bc8"
160
+ },
161
+ {
162
+ "file": "math/stream.mjs",
163
+ "packageFile": "stream.mjs",
164
+ "bytes": 2530,
165
+ "sha256": "2b26a895e531906be931af6f9f3764297d1ff98bf56875e9df457f82938fc03d"
166
+ },
167
+ {
168
+ "file": "math/index.d.ts",
169
+ "packageFile": "index.d.ts",
170
+ "bytes": 1698,
171
+ "sha256": "bc14a284cd5caffdf5bfe6825c49c360d313e693bbc0280aa924fd9fc594beb3"
172
+ },
173
+ {
174
+ "file": "math/classic.d.ts",
175
+ "packageFile": "classic.d.ts",
176
+ "bytes": 123,
177
+ "sha256": "dfc82014f54ed7364171cb73cc8bc118aba4a5929349f762da82988977f1593d"
178
+ },
179
+ {
180
+ "file": "math/delimiters.d.ts",
181
+ "packageFile": "delimiters.d.ts",
182
+ "bytes": 479,
183
+ "sha256": "db74cfb4fdd6d87e7f47a5de8ee3a4b78f409a4fe3cd2edeaa8e77660a489617"
184
+ },
185
+ {
186
+ "file": "math/integration.d.ts",
187
+ "packageFile": "integration.d.ts",
188
+ "bytes": 370,
189
+ "sha256": "4b3c53b01b34de3b328ef609e65aa3aa530fabc3522238bde335cb8c839cdda3"
190
+ },
191
+ {
192
+ "file": "math/markdown-it.d.ts",
193
+ "packageFile": "markdown-it.d.ts",
194
+ "bytes": 383,
195
+ "sha256": "bafce277328ba397bddbbb5101cc2545ba8b8c15830b47f2dc1f05af85de4b1d"
196
+ },
197
+ {
198
+ "file": "math/rehype.d.ts",
199
+ "packageFile": "rehype.d.ts",
200
+ "bytes": 340,
201
+ "sha256": "8f6b230284e64fb483d1ed2c6ed9420958cf1d7e609f71aee62322cabe56339b"
202
+ },
203
+ {
204
+ "file": "math/stream.d.ts",
205
+ "packageFile": "stream.d.ts",
206
+ "bytes": 630,
207
+ "sha256": "1b12bc176fdd243db75d247ff4dbb9b4787945167a033154a0d222ad68da4ff0"
208
+ },
209
+ {
210
+ "file": "math/examples/formula.mjs",
211
+ "packageFile": "examples/formula.mjs",
212
+ "bytes": 619,
213
+ "sha256": "43e3708d50737d1eac74f5132146838b58b733c5b0315b50d6eecd594ecb9e7d"
214
+ },
215
+ {
216
+ "file": "math/examples/markdown.mjs",
217
+ "packageFile": "examples/markdown.mjs",
218
+ "bytes": 375,
219
+ "sha256": "6dc4789538861d478f9afba403574b3f88928db4e9d4e4def88db87af8561641"
220
+ },
221
+ {
222
+ "file": "math/examples/stream.mjs",
223
+ "packageFile": "examples/stream.mjs",
224
+ "bytes": 841,
225
+ "sha256": "4b1feb25d38024a6f8531bb3353d0eea993628dcd1f97e736d42c9e076ec655b"
226
+ },
227
+ {
228
+ "file": "math/examples/unified.mjs",
229
+ "packageFile": "examples/unified.mjs",
230
+ "bytes": 1103,
231
+ "sha256": "9002f3f1f7cbbec2f8f9a4b3cdbde2f418fece7b8395e942441448b7f785ee9b"
232
+ },
233
+ {
234
+ "file": "math/examples/browser.html",
235
+ "packageFile": "examples/browser.html",
236
+ "bytes": 566,
237
+ "sha256": "c671541b4a3f1a4cb9e2b2ef2945778db825590f08a11ad48b768d132b5df96f"
238
+ },
239
+ {
240
+ "file": "math/examples/browser.mjs",
241
+ "packageFile": "examples/browser.mjs",
242
+ "bytes": 940,
243
+ "sha256": "c108898c6d8e8fec228980a06f430684de2dc7a4816208559a4f57a9a4f53c8b"
244
+ },
245
+ {
246
+ "file": "math/examples/react.tsx",
247
+ "packageFile": "examples/react.tsx",
248
+ "bytes": 899,
249
+ "sha256": "bfcbeb5219ce776a9c33f841c0aad222be9b6ceddffe0eb89f344bed170e0765"
250
+ },
251
+ {
252
+ "file": "math/examples/worker.mjs",
253
+ "packageFile": "examples/worker.mjs",
254
+ "bytes": 365,
255
+ "sha256": "612c334036dbf2f36f2357bf26a0a2601c976d4b193cc681facd8e003eaea4d4"
256
+ },
257
+ {
258
+ "file": "math/examples/README.md",
259
+ "packageFile": "examples/README.md",
260
+ "bytes": 1858,
261
+ "sha256": "c462b51cae9435336debca87e7e6407400ff06df3126ac8aeb4becc091cd31bf"
262
+ }
263
+ ]
105
264
  }
@@ -9,10 +9,15 @@ import {gzipSync} from 'node:zlib';
9
9
  import {Script} from 'node:vm';
10
10
 
11
11
  const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
12
- export const MATH_VERSION = '1.0.2';
12
+ export const MATH_VERSION = '1.1.0';
13
13
  export const MATH_BUNDLE = 'rapier-math-' + MATH_VERSION + '.js';
14
14
  export const MATH_SOURCES = ['font.js', 'symbols.js', 'parser.js', 'layout.js', 'fallbacks.js', 'responsive.js', 'browser.js', 'api.js'].map(name => 'math/' + name);
15
- export const MATH_RECIPE = ['math/build-font.py', 'math/FONT-SOURCE.md', 'math/FONT-LICENSE.txt', 'math/package-verify.mjs', 'tools/build-math.mjs', 'math/LICENSE-MIT'];
15
+ export const MATH_RECIPE = ['math/build-font.py', 'math/FONT-SOURCE.md', 'math/FONT-LICENSE.txt', 'math/package-verify.mjs', 'tools/build-math.mjs', 'math/LICENSE-MIT', 'math/adoption-verify.mjs', 'math/package-build.mjs', 'math/package-check.mjs', 'math/package-readme.mjs', 'math/README.md'];
16
+ export const MATH_ADOPTION = ['delimiters.mjs', 'markdown-rules.mjs', 'integration.mjs', 'markdown-it.mjs', 'rehype.mjs', 'stream.mjs',
17
+ 'index.d.ts', 'classic.d.ts', 'delimiters.d.ts', 'integration.d.ts', 'markdown-it.d.ts', 'rehype.d.ts', 'stream.d.ts',
18
+ 'examples/formula.mjs', 'examples/markdown.mjs', 'examples/stream.mjs', 'examples/unified.mjs',
19
+ 'examples/browser.html', 'examples/browser.mjs', 'examples/react.tsx', 'examples/worker.mjs', 'examples/README.md']
20
+ .map(file => ({file: 'math/' + file, packageFile: file}));
16
21
  const FONT = {
17
22
  name: 'Latin Modern Math', version: '1.959', license: 'LicenseRef-GUST-Font-License',
18
23
  licenseName: 'GUST Font License',
@@ -39,7 +44,7 @@ export async function fillMathLicense(markup, {root = ROOT} = {}) {
39
44
  return markup.replace(marker, () => '<pre class="license-text">' + escaped + '</pre>');
40
45
  }
41
46
 
42
- export async function buildMath({root = ROOT, updatePins = true} = {}) {
47
+ export async function assembleMath({root = ROOT} = {}) {
43
48
  const inputs = [];
44
49
  for (const file of MATH_SOURCES) {
45
50
  const bytes = await readFile(resolve(root, file));
@@ -66,6 +71,17 @@ export async function buildMath({root = ROOT, updatePins = true} = {}) {
66
71
  components: [{name: FONT.name, version: FONT.version, license: FONT.license, source: FONT.source, sha256: FONT.sourceSha256}],
67
72
  font: {...FONT, licenseBytes: license.length},
68
73
  };
74
+ resources.adoption = [];
75
+ for (const {file, packageFile} of MATH_ADOPTION) {
76
+ const content = await readFile(resolve(root, file));
77
+ resources.adoption.push({file, packageFile, bytes: content.length, sha256: digest(content)});
78
+ }
79
+ return {resources, bytes};
80
+ }
81
+
82
+ export async function buildMath({root = ROOT, updatePins = true} = {}) {
83
+ const {resources, bytes} = await assembleMath({root});
84
+ const {sha256, sha384, gzipBytes} = resources;
69
85
  const vendor = resolve(root, 'shell/vendor');
70
86
  await mkdir(vendor, {recursive: true});
71
87
  await writeFile(resolve(vendor, MATH_BUNDLE), bytes);
package/stream.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ // SPDX-License-Identifier: MIT
2
+ export interface MarkdownSnapshot {source: string; html: string; revision: number; final: boolean}
3
+ export interface MarkdownStream {
4
+ /** null means keep the previous preview; the chunk was still accepted. */
5
+ write(chunk: string): MarkdownSnapshot | null;
6
+ finish(): MarkdownSnapshot;
7
+ abort(): void;
8
+ /** Replace a message by resetting, then writing the replacement source. */
9
+ reset(): number;
10
+ readonly revision: number;
11
+ }
12
+ export function createMarkdownStream(renderMarkdown: (source: string) => string, options?: {
13
+ maxSourceLength?: number; previewEvery?: number;
14
+ }): MarkdownStream;
package/stream.mjs ADDED
@@ -0,0 +1,46 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Full Markdown previews have no append-only promise. Geometric admission bounds
3
+ // the total source passed to a synchronous host, including the final batch pass.
4
+ export function createMarkdownStream(renderMarkdown, {maxSourceLength = 1_000_000, previewEvery = 256} = {}) {
5
+ if (typeof renderMarkdown !== 'function') throw new TypeError('Supply a synchronous Markdown renderer.');
6
+ for (const value of [maxSourceLength, previewEvery])
7
+ if (!Number.isSafeInteger(value) || value < 1) throw new RangeError('Stream limits must be positive finite integers.');
8
+ let chunks = [], length = 0, threshold = previewEvery, closed = false, revision = 0, rendering = false;
9
+ function open() {
10
+ if (closed || rendering) throw new Error(closed ? 'This stream is closed; reset it before writing.' : 'A stream renderer cannot reenter its stream.');
11
+ }
12
+ function snapshot(final) {
13
+ const source = chunks.join('');
14
+ chunks = source ? [source] : [];
15
+ // Failed previews consumed work too. Further chunks must still wait for the
16
+ // next geometric threshold; only an explicit finish retries immediately.
17
+ threshold = Math.max(previewEvery, length * 2);
18
+ rendering = true;
19
+ let html;
20
+ try {
21
+ html = renderMarkdown(source);
22
+ if (typeof html !== 'string') throw new TypeError('The Markdown renderer must return a string synchronously.');
23
+ } finally { rendering = false; }
24
+ if (final) { closed = true; chunks = []; length = 0; }
25
+ return {source, html, revision, final};
26
+ }
27
+ return {
28
+ write(chunk) {
29
+ open();
30
+ if (typeof chunk !== 'string') throw new TypeError('Decode transport bytes to strings before writing.');
31
+ if (chunk.length > maxSourceLength - length)
32
+ throw Object.assign(new RangeError('The message exceeds its source budget; this chunk was not accepted.'), {code: 'resource-limit'});
33
+ if (!chunk.length) return null;
34
+ chunks.push(chunk); length += chunk.length; revision++;
35
+ return length >= threshold ? snapshot(false) : null;
36
+ },
37
+ finish() { open(); revision++; return snapshot(true); },
38
+ abort() {
39
+ if (rendering) throw new Error('A stream renderer cannot abort its stream.');
40
+ if (closed) return;
41
+ chunks = []; length = 0; closed = true; revision++;
42
+ },
43
+ reset() { if (rendering) throw new Error('A stream renderer cannot reset its stream.'); chunks = []; length = 0; threshold = previewEvery; closed = false; return ++revision; },
44
+ get revision() { return revision; },
45
+ };
46
+ }
package/verify.mjs CHANGED
@@ -15,10 +15,11 @@ const payload = await read(resources.bundle);
15
15
  assert.equal(payload.length, resources.bytes, 'payload length');
16
16
  assert.equal(hash(payload), resources.sha256, 'payload SHA-256');
17
17
  assert.equal(hash(payload, 'sha384', 'base64'), resources.sha384, 'payload SHA-384');
18
- for (const input of [...resources.sources, ...resources.recipe]) {
18
+ for (const input of [...resources.sources, ...resources.recipe, ...resources.adoption]) {
19
19
  const bytes = await read('src/' + input.file);
20
20
  assert.equal(bytes.length, input.bytes, input.file + ': length');
21
21
  assert.equal(hash(bytes), input.sha256, input.file + ': SHA-256');
22
+ if (input.packageFile) assert.deepEqual(await read(input.packageFile), bytes, input.packageFile + ': exact adoption source');
22
23
  }
23
24
  const rendererLicense = await read('src/math/LICENSE-MIT');
24
25
  for (const file of ['LICENSE-MIT', 'math-LICENSE.txt']) assert.deepEqual(await read(file), rendererLicense, file);
@@ -34,7 +35,11 @@ assert.equal(font.licenseDeclared, resources.font.license, 'font license identit
34
35
  assert.equal(sbom.hasExtractedLicensingInfos.find(item => item.licenseId === resources.font.license)?.extractedText,
35
36
  fontLicense.toString('utf8'), 'complete extracted font license');
36
37
  const {buildMath} = await import('./src/tools/build-math.mjs');
38
+ const {mathReadme} = await import('./src/math/package-readme.mjs');
39
+ assert.equal((await read('README.md')).toString('utf8'), mathReadme(resources), 'generated README source');
40
+ for (const [file, owner] of [['check.mjs', 'package-check.mjs'], ['verify.mjs', 'package-verify.mjs'], ['adoption-check.mjs', 'adoption-verify.mjs']])
41
+ assert.deepEqual(await read(file), await read('src/math/' + owner), file + ': exact carried owner');
37
42
  await buildMath({root: source, updatePins: false});
38
43
  assert.deepEqual(await read('src/shell/vendor/' + resources.bundle), payload, 'rebuilt payload bytes');
39
44
  console.log(JSON.stringify({file: resources.bundle, bytes: payload.length, sha256: resources.sha256,
40
- sources: resources.sources.length + resources.recipe.length, rebuild: 'exact', licenses: 'complete'}));
45
+ sources: resources.sources.length + resources.recipe.length, adoptionFiles: resources.adoption.length, rebuild: 'exact', licenses: 'complete'}));