sibujs 1.5.0 → 2.0.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/browser.cjs +238 -69
- package/dist/browser.d.cts +5 -0
- package/dist/browser.d.ts +5 -0
- package/dist/browser.js +6 -6
- package/dist/build.cjs +916 -292
- package/dist/build.js +15 -13
- package/dist/cdn.global.js +17 -16
- package/dist/chunk-2RA7SHDA.js +65 -0
- package/dist/chunk-2UPRY23K.js +80 -0
- package/dist/chunk-3JHCYHWN.js +125 -0
- package/dist/{chunk-VAPYJN4X.js → chunk-3LR7GLWQ.js} +93 -23
- package/dist/{chunk-RJ46C3CS.js → chunk-3NSGB5JN.js} +71 -20
- package/dist/{chunk-XUEEGU5O.js → chunk-52YJLLRO.js} +16 -4
- package/dist/{chunk-XHK6BDAJ.js → chunk-54EDRCEF.js} +25 -8
- package/dist/chunk-7JDB7I65.js +1327 -0
- package/dist/{chunk-WZSPOOER.js → chunk-CC65Y57T.js} +8 -5
- package/dist/{chunk-23VV7YD3.js → chunk-DFPFITST.js} +25 -30
- package/dist/{chunk-BGN5ZMP4.js → chunk-GTBNNBJ6.js} +14 -2
- package/dist/chunk-HB24TBAF.js +121 -0
- package/dist/{chunk-CZUGLNJS.js → chunk-ITX6OO3F.js} +3 -3
- package/dist/{chunk-BGTHZHJ5.js → chunk-JA6667UN.js} +188 -44
- package/dist/{chunk-7GRNSCFT.js → chunk-JXMMDLBY.js} +306 -183
- package/dist/{chunk-3X2YG6YM.js → chunk-JYD2PWXH.js} +59 -28
- package/dist/{chunk-SFKNRVCU.js → chunk-KLRMB5ZS.js} +135 -79
- package/dist/{chunk-5X6PP2UK.js → chunk-LMLD24FC.js} +2 -2
- package/dist/{chunk-M4NLBH4I.js → chunk-LYTCUZ7H.js} +3 -2
- package/dist/{chunk-BMPL52BF.js → chunk-MIUAXB7K.js} +118 -66
- package/dist/{chunk-JCDUJN2F.js → chunk-ND2664SF.js} +486 -153
- package/dist/{chunk-VQDZK23A.js → chunk-O2MNQFLP.js} +181 -66
- package/dist/{chunk-NHUC2QWH.js → chunk-R73P76YZ.js} +1 -1
- package/dist/{chunk-2BYQDGN3.js → chunk-SAHNHTFC.js} +234 -63
- package/dist/chunk-UCS6AMJ7.js +79 -0
- package/dist/{chunk-K4G4ZQNR.js → chunk-VLPPXTYG.js} +84 -38
- package/dist/{chunk-OUZZEE4S.js → chunk-WOMYAHHI.js} +17 -11
- package/dist/{customElement-BL3Uo8dL.d.cts → customElement-CPfIrbvg.d.cts} +14 -10
- package/dist/{customElement-BL3Uo8dL.d.ts → customElement-CPfIrbvg.d.ts} +14 -10
- package/dist/data.cjs +410 -99
- package/dist/data.d.cts +20 -2
- package/dist/data.d.ts +20 -2
- package/dist/data.js +11 -9
- package/dist/devtools.cjs +513 -223
- package/dist/devtools.d.cts +1 -1
- package/dist/devtools.d.ts +1 -1
- package/dist/devtools.js +12 -6
- package/dist/ecosystem.cjs +475 -144
- package/dist/ecosystem.d.cts +9 -7
- package/dist/ecosystem.d.ts +9 -7
- package/dist/ecosystem.js +12 -11
- package/dist/extras.cjs +3355 -1541
- package/dist/extras.d.cts +9 -9
- package/dist/extras.d.ts +9 -9
- package/dist/extras.js +58 -45
- package/dist/index.cjs +920 -292
- package/dist/index.d.cts +71 -8
- package/dist/index.d.ts +71 -8
- package/dist/index.js +28 -16
- package/dist/{introspect-BumjnBKr.d.cts → introspect-BWNjNw64.d.cts} +22 -2
- package/dist/{introspect-CZrlcaYy.d.ts → introspect-cY2pg9pW.d.ts} +22 -2
- package/dist/motion.cjs +77 -34
- package/dist/motion.js +4 -4
- package/dist/patterns.cjs +335 -69
- package/dist/patterns.d.cts +11 -12
- package/dist/patterns.d.ts +11 -12
- package/dist/patterns.js +7 -7
- package/dist/performance.cjs +279 -108
- package/dist/performance.d.cts +23 -16
- package/dist/performance.d.ts +23 -16
- package/dist/performance.js +13 -8
- package/dist/plugin-D30wlGW5.d.cts +71 -0
- package/dist/plugin-D30wlGW5.d.ts +71 -0
- package/dist/plugins.cjs +635 -260
- package/dist/plugins.d.cts +10 -3
- package/dist/plugins.d.ts +10 -3
- package/dist/plugins.js +106 -38
- package/dist/{ssr-Do_SiVoL.d.cts → ssr-CrVNy6Pa.d.cts} +9 -15
- package/dist/{ssr-Do_SiVoL.d.ts → ssr-CrVNy6Pa.d.ts} +9 -15
- package/dist/{ssr-4PBXAOO3.js → ssr-FXD2PPMC.js} +4 -3
- package/dist/ssr.cjs +642 -222
- package/dist/ssr.d.cts +26 -6
- package/dist/ssr.d.ts +26 -6
- package/dist/ssr.js +12 -11
- package/dist/{tagFactory-DaJ0YWX6.d.cts → tagFactory-S17H2qxu.d.cts} +9 -1
- package/dist/{tagFactory-DaJ0YWX6.d.ts → tagFactory-S17H2qxu.d.ts} +9 -1
- package/dist/testing.cjs +252 -63
- package/dist/testing.d.cts +17 -4
- package/dist/testing.d.ts +17 -4
- package/dist/testing.js +100 -44
- package/dist/ui.cjs +463 -137
- package/dist/ui.d.cts +1 -1
- package/dist/ui.d.ts +1 -1
- package/dist/ui.js +20 -17
- package/dist/widgets.cjs +977 -94
- package/dist/widgets.d.cts +104 -2
- package/dist/widgets.d.ts +104 -2
- package/dist/widgets.js +9 -7
- package/package.json +8 -2
- package/dist/chunk-32DY64NT.js +0 -282
- package/dist/chunk-3AIRKM3B.js +0 -1263
- package/dist/chunk-3ARAQO7B.js +0 -398
- package/dist/chunk-3CRQALYP.js +0 -877
- package/dist/chunk-4EI4AG32.js +0 -482
- package/dist/chunk-4MYMUBRS.js +0 -21
- package/dist/chunk-5ZYQ6KDD.js +0 -154
- package/dist/chunk-6BMPXPUW.js +0 -26
- package/dist/chunk-6HLLIF3K.js +0 -398
- package/dist/chunk-6LSNVCS2.js +0 -937
- package/dist/chunk-6SA3QQES.js +0 -61
- package/dist/chunk-77L6NL3X.js +0 -1097
- package/dist/chunk-7BF6TK55.js +0 -1097
- package/dist/chunk-7TQKR4PP.js +0 -294
- package/dist/chunk-7V26P53V.js +0 -712
- package/dist/chunk-AZ3ISID5.js +0 -298
- package/dist/chunk-B7SWRFUT.js +0 -332
- package/dist/chunk-BTU3TJDS.js +0 -365
- package/dist/chunk-BW3WT46K.js +0 -937
- package/dist/chunk-C6KFWOFV.js +0 -616
- package/dist/chunk-CHF5OHIA.js +0 -61
- package/dist/chunk-CHJ27IGK.js +0 -26
- package/dist/chunk-CMBFNA7L.js +0 -27
- package/dist/chunk-DAHRH4ON.js +0 -331
- package/dist/chunk-DKOHBI74.js +0 -924
- package/dist/chunk-DTCOOBMX.js +0 -725
- package/dist/chunk-EBGIRKQY.js +0 -616
- package/dist/chunk-EUZND3CB.js +0 -27
- package/dist/chunk-EVCZO745.js +0 -365
- package/dist/chunk-EWFVA3TJ.js +0 -282
- package/dist/chunk-F3FA4F32.js +0 -292
- package/dist/chunk-FGOEVHY3.js +0 -60
- package/dist/chunk-G3BOQPVO.js +0 -365
- package/dist/chunk-GCOK2LC3.js +0 -282
- package/dist/chunk-GJPXRJ45.js +0 -37
- package/dist/chunk-HGMJFBC7.js +0 -654
- package/dist/chunk-JAKHTMQU.js +0 -1000
- package/dist/chunk-JCI5M6U6.js +0 -956
- package/dist/chunk-K5ZUMYVS.js +0 -89
- package/dist/chunk-KQPDEVVS.js +0 -398
- package/dist/chunk-L6JRBDNS.js +0 -60
- package/dist/chunk-LA6KQEDU.js +0 -712
- package/dist/chunk-MB6QFH3I.js +0 -2776
- package/dist/chunk-MDVXJWFN.js +0 -304
- package/dist/chunk-MEZVEBPN.js +0 -2008
- package/dist/chunk-MK4ERFYL.js +0 -2249
- package/dist/chunk-MLKGABMK.js +0 -9
- package/dist/chunk-MQ5GOYPH.js +0 -2249
- package/dist/chunk-MYRV7VDM.js +0 -742
- package/dist/chunk-N6IZB6KJ.js +0 -567
- package/dist/chunk-NEKUBFPT.js +0 -60
- package/dist/chunk-NMRUZALC.js +0 -1097
- package/dist/chunk-NYVAC6P5.js +0 -37
- package/dist/chunk-NZIIMDWI.js +0 -84
- package/dist/chunk-OF7UZIVB.js +0 -725
- package/dist/chunk-P3XWXJZU.js +0 -282
- package/dist/chunk-P6W3STU4.js +0 -2249
- package/dist/chunk-PBHF5WKN.js +0 -616
- package/dist/chunk-PDZQY43A.js +0 -616
- package/dist/chunk-PTQJDMRT.js +0 -146
- package/dist/chunk-PZEGYCF5.js +0 -61
- package/dist/chunk-QBMDLBU2.js +0 -975
- package/dist/chunk-QWZG56ET.js +0 -2744
- package/dist/chunk-RQGQSLQK.js +0 -725
- package/dist/chunk-SDLZDHKP.js +0 -107
- package/dist/chunk-TDGZL5CU.js +0 -365
- package/dist/chunk-TNQWPPE6.js +0 -37
- package/dist/chunk-TSOKIX5Z.js +0 -654
- package/dist/chunk-UHNL42EF.js +0 -2730
- package/dist/chunk-UNXCEF6S.js +0 -21
- package/dist/chunk-V2XTI523.js +0 -347
- package/dist/chunk-VAU366PN.js +0 -2241
- package/dist/chunk-VMVDTCXB.js +0 -712
- package/dist/chunk-VQNQZCWJ.js +0 -61
- package/dist/chunk-VRW3FULF.js +0 -725
- package/dist/chunk-WADYRCO2.js +0 -304
- package/dist/chunk-WILQZRO4.js +0 -282
- package/dist/chunk-WR5D4EGH.js +0 -26
- package/dist/chunk-WUHJISPP.js +0 -298
- package/dist/chunk-XYU6TZOW.js +0 -182
- package/dist/chunk-Y6GP4QGG.js +0 -276
- package/dist/chunk-YECR7UIA.js +0 -347
- package/dist/chunk-YUTWTI4B.js +0 -654
- package/dist/chunk-Z65KYU7I.js +0 -26
- package/dist/chunk-Z6POF5YC.js +0 -975
- package/dist/chunk-ZBJP6WFL.js +0 -482
- package/dist/chunk-ZD6OAMTH.js +0 -277
- package/dist/chunk-ZWKZCBO6.js +0 -317
- package/dist/contracts-DDrwxvJ-.d.cts +0 -245
- package/dist/contracts-DDrwxvJ-.d.ts +0 -245
- package/dist/contracts-DOrhwbke.d.cts +0 -245
- package/dist/contracts-DOrhwbke.d.ts +0 -245
- package/dist/contracts-xo5ckdRP.d.cts +0 -240
- package/dist/contracts-xo5ckdRP.d.ts +0 -240
- package/dist/customElement-BKQfbSZQ.d.cts +0 -262
- package/dist/customElement-BKQfbSZQ.d.ts +0 -262
- package/dist/customElement-D2DJp_xn.d.cts +0 -313
- package/dist/customElement-D2DJp_xn.d.ts +0 -313
- package/dist/customElement-yz8uyk-0.d.cts +0 -308
- package/dist/customElement-yz8uyk-0.d.ts +0 -308
- package/dist/introspect-Cb0zgpi2.d.cts +0 -477
- package/dist/introspect-Y2xNXGSf.d.ts +0 -477
- package/dist/plugin-Bek4RhJY.d.cts +0 -43
- package/dist/plugin-Bek4RhJY.d.ts +0 -43
- package/dist/ssr-3RXHP5ES.js +0 -38
- package/dist/ssr-6GIMY5MX.js +0 -38
- package/dist/ssr-BA6sxxUd.d.cts +0 -135
- package/dist/ssr-BA6sxxUd.d.ts +0 -135
- package/dist/ssr-WKUPVSSK.js +0 -36
- package/dist/tagFactory-Dl8QCLga.d.cts +0 -23
- package/dist/tagFactory-Dl8QCLga.d.ts +0 -23
package/dist/ssr.cjs
CHANGED
|
@@ -70,33 +70,105 @@ function isDev() {
|
|
|
70
70
|
var _isDev = isDev();
|
|
71
71
|
function devAssert(condition, message) {
|
|
72
72
|
if (_isDev && !condition) {
|
|
73
|
-
throw new Error(`[
|
|
73
|
+
throw new Error(`[SibuJS] ${message}`);
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
function devWarn(message) {
|
|
77
77
|
if (_isDev) {
|
|
78
|
-
console.warn(`[
|
|
78
|
+
console.warn(`[SibuJS] ${message}`);
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
// src/core/ssr-context.ts
|
|
83
|
+
var als = null;
|
|
84
|
+
try {
|
|
85
|
+
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
86
|
+
const req = Function("return typeof require==='function'?require:null")();
|
|
87
|
+
if (req) {
|
|
88
|
+
const mod = req("node:async_hooks");
|
|
89
|
+
als = new mod.AsyncLocalStorage();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
} catch {
|
|
93
|
+
als = null;
|
|
94
|
+
}
|
|
95
|
+
var fallbackStore = { ssr: false, suspenseIdCounter: 0 };
|
|
96
|
+
function getSSRStore() {
|
|
97
|
+
if (als) {
|
|
98
|
+
const s2 = als.getStore();
|
|
99
|
+
if (s2) return s2;
|
|
100
|
+
}
|
|
101
|
+
return fallbackStore;
|
|
102
|
+
}
|
|
103
|
+
function isSSR() {
|
|
104
|
+
return getSSRStore().ssr;
|
|
105
|
+
}
|
|
106
|
+
|
|
82
107
|
// src/utils/sanitize.ts
|
|
108
|
+
var SAFE_URL_PROTOCOLS = ["http:", "https:", "mailto:", "tel:", "ftp:"];
|
|
83
109
|
function sanitizeUrl(url) {
|
|
84
110
|
const trimmed = url.replace(/[\x00-\x20\x7f-\x9f]+/g, "").trim();
|
|
85
111
|
if (!trimmed) return "";
|
|
86
112
|
const lower = trimmed.toLowerCase();
|
|
87
|
-
|
|
88
|
-
|
|
113
|
+
let schemeEnd = -1;
|
|
114
|
+
for (let i2 = 0; i2 < lower.length; i2++) {
|
|
115
|
+
const ch = lower.charCodeAt(i2);
|
|
116
|
+
if (ch === 58) {
|
|
117
|
+
schemeEnd = i2;
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
if (ch === 47 || ch === 63 || ch === 35) break;
|
|
89
121
|
}
|
|
122
|
+
if (schemeEnd === -1) return trimmed;
|
|
123
|
+
const scheme = lower.slice(0, schemeEnd + 1);
|
|
124
|
+
if (!/^[a-z][a-z0-9+.-]*:$/.test(scheme)) return trimmed;
|
|
125
|
+
if (SAFE_URL_PROTOCOLS.indexOf(scheme) === -1) return "";
|
|
90
126
|
return trimmed;
|
|
91
127
|
}
|
|
128
|
+
function sanitizeSrcset(value) {
|
|
129
|
+
const parts = value.split(",");
|
|
130
|
+
const out = [];
|
|
131
|
+
for (let i2 = 0; i2 < parts.length; i2++) {
|
|
132
|
+
const part = parts[i2].trim();
|
|
133
|
+
if (!part) continue;
|
|
134
|
+
const m = part.match(/^(\S+)(\s+.+)?$/);
|
|
135
|
+
if (!m) continue;
|
|
136
|
+
const safe = sanitizeUrl(m[1]);
|
|
137
|
+
if (!safe) continue;
|
|
138
|
+
out.push(m[2] ? `${safe}${m[2]}` : safe);
|
|
139
|
+
}
|
|
140
|
+
return out.join(", ");
|
|
141
|
+
}
|
|
92
142
|
function sanitizeCSSValue(value) {
|
|
93
|
-
const
|
|
94
|
-
|
|
143
|
+
const decoded = value.replace(/\\([0-9a-fA-F]{1,6})\s?/g, (_m, hex) => {
|
|
144
|
+
const code2 = Number.parseInt(hex, 16);
|
|
145
|
+
if (!Number.isFinite(code2) || code2 < 0 || code2 > 1114111) return "";
|
|
146
|
+
try {
|
|
147
|
+
return String.fromCodePoint(code2);
|
|
148
|
+
} catch {
|
|
149
|
+
return "";
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
const lower = decoded.toLowerCase().replace(/\s+/g, "");
|
|
153
|
+
if (lower.includes("url(") || lower.includes("expression(") || lower.includes("javascript:") || lower.includes("vbscript:") || lower.includes("-moz-binding") || lower.includes("behavior:") || lower.includes("@import") || lower.includes("image-set(") || lower.includes("filter:progid")) {
|
|
95
154
|
return "";
|
|
96
155
|
}
|
|
97
156
|
return value;
|
|
98
157
|
}
|
|
99
|
-
var URL_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
158
|
+
var URL_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
159
|
+
"href",
|
|
160
|
+
"xlink:href",
|
|
161
|
+
"src",
|
|
162
|
+
"action",
|
|
163
|
+
"formaction",
|
|
164
|
+
"formtarget",
|
|
165
|
+
"cite",
|
|
166
|
+
"poster",
|
|
167
|
+
"background",
|
|
168
|
+
"srcset",
|
|
169
|
+
"ping",
|
|
170
|
+
"data"
|
|
171
|
+
]);
|
|
100
172
|
function isUrlAttribute(attr) {
|
|
101
173
|
return URL_ATTRIBUTES.has(attr);
|
|
102
174
|
}
|
|
@@ -219,7 +291,7 @@ function hydrate(component, container, options = {}) {
|
|
|
219
291
|
options.onMismatch(first);
|
|
220
292
|
} else if (_isDev2) {
|
|
221
293
|
console.warn(
|
|
222
|
-
`[
|
|
294
|
+
`[SibuJS hydration] ${first.message}
|
|
223
295
|
at ${first.path}
|
|
224
296
|
server: ${first.serverValue}
|
|
225
297
|
client: ${first.clientValue}`
|
|
@@ -227,17 +299,9 @@ function hydrate(component, container, options = {}) {
|
|
|
227
299
|
}
|
|
228
300
|
}
|
|
229
301
|
}
|
|
230
|
-
|
|
302
|
+
container.replaceChildren(clientTree);
|
|
231
303
|
container.setAttribute("data-sibu-hydrated", "true");
|
|
232
304
|
}
|
|
233
|
-
function hydrateNode(serverNode, clientNode) {
|
|
234
|
-
if (!serverNode) return;
|
|
235
|
-
const serverChildren = Array.from(serverNode.children);
|
|
236
|
-
const clientChildren = Array.from(clientNode.children);
|
|
237
|
-
for (let i2 = 0; i2 < Math.min(serverChildren.length, clientChildren.length); i2++) {
|
|
238
|
-
hydrateNode(serverChildren[i2], clientChildren[i2]);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
305
|
function collectMismatches(serverNode, clientNode, path2, out, max = 5) {
|
|
242
306
|
if (out.length >= max) return;
|
|
243
307
|
const nodePath = path2 || clientNode?.tagName?.toLowerCase() || "(root)";
|
|
@@ -467,12 +531,16 @@ function renderToReadableStream(element) {
|
|
|
467
531
|
controller.enqueue(value);
|
|
468
532
|
}
|
|
469
533
|
},
|
|
470
|
-
cancel() {
|
|
471
|
-
generator.return(void 0);
|
|
534
|
+
async cancel() {
|
|
535
|
+
await generator.return(void 0);
|
|
472
536
|
}
|
|
473
537
|
});
|
|
474
538
|
}
|
|
539
|
+
var SAFE_ID = /^[A-Za-z0-9_-]+$/;
|
|
475
540
|
function island(id, component) {
|
|
541
|
+
if (!SAFE_ID.test(id)) {
|
|
542
|
+
throw new Error(`[SibuJS SSR] island: id must match [A-Za-z0-9_-]+ (got: ${JSON.stringify(id.slice(0, 32))})`);
|
|
543
|
+
}
|
|
476
544
|
const el = component();
|
|
477
545
|
el.setAttribute("data-sibu-island", id);
|
|
478
546
|
return el;
|
|
@@ -485,8 +553,9 @@ function hydrateIslands(container, islands) {
|
|
|
485
553
|
const factory = islands[id];
|
|
486
554
|
if (typeof factory !== "function") continue;
|
|
487
555
|
const clientTree = factory();
|
|
488
|
-
|
|
489
|
-
|
|
556
|
+
clientTree.setAttribute("data-sibu-island", id);
|
|
557
|
+
clientTree.setAttribute("data-sibu-hydrated", "true");
|
|
558
|
+
marker2.replaceWith(clientTree);
|
|
490
559
|
}
|
|
491
560
|
container.setAttribute("data-sibu-hydrated", "partial");
|
|
492
561
|
}
|
|
@@ -503,8 +572,9 @@ function hydrateProgressively(container, islands, options) {
|
|
|
503
572
|
for (const entry of entries) {
|
|
504
573
|
if (entry.isIntersecting) {
|
|
505
574
|
const clientTree = factory();
|
|
506
|
-
|
|
507
|
-
|
|
575
|
+
clientTree.setAttribute("data-sibu-island", id);
|
|
576
|
+
clientTree.setAttribute("data-sibu-hydrated", "true");
|
|
577
|
+
marker2.replaceWith(clientTree);
|
|
508
578
|
observer.disconnect();
|
|
509
579
|
break;
|
|
510
580
|
}
|
|
@@ -520,20 +590,37 @@ function hydrateProgressively(container, islands, options) {
|
|
|
520
590
|
for (const cleanup2 of cleanups) cleanup2();
|
|
521
591
|
};
|
|
522
592
|
}
|
|
523
|
-
var suspenseIdCounter = 0;
|
|
524
593
|
function resetSSRState() {
|
|
525
|
-
suspenseIdCounter = 0;
|
|
594
|
+
getSSRStore().suspenseIdCounter = 0;
|
|
595
|
+
}
|
|
596
|
+
function noop() {
|
|
526
597
|
}
|
|
527
598
|
function ssrSuspense(props) {
|
|
528
|
-
const
|
|
599
|
+
const store = getSSRStore();
|
|
600
|
+
const id = `sibu-sus-${store.suspenseIdCounter++}`;
|
|
601
|
+
const timeoutMs = props.timeoutMs ?? 3e4;
|
|
529
602
|
const fallbackEl = props.fallback();
|
|
530
603
|
const wrapper = document.createElement("div");
|
|
531
604
|
wrapper.setAttribute("data-sibu-suspense-id", id);
|
|
532
605
|
wrapper.appendChild(fallbackEl);
|
|
533
|
-
const
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
606
|
+
const fallbackHtml = renderToString(fallbackEl);
|
|
607
|
+
let timer;
|
|
608
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
609
|
+
timer = setTimeout(() => reject(new Error(`[SibuJS SSR] ssrSuspense timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
610
|
+
});
|
|
611
|
+
const raced = Promise.race([props.content(), timeoutPromise]);
|
|
612
|
+
const promise = raced.then(
|
|
613
|
+
(resolvedEl) => {
|
|
614
|
+
if (timer) clearTimeout(timer);
|
|
615
|
+
return { id, html: renderToString(resolvedEl) };
|
|
616
|
+
},
|
|
617
|
+
(err) => {
|
|
618
|
+
if (timer) clearTimeout(timer);
|
|
619
|
+
if (_isDev2) console.warn("[SibuJS SSR] ssrSuspense rejected:", err);
|
|
620
|
+
return { id, html: fallbackHtml };
|
|
621
|
+
}
|
|
622
|
+
);
|
|
623
|
+
promise.catch(noop);
|
|
537
624
|
return { element: wrapper, promise };
|
|
538
625
|
}
|
|
539
626
|
var SAFE_SUSPENSE_ID = /^[A-Za-z0-9_-]+$/;
|
|
@@ -561,14 +648,27 @@ var SSR_DATA_ATTR = "__SIBU_SSR_DATA__";
|
|
|
561
648
|
function escapeScriptJson(json) {
|
|
562
649
|
return json.replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
563
650
|
}
|
|
564
|
-
|
|
565
|
-
|
|
651
|
+
var DEFAULT_MAX_SSR_BYTES = 1024 * 1024;
|
|
652
|
+
function serializeState(state, nonce, options) {
|
|
653
|
+
const rawJson = JSON.stringify(state);
|
|
654
|
+
const maxBytes = options?.maxBytes ?? DEFAULT_MAX_SSR_BYTES;
|
|
655
|
+
const byteLen = typeof TextEncoder !== "undefined" ? new TextEncoder().encode(rawJson).byteLength : Buffer.byteLength(rawJson, "utf8");
|
|
656
|
+
if (byteLen > maxBytes) {
|
|
657
|
+
throw new Error(`[SibuJS SSR] serializeState: payload (${byteLen} bytes) exceeds maxBytes (${maxBytes})`);
|
|
658
|
+
}
|
|
659
|
+
const json = escapeScriptJson(rawJson);
|
|
566
660
|
const nonceAttr = nonce ? ` nonce="${escapeAttr(nonce)}"` : "";
|
|
567
661
|
return `<script${nonceAttr}>window.${SSR_DATA_ATTR}=${json}</script>`;
|
|
568
662
|
}
|
|
569
663
|
function deserializeState(validate) {
|
|
570
664
|
if (typeof window === "undefined") return void 0;
|
|
571
|
-
|
|
665
|
+
if (_isDev2 && !validate) {
|
|
666
|
+
console.warn(
|
|
667
|
+
"[SibuJS SSR] deserializeState() called without a validate guard \u2014 tampered SSR payloads will not be detected."
|
|
668
|
+
);
|
|
669
|
+
}
|
|
670
|
+
const w = window;
|
|
671
|
+
const raw = w[SSR_DATA_ATTR];
|
|
572
672
|
if (raw === void 0) return void 0;
|
|
573
673
|
if (validate && !validate(raw)) return void 0;
|
|
574
674
|
return raw;
|
|
@@ -586,11 +686,11 @@ var subscriberStack = new Array(32);
|
|
|
586
686
|
var stackCapacity = 32;
|
|
587
687
|
var stackTop = -1;
|
|
588
688
|
var currentSubscriber = null;
|
|
589
|
-
var signalSubscribers = /* @__PURE__ */ new WeakMap();
|
|
590
689
|
var SUBS = "__s";
|
|
591
690
|
var notifyDepth = 0;
|
|
592
691
|
var pendingQueue = [];
|
|
593
692
|
var pendingSet = /* @__PURE__ */ new Set();
|
|
693
|
+
var propagateStack = [];
|
|
594
694
|
function safeInvoke(sub2) {
|
|
595
695
|
try {
|
|
596
696
|
sub2();
|
|
@@ -636,7 +736,6 @@ function recordDependency(signal2) {
|
|
|
636
736
|
let subs = signal2[SUBS];
|
|
637
737
|
if (!subs) {
|
|
638
738
|
subs = /* @__PURE__ */ new Set();
|
|
639
|
-
signalSubscribers.set(signal2, subs);
|
|
640
739
|
signal2[SUBS] = subs;
|
|
641
740
|
}
|
|
642
741
|
subs.add(currentSubscriber);
|
|
@@ -658,17 +757,17 @@ function queueSignalNotification(signal2) {
|
|
|
658
757
|
}
|
|
659
758
|
}
|
|
660
759
|
}
|
|
661
|
-
var
|
|
760
|
+
var maxDrainIterations = 1e5;
|
|
662
761
|
function drainNotificationQueue() {
|
|
663
762
|
if (notifyDepth > 0) return;
|
|
664
763
|
notifyDepth++;
|
|
665
764
|
try {
|
|
666
765
|
let i2 = 0;
|
|
667
766
|
while (i2 < pendingQueue.length) {
|
|
668
|
-
if (i2 >=
|
|
767
|
+
if (i2 >= maxDrainIterations) {
|
|
669
768
|
if (typeof console !== "undefined") {
|
|
670
769
|
console.error(
|
|
671
|
-
`[SibuJS] Notification queue exceeded ${
|
|
770
|
+
`[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
|
|
672
771
|
);
|
|
673
772
|
}
|
|
674
773
|
break;
|
|
@@ -677,47 +776,52 @@ function drainNotificationQueue() {
|
|
|
677
776
|
i2++;
|
|
678
777
|
}
|
|
679
778
|
} finally {
|
|
680
|
-
pendingQueue.length = 0;
|
|
681
|
-
pendingSet.clear();
|
|
682
779
|
notifyDepth--;
|
|
780
|
+
if (notifyDepth === 0) {
|
|
781
|
+
pendingQueue.length = 0;
|
|
782
|
+
pendingSet.clear();
|
|
783
|
+
}
|
|
683
784
|
}
|
|
684
785
|
}
|
|
685
786
|
function propagateDirty(sub2) {
|
|
686
787
|
sub2();
|
|
687
|
-
|
|
688
|
-
|
|
788
|
+
const rootSig = sub2._sig;
|
|
789
|
+
if (!rootSig) return;
|
|
790
|
+
const stack = propagateStack;
|
|
791
|
+
const baseLen = stack.length;
|
|
792
|
+
stack.push(rootSig);
|
|
793
|
+
while (stack.length > baseLen) {
|
|
794
|
+
const sig = stack.pop();
|
|
689
795
|
const first = sig.__f;
|
|
690
796
|
if (first) {
|
|
691
797
|
if (first._c) {
|
|
692
798
|
const nSig = first._sig;
|
|
693
|
-
nSig._d
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
if (!pendingSet.has(first)) {
|
|
799
|
+
if (!nSig._d) {
|
|
800
|
+
nSig._d = true;
|
|
801
|
+
stack.push(nSig);
|
|
802
|
+
}
|
|
803
|
+
} else if (!pendingSet.has(first)) {
|
|
698
804
|
pendingSet.add(first);
|
|
699
805
|
pendingQueue.push(first);
|
|
700
806
|
}
|
|
701
|
-
|
|
807
|
+
continue;
|
|
702
808
|
}
|
|
703
809
|
const subs = sig[SUBS];
|
|
704
|
-
if (!subs)
|
|
705
|
-
let nextSig;
|
|
810
|
+
if (!subs) continue;
|
|
706
811
|
for (const s2 of subs) {
|
|
707
812
|
if (s2._c) {
|
|
708
|
-
s2();
|
|
709
813
|
const nSig = s2._sig;
|
|
710
|
-
if (nSig && !
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
814
|
+
if (nSig && !nSig._d) {
|
|
815
|
+
nSig._d = true;
|
|
816
|
+
stack.push(nSig);
|
|
817
|
+
} else if (!nSig) {
|
|
818
|
+
s2();
|
|
714
819
|
}
|
|
715
820
|
} else if (!pendingSet.has(s2)) {
|
|
716
821
|
pendingSet.add(s2);
|
|
717
822
|
pendingQueue.push(s2);
|
|
718
823
|
}
|
|
719
824
|
}
|
|
720
|
-
sig = nextSig;
|
|
721
825
|
}
|
|
722
826
|
}
|
|
723
827
|
function notifySubscribers(signal2) {
|
|
@@ -741,13 +845,23 @@ function notifySubscribers(signal2) {
|
|
|
741
845
|
}
|
|
742
846
|
let i2 = 0;
|
|
743
847
|
while (i2 < pendingQueue.length) {
|
|
848
|
+
if (i2 >= maxDrainIterations) {
|
|
849
|
+
if (typeof console !== "undefined") {
|
|
850
|
+
console.error(
|
|
851
|
+
`[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
|
|
852
|
+
);
|
|
853
|
+
}
|
|
854
|
+
break;
|
|
855
|
+
}
|
|
744
856
|
safeInvoke(pendingQueue[i2]);
|
|
745
857
|
i2++;
|
|
746
858
|
}
|
|
747
859
|
} finally {
|
|
748
|
-
pendingQueue.length = 0;
|
|
749
|
-
pendingSet.clear();
|
|
750
860
|
notifyDepth--;
|
|
861
|
+
if (notifyDepth === 0) {
|
|
862
|
+
pendingQueue.length = 0;
|
|
863
|
+
pendingSet.clear();
|
|
864
|
+
}
|
|
751
865
|
}
|
|
752
866
|
return;
|
|
753
867
|
}
|
|
@@ -767,30 +881,48 @@ function notifySubscribers(signal2) {
|
|
|
767
881
|
notifyDepth++;
|
|
768
882
|
try {
|
|
769
883
|
let directCount = 0;
|
|
884
|
+
let hasComputedSub = false;
|
|
770
885
|
for (const sub2 of subs) {
|
|
886
|
+
if (sub2._c) hasComputedSub = true;
|
|
771
887
|
pendingQueue[directCount++] = sub2;
|
|
772
888
|
}
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
889
|
+
if (!hasComputedSub) {
|
|
890
|
+
for (let i3 = 0; i3 < directCount; i3++) {
|
|
891
|
+
safeInvoke(pendingQueue[i3]);
|
|
776
892
|
}
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
893
|
+
} else {
|
|
894
|
+
for (let i3 = 0; i3 < directCount; i3++) {
|
|
895
|
+
if (pendingQueue[i3]._c) {
|
|
896
|
+
propagateDirty(pendingQueue[i3]);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
for (let i3 = 0; i3 < directCount; i3++) {
|
|
900
|
+
const sub2 = pendingQueue[i3];
|
|
901
|
+
if (!sub2._c && !pendingSet.has(sub2)) {
|
|
902
|
+
pendingSet.add(sub2);
|
|
903
|
+
safeInvoke(sub2);
|
|
782
904
|
}
|
|
783
905
|
}
|
|
784
906
|
}
|
|
785
907
|
let i2 = directCount;
|
|
786
908
|
while (i2 < pendingQueue.length) {
|
|
909
|
+
if (i2 - directCount >= maxDrainIterations) {
|
|
910
|
+
if (typeof console !== "undefined") {
|
|
911
|
+
console.error(
|
|
912
|
+
`[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
|
|
913
|
+
);
|
|
914
|
+
}
|
|
915
|
+
break;
|
|
916
|
+
}
|
|
787
917
|
safeInvoke(pendingQueue[i2]);
|
|
788
918
|
i2++;
|
|
789
919
|
}
|
|
790
920
|
} finally {
|
|
791
|
-
pendingQueue.length = 0;
|
|
792
|
-
pendingSet.clear();
|
|
793
921
|
notifyDepth--;
|
|
922
|
+
if (notifyDepth === 0) {
|
|
923
|
+
pendingQueue.length = 0;
|
|
924
|
+
pendingSet.clear();
|
|
925
|
+
}
|
|
794
926
|
}
|
|
795
927
|
}
|
|
796
928
|
function cleanup(subscriber) {
|
|
@@ -801,7 +933,9 @@ function cleanup(subscriber) {
|
|
|
801
933
|
if (subs) {
|
|
802
934
|
subs.delete(subscriber);
|
|
803
935
|
if (singleDep.__f === subscriber) {
|
|
804
|
-
singleDep.__f = void 0;
|
|
936
|
+
singleDep.__f = subs.size === 1 ? subs.values().next().value : void 0;
|
|
937
|
+
} else if (subs.size === 1 && singleDep.__f === void 0) {
|
|
938
|
+
singleDep.__f = subs.values().next().value;
|
|
805
939
|
}
|
|
806
940
|
}
|
|
807
941
|
sub2._dep = void 0;
|
|
@@ -814,19 +948,15 @@ function cleanup(subscriber) {
|
|
|
814
948
|
if (subs) {
|
|
815
949
|
subs.delete(subscriber);
|
|
816
950
|
if (signal2.__f === subscriber) {
|
|
817
|
-
signal2.__f = void 0;
|
|
951
|
+
signal2.__f = subs.size === 1 ? subs.values().next().value : void 0;
|
|
952
|
+
} else if (subs.size === 1 && signal2.__f === void 0) {
|
|
953
|
+
signal2.__f = subs.values().next().value;
|
|
818
954
|
}
|
|
819
955
|
}
|
|
820
956
|
}
|
|
821
957
|
deps.clear();
|
|
822
958
|
}
|
|
823
959
|
|
|
824
|
-
// src/core/ssr-context.ts
|
|
825
|
-
var ssrMode = false;
|
|
826
|
-
function isSSR() {
|
|
827
|
-
return ssrMode;
|
|
828
|
-
}
|
|
829
|
-
|
|
830
960
|
// src/core/signals/effect.ts
|
|
831
961
|
var _g = globalThis;
|
|
832
962
|
function effect(effectFn, options) {
|
|
@@ -834,26 +964,86 @@ function effect(effectFn, options) {
|
|
|
834
964
|
if (isSSR()) return () => {
|
|
835
965
|
};
|
|
836
966
|
const onError = options?.onError;
|
|
967
|
+
let userCleanups = [];
|
|
968
|
+
const onCleanup = (fn) => {
|
|
969
|
+
userCleanups.push(fn);
|
|
970
|
+
};
|
|
971
|
+
const runUserCleanups = () => {
|
|
972
|
+
if (userCleanups.length === 0) return;
|
|
973
|
+
const list = userCleanups;
|
|
974
|
+
userCleanups = [];
|
|
975
|
+
for (let i2 = list.length - 1; i2 >= 0; i2--) {
|
|
976
|
+
try {
|
|
977
|
+
list[i2]();
|
|
978
|
+
} catch (err) {
|
|
979
|
+
if (typeof console !== "undefined") {
|
|
980
|
+
console.warn("[SibuJS effect] onCleanup threw:", err);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
};
|
|
985
|
+
const invokeBody = () => effectFn(onCleanup);
|
|
837
986
|
const wrappedFn = onError ? () => {
|
|
838
987
|
try {
|
|
839
|
-
|
|
988
|
+
invokeBody();
|
|
840
989
|
} catch (err) {
|
|
841
990
|
onError(err);
|
|
842
991
|
}
|
|
843
|
-
} :
|
|
992
|
+
} : invokeBody;
|
|
844
993
|
let cleanupHandle = () => {
|
|
845
994
|
};
|
|
995
|
+
let running = false;
|
|
846
996
|
const subscriber = () => {
|
|
847
|
-
|
|
848
|
-
|
|
997
|
+
if (running) {
|
|
998
|
+
if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
|
|
999
|
+
console.warn(
|
|
1000
|
+
"[SibuJS] effect re-entered itself while running \u2014 the triggering update will be ignored. Wrap mutual writes in `batch()` or split the effect to avoid this."
|
|
1001
|
+
);
|
|
1002
|
+
}
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
running = true;
|
|
1006
|
+
try {
|
|
1007
|
+
runUserCleanups();
|
|
1008
|
+
cleanupHandle();
|
|
1009
|
+
cleanupHandle = track(wrappedFn, subscriber);
|
|
1010
|
+
} finally {
|
|
1011
|
+
running = false;
|
|
1012
|
+
}
|
|
849
1013
|
};
|
|
850
|
-
|
|
1014
|
+
running = true;
|
|
1015
|
+
try {
|
|
1016
|
+
cleanupHandle = track(wrappedFn, subscriber);
|
|
1017
|
+
} finally {
|
|
1018
|
+
running = false;
|
|
1019
|
+
}
|
|
851
1020
|
const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
852
1021
|
if (hook) hook.emit("effect:create", { effectFn });
|
|
1022
|
+
let disposed = false;
|
|
853
1023
|
return () => {
|
|
1024
|
+
if (disposed) return;
|
|
1025
|
+
disposed = true;
|
|
854
1026
|
const h = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
855
|
-
if (h)
|
|
856
|
-
|
|
1027
|
+
if (h) {
|
|
1028
|
+
try {
|
|
1029
|
+
h.emit("effect:destroy", { effectFn });
|
|
1030
|
+
} catch {
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
try {
|
|
1034
|
+
runUserCleanups();
|
|
1035
|
+
} catch (err) {
|
|
1036
|
+
if (typeof console !== "undefined") {
|
|
1037
|
+
console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
try {
|
|
1041
|
+
cleanupHandle();
|
|
1042
|
+
} catch (err) {
|
|
1043
|
+
if (typeof console !== "undefined") {
|
|
1044
|
+
console.warn("[SibuJS effect] dispose threw:", err);
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
857
1047
|
};
|
|
858
1048
|
}
|
|
859
1049
|
|
|
@@ -1025,10 +1215,13 @@ function enqueueBatchedSignal(signal2) {
|
|
|
1025
1215
|
return true;
|
|
1026
1216
|
}
|
|
1027
1217
|
function flushBatch() {
|
|
1028
|
-
|
|
1029
|
-
|
|
1218
|
+
try {
|
|
1219
|
+
for (const signal2 of pendingSignals) {
|
|
1220
|
+
queueSignalNotification(signal2);
|
|
1221
|
+
}
|
|
1222
|
+
} finally {
|
|
1223
|
+
pendingSignals.clear();
|
|
1030
1224
|
}
|
|
1031
|
-
pendingSignals.clear();
|
|
1032
1225
|
drainNotificationQueue();
|
|
1033
1226
|
}
|
|
1034
1227
|
|
|
@@ -1075,24 +1268,42 @@ function createISR(options) {
|
|
|
1075
1268
|
const { revalidateAfter, fetcher, initialData } = options;
|
|
1076
1269
|
const [data2, setData] = signal(initialData);
|
|
1077
1270
|
const [timestamp, setTimestamp] = signal(initialData !== void 0 ? Date.now() : 0);
|
|
1271
|
+
const controller = new AbortController();
|
|
1272
|
+
let inFlight = false;
|
|
1273
|
+
let disposed = false;
|
|
1078
1274
|
const isStale = () => {
|
|
1079
1275
|
const ts = timestamp();
|
|
1080
1276
|
if (ts === 0) return true;
|
|
1081
1277
|
return Date.now() - ts >= revalidateAfter;
|
|
1082
1278
|
};
|
|
1083
1279
|
const revalidate = async () => {
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1280
|
+
if (disposed || inFlight) return;
|
|
1281
|
+
if (controller.signal.aborted) return;
|
|
1282
|
+
inFlight = true;
|
|
1283
|
+
try {
|
|
1284
|
+
const result = await fetcher({ signal: controller.signal });
|
|
1285
|
+
if (disposed || controller.signal.aborted) return;
|
|
1286
|
+
setData(result);
|
|
1287
|
+
setTimestamp(Date.now());
|
|
1288
|
+
} finally {
|
|
1289
|
+
inFlight = false;
|
|
1290
|
+
}
|
|
1087
1291
|
};
|
|
1088
1292
|
if (initialData === void 0) {
|
|
1089
|
-
revalidate()
|
|
1293
|
+
revalidate().catch((err) => {
|
|
1294
|
+
if (typeof console !== "undefined") console.warn("[SibuJS ISR] initial fetch failed", err);
|
|
1295
|
+
});
|
|
1090
1296
|
}
|
|
1091
1297
|
const intervalId = setInterval(() => {
|
|
1092
|
-
revalidate()
|
|
1298
|
+
revalidate().catch((err) => {
|
|
1299
|
+
if (typeof console !== "undefined") console.warn("[SibuJS ISR] revalidate failed", err);
|
|
1300
|
+
});
|
|
1093
1301
|
}, revalidateAfter);
|
|
1094
1302
|
const dispose = () => {
|
|
1303
|
+
if (disposed) return;
|
|
1304
|
+
disposed = true;
|
|
1095
1305
|
clearInterval(intervalId);
|
|
1306
|
+
controller.abort();
|
|
1096
1307
|
};
|
|
1097
1308
|
return { data: data2, isStale, revalidate, dispose };
|
|
1098
1309
|
}
|
|
@@ -1200,6 +1411,9 @@ function createMiddlewareChain() {
|
|
|
1200
1411
|
|
|
1201
1412
|
// src/reactivity/bindAttribute.ts
|
|
1202
1413
|
var _isDev5 = isDev();
|
|
1414
|
+
function setProp(el, key, val) {
|
|
1415
|
+
el[key] = val;
|
|
1416
|
+
}
|
|
1203
1417
|
function isEventHandlerAttr3(name) {
|
|
1204
1418
|
if (name.length < 3) return false;
|
|
1205
1419
|
const lower = name.toLowerCase();
|
|
@@ -1225,7 +1439,7 @@ function bindAttribute(el, attr, getter) {
|
|
|
1225
1439
|
}
|
|
1226
1440
|
if (typeof value === "boolean") {
|
|
1227
1441
|
if (attr in el && (attr === "checked" || attr === "disabled" || attr === "selected")) {
|
|
1228
|
-
el
|
|
1442
|
+
setProp(el, attr, value);
|
|
1229
1443
|
} else if (value) {
|
|
1230
1444
|
el.setAttribute(attr, "");
|
|
1231
1445
|
} else {
|
|
@@ -1235,7 +1449,7 @@ function bindAttribute(el, attr, getter) {
|
|
|
1235
1449
|
}
|
|
1236
1450
|
const str = String(value);
|
|
1237
1451
|
if ((attr === "value" || attr === "checked") && attr in el) {
|
|
1238
|
-
el
|
|
1452
|
+
setProp(el, attr, attr === "checked" ? Boolean(value) : str);
|
|
1239
1453
|
} else {
|
|
1240
1454
|
el.setAttribute(attr, isUrlAttribute(attr) ? sanitizeUrl(str) : str);
|
|
1241
1455
|
}
|
|
@@ -1272,24 +1486,29 @@ function bindChildNode(placeholder, getter) {
|
|
|
1272
1486
|
let newNodes;
|
|
1273
1487
|
if (Array.isArray(result)) {
|
|
1274
1488
|
newNodes = [];
|
|
1489
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1275
1490
|
for (let i2 = 0; i2 < result.length; i2++) {
|
|
1276
1491
|
const item = result[i2];
|
|
1277
1492
|
if (item == null || typeof item === "boolean") continue;
|
|
1278
|
-
|
|
1493
|
+
const node = item instanceof Node ? item : document.createTextNode(String(item));
|
|
1494
|
+
if (seen.has(node)) {
|
|
1495
|
+
if (_isDev6)
|
|
1496
|
+
devWarn("bindChildNode: duplicate node reference in array \u2014 only the first occurrence is rendered.");
|
|
1497
|
+
continue;
|
|
1498
|
+
}
|
|
1499
|
+
seen.add(node);
|
|
1500
|
+
newNodes.push(node);
|
|
1279
1501
|
}
|
|
1280
1502
|
} else {
|
|
1281
1503
|
const node = result instanceof Node ? result : document.createTextNode(String(result));
|
|
1282
1504
|
newNodes = [node];
|
|
1283
1505
|
}
|
|
1284
|
-
|
|
1285
|
-
if (
|
|
1506
|
+
let reused;
|
|
1507
|
+
if (lastNodes.length > 0 && newNodes.length > 0) {
|
|
1508
|
+
const lastSet = new Set(lastNodes);
|
|
1509
|
+
reused = /* @__PURE__ */ new Set();
|
|
1286
1510
|
for (let i2 = 0; i2 < newNodes.length; i2++) {
|
|
1287
|
-
|
|
1288
|
-
if (newNodes[i2] === lastNodes[j]) {
|
|
1289
|
-
reused.add(newNodes[i2]);
|
|
1290
|
-
break;
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1511
|
+
if (lastSet.has(newNodes[i2])) reused.add(newNodes[i2]);
|
|
1293
1512
|
}
|
|
1294
1513
|
}
|
|
1295
1514
|
for (let i2 = 0; i2 < lastNodes.length; i2++) {
|
|
@@ -1329,6 +1548,30 @@ function registerDisposer(node, teardown) {
|
|
|
1329
1548
|
|
|
1330
1549
|
// src/core/rendering/tagFactory.ts
|
|
1331
1550
|
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
1551
|
+
var _isDev8 = isDev();
|
|
1552
|
+
var BLOCKED_TAGS = /* @__PURE__ */ new Set(["script", "iframe", "object", "embed", "frame", "frameset"]);
|
|
1553
|
+
function validateTagName(tag) {
|
|
1554
|
+
const lower = tag.toLowerCase();
|
|
1555
|
+
if (BLOCKED_TAGS.has(lower)) {
|
|
1556
|
+
throw new Error(`tagFactory: refusing to create <${tag}> \u2014 tag is blocked for security reasons.`);
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
var CLOBBER_RISKY_IDS = /* @__PURE__ */ new Set([
|
|
1560
|
+
"config",
|
|
1561
|
+
"location",
|
|
1562
|
+
"history",
|
|
1563
|
+
"document",
|
|
1564
|
+
"window",
|
|
1565
|
+
"navigator",
|
|
1566
|
+
"name",
|
|
1567
|
+
"top",
|
|
1568
|
+
"parent",
|
|
1569
|
+
"self",
|
|
1570
|
+
"frames"
|
|
1571
|
+
]);
|
|
1572
|
+
function setProp2(el, key, val) {
|
|
1573
|
+
el[key] = val;
|
|
1574
|
+
}
|
|
1332
1575
|
var kebabCache = /* @__PURE__ */ new Map();
|
|
1333
1576
|
function toKebab(prop) {
|
|
1334
1577
|
let cached = kebabCache.get(prop);
|
|
@@ -1453,79 +1696,103 @@ function appendChildren(el, nodes) {
|
|
|
1453
1696
|
}
|
|
1454
1697
|
}
|
|
1455
1698
|
}
|
|
1456
|
-
var tagFactory = (tag, ns) =>
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
if (
|
|
1461
|
-
|
|
1462
|
-
|
|
1699
|
+
var tagFactory = (tag, ns) => {
|
|
1700
|
+
return (first, second) => {
|
|
1701
|
+
validateTagName(tag);
|
|
1702
|
+
const el = ns ? document.createElementNS(ns, tag) : document.createElement(tag);
|
|
1703
|
+
if (first === void 0) return el;
|
|
1704
|
+
if (typeof first === "string") {
|
|
1705
|
+
if (second !== void 0) {
|
|
1706
|
+
el.setAttribute("class", first);
|
|
1707
|
+
appendChildren(el, second);
|
|
1708
|
+
return el;
|
|
1709
|
+
}
|
|
1710
|
+
el.textContent = first;
|
|
1463
1711
|
return el;
|
|
1464
1712
|
}
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1713
|
+
if (typeof first === "number") {
|
|
1714
|
+
el.textContent = String(first);
|
|
1715
|
+
return el;
|
|
1716
|
+
}
|
|
1717
|
+
if (Array.isArray(first) || first instanceof Node || typeof first === "function") {
|
|
1718
|
+
appendChildren(el, first);
|
|
1719
|
+
return el;
|
|
1720
|
+
}
|
|
1721
|
+
const props = first;
|
|
1722
|
+
const pClass = props.class;
|
|
1723
|
+
if (pClass != null) applyClass(el, pClass);
|
|
1724
|
+
const pId = props.id;
|
|
1725
|
+
if (pId != null) {
|
|
1726
|
+
if (_isDev8 && typeof pId === "string" && CLOBBER_RISKY_IDS.has(pId.toLowerCase())) {
|
|
1727
|
+
devWarn(
|
|
1728
|
+
`tagFactory: element id="${pId}" matches a common global and may cause DOM clobbering. Avoid setting ids from untrusted input.`
|
|
1729
|
+
);
|
|
1730
|
+
}
|
|
1731
|
+
el.id = pId;
|
|
1732
|
+
}
|
|
1733
|
+
const pNodes = second !== void 0 ? second : props.nodes;
|
|
1734
|
+
if (pNodes != null) appendChildren(el, pNodes);
|
|
1735
|
+
const pOn = props.on;
|
|
1736
|
+
if (pOn) {
|
|
1737
|
+
for (const ev in pOn) {
|
|
1738
|
+
const handler = pOn[ev];
|
|
1739
|
+
if (typeof handler === "function") {
|
|
1740
|
+
el.addEventListener(ev, handler);
|
|
1741
|
+
} else if (_isDev8) {
|
|
1742
|
+
devWarn(
|
|
1743
|
+
`tagFactory: on.${ev} handler is not a function (got ${typeof handler}). Event listener was not attached.`
|
|
1744
|
+
);
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
const pStyle = props.style;
|
|
1749
|
+
if (pStyle != null) applyStyle(el, pStyle);
|
|
1750
|
+
const pRef = props.ref;
|
|
1751
|
+
if (pRef) pRef.current = el;
|
|
1752
|
+
for (const key in props) {
|
|
1753
|
+
switch (key) {
|
|
1754
|
+
case "class":
|
|
1755
|
+
case "id":
|
|
1756
|
+
case "nodes":
|
|
1757
|
+
case "on":
|
|
1758
|
+
case "style":
|
|
1759
|
+
case "ref":
|
|
1760
|
+
case "onElement":
|
|
1761
|
+
continue;
|
|
1762
|
+
// already handled above / below
|
|
1763
|
+
default: {
|
|
1764
|
+
const value = props[key];
|
|
1765
|
+
if (value == null) continue;
|
|
1766
|
+
const lkey = key.toLowerCase();
|
|
1767
|
+
if (lkey[0] === "o" && lkey[1] === "n") continue;
|
|
1768
|
+
if (typeof value === "function") {
|
|
1769
|
+
registerDisposer(el, bindAttribute(el, key, value));
|
|
1770
|
+
} else if (typeof value === "boolean") {
|
|
1771
|
+
if (key in el && (key === "checked" || key === "disabled" || key === "selected")) {
|
|
1772
|
+
setProp2(el, key, value);
|
|
1773
|
+
} else if (value) {
|
|
1774
|
+
el.setAttribute(key, "");
|
|
1775
|
+
} else {
|
|
1776
|
+
el.removeAttribute(key);
|
|
1777
|
+
}
|
|
1515
1778
|
} else {
|
|
1516
|
-
|
|
1779
|
+
const str = String(value);
|
|
1780
|
+
if (lkey === "srcset") {
|
|
1781
|
+
el.setAttribute(key, sanitizeSrcset(str));
|
|
1782
|
+
} else if (isUrlAttribute(lkey)) {
|
|
1783
|
+
el.setAttribute(key, sanitizeUrl(str));
|
|
1784
|
+
} else {
|
|
1785
|
+
el.setAttribute(key, str);
|
|
1786
|
+
}
|
|
1517
1787
|
}
|
|
1518
|
-
} else {
|
|
1519
|
-
const str = String(value);
|
|
1520
|
-
el.setAttribute(key, isUrlAttribute(key) ? sanitizeUrl(str) : str);
|
|
1521
1788
|
}
|
|
1522
1789
|
}
|
|
1523
1790
|
}
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1791
|
+
if (props.onElement && typeof props.onElement === "function") {
|
|
1792
|
+
props.onElement(el);
|
|
1793
|
+
}
|
|
1794
|
+
return el;
|
|
1795
|
+
};
|
|
1529
1796
|
};
|
|
1530
1797
|
|
|
1531
1798
|
// src/core/rendering/html.ts
|
|
@@ -1679,31 +1946,38 @@ function createMicroApp(config) {
|
|
|
1679
1946
|
}
|
|
1680
1947
|
let mounted = false;
|
|
1681
1948
|
function mount(component) {
|
|
1682
|
-
|
|
1683
|
-
root.innerHTML = "";
|
|
1684
|
-
} else {
|
|
1685
|
-
while (root.firstChild) {
|
|
1686
|
-
root.removeChild(root.firstChild);
|
|
1687
|
-
}
|
|
1688
|
-
}
|
|
1949
|
+
root.replaceChildren();
|
|
1689
1950
|
const el = component();
|
|
1690
1951
|
root.appendChild(el);
|
|
1691
1952
|
mounted = true;
|
|
1692
1953
|
}
|
|
1693
1954
|
function unmount() {
|
|
1694
1955
|
if (!mounted) return;
|
|
1695
|
-
|
|
1696
|
-
root.innerHTML = "";
|
|
1697
|
-
} else {
|
|
1698
|
-
while (root.firstChild) {
|
|
1699
|
-
root.removeChild(root.firstChild);
|
|
1700
|
-
}
|
|
1701
|
-
}
|
|
1956
|
+
root.replaceChildren();
|
|
1702
1957
|
mounted = false;
|
|
1703
1958
|
}
|
|
1704
1959
|
return { mount, unmount, element: host };
|
|
1705
1960
|
}
|
|
1706
|
-
function loadRemoteModule(url) {
|
|
1961
|
+
function loadRemoteModule(url, optionsOrAllowedOrigins = []) {
|
|
1962
|
+
const opts = Array.isArray(optionsOrAllowedOrigins) ? { allowedOrigins: optionsOrAllowedOrigins } : optionsOrAllowedOrigins;
|
|
1963
|
+
const allowedOrigins = opts.allowedOrigins ?? [];
|
|
1964
|
+
if (allowedOrigins.length > 0) {
|
|
1965
|
+
let parsed;
|
|
1966
|
+
try {
|
|
1967
|
+
parsed = new URL(url, typeof location !== "undefined" ? location.href : void 0);
|
|
1968
|
+
} catch {
|
|
1969
|
+
return Promise.reject(new Error(`loadRemoteModule: invalid URL "${url}"`));
|
|
1970
|
+
}
|
|
1971
|
+
if (!allowedOrigins.includes(parsed.origin)) {
|
|
1972
|
+
return Promise.reject(new Error(`loadRemoteModule: origin "${parsed.origin}" is not in the allowlist`));
|
|
1973
|
+
}
|
|
1974
|
+
} else if (!opts.unsafelyAllowAnyOrigin) {
|
|
1975
|
+
return Promise.reject(
|
|
1976
|
+
new Error(
|
|
1977
|
+
`loadRemoteModule: refused to import "${url}" with no allowedOrigins. Pass { allowedOrigins: [...] } to restrict the origin, or { unsafelyAllowAnyOrigin: true } to opt in to unrestricted imports (CWE-829).`
|
|
1978
|
+
)
|
|
1979
|
+
);
|
|
1980
|
+
}
|
|
1707
1981
|
const cached = moduleCache.get(url);
|
|
1708
1982
|
if (cached) return cached;
|
|
1709
1983
|
const promise = import(
|
|
@@ -1793,21 +2067,49 @@ function serviceWorker(scriptUrl, options) {
|
|
|
1793
2067
|
const [isReady, setIsReady] = signal(false);
|
|
1794
2068
|
const [isUpdateAvailable, setIsUpdateAvailable] = signal(false);
|
|
1795
2069
|
const [error, setError] = signal(null);
|
|
2070
|
+
let disposed = false;
|
|
2071
|
+
let updateFoundHandler = null;
|
|
2072
|
+
let stateChangeHandler = null;
|
|
2073
|
+
let trackedWorker = null;
|
|
2074
|
+
let trackedReg = null;
|
|
2075
|
+
function detachListeners() {
|
|
2076
|
+
if (trackedReg && updateFoundHandler) {
|
|
2077
|
+
trackedReg.removeEventListener("updatefound", updateFoundHandler);
|
|
2078
|
+
}
|
|
2079
|
+
if (trackedWorker && stateChangeHandler) {
|
|
2080
|
+
trackedWorker.removeEventListener("statechange", stateChangeHandler);
|
|
2081
|
+
}
|
|
2082
|
+
updateFoundHandler = null;
|
|
2083
|
+
stateChangeHandler = null;
|
|
2084
|
+
trackedWorker = null;
|
|
2085
|
+
trackedReg = null;
|
|
2086
|
+
}
|
|
1796
2087
|
if ("serviceWorker" in navigator) {
|
|
1797
2088
|
navigator.serviceWorker.register(scriptUrl, options).then((reg) => {
|
|
2089
|
+
if (disposed) return;
|
|
1798
2090
|
setRegistration(reg);
|
|
1799
2091
|
setIsReady(true);
|
|
1800
|
-
|
|
2092
|
+
trackedReg = reg;
|
|
2093
|
+
updateFoundHandler = () => {
|
|
2094
|
+
if (disposed) return;
|
|
1801
2095
|
const newWorker = reg.installing;
|
|
1802
2096
|
if (newWorker) {
|
|
1803
|
-
|
|
2097
|
+
if (trackedWorker && stateChangeHandler) {
|
|
2098
|
+
trackedWorker.removeEventListener("statechange", stateChangeHandler);
|
|
2099
|
+
}
|
|
2100
|
+
trackedWorker = newWorker;
|
|
2101
|
+
stateChangeHandler = () => {
|
|
2102
|
+
if (disposed) return;
|
|
1804
2103
|
if (newWorker.state === "installed" && navigator.serviceWorker.controller) {
|
|
1805
2104
|
setIsUpdateAvailable(true);
|
|
1806
2105
|
}
|
|
1807
|
-
}
|
|
2106
|
+
};
|
|
2107
|
+
newWorker.addEventListener("statechange", stateChangeHandler);
|
|
1808
2108
|
}
|
|
1809
|
-
}
|
|
2109
|
+
};
|
|
2110
|
+
reg.addEventListener("updatefound", updateFoundHandler);
|
|
1810
2111
|
}).catch((err) => {
|
|
2112
|
+
if (disposed) return;
|
|
1811
2113
|
setError(err instanceof Error ? err : new Error(String(err)));
|
|
1812
2114
|
});
|
|
1813
2115
|
}
|
|
@@ -1818,6 +2120,8 @@ function serviceWorker(scriptUrl, options) {
|
|
|
1818
2120
|
}
|
|
1819
2121
|
}
|
|
1820
2122
|
async function unregister() {
|
|
2123
|
+
disposed = true;
|
|
2124
|
+
detachListeners();
|
|
1821
2125
|
const reg = registration();
|
|
1822
2126
|
if (reg) {
|
|
1823
2127
|
const result = await reg.unregister();
|
|
@@ -1838,24 +2142,37 @@ function worker(workerFn2) {
|
|
|
1838
2142
|
const [error, setError] = signal(null);
|
|
1839
2143
|
const [loading, setLoading] = signal(false);
|
|
1840
2144
|
let worker2 = null;
|
|
2145
|
+
let blobUrl = null;
|
|
2146
|
+
const revokeBlobUrl = () => {
|
|
2147
|
+
if (blobUrl) {
|
|
2148
|
+
URL.revokeObjectURL(blobUrl);
|
|
2149
|
+
blobUrl = null;
|
|
2150
|
+
}
|
|
2151
|
+
};
|
|
1841
2152
|
try {
|
|
1842
2153
|
if (typeof Worker === "undefined") {
|
|
1843
2154
|
throw new Error("Web Workers are not supported in this environment");
|
|
1844
2155
|
}
|
|
1845
2156
|
const fnBody = workerFn2.toString();
|
|
1846
2157
|
const blob = new Blob([`self.onmessage = ${fnBody};`], { type: "application/javascript" });
|
|
1847
|
-
|
|
1848
|
-
worker2 = new Worker(
|
|
1849
|
-
|
|
1850
|
-
|
|
2158
|
+
blobUrl = URL.createObjectURL(blob);
|
|
2159
|
+
worker2 = new Worker(blobUrl);
|
|
2160
|
+
worker2.addEventListener("message", (e) => {
|
|
2161
|
+
revokeBlobUrl();
|
|
1851
2162
|
setResult(e.data);
|
|
1852
2163
|
setLoading(false);
|
|
1853
|
-
};
|
|
1854
|
-
worker2.
|
|
2164
|
+
});
|
|
2165
|
+
worker2.addEventListener("error", (e) => {
|
|
2166
|
+
revokeBlobUrl();
|
|
1855
2167
|
setError(new Error(e.message || "Worker error"));
|
|
1856
2168
|
setLoading(false);
|
|
1857
|
-
|
|
2169
|
+
if (worker2) {
|
|
2170
|
+
worker2.terminate();
|
|
2171
|
+
worker2 = null;
|
|
2172
|
+
}
|
|
2173
|
+
});
|
|
1858
2174
|
} catch (err) {
|
|
2175
|
+
revokeBlobUrl();
|
|
1859
2176
|
setError(err instanceof Error ? err : new Error(String(err)));
|
|
1860
2177
|
}
|
|
1861
2178
|
function post(data2) {
|
|
@@ -1869,6 +2186,7 @@ function worker(workerFn2) {
|
|
|
1869
2186
|
if (!worker2) return;
|
|
1870
2187
|
worker2.terminate();
|
|
1871
2188
|
worker2 = null;
|
|
2189
|
+
revokeBlobUrl();
|
|
1872
2190
|
setLoading(false);
|
|
1873
2191
|
}
|
|
1874
2192
|
return { post, result, error, loading, terminate };
|
|
@@ -1876,6 +2194,14 @@ function worker(workerFn2) {
|
|
|
1876
2194
|
function workerFn(fn) {
|
|
1877
2195
|
const [loading, setLoading] = signal(false);
|
|
1878
2196
|
let worker2 = null;
|
|
2197
|
+
let blobUrl = null;
|
|
2198
|
+
const revokeBlobUrl = () => {
|
|
2199
|
+
if (blobUrl) {
|
|
2200
|
+
URL.revokeObjectURL(blobUrl);
|
|
2201
|
+
blobUrl = null;
|
|
2202
|
+
}
|
|
2203
|
+
};
|
|
2204
|
+
const queue = [];
|
|
1879
2205
|
try {
|
|
1880
2206
|
if (typeof Worker === "undefined") {
|
|
1881
2207
|
throw new Error("Web Workers are not supported in this environment");
|
|
@@ -1891,10 +2217,26 @@ function workerFn(fn) {
|
|
|
1891
2217
|
],
|
|
1892
2218
|
{ type: "application/javascript" }
|
|
1893
2219
|
);
|
|
1894
|
-
|
|
1895
|
-
worker2 = new Worker(
|
|
1896
|
-
|
|
2220
|
+
blobUrl = URL.createObjectURL(blob);
|
|
2221
|
+
worker2 = new Worker(blobUrl);
|
|
2222
|
+
worker2.addEventListener("message", (e) => {
|
|
2223
|
+
revokeBlobUrl();
|
|
2224
|
+
const head2 = queue.shift();
|
|
2225
|
+
if (queue.length === 0) setLoading(false);
|
|
2226
|
+
if (head2) head2.resolve(e.data);
|
|
2227
|
+
});
|
|
2228
|
+
worker2.addEventListener("error", (e) => {
|
|
2229
|
+
revokeBlobUrl();
|
|
2230
|
+
const err = new Error(e.message || "Worker error");
|
|
2231
|
+
while (queue.length > 0) queue.shift().reject(err);
|
|
2232
|
+
setLoading(false);
|
|
2233
|
+
if (worker2) {
|
|
2234
|
+
worker2.terminate();
|
|
2235
|
+
worker2 = null;
|
|
2236
|
+
}
|
|
2237
|
+
});
|
|
1897
2238
|
} catch {
|
|
2239
|
+
revokeBlobUrl();
|
|
1898
2240
|
}
|
|
1899
2241
|
function run(...args) {
|
|
1900
2242
|
return new Promise((resolve, reject) => {
|
|
@@ -1903,14 +2245,7 @@ function workerFn(fn) {
|
|
|
1903
2245
|
return;
|
|
1904
2246
|
}
|
|
1905
2247
|
setLoading(true);
|
|
1906
|
-
|
|
1907
|
-
setLoading(false);
|
|
1908
|
-
resolve(e.data);
|
|
1909
|
-
};
|
|
1910
|
-
worker2.onerror = (e) => {
|
|
1911
|
-
setLoading(false);
|
|
1912
|
-
reject(new Error(e.message || "Worker error"));
|
|
1913
|
-
};
|
|
2248
|
+
queue.push({ resolve, reject });
|
|
1914
2249
|
worker2.postMessage(args);
|
|
1915
2250
|
});
|
|
1916
2251
|
}
|
|
@@ -1918,6 +2253,9 @@ function workerFn(fn) {
|
|
|
1918
2253
|
if (!worker2) return;
|
|
1919
2254
|
worker2.terminate();
|
|
1920
2255
|
worker2 = null;
|
|
2256
|
+
const err = new Error("Worker terminated");
|
|
2257
|
+
while (queue.length > 0) queue.shift().reject(err);
|
|
2258
|
+
revokeBlobUrl();
|
|
1921
2259
|
setLoading(false);
|
|
1922
2260
|
}
|
|
1923
2261
|
return { run, loading, terminate };
|
|
@@ -1925,20 +2263,63 @@ function workerFn(fn) {
|
|
|
1925
2263
|
function createWorkerPool(workerFn2, poolSize) {
|
|
1926
2264
|
const size = poolSize || typeof navigator !== "undefined" && navigator.hardwareConcurrency || 4;
|
|
1927
2265
|
const workers = [];
|
|
2266
|
+
const queues = [];
|
|
2267
|
+
const inflight = [];
|
|
1928
2268
|
let currentIndex = 0;
|
|
1929
2269
|
let alive = true;
|
|
2270
|
+
let blobUrl = null;
|
|
2271
|
+
let firedOnce = false;
|
|
2272
|
+
const revokeBlobUrl = () => {
|
|
2273
|
+
if (blobUrl) {
|
|
2274
|
+
URL.revokeObjectURL(blobUrl);
|
|
2275
|
+
blobUrl = null;
|
|
2276
|
+
}
|
|
2277
|
+
};
|
|
2278
|
+
function dispatchNext(idx) {
|
|
2279
|
+
if (!alive || inflight[idx] || queues[idx].length === 0) return;
|
|
2280
|
+
const w = workers[idx];
|
|
2281
|
+
const slot2 = queues[idx].shift();
|
|
2282
|
+
const onMsg = (e) => {
|
|
2283
|
+
if (!firedOnce) {
|
|
2284
|
+
firedOnce = true;
|
|
2285
|
+
revokeBlobUrl();
|
|
2286
|
+
}
|
|
2287
|
+
w.removeEventListener("message", onMsg);
|
|
2288
|
+
w.removeEventListener("error", onErr);
|
|
2289
|
+
inflight[idx] = null;
|
|
2290
|
+
slot2.resolve(e.data);
|
|
2291
|
+
dispatchNext(idx);
|
|
2292
|
+
};
|
|
2293
|
+
const onErr = (e) => {
|
|
2294
|
+
if (!firedOnce) {
|
|
2295
|
+
firedOnce = true;
|
|
2296
|
+
revokeBlobUrl();
|
|
2297
|
+
}
|
|
2298
|
+
w.removeEventListener("message", onMsg);
|
|
2299
|
+
w.removeEventListener("error", onErr);
|
|
2300
|
+
inflight[idx] = null;
|
|
2301
|
+
slot2.reject(new Error(e.message || "Worker error"));
|
|
2302
|
+
dispatchNext(idx);
|
|
2303
|
+
};
|
|
2304
|
+
inflight[idx] = { ...slot2, onMsg, onErr };
|
|
2305
|
+
w.addEventListener("message", onMsg);
|
|
2306
|
+
w.addEventListener("error", onErr);
|
|
2307
|
+
w.postMessage(slot2.data);
|
|
2308
|
+
}
|
|
1930
2309
|
try {
|
|
1931
2310
|
if (typeof Worker === "undefined") {
|
|
1932
2311
|
throw new Error("Web Workers are not supported in this environment");
|
|
1933
2312
|
}
|
|
1934
2313
|
const fnBody = workerFn2.toString();
|
|
1935
2314
|
const blob = new Blob([`self.onmessage = ${fnBody};`], { type: "application/javascript" });
|
|
1936
|
-
|
|
2315
|
+
blobUrl = URL.createObjectURL(blob);
|
|
1937
2316
|
for (let i2 = 0; i2 < size; i2++) {
|
|
1938
|
-
workers.push(new Worker(
|
|
2317
|
+
workers.push(new Worker(blobUrl));
|
|
2318
|
+
queues.push([]);
|
|
2319
|
+
inflight.push(null);
|
|
1939
2320
|
}
|
|
1940
|
-
URL.revokeObjectURL(url);
|
|
1941
2321
|
} catch {
|
|
2322
|
+
revokeBlobUrl();
|
|
1942
2323
|
}
|
|
1943
2324
|
function execute(data2) {
|
|
1944
2325
|
return new Promise((resolve, reject) => {
|
|
@@ -1946,23 +2327,25 @@ function createWorkerPool(workerFn2, poolSize) {
|
|
|
1946
2327
|
reject(new Error("Worker pool is not available"));
|
|
1947
2328
|
return;
|
|
1948
2329
|
}
|
|
1949
|
-
const
|
|
2330
|
+
const idx = currentIndex % workers.length;
|
|
1950
2331
|
currentIndex++;
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
};
|
|
1954
|
-
worker2.onerror = (e) => {
|
|
1955
|
-
reject(new Error(e.message || "Worker error"));
|
|
1956
|
-
};
|
|
1957
|
-
worker2.postMessage(data2);
|
|
2332
|
+
queues[idx].push({ data: data2, resolve, reject });
|
|
2333
|
+
dispatchNext(idx);
|
|
1958
2334
|
});
|
|
1959
2335
|
}
|
|
1960
2336
|
function terminate() {
|
|
1961
2337
|
alive = false;
|
|
1962
|
-
for (const w of workers)
|
|
1963
|
-
|
|
2338
|
+
for (const w of workers) w.terminate();
|
|
2339
|
+
const err = new Error("Worker pool terminated");
|
|
2340
|
+
for (let i2 = 0; i2 < queues.length; i2++) {
|
|
2341
|
+
const inf = inflight[i2];
|
|
2342
|
+
if (inf) inf.reject(err);
|
|
2343
|
+
for (const s2 of queues[i2]) s2.reject(err);
|
|
2344
|
+
queues[i2] = [];
|
|
2345
|
+
inflight[i2] = null;
|
|
1964
2346
|
}
|
|
1965
2347
|
workers.length = 0;
|
|
2348
|
+
revokeBlobUrl();
|
|
1966
2349
|
}
|
|
1967
2350
|
return { execute, terminate };
|
|
1968
2351
|
}
|
|
@@ -1998,7 +2381,28 @@ function wasm(source2, config = {}) {
|
|
|
1998
2381
|
};
|
|
1999
2382
|
}
|
|
2000
2383
|
async function loadWasmModule(source2, imports, cacheKey) {
|
|
2001
|
-
const
|
|
2384
|
+
const isOptionsBag = !!(imports && ("allowedOrigins" in imports || "unsafelyAllowAnyOrigin" in imports));
|
|
2385
|
+
const opts = isOptionsBag ? imports : { imports, cacheKey };
|
|
2386
|
+
const wasmImports = opts.imports;
|
|
2387
|
+
const key = opts.cacheKey || (typeof source2 === "string" ? source2 : void 0);
|
|
2388
|
+
if (typeof source2 === "string") {
|
|
2389
|
+
const allowed = opts.allowedOrigins ?? [];
|
|
2390
|
+
if (allowed.length > 0) {
|
|
2391
|
+
let parsed;
|
|
2392
|
+
try {
|
|
2393
|
+
parsed = new URL(source2, typeof location !== "undefined" ? location.href : void 0);
|
|
2394
|
+
} catch {
|
|
2395
|
+
throw new Error(`loadWasmModule: invalid URL "${source2}"`);
|
|
2396
|
+
}
|
|
2397
|
+
if (!allowed.includes(parsed.origin)) {
|
|
2398
|
+
throw new Error(`loadWasmModule: origin "${parsed.origin}" is not in the allowlist`);
|
|
2399
|
+
}
|
|
2400
|
+
} else if (!opts.unsafelyAllowAnyOrigin) {
|
|
2401
|
+
throw new Error(
|
|
2402
|
+
`loadWasmModule: refused to fetch "${source2}" with no allowedOrigins. Pass { allowedOrigins: [...] } to restrict the origin, or { unsafelyAllowAnyOrigin: true } to opt in (CWE-829).`
|
|
2403
|
+
);
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2002
2406
|
if (key) {
|
|
2003
2407
|
const cachedInstance = instanceCache.get(key);
|
|
2004
2408
|
if (cachedInstance) {
|
|
@@ -2014,7 +2418,7 @@ async function loadWasmModule(source2, imports, cacheKey) {
|
|
|
2014
2418
|
if (typeof source2 === "string") {
|
|
2015
2419
|
if (typeof WebAssembly.instantiateStreaming === "function") {
|
|
2016
2420
|
const response2 = fetch(source2);
|
|
2017
|
-
const result = await WebAssembly.instantiateStreaming(response2,
|
|
2421
|
+
const result = await WebAssembly.instantiateStreaming(response2, wasmImports || {});
|
|
2018
2422
|
if (key) {
|
|
2019
2423
|
moduleCache2.set(key, result.module);
|
|
2020
2424
|
instanceCache.set(key, result.instance);
|
|
@@ -2031,12 +2435,28 @@ async function loadWasmModule(source2, imports, cacheKey) {
|
|
|
2031
2435
|
module2 = await WebAssembly.compile(bytes);
|
|
2032
2436
|
if (key) moduleCache2.set(key, module2);
|
|
2033
2437
|
}
|
|
2034
|
-
const instance = await WebAssembly.instantiate(module2,
|
|
2438
|
+
const instance = await WebAssembly.instantiate(module2, wasmImports || {});
|
|
2035
2439
|
if (key) instanceCache.set(key, instance);
|
|
2036
2440
|
return instance;
|
|
2037
2441
|
}
|
|
2038
|
-
async function preloadWasm(url) {
|
|
2442
|
+
async function preloadWasm(url, options = {}) {
|
|
2039
2443
|
if (moduleCache2.has(url)) return;
|
|
2444
|
+
const allowed = options.allowedOrigins ?? [];
|
|
2445
|
+
if (allowed.length > 0) {
|
|
2446
|
+
let parsed;
|
|
2447
|
+
try {
|
|
2448
|
+
parsed = new URL(url, typeof location !== "undefined" ? location.href : void 0);
|
|
2449
|
+
} catch {
|
|
2450
|
+
throw new Error(`preloadWasm: invalid URL "${url}"`);
|
|
2451
|
+
}
|
|
2452
|
+
if (!allowed.includes(parsed.origin)) {
|
|
2453
|
+
throw new Error(`preloadWasm: origin "${parsed.origin}" is not in the allowlist`);
|
|
2454
|
+
}
|
|
2455
|
+
} else if (!options.unsafelyAllowAnyOrigin) {
|
|
2456
|
+
throw new Error(
|
|
2457
|
+
`preloadWasm: refused to fetch "${url}" with no allowedOrigins. Pass { allowedOrigins: [...] } or { unsafelyAllowAnyOrigin: true } (CWE-829).`
|
|
2458
|
+
);
|
|
2459
|
+
}
|
|
2040
2460
|
let module2;
|
|
2041
2461
|
if (typeof WebAssembly.compileStreaming === "function") {
|
|
2042
2462
|
module2 = await WebAssembly.compileStreaming(fetch(url));
|