sibujs 3.4.0 → 4.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/LICENSE +21 -21
- package/README.md +118 -15
- package/dist/browser.cjs +410 -101
- package/dist/browser.d.cts +50 -5
- package/dist/browser.d.ts +50 -5
- package/dist/browser.js +8 -6
- package/dist/build.cjs +777 -385
- package/dist/build.js +15 -15
- package/dist/cdn.global.js +11 -7
- package/dist/{chunk-JCO7AHNU.js → chunk-3EKJK5FZ.js} +73 -34
- package/dist/chunk-4MFZMLUU.js +147 -0
- package/dist/chunk-52XFPGSN.js +123 -0
- package/dist/chunk-5INI7D2L.js +194 -0
- package/dist/{chunk-OAVXAFAY.js → chunk-6LG2T7LO.js} +5 -5
- package/dist/{chunk-OOUQUCNN.js → chunk-ACBT6UZS.js} +57 -22
- package/dist/{chunk-F7HW2NI5.js → chunk-AW6HPJ3L.js} +1 -1
- package/dist/chunk-DHDZ7IVN.js +109 -0
- package/dist/{chunk-JNGGM2XE.js → chunk-DQVUBVNX.js} +16 -25
- package/dist/{chunk-AXFWGY32.js → chunk-DR7H6UGM.js} +232 -67
- package/dist/{chunk-WSGLJYFI.js → chunk-E62UADKL.js} +9 -3
- package/dist/{chunk-3Z2NGWQI.js → chunk-ES2GERY2.js} +1 -1
- package/dist/{chunk-S3V6ZXPI.js → chunk-EWEE7NBF.js} +115 -41
- package/dist/{chunk-3DVGCW7O.js → chunk-F64ZPCFE.js} +8 -8
- package/dist/{chunk-APFRCIRI.js → chunk-GCSGGA3Q.js} +295 -198
- package/dist/{chunk-EX77FXTT.js → chunk-HYCCIYNS.js} +228 -51
- package/dist/{chunk-ELXMRSVN.js → chunk-NF3LYCQH.js} +259 -124
- package/dist/chunk-O3QQ3XOL.js +61 -0
- package/dist/{chunk-BUKYCWHX.js → chunk-PBQEW5VW.js} +14 -10
- package/dist/{chunk-H5FI5WDP.js → chunk-RRIDJQ5C.js} +3 -3
- package/dist/{chunk-S3NFJO6L.js → chunk-TIRZCERI.js} +36 -5
- package/dist/{chunk-ODXVQ3CS.js → chunk-VFHIVEPF.js} +309 -188
- package/dist/chunk-VPP2FONR.js +142 -0
- package/dist/{chunk-KZBEVFXZ.js → chunk-W55YHPEP.js} +5 -3
- package/dist/{chunk-FKQG7FTY.js → chunk-WN6WDFCB.js} +4 -4
- package/dist/{chunk-CEBWDD63.js → chunk-WOOMUJFT.js} +228 -86
- package/dist/{chunk-ITWCTZYH.js → chunk-Z2CE42DB.js} +19 -25
- package/dist/{contracts-ey_Qh8ef.d.cts → contracts-Hg6ITAEF.d.cts} +6 -6
- package/dist/{contracts-ey_Qh8ef.d.ts → contracts-Hg6ITAEF.d.ts} +6 -6
- package/dist/{customElement-CPfIrbvg.d.ts → customElement-CKLoO63w.d.cts} +73 -11
- package/dist/{customElement-CPfIrbvg.d.cts → customElement-CKLoO63w.d.ts} +73 -11
- package/dist/data.cjs +483 -205
- package/dist/data.d.cts +139 -7
- package/dist/data.d.ts +139 -7
- package/dist/data.js +9 -9
- package/dist/devtools.cjs +222 -87
- package/dist/devtools.js +8 -8
- package/dist/dispose-4FZ6OFXR.js +19 -0
- package/dist/ecosystem.cjs +419 -175
- package/dist/ecosystem.js +11 -11
- package/dist/extras.cjs +1981 -765
- package/dist/extras.d.cts +7 -7
- package/dist/extras.d.ts +7 -7
- package/dist/extras.js +29 -26
- package/dist/index.cjs +784 -385
- package/dist/index.d.cts +299 -28
- package/dist/index.d.ts +299 -28
- package/dist/index.js +33 -18
- package/dist/motion.cjs +180 -50
- package/dist/motion.d.cts +26 -0
- package/dist/motion.d.ts +26 -0
- package/dist/motion.js +5 -5
- package/dist/patterns.cjs +437 -170
- package/dist/patterns.d.cts +60 -13
- package/dist/patterns.d.ts +60 -13
- package/dist/patterns.js +7 -7
- package/dist/performance.cjs +333 -148
- package/dist/performance.d.cts +55 -7
- package/dist/performance.d.ts +55 -7
- package/dist/performance.js +9 -9
- package/dist/plugins.cjs +1374 -576
- package/dist/plugins.d.cts +227 -9
- package/dist/plugins.d.ts +227 -9
- package/dist/plugins.js +613 -321
- package/dist/{ssr-D62yFwuw.d.cts → ssr-BiPRdZ6n.d.cts} +44 -15
- package/dist/{ssr-D62yFwuw.d.ts → ssr-BiPRdZ6n.d.ts} +44 -15
- package/dist/{ssr-2PPULEK2.js → ssr-JFP5535P.js} +5 -4
- package/dist/ssr.cjs +1104 -443
- package/dist/ssr.d.cts +230 -20
- package/dist/ssr.d.ts +230 -20
- package/dist/ssr.js +15 -12
- package/dist/{startup-0Qv6aosO.d.cts → startup-BMpaiMhP.d.cts} +1 -1
- package/dist/{startup-0Qv6aosO.d.ts → startup-BMpaiMhP.d.ts} +1 -1
- package/dist/testing.cjs +205 -63
- package/dist/testing.d.cts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +8 -10
- package/dist/ui.cjs +640 -199
- package/dist/ui.d.cts +2 -2
- package/dist/ui.d.ts +2 -2
- package/dist/ui.js +10 -10
- package/dist/widgets.cjs +207 -82
- package/dist/widgets.js +9 -9
- package/package.json +12 -7
- package/dist/chunk-5VGSK6D2.js +0 -80
- package/dist/chunk-COY6PUD2.js +0 -34
- package/dist/chunk-K35BAR4P.js +0 -96
- package/dist/chunk-L3GAGWCC.js +0 -111
- package/dist/chunk-RT3IYFN2.js +0 -38
package/dist/ssr.cjs
CHANGED
|
@@ -43,6 +43,7 @@ __export(ssr_exports, {
|
|
|
43
43
|
island: () => island,
|
|
44
44
|
loadRemoteModule: () => loadRemoteModule,
|
|
45
45
|
loadWasmModule: () => loadWasmModule,
|
|
46
|
+
loadWasmModuleWithOptions: () => loadWasmModuleWithOptions,
|
|
46
47
|
preloadWasm: () => preloadWasm,
|
|
47
48
|
renderToDocument: () => renderToDocument,
|
|
48
49
|
renderToReadableStream: () => renderToReadableStream,
|
|
@@ -86,6 +87,170 @@ function devWarn(message) {
|
|
|
86
87
|
}
|
|
87
88
|
}
|
|
88
89
|
|
|
90
|
+
// src/core/errors.ts
|
|
91
|
+
var ERROR_STATE_KEY = /* @__PURE__ */ Symbol.for("sibujs.runtime-errors.v1");
|
|
92
|
+
function state() {
|
|
93
|
+
const g2 = globalThis;
|
|
94
|
+
const existing = g2[ERROR_STATE_KEY];
|
|
95
|
+
if (existing) return existing;
|
|
96
|
+
const created = { handler: null, handlerDepth: 0 };
|
|
97
|
+
g2[ERROR_STATE_KEY] = created;
|
|
98
|
+
return created;
|
|
99
|
+
}
|
|
100
|
+
function normalizeError(value) {
|
|
101
|
+
if (value instanceof Error) return value;
|
|
102
|
+
const message = typeof value === "string" ? value : `Non-Error value thrown: ${safeStringify(value)}`;
|
|
103
|
+
return new Error(message, { cause: value });
|
|
104
|
+
}
|
|
105
|
+
function safeStringify(value) {
|
|
106
|
+
if (typeof value === "symbol") return value.toString();
|
|
107
|
+
try {
|
|
108
|
+
return typeof value === "object" && value !== null ? JSON.stringify(value) : String(value);
|
|
109
|
+
} catch {
|
|
110
|
+
return Object.prototype.toString.call(value);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function describe(context) {
|
|
114
|
+
return context.name ? `${context.phase} "${context.name}"` : context.phase;
|
|
115
|
+
}
|
|
116
|
+
var REACTIVE_REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
|
|
117
|
+
function untrackedRun(fn) {
|
|
118
|
+
const api = globalThis[REACTIVE_REGISTRY_KEY];
|
|
119
|
+
return api?.untracked ? api.untracked(fn) : fn();
|
|
120
|
+
}
|
|
121
|
+
var PROPAGATE_EVENT = "sibu:error-propagate";
|
|
122
|
+
function claimedByBoundary(error, context) {
|
|
123
|
+
const raw = context.node;
|
|
124
|
+
if (!raw || typeof CustomEvent === "undefined") return false;
|
|
125
|
+
const target = resolveEventTarget(raw);
|
|
126
|
+
if (!target) return false;
|
|
127
|
+
try {
|
|
128
|
+
const event = new CustomEvent(PROPAGATE_EVENT, {
|
|
129
|
+
bubbles: true,
|
|
130
|
+
cancelable: true,
|
|
131
|
+
detail: { error, context }
|
|
132
|
+
});
|
|
133
|
+
return target.dispatchEvent(event) === false;
|
|
134
|
+
} catch {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function resolveEventTarget(node) {
|
|
139
|
+
const ELEMENT_NODE = 1;
|
|
140
|
+
let current = node;
|
|
141
|
+
while (current) {
|
|
142
|
+
if (current.nodeType === ELEMENT_NODE && typeof current.dispatchEvent === "function") {
|
|
143
|
+
return current;
|
|
144
|
+
}
|
|
145
|
+
current = current.parentNode;
|
|
146
|
+
}
|
|
147
|
+
const self = node;
|
|
148
|
+
return typeof self.dispatchEvent === "function" ? self : null;
|
|
149
|
+
}
|
|
150
|
+
function defaultReport(error, context) {
|
|
151
|
+
if (typeof console === "undefined") return;
|
|
152
|
+
console.error(`[SibuJS] Uncaught error during ${describe(context)}: ${error.message}`, error);
|
|
153
|
+
}
|
|
154
|
+
function reportError(error, context) {
|
|
155
|
+
const err = normalizeError(error);
|
|
156
|
+
untrackedRun(() => {
|
|
157
|
+
if (claimedByBoundary(err, context)) return;
|
|
158
|
+
const s2 = state();
|
|
159
|
+
const handler = s2.handler;
|
|
160
|
+
if (handler && s2.handlerDepth === 0) {
|
|
161
|
+
s2.handlerDepth++;
|
|
162
|
+
try {
|
|
163
|
+
handler(error instanceof Error ? error : err, context);
|
|
164
|
+
return;
|
|
165
|
+
} catch (handlerError) {
|
|
166
|
+
if (typeof console !== "undefined") {
|
|
167
|
+
const detail = handlerError instanceof Error ? handlerError.message : String(handlerError);
|
|
168
|
+
console.error(`[SibuJS] runtime error handler itself threw: ${detail}`, handlerError);
|
|
169
|
+
}
|
|
170
|
+
} finally {
|
|
171
|
+
s2.handlerDepth--;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
defaultReport(err, context);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// src/core/rendering/dispose.ts
|
|
179
|
+
var elementDisposers = /* @__PURE__ */ new WeakMap();
|
|
180
|
+
var MAX_DRAIN_TEARDOWNS = 1e4;
|
|
181
|
+
function reportDrainRunaway(label2, executed, remaining) {
|
|
182
|
+
if (typeof console === "undefined") return;
|
|
183
|
+
console.error(
|
|
184
|
+
`[SibuJS ${label2}] runaway cleanup: stopped after running ${executed} teardowns with ${remaining} still queued. Cleanup production did not stabilize before the teardown safety ceiling was reached \u2014 the remaining work was NOT run.`,
|
|
185
|
+
{ executed, remaining }
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
var _isDev2 = isDev();
|
|
189
|
+
var activeBindingCount = 0;
|
|
190
|
+
function registerDisposer(node, teardown) {
|
|
191
|
+
let disposers = elementDisposers.get(node);
|
|
192
|
+
if (!disposers) {
|
|
193
|
+
disposers = [];
|
|
194
|
+
elementDisposers.set(node, disposers);
|
|
195
|
+
}
|
|
196
|
+
disposers.push(teardown);
|
|
197
|
+
if (_isDev2) activeBindingCount++;
|
|
198
|
+
}
|
|
199
|
+
function dispose(node) {
|
|
200
|
+
const stack = [node];
|
|
201
|
+
const order = [];
|
|
202
|
+
while (stack.length > 0) {
|
|
203
|
+
const current = stack.pop();
|
|
204
|
+
order.push(current);
|
|
205
|
+
const children = Array.from(current.childNodes);
|
|
206
|
+
for (let i2 = 0; i2 < children.length; i2++) {
|
|
207
|
+
stack.push(children[i2]);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
for (let i2 = order.length - 1; i2 >= 0; i2--) {
|
|
211
|
+
const current = order[i2];
|
|
212
|
+
if (elementDisposers.has(current)) {
|
|
213
|
+
let executed = 0;
|
|
214
|
+
let runaway = false;
|
|
215
|
+
while (!runaway) {
|
|
216
|
+
const pending = elementDisposers.get(current);
|
|
217
|
+
if (!pending || pending.length === 0) break;
|
|
218
|
+
const snapshot = pending.slice();
|
|
219
|
+
elementDisposers.delete(current);
|
|
220
|
+
if (_isDev2) activeBindingCount -= snapshot.length;
|
|
221
|
+
for (let i3 = 0; i3 < snapshot.length; i3++) {
|
|
222
|
+
if (executed >= MAX_DRAIN_TEARDOWNS) {
|
|
223
|
+
const rest = snapshot.slice(i3);
|
|
224
|
+
const added = elementDisposers.get(current);
|
|
225
|
+
elementDisposers.set(current, added ? rest.concat(added) : rest);
|
|
226
|
+
if (_isDev2) activeBindingCount += rest.length;
|
|
227
|
+
reportDrainRunaway("dispose", executed, rest.length + (added?.length ?? 0));
|
|
228
|
+
runaway = true;
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
executed++;
|
|
232
|
+
try {
|
|
233
|
+
snapshot[i3]();
|
|
234
|
+
} catch (err) {
|
|
235
|
+
reportError(err, { phase: "cleanup", name: "disposer" });
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function replaceChildrenSafely(parent, ...next) {
|
|
243
|
+
for (let i2 = 0; i2 < next.length; i2++) {
|
|
244
|
+
const node = next[i2];
|
|
245
|
+
node.parentNode?.removeChild(node);
|
|
246
|
+
}
|
|
247
|
+
const current = Array.from(parent.childNodes);
|
|
248
|
+
for (let i2 = 0; i2 < current.length; i2++) {
|
|
249
|
+
dispose(current[i2]);
|
|
250
|
+
}
|
|
251
|
+
parent.replaceChildren(...next);
|
|
252
|
+
}
|
|
253
|
+
|
|
89
254
|
// src/core/ssr-context.ts
|
|
90
255
|
var SSR_KEY = /* @__PURE__ */ Symbol.for("sibujs.ssr.v1");
|
|
91
256
|
function detectSSRShared() {
|
|
@@ -96,20 +261,24 @@ function detectSSRShared() {
|
|
|
96
261
|
const getBuiltin = process.getBuiltinModule;
|
|
97
262
|
if (typeof getBuiltin === "function") {
|
|
98
263
|
mod = getBuiltin("node:async_hooks");
|
|
99
|
-
} else {
|
|
100
|
-
|
|
101
|
-
if (req) mod = req("node:async_hooks");
|
|
264
|
+
} else if (typeof require === "function") {
|
|
265
|
+
mod = require("async_hooks");
|
|
102
266
|
}
|
|
103
267
|
if (mod) detected = new mod.AsyncLocalStorage();
|
|
104
268
|
}
|
|
105
269
|
} catch {
|
|
106
270
|
detected = null;
|
|
107
271
|
}
|
|
108
|
-
return {
|
|
272
|
+
return {
|
|
273
|
+
als: detected,
|
|
274
|
+
fallbackStore: { ssr: false, suspenseIdCounter: 0, locale: void 0 },
|
|
275
|
+
fallbackDepth: 0
|
|
276
|
+
};
|
|
109
277
|
}
|
|
110
278
|
var _shared = globalThis[SSR_KEY] ?? (globalThis[SSR_KEY] = detectSSRShared());
|
|
111
279
|
var als = _shared.als;
|
|
112
280
|
var fallbackStore = _shared.fallbackStore;
|
|
281
|
+
_shared.fallbackDepth ?? (_shared.fallbackDepth = 0);
|
|
113
282
|
function getSSRStore() {
|
|
114
283
|
if (als) {
|
|
115
284
|
const s2 = als.getStore();
|
|
@@ -125,16 +294,21 @@ function isSSR() {
|
|
|
125
294
|
function stripControlChars(value) {
|
|
126
295
|
return value.replace(/[\x00-\x20\x7f-\x9f]+/g, "");
|
|
127
296
|
}
|
|
297
|
+
function canonicalAttrName(name) {
|
|
298
|
+
return name.replace(/[A-Z]/g, (ch) => String.fromCharCode(ch.charCodeAt(0) + 32));
|
|
299
|
+
}
|
|
128
300
|
function isEventHandlerAttr(name) {
|
|
129
301
|
if (name.length < 3) return false;
|
|
130
302
|
const lower = name.toLowerCase();
|
|
131
303
|
return lower[0] === "o" && lower[1] === "n" && lower.charCodeAt(2) >= 97 && lower.charCodeAt(2) <= 122;
|
|
132
304
|
}
|
|
133
305
|
var SAFE_URL_PROTOCOLS = ["http:", "https:", "mailto:", "tel:", "ftp:"];
|
|
306
|
+
var OUTER_TRIM = /^[\x00-\x20\x7f-\x9f]+|[\x00-\x20\x7f-\x9f]+$/g;
|
|
134
307
|
function sanitizeUrl(url) {
|
|
135
|
-
const
|
|
136
|
-
if (!
|
|
137
|
-
const
|
|
308
|
+
const probe = stripControlChars(url).trim();
|
|
309
|
+
if (!probe) return "";
|
|
310
|
+
const output2 = url.replace(OUTER_TRIM, "");
|
|
311
|
+
const lower = probe.toLowerCase();
|
|
138
312
|
let schemeEnd = -1;
|
|
139
313
|
for (let i2 = 0; i2 < lower.length; i2++) {
|
|
140
314
|
const ch = lower.charCodeAt(i2);
|
|
@@ -144,12 +318,13 @@ function sanitizeUrl(url) {
|
|
|
144
318
|
}
|
|
145
319
|
if (ch === 47 || ch === 63 || ch === 35) break;
|
|
146
320
|
}
|
|
147
|
-
if (schemeEnd === -1) return
|
|
321
|
+
if (schemeEnd === -1) return output2;
|
|
148
322
|
const scheme = lower.slice(0, schemeEnd + 1);
|
|
149
|
-
if (!/^[a-z][a-z0-9+.-]*:$/.test(scheme)) return
|
|
323
|
+
if (!/^[a-z][a-z0-9+.-]*:$/.test(scheme)) return output2;
|
|
150
324
|
if (SAFE_URL_PROTOCOLS.indexOf(scheme) === -1) return "";
|
|
151
|
-
return
|
|
325
|
+
return output2;
|
|
152
326
|
}
|
|
327
|
+
var SRCSET_DESCRIPTOR = /^\s+\d+(\.\d+)?[wx]$/;
|
|
153
328
|
function sanitizeSrcset(value) {
|
|
154
329
|
const parts = value.split(",");
|
|
155
330
|
const out = [];
|
|
@@ -158,6 +333,7 @@ function sanitizeSrcset(value) {
|
|
|
158
333
|
if (!part) continue;
|
|
159
334
|
const m = part.match(/^(\S+)(\s+.+)?$/);
|
|
160
335
|
if (!m) continue;
|
|
336
|
+
if (m[2] !== void 0 && !SRCSET_DESCRIPTOR.test(m[2])) continue;
|
|
161
337
|
const safe = sanitizeUrl(m[1]);
|
|
162
338
|
if (!safe) continue;
|
|
163
339
|
out.push(m[2] ? `${safe}${m[2]}` : safe);
|
|
@@ -165,23 +341,83 @@ function sanitizeSrcset(value) {
|
|
|
165
341
|
return out.join(", ");
|
|
166
342
|
}
|
|
167
343
|
var CSS_DANGER_GATE = /[(:@\\]/;
|
|
344
|
+
function isCssWhitespace(ch) {
|
|
345
|
+
return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f";
|
|
346
|
+
}
|
|
347
|
+
function isHexDigit(ch) {
|
|
348
|
+
return ch >= "0" && ch <= "9" || ch >= "a" && ch <= "f" || ch >= "A" && ch <= "F";
|
|
349
|
+
}
|
|
350
|
+
function decodeCssEscapes(value) {
|
|
351
|
+
let out = "";
|
|
352
|
+
for (let i2 = 0; i2 < value.length; i2++) {
|
|
353
|
+
const ch = value[i2];
|
|
354
|
+
if (ch !== "\\") {
|
|
355
|
+
out += ch;
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
if (i2 + 1 >= value.length) break;
|
|
359
|
+
const next = value[i2 + 1];
|
|
360
|
+
if (next === "\n" || next === "\f") {
|
|
361
|
+
i2 += 1;
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
if (next === "\r") {
|
|
365
|
+
i2 += value[i2 + 2] === "\n" ? 2 : 1;
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
if (isHexDigit(next)) {
|
|
369
|
+
let hex = "";
|
|
370
|
+
let j = i2 + 1;
|
|
371
|
+
while (j < value.length && hex.length < 6 && isHexDigit(value[j])) {
|
|
372
|
+
hex += value[j];
|
|
373
|
+
j++;
|
|
374
|
+
}
|
|
375
|
+
if (j < value.length && isCssWhitespace(value[j])) {
|
|
376
|
+
j += value[j] === "\r" && value[j + 1] === "\n" ? 2 : 1;
|
|
377
|
+
}
|
|
378
|
+
const code2 = Number.parseInt(hex, 16);
|
|
379
|
+
const invalid = code2 === 0 || code2 > 1114111 || code2 >= 55296 && code2 <= 57343;
|
|
380
|
+
out += invalid ? "\uFFFD" : String.fromCodePoint(code2);
|
|
381
|
+
i2 = j - 1;
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
out += next;
|
|
385
|
+
i2 += 1;
|
|
386
|
+
}
|
|
387
|
+
return out;
|
|
388
|
+
}
|
|
168
389
|
function sanitizeCSSValue(value) {
|
|
169
390
|
if (!CSS_DANGER_GATE.test(value)) return value;
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
if (!Number.isFinite(code2) || code2 < 0 || code2 > 1114111) return "";
|
|
173
|
-
try {
|
|
174
|
-
return String.fromCodePoint(code2);
|
|
175
|
-
} catch {
|
|
176
|
-
return "";
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
const lower = decoded.toLowerCase().replace(/\s+/g, "");
|
|
391
|
+
const normalized = value.includes("\\") ? decodeCssEscapes(value) : value;
|
|
392
|
+
const lower = normalized.toLowerCase().replace(/\s+/g, "");
|
|
180
393
|
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")) {
|
|
181
394
|
return "";
|
|
182
395
|
}
|
|
183
396
|
return value;
|
|
184
397
|
}
|
|
398
|
+
function sanitizeStyleAttribute(cssText) {
|
|
399
|
+
const input2 = String(cssText);
|
|
400
|
+
if (input2.trim() === "") return "";
|
|
401
|
+
if (typeof document === "undefined") {
|
|
402
|
+
return sanitizeCSSValue(input2) === "" ? "" : input2;
|
|
403
|
+
}
|
|
404
|
+
const probe = document.createElement("div");
|
|
405
|
+
try {
|
|
406
|
+
probe.style.cssText = input2;
|
|
407
|
+
} catch {
|
|
408
|
+
return "";
|
|
409
|
+
}
|
|
410
|
+
const declarations = [];
|
|
411
|
+
for (let i2 = 0; i2 < probe.style.length; i2++) {
|
|
412
|
+
const property = probe.style[i2];
|
|
413
|
+
const value = probe.style.getPropertyValue(property);
|
|
414
|
+
if (sanitizeCSSValue(value) === "") continue;
|
|
415
|
+
if (sanitizeCSSValue(`${property}:${value}`) === "") continue;
|
|
416
|
+
const priority = probe.style.getPropertyPriority(property);
|
|
417
|
+
declarations.push(`${property}: ${value}${priority ? ` !${priority}` : ""}`);
|
|
418
|
+
}
|
|
419
|
+
return declarations.join("; ");
|
|
420
|
+
}
|
|
185
421
|
var URL_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
186
422
|
"href",
|
|
187
423
|
"xlink:href",
|
|
@@ -194,40 +430,202 @@ var URL_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
|
194
430
|
"background",
|
|
195
431
|
"srcset",
|
|
196
432
|
"ping",
|
|
197
|
-
"data"
|
|
433
|
+
"data",
|
|
434
|
+
// `<html manifest>` is a URL sink too. It was carried only by the SSR
|
|
435
|
+
// serializers' private copy of this set; folding that copy into this one would
|
|
436
|
+
// otherwise have silently dropped its coverage.
|
|
437
|
+
"manifest"
|
|
198
438
|
]);
|
|
439
|
+
var HTML_CONTENT_ATTRIBUTES = /* @__PURE__ */ new Set(["srcdoc"]);
|
|
440
|
+
function isHtmlContentAttribute(name) {
|
|
441
|
+
return HTML_CONTENT_ATTRIBUTES.has(name.toLowerCase());
|
|
442
|
+
}
|
|
443
|
+
function isPolicyAttribute(name) {
|
|
444
|
+
const lower = name.toLowerCase();
|
|
445
|
+
return lower === "style" || URL_ATTRIBUTES.has(lower);
|
|
446
|
+
}
|
|
199
447
|
function sanitizeAttributeString(attr, value) {
|
|
200
448
|
const lower = attr.toLowerCase();
|
|
201
449
|
if (lower === "srcset") return sanitizeSrcset(value);
|
|
202
450
|
if (URL_ATTRIBUTES.has(lower)) return sanitizeUrl(value);
|
|
451
|
+
if (lower === "style") return sanitizeStyleAttribute(value);
|
|
203
452
|
return value;
|
|
204
453
|
}
|
|
205
454
|
|
|
455
|
+
// src/utils/metaRefresh.ts
|
|
456
|
+
function isAsciiWhitespace(ch) {
|
|
457
|
+
return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f";
|
|
458
|
+
}
|
|
459
|
+
function trimAsciiWhitespace(value) {
|
|
460
|
+
let start = 0;
|
|
461
|
+
let end = value.length;
|
|
462
|
+
while (start < end && isAsciiWhitespace(value[start])) start++;
|
|
463
|
+
while (end > start && isAsciiWhitespace(value[end - 1])) end--;
|
|
464
|
+
return value.slice(start, end);
|
|
465
|
+
}
|
|
466
|
+
var forbidden = (reason) => ({ kind: "forbidden", reason });
|
|
467
|
+
function parseMetaRefreshContent(content) {
|
|
468
|
+
const raw = trimAsciiWhitespace(stripControlChars(content));
|
|
469
|
+
if (raw === "") return forbidden("empty content");
|
|
470
|
+
let i2 = 0;
|
|
471
|
+
while (i2 < raw.length && raw[i2] >= "0" && raw[i2] <= "9") i2++;
|
|
472
|
+
if (i2 === 0) return forbidden("missing or non-numeric delay");
|
|
473
|
+
const delay = Number.parseInt(raw.slice(0, i2), 10);
|
|
474
|
+
if (!Number.isFinite(delay)) return forbidden("unparseable delay");
|
|
475
|
+
while (i2 < raw.length && isAsciiWhitespace(raw[i2])) i2++;
|
|
476
|
+
if (i2 === raw.length) return { kind: "delay-only", delay };
|
|
477
|
+
if (raw[i2] !== ";" && raw[i2] !== ",") return forbidden("unexpected text after delay");
|
|
478
|
+
i2++;
|
|
479
|
+
while (i2 < raw.length && isAsciiWhitespace(raw[i2])) i2++;
|
|
480
|
+
const keyStart = i2;
|
|
481
|
+
while (i2 < raw.length && !isAsciiWhitespace(raw[i2]) && raw[i2] !== "=") i2++;
|
|
482
|
+
const key = raw.slice(keyStart, i2).toLowerCase();
|
|
483
|
+
if (key !== "url") return forbidden(`unsupported refresh key ${JSON.stringify(key)}`);
|
|
484
|
+
while (i2 < raw.length && isAsciiWhitespace(raw[i2])) i2++;
|
|
485
|
+
if (raw[i2] !== "=") return forbidden("missing '=' after url");
|
|
486
|
+
i2++;
|
|
487
|
+
while (i2 < raw.length && isAsciiWhitespace(raw[i2])) i2++;
|
|
488
|
+
let destination;
|
|
489
|
+
const quote = raw[i2];
|
|
490
|
+
if (quote === '"' || quote === "'") {
|
|
491
|
+
const close = raw.indexOf(quote, i2 + 1);
|
|
492
|
+
if (close === -1) return forbidden("unterminated quoted destination");
|
|
493
|
+
destination = raw.slice(i2 + 1, close);
|
|
494
|
+
const rest = trimAsciiWhitespace(raw.slice(close + 1));
|
|
495
|
+
if (rest !== "") return forbidden("trailing text after quoted destination");
|
|
496
|
+
} else {
|
|
497
|
+
destination = trimAsciiWhitespace(raw.slice(i2));
|
|
498
|
+
if (/[;,]/.test(destination)) return forbidden("multiple refresh assignments");
|
|
499
|
+
}
|
|
500
|
+
if (destination === "") return forbidden("empty destination");
|
|
501
|
+
const sanitizedDestination = sanitizeUrl(destination);
|
|
502
|
+
if (sanitizedDestination === "") return forbidden("destination uses a disallowed protocol");
|
|
503
|
+
return { kind: "allowed", delay, destination, sanitizedDestination };
|
|
504
|
+
}
|
|
505
|
+
function resolveMetaRefreshPolicy(attributes) {
|
|
506
|
+
let httpEquiv;
|
|
507
|
+
let content;
|
|
508
|
+
let duplicateName;
|
|
509
|
+
const seen = /* @__PURE__ */ new Set();
|
|
510
|
+
for (const [key, value] of attributes) {
|
|
511
|
+
const canonical = canonicalAttrName(key);
|
|
512
|
+
if (seen.has(canonical)) duplicateName = canonical;
|
|
513
|
+
seen.add(canonical);
|
|
514
|
+
if (canonical === "http-equiv") httpEquiv = value;
|
|
515
|
+
else if (canonical === "content") content = value;
|
|
516
|
+
}
|
|
517
|
+
if (duplicateName !== void 0) {
|
|
518
|
+
return forbidden(`duplicate case-insensitive attribute ${JSON.stringify(duplicateName)}`);
|
|
519
|
+
}
|
|
520
|
+
if (typeof httpEquiv !== "string") return { kind: "not-refresh" };
|
|
521
|
+
if (trimAsciiWhitespace(stripControlChars(httpEquiv)).toLowerCase() !== "refresh") {
|
|
522
|
+
return { kind: "not-refresh" };
|
|
523
|
+
}
|
|
524
|
+
if (typeof content !== "string") return { kind: "not-refresh" };
|
|
525
|
+
return parseMetaRefreshContent(content);
|
|
526
|
+
}
|
|
527
|
+
function normalizeMetaAttributes(props) {
|
|
528
|
+
const out = /* @__PURE__ */ new Map();
|
|
529
|
+
const seen = /* @__PURE__ */ new Set();
|
|
530
|
+
for (const key of Object.keys(props)) {
|
|
531
|
+
const canonical = canonicalAttrName(key);
|
|
532
|
+
if (seen.has(canonical)) return null;
|
|
533
|
+
seen.add(canonical);
|
|
534
|
+
out.set(key, props[key]);
|
|
535
|
+
}
|
|
536
|
+
return out;
|
|
537
|
+
}
|
|
538
|
+
function isForbiddenMetaEntry(props) {
|
|
539
|
+
const normalized = normalizeMetaAttributes(props);
|
|
540
|
+
if (normalized === null) return true;
|
|
541
|
+
return resolveMetaRefreshPolicy(normalized).kind === "forbidden";
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// src/utils/headEntry.ts
|
|
545
|
+
var SAFE_HEAD_ATTR_NAME = /^[A-Za-z_:][-A-Za-z0-9_.:]*$/;
|
|
546
|
+
function isEmittableHeadAttrName(name) {
|
|
547
|
+
if (!SAFE_HEAD_ATTR_NAME.test(name)) return false;
|
|
548
|
+
const canonical = canonicalAttrName(name);
|
|
549
|
+
if (isEventHandlerAttr(canonical)) return false;
|
|
550
|
+
if (isHtmlContentAttribute(canonical)) return false;
|
|
551
|
+
return true;
|
|
552
|
+
}
|
|
553
|
+
function sanitizeHeadAttrValue(canonical, value) {
|
|
554
|
+
if (!isPolicyAttribute(canonical)) return value;
|
|
555
|
+
const safe = sanitizeAttributeString(canonical, value);
|
|
556
|
+
return safe === "" ? null : safe;
|
|
557
|
+
}
|
|
558
|
+
function findDuplicateAttributeName(names) {
|
|
559
|
+
const seen = /* @__PURE__ */ new Set();
|
|
560
|
+
for (const name of names) {
|
|
561
|
+
const canonical = canonicalAttrName(name);
|
|
562
|
+
if (seen.has(canonical)) return canonical;
|
|
563
|
+
seen.add(canonical);
|
|
564
|
+
}
|
|
565
|
+
return null;
|
|
566
|
+
}
|
|
567
|
+
function planEntry(props, resolver, applyRefreshPolicy) {
|
|
568
|
+
const rawNames = Object.keys(props);
|
|
569
|
+
const duplicate = findDuplicateAttributeName(rawNames);
|
|
570
|
+
if (duplicate !== null) {
|
|
571
|
+
return { kind: "drop", reason: `duplicate case-insensitive attribute ${JSON.stringify(duplicate)}` };
|
|
572
|
+
}
|
|
573
|
+
const attributes = /* @__PURE__ */ new Map();
|
|
574
|
+
let reactive = false;
|
|
575
|
+
for (const name of rawNames) {
|
|
576
|
+
if (!Object.hasOwn(props, name)) continue;
|
|
577
|
+
if (!isEmittableHeadAttrName(name)) continue;
|
|
578
|
+
const authored = props[name];
|
|
579
|
+
if (resolver.isReactiveValue?.(authored)) reactive = true;
|
|
580
|
+
const canonical = canonicalAttrName(name);
|
|
581
|
+
const sanitized = sanitizeHeadAttrValue(canonical, resolver.resolveValue(name, authored));
|
|
582
|
+
if (sanitized === null) continue;
|
|
583
|
+
attributes.set(canonical, sanitized);
|
|
584
|
+
}
|
|
585
|
+
if (attributes.size === 0) return { kind: "drop", reason: "no effective attributes remain" };
|
|
586
|
+
const refresh = applyRefreshPolicy ? resolveMetaRefreshPolicy(attributes) : { kind: "not-refresh" };
|
|
587
|
+
if (refresh.kind === "forbidden") return { kind: "drop", reason: refresh.reason };
|
|
588
|
+
if (reactive && (refresh.kind === "allowed" || refresh.kind === "delay-only")) {
|
|
589
|
+
return { kind: "drop", reason: "a reactive meta entry may not publish a native refresh directive" };
|
|
590
|
+
}
|
|
591
|
+
return { kind: "publish", attributes, refresh };
|
|
592
|
+
}
|
|
593
|
+
function planMetaEntry(props, resolver) {
|
|
594
|
+
return planEntry(props, resolver, true);
|
|
595
|
+
}
|
|
596
|
+
function planHeadElementEntry(props, resolver) {
|
|
597
|
+
return planEntry(props, resolver, false);
|
|
598
|
+
}
|
|
599
|
+
var STATIC_VALUE_RESOLVER = {
|
|
600
|
+
resolveValue: (_name, value) => String(value)
|
|
601
|
+
};
|
|
602
|
+
function escapeHeadAttrValue(value) {
|
|
603
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
604
|
+
}
|
|
605
|
+
function serializeHeadEntry(tag, attrs) {
|
|
606
|
+
const plan = tag === "meta" ? planMetaEntry(attrs, STATIC_VALUE_RESOLVER) : planHeadElementEntry(attrs, STATIC_VALUE_RESOLVER);
|
|
607
|
+
if (plan.kind === "drop") return "";
|
|
608
|
+
const pairs = Array.from(plan.attributes, ([name, value]) => `${name}="${escapeHeadAttrValue(value)}"`).join(" ");
|
|
609
|
+
return `<${tag} ${pairs} />`;
|
|
610
|
+
}
|
|
611
|
+
|
|
206
612
|
// src/platform/ssr.ts
|
|
207
|
-
function
|
|
208
|
-
return
|
|
613
|
+
function isPolicyAttr(lowerName) {
|
|
614
|
+
return isPolicyAttribute(lowerName);
|
|
209
615
|
}
|
|
210
|
-
|
|
616
|
+
function isForbiddenSsrAttr(lowerName) {
|
|
617
|
+
return isHtmlContentAttribute(lowerName);
|
|
618
|
+
}
|
|
619
|
+
function sanitizeSsrAttributeValue(lowerName, value) {
|
|
620
|
+
return sanitizeAttributeString(lowerName, value);
|
|
621
|
+
}
|
|
622
|
+
var _isDev3 = isDev();
|
|
211
623
|
var SAFE_ATTR_NAME = /^[A-Za-z_:][-A-Za-z0-9_.:]*$/;
|
|
212
624
|
function isSafeAttrName(name) {
|
|
213
625
|
return SAFE_ATTR_NAME.test(name);
|
|
214
626
|
}
|
|
215
|
-
var URL_ATTRS = /* @__PURE__ */ new Set([
|
|
216
|
-
"href",
|
|
217
|
-
"src",
|
|
218
|
-
"action",
|
|
219
|
-
"formaction",
|
|
220
|
-
"cite",
|
|
221
|
-
"poster",
|
|
222
|
-
"background",
|
|
223
|
-
"srcset",
|
|
224
|
-
"ping",
|
|
225
|
-
"manifest",
|
|
226
|
-
"data",
|
|
227
|
-
"xlink:href"
|
|
228
|
-
]);
|
|
229
627
|
function ssrErrorComment(err) {
|
|
230
|
-
if (
|
|
628
|
+
if (_isDev3) {
|
|
231
629
|
const msg = escapeHtml(err instanceof Error ? err.message : String(err));
|
|
232
630
|
return `<!--SSR error: ${safeCommentText(msg)}-->`;
|
|
233
631
|
}
|
|
@@ -273,20 +671,21 @@ function renderToString(element) {
|
|
|
273
671
|
}
|
|
274
672
|
const tag = element.tagName.toLowerCase();
|
|
275
673
|
if (tag === "script" || tag === "style") {
|
|
276
|
-
return
|
|
674
|
+
return _isDev3 ? `<!--ssr:${tag}-stripped-->` : "";
|
|
277
675
|
}
|
|
278
676
|
if (!/^[a-z][a-z0-9-]*$/i.test(tag)) {
|
|
279
|
-
return
|
|
677
|
+
return _isDev3 ? "<!--ssr:invalid-tag-->" : "";
|
|
280
678
|
}
|
|
281
679
|
let html2 = `<${tag}`;
|
|
282
680
|
for (const attr of Array.from(element.attributes)) {
|
|
283
681
|
const rawName = attr.name;
|
|
284
682
|
if (!isSafeAttrName(rawName)) continue;
|
|
285
683
|
if (isEventHandlerAttr(rawName)) continue;
|
|
684
|
+
if (isForbiddenSsrAttr(rawName)) continue;
|
|
286
685
|
const lowerName = rawName.toLowerCase();
|
|
287
686
|
let value = attr.value;
|
|
288
|
-
if (
|
|
289
|
-
value =
|
|
687
|
+
if (isPolicyAttr(lowerName)) {
|
|
688
|
+
value = sanitizeSsrAttributeValue(lowerName, value);
|
|
290
689
|
if (!value) continue;
|
|
291
690
|
}
|
|
292
691
|
html2 += ` ${rawName}="${escapeAttr(value)}"`;
|
|
@@ -317,7 +716,7 @@ function hydrate(component, container, options = {}) {
|
|
|
317
716
|
const first = mismatches[0];
|
|
318
717
|
if (options.onMismatch) {
|
|
319
718
|
options.onMismatch(first);
|
|
320
|
-
} else if (
|
|
719
|
+
} else if (_isDev3) {
|
|
321
720
|
console.warn(
|
|
322
721
|
`[SibuJS hydration] ${first.message}
|
|
323
722
|
at ${first.path}
|
|
@@ -327,9 +726,20 @@ function hydrate(component, container, options = {}) {
|
|
|
327
726
|
}
|
|
328
727
|
}
|
|
329
728
|
}
|
|
330
|
-
container
|
|
729
|
+
replaceChildrenSafely(container, clientTree);
|
|
331
730
|
container.setAttribute("data-sibu-hydrated", "true");
|
|
332
731
|
}
|
|
732
|
+
function directTextOf(node) {
|
|
733
|
+
let out = "";
|
|
734
|
+
for (const child of Array.from(node.childNodes)) {
|
|
735
|
+
if (child.nodeType === 3) out += child.nodeValue ?? "";
|
|
736
|
+
}
|
|
737
|
+
return out.replace(/\s+/g, " ").trim();
|
|
738
|
+
}
|
|
739
|
+
function disposeAndReplace(outgoing, next) {
|
|
740
|
+
dispose(outgoing);
|
|
741
|
+
outgoing.replaceWith(next);
|
|
742
|
+
}
|
|
333
743
|
function collectMismatches(serverNode, clientNode, path2, out, max = 5) {
|
|
334
744
|
if (out.length >= max) return;
|
|
335
745
|
const nodePath = path2 || clientNode?.tagName?.toLowerCase() || "(root)";
|
|
@@ -405,6 +815,18 @@ function collectMismatches(serverNode, clientNode, path2, out, max = 5) {
|
|
|
405
815
|
});
|
|
406
816
|
}
|
|
407
817
|
}
|
|
818
|
+
const serverText = directTextOf(serverNode);
|
|
819
|
+
const clientText = directTextOf(clientNode);
|
|
820
|
+
if (serverText !== clientText) {
|
|
821
|
+
out.push({
|
|
822
|
+
kind: "text",
|
|
823
|
+
path: nodePath,
|
|
824
|
+
serverValue: serverText,
|
|
825
|
+
clientValue: clientText,
|
|
826
|
+
message: "Text content differs between server and client."
|
|
827
|
+
});
|
|
828
|
+
if (out.length >= max) return;
|
|
829
|
+
}
|
|
408
830
|
const serverChildren = Array.from(serverNode.children);
|
|
409
831
|
const clientChildren = Array.from(clientNode.children);
|
|
410
832
|
const max2 = Math.max(serverChildren.length, clientChildren.length);
|
|
@@ -424,10 +846,11 @@ function buildAttrString(attrs, { allowEventHandlers = false } = {}) {
|
|
|
424
846
|
if (!Object.hasOwn(attrs, rawKey)) continue;
|
|
425
847
|
if (!isSafeAttrName(rawKey)) continue;
|
|
426
848
|
if (!allowEventHandlers && isEventHandlerAttr(rawKey)) continue;
|
|
849
|
+
if (isForbiddenSsrAttr(rawKey)) continue;
|
|
427
850
|
const lowerKey = rawKey.toLowerCase();
|
|
428
851
|
let value = String(attrs[rawKey]);
|
|
429
|
-
if (
|
|
430
|
-
value =
|
|
852
|
+
if (isPolicyAttr(lowerKey)) {
|
|
853
|
+
value = sanitizeSsrAttributeValue(lowerKey, value);
|
|
431
854
|
if (!value) continue;
|
|
432
855
|
}
|
|
433
856
|
out.push(`${rawKey}="${escapeAttr(value)}"`);
|
|
@@ -435,18 +858,7 @@ function buildAttrString(attrs, { allowEventHandlers = false } = {}) {
|
|
|
435
858
|
return out.join(" ");
|
|
436
859
|
}
|
|
437
860
|
function isDangerousMetaRefresh(metaProps) {
|
|
438
|
-
|
|
439
|
-
let content;
|
|
440
|
-
for (const k in metaProps) {
|
|
441
|
-
const lk = k.toLowerCase();
|
|
442
|
-
if (lk === "http-equiv") httpEquiv = metaProps[k];
|
|
443
|
-
else if (lk === "content") content = metaProps[k];
|
|
444
|
-
}
|
|
445
|
-
if (typeof httpEquiv !== "string") return false;
|
|
446
|
-
if (httpEquiv.toLowerCase() !== "refresh") return false;
|
|
447
|
-
if (typeof content !== "string") return false;
|
|
448
|
-
const normalized = stripControlChars(content).toLowerCase();
|
|
449
|
-
return normalized.includes("url=javascript:") || normalized.includes("url=data:") || normalized.includes("url=vbscript:") || normalized.includes("url=blob:");
|
|
861
|
+
return isForbiddenMetaEntry(metaProps);
|
|
450
862
|
}
|
|
451
863
|
function renderToDocument(component, options = {}) {
|
|
452
864
|
let content;
|
|
@@ -455,15 +867,8 @@ function renderToDocument(component, options = {}) {
|
|
|
455
867
|
} catch (err) {
|
|
456
868
|
content = ssrErrorComment(err);
|
|
457
869
|
}
|
|
458
|
-
const metaTags = (options.meta || []).map((attrs) =>
|
|
459
|
-
|
|
460
|
-
const pairs = buildAttrString(attrs);
|
|
461
|
-
return pairs ? `<meta ${pairs} />` : "";
|
|
462
|
-
}).filter(Boolean).join("\n ");
|
|
463
|
-
const linkTags = (options.links || []).map((attrs) => {
|
|
464
|
-
const pairs = buildAttrString(attrs);
|
|
465
|
-
return pairs ? `<link ${pairs} />` : "";
|
|
466
|
-
}).filter(Boolean).join("\n ");
|
|
870
|
+
const metaTags = (options.meta || []).map((attrs) => serializeHeadEntry("meta", attrs)).filter(Boolean).join("\n ");
|
|
871
|
+
const linkTags = (options.links || []).map((attrs) => serializeHeadEntry("link", attrs)).filter(Boolean).join("\n ");
|
|
467
872
|
const scriptTags = (options.scripts || []).map((src) => {
|
|
468
873
|
const safe = sanitizeUrl(String(src));
|
|
469
874
|
if (!safe) return "";
|
|
@@ -512,11 +917,11 @@ async function* renderToStream(element) {
|
|
|
512
917
|
}
|
|
513
918
|
const tag = element.tagName.toLowerCase();
|
|
514
919
|
if (tag === "script" || tag === "style") {
|
|
515
|
-
if (
|
|
920
|
+
if (_isDev3) yield `<!--ssr:${tag}-stripped-->`;
|
|
516
921
|
return;
|
|
517
922
|
}
|
|
518
923
|
if (!/^[a-z][a-z0-9-]*$/i.test(tag)) {
|
|
519
|
-
if (
|
|
924
|
+
if (_isDev3) yield "<!--ssr:invalid-tag-->";
|
|
520
925
|
return;
|
|
521
926
|
}
|
|
522
927
|
let openTag = `<${tag}`;
|
|
@@ -524,14 +929,18 @@ async function* renderToStream(element) {
|
|
|
524
929
|
const rawName = attr.name;
|
|
525
930
|
if (!isSafeAttrName(rawName)) continue;
|
|
526
931
|
if (isEventHandlerAttr(rawName)) continue;
|
|
932
|
+
if (isForbiddenSsrAttr(rawName)) continue;
|
|
527
933
|
const lowerName = rawName.toLowerCase();
|
|
528
934
|
let value = attr.value;
|
|
529
|
-
if (
|
|
530
|
-
value =
|
|
935
|
+
if (isPolicyAttr(lowerName)) {
|
|
936
|
+
value = sanitizeSsrAttributeValue(lowerName, value);
|
|
531
937
|
if (!value) continue;
|
|
532
938
|
}
|
|
533
939
|
openTag += ` ${rawName}="${escapeAttr(value)}"`;
|
|
534
940
|
}
|
|
941
|
+
if (element.dataset && !element.dataset.sibuHydrate) {
|
|
942
|
+
openTag += ` data-sibu-ssr="true"`;
|
|
943
|
+
}
|
|
535
944
|
if (VOID_ELEMENTS.has(tag)) {
|
|
536
945
|
yield `${openTag} />`;
|
|
537
946
|
return;
|
|
@@ -578,9 +987,13 @@ function island(id, component) {
|
|
|
578
987
|
el.setAttribute("data-sibu-island", id);
|
|
579
988
|
return el;
|
|
580
989
|
}
|
|
990
|
+
function isAlreadyHydrated(element) {
|
|
991
|
+
return element.hasAttribute("data-sibu-hydrated");
|
|
992
|
+
}
|
|
581
993
|
function hydrateIslands(container, islands) {
|
|
582
994
|
const markers = container.querySelectorAll("[data-sibu-island]");
|
|
583
995
|
for (const marker2 of Array.from(markers)) {
|
|
996
|
+
if (isAlreadyHydrated(marker2)) continue;
|
|
584
997
|
const id = marker2.getAttribute("data-sibu-island") ?? "";
|
|
585
998
|
if (!Object.hasOwn(islands, id)) continue;
|
|
586
999
|
const factory = islands[id];
|
|
@@ -588,7 +1001,7 @@ function hydrateIslands(container, islands) {
|
|
|
588
1001
|
const clientTree = factory();
|
|
589
1002
|
clientTree.setAttribute("data-sibu-island", id);
|
|
590
1003
|
clientTree.setAttribute("data-sibu-hydrated", "true");
|
|
591
|
-
marker2
|
|
1004
|
+
disposeAndReplace(marker2, clientTree);
|
|
592
1005
|
}
|
|
593
1006
|
container.setAttribute("data-sibu-hydrated", "partial");
|
|
594
1007
|
}
|
|
@@ -596,6 +1009,7 @@ function hydrateProgressively(container, islands, options) {
|
|
|
596
1009
|
const markers = container.querySelectorAll("[data-sibu-island]");
|
|
597
1010
|
const cleanups = [];
|
|
598
1011
|
for (const marker2 of Array.from(markers)) {
|
|
1012
|
+
if (isAlreadyHydrated(marker2)) continue;
|
|
599
1013
|
const id = marker2.getAttribute("data-sibu-island") ?? "";
|
|
600
1014
|
if (!Object.hasOwn(islands, id)) continue;
|
|
601
1015
|
const factory = islands[id];
|
|
@@ -605,10 +1019,11 @@ function hydrateProgressively(container, islands, options) {
|
|
|
605
1019
|
for (const entry of entries) {
|
|
606
1020
|
if (entry.isIntersecting) {
|
|
607
1021
|
observer.disconnect();
|
|
1022
|
+
if (isAlreadyHydrated(marker2) || marker2.parentNode === null) break;
|
|
608
1023
|
const clientTree = factory();
|
|
609
1024
|
clientTree.setAttribute("data-sibu-island", id);
|
|
610
1025
|
clientTree.setAttribute("data-sibu-hydrated", "true");
|
|
611
|
-
marker2
|
|
1026
|
+
disposeAndReplace(marker2, clientTree);
|
|
612
1027
|
break;
|
|
613
1028
|
}
|
|
614
1029
|
}
|
|
@@ -649,7 +1064,7 @@ function ssrSuspense(props) {
|
|
|
649
1064
|
},
|
|
650
1065
|
(err) => {
|
|
651
1066
|
if (timer) clearTimeout(timer);
|
|
652
|
-
if (
|
|
1067
|
+
if (_isDev3) console.warn("[SibuJS SSR] ssrSuspense rejected:", err);
|
|
653
1068
|
return { id, html: fallbackHtml };
|
|
654
1069
|
}
|
|
655
1070
|
);
|
|
@@ -664,7 +1079,7 @@ function suspenseSwapScript(id, nonce) {
|
|
|
664
1079
|
);
|
|
665
1080
|
}
|
|
666
1081
|
const nonceAttr = nonce ? ` nonce="${escapeAttr(nonce)}"` : "";
|
|
667
|
-
return `<script${nonceAttr}>(function(){var t=document.getElementById("sibu-resolved-${id}");var f=document.querySelector('[data-sibu-suspense-id="${id}"]');if(t&&f){while(t.firstChild)f.appendChild(t.firstChild);t.remove();f.removeAttribute("data-sibu-suspense-id");}})()</script>`;
|
|
1082
|
+
return `<script${nonceAttr}>(function(){var t=document.getElementById("sibu-resolved-${id}");var f=document.querySelector('[data-sibu-suspense-id="${id}"]');if(t&&f){while(f.firstChild)f.removeChild(f.firstChild);while(t.firstChild)f.appendChild(t.firstChild);t.remove();f.removeAttribute("data-sibu-suspense-id");}})()</script>`;
|
|
668
1083
|
}
|
|
669
1084
|
async function* renderToSuspenseStream(element, pendingBoundaries = [], options) {
|
|
670
1085
|
yield* renderToStream(element);
|
|
@@ -682,8 +1097,8 @@ function escapeScriptJson(json) {
|
|
|
682
1097
|
return json.replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
683
1098
|
}
|
|
684
1099
|
var DEFAULT_MAX_SSR_BYTES = 1024 * 1024;
|
|
685
|
-
function serializeState(
|
|
686
|
-
const rawJson = JSON.stringify(
|
|
1100
|
+
function serializeState(state2, nonce, options) {
|
|
1101
|
+
const rawJson = JSON.stringify(state2);
|
|
687
1102
|
const maxBytes = options?.maxBytes ?? DEFAULT_MAX_SSR_BYTES;
|
|
688
1103
|
const byteLen = typeof TextEncoder !== "undefined" ? new TextEncoder().encode(rawJson).byteLength : Buffer.byteLength(rawJson, "utf8");
|
|
689
1104
|
if (byteLen > maxBytes) {
|
|
@@ -695,7 +1110,7 @@ function serializeState(state, nonce, options) {
|
|
|
695
1110
|
}
|
|
696
1111
|
function deserializeState(validate) {
|
|
697
1112
|
if (typeof window === "undefined") return void 0;
|
|
698
|
-
if (
|
|
1113
|
+
if (_isDev3 && !validate) {
|
|
699
1114
|
console.warn(
|
|
700
1115
|
"[SibuJS SSR] deserializeState() called without a validate guard \u2014 tampered SSR payloads will not be detected."
|
|
701
1116
|
);
|
|
@@ -713,69 +1128,7 @@ function escapeAttr(str) {
|
|
|
713
1128
|
return str.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
714
1129
|
}
|
|
715
1130
|
|
|
716
|
-
// src/core/rendering/dispose.ts
|
|
717
|
-
var elementDisposers = /* @__PURE__ */ new WeakMap();
|
|
718
|
-
var _isDev3 = isDev();
|
|
719
|
-
var activeBindingCount = 0;
|
|
720
|
-
function registerDisposer(node, teardown) {
|
|
721
|
-
let disposers = elementDisposers.get(node);
|
|
722
|
-
if (!disposers) {
|
|
723
|
-
disposers = [];
|
|
724
|
-
elementDisposers.set(node, disposers);
|
|
725
|
-
}
|
|
726
|
-
disposers.push(teardown);
|
|
727
|
-
if (_isDev3) activeBindingCount++;
|
|
728
|
-
}
|
|
729
|
-
function dispose(node) {
|
|
730
|
-
const stack = [node];
|
|
731
|
-
const order = [];
|
|
732
|
-
while (stack.length > 0) {
|
|
733
|
-
const current = stack.pop();
|
|
734
|
-
order.push(current);
|
|
735
|
-
const children = Array.from(current.childNodes);
|
|
736
|
-
for (let i2 = 0; i2 < children.length; i2++) {
|
|
737
|
-
stack.push(children[i2]);
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
for (let i2 = order.length - 1; i2 >= 0; i2--) {
|
|
741
|
-
const current = order[i2];
|
|
742
|
-
const disposers = elementDisposers.get(current);
|
|
743
|
-
if (disposers) {
|
|
744
|
-
const snapshot = disposers.slice();
|
|
745
|
-
elementDisposers.delete(current);
|
|
746
|
-
if (_isDev3) activeBindingCount -= snapshot.length;
|
|
747
|
-
for (const d of snapshot) {
|
|
748
|
-
try {
|
|
749
|
-
d();
|
|
750
|
-
} catch (err) {
|
|
751
|
-
if (_isDev3 && typeof console !== "undefined") {
|
|
752
|
-
console.warn("[SibuJS] Disposer threw during cleanup:", err);
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
let extraPasses = 0;
|
|
757
|
-
while (extraPasses++ < 8) {
|
|
758
|
-
const added = elementDisposers.get(current);
|
|
759
|
-
if (!added || added.length === 0) break;
|
|
760
|
-
const moreSnapshot = added.slice();
|
|
761
|
-
elementDisposers.delete(current);
|
|
762
|
-
if (_isDev3) activeBindingCount -= moreSnapshot.length;
|
|
763
|
-
for (const d of moreSnapshot) {
|
|
764
|
-
try {
|
|
765
|
-
d();
|
|
766
|
-
} catch (err) {
|
|
767
|
-
if (_isDev3 && typeof console !== "undefined") {
|
|
768
|
-
console.warn("[SibuJS] Disposer threw during cleanup:", err);
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
|
|
777
1131
|
// src/reactivity/track-core.ts
|
|
778
|
-
var _isDev4 = isDev();
|
|
779
1132
|
var POOL_MAX = 4096;
|
|
780
1133
|
var nodePool = [];
|
|
781
1134
|
function createNode() {
|
|
@@ -783,6 +1136,7 @@ function createNode() {
|
|
|
783
1136
|
sig: null,
|
|
784
1137
|
sub: null,
|
|
785
1138
|
epoch: 0,
|
|
1139
|
+
depVersion: 0,
|
|
786
1140
|
sigPrev: null,
|
|
787
1141
|
sigNext: null,
|
|
788
1142
|
subPrev: null,
|
|
@@ -867,8 +1221,27 @@ function safeInvoke(sub2) {
|
|
|
867
1221
|
try {
|
|
868
1222
|
sub2();
|
|
869
1223
|
} catch (err) {
|
|
870
|
-
|
|
1224
|
+
const s2 = sub2;
|
|
1225
|
+
reportError(err, { phase: s2._errorPhase ?? "effect", name: s2.__name, node: s2._errorNode });
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
function depsChanged(sub2) {
|
|
1229
|
+
let node = sub2.depsHead ?? null;
|
|
1230
|
+
if (node === null) return true;
|
|
1231
|
+
while (node !== null) {
|
|
1232
|
+
const sig = node.sig;
|
|
1233
|
+
if (sig === null) return true;
|
|
1234
|
+
if (sig._d === true && sig._validate !== void 0) {
|
|
1235
|
+
try {
|
|
1236
|
+
sig._validate();
|
|
1237
|
+
} catch {
|
|
1238
|
+
return true;
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
if (node.depVersion !== sig.__v) return true;
|
|
1242
|
+
node = node.subNext;
|
|
871
1243
|
}
|
|
1244
|
+
return false;
|
|
872
1245
|
}
|
|
873
1246
|
var suspendDepth = 0;
|
|
874
1247
|
var trackingSuspended = false;
|
|
@@ -950,7 +1323,7 @@ function track(effectFn, subscriber) {
|
|
|
950
1323
|
const sub2 = subscriber;
|
|
951
1324
|
return sub2._dispose ?? (sub2._dispose = () => cleanup(subscriber));
|
|
952
1325
|
}
|
|
953
|
-
function reactiveBinding(commit) {
|
|
1326
|
+
function reactiveBinding(commit, ownerNode) {
|
|
954
1327
|
const run = () => {
|
|
955
1328
|
const s2 = subscriber;
|
|
956
1329
|
if (s2._disposed || s2._reentrant) return;
|
|
@@ -966,13 +1339,17 @@ function reactiveBinding(commit) {
|
|
|
966
1339
|
subscriber.depsTail = null;
|
|
967
1340
|
subscriber._epoch = 0;
|
|
968
1341
|
subscriber._structDirty = false;
|
|
1342
|
+
subscriber._hasComputedDep = false;
|
|
969
1343
|
subscriber._runEpoch = 0;
|
|
970
1344
|
subscriber._runs = 0;
|
|
971
1345
|
subscriber._reentrant = false;
|
|
972
1346
|
subscriber._disposed = false;
|
|
1347
|
+
subscriber._errorPhase = "binding";
|
|
1348
|
+
subscriber._errorNode = ownerNode;
|
|
973
1349
|
run();
|
|
974
1350
|
return subscriber._dispose ?? (subscriber._dispose = () => {
|
|
975
1351
|
subscriber._disposed = true;
|
|
1352
|
+
subscriber._errorNode = void 0;
|
|
976
1353
|
cleanup(subscriber);
|
|
977
1354
|
});
|
|
978
1355
|
}
|
|
@@ -984,9 +1361,12 @@ function recordDependency(signal2) {
|
|
|
984
1361
|
const active = sig.__activeNode ?? null;
|
|
985
1362
|
if (active !== null && active.sub === sub2) {
|
|
986
1363
|
active.epoch = epoch;
|
|
1364
|
+
active.depVersion = sig.__v ?? Number.NaN;
|
|
987
1365
|
return;
|
|
988
1366
|
}
|
|
1367
|
+
if (sig._validate !== void 0) sub2._hasComputedDep = true;
|
|
989
1368
|
const node = allocNode(signal2, sub2, epoch);
|
|
1369
|
+
node.depVersion = sig.__v ?? Number.NaN;
|
|
990
1370
|
node.prevActive = active;
|
|
991
1371
|
sig.__activeNode = node;
|
|
992
1372
|
linkSub(sub2, node);
|
|
@@ -1005,9 +1385,10 @@ function cleanup(subscriber) {
|
|
|
1005
1385
|
node = next;
|
|
1006
1386
|
}
|
|
1007
1387
|
}
|
|
1008
|
-
var maxSubscriberRepeats =
|
|
1388
|
+
var maxSubscriberRepeats = 1e3;
|
|
1009
1389
|
var maxDrainIterations = 1e6;
|
|
1010
1390
|
var drainEpoch = 0;
|
|
1391
|
+
var quarantined = /* @__PURE__ */ new Set();
|
|
1011
1392
|
function setMaxSubscriberRepeats(n) {
|
|
1012
1393
|
const prev = maxSubscriberRepeats;
|
|
1013
1394
|
if (Number.isFinite(n) && n > 0) maxSubscriberRepeats = Math.floor(n);
|
|
@@ -1029,19 +1410,21 @@ function tickRepeat(sub2) {
|
|
|
1029
1410
|
return s2._runs > maxSubscriberRepeats;
|
|
1030
1411
|
}
|
|
1031
1412
|
function cycleError(sub2) {
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
`
|
|
1036
|
-
)
|
|
1037
|
-
|
|
1413
|
+
const name = sub2.__name ?? "<unnamed>";
|
|
1414
|
+
reportError(
|
|
1415
|
+
new Error(
|
|
1416
|
+
`subscriber "${name}" fired more than ${maxSubscriberRepeats} times \u2014 likely a write-reads-self cycle between effects/signals. This subscriber is quarantined for the rest of this update; other pending work still runs.`
|
|
1417
|
+
),
|
|
1418
|
+
{ phase: "scheduler", name }
|
|
1419
|
+
);
|
|
1038
1420
|
}
|
|
1039
1421
|
function absoluteDrainError() {
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
`
|
|
1043
|
-
)
|
|
1044
|
-
|
|
1422
|
+
reportError(
|
|
1423
|
+
new Error(
|
|
1424
|
+
`Notification drain exceeded ${maxDrainIterations} iterations \u2014 absolute safety net tripped. Breaking to prevent infinite loop.`
|
|
1425
|
+
),
|
|
1426
|
+
{ phase: "scheduler" }
|
|
1427
|
+
);
|
|
1045
1428
|
}
|
|
1046
1429
|
function drainQueue() {
|
|
1047
1430
|
let i2 = 0;
|
|
@@ -1051,11 +1434,14 @@ function drainQueue() {
|
|
|
1051
1434
|
break;
|
|
1052
1435
|
}
|
|
1053
1436
|
const sub2 = pendingQueue[i2++];
|
|
1437
|
+
pendingSet.delete(sub2);
|
|
1438
|
+
if (quarantined.size > 0 && quarantined.has(sub2)) continue;
|
|
1439
|
+
if (sub2._hasComputedDep === true && !depsChanged(sub2)) continue;
|
|
1054
1440
|
if (tickRepeat(sub2)) {
|
|
1055
1441
|
cycleError(sub2);
|
|
1056
|
-
|
|
1442
|
+
quarantined.add(sub2);
|
|
1443
|
+
continue;
|
|
1057
1444
|
}
|
|
1058
|
-
pendingSet.delete(sub2);
|
|
1059
1445
|
safeInvoke(sub2);
|
|
1060
1446
|
}
|
|
1061
1447
|
}
|
|
@@ -1070,6 +1456,7 @@ function drainNotificationQueue() {
|
|
|
1070
1456
|
if (notifyDepth === 0) {
|
|
1071
1457
|
pendingQueue.length = 0;
|
|
1072
1458
|
pendingSet.clear();
|
|
1459
|
+
if (quarantined.size > 0) quarantined.clear();
|
|
1073
1460
|
}
|
|
1074
1461
|
}
|
|
1075
1462
|
}
|
|
@@ -1163,6 +1550,7 @@ function notifySubscribers(signal2) {
|
|
|
1163
1550
|
if (notifyDepth === 0) {
|
|
1164
1551
|
pendingQueue.length = 0;
|
|
1165
1552
|
pendingSet.clear();
|
|
1553
|
+
if (quarantined.size > 0) quarantined.clear();
|
|
1166
1554
|
}
|
|
1167
1555
|
}
|
|
1168
1556
|
}
|
|
@@ -1189,14 +1577,14 @@ function forEachSubscriber(signal2, visit) {
|
|
|
1189
1577
|
}
|
|
1190
1578
|
|
|
1191
1579
|
// src/reactivity/track.ts
|
|
1192
|
-
var
|
|
1193
|
-
var _runtimeVersion = true ? "
|
|
1580
|
+
var _isDev4 = isDev();
|
|
1581
|
+
var _runtimeVersion = true ? "4.0.0" : "dev";
|
|
1194
1582
|
var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
|
|
1195
1583
|
function resolveReactiveApi() {
|
|
1196
1584
|
const g2 = globalThis;
|
|
1197
1585
|
const existing = g2[REGISTRY_KEY];
|
|
1198
1586
|
if (existing) {
|
|
1199
|
-
if (
|
|
1587
|
+
if (_isDev4 && !existing.__dupWarned) {
|
|
1200
1588
|
existing.__dupWarned = true;
|
|
1201
1589
|
devWarn(
|
|
1202
1590
|
`Multiple instances of the reactive runtime detected on this page (active: ${existing.version}, duplicate: ${_runtimeVersion}). Reactivity still works \u2014 all copies share the first one \u2014 but de-duplicate sibujs in your bundler (e.g. Vite optimizeDeps.exclude: ['sibujs'] or resolve.dedupe: ['sibujs']).`
|
|
@@ -1257,7 +1645,7 @@ function flushUserCleanups(ctx) {
|
|
|
1257
1645
|
try {
|
|
1258
1646
|
list[i2]();
|
|
1259
1647
|
} catch (err) {
|
|
1260
|
-
|
|
1648
|
+
reportError(err, { phase: "cleanup" });
|
|
1261
1649
|
}
|
|
1262
1650
|
}
|
|
1263
1651
|
}
|
|
@@ -1270,11 +1658,12 @@ function drainReruns(ctx) {
|
|
|
1270
1658
|
} while (ctx.rerunPending && ++reruns <= MAX_RERUNS);
|
|
1271
1659
|
if (ctx.rerunPending) {
|
|
1272
1660
|
ctx.rerunPending = false;
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
`
|
|
1276
|
-
)
|
|
1277
|
-
|
|
1661
|
+
reportError(
|
|
1662
|
+
new Error(
|
|
1663
|
+
`effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
|
|
1664
|
+
),
|
|
1665
|
+
{ phase: "effect" }
|
|
1666
|
+
);
|
|
1278
1667
|
}
|
|
1279
1668
|
}
|
|
1280
1669
|
function disposeEffect(ctx) {
|
|
@@ -1351,6 +1740,8 @@ function effect(effectFn, options) {
|
|
|
1351
1740
|
sub2.depsTail = null;
|
|
1352
1741
|
sub2._epoch = 0;
|
|
1353
1742
|
sub2._structDirty = false;
|
|
1743
|
+
sub2._hasComputedDep = false;
|
|
1744
|
+
sub2._errorPhase = "effect";
|
|
1354
1745
|
sub2._runEpoch = 0;
|
|
1355
1746
|
sub2._runs = 0;
|
|
1356
1747
|
ctx.subscriber = sub2;
|
|
@@ -1367,39 +1758,178 @@ function effect(effectFn, options) {
|
|
|
1367
1758
|
return () => disposeEffect(ctx);
|
|
1368
1759
|
}
|
|
1369
1760
|
|
|
1370
|
-
// src/
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
return value;
|
|
1761
|
+
// src/utils/globalSingleton.ts
|
|
1762
|
+
function globalSingleton(key, create) {
|
|
1763
|
+
const g2 = globalThis;
|
|
1764
|
+
return g2[key] ?? (g2[key] = create());
|
|
1375
1765
|
}
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1766
|
+
|
|
1767
|
+
// src/utils/singletonResource.ts
|
|
1768
|
+
function singletonResource(io) {
|
|
1769
|
+
let original = null;
|
|
1770
|
+
let captured = false;
|
|
1771
|
+
const stack = [];
|
|
1772
|
+
function apply() {
|
|
1773
|
+
if (stack.length > 0) {
|
|
1774
|
+
io.write(stack[stack.length - 1].value);
|
|
1775
|
+
return;
|
|
1776
|
+
}
|
|
1777
|
+
if (captured) {
|
|
1778
|
+
io.write(original);
|
|
1779
|
+
captured = false;
|
|
1780
|
+
original = null;
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
return {
|
|
1784
|
+
acquire(value) {
|
|
1785
|
+
if (!captured) {
|
|
1786
|
+
original = io.read();
|
|
1787
|
+
captured = true;
|
|
1788
|
+
}
|
|
1789
|
+
const entry = { value, released: false };
|
|
1790
|
+
stack.push(entry);
|
|
1791
|
+
apply();
|
|
1792
|
+
return {
|
|
1793
|
+
set(next) {
|
|
1794
|
+
if (entry.released) return;
|
|
1795
|
+
entry.value = next;
|
|
1796
|
+
apply();
|
|
1797
|
+
},
|
|
1798
|
+
release() {
|
|
1799
|
+
if (entry.released) return;
|
|
1800
|
+
entry.released = true;
|
|
1801
|
+
const index = stack.indexOf(entry);
|
|
1802
|
+
if (index !== -1) stack.splice(index, 1);
|
|
1803
|
+
apply();
|
|
1804
|
+
}
|
|
1805
|
+
};
|
|
1806
|
+
},
|
|
1807
|
+
ownerCount() {
|
|
1808
|
+
return stack.length;
|
|
1809
|
+
}
|
|
1810
|
+
};
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
// src/utils/documentResources.ts
|
|
1814
|
+
var INERT_LEASE = {
|
|
1815
|
+
set() {
|
|
1816
|
+
},
|
|
1817
|
+
release() {
|
|
1818
|
+
}
|
|
1819
|
+
};
|
|
1820
|
+
function inertLease() {
|
|
1821
|
+
return INERT_LEASE;
|
|
1379
1822
|
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1823
|
+
var titleResource = globalSingleton(
|
|
1824
|
+
/* @__PURE__ */ Symbol.for("sibujs.documentTitle.v1"),
|
|
1825
|
+
() => singletonResource({
|
|
1826
|
+
read: () => document.title,
|
|
1827
|
+
write: (value) => {
|
|
1828
|
+
document.title = value;
|
|
1829
|
+
}
|
|
1830
|
+
})
|
|
1831
|
+
);
|
|
1832
|
+
var baseResource = globalSingleton(
|
|
1833
|
+
/* @__PURE__ */ Symbol.for("sibujs.documentBase.v1"),
|
|
1834
|
+
() => singletonResource({
|
|
1835
|
+
read: () => {
|
|
1836
|
+
const el = document.head.querySelector("base");
|
|
1837
|
+
if (!el) return null;
|
|
1838
|
+
return {
|
|
1839
|
+
href: el.getAttribute("href") ?? void 0,
|
|
1840
|
+
target: el.getAttribute("target") ?? void 0
|
|
1841
|
+
};
|
|
1842
|
+
},
|
|
1843
|
+
write: (spec) => {
|
|
1844
|
+
const existing = document.head.querySelector("base");
|
|
1845
|
+
if (spec === null) {
|
|
1846
|
+
existing?.remove();
|
|
1847
|
+
return;
|
|
1848
|
+
}
|
|
1849
|
+
const el = existing ?? document.head.appendChild(document.createElement("base"));
|
|
1850
|
+
if (spec.href === void 0) el.removeAttribute("href");
|
|
1851
|
+
else el.setAttribute("href", spec.href);
|
|
1852
|
+
if (spec.target === void 0) el.removeAttribute("target");
|
|
1853
|
+
else el.setAttribute("target", spec.target);
|
|
1854
|
+
}
|
|
1855
|
+
})
|
|
1856
|
+
);
|
|
1857
|
+
var scrollRestorationResource = globalSingleton(
|
|
1858
|
+
/* @__PURE__ */ Symbol.for("sibujs.historyScrollRestoration.v1"),
|
|
1859
|
+
() => singletonResource({
|
|
1860
|
+
read: () => history.scrollRestoration,
|
|
1861
|
+
write: (mode) => {
|
|
1862
|
+
history.scrollRestoration = mode;
|
|
1863
|
+
}
|
|
1864
|
+
})
|
|
1865
|
+
);
|
|
1866
|
+
function acquireScrollRestorationMode(mode) {
|
|
1867
|
+
if (typeof history === "undefined" || !("scrollRestoration" in history)) {
|
|
1868
|
+
return inertLease();
|
|
1383
1869
|
}
|
|
1384
|
-
return
|
|
1870
|
+
return scrollRestorationResource.acquire(mode);
|
|
1385
1871
|
}
|
|
1386
|
-
function
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
if (httpEquiv.toLowerCase() !== "refresh") return false;
|
|
1390
|
-
const content = getMetaAttr(metaProps, "content");
|
|
1391
|
-
if (typeof content !== "string") return false;
|
|
1392
|
-
return isDangerousRefreshContent(content);
|
|
1872
|
+
function acquireTitle(value) {
|
|
1873
|
+
if (typeof document === "undefined") return inertLease();
|
|
1874
|
+
return titleResource.acquire(value);
|
|
1393
1875
|
}
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
if (isEventHandlerAttr(name)) return false;
|
|
1398
|
-
return true;
|
|
1876
|
+
function acquireBase(spec) {
|
|
1877
|
+
if (typeof document === "undefined") return inertLease();
|
|
1878
|
+
return baseResource.acquire(spec);
|
|
1399
1879
|
}
|
|
1880
|
+
|
|
1881
|
+
// src/platform/head.ts
|
|
1400
1882
|
function escapeScriptJsonLocal(json) {
|
|
1401
1883
|
return json.replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
1402
1884
|
}
|
|
1885
|
+
var CLIENT_RESOLVER = {
|
|
1886
|
+
resolveValue: (_name, value) => typeof value === "function" ? String(value()) : String(value),
|
|
1887
|
+
isReactiveValue: (value) => typeof value === "function"
|
|
1888
|
+
};
|
|
1889
|
+
function applyMetaEntry(metaProps, managedElements) {
|
|
1890
|
+
let current = null;
|
|
1891
|
+
const retract = () => {
|
|
1892
|
+
if (!current) return;
|
|
1893
|
+
const index = managedElements.indexOf(current);
|
|
1894
|
+
if (index !== -1) managedElements.splice(index, 1);
|
|
1895
|
+
current.remove();
|
|
1896
|
+
current = null;
|
|
1897
|
+
};
|
|
1898
|
+
const alreadyPublished = (attributes) => {
|
|
1899
|
+
if (!current || current.attributes.length !== attributes.size) return false;
|
|
1900
|
+
for (const [name, value] of attributes) {
|
|
1901
|
+
if (current.getAttribute(name) !== value) return false;
|
|
1902
|
+
}
|
|
1903
|
+
return true;
|
|
1904
|
+
};
|
|
1905
|
+
const publish = (attributes) => {
|
|
1906
|
+
if (alreadyPublished(attributes)) return;
|
|
1907
|
+
const next = document.createElement("meta");
|
|
1908
|
+
for (const [name, value] of attributes) next.setAttribute(name, value);
|
|
1909
|
+
const index = current ? managedElements.indexOf(current) : -1;
|
|
1910
|
+
if (current?.parentNode) {
|
|
1911
|
+
current.replaceWith(next);
|
|
1912
|
+
} else {
|
|
1913
|
+
current?.remove();
|
|
1914
|
+
document.head.appendChild(next);
|
|
1915
|
+
}
|
|
1916
|
+
if (index !== -1) managedElements[index] = next;
|
|
1917
|
+
else managedElements.push(next);
|
|
1918
|
+
current = next;
|
|
1919
|
+
};
|
|
1920
|
+
const stopEffect = effect(() => {
|
|
1921
|
+
const plan = planMetaEntry(metaProps, CLIENT_RESOLVER);
|
|
1922
|
+
if (plan.kind === "drop") {
|
|
1923
|
+
retract();
|
|
1924
|
+
return;
|
|
1925
|
+
}
|
|
1926
|
+
publish(plan.attributes);
|
|
1927
|
+
});
|
|
1928
|
+
return () => {
|
|
1929
|
+
stopEffect();
|
|
1930
|
+
retract();
|
|
1931
|
+
};
|
|
1932
|
+
}
|
|
1403
1933
|
function Head(props) {
|
|
1404
1934
|
const anchor = document.createComment("sibu-head");
|
|
1405
1935
|
const managedElements = [];
|
|
@@ -1416,78 +1946,52 @@ function Head(props) {
|
|
|
1416
1946
|
cleanup3();
|
|
1417
1947
|
if (props.title) {
|
|
1418
1948
|
if (typeof props.title === "function") {
|
|
1419
|
-
const
|
|
1420
|
-
|
|
1949
|
+
const getter = props.title;
|
|
1950
|
+
let lease = null;
|
|
1951
|
+
const stopEffect = effect(() => {
|
|
1952
|
+
const next = getter();
|
|
1953
|
+
if (lease) lease.set(next);
|
|
1954
|
+
else lease = acquireTitle(next);
|
|
1955
|
+
});
|
|
1956
|
+
effectCleanups.push(() => {
|
|
1957
|
+
stopEffect();
|
|
1958
|
+
lease?.release();
|
|
1959
|
+
lease = null;
|
|
1421
1960
|
});
|
|
1422
|
-
effectCleanups.push(cleanupFn);
|
|
1423
1961
|
} else {
|
|
1424
|
-
|
|
1962
|
+
const lease = acquireTitle(props.title);
|
|
1963
|
+
effectCleanups.push(() => lease.release());
|
|
1425
1964
|
}
|
|
1426
1965
|
}
|
|
1427
1966
|
if (props.meta) {
|
|
1428
1967
|
for (const metaProps of props.meta) {
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
const isRefreshNow = () => {
|
|
1432
|
-
const eq = typeof httpEquiv === "function" ? httpEquiv() : httpEquiv;
|
|
1433
|
-
return typeof eq === "string" && eq.toLowerCase() === "refresh";
|
|
1434
|
-
};
|
|
1435
|
-
const el = document.createElement("meta");
|
|
1436
|
-
for (const [key, value] of Object.entries(metaProps)) {
|
|
1437
|
-
if (!isSafeHeadAttr(key)) continue;
|
|
1438
|
-
const isContent = key.toLowerCase() === "content";
|
|
1439
|
-
if (typeof value === "function") {
|
|
1440
|
-
const cleanupFn = effect(() => {
|
|
1441
|
-
const resolved = value();
|
|
1442
|
-
if (isContent && isRefreshNow() && isDangerousRefreshContent(resolved)) {
|
|
1443
|
-
el.removeAttribute(key);
|
|
1444
|
-
return;
|
|
1445
|
-
}
|
|
1446
|
-
el.setAttribute(key, sanitizeHeadAttr(key, resolved));
|
|
1447
|
-
});
|
|
1448
|
-
effectCleanups.push(cleanupFn);
|
|
1449
|
-
} else {
|
|
1450
|
-
if (isContent && isRefreshNow() && isDangerousRefreshContent(value)) continue;
|
|
1451
|
-
el.setAttribute(key, sanitizeHeadAttr(key, value));
|
|
1452
|
-
}
|
|
1453
|
-
}
|
|
1454
|
-
document.head.appendChild(el);
|
|
1455
|
-
managedElements.push(el);
|
|
1968
|
+
const stop2 = applyMetaEntry(metaProps, managedElements);
|
|
1969
|
+
effectCleanups.push(stop2);
|
|
1456
1970
|
}
|
|
1457
1971
|
}
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
}
|
|
1469
|
-
if (props.script) {
|
|
1470
|
-
for (const scriptProps of props.script) {
|
|
1471
|
-
const el = document.createElement("script");
|
|
1472
|
-
for (const [key, value] of Object.entries(scriptProps)) {
|
|
1473
|
-
if (!isSafeHeadAttr(key)) continue;
|
|
1474
|
-
el.setAttribute(key, sanitizeHeadAttr(key, value));
|
|
1475
|
-
}
|
|
1972
|
+
for (const [tag, entries] of [
|
|
1973
|
+
["link", props.link],
|
|
1974
|
+
["script", props.script]
|
|
1975
|
+
]) {
|
|
1976
|
+
if (!entries) continue;
|
|
1977
|
+
for (const entryProps of entries) {
|
|
1978
|
+
const plan = planHeadElementEntry(entryProps, STATIC_VALUE_RESOLVER);
|
|
1979
|
+
if (plan.kind === "drop") continue;
|
|
1980
|
+
const el = document.createElement(tag);
|
|
1981
|
+
for (const [name, value] of plan.attributes) el.setAttribute(name, value);
|
|
1476
1982
|
document.head.appendChild(el);
|
|
1477
1983
|
managedElements.push(el);
|
|
1478
1984
|
}
|
|
1479
1985
|
}
|
|
1480
1986
|
if (props.base) {
|
|
1481
|
-
const
|
|
1482
|
-
if (existing) existing.remove();
|
|
1483
|
-
const el = document.createElement("base");
|
|
1987
|
+
const spec = {};
|
|
1484
1988
|
if (props.base.href) {
|
|
1485
1989
|
const safeHref = sanitizeUrl(props.base.href);
|
|
1486
|
-
if (safeHref)
|
|
1990
|
+
if (safeHref) spec.href = safeHref;
|
|
1487
1991
|
}
|
|
1488
|
-
if (props.base.target)
|
|
1489
|
-
|
|
1490
|
-
|
|
1992
|
+
if (props.base.target) spec.target = props.base.target;
|
|
1993
|
+
const lease = acquireBase(spec);
|
|
1994
|
+
effectCleanups.push(() => lease.release());
|
|
1491
1995
|
}
|
|
1492
1996
|
};
|
|
1493
1997
|
apply();
|
|
@@ -1510,7 +2014,9 @@ function setCanonical(url) {
|
|
|
1510
2014
|
link2.rel = "canonical";
|
|
1511
2015
|
document.head.appendChild(link2);
|
|
1512
2016
|
}
|
|
1513
|
-
|
|
2017
|
+
const safe = sanitizeUrl(url);
|
|
2018
|
+
if (safe) link2.setAttribute("href", safe);
|
|
2019
|
+
else link2.removeAttribute("href");
|
|
1514
2020
|
}
|
|
1515
2021
|
|
|
1516
2022
|
// src/platform/staticSiteGenerator.ts
|
|
@@ -1584,70 +2090,72 @@ var isBatching = API2.isBatching;
|
|
|
1584
2090
|
|
|
1585
2091
|
// src/core/signals/signal.ts
|
|
1586
2092
|
var _g2 = globalThis;
|
|
1587
|
-
var
|
|
2093
|
+
var _isDev5 = isDev();
|
|
1588
2094
|
function signal(initial, options) {
|
|
1589
|
-
const
|
|
2095
|
+
const state2 = {
|
|
1590
2096
|
value: initial,
|
|
1591
2097
|
__v: 0,
|
|
1592
2098
|
__sc: 0,
|
|
1593
2099
|
subsHead: null,
|
|
1594
2100
|
subsTail: null,
|
|
1595
2101
|
__activeNode: null,
|
|
1596
|
-
__name: void 0
|
|
2102
|
+
__name: void 0,
|
|
2103
|
+
_d: false,
|
|
2104
|
+
_validate: void 0
|
|
1597
2105
|
};
|
|
1598
|
-
const debugName =
|
|
2106
|
+
const debugName = _isDev5 ? options?.name : void 0;
|
|
1599
2107
|
const equalsFn = options?.equals;
|
|
1600
|
-
if (debugName)
|
|
2108
|
+
if (debugName) state2.__name = debugName;
|
|
1601
2109
|
function get() {
|
|
1602
|
-
recordDependency2(
|
|
1603
|
-
return
|
|
2110
|
+
recordDependency2(state2);
|
|
2111
|
+
return state2.value;
|
|
1604
2112
|
}
|
|
1605
|
-
get.__signal =
|
|
2113
|
+
get.__signal = state2;
|
|
1606
2114
|
if (debugName) get.__name = debugName;
|
|
1607
2115
|
let set;
|
|
1608
2116
|
if (equalsFn) {
|
|
1609
2117
|
set = (next) => {
|
|
1610
|
-
const prev =
|
|
2118
|
+
const prev = state2.value;
|
|
1611
2119
|
const newValue = typeof next === "function" ? next(prev) : next;
|
|
1612
2120
|
if (equalsFn(prev, newValue)) return;
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
if (
|
|
2121
|
+
state2.value = newValue;
|
|
2122
|
+
state2.__v++;
|
|
2123
|
+
if (_isDev5) {
|
|
1616
2124
|
const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1617
|
-
if (hook) hook.emit("signal:update", { signal:
|
|
2125
|
+
if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
|
|
1618
2126
|
}
|
|
1619
|
-
if (!enqueueBatchedSignal(
|
|
1620
|
-
notifySubscribers2(
|
|
2127
|
+
if (!enqueueBatchedSignal(state2)) {
|
|
2128
|
+
notifySubscribers2(state2);
|
|
1621
2129
|
}
|
|
1622
2130
|
};
|
|
1623
|
-
} else if (
|
|
2131
|
+
} else if (_isDev5) {
|
|
1624
2132
|
set = (next) => {
|
|
1625
|
-
const prev =
|
|
2133
|
+
const prev = state2.value;
|
|
1626
2134
|
const newValue = typeof next === "function" ? next(prev) : next;
|
|
1627
2135
|
if (Object.is(newValue, prev)) return;
|
|
1628
|
-
|
|
1629
|
-
|
|
2136
|
+
state2.value = newValue;
|
|
2137
|
+
state2.__v++;
|
|
1630
2138
|
const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1631
|
-
if (hook) hook.emit("signal:update", { signal:
|
|
1632
|
-
if (!enqueueBatchedSignal(
|
|
1633
|
-
notifySubscribers2(
|
|
2139
|
+
if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
|
|
2140
|
+
if (!enqueueBatchedSignal(state2)) {
|
|
2141
|
+
notifySubscribers2(state2);
|
|
1634
2142
|
}
|
|
1635
2143
|
};
|
|
1636
2144
|
} else {
|
|
1637
2145
|
set = (next) => {
|
|
1638
|
-
const prev =
|
|
2146
|
+
const prev = state2.value;
|
|
1639
2147
|
const newValue = typeof next === "function" ? next(prev) : next;
|
|
1640
2148
|
if (Object.is(newValue, prev)) return;
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
if (!enqueueBatchedSignal(
|
|
1644
|
-
notifySubscribers2(
|
|
2149
|
+
state2.value = newValue;
|
|
2150
|
+
state2.__v++;
|
|
2151
|
+
if (!enqueueBatchedSignal(state2)) {
|
|
2152
|
+
notifySubscribers2(state2);
|
|
1645
2153
|
}
|
|
1646
2154
|
};
|
|
1647
2155
|
}
|
|
1648
|
-
if (
|
|
2156
|
+
if (_isDev5) {
|
|
1649
2157
|
const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1650
|
-
if (hook) hook.emit("signal:create", { signal:
|
|
2158
|
+
if (hook) hook.emit("signal:create", { signal: state2, name: debugName, getter: get, initial });
|
|
1651
2159
|
}
|
|
1652
2160
|
return [get, set];
|
|
1653
2161
|
}
|
|
@@ -1733,17 +2241,39 @@ function createAction(actionFn) {
|
|
|
1733
2241
|
}
|
|
1734
2242
|
|
|
1735
2243
|
// src/platform/scrollRestoration.ts
|
|
2244
|
+
var DEFAULT_STATE_KEY = "__sibuScrollKey";
|
|
1736
2245
|
function scrollRestoration(options) {
|
|
1737
2246
|
const mode = options?.mode ?? "auto";
|
|
2247
|
+
const stateKey = options?.stateKey ?? DEFAULT_STATE_KEY;
|
|
1738
2248
|
const positions = /* @__PURE__ */ new Map();
|
|
1739
2249
|
let popstateHandler = null;
|
|
1740
2250
|
let currentKey = null;
|
|
1741
|
-
|
|
2251
|
+
if (options?.getKey) {
|
|
2252
|
+
try {
|
|
2253
|
+
currentKey = options.getKey();
|
|
2254
|
+
} catch {
|
|
2255
|
+
currentKey = null;
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
const record = (key) => {
|
|
1742
2259
|
if (typeof window !== "undefined") {
|
|
1743
2260
|
positions.set(key, { x: window.scrollX, y: window.scrollY });
|
|
1744
2261
|
}
|
|
1745
2262
|
currentKey = key;
|
|
1746
2263
|
};
|
|
2264
|
+
const tagCurrentEntry = (key) => {
|
|
2265
|
+
if (typeof window === "undefined" || typeof history === "undefined") return;
|
|
2266
|
+
try {
|
|
2267
|
+
const existing = history.state ?? {};
|
|
2268
|
+
if (existing[stateKey] === key) return;
|
|
2269
|
+
history.replaceState({ ...existing, [stateKey]: key }, "");
|
|
2270
|
+
} catch {
|
|
2271
|
+
}
|
|
2272
|
+
};
|
|
2273
|
+
const save = (key) => {
|
|
2274
|
+
record(key);
|
|
2275
|
+
if (mode === "auto") tagCurrentEntry(key);
|
|
2276
|
+
};
|
|
1747
2277
|
const restore = (key) => {
|
|
1748
2278
|
const pos = positions.get(key);
|
|
1749
2279
|
if (pos && typeof window !== "undefined") {
|
|
@@ -1754,11 +2284,32 @@ function scrollRestoration(options) {
|
|
|
1754
2284
|
const getPosition = (key) => {
|
|
1755
2285
|
return positions.get(key);
|
|
1756
2286
|
};
|
|
2287
|
+
const onNavigation = (key) => {
|
|
2288
|
+
if (mode !== "auto") return;
|
|
2289
|
+
if (currentKey !== null && currentKey !== key) record(currentKey);
|
|
2290
|
+
currentKey = key;
|
|
2291
|
+
tagCurrentEntry(key);
|
|
2292
|
+
};
|
|
2293
|
+
let nativeModeLease = null;
|
|
1757
2294
|
if (mode === "auto" && typeof window !== "undefined") {
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
2295
|
+
nativeModeLease = acquireScrollRestorationMode("manual");
|
|
2296
|
+
if (currentKey !== null) tagCurrentEntry(currentKey);
|
|
2297
|
+
popstateHandler = (event) => {
|
|
2298
|
+
const state2 = event.state ?? null;
|
|
2299
|
+
const rawDestination = state2 ? state2[stateKey] : void 0;
|
|
2300
|
+
const destination = typeof rawDestination === "string" ? rawDestination : null;
|
|
2301
|
+
let outgoing = currentKey;
|
|
2302
|
+
if (outgoing === null && options?.getKey) {
|
|
2303
|
+
try {
|
|
2304
|
+
outgoing = options.getKey();
|
|
2305
|
+
} catch {
|
|
2306
|
+
outgoing = null;
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
if (outgoing !== null && outgoing !== destination) {
|
|
2310
|
+
record(outgoing);
|
|
1761
2311
|
}
|
|
2312
|
+
if (destination !== null) restore(destination);
|
|
1762
2313
|
};
|
|
1763
2314
|
window.addEventListener("popstate", popstateHandler);
|
|
1764
2315
|
}
|
|
@@ -1767,9 +2318,11 @@ function scrollRestoration(options) {
|
|
|
1767
2318
|
window.removeEventListener("popstate", popstateHandler);
|
|
1768
2319
|
popstateHandler = null;
|
|
1769
2320
|
}
|
|
2321
|
+
nativeModeLease?.release();
|
|
2322
|
+
nativeModeLease = null;
|
|
1770
2323
|
positions.clear();
|
|
1771
2324
|
};
|
|
1772
|
-
return { save, restore, getPosition, dispose: dispose2 };
|
|
2325
|
+
return { save, restore, onNavigation, getPosition, dispose: dispose2 };
|
|
1773
2326
|
}
|
|
1774
2327
|
|
|
1775
2328
|
// src/platform/routeMiddleware.ts
|
|
@@ -1803,17 +2356,86 @@ function createMiddlewareChain() {
|
|
|
1803
2356
|
return { use: use2, run };
|
|
1804
2357
|
}
|
|
1805
2358
|
|
|
1806
|
-
// src/
|
|
1807
|
-
var
|
|
2359
|
+
// src/utils/setSafeAttribute.ts
|
|
2360
|
+
var _isDev6 = isDev();
|
|
2361
|
+
var HTML_NS = "http://www.w3.org/1999/xhtml";
|
|
2362
|
+
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
2363
|
+
var XLINK_NS = "http://www.w3.org/1999/xlink";
|
|
2364
|
+
var XML_NS = "http://www.w3.org/XML/1998/namespace";
|
|
2365
|
+
var BOOLEAN_IDL_ATTRS = /* @__PURE__ */ new Set(["checked", "disabled", "selected"]);
|
|
2366
|
+
function isHtmlElement(el) {
|
|
2367
|
+
return el.namespaceURI === null || el.namespaceURI === HTML_NS;
|
|
2368
|
+
}
|
|
1808
2369
|
function setProp(el, key, val) {
|
|
1809
2370
|
el[key] = val;
|
|
1810
2371
|
}
|
|
1811
|
-
function
|
|
1812
|
-
if (
|
|
1813
|
-
|
|
2372
|
+
function namespaceFor(el, name) {
|
|
2373
|
+
if (el.namespaceURI !== SVG_NS) return null;
|
|
2374
|
+
const lower = name.toLowerCase();
|
|
2375
|
+
if (lower.startsWith("xlink:")) return XLINK_NS;
|
|
2376
|
+
if (lower.startsWith("xml:")) return XML_NS;
|
|
2377
|
+
return null;
|
|
2378
|
+
}
|
|
2379
|
+
function setSafeAttribute(el, name, value, options = {}) {
|
|
2380
|
+
const ns = namespaceFor(el, name);
|
|
2381
|
+
const localName = ns ? name.slice(name.indexOf(":") + 1) : name;
|
|
2382
|
+
if (isEventHandlerAttr(name)) {
|
|
2383
|
+
if (_isDev6) {
|
|
1814
2384
|
devWarn(
|
|
1815
|
-
|
|
2385
|
+
`${options.label ?? "setSafeAttribute"}: refusing to set event-handler attribute "${name}". Its value would be evaluated as JavaScript. Use on:{ ${name.slice(2)}: fn } instead.`
|
|
1816
2386
|
);
|
|
2387
|
+
}
|
|
2388
|
+
if (ns) el.removeAttributeNS(ns, localName);
|
|
2389
|
+
else el.removeAttribute(name);
|
|
2390
|
+
return false;
|
|
2391
|
+
}
|
|
2392
|
+
if (value == null) {
|
|
2393
|
+
if (ns) el.removeAttributeNS(ns, localName);
|
|
2394
|
+
else el.removeAttribute(name);
|
|
2395
|
+
return true;
|
|
2396
|
+
}
|
|
2397
|
+
if (isHtmlContentAttribute(name)) {
|
|
2398
|
+
if (_isDev6) {
|
|
2399
|
+
devWarn(
|
|
2400
|
+
`${options.label ?? "setSafeAttribute"}: refusing to set "${name}". The browser parses this attribute as a nested HTML document, so a generic string value cannot be made safe \u2014 attribute escaping is undone before the parse. Build the frame's content another way.`
|
|
2401
|
+
);
|
|
2402
|
+
}
|
|
2403
|
+
if (ns) el.removeAttributeNS(ns, localName);
|
|
2404
|
+
else el.removeAttribute(name);
|
|
2405
|
+
return false;
|
|
2406
|
+
}
|
|
2407
|
+
const html2 = isHtmlElement(el);
|
|
2408
|
+
const idlName = html2 ? name.toLowerCase() : name;
|
|
2409
|
+
if (typeof value === "boolean") {
|
|
2410
|
+
if (html2 && BOOLEAN_IDL_ATTRS.has(idlName) && idlName in el) {
|
|
2411
|
+
setProp(el, idlName, value);
|
|
2412
|
+
} else if (value) {
|
|
2413
|
+
if (ns) el.setAttributeNS(ns, name, "");
|
|
2414
|
+
else el.setAttribute(name, "");
|
|
2415
|
+
} else if (ns) {
|
|
2416
|
+
el.removeAttributeNS(ns, localName);
|
|
2417
|
+
} else {
|
|
2418
|
+
el.removeAttribute(name);
|
|
2419
|
+
}
|
|
2420
|
+
return true;
|
|
2421
|
+
}
|
|
2422
|
+
const str = String(value);
|
|
2423
|
+
if (options.syncValueProperty !== false && html2 && (idlName === "value" || idlName === "checked") && idlName in el) {
|
|
2424
|
+
setProp(el, idlName, idlName === "checked" ? Boolean(value) : str);
|
|
2425
|
+
return true;
|
|
2426
|
+
}
|
|
2427
|
+
const safe = sanitizeAttributeString(name, str);
|
|
2428
|
+
const current = ns ? el.getAttributeNS(ns, localName) : el.getAttribute(name);
|
|
2429
|
+
if (current === safe) return true;
|
|
2430
|
+
if (ns) el.setAttributeNS(ns, name, safe);
|
|
2431
|
+
else el.setAttribute(name, safe);
|
|
2432
|
+
return true;
|
|
2433
|
+
}
|
|
2434
|
+
|
|
2435
|
+
// src/reactivity/bindAttribute.ts
|
|
2436
|
+
function bindAttribute(el, attr, getter) {
|
|
2437
|
+
if (isEventHandlerAttr(attr)) {
|
|
2438
|
+
setSafeAttribute(el, attr, "", { label: `bindAttribute("${attr}")` });
|
|
1817
2439
|
return () => {
|
|
1818
2440
|
};
|
|
1819
2441
|
}
|
|
@@ -1822,32 +2444,16 @@ function bindAttribute(el, attr, getter) {
|
|
|
1822
2444
|
try {
|
|
1823
2445
|
value = getter();
|
|
1824
2446
|
} catch (err) {
|
|
1825
|
-
|
|
1826
|
-
devWarn(`bindAttribute: getter for "${attr}" threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
2447
|
+
reportError(err, { phase: "binding", name: `bindAttribute("${attr}")`, node: el });
|
|
1827
2448
|
return;
|
|
1828
2449
|
}
|
|
1829
|
-
|
|
1830
|
-
if (attr in el && (attr === "checked" || attr === "disabled" || attr === "selected")) {
|
|
1831
|
-
setProp(el, attr, value);
|
|
1832
|
-
} else if (value) {
|
|
1833
|
-
el.setAttribute(attr, "");
|
|
1834
|
-
} else {
|
|
1835
|
-
el.removeAttribute(attr);
|
|
1836
|
-
}
|
|
1837
|
-
return;
|
|
1838
|
-
}
|
|
1839
|
-
const str = String(value);
|
|
1840
|
-
if ((attr === "value" || attr === "checked") && attr in el) {
|
|
1841
|
-
setProp(el, attr, attr === "checked" ? Boolean(value) : str);
|
|
1842
|
-
} else {
|
|
1843
|
-
el.setAttribute(attr, sanitizeAttributeString(attr, str));
|
|
1844
|
-
}
|
|
2450
|
+
setSafeAttribute(el, attr, value, { label: `bindAttribute("${attr}")` });
|
|
1845
2451
|
}
|
|
1846
|
-
return reactiveBinding2(commit);
|
|
2452
|
+
return reactiveBinding2(commit, el);
|
|
1847
2453
|
}
|
|
1848
2454
|
|
|
1849
2455
|
// src/reactivity/bindChildNode.ts
|
|
1850
|
-
var
|
|
2456
|
+
var _isDev7 = isDev();
|
|
1851
2457
|
function bindChildNode(placeholder, getter) {
|
|
1852
2458
|
let lastNodes = [];
|
|
1853
2459
|
function commit() {
|
|
@@ -1855,7 +2461,7 @@ function bindChildNode(placeholder, getter) {
|
|
|
1855
2461
|
try {
|
|
1856
2462
|
result = getter();
|
|
1857
2463
|
} catch (err) {
|
|
1858
|
-
|
|
2464
|
+
reportError(err, { phase: "binding", name: "bindChildNode", node: placeholder });
|
|
1859
2465
|
return;
|
|
1860
2466
|
}
|
|
1861
2467
|
if (result == null || typeof result === "boolean") {
|
|
@@ -1881,7 +2487,7 @@ function bindChildNode(placeholder, getter) {
|
|
|
1881
2487
|
if (item == null || typeof item === "boolean") continue;
|
|
1882
2488
|
const node = item instanceof Node ? item : document.createTextNode(String(item));
|
|
1883
2489
|
if (seen.has(node)) {
|
|
1884
|
-
if (
|
|
2490
|
+
if (_isDev7)
|
|
1885
2491
|
devWarn("bindChildNode: duplicate node reference in array \u2014 only the first occurrence is rendered.");
|
|
1886
2492
|
continue;
|
|
1887
2493
|
}
|
|
@@ -1916,12 +2522,12 @@ function bindChildNode(placeholder, getter) {
|
|
|
1916
2522
|
}
|
|
1917
2523
|
lastNodes = newNodes;
|
|
1918
2524
|
}
|
|
1919
|
-
return reactiveBinding2(commit);
|
|
2525
|
+
return reactiveBinding2(commit, placeholder);
|
|
1920
2526
|
}
|
|
1921
2527
|
|
|
1922
2528
|
// src/core/rendering/tagFactory.ts
|
|
1923
|
-
var
|
|
1924
|
-
var
|
|
2529
|
+
var SVG_NS2 = "http://www.w3.org/2000/svg";
|
|
2530
|
+
var _isDev8 = isDev();
|
|
1925
2531
|
var BLOCKED_TAGS = /* @__PURE__ */ new Set(["script", "iframe", "object", "embed", "frame", "frameset"]);
|
|
1926
2532
|
function isBlockedTag(tag) {
|
|
1927
2533
|
return BLOCKED_TAGS.has(tag.toLowerCase());
|
|
@@ -1939,9 +2545,6 @@ var CLOBBER_RISKY_IDS = /* @__PURE__ */ new Set([
|
|
|
1939
2545
|
"self",
|
|
1940
2546
|
"frames"
|
|
1941
2547
|
]);
|
|
1942
|
-
function setProp2(el, key, val) {
|
|
1943
|
-
el[key] = val;
|
|
1944
|
-
}
|
|
1945
2548
|
function looksLikeClassList(s2) {
|
|
1946
2549
|
const t = s2.trim();
|
|
1947
2550
|
if (!t) return false;
|
|
@@ -1965,13 +2568,13 @@ function toKebab(prop) {
|
|
|
1965
2568
|
function applyStyle(el, style2) {
|
|
1966
2569
|
if (typeof style2 === "function") {
|
|
1967
2570
|
const teardown = track2(() => {
|
|
1968
|
-
el.setAttribute("style", style2());
|
|
2571
|
+
el.setAttribute("style", sanitizeStyleAttribute(String(style2())));
|
|
1969
2572
|
});
|
|
1970
2573
|
registerDisposer(el, teardown);
|
|
1971
2574
|
return;
|
|
1972
2575
|
}
|
|
1973
2576
|
if (typeof style2 === "string") {
|
|
1974
|
-
el.setAttribute("style", style2);
|
|
2577
|
+
el.setAttribute("style", sanitizeStyleAttribute(style2));
|
|
1975
2578
|
return;
|
|
1976
2579
|
}
|
|
1977
2580
|
const htmlEl = el;
|
|
@@ -2092,7 +2695,7 @@ var tagFactory = (tag, ns) => {
|
|
|
2092
2695
|
appendChildren(el, second);
|
|
2093
2696
|
return el;
|
|
2094
2697
|
}
|
|
2095
|
-
if (
|
|
2698
|
+
if (_isDev8 && looksLikeClassList(first)) {
|
|
2096
2699
|
devWarn(
|
|
2097
2700
|
`tagFactory: lone string "${first}" looks like a class list but is being rendered as TEXT. For a class, use ${tag}({ class: "${first}" }) \u2014 or ${tag}("${first}", children) to set the class AND add children.`
|
|
2098
2701
|
);
|
|
@@ -2113,7 +2716,7 @@ var tagFactory = (tag, ns) => {
|
|
|
2113
2716
|
if (pClass != null) applyClass(el, pClass);
|
|
2114
2717
|
const pId = props.id;
|
|
2115
2718
|
if (pId != null) {
|
|
2116
|
-
if (
|
|
2719
|
+
if (_isDev8 && typeof pId === "string" && CLOBBER_RISKY_IDS.has(pId.toLowerCase())) {
|
|
2117
2720
|
devWarn(
|
|
2118
2721
|
`tagFactory: element id="${pId}" matches a common global and may cause DOM clobbering. Avoid setting ids from untrusted input.`
|
|
2119
2722
|
);
|
|
@@ -2128,7 +2731,7 @@ var tagFactory = (tag, ns) => {
|
|
|
2128
2731
|
const handler = pOn[ev];
|
|
2129
2732
|
if (typeof handler === "function") {
|
|
2130
2733
|
el.addEventListener(ev, handler);
|
|
2131
|
-
} else if (
|
|
2734
|
+
} else if (_isDev8) {
|
|
2132
2735
|
devWarn(
|
|
2133
2736
|
`tagFactory: on.${ev} handler is not a function (got ${typeof handler}). Event listener was not attached.`
|
|
2134
2737
|
);
|
|
@@ -2156,16 +2759,8 @@ var tagFactory = (tag, ns) => {
|
|
|
2156
2759
|
if (isEventHandlerAttr(key)) continue;
|
|
2157
2760
|
if (typeof value === "function") {
|
|
2158
2761
|
registerDisposer(el, bindAttribute(el, key, value));
|
|
2159
|
-
} else if (typeof value === "boolean") {
|
|
2160
|
-
if (key in el && (key === "checked" || key === "disabled" || key === "selected")) {
|
|
2161
|
-
setProp2(el, key, value);
|
|
2162
|
-
} else if (value) {
|
|
2163
|
-
el.setAttribute(key, "");
|
|
2164
|
-
} else {
|
|
2165
|
-
el.removeAttribute(key);
|
|
2166
|
-
}
|
|
2167
2762
|
} else {
|
|
2168
|
-
el
|
|
2763
|
+
setSafeAttribute(el, key, value, { syncValueProperty: false, label: "tagFactory" });
|
|
2169
2764
|
}
|
|
2170
2765
|
}
|
|
2171
2766
|
}
|
|
@@ -2250,7 +2845,7 @@ var param = tagFactory("param");
|
|
|
2250
2845
|
var picture = tagFactory("picture");
|
|
2251
2846
|
var portal = tagFactory("portal");
|
|
2252
2847
|
var source = tagFactory("source");
|
|
2253
|
-
var svg = tagFactory("svg",
|
|
2848
|
+
var svg = tagFactory("svg", SVG_NS2);
|
|
2254
2849
|
var math = tagFactory("math");
|
|
2255
2850
|
var canvas = tagFactory("canvas");
|
|
2256
2851
|
var noscript = tagFactory("noscript");
|
|
@@ -2291,36 +2886,30 @@ var base = tagFactory("base");
|
|
|
2291
2886
|
var link = tagFactory("link");
|
|
2292
2887
|
var meta = tagFactory("meta");
|
|
2293
2888
|
var style = tagFactory("style");
|
|
2294
|
-
var circle = tagFactory("circle",
|
|
2295
|
-
var ellipse = tagFactory("ellipse",
|
|
2296
|
-
var g = tagFactory("g",
|
|
2297
|
-
var line = tagFactory("line",
|
|
2298
|
-
var path = tagFactory("path",
|
|
2299
|
-
var polygon = tagFactory("polygon",
|
|
2300
|
-
var polyline = tagFactory("polyline",
|
|
2301
|
-
var rect = tagFactory("rect",
|
|
2302
|
-
var text = tagFactory("text",
|
|
2303
|
-
var tspan = tagFactory("tspan",
|
|
2304
|
-
var defs = tagFactory("defs",
|
|
2305
|
-
var clipPath = tagFactory("clipPath",
|
|
2306
|
-
var mask = tagFactory("mask",
|
|
2307
|
-
var pattern = tagFactory("pattern",
|
|
2308
|
-
var linearGradient = tagFactory("linearGradient",
|
|
2309
|
-
var radialGradient = tagFactory("radialGradient",
|
|
2310
|
-
var stop = tagFactory("stop",
|
|
2311
|
-
var use = tagFactory("use",
|
|
2312
|
-
var symbol = tagFactory("symbol",
|
|
2313
|
-
var marker = tagFactory("marker",
|
|
2889
|
+
var circle = tagFactory("circle", SVG_NS2);
|
|
2890
|
+
var ellipse = tagFactory("ellipse", SVG_NS2);
|
|
2891
|
+
var g = tagFactory("g", SVG_NS2);
|
|
2892
|
+
var line = tagFactory("line", SVG_NS2);
|
|
2893
|
+
var path = tagFactory("path", SVG_NS2);
|
|
2894
|
+
var polygon = tagFactory("polygon", SVG_NS2);
|
|
2895
|
+
var polyline = tagFactory("polyline", SVG_NS2);
|
|
2896
|
+
var rect = tagFactory("rect", SVG_NS2);
|
|
2897
|
+
var text = tagFactory("text", SVG_NS2);
|
|
2898
|
+
var tspan = tagFactory("tspan", SVG_NS2);
|
|
2899
|
+
var defs = tagFactory("defs", SVG_NS2);
|
|
2900
|
+
var clipPath = tagFactory("clipPath", SVG_NS2);
|
|
2901
|
+
var mask = tagFactory("mask", SVG_NS2);
|
|
2902
|
+
var pattern = tagFactory("pattern", SVG_NS2);
|
|
2903
|
+
var linearGradient = tagFactory("linearGradient", SVG_NS2);
|
|
2904
|
+
var radialGradient = tagFactory("radialGradient", SVG_NS2);
|
|
2905
|
+
var stop = tagFactory("stop", SVG_NS2);
|
|
2906
|
+
var use = tagFactory("use", SVG_NS2);
|
|
2907
|
+
var symbol = tagFactory("symbol", SVG_NS2);
|
|
2908
|
+
var marker = tagFactory("marker", SVG_NS2);
|
|
2314
2909
|
var center = tagFactory("center");
|
|
2315
2910
|
var font = tagFactory("font");
|
|
2316
2911
|
var marquee = tagFactory("marquee");
|
|
2317
2912
|
|
|
2318
|
-
// src/utils/globalSingleton.ts
|
|
2319
|
-
function globalSingleton(key, create) {
|
|
2320
|
-
const g2 = globalThis;
|
|
2321
|
-
return g2[key] ?? (g2[key] = create());
|
|
2322
|
-
}
|
|
2323
|
-
|
|
2324
2913
|
// src/platform/microfrontend.ts
|
|
2325
2914
|
var moduleCache = globalSingleton(
|
|
2326
2915
|
/* @__PURE__ */ Symbol.for("sibujs.microfrontend.moduleCache.v1"),
|
|
@@ -2337,14 +2926,13 @@ function createMicroApp(config) {
|
|
|
2337
2926
|
}
|
|
2338
2927
|
let mounted = false;
|
|
2339
2928
|
function mount(component) {
|
|
2340
|
-
root.replaceChildren();
|
|
2341
2929
|
const el = component();
|
|
2342
|
-
root
|
|
2930
|
+
replaceChildrenSafely(root, el);
|
|
2343
2931
|
mounted = true;
|
|
2344
2932
|
}
|
|
2345
2933
|
function unmount() {
|
|
2346
2934
|
if (!mounted) return;
|
|
2347
|
-
root
|
|
2935
|
+
replaceChildrenSafely(root);
|
|
2348
2936
|
mounted = false;
|
|
2349
2937
|
}
|
|
2350
2938
|
return { mount, unmount, element: host };
|
|
@@ -2391,18 +2979,21 @@ function defineRemoteComponent(name, loader) {
|
|
|
2391
2979
|
class: "sibu-remote",
|
|
2392
2980
|
"data-remote-component": name
|
|
2393
2981
|
});
|
|
2394
|
-
container.appendChild(span({ class: "sibu-remote-loading",
|
|
2982
|
+
container.appendChild(span({ class: "sibu-remote-loading" }, "Loading..."));
|
|
2983
|
+
let disposed = false;
|
|
2984
|
+
registerDisposer(container, () => {
|
|
2985
|
+
disposed = true;
|
|
2986
|
+
});
|
|
2395
2987
|
loader().then((mod) => {
|
|
2396
2988
|
cached = mod.default;
|
|
2397
|
-
|
|
2398
|
-
container
|
|
2989
|
+
if (disposed) return;
|
|
2990
|
+
replaceChildrenSafely(container, cached());
|
|
2399
2991
|
}).catch((err) => {
|
|
2992
|
+
if (disposed) return;
|
|
2400
2993
|
const message = err instanceof Error ? err.message : String(err);
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
nodes: `Failed to load remote component "${name}": ${message}`
|
|
2405
|
-
})
|
|
2994
|
+
replaceChildrenSafely(
|
|
2995
|
+
container,
|
|
2996
|
+
div({ class: "sibu-remote-error" }, `Failed to load remote component "${name}": ${message}`)
|
|
2406
2997
|
);
|
|
2407
2998
|
});
|
|
2408
2999
|
return container;
|
|
@@ -2458,11 +3049,16 @@ function serviceWorker(scriptUrl, options) {
|
|
|
2458
3049
|
const [isReady, setIsReady] = signal(false);
|
|
2459
3050
|
const [isUpdateAvailable, setIsUpdateAvailable] = signal(false);
|
|
2460
3051
|
const [error, setError] = signal(null);
|
|
2461
|
-
let
|
|
3052
|
+
let unregistered = false;
|
|
3053
|
+
let unregisterRequested = false;
|
|
3054
|
+
let registrationPromise = null;
|
|
3055
|
+
let arrivedRegistration = null;
|
|
3056
|
+
let pendingUnregister = null;
|
|
2462
3057
|
let updateFoundHandler = null;
|
|
2463
3058
|
let stateChangeHandler = null;
|
|
2464
3059
|
let trackedWorker = null;
|
|
2465
3060
|
let trackedReg = null;
|
|
3061
|
+
const supported = typeof navigator !== "undefined" && !!navigator.serviceWorker;
|
|
2466
3062
|
function detachListeners() {
|
|
2467
3063
|
if (trackedReg && updateFoundHandler) {
|
|
2468
3064
|
trackedReg.removeEventListener("updatefound", updateFoundHandler);
|
|
@@ -2475,33 +3071,53 @@ function serviceWorker(scriptUrl, options) {
|
|
|
2475
3071
|
trackedWorker = null;
|
|
2476
3072
|
trackedReg = null;
|
|
2477
3073
|
}
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
const newWorker = reg.installing;
|
|
2487
|
-
if (newWorker) {
|
|
2488
|
-
if (trackedWorker && stateChangeHandler) {
|
|
2489
|
-
trackedWorker.removeEventListener("statechange", stateChangeHandler);
|
|
2490
|
-
}
|
|
2491
|
-
trackedWorker = newWorker;
|
|
2492
|
-
stateChangeHandler = () => {
|
|
2493
|
-
if (disposed) return;
|
|
2494
|
-
if (newWorker.state === "installed" && navigator.serviceWorker.controller) {
|
|
2495
|
-
setIsUpdateAvailable(true);
|
|
2496
|
-
}
|
|
2497
|
-
};
|
|
2498
|
-
newWorker.addEventListener("statechange", stateChangeHandler);
|
|
3074
|
+
function attachListeners(reg) {
|
|
3075
|
+
trackedReg = reg;
|
|
3076
|
+
updateFoundHandler = () => {
|
|
3077
|
+
if (unregistered) return;
|
|
3078
|
+
const newWorker = reg.installing;
|
|
3079
|
+
if (newWorker) {
|
|
3080
|
+
if (trackedWorker && stateChangeHandler) {
|
|
3081
|
+
trackedWorker.removeEventListener("statechange", stateChangeHandler);
|
|
2499
3082
|
}
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
3083
|
+
trackedWorker = newWorker;
|
|
3084
|
+
stateChangeHandler = () => {
|
|
3085
|
+
if (unregistered) return;
|
|
3086
|
+
if (newWorker.state === "installed" && navigator.serviceWorker.controller) {
|
|
3087
|
+
setIsUpdateAvailable(true);
|
|
3088
|
+
}
|
|
3089
|
+
};
|
|
3090
|
+
newWorker.addEventListener("statechange", stateChangeHandler);
|
|
3091
|
+
}
|
|
3092
|
+
};
|
|
3093
|
+
reg.addEventListener("updatefound", updateFoundHandler);
|
|
3094
|
+
}
|
|
3095
|
+
function activate(reg) {
|
|
3096
|
+
setRegistration(reg);
|
|
3097
|
+
setIsReady(true);
|
|
3098
|
+
attachListeners(reg);
|
|
3099
|
+
}
|
|
3100
|
+
function recoverFailedUnregister() {
|
|
3101
|
+
unregisterRequested = false;
|
|
3102
|
+
if (!registration() && arrivedRegistration) {
|
|
3103
|
+
activate(arrivedRegistration);
|
|
3104
|
+
arrivedRegistration = null;
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
3107
|
+
function adopt(reg) {
|
|
3108
|
+
if (unregisterRequested) {
|
|
3109
|
+
arrivedRegistration = reg;
|
|
3110
|
+
return null;
|
|
3111
|
+
}
|
|
3112
|
+
activate(reg);
|
|
3113
|
+
return reg;
|
|
3114
|
+
}
|
|
3115
|
+
if (supported) {
|
|
3116
|
+
registrationPromise = navigator.serviceWorker.register(scriptUrl, options).then(adopt).catch((err) => {
|
|
3117
|
+
if (!unregistered) {
|
|
3118
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
3119
|
+
}
|
|
3120
|
+
return null;
|
|
2505
3121
|
});
|
|
2506
3122
|
}
|
|
2507
3123
|
async function update() {
|
|
@@ -2510,19 +3126,43 @@ function serviceWorker(scriptUrl, options) {
|
|
|
2510
3126
|
await reg.update();
|
|
2511
3127
|
}
|
|
2512
3128
|
}
|
|
2513
|
-
async function
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
const reg = registration();
|
|
2517
|
-
if (reg) {
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
setRegistration(null);
|
|
2521
|
-
setIsReady(false);
|
|
2522
|
-
}
|
|
2523
|
-
return result;
|
|
3129
|
+
async function performUnregister() {
|
|
3130
|
+
unregisterRequested = true;
|
|
3131
|
+
if (registrationPromise) await registrationPromise.catch(() => null);
|
|
3132
|
+
const reg = registration() ?? arrivedRegistration;
|
|
3133
|
+
if (!reg) {
|
|
3134
|
+
unregisterRequested = false;
|
|
3135
|
+
return false;
|
|
2524
3136
|
}
|
|
2525
|
-
|
|
3137
|
+
let result;
|
|
3138
|
+
try {
|
|
3139
|
+
result = await reg.unregister();
|
|
3140
|
+
} catch (err) {
|
|
3141
|
+
recoverFailedUnregister();
|
|
3142
|
+
throw err;
|
|
3143
|
+
}
|
|
3144
|
+
if (result) {
|
|
3145
|
+
detachListeners();
|
|
3146
|
+
setRegistration(null);
|
|
3147
|
+
setIsReady(false);
|
|
3148
|
+
setIsUpdateAvailable(false);
|
|
3149
|
+
arrivedRegistration = null;
|
|
3150
|
+
unregistered = true;
|
|
3151
|
+
} else {
|
|
3152
|
+
recoverFailedUnregister();
|
|
3153
|
+
}
|
|
3154
|
+
return result;
|
|
3155
|
+
}
|
|
3156
|
+
async function unregister() {
|
|
3157
|
+
if (!supported || unregistered) return false;
|
|
3158
|
+
if (pendingUnregister) return pendingUnregister;
|
|
3159
|
+
const request = performUnregister();
|
|
3160
|
+
pendingUnregister = request;
|
|
3161
|
+
void request.catch(() => {
|
|
3162
|
+
}).then(() => {
|
|
3163
|
+
if (pendingUnregister === request) pendingUnregister = null;
|
|
3164
|
+
});
|
|
3165
|
+
return request;
|
|
2526
3166
|
}
|
|
2527
3167
|
return { registration, isReady, isUpdateAvailable, error, update, unregister };
|
|
2528
3168
|
}
|
|
@@ -2750,22 +3390,34 @@ var instanceCache = globalSingleton(
|
|
|
2750
3390
|
/* @__PURE__ */ Symbol.for("sibujs.wasm.instanceCache.v1"),
|
|
2751
3391
|
() => /* @__PURE__ */ new Map()
|
|
2752
3392
|
);
|
|
3393
|
+
var inFlightCache = globalSingleton(
|
|
3394
|
+
/* @__PURE__ */ Symbol.for("sibujs.wasm.inFlightCache.v1"),
|
|
3395
|
+
() => /* @__PURE__ */ new Map()
|
|
3396
|
+
);
|
|
3397
|
+
var cacheState = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.wasm.cacheState.v1"), () => ({ generation: 0 }));
|
|
3398
|
+
function isCurrentGeneration(generation) {
|
|
3399
|
+
return generation === cacheState.generation;
|
|
3400
|
+
}
|
|
2753
3401
|
function wasm(source2, config = {}) {
|
|
2754
3402
|
const [instance, setInstance] = signal(null);
|
|
2755
3403
|
const [loading, setLoading] = signal(true);
|
|
2756
3404
|
const [error, setError] = signal(null);
|
|
2757
3405
|
const cacheKey = config.cacheKey || (typeof source2 === "string" ? source2 : void 0);
|
|
3406
|
+
let runId = 0;
|
|
2758
3407
|
async function load() {
|
|
3408
|
+
const myRun = ++runId;
|
|
2759
3409
|
setLoading(true);
|
|
2760
3410
|
setError(null);
|
|
2761
3411
|
setInstance(null);
|
|
2762
3412
|
try {
|
|
2763
|
-
const wasmInstance = await
|
|
3413
|
+
const wasmInstance = await loadWasmModuleWithOptions(source2, { ...config, cacheKey });
|
|
3414
|
+
if (myRun !== runId) return;
|
|
2764
3415
|
setInstance(wasmInstance.exports);
|
|
2765
3416
|
} catch (err) {
|
|
3417
|
+
if (myRun !== runId) return;
|
|
2766
3418
|
setError(err instanceof Error ? err : new Error(String(err)));
|
|
2767
3419
|
} finally {
|
|
2768
|
-
setLoading(false);
|
|
3420
|
+
if (myRun === runId) setLoading(false);
|
|
2769
3421
|
}
|
|
2770
3422
|
}
|
|
2771
3423
|
load();
|
|
@@ -2778,9 +3430,9 @@ function wasm(source2, config = {}) {
|
|
|
2778
3430
|
};
|
|
2779
3431
|
}
|
|
2780
3432
|
async function loadWasmModule(source2, imports, cacheKey) {
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
3433
|
+
return loadWasmModuleWithOptions(source2, { imports, cacheKey });
|
|
3434
|
+
}
|
|
3435
|
+
async function loadWasmModuleWithOptions(source2, opts = {}) {
|
|
2784
3436
|
const key = opts.cacheKey || (typeof source2 === "string" ? source2 : void 0);
|
|
2785
3437
|
if (typeof source2 === "string") {
|
|
2786
3438
|
const allowed = opts.allowedOrigins ?? [];
|
|
@@ -2800,44 +3452,50 @@ async function loadWasmModule(source2, imports, cacheKey) {
|
|
|
2800
3452
|
);
|
|
2801
3453
|
}
|
|
2802
3454
|
}
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
3455
|
+
const generation = cacheState.generation;
|
|
3456
|
+
if (!key) return instantiateWasm(source2, opts.imports, void 0, generation);
|
|
3457
|
+
const cachedInstance = instanceCache.get(key);
|
|
3458
|
+
if (cachedInstance) return cachedInstance;
|
|
3459
|
+
const inFlight = inFlightCache.get(key);
|
|
3460
|
+
if (inFlight) return inFlight;
|
|
3461
|
+
const pending = instantiateWasm(source2, opts.imports, key, generation).then((instance) => {
|
|
3462
|
+
if (isCurrentGeneration(generation)) instanceCache.set(key, instance);
|
|
3463
|
+
return instance;
|
|
3464
|
+
});
|
|
3465
|
+
inFlightCache.set(key, pending);
|
|
3466
|
+
void pending.catch(() => {
|
|
3467
|
+
}).then(() => {
|
|
3468
|
+
if (inFlightCache.get(key) === pending) inFlightCache.delete(key);
|
|
3469
|
+
});
|
|
3470
|
+
return pending;
|
|
3471
|
+
}
|
|
3472
|
+
async function instantiateWasm(source2, wasmImports, key, generation) {
|
|
2810
3473
|
const cachedModule = key ? moduleCache2.get(key) : void 0;
|
|
2811
3474
|
if (cachedModule) {
|
|
2812
|
-
|
|
3475
|
+
return WebAssembly.instantiate(cachedModule, wasmImports || {});
|
|
3476
|
+
}
|
|
3477
|
+
let bytes;
|
|
3478
|
+
if (typeof source2 === "string") {
|
|
3479
|
+
if (typeof WebAssembly.instantiateStreaming === "function") {
|
|
3480
|
+
const response2 = fetch(source2);
|
|
3481
|
+
const result = await WebAssembly.instantiateStreaming(response2, wasmImports || {});
|
|
3482
|
+
if (key && isCurrentGeneration(generation)) moduleCache2.set(key, result.module);
|
|
3483
|
+
return result.instance;
|
|
3484
|
+
}
|
|
3485
|
+
const response = await fetch(source2);
|
|
3486
|
+
bytes = await response.arrayBuffer();
|
|
3487
|
+
} else if (source2 instanceof Uint8Array) {
|
|
3488
|
+
bytes = source2.buffer.slice(source2.byteOffset, source2.byteOffset + source2.byteLength);
|
|
2813
3489
|
} else {
|
|
2814
|
-
|
|
2815
|
-
if (typeof source2 === "string") {
|
|
2816
|
-
if (typeof WebAssembly.instantiateStreaming === "function") {
|
|
2817
|
-
const response2 = fetch(source2);
|
|
2818
|
-
const result = await WebAssembly.instantiateStreaming(response2, wasmImports || {});
|
|
2819
|
-
if (key) {
|
|
2820
|
-
moduleCache2.set(key, result.module);
|
|
2821
|
-
instanceCache.set(key, result.instance);
|
|
2822
|
-
}
|
|
2823
|
-
return result.instance;
|
|
2824
|
-
}
|
|
2825
|
-
const response = await fetch(source2);
|
|
2826
|
-
bytes = await response.arrayBuffer();
|
|
2827
|
-
} else if (source2 instanceof Uint8Array) {
|
|
2828
|
-
bytes = source2.buffer.slice(source2.byteOffset, source2.byteOffset + source2.byteLength);
|
|
2829
|
-
} else {
|
|
2830
|
-
bytes = source2;
|
|
2831
|
-
}
|
|
2832
|
-
module2 = await WebAssembly.compile(bytes);
|
|
2833
|
-
if (key) moduleCache2.set(key, module2);
|
|
3490
|
+
bytes = source2;
|
|
2834
3491
|
}
|
|
2835
|
-
const
|
|
2836
|
-
if (key)
|
|
2837
|
-
return
|
|
3492
|
+
const module2 = await WebAssembly.compile(bytes);
|
|
3493
|
+
if (key && isCurrentGeneration(generation)) moduleCache2.set(key, module2);
|
|
3494
|
+
return WebAssembly.instantiate(module2, wasmImports || {});
|
|
2838
3495
|
}
|
|
2839
3496
|
async function preloadWasm(url, options = {}) {
|
|
2840
3497
|
if (moduleCache2.has(url)) return;
|
|
3498
|
+
const generation = cacheState.generation;
|
|
2841
3499
|
const allowed = options.allowedOrigins ?? [];
|
|
2842
3500
|
if (allowed.length > 0) {
|
|
2843
3501
|
let parsed;
|
|
@@ -2862,7 +3520,7 @@ async function preloadWasm(url, options = {}) {
|
|
|
2862
3520
|
const bytes = await response.arrayBuffer();
|
|
2863
3521
|
module2 = await WebAssembly.compile(bytes);
|
|
2864
3522
|
}
|
|
2865
|
-
moduleCache2.set(url, module2);
|
|
3523
|
+
if (isCurrentGeneration(generation)) moduleCache2.set(url, module2);
|
|
2866
3524
|
}
|
|
2867
3525
|
function createWasmBridge(instance) {
|
|
2868
3526
|
const exports2 = instance.exports;
|
|
@@ -2902,8 +3560,10 @@ function createWasmBridge(instance) {
|
|
|
2902
3560
|
};
|
|
2903
3561
|
}
|
|
2904
3562
|
function clearWasmCache() {
|
|
3563
|
+
cacheState.generation++;
|
|
2905
3564
|
moduleCache2.clear();
|
|
2906
3565
|
instanceCache.clear();
|
|
3566
|
+
inFlightCache.clear();
|
|
2907
3567
|
}
|
|
2908
3568
|
function isWasmCached(key) {
|
|
2909
3569
|
return moduleCache2.has(key);
|
|
@@ -2933,6 +3593,7 @@ function isWasmCached(key) {
|
|
|
2933
3593
|
island,
|
|
2934
3594
|
loadRemoteModule,
|
|
2935
3595
|
loadWasmModule,
|
|
3596
|
+
loadWasmModuleWithOptions,
|
|
2936
3597
|
preloadWasm,
|
|
2937
3598
|
renderToDocument,
|
|
2938
3599
|
renderToReadableStream,
|