@danielsimonjr/sciengjs 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +183 -3
- package/THIRD_PARTY_NOTICES.md +57 -0
- package/assets/fonts/LiberationSerif-Regular.LICENSE.txt +102 -0
- package/assets/fonts/LiberationSerif-Regular.ttf +0 -0
- package/assets/index_bg.wasm +0 -0
- package/assets/licenses/EPL-2.0.txt +278 -0
- package/assets/licenses/MPL-2.0.txt +374 -0
- package/dist/assets.d.ts +12 -0
- package/dist/assets.js +63 -0
- package/dist/assets.js.map +1 -0
- package/dist/config.d.ts +32 -0
- package/dist/config.js +186 -0
- package/dist/config.js.map +1 -0
- package/dist/core.d.ts +10 -0
- package/dist/core.js +85 -0
- package/dist/core.js.map +1 -0
- package/dist/errors.d.ts +24 -0
- package/dist/errors.js +40 -0
- package/dist/errors.js.map +1 -0
- package/dist/graph/backend-png.d.ts +5 -0
- package/dist/graph/backend-png.js +57 -0
- package/dist/graph/backend-png.js.map +1 -0
- package/dist/graph/backend-svg.d.ts +3 -0
- package/dist/graph/backend-svg.js +12 -0
- package/dist/graph/backend-svg.js.map +1 -0
- package/dist/graph/dot.d.ts +12 -0
- package/dist/graph/dot.js +48 -0
- package/dist/graph/dot.js.map +1 -0
- package/dist/graph/image.d.ts +23 -0
- package/dist/graph/image.js +292 -0
- package/dist/graph/image.js.map +1 -0
- package/dist/graph/index.d.ts +3 -0
- package/dist/graph/index.js +3 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/pairs.d.ts +11 -0
- package/dist/pairs.js +43 -0
- package/dist/pairs.js.map +1 -0
- package/dist/raster/index.d.ts +24 -0
- package/dist/raster/index.js +83 -0
- package/dist/raster/index.js.map +1 -0
- package/dist/raster/warnings.d.ts +12 -0
- package/dist/raster/warnings.js +175 -0
- package/dist/raster/warnings.js.map +1 -0
- package/dist/registry.d.ts +13 -0
- package/dist/registry.js +17 -0
- package/dist/registry.js.map +1 -0
- package/dist/types.d.ts +48 -0
- package/dist/types.js +19 -0
- package/dist/types.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/dist/worker/entry.d.ts +1 -0
- package/dist/worker/entry.js +38 -0
- package/dist/worker/entry.js.map +1 -0
- package/dist/worker/protocol.d.ts +39 -0
- package/dist/worker/protocol.js +2 -0
- package/dist/worker/protocol.js.map +1 -0
- package/dist/worker/run.d.ts +30 -0
- package/dist/worker/run.js +90 -0
- package/dist/worker/run.js.map +1 -0
- package/package.json +71 -6
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
function tableOf(view, tag) {
|
|
2
|
+
if (view.byteLength < 12) {
|
|
3
|
+
return undefined;
|
|
4
|
+
}
|
|
5
|
+
const count = view.getUint16(4);
|
|
6
|
+
for (let i = 0; i < count; i++) {
|
|
7
|
+
const entry = 12 + i * 16;
|
|
8
|
+
if (entry + 16 > view.byteLength) {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
let name = "";
|
|
12
|
+
for (let k = 0; k < 4; k++) {
|
|
13
|
+
name += String.fromCharCode(view.getUint8(entry + k));
|
|
14
|
+
}
|
|
15
|
+
if (name === tag) {
|
|
16
|
+
return { offset: view.getUint32(entry + 8), length: view.getUint32(entry + 12) };
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
function format4(view, start) {
|
|
22
|
+
const segments = view.getUint16(start + 6) / 2;
|
|
23
|
+
const ends = start + 14;
|
|
24
|
+
const starts = ends + segments * 2 + 2;
|
|
25
|
+
const deltas = starts + segments * 2;
|
|
26
|
+
const ranges = deltas + segments * 2;
|
|
27
|
+
return (cp) => {
|
|
28
|
+
if (cp > 0xffff) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
for (let s = 0; s < segments; s++) {
|
|
32
|
+
if (cp > view.getUint16(ends + s * 2)) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const first = view.getUint16(starts + s * 2);
|
|
36
|
+
if (cp < first) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
const offset = view.getUint16(ranges + s * 2);
|
|
40
|
+
let glyph;
|
|
41
|
+
if (offset === 0) {
|
|
42
|
+
glyph = (cp + view.getUint16(deltas + s * 2)) & 0xffff;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
const at = ranges + s * 2 + offset + (cp - first) * 2;
|
|
46
|
+
if (at + 2 > view.byteLength) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
glyph = view.getUint16(at);
|
|
50
|
+
if (glyph !== 0) {
|
|
51
|
+
glyph = (glyph + view.getUint16(deltas + s * 2)) & 0xffff;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return glyph !== 0;
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function format12(view, start) {
|
|
60
|
+
const groups = view.getUint32(start + 12);
|
|
61
|
+
return (cp) => {
|
|
62
|
+
for (let g = 0; g < groups; g++) {
|
|
63
|
+
const at = start + 16 + g * 12;
|
|
64
|
+
if (at + 12 > view.byteLength) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
if (cp >= view.getUint32(at) && cp <= view.getUint32(at + 4)) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return false;
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Reads the Unicode character map of a TrueType or OpenType font (formats 4 and 12).
|
|
76
|
+
* Returns undefined when the font has no readable map, so the caller skips the check.
|
|
77
|
+
*/
|
|
78
|
+
export function fontCovers(font) {
|
|
79
|
+
const view = new DataView(font.buffer, font.byteOffset, font.byteLength);
|
|
80
|
+
try {
|
|
81
|
+
const cmap = tableOf(view, "cmap");
|
|
82
|
+
if (cmap === undefined) {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
const tests = [];
|
|
86
|
+
const count = view.getUint16(cmap.offset + 2);
|
|
87
|
+
for (let i = 0; i < count; i++) {
|
|
88
|
+
const record = cmap.offset + 4 + i * 8;
|
|
89
|
+
const platform = view.getUint16(record);
|
|
90
|
+
const encoding = view.getUint16(record + 2);
|
|
91
|
+
const unicode = platform === 0 || (platform === 3 && (encoding === 1 || encoding === 10));
|
|
92
|
+
if (!unicode) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const sub = cmap.offset + view.getUint32(record + 4);
|
|
96
|
+
const format = view.getUint16(sub);
|
|
97
|
+
if (format === 4) {
|
|
98
|
+
tests.push(format4(view, sub));
|
|
99
|
+
}
|
|
100
|
+
else if (format === 12) {
|
|
101
|
+
tests.push(format12(view, sub));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (tests.length === 0) {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
return (cp) => tests.some((test) => test(cp));
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// A named entity of SVG (amp, lt, gt, quot, apos) stands for an ASCII character, which the check skips. Only a numeric reference can name a character that the font lacks.
|
|
114
|
+
function decodeEntities(text) {
|
|
115
|
+
return text.replace(/&#(x[0-9a-fA-F]+|[0-9]+);/g, (whole, body) => {
|
|
116
|
+
const value = body.startsWith("x") ? parseInt(body.slice(1), 16) : parseInt(body, 10);
|
|
117
|
+
return value <= 0x10ffff ? String.fromCodePoint(value) : whole;
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
function isBold(weight) {
|
|
121
|
+
const value = weight.trim().toLowerCase();
|
|
122
|
+
return value === "bold" || value === "bolder" || (/^[0-9]+$/.test(value) && Number(value) >= 600);
|
|
123
|
+
}
|
|
124
|
+
/** The code point text of an SVG fragment: the markup removed and the entities read. */
|
|
125
|
+
function textOf(markup) {
|
|
126
|
+
return decodeEntities(markup.replace(/<[^>]*>/g, ""));
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Lists what the raster font cannot draw in the text of an SVG. The font has one face, so bold and
|
|
130
|
+
* italic text draw in that face. A character without a glyph draws no pixel. Each kind gives one warning.
|
|
131
|
+
*/
|
|
132
|
+
export function rasterWarnings(svg, font) {
|
|
133
|
+
const covers = fontCovers(font);
|
|
134
|
+
let bold = 0;
|
|
135
|
+
let italic = 0;
|
|
136
|
+
const missing = new Set();
|
|
137
|
+
for (const match of svg.matchAll(/<(?:text|tspan)\b([^>]*)>/g)) {
|
|
138
|
+
const attributes = match[1];
|
|
139
|
+
const weight = /\sfont-weight="([^"]*)"/.exec(attributes);
|
|
140
|
+
if (weight !== null && isBold(weight[1])) {
|
|
141
|
+
bold++;
|
|
142
|
+
}
|
|
143
|
+
const style = /\sfont-style="([^"]*)"/.exec(attributes);
|
|
144
|
+
if (style !== null && (style[1] === "italic" || style[1] === "oblique")) {
|
|
145
|
+
italic++;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (covers !== undefined) {
|
|
149
|
+
for (const match of svg.matchAll(/<text\b[^>]*>([\s\S]*?)<\/text>/g)) {
|
|
150
|
+
for (const ch of textOf(match[1])) {
|
|
151
|
+
const cp = ch.codePointAt(0);
|
|
152
|
+
if (cp > 0x20 && cp !== 0xa0 && !(cp >= 0x7f && cp < 0xa0) && !covers(cp)) {
|
|
153
|
+
missing.add(cp);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const warnings = [];
|
|
159
|
+
if (covers === undefined && /<text\b/.test(svg)) {
|
|
160
|
+
warnings.push("the raster font has no readable character map, so the render cannot check which characters it draws");
|
|
161
|
+
}
|
|
162
|
+
if (bold > 0) {
|
|
163
|
+
warnings.push(`${bold} bold text element(s) draw in the one face of the raster font`);
|
|
164
|
+
}
|
|
165
|
+
if (italic > 0) {
|
|
166
|
+
warnings.push(`${italic} italic text element(s) draw in the one face of the raster font`);
|
|
167
|
+
}
|
|
168
|
+
if (missing.size > 0) {
|
|
169
|
+
const first = [...missing][0];
|
|
170
|
+
const code = "U+" + first.toString(16).toUpperCase().padStart(4, "0");
|
|
171
|
+
warnings.push(`${missing.size} character(s) have no glyph in the raster font and draw blank (first: ${code})`);
|
|
172
|
+
}
|
|
173
|
+
return warnings;
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=warnings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"warnings.js","sourceRoot":"","sources":["../../src/raster/warnings.ts"],"names":[],"mappings":"AAGA,SAAS,OAAO,CAAC,IAAc,EAAE,GAAW;IAC1C,IAAI,IAAI,CAAC,UAAU,GAAG,EAAE,EAAE,CAAC;QACzB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IAChC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC;QAC1B,IAAI,KAAK,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;YACjC,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3B,IAAI,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;QACxD,CAAC;QACD,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjB,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,GAAG,EAAE,CAAC,EAAE,CAAC;QACnF,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,OAAO,CAAC,IAAc,EAAE,KAAa;IAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG,KAAK,GAAG,EAAE,CAAC;IACxB,MAAM,MAAM,GAAG,IAAI,GAAG,QAAQ,GAAG,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,CAAC,CAAC;IACrC,MAAM,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,CAAC,CAAC;IACrC,OAAO,CAAC,EAAE,EAAE,EAAE;QACZ,IAAI,EAAE,GAAG,MAAM,EAAE,CAAC;YAChB,OAAO,KAAK,CAAC;QACf,CAAC;QACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC;YAClC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBACtC,SAAS;YACX,CAAC;YACD,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;YAC7C,IAAI,EAAE,GAAG,KAAK,EAAE,CAAC;gBACf,OAAO,KAAK,CAAC;YACf,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;YAC9C,IAAI,KAAa,CAAC;YAClB,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;gBACjB,KAAK,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC;YACzD,CAAC;iBAAM,CAAC;gBACN,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,GAAG,CAAC,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;gBACtD,IAAI,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;oBAC7B,OAAO,KAAK,CAAC;gBACf,CAAC;gBACD,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;gBAC3B,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;oBAChB,KAAK,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC;gBAC5D,CAAC;YACH,CAAC;YACD,OAAO,KAAK,KAAK,CAAC,CAAC;QACrB,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,IAAc,EAAE,KAAa;IAC7C,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;IAC1C,OAAO,CAAC,EAAE,EAAE,EAAE;QACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAChC,MAAM,EAAE,GAAG,KAAK,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC;YAC/B,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;gBAC9B,OAAO,KAAK,CAAC;YACf,CAAC;YACD,IAAI,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;gBAC7D,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,IAAgB;IACzC,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IACzE,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACnC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,KAAK,GAAmB,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC9C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YAC5C,MAAM,OAAO,GAAG,QAAQ,KAAK,CAAC,IAAI,CAAC,QAAQ,KAAK,CAAC,IAAI,CAAC,QAAQ,KAAK,CAAC,IAAI,QAAQ,KAAK,EAAE,CAAC,CAAC,CAAC;YAC1F,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,SAAS;YACX,CAAC;YACD,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YACrD,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YACnC,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;gBACjB,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;YACjC,CAAC;iBAAM,IAAI,MAAM,KAAK,EAAE,EAAE,CAAC;gBACzB,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;YAClC,CAAC;QACH,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,2KAA2K;AAC3K,SAAS,cAAc,CAAC,IAAY;IAClC,OAAO,IAAI,CAAC,OAAO,CAAC,4BAA4B,EAAE,CAAC,KAAK,EAAE,IAAY,EAAE,EAAE;QACxE,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACtF,OAAO,KAAK,IAAI,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACjE,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,MAAM,CAAC,MAAc;IAC5B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC1C,OAAO,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,QAAQ,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC;AACpG,CAAC;AAED,wFAAwF;AACxF,SAAS,MAAM,CAAC,MAAc;IAC5B,OAAO,cAAc,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,CAAC;AACxD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,IAAgB;IAC1D,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,QAAQ,CAAC,4BAA4B,CAAC,EAAE,CAAC;QAC/D,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAC5B,MAAM,MAAM,GAAG,yBAAyB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC1D,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACzC,IAAI,EAAE,CAAC;QACT,CAAC;QACD,MAAM,KAAK,GAAG,wBAAwB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACxD,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,EAAE,CAAC;YACxE,MAAM,EAAE,CAAC;QACX,CAAC;IACH,CAAC;IACD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,QAAQ,CAAC,kCAAkC,CAAC,EAAE,CAAC;YACrE,KAAK,MAAM,EAAE,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBAClC,MAAM,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,CAAC,CAAW,CAAC;gBACvC,IAAI,EAAE,GAAG,IAAI,IAAI,EAAE,KAAK,IAAI,IAAI,CAAC,CAAC,EAAE,IAAI,IAAI,IAAI,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;oBAC1E,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAClB,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,MAAM,KAAK,SAAS,IAAI,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAChD,QAAQ,CAAC,IAAI,CAAC,qGAAqG,CAAC,CAAC;IACvH,CAAC;IACD,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;QACb,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,+DAA+D,CAAC,CAAC;IACxF,CAAC;IACD,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;QACf,QAAQ,CAAC,IAAI,CAAC,GAAG,MAAM,iEAAiE,CAAC,CAAC;IAC5F,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,KAAK,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,IAAI,GAAG,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QACtE,QAAQ,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,yEAAyE,IAAI,GAAG,CAAC,CAAC;IACjH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** One backend: the module that the worker loads for a (type, format) pair. */
|
|
2
|
+
export interface BackendEntry {
|
|
3
|
+
/** The backend name that a result reports, for example "graph/viz". */
|
|
4
|
+
backend: string;
|
|
5
|
+
/** An absolute file URL of a module that exports `render`. */
|
|
6
|
+
moduleUrl: string;
|
|
7
|
+
}
|
|
8
|
+
/** Maps "type:format" to a backend. A key is missing when no backend is built in. */
|
|
9
|
+
export type Registry = Record<string, BackendEntry>;
|
|
10
|
+
/** The backends of this build. */
|
|
11
|
+
export declare const DEFAULT_REGISTRY: Registry;
|
|
12
|
+
/** Builds the registry key of a pair. */
|
|
13
|
+
export declare function registryKey(type: string, format: string): string;
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { extname } from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
/** The URL of a backend module of this package. The extension follows the build: .ts from src, .js from dist. */
|
|
4
|
+
function backendUrl(name) {
|
|
5
|
+
const extension = extname(fileURLToPath(import.meta.url));
|
|
6
|
+
return new URL(`./${name}${extension}`, import.meta.url).href;
|
|
7
|
+
}
|
|
8
|
+
/** The backends of this build. */
|
|
9
|
+
export const DEFAULT_REGISTRY = {
|
|
10
|
+
"dot:svg": { backend: "graph/viz", moduleUrl: backendUrl("graph/backend-svg") },
|
|
11
|
+
"dot:png": { backend: "graph/viz+raster/resvg", moduleUrl: backendUrl("graph/backend-png") },
|
|
12
|
+
};
|
|
13
|
+
/** Builds the registry key of a pair. */
|
|
14
|
+
export function registryKey(type, format) {
|
|
15
|
+
return `${type}:${format}`;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAazC,iHAAiH;AACjH,SAAS,UAAU,CAAC,IAAY;IAC9B,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,OAAO,IAAI,GAAG,CAAC,KAAK,IAAI,GAAG,SAAS,EAAE,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;AAChE,CAAC;AAED,kCAAkC;AAClC,MAAM,CAAC,MAAM,gBAAgB,GAAa;IACxC,SAAS,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,CAAC,mBAAmB,CAAC,EAAE;IAC/E,SAAS,EAAE,EAAE,OAAO,EAAE,wBAAwB,EAAE,SAAS,EAAE,UAAU,CAAC,mBAAmB,CAAC,EAAE;CAC7F,CAAC;AAEF,yCAAyC;AACzC,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,MAAc;IACtD,OAAO,GAAG,IAAI,IAAI,MAAM,EAAE,CAAC;AAC7B,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** The input types of the design. */
|
|
2
|
+
export declare const INPUT_TYPES: readonly ["dot", "mermaid", "math", "tikz", "plot", "sysml", "step", "iges", "brep", "cad", "html"];
|
|
3
|
+
/** One input type of the design. */
|
|
4
|
+
export type InputType = (typeof INPUT_TYPES)[number];
|
|
5
|
+
/** The output formats of the design. */
|
|
6
|
+
export declare const OUTPUT_FORMATS: readonly ["svg", "png", "pdf", "html", "md", "glb", "stl"];
|
|
7
|
+
/** One output format of the design. */
|
|
8
|
+
export type OutputFormat = (typeof OUTPUT_FORMATS)[number];
|
|
9
|
+
/** Input types whose source is bytes, not text. */
|
|
10
|
+
export declare const BINARY_INPUT_TYPES: readonly InputType[];
|
|
11
|
+
/** Per-type options. Each increment types its own keys. I0 keeps the bag open. */
|
|
12
|
+
export type RenderOptions = Record<string, unknown>;
|
|
13
|
+
/** One render request. The source is text, or bytes for the binary input types. */
|
|
14
|
+
export interface RenderRequest {
|
|
15
|
+
type: InputType;
|
|
16
|
+
/** Text for text formats, bytes for step, iges and brep. */
|
|
17
|
+
source: string | Uint8Array;
|
|
18
|
+
format: OutputFormat;
|
|
19
|
+
options?: RenderOptions;
|
|
20
|
+
}
|
|
21
|
+
/** One extracted asset, for example an image. */
|
|
22
|
+
export interface RenderAsset {
|
|
23
|
+
name: string;
|
|
24
|
+
bytes: Uint8Array;
|
|
25
|
+
mime: string;
|
|
26
|
+
}
|
|
27
|
+
/** The result of one render. */
|
|
28
|
+
export interface RenderResult {
|
|
29
|
+
bytes: Uint8Array;
|
|
30
|
+
mime: string;
|
|
31
|
+
/** The backend that rendered, for example "graph/viz". */
|
|
32
|
+
backend: string;
|
|
33
|
+
/** Each item names its source line when it has one. */
|
|
34
|
+
warnings: string[];
|
|
35
|
+
/** Extracted images, for example from an HTML to Markdown render. */
|
|
36
|
+
assets?: RenderAsset[];
|
|
37
|
+
}
|
|
38
|
+
/** One supported pair with its stage and its readiness. */
|
|
39
|
+
export interface Capability {
|
|
40
|
+
type: InputType;
|
|
41
|
+
format: OutputFormat;
|
|
42
|
+
/** The stage that the pair needs. */
|
|
43
|
+
stage: string;
|
|
44
|
+
/** True when the stage is ready on this machine. */
|
|
45
|
+
ready: boolean;
|
|
46
|
+
/** Why the pair is not ready, when it is not. */
|
|
47
|
+
reason?: string;
|
|
48
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** The input types of the design. */
|
|
2
|
+
export const INPUT_TYPES = [
|
|
3
|
+
"dot",
|
|
4
|
+
"mermaid",
|
|
5
|
+
"math",
|
|
6
|
+
"tikz",
|
|
7
|
+
"plot",
|
|
8
|
+
"sysml",
|
|
9
|
+
"step",
|
|
10
|
+
"iges",
|
|
11
|
+
"brep",
|
|
12
|
+
"cad",
|
|
13
|
+
"html",
|
|
14
|
+
];
|
|
15
|
+
/** The output formats of the design. */
|
|
16
|
+
export const OUTPUT_FORMATS = ["svg", "png", "pdf", "html", "md", "glb", "stl"];
|
|
17
|
+
/** Input types whose source is bytes, not text. */
|
|
18
|
+
export const BINARY_INPUT_TYPES = ["step", "iges", "brep"];
|
|
19
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,KAAK;IACL,SAAS;IACT,MAAM;IACN,MAAM;IACN,MAAM;IACN,OAAO;IACP,MAAM;IACN,MAAM;IACN,MAAM;IACN,KAAK;IACL,MAAM;CACE,CAAC;AAKX,wCAAwC;AACxC,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAU,CAAC;AAKzF,mDAAmD;AACnD,MAAM,CAAC,MAAM,kBAAkB,GAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const ENGINE_VERSION = "0.1.1";
|
package/dist/version.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
return path;
|
|
8
|
+
};
|
|
9
|
+
import { parentPort, workerData } from "node:worker_threads";
|
|
10
|
+
import { RenderError, RENDER_ERROR_CODES, isRenderError } from "../errors.js";
|
|
11
|
+
/**
|
|
12
|
+
* The worker side of a render. The worker loads the backend module, runs one request and posts one message.
|
|
13
|
+
* A backend error that is a RenderError keeps its code. Any other error maps to E_INTERNAL.
|
|
14
|
+
*/
|
|
15
|
+
async function run(job) {
|
|
16
|
+
try {
|
|
17
|
+
const mod = (await import(__rewriteRelativeImportExtension(job.moduleUrl)));
|
|
18
|
+
if (typeof mod.render !== "function") {
|
|
19
|
+
throw new RenderError("E_INTERNAL", `backend module ${job.moduleUrl} exports no render function`);
|
|
20
|
+
}
|
|
21
|
+
const out = await mod.render(job.request);
|
|
22
|
+
return { ok: true, bytes: out.bytes, warnings: out.warnings ?? [] };
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
if (isRenderError(err)) {
|
|
26
|
+
return { ok: false, code: err.code, message: err.message, line: err.line, column: err.column };
|
|
27
|
+
}
|
|
28
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
29
|
+
return { ok: false, code: "E_INTERNAL", message };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const job = workerData;
|
|
33
|
+
const message = await run(job);
|
|
34
|
+
if (!message.ok && !RENDER_ERROR_CODES.includes(message.code)) {
|
|
35
|
+
message.code = "E_INTERNAL";
|
|
36
|
+
}
|
|
37
|
+
parentPort?.postMessage(message);
|
|
38
|
+
//# sourceMappingURL=entry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry.js","sourceRoot":"","sources":["../../src/worker/entry.ts"],"names":[],"mappings":";;;;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAwB,MAAM,cAAc,CAAC;AAGpG;;;GAGG;AACH,KAAK,UAAU,GAAG,CAAC,GAAc;IAC/B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,kCAAC,GAAG,CAAC,SAAS,EAAC,CAAuF,CAAC;QAChI,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;YACrC,MAAM,IAAI,WAAW,CAAC,YAAY,EAAE,kBAAkB,GAAG,CAAC,SAAS,6BAA6B,CAAC,CAAC;QACpG,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC1C,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;IACtE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;YACvB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC;QACjG,CAAC;QACD,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC;IACpD,CAAC;AACH,CAAC;AAED,MAAM,GAAG,GAAG,UAAuB,CAAC;AACpC,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC;AAC/B,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,CAAE,kBAAwC,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;IACrF,OAAO,CAAC,IAAI,GAAG,YAAsC,CAAC;AACxD,CAAC;AACD,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { InputType, OutputFormat, RenderOptions } from "../types.ts";
|
|
2
|
+
/** The configuration values that a backend reads. */
|
|
3
|
+
export interface BackendSettings {
|
|
4
|
+
/** The memory limit of one render, in megabytes. */
|
|
5
|
+
maxMemoryMb: number;
|
|
6
|
+
/** The path of the raster font that replaces the bundled font, or undefined for the bundled font. */
|
|
7
|
+
rasterFont: string | undefined;
|
|
8
|
+
/** The folder URL of the bundled files: the raster font and the resvg WASM file. */
|
|
9
|
+
assetsUrl: string;
|
|
10
|
+
}
|
|
11
|
+
/** What the worker receives: one render request and the backend module that serves it. */
|
|
12
|
+
export interface WorkerJob {
|
|
13
|
+
moduleUrl: string;
|
|
14
|
+
backend: string;
|
|
15
|
+
request: {
|
|
16
|
+
type: InputType;
|
|
17
|
+
format: OutputFormat;
|
|
18
|
+
source: string | Uint8Array;
|
|
19
|
+
options: RenderOptions;
|
|
20
|
+
settings: BackendSettings;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** The value that a backend module returns from `render`. */
|
|
24
|
+
export interface BackendOutput {
|
|
25
|
+
bytes: Uint8Array;
|
|
26
|
+
warnings?: string[];
|
|
27
|
+
}
|
|
28
|
+
/** The one message that a worker sends back. */
|
|
29
|
+
export type WorkerMessage = {
|
|
30
|
+
ok: true;
|
|
31
|
+
bytes: Uint8Array;
|
|
32
|
+
warnings: string[];
|
|
33
|
+
} | {
|
|
34
|
+
ok: false;
|
|
35
|
+
code: string;
|
|
36
|
+
message: string;
|
|
37
|
+
line?: number;
|
|
38
|
+
column?: number;
|
|
39
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/worker/protocol.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Worker } from "node:worker_threads";
|
|
2
|
+
import type { BackendOutput, WorkerJob } from "./protocol.ts";
|
|
3
|
+
/** The exit of one worker. The exit code is the code that `Worker.terminate` reports. */
|
|
4
|
+
export interface WorkerExit {
|
|
5
|
+
exitCode: number;
|
|
6
|
+
}
|
|
7
|
+
/** The limits of one worker render. */
|
|
8
|
+
export interface WorkerLimits {
|
|
9
|
+
/** The time limit of one render, in milliseconds. */
|
|
10
|
+
timeoutMs: number;
|
|
11
|
+
/**
|
|
12
|
+
* The heap limit of the worker, in megabytes. Node enforces it. Bun 1.4.4 accepts the option
|
|
13
|
+
* and does not enforce it, so the limit holds only under Node, the release runtime.
|
|
14
|
+
*/
|
|
15
|
+
maxMemoryMb: number;
|
|
16
|
+
/** Called once the worker has exited, before the render settles. Tests use it to prove the exit. */
|
|
17
|
+
onWorkerExit?: (exit: WorkerExit) => void;
|
|
18
|
+
/** The longest wait for the worker to stop after the render settles, in milliseconds. */
|
|
19
|
+
terminateWaitMs?: number;
|
|
20
|
+
/** Creates the worker. Tests replace it with a double. */
|
|
21
|
+
createWorker?: (entry: URL, data: WorkerJob, maxMemoryMb: number) => Worker;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Runs one render in a fresh worker. The worker ends at the first of: its message, its error,
|
|
25
|
+
* its exit, or the time limit. The promise settles only after the worker has exited.
|
|
26
|
+
*
|
|
27
|
+
* A heap exhaustion (`ERR_WORKER_OUT_OF_MEMORY`) maps to `E_LIMIT`. The design lists the heap
|
|
28
|
+
* limit as a resource limit of a source. A crash of any other kind maps to `E_INTERNAL`.
|
|
29
|
+
*/
|
|
30
|
+
export declare function runInWorker(job: WorkerJob, limits: WorkerLimits): Promise<BackendOutput>;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { Worker } from "node:worker_threads";
|
|
2
|
+
import { extname } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { RenderError } from "../errors.js";
|
|
5
|
+
/** The default longest wait for a worker to stop. It is the only place where this value is set. */
|
|
6
|
+
const DEFAULT_TERMINATE_WAIT_MS = 5000;
|
|
7
|
+
function createDefaultWorker(entry, data, maxMemoryMb) {
|
|
8
|
+
return new Worker(entry, {
|
|
9
|
+
workerData: data,
|
|
10
|
+
resourceLimits: { maxOldGenerationSizeMb: maxMemoryMb },
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
/** Terminates the worker. It rejects when terminate throws, rejects or does not resolve within the wait. */
|
|
14
|
+
function stopWorker(worker, waitMs) {
|
|
15
|
+
let timer;
|
|
16
|
+
const bound = new Promise((_, reject) => {
|
|
17
|
+
timer = setTimeout(() => reject(new Error(`the worker did not stop within ${waitMs} ms`)), waitMs);
|
|
18
|
+
});
|
|
19
|
+
const stop = new Promise((resolve) => resolve(worker.terminate()));
|
|
20
|
+
return Promise.race([stop, bound]).finally(() => clearTimeout(timer));
|
|
21
|
+
}
|
|
22
|
+
function messageOf(err) {
|
|
23
|
+
return err instanceof Error ? err.message : String(err);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The entry file sits next to this module. Its extension follows this module:
|
|
27
|
+
* `.ts` under Bun tests, `.js` in the built package.
|
|
28
|
+
*/
|
|
29
|
+
function entryUrl() {
|
|
30
|
+
const here = fileURLToPath(import.meta.url);
|
|
31
|
+
return new URL(`./entry${extname(here)}`, import.meta.url);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Runs one render in a fresh worker. The worker ends at the first of: its message, its error,
|
|
35
|
+
* its exit, or the time limit. The promise settles only after the worker has exited.
|
|
36
|
+
*
|
|
37
|
+
* A heap exhaustion (`ERR_WORKER_OUT_OF_MEMORY`) maps to `E_LIMIT`. The design lists the heap
|
|
38
|
+
* limit as a resource limit of a source. A crash of any other kind maps to `E_INTERNAL`.
|
|
39
|
+
*/
|
|
40
|
+
export function runInWorker(job, limits) {
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
const create = limits.createWorker ?? createDefaultWorker;
|
|
43
|
+
const worker = create(entryUrl(), job, limits.maxMemoryMb);
|
|
44
|
+
const terminateWaitMs = limits.terminateWaitMs ?? DEFAULT_TERMINATE_WAIT_MS;
|
|
45
|
+
let settled = false;
|
|
46
|
+
let timer;
|
|
47
|
+
const settle = (action) => {
|
|
48
|
+
if (settled)
|
|
49
|
+
return;
|
|
50
|
+
settled = true;
|
|
51
|
+
if (timer !== undefined)
|
|
52
|
+
clearTimeout(timer);
|
|
53
|
+
stopWorker(worker, terminateWaitMs)
|
|
54
|
+
.then((exitCode) => {
|
|
55
|
+
limits.onWorkerExit?.({ exitCode });
|
|
56
|
+
})
|
|
57
|
+
.then(action, (err) => {
|
|
58
|
+
reject(new RenderError("E_INTERNAL", `render worker did not stop cleanly: ${messageOf(err)}`));
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
timer = setTimeout(() => {
|
|
62
|
+
settle(() => reject(new RenderError("E_TIMEOUT", `render did not finish within ${limits.timeoutMs} ms`)));
|
|
63
|
+
}, limits.timeoutMs);
|
|
64
|
+
worker.once("message", (msg) => {
|
|
65
|
+
settle(() => {
|
|
66
|
+
if (msg.ok) {
|
|
67
|
+
resolve({ bytes: msg.bytes, warnings: msg.warnings });
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
const code = msg.code;
|
|
71
|
+
reject(new RenderError(code, msg.message, { line: msg.line, column: msg.column }));
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
worker.once("error", (err) => {
|
|
76
|
+
settle(() => {
|
|
77
|
+
if (err.code === "ERR_WORKER_OUT_OF_MEMORY") {
|
|
78
|
+
reject(new RenderError("E_LIMIT", `render exceeded the heap limit of ${limits.maxMemoryMb} MB`));
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
reject(new RenderError("E_INTERNAL", `render worker failed: ${err.message}`));
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
worker.once("exit", (code) => {
|
|
86
|
+
settle(() => reject(new RenderError("E_INTERNAL", `render worker exited with code ${code} before it returned`)));
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/worker/run.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,71 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@danielsimonjr/sciengjs",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
6
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@danielsimonjr/sciengjs",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "A local rendering engine for science and engineering diagrams, documents and models",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"sideEffects": false,
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./graph": {
|
|
17
|
+
"types": "./dist/graph/index.d.ts",
|
|
18
|
+
"default": "./dist/graph/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./raster": {
|
|
21
|
+
"types": "./dist/raster/index.d.ts",
|
|
22
|
+
"default": "./dist/raster/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./package.json": "./package.json"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"assets",
|
|
29
|
+
"THIRD_PARTY_NOTICES.md",
|
|
30
|
+
"README.md",
|
|
31
|
+
"LICENSE"
|
|
32
|
+
],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "node scripts/prepare-build.mjs && tsc -p tsconfig.build.json",
|
|
35
|
+
"typecheck": "tsc --noEmit",
|
|
36
|
+
"test": "bun test",
|
|
37
|
+
"smoke": "node test/smoke/smoke.mjs",
|
|
38
|
+
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
39
|
+
"test:node": "node --test test/node/worker.node.mjs"
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"graphviz",
|
|
43
|
+
"mermaid",
|
|
44
|
+
"latex",
|
|
45
|
+
"tikz",
|
|
46
|
+
"svg",
|
|
47
|
+
"diagram",
|
|
48
|
+
"rendering"
|
|
49
|
+
],
|
|
50
|
+
"repository": {
|
|
51
|
+
"type": "git",
|
|
52
|
+
"url": "git+https://github.com/danielsimonjr/sciengjs.git"
|
|
53
|
+
},
|
|
54
|
+
"bugs": {
|
|
55
|
+
"url": "https://github.com/danielsimonjr/sciengjs/issues"
|
|
56
|
+
},
|
|
57
|
+
"homepage": "https://github.com/danielsimonjr/sciengjs#readme",
|
|
58
|
+
"engines": {
|
|
59
|
+
"node": ">=22"
|
|
60
|
+
},
|
|
61
|
+
"packageManager": "bun@1.4.4",
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@types/bun": "^1.4.4",
|
|
64
|
+
"@types/node": "^22",
|
|
65
|
+
"typescript": "^7.0.2"
|
|
66
|
+
},
|
|
67
|
+
"dependencies": {
|
|
68
|
+
"@resvg/resvg-wasm": "2.6.2",
|
|
69
|
+
"@viz-js/viz": "3.31.0"
|
|
70
|
+
}
|
|
71
|
+
}
|