diff --git a/.sizes.json b/.sizes.json
index 939193f94..4d3386521 100644
--- a/.sizes.json
+++ b/.sizes.json
@@ -7,68 +7,68 @@
{
"name": "*",
"total": {
- "min": 13988,
- "brotli": 5409
+ "min": 14514,
+ "brotli": 5587
}
},
{
"name": "counter",
"user": {
"min": 301,
- "brotli": 211
+ "brotli": 210
},
"runtime": {
- "min": 3670,
- "brotli": 1543
+ "min": 3774,
+ "brotli": 1588
},
"total": {
- "min": 3971,
- "brotli": 1754
+ "min": 4075,
+ "brotli": 1798
}
},
{
"name": "counter 💧",
"user": {
"min": 219,
- "brotli": 160
+ "brotli": 161
},
"runtime": {
- "min": 2297,
- "brotli": 1086
+ "min": 2555,
+ "brotli": 1162
},
"total": {
- "min": 2516,
- "brotli": 1246
+ "min": 2774,
+ "brotli": 1323
}
},
{
"name": "comments",
"user": {
- "min": 1071,
- "brotli": 588
+ "min": 1066,
+ "brotli": 592
},
"runtime": {
- "min": 7435,
- "brotli": 3064
+ "min": 7677,
+ "brotli": 3176
},
"total": {
- "min": 8506,
- "brotli": 3652
+ "min": 8743,
+ "brotli": 3768
}
},
{
"name": "comments 💧",
"user": {
- "min": 934,
+ "min": 939,
"brotli": 544
},
"runtime": {
- "min": 7927,
- "brotli": 3302
+ "min": 8178,
+ "brotli": 3379
},
"total": {
- "min": 8861,
- "brotli": 3846
+ "min": 9117,
+ "brotli": 3923
}
}
]
diff --git a/.sizes/comments.csr/entry.js b/.sizes/comments.csr/entry.js
index 2c7cc6db2..e9dfaaf7e 100644
--- a/.sizes/comments.csr/entry.js
+++ b/.sizes/comments.csr/entry.js
@@ -2,30 +2,30 @@ import {
r as s,
c as n,
a,
- i as o,
- v as t,
- b as l,
- l as i,
+ o,
+ b as t,
+ v as l,
+ q as i,
d as c,
e as u,
f as m,
- g as d,
- q as e,
+ i as d,
+ g as e,
h as r,
j as v,
- o as $,
- k as f,
+ k as $,
+ l as f,
m as b,
n as p,
-} from "./runtime-BUIbQm-R.js";
-const h = o(2, (s) => {
+} from "./runtime-BHYPJMig.js";
+const h = e(2, (s) => {
const {
_: { 6: n, 8: a },
} = s;
H(s[0], { comments: n.comments, path: a });
}),
- k = a(8, null, void 0, h),
- K = a(6, null, void 0, h),
+ k = t(8, null, void 0, h),
+ K = t(6, null, void 0, h),
T = s(
"QURHKITf",
n(
@@ -37,47 +37,47 @@ const h = o(2, (s) => {
[K, k],
),
),
- _ = o(2, (s) => {
+ _ = e(2, (s) => {
const {
_: { 2: n },
7: a,
} = s;
U(s, `${n.path || "c"}-${a}`);
}),
- j = r(4),
- E = v("ZcKJNKFe", (s) =>
- $(
+ j = v(4),
+ E = a("ZcKJNKFe", (s) =>
+ o(
s[2],
"click",
((s) => {
const { 9: n } = s;
return function () {
- f(s, F, !n);
+ i(s, F, !n);
};
})(s),
),
),
- F = t(9, (s, n) => {
- m(s[0], "hidden", !n), d(s[3], n ? "[-]" : "[+]"), e(s, E);
+ F = l(9, (s, n) => {
+ c(s[0], "hidden", !n), u(s[3], n ? "[-]" : "[+]"), m(s, E);
}),
- U = t(8, (s, n) => m(s[0], "id", n), u(k, 4)),
- Z = t(7, null, _),
- g = t(
+ U = l(8, (s, n) => c(s[0], "id", n), r(k, 4)),
+ Z = l(7, null, _),
+ g = l(
6,
(s, n) => {
- d(s[1], n.text), j(s, n.comments ? T : null);
+ u(s[1], n.text), j(s, n.comments ? T : null);
},
- l([j, u(K, 4)]),
+ d([j, r(K, 4)]),
),
- q = t(
+ q = l(
5,
(s, n) => {
g(s, n[0]), Z(s, n[1]);
},
- l([g, Z]),
+ d([g, Z]),
),
- x = a(2, null, void 0, _),
- D = i(
+ x = t(2, null, void 0, _),
+ D = f(
0,
s(
"$F_EaYZk",
@@ -93,11 +93,11 @@ const h = o(2, (s) => {
),
),
),
- H = t(2, (s, n) => D(s, [n.comments]), l([D, c(x, 0)])),
+ H = l(2, (s, n) => D(s, [n.comments]), d([D, $(x, 0)])),
I = "
",
J = " b",
N = function () {},
- Q = t(2, (s, n) => H(s[0], n), p(0, H));
+ Q = l(2, (s, n) => H(s[0], n), p(0, H));
b(
n(
`${I}`,
@@ -107,7 +107,7 @@ b(
},
void 0,
void 0,
- t(1, (s, n) => Q(s, n[0]), Q),
+ l(1, (s, n) => Q(s, n[0]), Q),
),
"rUbTinTf",
).mount();
diff --git a/.sizes/comments.ssr/entry.js b/.sizes/comments.ssr/entry.js
index 7745951fb..a72da3628 100644
--- a/.sizes/comments.ssr/entry.js
+++ b/.sizes/comments.ssr/entry.js
@@ -2,29 +2,29 @@ import {
r as s,
c as n,
a,
- b as t,
- o,
+ o as t,
+ b as o,
v as l,
- d as c,
- e as i,
- i as u,
- f as m,
- q as e,
+ q as c,
+ d as i,
+ e as u,
+ i as m,
+ f as e,
g as d,
h as r,
j as f,
k as p,
l as v,
m as $,
-} from "./runtime-C35w6ZnD.js";
-const b = m(2, (s) => {
+} from "./runtime-CcsLIbdY.js";
+const b = e(2, (s) => {
const {
_: { 6: n, 8: a },
} = s;
I(s[0], { comments: n.comments, path: a });
}),
- h = t(8, null, void 0, b),
- k = t(6, null, void 0, b),
+ h = o(8, null, void 0, b),
+ k = o(6, null, void 0, b),
K = s(
"QURHKITf",
n(
@@ -36,7 +36,7 @@ const b = m(2, (s) => {
[k, h],
),
),
- _ = m(2, (s) => {
+ _ = e(2, (s) => {
const {
_: { 2: n },
7: a,
@@ -45,37 +45,37 @@ const b = m(2, (s) => {
}),
j = r(4),
E = a("ZcKJNKFe", (s) =>
- o(
+ t(
s[2],
"click",
((s) => {
const { 9: n } = s;
return function () {
- e(s, F, !n);
+ c(s, F, !n);
};
})(s),
),
),
F = l(9, (s, n) => {
- c(s[0], "hidden", !n), i(s[3], n ? "[-]" : "[+]"), p(s, E);
+ i(s[0], "hidden", !n), u(s[3], n ? "[-]" : "[+]"), f(s, E);
}),
- Z = l(8, (s, n) => c(s[0], "id", n), d(h, 4)),
+ Z = l(8, (s, n) => i(s[0], "id", n), d(h, 4)),
g = l(7, null, _),
q = l(
6,
(s, n) => {
- i(s[1], n.text), j(s, n.comments ? K : null);
+ u(s[1], n.text), j(s, n.comments ? K : null);
},
- u([j, d(k, 4)]),
+ m([j, d(k, 4)]),
),
x = l(
5,
(s, n) => {
q(s, n[0]), g(s, n[1]);
},
- u([q, g]),
+ m([q, g]),
),
- D = t(2, null, void 0, _),
+ D = o(2, null, void 0, _),
H = v(
0,
s(
@@ -92,7 +92,7 @@ const b = m(2, (s) => {
),
),
),
- I = l(2, (s, n) => H(s, [n.comments]), u([H, f(D, 0)])),
+ I = l(2, (s, n) => H(s, [n.comments]), m([H, p(D, 0)])),
J = "",
N = " b",
Q = function () {};
diff --git a/.sizes/counter.csr/entry.js b/.sizes/counter.csr/entry.js
index da12fb0be..72e2a72e4 100644
--- a/.sizes/counter.csr/entry.js
+++ b/.sizes/counter.csr/entry.js
@@ -1,30 +1,30 @@
import {
- c as s,
- a as t,
- v as a,
- d as n,
+ r as s,
+ c as t,
+ o as a,
+ a as n,
q as o,
- r as c,
- o as i,
+ v as c,
+ d as i,
b as r,
-} from "./runtime-C853KoX8.js";
-const u = c("XBSGKvBc", (s) =>
- i(
+} from "./runtime-BhCks4YK.js";
+const u = s("XBSGKvBc", (s) =>
+ a(
s[0],
"click",
((s) => {
const { 2: t } = s;
return function () {
- r(s, m, t + 1);
+ o(s, m, t + 1);
};
})(s),
),
),
- m = a(2, (s, t) => {
- n(s[1], t), o(s, u);
+ m = c(2, (s, t) => {
+ i(s[1], t), r(s, u);
});
-s(
- t("", "D D m", (s) => {
+t(
+ n("", "D D m", (s) => {
m(s, 0);
}),
"tPaZsVqd",
diff --git a/.sizes/counter.ssr/entry.js b/.sizes/counter.ssr/entry.js
index c4f5b1b86..e357bcc32 100644
--- a/.sizes/counter.ssr/entry.js
+++ b/.sizes/counter.ssr/entry.js
@@ -6,7 +6,7 @@ import {
d as o,
a as r,
i as t,
-} from "./runtime-BOMPXhWq.js";
+} from "./runtime-BP-z7kLQ.js";
const i = s("XBSGKvBc", (s) =>
a(
s[0],
diff --git a/.sizes/dom.js b/.sizes/dom.js
index 8d7f88fba..cc50cb816 100644
--- a/.sizes/dom.js
+++ b/.sizes/dom.js
@@ -1,5 +1,5 @@
function e(e) {
- return { p: 1, $global: e };
+ return { B: 1, $global: e };
}
var t = e({});
function n(e) {
@@ -7,13 +7,13 @@ function n(e) {
}
function r(e) {
return (t, n) => {
- t.i ??= new Map();
- let r = t.i.get(n);
- return r || ((r = e(t, n)), t.i.set(n, r)), r;
+ t.m ??= new Map();
+ let r = t.m.get(n);
+ return r || ((r = e(t, n)), t.m.set(n, r)), r;
};
}
-var o = r((e, t) => t && { ...t, j: e }),
- l = r((e, t) =>
+var o = r((e, t) => t && { ...t, n: e }),
+ i = r((e, t) =>
t.length
? function (...n) {
return t.call(this, e, ...n);
@@ -22,20 +22,20 @@ var o = r((e, t) => t && { ...t, j: e }),
return t.call(this, e);
},
);
-function i(e) {
- f(e), e._?.g?.delete(e);
- let t = e.q?.c;
- if (t) for (let n of t) n.h?.(e);
+function l(e) {
+ f(e), e._?.j?.delete(e);
+ let t = e.C?.c;
+ if (t) for (let n of t) n.k?.(e);
return e;
}
function f(e) {
- let t = e.g;
+ let t = e.j;
if (t) for (let e of t) f(e);
- let n = e.k;
+ let n = e.o;
if (n) for (let e of n.values()) e.abort();
}
function u(e) {
- i(e);
+ l(e);
let t = e.a,
n = e.b.nextSibling;
for (; t !== n; ) {
@@ -56,12 +56,12 @@ function s(e, t) {
return t ? e : "";
}
var d = /^(--|ta|or|li|z)|n-c|i(do|nk|m|t)|w$|we/;
-function g(e, t) {
+function h(e, t) {
return t || 0 === t
? `${e}:${"number" == typeof t && t && !d.test(e) ? t + "px" : t}`
: "";
}
-function h(e, t, n) {
+function g(e, t, n) {
switch (typeof e) {
case "string":
return e;
@@ -70,14 +70,14 @@ function h(e, t, n) {
let r = "",
o = "";
if (Array.isArray(e))
- for (let l of e) {
- let e = h(l, t, n);
+ for (let i of e) {
+ let e = g(i, t, n);
"" !== e && ((r += o + e), (o = t));
}
else
- for (let l in e) {
- let i = n(l, e[l]);
- "" !== i && ((r += o + i), (o = t));
+ for (let i in e) {
+ let l = n(i, e[i]);
+ "" !== l && ((r += o + l), (o = t));
}
return r;
}
@@ -85,41 +85,41 @@ function h(e, t, n) {
return "";
}
function p(e, t) {
- let n = e.k;
+ let n = e.o;
if (n) {
let e = n.get(t);
e && (e.abort(), n.delete(t));
}
}
function b(e, t) {
- let n = (e.k ??= new Map()),
+ let n = (e.o ??= new Map()),
r = n.get(t);
return (
r ||
((function (e) {
let t = e._;
- for (; t && !t.g?.has(e); ) (t.g ||= new Set()).add(e), (t = (e = t)._);
+ for (; t && !t.j?.has(e); ) (t.j ||= new Set()).add(e), (t = (e = t)._);
})(e),
n.set(t, (r = new AbortController()))),
r.signal
);
}
var v = new Map(),
- m = new WeakMap(),
- y = { capture: !0 };
-function w(e, t, n) {
+ w = new WeakMap(),
+ m = { capture: !0 };
+function y(e, t, n) {
let r = v.get(t);
r || v.set(t, (r = new WeakMap())),
r.has(e) ||
(function (e, t) {
let n = e.getRootNode(),
- r = m.get(n);
- r || m.set(n, (r = new Set())),
- r.has(t) || (r.add(t), n.addEventListener(t, N, y));
+ r = w.get(n);
+ r || w.set(n, (r = new Set())),
+ r.has(t) || (r.add(t), n.addEventListener(t, C, m));
})(e, t),
r.set(e, n || void 0);
}
-function N(e) {
+function C(e) {
let t = e.target;
if (t) {
let n = v.get(e.type);
@@ -127,9 +127,9 @@ function N(e) {
for (; (t = t.parentElement) && !e.cancelBubble; ) n.get(t)?.(e, t);
}
}
-var S = /^on[A-Z-]/;
-function C(e, t, n) {
- x(
+var x = /^on[A-Z-]/;
+function $(e, t, n) {
+ S(
e,
t,
(function (e) {
@@ -137,28 +137,28 @@ function C(e, t, n) {
})(n),
);
}
-function x(e, t, n) {
+function S(e, t, n) {
void 0 === n ? e.removeAttribute(t) : e.setAttribute(t, n);
}
-function $(e, t) {
- x(
+function N(e, t) {
+ S(
e,
"class",
(function (e) {
- return h(e, " ", s);
- })(t) || void 0,
- );
-}
-function k(e, t) {
- x(
- e,
- "style",
- (function (e) {
- return h(e, ";", g);
+ return g(e, " ", s);
})(t) || void 0,
);
}
function M(e, t) {
+ S(
+ e,
+ "style",
+ (function (e) {
+ return g(e, ";", h);
+ })(t) || void 0,
+ );
+}
+function k(e, t) {
let n = (function (e) {
return e || 0 === e ? e + "" : "";
})(t);
@@ -172,51 +172,51 @@ function A(e, t, n) {
let t = n[e];
switch (e) {
case "class":
- $(o, t);
+ N(o, t);
break;
case "style":
- k(o, t);
+ M(o, t);
break;
case "renderBody":
break;
default:
- S.test(e)
+ x.test(e)
? ((r ??= {})["-" === e[2] ? e.slice(3) : e.slice(2).toLowerCase()] =
t)
- : C(o, e, t);
+ : $(o, e, t);
}
}
e[t + "~"] = r;
}
-function T(e, t) {
+function _(e, t) {
let n = e[t],
r = e[t + "~"];
- for (let e in r) w(n, e, r[e]);
+ for (let e in r) y(n, e, r[e]);
}
var E = document.createElement("template");
function B(e, t, n) {
let r = e[n],
o = e[n + "-"] || r,
- l = r.parentNode,
- i = o.nextSibling;
+ i = r.parentNode,
+ l = o.nextSibling;
E.innerHTML = t || 0 === t ? `${t}` : "";
let f = E.content;
- (e[n] = f.firstChild), (e[n + "-"] = f.lastChild), l.insertBefore(f, r);
+ (e[n] = f.firstChild), (e[n + "-"] = f.lastChild), i.insertBefore(f, r);
let u = r;
- for (; u !== i; ) {
+ for (; u !== l; ) {
let e = u.nextSibling;
u.remove(), (u = e);
}
}
-function _(e, t, n) {
+function I(e, t, n) {
let r = e[n],
o = e[n + "-"],
- l = e[t];
- if (o) for (let e in o) e in r || (l[e] = void 0);
- for (let e in r) l[e] = r[e];
+ i = e[t];
+ if (o) for (let e in o) e in r || (i[e] = void 0);
+ for (let e in r) i[e] = r[e];
e[n + "-"] = r;
}
-function j(e, t, n) {
+function T(e, t, n) {
let r = e[t];
r
? (Object.assign(r, n), r.onUpdate?.())
@@ -224,19 +224,19 @@ function j(e, t, n) {
n.onMount?.(),
(b(e, "-" + t).onabort = () => n.onDestroy?.()));
}
-var I = {},
+var j = {},
O = {},
q = {};
-function W(e, t) {
+function R(e, t) {
let n = e + "#";
return (e, r) => {
- r !== I && void 0 === e[n] && t(e, r);
+ r !== j && void 0 === e[n] && t(e, r);
};
}
-function L(e, t) {
+function z(e, t) {
let n = e + "#";
return (r, o) => {
- if (o !== I && o !== O && o !== q) {
+ if (o !== j && o !== O && o !== q) {
if (null != o && "function" != typeof o)
throw new Error(`Invalid value ${o} for change handler '${e}'`);
if (void 0 !== r[n]) {
@@ -254,113 +254,114 @@ function L(e, t) {
t(r, o);
};
}
-function R(e, t, n) {
+function D(e, t, n) {
let r = e + "#";
- return (o, l) => {
- if (l === I) 1 === (o[r] = (o[r] ?? 0) + 1) && n?.(o, I);
- else if (l !== q) {
- let i = void 0 !== o[r];
+ return (o, i) => {
+ if (i === j) 1 === (o[r] = (o[r] ?? 0) + 1) && n?.(o, j);
+ else if (i !== q) {
+ let l = void 0 !== o[r];
1 === (o[r] ||= 1) &&
- (l === O || (i && o[e] === l)
+ (i === O || (l && o[e] === i)
? n?.(o, O)
- : ((o[e] = l), t?.(o, l), n?.(o, q))),
+ : ((o[e] = i), t?.(o, i), n?.(o, q))),
o[r]--;
}
};
}
-var D = 0;
-function F(e, t, n) {
- let r = "?" + D++,
+var F = 0;
+function L(e, t, n) {
+ let r = "?" + F++,
o = r + "#";
- return (l, i) => {
- i === I
- ? 1 === (l[o] = (l[o] ?? 0) + 1) && n?.(l, I)
- : void 0 === l[o]
- ? ((l[o] = e - 1), (l[r] = !0))
- : 0 == --l[o]
- ? i === q || l[r]
- ? ((l[r] = !1), t(l, 0), n?.(l, q))
- : n?.(l, O)
- : (l[r] ||= i === q);
+ return (i, l) => {
+ l === j
+ ? 1 === (i[o] = (i[o] ?? 0) + 1) && n?.(i, j)
+ : void 0 === i[o]
+ ? ((i[o] = e - 1), (i[r] = !0))
+ : 0 == --i[o]
+ ? l === q || i[r]
+ ? ((i[r] = !1), t(i, 0), n?.(i, q))
+ : n?.(i, O)
+ : (i[r] ||= l === q);
};
}
-var H = (e) => e._;
-function z(e, t, n, r) {
- let o = "?" + D++,
- l = o + 1,
- i = n || H,
+var W = (e) => e._;
+function P(e, t, n, r) {
+ let o = "?" + F++,
+ i = o + 1,
+ l = n || W,
f = "function" == typeof e ? e : () => e;
return (e, n) => {
- if (n === I) 1 === (e[l] = (e[l] ?? 0) + 1) && r?.(e, I);
+ if (n === j) 1 === (e[i] = (e[i] ?? 0) + 1) && r?.(e, j);
else {
let u, a;
- if (void 0 === e[l]) {
- (u = i(e)), (a = f(e));
+ if (void 0 === e[i]) {
+ (u = l(e)), (a = f(e));
let t = u[a + "#"],
- r = void 0 === t ? !u.p : 0 === t;
- (e[l] = r ? 1 : 2), (n = q);
+ r = void 0 === t ? !u.B : 0 === t;
+ (e[i] = r ? 1 : 2), (n = q);
}
- 0 == --e[l]
+ 0 == --e[i]
? n === q || e[o]
- ? ((e[o] = !1), (u ??= i(e)), (a ??= f(e)), t?.(e, u[a]), r?.(e, q))
+ ? ((e[o] = !1), (u ??= l(e)), (a ??= f(e)), t?.(e, u[a]), r?.(e, q))
: r?.(e, O)
: (e[o] ||= n === q);
}
};
}
-function J(e, t, n, r) {
- let o = n || H,
- i = "function" == typeof e ? e : () => e,
- f = z(i, t, o, r);
+function G(e, t, n, r) {
+ let o = n || W,
+ l = "function" == typeof e ? e : () => e,
+ f = P(l, t, o, r);
return (
- (f.e = (e) => {
+ (f.g = (e) => {
let t = o(e),
- n = i(e) + "*";
- (t[n] ??= new Set()), t[n].add(l(e, f));
+ n = l(e) + "*";
+ (t[n] ??= new Set()), t[n].add(i(e, f));
}),
- (f.h = (e) => {
+ (f.k = (e) => {
let t = o(e),
- n = i(e) + "*";
- t[n]?.delete(l(e, f));
+ n = l(e) + "*";
+ t[n]?.delete(i(e, f));
}),
f
);
}
-function U(e, t) {
+function H(e, t) {
let n = (n, r) => {
let o = n[t];
for (let t of e) t(o, r);
};
return (
- (n.e = (n) => {
+ (n.g = (n) => {
let r = n[t];
- for (let t of e) t.e?.(r);
+ for (let t of e) t.g?.(r);
}),
- (n.h = (n) => {
+ (n.k = (n) => {
let r = n[t];
- for (let t of e) t.h?.(r);
+ for (let t of e) t.k?.(r);
}),
n
);
}
-function V(e) {
+function J(e) {
let t = e + "*";
return (e, n) => {
let r = e[t];
if (r) for (let e of r) e(n);
};
}
-function Z(e, t, n) {
+function U(e, t, n) {
e[t]["/"] = (t) => n(e, t);
}
-var G = (e, t) => e["/"]?.(t),
+var Z = (e, t) => e["/"]?.(t),
K = (e, t, n) => {
let r = e?.c;
if (r) for (let e of r) e(t, n);
},
- P = 0;
-function Q() {
- return "c" + P++;
+ Q = new WeakMap();
+function V({ $global: e }) {
+ let t = Q.get(e) || 0;
+ return Q.set(e, t + 1), "c" + e.runtimeId + e.renderId + t.toString(36);
}
function X(e, t) {
return (n, r) => {
@@ -385,17 +386,17 @@ function ne(e, t, n) {
}
function re(t, n, r) {
let o,
- l = 0,
i = 0,
+ l = 0,
f = 0;
for (; (o = t.charCodeAt(r++)); )
- if (((i = l), (l = 0), o >= 117)) l = 10 * i + o - 117;
+ if (((l = i), (i = 0), o >= 117)) i = 10 * l + o - 117;
else if (o >= 107) {
- for (o = 10 * i + o - 107; o--; ) ee.parentNode();
+ for (o = 10 * l + o - 107; o--; ) ee.parentNode();
ee.nextSibling();
} else if (o >= 97)
- for (o = 10 * i + o - 97; o--; ) !ee.nextSibling() && ee.nextNode();
- else if (o >= 67) for (o = 20 * i + o - 67; o--; ) ee.nextNode();
+ for (o = 10 * l + o - 97; o--; ) !ee.nextSibling() && ee.nextNode();
+ else if (o >= 67) for (o = 20 * l + o - 67; o--; ) ee.nextNode();
else if (47 === o) r = re(t, (n[f++] = e(n.$global)), r);
else {
if (38 === o) return r;
@@ -414,51 +415,51 @@ function re(t, n, r) {
}
function oe(t, n, r) {
let o = e(n);
- if (((o._ = t.j || r), (o.q = t), le(t, o), t.c)) for (let e of t.c) e.e?.(o);
+ if (((o._ = t.n || r), (o.C = t), ie(t, o), t.c)) for (let e of t.c) e.g?.(o);
return o;
}
-function le(e, t) {
- let n = "string" == typeof e ? document.createElement(e) : e.f();
+function ie(e, t) {
+ let n = "string" == typeof e ? document.createElement(e) : e.l();
return (
- ne(11 === n.nodeType ? n.firstChild : n, e.l ?? " ", t),
+ ne(11 === n.nodeType ? n.firstChild : n, e.p ?? " ", t),
(t.a = 11 === n.nodeType ? n.firstChild : n),
(t.b = 11 === n.nodeType ? n.lastChild : n),
- e.m && e.m(t),
+ e.q && e.q(t),
n
);
}
-function ie(e, t, n) {
- return (r, l) => {
- let i = r[e + "("];
- if (!i || i === t || l === q) return;
+function le(e, t, n) {
+ return (r, i) => {
+ let l = r[e + "("];
+ if (!l || l === t || i === q) return;
let f = r[e + "!"];
- if (l === I || l === O) return i.d?.(f, l);
- if ("string" == typeof i) A(f, 0, l()), he(f, 0, t && o(r, t));
- else if (i.d) {
- let e = l();
- i.d(f, n ? e : [t ? { ...e, renderBody: o(r, t) } : e]);
+ if (i === j || i === O) return l.f?.(f, i);
+ if ("string" == typeof l) A(f, 0, i()), ge(f, 0, t && o(r, t));
+ else if (l.f) {
+ let e = i();
+ l.f(f, n ? e : [t ? { ...e, renderBody: o(r, t) } : e]);
}
};
}
-function fe(e, t, n, r, o = 0, l) {
+function fe(e, t, n, r, o = 0, i) {
return {
- n: e,
- l: t && te(t),
- m: n,
- f: ue,
+ r: e,
+ p: t && te(t),
+ q: n,
+ l: ue,
c: new Set(r),
- r: o,
- o: void 0,
- d: l,
- j: void 0,
+ D: o,
+ t: void 0,
+ f: i,
+ n: void 0,
};
}
function ue() {
- let e = this.o;
+ let e = this.t;
if (!e) {
- let t = this.l,
+ let t = this.p,
n = t && t.length < 4 && 32 !== t.charCodeAt(t.length - 1);
- this.o = e = (function (e, t) {
+ this.t = e = (function (e, t) {
let n;
ce.innerHTML = e;
let r = ce.content;
@@ -468,7 +469,7 @@ function ue() {
: n || (n = ae.createTextNode("")),
n
);
- })(this.n, n);
+ })(this.r, n);
}
return e.cloneNode(!0);
}
@@ -476,433 +477,462 @@ var ae = document,
ce = ae.createElement("template");
var se = function (e, t, r) {
let o = e + "(",
- l = e + "!";
- return (i, f) => {
+ i = e + "!";
+ return (l, f) => {
if (f === q) return;
- let c = i[o],
+ let c = l[o],
s = f;
- if (f !== I && f !== O) {
+ if (f !== j && f !== O) {
let r = f ? f._ || f.renderBody || f : void 0;
r !== c
- ? ((c = i[o] = r),
+ ? ((c = l[o] = r),
(function (e, t, r) {
let o,
- l = e[t + "!"];
+ i = e[t + "!"];
r
- ? ((o = e[t + "!"] = oe(r, e.$global, e)), (l = l || n(e[t])))
+ ? ((o = e[t + "!"] = oe(r, e.$global, e)), (i = i || n(e[t])))
: ((o = n(e[t])), (e[t + "!"] = void 0)),
- a(o, l.a.parentNode, l.a),
- u(l);
- })(i, e, r),
- t?.(i),
+ a(o, i.a.parentNode, i.a),
+ u(i);
+ })(l, e, r),
+ t?.(l),
(s = q))
: (s = O);
}
- r?.(i, s), K(c, i[l], s);
+ r?.(l, s), K(c, l[i], s);
};
};
function de(e, t) {
let n = t + "!",
r = t + "(";
return (t, o) => {
- let l = t[n];
- if (l) {
+ let i = t[n];
+ if (i) {
let n = t[r];
- (!n?.c || n.c.has(e)) && e(l, o);
+ (!n?.c || n.c.has(e)) && e(i, o);
}
};
}
-var ge = function (e, t, n) {
+var he = function (e, t, n) {
let r = e + "(",
o = e + "!";
- return (l, i) => {
- if (i === q) return;
- let f = l[r],
- u = i;
- if (i !== I && i !== O) {
- let n = i ? i._ || i.renderBody || i : void 0;
- n !== f ? ((f = l[r] = n), he(l, e, n), t?.(l), (u = q)) : (u = O);
+ return (i, l) => {
+ if (l === q) return;
+ let f = i[r],
+ u = l;
+ if (l !== j && l !== O) {
+ let n = l ? l._ || l.renderBody || l : void 0;
+ n !== f ? ((f = i[r] = n), ge(i, e, n), t?.(i), (u = q)) : (u = O);
}
- n?.(l, u), K(f, l[o], u);
+ n?.(i, u), K(f, i[o], u);
};
};
-function he(e, t, n) {
+function ge(e, t, n) {
let r = e[t + "!"],
o = e[t];
if (((o.textContent = ""), n)) {
a((e[t + "!"] = oe(n, e.$global, e)), o, null);
}
- r && i(r);
+ r && l(r);
}
var pe = new Map([[Symbol(), n(void 0)]]),
be = [n(void 0)],
ve = new Map(),
- me = [];
-function ye(e, t) {
- return Se(e, t, (e, t) => {
- let [n, r = xe] = e,
+ we = [];
+function me(e, t) {
+ return xe(e, t, (e, t) => {
+ let [n, r = Se] = e,
o = 0;
for (let e of n) t(r(e, o), [e, o, n]), o++;
});
}
-function we(e, t) {
- return Se(e, t, (e, t) => {
- let [n, r = $e] = e;
+function ye(e, t) {
+ return xe(e, t, (e, t) => {
+ let [n, r = Ne] = e;
for (let e in n) {
let o = n[e];
t(r(e, o), [e, o, n]);
}
});
}
-function Ne(e, t) {
- return Se(e, t, (e, t) => {
- let [n, r = 0, o = 1, l = $e] = e,
- i = (n - r) / o;
- for (let e = 0; e <= i; e++) {
+function Ce(e, t) {
+ return xe(e, t, (e, t) => {
+ let [n, r = 0, o = 1, i = Ne] = e,
+ l = (n - r) / o;
+ for (let e = 0; e <= l; e++) {
let n = r + e * o;
- t(l(n), [n]);
+ t(i(n), [n]);
}
});
}
-function Se(e, t, r) {
+function xe(e, t, r) {
let o = e + "!",
- l = t.c,
- f = t.d;
+ i = t.c,
+ f = t.f;
return (s, d) => {
if (d === q) return;
- if (d === I || d === O) {
+ if (d === j || d === O) {
for (let t of s[o] ?? s[e + "("].values()) {
f?.(t, d);
- for (let e of l) e(t, d);
+ for (let e of i) e(t, d);
}
return;
}
- let g,
- h,
+ let h,
+ g,
p,
b,
v = s[e],
- m = 8 === v.nodeType || 3 === v.nodeType,
- y = s[e + "("] || (m ? pe : ve),
- w = s[e + "!"] || Array.from(y.values()),
- N = !0;
+ w = 8 === v.nodeType || 3 === v.nodeType,
+ m = s[e + "("] || (w ? pe : ve),
+ y = s[e + "!"] || Array.from(m.values()),
+ C = !0;
if (
(r(d, (e, n) => {
- let r = y.get(e),
+ let r = m.get(e),
o = O;
- if ((r || ((r = oe(t, s.$global, s)), (o = q)), f && f(r, n), l))
- for (let e of l) e(r, o);
- g ? (g.set(e, r), h.push(r)) : ((g = new Map([[e, r]])), (h = [r]));
+ if ((r || ((r = oe(t, s.$global, s)), (o = q)), f && f(r, n), i))
+ for (let e of i) e(r, o);
+ h ? (h.set(e, r), g.push(r)) : ((h = new Map([[e, r]])), (g = [r]));
}),
- !g)
+ !h)
)
- if (m) (g = pe), (h = be), n(v);
+ if (w) (h = pe), (g = be), n(v);
else {
- if (t.r) for (let e = 0; e < w.length; e++) i(w[e]);
- (v.textContent = ""), (g = ve), (h = me), (N = !1);
+ if (t.D) for (let e = 0; e < y.length; e++) l(y[e]);
+ (v.textContent = ""), (h = ve), (g = we), (C = !1);
}
- if (N) {
- if (m) {
- y === pe && n(v);
- let e = w[w.length - 1];
+ if (C) {
+ if (w) {
+ m === pe && n(v);
+ let e = y[y.length - 1];
(p = e.b.nextSibling), (b = e.a.parentNode);
} else (p = null), (b = v);
!(function (e, t, n, r) {
let o,
- l,
i,
+ l,
f,
s,
d,
- g = 0,
h = 0,
+ g = 0,
p = t.length - 1,
b = n.length - 1,
- v = t[g],
- m = n[h],
- y = t[p],
- w = n[b];
+ v = t[h],
+ w = n[g],
+ m = t[p],
+ y = n[b];
e: {
- for (; v === m; ) {
- if ((++g, ++h, g > p || h > b)) break e;
- (v = t[g]), (m = n[h]);
+ for (; v === w; ) {
+ if ((++h, ++g, h > p || g > b)) break e;
+ (v = t[h]), (w = n[g]);
}
- for (; y === w; ) {
- if ((--p, --b, g > p || h > b)) break e;
- (y = t[p]), (w = n[b]);
+ for (; m === y; ) {
+ if ((--p, --b, h > p || g > b)) break e;
+ (m = t[p]), (y = n[b]);
}
}
- if (g > p) {
- if (h <= b) {
- (i = b + 1), (f = i < n.length ? n[i].a : r);
+ if (h > p) {
+ if (g <= b) {
+ (l = b + 1), (f = l < n.length ? n[l].a : r);
do {
- a(n[h++], e, f);
- } while (h <= b);
+ a(n[g++], e, f);
+ } while (g <= b);
}
- } else if (h > b)
+ } else if (g > b)
do {
- u(t[g++]);
- } while (g <= p);
+ u(t[h++]);
+ } while (h <= p);
else {
- let v = p - g + 1,
- m = b - h + 1,
- y = t,
- w = new Array(m);
- for (o = 0; o < m; ++o) w[o] = -1;
- let N = 0,
- S = 0,
- C = new Map();
- for (l = h; l <= b; ++l) C.set(n[l], l);
- for (o = g; o <= p && S < m; ++o)
+ let v = p - h + 1,
+ w = b - g + 1,
+ m = t,
+ y = new Array(w);
+ for (o = 0; o < w; ++o) y[o] = -1;
+ let C = 0,
+ x = 0,
+ $ = new Map();
+ for (i = g; i <= b; ++i) $.set(n[i], i);
+ for (o = h; o <= p && x < w; ++o)
(s = t[o]),
- (l = C.get(s)),
- void 0 !== l &&
- ((N = N > l ? c : l),
- ++S,
- (d = n[l]),
- (w[l - h] = o),
- (y[o] = null));
- if (v === t.length && 0 === S) {
- for (; h < m; ++h) a(n[h], e, r);
- for (; g < v; ++g) u(t[g]);
+ (i = $.get(s)),
+ void 0 !== i &&
+ ((C = C > i ? c : i),
+ ++x,
+ (d = n[i]),
+ (y[i - g] = o),
+ (m[o] = null));
+ if (v === t.length && 0 === x) {
+ for (; g < w; ++g) a(n[g], e, r);
+ for (; h < v; ++h) u(t[h]);
} else {
- for (o = v - S; o > 0; ) (s = y[g++]), null !== s && (u(s), o--);
- if (N === c) {
+ for (o = v - x; o > 0; ) (s = m[h++]), null !== s && (u(s), o--);
+ if (C === c) {
let t = (function (e) {
let t,
n,
r = e.slice(),
o = [];
o.push(0);
- for (let l = 0, i = e.length; l < i; ++l) {
- if (-1 === e[l]) continue;
- let i = o[o.length - 1];
- if (e[i] < e[l]) (r[l] = i), o.push(l);
+ for (let i = 0, l = e.length; i < l; ++i) {
+ if (-1 === e[i]) continue;
+ let l = o[o.length - 1];
+ if (e[l] < e[i]) (r[i] = l), o.push(i);
else {
for (t = 0, n = o.length - 1; t < n; ) {
let r = ((t + n) / 2) | 0;
- e[o[r]] < e[l] ? (t = r + 1) : (n = r);
+ e[o[r]] < e[i] ? (t = r + 1) : (n = r);
}
- e[l] < e[o[t]] && (t > 0 && (r[l] = o[t - 1]), (o[t] = l));
+ e[i] < e[o[t]] && (t > 0 && (r[i] = o[t - 1]), (o[t] = i));
}
}
for (t = o.length, n = o[t - 1]; t-- > 0; )
(o[t] = n), (n = r[n]);
return o;
- })(w);
- for (l = t.length - 1, i = n.length, o = m - 1; o >= 0; --o)
- -1 === w[o] || l < 0 || o !== t[l]
- ? ((N = o + h),
- (d = n[N++]),
- (f = N < i ? n[N].a : r),
+ })(y);
+ for (i = t.length - 1, l = n.length, o = w - 1; o >= 0; --o)
+ -1 === y[o] || i < 0 || o !== t[i]
+ ? ((C = o + g),
+ (d = n[C++]),
+ (f = C < l ? n[C].a : r),
a(d, e, f))
- : --l;
- } else if (S !== m)
- for (i = n.length, o = m - 1; o >= 0; --o)
- -1 === w[o] &&
- ((N = o + h),
- (d = n[N++]),
- (f = N < i ? n[N].a : r),
+ : --i;
+ } else if (x !== w)
+ for (l = n.length, o = w - 1; o >= 0; --o)
+ -1 === y[o] &&
+ ((C = o + g),
+ (d = n[C++]),
+ (f = C < l ? n[C].a : r),
a(d, e, f));
}
}
- })(b, w, h, p);
+ })(b, y, g, p);
}
- (s[e + "("] = g), (s[e + "!"] = h);
+ (s[e + "("] = h), (s[e + "!"] = g);
};
}
-function Ce(e, t) {
+function $e(e, t) {
let n = t + "!";
return (r, o) => {
- let l = r[n] ?? r[t + "("]?.values() ?? [];
- for (let t of l) e(t, o);
+ let i = r[n] ?? r[t + "("]?.values() ?? [];
+ for (let t of i) e(t, o);
};
}
-function xe(e, t) {
+function Se(e, t) {
return t;
}
-function $e(e) {
+function Ne(e) {
return e;
}
-var ke = {},
- Me = document;
-function Ae(e, t) {
- return (ke[e] = t), t;
-}
-function Te(e, t) {
- return (ke[e] = (e) => (n) => t(e, n)), t;
-}
-function Ee(e, t) {
- return (ke[e] = (e) => o(e, t)), t;
-}
-var Be = {};
-function _e(e = "M") {
- let t,
- n,
- r = e.length,
- o = e + "$h",
- l = window[o],
- i = Me.createTreeWalker(Me, 128),
- f = (e) => Be[e] ?? (Be[e] = {}),
- u = [],
- a = { push: s },
- c = { _: ke };
- if (l) for (let e = 0; e < l.length; e += 2) s(l[e], l[e + 1]);
- else window[o] = a;
- function s(o, l) {
- "loading" !== Me.readyState && (i.currentNode = Me);
- let a = o(c);
- if (a) {
- Be.$global ||= a.$global || {};
- for (let e in a) {
- if ("$global" === e) continue;
- let t = parseInt(e),
- n = a[t],
- r = Be[t];
- (n.$global = a.$global), r !== n && (Be[t] = Object.assign(n, r));
- }
+var Me = {},
+ ke = class {
+ u = [];
+ x = {};
+ E = { _: Me };
+ constructor(e, t, n) {
+ (this.F = e), (this.G = t), (this.y = n), (this.z = e[n]), this.A();
}
- for (; (n = i.nextNode()); ) {
- let o = n.nodeValue;
- if (o.startsWith(e)) {
- let e = o[r],
- l = parseInt(o.slice(r + 1)),
- i = f(l),
- a = o.slice(o.indexOf(" ") + 1);
- if ("*" === e) i[a] = n.previousSibling;
- else if ("[" === e) u.push(t), (t = l), (i.a = n);
- else if ("]" === e) {
- if (((i[a] = n), l < t)) {
- let e = Be[t],
- r = n.parentNode,
- o = e.a;
- r !== o.parentNode && r.prepend(o),
- (e.b = n.previousSibling),
- (t = u.pop());
- }
- } else if ("|" === e) {
- i[parseInt(a)] = n;
- let e = JSON.parse("[" + a.slice(a.indexOf(" ") + 1) + "]");
- for (let t = e.length - 1; t >= 0; t--) {
- let r = f(e[t]);
- for (; 8 === (n = n.previousSibling).nodeType; );
- r.a = r.b = n;
+ w() {
+ this.z.w(), this.A();
+ }
+ A() {
+ let e = this.z,
+ t = this.E,
+ n = this.x,
+ r = e.v;
+ if (r.length) {
+ let t = e.i.length;
+ e.v = [];
+ for (let e of r) {
+ let r = e.data,
+ o = r[t],
+ i = parseInt(r.slice(t + 1)),
+ l = (n[i] ??= {}),
+ f = r.slice(r.indexOf(" ") + 1);
+ if ("*" === o) l[f] = e.previousSibling;
+ else if ("[" === o) this.u.push(this.h), (this.h = i), (l.a = e);
+ else if ("]" === o) {
+ if (((l[f] = e), i < this.h)) {
+ let t = n[this.h],
+ r = e.parentNode,
+ o = t.a;
+ r !== o.parentNode && r.prepend(o),
+ (t.b = e.previousSibling),
+ (this.h = this.u.pop());
+ }
+ } else if ("|" === o) {
+ l[parseInt(f)] = e;
+ let t = JSON.parse("[" + f.slice(f.indexOf(" ") + 1) + "]"),
+ r = e;
+ for (let e = t.length - 1; e >= 0; e--) {
+ let o = (n[t[e]] ??= {});
+ for (; 8 === (r = r.previousSibling).nodeType; );
+ o.a = o.b = r;
+ }
}
}
}
+ let o = e.s;
+ if (o) {
+ e.s = [];
+ for (let e of o) {
+ let r = e(t),
+ { $global: o } = n;
+ o ||
+ ((n.$global = o = r.$ || {}),
+ (o.runtimeId = this.G),
+ (o.renderId = this.y));
+ for (let e in r)
+ if ("$" !== e) {
+ let t = r[e],
+ i = n[e];
+ (t.$global = o), i !== t && (n[e] = Object.assign(t, i));
+ }
+ }
+ }
+ let i = e.e;
+ if (i) {
+ e.e = [];
+ for (let e = 0; e < i.length; e += 2) Me[i[e + 1]](n[i[e]]);
+ }
+ e.d && delete this.F[this.y];
}
- for (let e = 0; e < l.length; e += 2) ke[l[e + 1]](Be[l[e]]);
+ };
+function Ae(e, t) {
+ return (Me[e] = t), t;
+}
+function _e(e, t) {
+ return (Me[e] = (e) => (n) => t(e, n)), t;
+}
+function Ee(e, t) {
+ return (Me[e] = (e) => o(e, t)), t;
+}
+function Be(e = "M") {
+ let t,
+ n = (r) => (n[r] = t[r] = new ke(t, e, r));
+ function r(r) {
+ t = r;
+ for (let e in r) n(e);
+ Object.defineProperty(window, e, { configurable: !0, value: n });
}
+ window[e]
+ ? r(window[e])
+ : Object.defineProperty(window, e, { configurable: !0, set: r });
}
-function je(e, t) {
- return Ae(e, t.e), t;
+function Ie(e, t) {
+ return Ae(e, t.g), t;
}
-var Ie,
- Oe = (() => {
+var Te,
+ je = (() => {
let { port1: e, port2: t } = new MessageChannel();
return (
(e.onmessage = () => {
- (Ie = !1), ze();
+ (Te = !1), We();
}),
t
);
})();
+function Oe() {
+ We(), requestAnimationFrame(qe);
+}
function qe() {
- ze(), requestAnimationFrame(We);
+ je.postMessage(0);
}
-function We() {
- Oe.postMessage(0);
-}
-var Le = [],
- Re = [];
+var Re = [],
+ ze = [];
function De(e, t, n, r) {
return n ? (n(r), r) : Fe(e, t, r);
}
function Fe(e, t, n) {
- return Ie || ((Ie = !0), queueMicrotask(qe)), t(e, I), Le.push(e, t, n), n;
+ return Te || ((Te = !0), queueMicrotask(Oe)), t(e, j), Re.push(e, t, n), n;
}
-function He(e, t) {
- Re.push(e, t);
+function Le(e, t) {
+ ze.push(e, t);
}
-function ze() {
+function We() {
try {
- Ve();
- } finally {
- Le = [];
- }
- try {
- Ue();
+ He();
} finally {
Re = [];
}
-}
-function Je(e) {
- let t = Le,
- n = Re,
- r = (Re = []);
- Le = [];
try {
- e(), Ve();
+ Ge();
} finally {
- (Le = t), (Re = n);
+ ze = [];
+ }
+}
+function Pe(e) {
+ let t = Re,
+ n = ze,
+ r = (ze = []);
+ Re = [];
+ try {
+ e(), He();
+ } finally {
+ (Re = t), (ze = n);
}
return r;
}
-function Ue(e = Re) {
+function Ge(e = ze) {
for (let t = 0; t < e.length; t += 2) {
let n = e[t];
(0, e[t + 1])(n);
}
}
-function Ve() {
- for (let e = 0; e < Le.length; e += 3) {
- let t = Le[e + 0];
- (0, Le[e + 1])(t, Le[e + 2]);
+function He() {
+ for (let e = 0; e < Re.length; e += 3) {
+ let t = Re[e + 0];
+ (0, Re[e + 1])(t, Re[e + 2]);
}
}
-var Ze = (e, t) => Ae(t, new Ge(e)),
- Ge = class {
+var Je = (e, t) => Ae(t, new Ue(e)),
+ Ue = class {
_;
constructor(e) {
this._ = e;
}
mount(t = {}, n, r) {
let o,
- l,
- { $global: i = {}, ...f } = t,
- a = this._.d,
- c = Je(() => {
- (o = e(i)), (l = le(this._, o)), a && a(o, [f]);
+ i,
+ { $global: l } = t;
+ l
+ ? (({ $global: l, ...t } = t),
+ (l = { runtimeId: "M", renderId: "_", ...l }))
+ : (l = { runtimeId: "M", renderId: "_" });
+ let f = this._.f,
+ a = Pe(() => {
+ (o = e(l)), (i = ie(this._, o)), f && f(o, [t]);
});
switch (r) {
case "afterbegin":
- n.insertBefore(l, n.firstChild);
+ n.insertBefore(i, n.firstChild);
break;
case "afterend":
- n.parentElement.insertBefore(l, n.nextSibling);
+ n.parentElement.insertBefore(i, n.nextSibling);
break;
case "beforebegin":
- n.parentElement.insertBefore(l, n);
+ n.parentElement.insertBefore(i, n);
break;
default:
- n.appendChild(l);
+ n.appendChild(i);
}
return (
- Ue(c),
+ Ge(a),
{
update: (e) => {
- a &&
+ f &&
(function (e) {
- let t = Le,
- n = Re;
- (Le = []), (Re = []);
+ let t = Re,
+ n = ze;
+ (Re = []), (ze = []);
try {
- e(), Ve(), (Le = t), Ue();
+ e(), He(), (Re = t), Ge();
} finally {
- (Le = t), (Re = n);
+ (Re = t), (ze = n);
}
})(() => {
- a(o, I), a(o, [e]);
+ f(o, j), f(o, [e]);
});
},
destroy: () => {
@@ -912,108 +942,111 @@ var Ze = (e, t) => Ae(t, new Ge(e)),
);
}
},
+ Ze = new Map(),
Ke = {
- register: Ae,
patchConditionals: function (e) {
- (se = e(se)), (ge = e(ge));
+ (se = e(se)), (he = e(he));
},
- queueEffect: He,
- isOp: (e) => e === I || e === O || e === q,
- isRenderer: (e) => void 0 !== e.f,
+ queueEffect: Le,
+ init() {
+ Ae("$C_s", (e) => {
+ Ze.set(e.m5c, e);
+ });
+ },
+ registerRenderer(e) {
+ Ae("$C_r", e);
+ },
+ isOp: (e) => e === j || e === O || e === q,
+ isRenderer: (e) => void 0 !== e.l,
getStartNode: (e) => e.a,
setScopeNodes(e, t, n) {
(e.a = t), (e.b = n);
},
runComponentEffects() {
- Ue(this.effects);
- },
- resolveRenderer(e) {
- if (e && "object" == typeof e) {
- if (Array.isArray(e)) {
- let [t, n] = e;
- return (function (e, t) {
- let n = ke[e];
- return t && n ? (n.n ? o(t, n) : n(t)) : n;
- })(t, Be[n]);
- }
- if (e.f) return e;
- }
+ Ge(this.effects);
},
+ resolveRegistered: (e, { runtimeId: t, componentIdPrefix: n }) =>
+ Array.isArray(e) && "string" == typeof e[0]
+ ? (function (e, t) {
+ let n = Me[e];
+ return t && n ? (n.r ? o(t, n) : n(t)) : n;
+ })(e[0], 2 === e.length && window[t]?.["s" === n ? "_" : n]?.x[e[1]])
+ : e,
createRenderer(e, t, n) {
let r = fe("", void 0, e, void 0, 1, n);
- return (r.f = t), r;
+ return (r.l = t), r;
},
- render(e, t, n, r, o) {
- let l = r.d || Pe,
- i = !1,
- f = e ? (n.scope = Be[t.global.componentIdToScopeId[n.id]]) : n.scope;
+ render(e, t, n, r) {
+ let o = t.scope;
+ o || ((o = Ze.get(t.id)), o && ((t.scope = o), Ze.delete(t.id)));
+ let i = n.f || Qe,
+ l = !1;
if (
- ((n.effects = Je(() => {
- if (f) l(f, I), (i = !0);
+ ((t.effects = Pe(() => {
+ if (o) i(o, j), (l = !0);
else {
- f = n.scope = oe(r, t.global);
- let e = r.c;
- if (e) for (let t of e) t(n.scope, O);
+ o = t.scope = oe(n, e.global);
+ let r = n.c;
+ if (r) for (let e of r) e(t.scope, O);
}
- l(f, o);
+ i(o, r);
})),
- !i)
+ !l)
)
- return f.a === f.b ? f.a : f.a.parentNode;
+ return o.a === o.b ? o.a : o.a.parentNode;
},
};
-function Pe() {}
+function Qe() {}
export {
- C as attr,
+ $ as attr,
A as attrs,
- T as attrsEvents,
- l as bindFunction,
+ _ as attrsEvents,
+ i as bindFunction,
o as bindRenderer,
- L as changeHandler,
- U as childClosures,
- $ as classAttr,
- z as closure,
+ z as changeHandler,
+ H as childClosures,
+ N as classAttr,
+ P as closure,
Ke as compat,
se as conditional,
- ge as conditionalOnlyChild,
+ he as conditionalOnlyChild,
fe as createRenderer,
e as createScope,
oe as createScopeWithRenderer,
- Ze as createTemplate,
- M as data,
- J as dynamicClosure,
- V as dynamicSubscribers,
- ie as dynamicTagAttrs,
+ Je as createTemplate,
+ k as data,
+ G as dynamicClosure,
+ J as dynamicSubscribers,
+ le as dynamicTagAttrs,
b as getAbortSignal,
B as html,
X as inChild,
de as inConditionalScope,
- Ce as inLoopScope,
- _e as init,
- W as initValue,
- F as intersection,
+ $e as inLoopScope,
+ Be as init,
+ R as initValue,
+ L as intersection,
Y as intersections,
- j as lifecycle,
- we as loopIn,
- ye as loopOf,
- Ne as loopTo,
- Q as nextTagId,
- w as on,
- Je as prepare,
- _ as props,
+ T as lifecycle,
+ ye as loopIn,
+ me as loopOf,
+ Ce as loopTo,
+ V as nextTagId,
+ y as on,
+ Pe as prepare,
+ I as props,
De as queueControllableSource,
- He as queueEffect,
+ Le as queueEffect,
Fe as queueSource,
Ae as register,
- Te as registerBoundSignal,
+ _e as registerBoundSignal,
Ee as registerRenderer,
- je as registerSubscriber,
+ Ie as registerSubscriber,
p as resetAbortSignal,
- ze as run,
- Ue as runEffects,
- Be as scopeLookup,
- Z as setTagVar,
- k as styleAttr,
- G as tagVarSignal,
- R as value,
+ We as run,
+ Ge as runEffects,
+ U as setTagVar,
+ M as styleAttr,
+ Z as tagVarSignal,
+ D as value,
};
diff --git a/packages/marko/src/node_modules/@internal/components-entry/index.js b/packages/marko/src/node_modules/@internal/components-entry/index.js
index c570743e9..92088521c 100644
--- a/packages/marko/src/node_modules/@internal/components-entry/index.js
+++ b/packages/marko/src/node_modules/@internal/components-entry/index.js
@@ -1,7 +1,7 @@
"use strict";
var warp10 = require("warp10");
-var w10NOOP = require("warp10/constants").NOOP;
+var w10ToJSON = require("../../../runtime/helpers/serialize-noop").___toJSON;
var safeJSONRegExp = /<\/|\u2028|\u2029/g;
var IGNORE_GLOBAL_TYPES = new Set(["undefined", "function", "symbol"]);
var DEFAULT_RUNTIME_ID = "M";
@@ -136,7 +136,7 @@ function addComponentsFromContext(componentsContext, componentsToHydrate) {
}
}
- if (typeof renderBody === "function" && renderBody.toJSON && renderBody.toJSON() === w10NOOP) {
+ if (typeof renderBody === "function" && renderBody.toJSON === w10ToJSON) {
flags |= FLAG_HAS_RENDER_BODY;
renderBody = input.renderBody = undefined;
}
diff --git a/packages/marko/src/node_modules/@internal/components-registry/index-browser.js b/packages/marko/src/node_modules/@internal/components-registry/index-browser.js
index 38b455df6..5dcc9659d 100644
--- a/packages/marko/src/node_modules/@internal/components-registry/index-browser.js
+++ b/packages/marko/src/node_modules/@internal/components-registry/index-browser.js
@@ -449,7 +449,7 @@ function initServerRendered(renderedComponents, host) {
}
}
- var prefix = renderedComponents.p || "";
+ var prefix = renderedComponents.p || "s";
var meta = serverRenderedMeta[prefix];
var isLast = renderedComponents.l;
@@ -470,8 +470,11 @@ function initServerRendered(renderedComponents, host) {
indexServerComponentBoundaries(host, runtimeId);
eventDelegation.___init(host);
- if (renderedComponents.g) {
- meta.___globals = renderedComponents.g;
+ if (!meta.___globals) {
+ meta.___globals = Object.assign({
+ runtimeId: runtimeId,
+ componentIdPrefix: prefix,
+ }, renderedComponents.g);
}
if (renderedComponents.t) {
diff --git a/packages/marko/src/runtime/components/ComponentDef.js b/packages/marko/src/runtime/components/ComponentDef.js
index 53b22e608..10dc06073 100644
--- a/packages/marko/src/runtime/components/ComponentDef.js
+++ b/packages/marko/src/runtime/components/ComponentDef.js
@@ -1,8 +1,8 @@
"use strict";
var complain = "MARKO_DEBUG" && require("complain");
var extend = require("raptor-util/extend");
-var w10Noop = require("warp10/constants").NOOP;
var componentUtil = require("@internal/components-util");
+var w10NOOP = require("../helpers/serialize-noop").___noop;
var attachBubblingEvent = componentUtil.___attachBubblingEvent;
var addDelegatedEventHandler =
require("./event-delegation").___addDelegatedEventHandler;
@@ -98,7 +98,7 @@ ComponentDef.___deserialize = function (o, types, global, registry) {
var componentProps = extra.w || EMPTY_OBJECT;
var flags = extra.f;
var isLegacy = flags & FLAG_IS_LEGACY;
- var renderBody = flags & FLAG_HAS_RENDER_BODY ? w10Noop : extra.r;
+ var renderBody = flags & FLAG_HAS_RENDER_BODY ? w10NOOP : extra.r;
var component =
typeName /* legacy */ &&
@@ -107,6 +107,7 @@ ComponentDef.___deserialize = function (o, types, global, registry) {
// Prevent newly created component from being queued for update since we area
// just building it from the server info
component.___updateQueued = true;
+ component.___global = global;
if (isLegacy) {
component.widgetConfig = componentProps;
@@ -156,8 +157,6 @@ ComponentDef.___deserialize = function (o, types, global, registry) {
component.___setCustomEvents(customEvents, scope);
}
- component.___global = global;
-
return {
id: id,
___component: component,
diff --git a/packages/marko/src/runtime/helpers/dynamic-tag.js b/packages/marko/src/runtime/helpers/dynamic-tag.js
index cce2a480a..78ac15a07 100644
--- a/packages/marko/src/runtime/helpers/dynamic-tag.js
+++ b/packages/marko/src/runtime/helpers/dynamic-tag.js
@@ -1,14 +1,13 @@
"use strict";
var complain = "MARKO_DEBUG" && require("complain");
-var w10NOOP = require("warp10/constants").NOOP;
var ComponentDef = require("../components/ComponentDef");
var ComponentsContext = require("../components/ComponentsContext");
+var serializeNOOP = require("../helpers/serialize-noop");
+var w10NOOP = serializeNOOP.___noop;
+var w10ToJSON = serializeNOOP.___toJSON;
var changeCase = require("./_change-case");
var getComponentsContext = ComponentsContext.___getComponentsContext;
-var RENDER_BODY_TO_JSON = function () {
- return w10NOOP;
-};
var FLAG_WILL_RERENDER_IN_BROWSER = 1;
// var FLAG_HAS_RENDER_BODY = 2;
@@ -80,7 +79,12 @@ module.exports = function dynamicTag(
}
if (dynamicTag.___runtimeCompat) {
- renderer = dynamicTag.___runtimeCompat(renderer, render, args);
+ renderer = dynamicTag.___runtimeCompat(
+ renderer,
+ render,
+ args,
+ out.global,
+ );
}
if (renderer) {
@@ -113,7 +117,7 @@ module.exports = function dynamicTag(
parentComponentDef.id + "-" + parentComponentDef.___nextKey(key),
globalContext,
);
- render.toJSON = RENDER_BODY_TO_JSON;
+ render.toJSON = w10ToJSON;
if (args) {
render.apply(null, [out].concat(args, attrs));
diff --git a/packages/marko/src/runtime/helpers/serialize-noop.js b/packages/marko/src/runtime/helpers/serialize-noop.js
new file mode 100644
index 000000000..038ae4c9c
--- /dev/null
+++ b/packages/marko/src/runtime/helpers/serialize-noop.js
@@ -0,0 +1,5 @@
+var w10NOOP = require("warp10/constants").NOOP;
+exports.___noop = w10NOOP;
+exports.___toJSON = function () {
+ return w10NOOP;
+};
diff --git a/packages/marko/src/runtime/helpers/tags-compat/html-debug.js b/packages/marko/src/runtime/helpers/tags-compat/html-debug.js
index 4eebcfdd4..d1a258f1e 100644
--- a/packages/marko/src/runtime/helpers/tags-compat/html-debug.js
+++ b/packages/marko/src/runtime/helpers/tags-compat/html-debug.js
@@ -1,3 +1,3 @@
exports.s = require("./runtime-html.js").p(
- require("@marko/runtime-tags/debug/html"),
+ require("@marko/runtime-tags/debug/html").compat,
);
diff --git a/packages/marko/src/runtime/helpers/tags-compat/html-debug.mjs b/packages/marko/src/runtime/helpers/tags-compat/html-debug.mjs
index 3ccf53eea..45e7094a1 100644
--- a/packages/marko/src/runtime/helpers/tags-compat/html-debug.mjs
+++ b/packages/marko/src/runtime/helpers/tags-compat/html-debug.mjs
@@ -1,3 +1,3 @@
-import * as api from "@marko/runtime-tags/debug/html";
+import { compat } from "@marko/runtime-tags/debug/html";
import { p } from "./runtime-html.js";
-export const s = p(api);
+export const s = p(compat);
diff --git a/packages/marko/src/runtime/helpers/tags-compat/html.js b/packages/marko/src/runtime/helpers/tags-compat/html.js
index eb9e70286..408749b04 100644
--- a/packages/marko/src/runtime/helpers/tags-compat/html.js
+++ b/packages/marko/src/runtime/helpers/tags-compat/html.js
@@ -1 +1,3 @@
-exports.s = require("./runtime-html.js").p(require("@marko/runtime-tags/html"));
+exports.s = require("./runtime-html.js").p(
+ require("@marko/runtime-tags/html").compat,
+);
diff --git a/packages/marko/src/runtime/helpers/tags-compat/html.mjs b/packages/marko/src/runtime/helpers/tags-compat/html.mjs
index d7f537e97..5a1bd3ccf 100644
--- a/packages/marko/src/runtime/helpers/tags-compat/html.mjs
+++ b/packages/marko/src/runtime/helpers/tags-compat/html.mjs
@@ -1,3 +1,3 @@
-import * as api from "@marko/runtime-tags/html";
+import { compat } from "@marko/runtime-tags/html";
import { p } from "./runtime-html.js";
-export const s = p(api);
+export const s = p(compat);
diff --git a/packages/marko/src/runtime/helpers/tags-compat/runtime-dom.js b/packages/marko/src/runtime/helpers/tags-compat/runtime-dom.js
index a39d6d4cc..6d99da883 100644
--- a/packages/marko/src/runtime/helpers/tags-compat/runtime-dom.js
+++ b/packages/marko/src/runtime/helpers/tags-compat/runtime-dom.js
@@ -15,12 +15,17 @@ exports.p = function (domCompat) {
renderer,
renderBody,
args,
+ global,
) {
- const tagsRenderer = domCompat.resolveRenderer(renderer || renderBody);
+ const tagsRenderer = domCompat.resolveRegistered(
+ renderer || renderBody,
+ global,
+ );
- if (tagsRenderer) {
- return (input, out) =>
- TagsCompat({ i: args ? args : input, r: tagsRenderer }, out);
+ if (tagsRenderer && domCompat.isRenderer(tagsRenderer)) {
+ return (input, out) => {
+ return TagsCompat({ i: args ? args : input, r: tagsRenderer }, out);
+ };
}
return renderer;
@@ -29,17 +34,9 @@ exports.p = function (domCompat) {
const TagsCompatId = "tags-compat";
const TagsCompat = createRenderer(
function (_, out, componentDef, component) {
- const isHydrate =
- ___getComponentsContext(out).___globalContext.___isHydrate;
const input = Array.isArray(_.i) ? _.i : [_.i];
- const tagsRenderer = domCompat.resolveRenderer(_.r);
- const newNode = domCompat.render(
- isHydrate,
- out,
- component,
- tagsRenderer,
- input,
- );
+ const tagsRenderer = domCompat.resolveRegistered(_.r, global);
+ const newNode = domCompat.render(out, component, tagsRenderer, input);
out.bf(out.___assignedKey, component, !newNode);
if (newNode) {
@@ -111,8 +108,12 @@ exports.p = function (domCompat) {
customEvents,
scopeId,
) {
+ const global = this.___global;
for (const customEvent of customEvents) {
- customEvent[1] = domCompat.resolveRenderer(customEvent[1]);
+ customEvent[1] = domCompat.resolveRegistered(
+ customEvent[1],
+ global,
+ );
}
setCustomEvents.call(this, customEvents, scopeId);
@@ -141,19 +142,21 @@ exports.p = function (domCompat) {
return newRenderer;
}
- domCompat.register("@marko/tags-compat-5-to-6", create5to6Renderer);
+ domCompat.registerRenderer(create5to6Renderer);
+ domCompat.init();
function renderAndMorph(scope, renderer, renderBody, input) {
const out = defaultCreateOut();
let host = domCompat.getStartNode(scope);
let rootNode = host.fragment;
if (!rootNode) {
- const component = (scope.marko5Component = ___componentLookup[scope.m5c]);
+ const component = (scope.___marko5Component =
+ ___componentLookup[scope.m5c]);
rootNode = component.___rootNode;
host = rootNode.startNode;
domCompat.setScopeNodes(host, rootNode.endNode);
}
- const existingComponent = scope.marko5Component;
+ const existingComponent = scope.___marko5Component;
const componentsContext = ___getComponentsContext(out);
const globalComponentsContext = componentsContext.___globalContext;
let customEvents;
@@ -191,7 +194,7 @@ exports.p = function (domCompat) {
component.___rootNode = rootNode;
component.___input = input[0];
component.___customEvents = customEvents;
- scope.marko5Component = component;
+ scope.___marko5Component = component;
});
}
diff --git a/packages/marko/src/runtime/helpers/tags-compat/runtime-html.js b/packages/marko/src/runtime/helpers/tags-compat/runtime-html.js
index 7b131c3ef..171669ec6 100644
--- a/packages/marko/src/runtime/helpers/tags-compat/runtime-html.js
+++ b/packages/marko/src/runtime/helpers/tags-compat/runtime-html.js
@@ -6,37 +6,28 @@ const createRenderer = require("../../components/renderer");
const defaultCreateOut = require("../../createOut");
const dynamicTag5 = require("../dynamic-tag");
-exports.p = function (tagsAPI) {
- const {
- patchDynamicTag,
- createRenderFn,
- fork,
- write,
- serializerRegister,
- writeScope,
- nextScopeId,
- getRegistryInfo,
- } = tagsAPI;
- const FN_TO_JSON = function () {
- // TODO: this should instead return an object that contains getRegistryInfo
- // then in the dom-compat, handle that object to lookup the function in the registry
- // (we also need to do this for events)
- return getRegistryInfo(this);
- };
-
+exports.p = function (htmlCompat) {
const isMarko6 = (fn) => !!fn.___isTagsAPI;
const isMarko5 = (fn) => !fn.___isTagsAPI;
+ const writeHTML = (result) => {
+ const state = result.out._state;
+ const writer = state.writer;
+ state.events.emit("___toString", writer);
+ htmlCompat.writeScript(writer._scripts);
+ htmlCompat.write(writer._content);
+ };
dynamicTag5.___runtimeCompat = function tagsToVdom(
tagsRenderer,
renderBody,
args,
) {
- if (tagsRenderer ? isMarko5(tagsRenderer) : isMarko5(renderBody))
+ if (tagsRenderer ? isMarko5(tagsRenderer) : isMarko5(renderBody)) {
return tagsRenderer;
+ }
if (!tagsRenderer && renderBody) {
- renderBody.toJSON = FN_TO_JSON;
+ renderBody.toJSON = htmlCompat.toJSON;
}
return (input, out) =>
@@ -53,29 +44,9 @@ exports.p = function (tagsAPI) {
function (_, out, componentDef, component) {
const input = _.i;
const tagsRenderer = _.r;
- const renderFn = createRenderFn(tagsRenderer);
const willRerender = componentDef._wrr;
- const $global = out.global;
- const streamData = ($global.streamData = $global.streamData || {});
-
- if (willRerender) {
- $global.serializedGlobals = $global.serializedGlobals || {};
- $global.serializedGlobals.componentIdToScopeId = true;
- $global.componentIdToScopeId = $global.componentIdToScopeId || {};
- $global.componentIdToScopeId[component.id] = streamData.scopeId || 0;
- }
-
out.bf(out.___assignedKey, component, willRerender);
- renderFn(
- out.beginAsync(),
- input,
- {
- ...$global,
- componentIdToScopeId: undefined,
- streamData: undefined,
- },
- streamData,
- );
+ htmlCompat.render(tagsRenderer, willRerender, out, component, input);
out.ef();
},
// eslint-disable-next-line no-constant-condition
@@ -92,7 +63,7 @@ exports.p = function (tagsAPI) {
{},
);
- patchDynamicTag(
+ htmlCompat.patchDynamicTag(
function getRenderer(tag) {
const renderer = tag._ || tag.renderBody || tag;
if (isMarko6(renderer)) return renderer;
@@ -108,8 +79,6 @@ exports.p = function (tagsAPI) {
const out = defaultCreateOut();
let customEvents;
- out.global.streamData = tagsAPI.getStreamData();
-
if (renderer5) {
const normalizedInput = {};
@@ -122,7 +91,7 @@ exports.p = function (tagsAPI) {
(c === "-" ? "" : c.toLowerCase()) + key.slice(3),
value,
]);
- value.toJSON = FN_TO_JSON;
+ value.toJSON = htmlCompat.toJSON;
} else {
normalizedInput[key] = input[key];
}
@@ -136,9 +105,7 @@ exports.p = function (tagsAPI) {
const component = componentsContext.___components[0];
if (component) {
component.___component.___customEvents = customEvents;
- writeScope(nextScopeId(), {
- m5c: component.id,
- });
+ htmlCompat.writeSetScopeForComponent(component.id);
}
initComponentsTag({}, out);
@@ -147,7 +114,7 @@ exports.p = function (tagsAPI) {
out.once("finish", (result) => {
if (!async) {
async = false;
- write(result.toString());
+ writeHTML(result);
}
});
@@ -155,9 +122,7 @@ exports.p = function (tagsAPI) {
if (async !== false) {
async = true;
- fork(out, (result) => {
- write(result.toString());
- });
+ htmlCompat.fork(out, writeHTML);
}
};
},
@@ -167,11 +132,5 @@ exports.p = function (tagsAPI) {
},
);
- return function serialized5to6(renderer, id) {
- return serializerRegister(
- "@marko/tags-compat-5-to-6",
- renderer,
- serializerRegister(id, () => {}),
- );
- };
+ return htmlCompat.registerRenderer;
};
diff --git a/packages/runtime-tags/src/__tests__/serializer.test.ts b/packages/runtime-tags/src/__tests__/serializer.test.ts
index 517f595b1..dac0661d8 100644
--- a/packages/runtime-tags/src/__tests__/serializer.test.ts
+++ b/packages/runtime-tags/src/__tests__/serializer.test.ts
@@ -1,1171 +1,1190 @@
import assert from "node:assert/strict";
import { inspect } from "node:util";
import { Serializer, register, stringify } from "../html/serializer";
+import type { Boundary } from "../html/writer";
-it("example", () => {
- const data = {
- strings: "hello\nworld",
- numbers: [1, NaN, Infinity],
- booleans: [true, false],
- void: [null, undefined],
- regexps: /abc/g,
- maps: new Map([[1, 2]]),
- sets: new Set([1, 2]),
- nested: {
- object: {
- "special-keys": 1,
- },
- },
- };
-
- (data.nested.object as any).cyclical = data;
-
- assertStringify(
- data,
- `{strings:"hello\\nworld",numbers:[1,NaN,Infinity],booleans:[!0,!1],void:[null,],regexps:/abc/g,maps:new Map(_.a=[[1,2]]),sets:new Set(_.b=[1,2]),nested:{object:_.d={"special-keys":1}}},_.d.cyclical=_.c`,
- );
-});
-
-describe("primitives", () => {
- describe("undefined", () => {
- it("literal", () => assertStringify(undefined, ``));
- it("in object", () => assertStringify({ x: undefined }, `{}`));
- });
-
- it("null", () => {
- assertStringify(null, `null`);
- });
-
- describe("booleans", () => {
- it("true", () => assertStringify(true, `!0`));
- it("false", () => assertStringify(false, `!1`));
- });
-
- describe("string", () => {
- it("empty", () => assertStringify("", `""`));
- it("normal", () => assertStringify("test", `"test"`));
- it("special characters", () =>
- assertStringify(
- '"\b\t\n\f\r\v\0 {
- it("zero", () => assertStringify(0, `0`));
- it("positive", () => assertStringify(1, `1`));
- it("negative", () => assertStringify(-1, `-1`));
- it("decimal", () => assertStringify(0.1, `0.1`));
- it("negative decimal", () => assertStringify(-0.1, `-0.1`));
- it("NaN", () => assertStringify(NaN, `NaN`));
- it("Infinity", () => assertStringify(Infinity, `Infinity`));
- it("negative Infinity", () => assertStringify(-Infinity, `-Infinity`));
- });
-
- describe("bigint", () => {
- it("zero", () => assertStringify(BigInt(0), `0n`));
- it("positive", () => assertStringify(BigInt(1), `1n`));
- it("negative", () => assertStringify(BigInt(-1), `-1n`));
- it("max safe integer", () =>
- assertStringify(
- BigInt(Number.MAX_SAFE_INTEGER),
- `${Number.MAX_SAFE_INTEGER}n`,
- ));
- });
-});
-
-describe("symbol", () => {
- it("unknown", () => assertStringify({ s: Symbol() }, `{s:Symbol()}`));
- it("unknown duplicated", () => {
- const a = Symbol();
- const b = Symbol();
- assertStringify(
- { a: [a, a], b: [b, b] },
- `{a:[_.a=Symbol(),_.a],b:[_.b=Symbol(),_.b]}`,
- );
- });
- it("for", () => assertStringify(Symbol.for("test"), `Symbol.for("test")`));
- it("iterator", () => assertStringify(Symbol.iterator, `Symbol.iterator`));
- it("asyncIterator", () =>
- assertStringify(Symbol.asyncIterator, `Symbol.asyncIterator`));
- it("hasInstance", () =>
- assertStringify(Symbol.hasInstance, `Symbol.hasInstance`));
- it("isConcatSpreadable", () =>
- assertStringify(Symbol.isConcatSpreadable, `Symbol.isConcatSpreadable`));
- it("match", () => assertStringify(Symbol.match, `Symbol.match`));
- it("matchAll", () => assertStringify(Symbol.matchAll, `Symbol.matchAll`));
- it("replace", () => assertStringify(Symbol.replace, `Symbol.replace`));
- it("search", () => assertStringify(Symbol.search, `Symbol.search`));
- it("species", () => assertStringify(Symbol.species, `Symbol.species`));
- it("split", () => assertStringify(Symbol.split, `Symbol.split`));
- it("toPrimitive", () =>
- assertStringify(Symbol.toPrimitive, `Symbol.toPrimitive`));
- it("toStringTag", () =>
- assertStringify(Symbol.toStringTag, `Symbol.toStringTag`));
- it("unscopables", () =>
- assertStringify(Symbol.unscopables, `Symbol.unscopables`));
-});
-
-describe("weakmap", () => {
- it("empty", () => assertStringify(new WeakMap(), `new WeakMap`));
- it("values ignored", () =>
- assertStringify(new WeakMap([[{}, 1]]), `new WeakMap`));
-});
-
-describe("weakset", () => {
- it("empty", () => assertStringify(new WeakSet(), `new WeakSet`));
- it("values ignored", () => assertStringify(new WeakSet([{}]), `new WeakSet`));
-});
-
-describe("date", () => {
- it("epoch", () =>
- assertStringify(new Date(0), `new Date("1970-01-01T00:00:00.000Z")`));
- it("now", () =>
- assertStringify(new Date(), `new Date("${new Date().toISOString()}")`));
-});
-
-describe("map", () => {
- it("empty", () => assertStringify(new Map(), `new Map`));
- it("nested", () =>
- assertStringify(new Map([[1, new Map()]]), `new Map(_.a=[[1,new Map]])`));
- it("circular key", () => {
- const map = new Map();
- map.set(map, 1);
- assertStringify(
- map,
- `((m,i)=>(i[0][0]=m,i.forEach(i=>m.set(i[0],i[1])),m))(new Map,_.a=[[,1]])`,
- );
- });
- it("circular value", () => {
- const map = new Map();
- map.set(1, map);
- assertStringify(
- map,
- `((m,i)=>(i[0][1]=m,i.forEach(i=>m.set(i[0],i[1])),m))(new Map,_.a=[[1]])`,
- );
- });
- it("circular key and value", () => {
- const map = new Map();
- map.set(map, map);
- assertStringify(
- map,
- `((m,i)=>(i[0][0]=i[0][1]=m,i.forEach(i=>m.set(i[0],i[1])),m))(new Map,_.a=[[]])`,
- );
- });
- it("circular mixed", () => {
- const map = new Map([[1, 2]]);
- map.set(map, map);
- map.set(3, 4);
- assertStringify(
- map,
- `((m,i)=>(i[1][0]=i[1][1]=m,i.forEach(i=>m.set(i[0],i[1])),m))(new Map,_.a=[[1,2],[],[3,4]])`,
- );
- });
- it("circular nested", () => {
- const map = new Map([[1, 2]]);
- map.set(3, { nested: map });
-
- assertStringify(map, `new Map(_.a=[[1,2],[3,_.c={}]]),_.c.nested=_.b`);
- });
- it("dedupe value and keys across flushes", () => {
- const serializer = assertSerializer();
- const objA = { a: 1 };
- const objB = { b: 2 };
- const map = new Map([[objA, objB]]);
-
- serializer.assertStringify(map, `new Map(_.a=[[{a:1},{b:2}]])`);
- serializer.assertStringify({ c: objA }, `{c:_.c=_.a[0][0]}`);
- serializer.assertStringify({ d: objB }, `{d:_.e=_.a[0][1]}`);
- serializer.assertStringify(
- { e: map, f: objA, g: objB },
- `{e:_.b,f:_.c,g:_.e}`,
- );
- });
-});
-
-describe("set", () => {
- it("empty", () => assertStringify(new Set(), `new Set`));
- it("nested", () =>
- assertStringify(new Set([new Set(), 1]), `new Set(_.a=[new Set,1])`));
- it("circular", () => {
- const set = new Set();
- set.add(set);
- assertStringify(
- set,
- `((s,i)=>(i[0]=s,i.forEach(i=>s.add(i)),s))(new Set,_.a=[])`,
- );
- });
- it("circular nested", () => {
- const set = new Set([1]);
- set.add({ nested: set });
-
- assertStringify(set, `new Set(_.a=[1,_.c={}]),_.c.nested=_.b`);
- });
- it("dedupe values across flushes", () => {
- const serializer = assertSerializer();
- const objA = { a: 1 };
- const objB = { b: 2 };
- const set = new Set([objA, objB]);
- serializer.assertStringify(set, `new Set(_.a=[{a:1},{b:2}])`);
- serializer.assertStringify({ c: objA }, `{c:_.c=_.a[0]}`);
- serializer.assertStringify({ d: objB }, `{d:_.e=_.a[1]}`);
- serializer.assertStringify(
- { e: set, f: objA, g: objB },
- `{e:_.b,f:_.c,g:_.e}`,
- );
- });
-});
-
-describe("object", () => {
- it("empty", () => assertStringify({}, `{}`));
- it("nested", () => assertStringify({ a: { b: 1 } }, `{a:{b:1}}`));
- it("dashed-keys", () => assertStringify({ "a-b": 1 }, `{"a-b":1}`));
- it("invalid-keys", () =>
- assertStringify({ "0": 1, "a:": 2, "[": 3 }, `{0:1,"a:":2,"[":3}`));
- it("circular", () => {
- const obj: any = { a: 1 };
- obj.obj = obj;
- assertStringify(obj, `{a:1},_.a.obj=_.a`);
- });
- it("circular nested", () => {
- const obj: any = { a: 1 };
- obj.b = { nested: obj };
- assertStringify(obj, `{a:1,b:_.b={}},_.b.nested=_.a`);
- });
- it("circular object", () => {
- const parent = {
- name: "parent",
- } as any;
-
- const child = {
- parent,
- };
-
- parent.firstChild = child;
- parent.children = [child];
-
- assertStringify(
- { parent },
- `{parent:_.a={name:"parent",firstChild:_.b={},children:[_.b]}},_.b.parent=_.a`,
- );
- });
- it("circular object combined assignments", () => {
- const parent = {
- name: "parent",
- } as any;
-
- const child = {
- parentA: parent,
- parentB: parent,
- "parent-c": parent,
- };
-
- parent.children = [child];
-
- assertStringify(
- { parent },
- `{parent:_.a={name:"parent",children:[_.b={}]}},_.b.parentA=_.b.parentB=_.b["parent-c"]=_.a`,
- );
- });
-
- it("known objects", () => {
- assertStringify(
- [console, Math, JSON, globalThis],
- `[console,Math,JSON,globalThis]`,
- );
- });
-});
-
-describe("array", () => {
- it("empty", () => assertStringify([], `[]`));
- it("nested", () => assertStringify([1, [2]], `[1,[2]]`));
- // eslint-disable-next-line no-sparse-arrays
- it("sparse", () => assertStringify([, 1, 2], `[,1,2]`));
- it("undefined", () => assertStringify([undefined, 1, 2], `[,1,2]`));
- it("circular", () => {
- const arr: any = [1];
- arr.push(arr);
- assertStringify(arr, `[1,],_.a[1]=_.a`);
- });
- it("circular nested", () => {
- const arr: any = [1];
- arr.push({ nested: arr });
- assertStringify(arr, `[1,_.b={}],_.b.nested=_.a`);
- });
- it("circular combined assignments", () => {
- const a: any = [];
- const b: any = [a, 1, a];
- a.push(b);
- a.push(2);
- a.push(b);
-
- assertStringify(a, `[_.b=[,1,],2,_.b],_.b[0]=_.b[2]=_.a`);
- });
-
- it("circular with empty", () => {
- const a: any = [];
- const b: any = [a, 1, a];
- a.push(b);
- a.push(2);
- a.push(undefined);
-
- assertStringify(a, `[_.b=[,1,],2,],_.b[0]=_.b[2]=_.a`);
- });
-});
-
-describe("null prototype", () => {
- it("empty", () => {
- const obj = Object.create(null);
- assertStringify(obj, `{__proto__:null}`);
- });
-
- it("nested", () => {
- const obj = Object.create(null);
- obj.a = Object.create(null);
- obj.a.b = 1;
- assertStringify(obj, `{a:{b:1,__proto__:null},__proto__:null}`);
- });
-});
-
-describe("misc", () => {
- it("nested", () =>
- assertStringify(
- {
- array: [
- {
- a: 1,
- b: ["c"],
- },
- 2,
- ],
- },
- `{array:[{a:1,b:["c"]},2]}`,
- ));
-
- it("shared", () => {
- const registered = new Date(0);
- const pattern = /test/;
- const child = { name: "Henry" };
- const children = [child];
-
- const mother = {
- name: "Jane",
- registered,
- pattern,
- firstChild: child,
- children,
- };
-
- const father = {
- name: "Frank",
- registered,
- pattern,
- firstChild: child,
- children,
- };
-
- assertStringify(
- {
- mother,
- father,
- },
- `{mother:{name:"Jane",registered:_.b=new Date("1970-01-01T00:00:00.000Z"),pattern:_.c=/test/,firstChild:_.a={name:"Henry"},children:_.d=[_.a]},father:{name:"Frank",registered:_.b,pattern:_.c,firstChild:_.a,children:_.d}}`,
- );
- });
-
- it("unsupported constructor", () => {
- class Thing {}
- assertStringify({ thing: new Thing() }, `{}`);
- });
-
- it("unsupported prototype", () => {
- const obj = Object.create({ x: 1 });
- obj.y = 2;
- assertStringify(obj, `{y:2}`);
- });
-
- it("unsupported prototype with nested", () => {
- const obj = Object.create({ x: 1 });
- obj.y = { z: 2 };
- assertStringify(obj, `{y:{z:2}}`);
- });
-
- it("Symbol.iterator inline", () => {
- const obj = {
- x: 1,
- *[Symbol.iterator]() {
- yield 1;
- yield 2;
- yield 3;
+describe("serializer", () => {
+ it("example", () => {
+ const data = {
+ strings: "hello\nworld",
+ numbers: [1, NaN, Infinity],
+ booleans: [true, false],
+ void: [null, undefined],
+ regexps: /abc/g,
+ maps: new Map([[1, 2]]),
+ sets: new Set([1, 2]),
+ nested: {
+ object: {
+ "special-keys": 1,
+ },
},
};
+ (data.nested.object as any).cyclical = data;
+
assertStringify(
- obj,
- `{x:1,[Symbol.iterator]:(a=>()=>a.values())(_.a=[1,2,3])}`,
+ data,
+ `{strings:"hello\\nworld",numbers:[1,NaN,Infinity],booleans:[!0,!1],void:[null,],regexps:/abc/g,maps:new Map(_.a=[[1,2]]),sets:new Set(_.b=[1,2]),nested:{object:_.d={"special-keys":1}}},_.d.cyclical=_.c`,
);
});
- it("Symbol.iterator registered", () => {
- const obj = {
- y: 2,
- [Symbol.iterator]: iterate,
- };
+ describe("primitives", () => {
+ describe("undefined", () => {
+ it("literal", () => assertStringify(undefined, ``));
+ it("in object", () => assertStringify({ x: undefined }, `{}`));
+ });
- function* iterate() {
- yield 1;
- yield 2;
- yield 3;
- }
+ it("null", () => {
+ assertStringify(null, `null`);
+ });
- register("iterate", iterate);
+ describe("booleans", () => {
+ it("true", () => assertStringify(true, `!0`));
+ it("false", () => assertStringify(false, `!1`));
+ });
- assertStringify(obj, `{y:2,[Symbol.iterator]:_._.iterate}`, {
- _: { iterate },
+ describe("string", () => {
+ it("empty", () => assertStringify("", `""`));
+ it("normal", () => assertStringify("test", `"test"`));
+ it("special characters", () =>
+ assertStringify(
+ '"\b\t\n\f\r\v\0 {
+ it("zero", () => assertStringify(0, `0`));
+ it("positive", () => assertStringify(1, `1`));
+ it("negative", () => assertStringify(-1, `-1`));
+ it("decimal", () => assertStringify(0.1, `0.1`));
+ it("negative decimal", () => assertStringify(-0.1, `-0.1`));
+ it("NaN", () => assertStringify(NaN, `NaN`));
+ it("Infinity", () => assertStringify(Infinity, `Infinity`));
+ it("negative Infinity", () => assertStringify(-Infinity, `-Infinity`));
+ });
+
+ describe("bigint", () => {
+ it("zero", () => assertStringify(BigInt(0), `0n`));
+ it("positive", () => assertStringify(BigInt(1), `1n`));
+ it("negative", () => assertStringify(BigInt(-1), `-1n`));
+ it("max safe integer", () =>
+ assertStringify(
+ BigInt(Number.MAX_SAFE_INTEGER),
+ `${Number.MAX_SAFE_INTEGER}n`,
+ ));
});
});
-});
-describe("typed arrays", () => {
- it("ArrayBuffer", () =>
- assertStringify(new ArrayBuffer(32), `new ArrayBuffer(32)`));
- it("Empty ArrayBuffer", () =>
- assertStringify(new ArrayBuffer(0), `new ArrayBuffer`));
- it("Uint8Array empty", () =>
- assertStringify(new Uint8Array(), `new Uint8Array`));
- it("Uint8Array", () =>
- assertStringify(new Uint8Array([1, 2, 3]), `new Uint8Array([1,2,3])`));
- it("Uint8ClampedArray", () =>
- assertStringify(
- new Uint8ClampedArray([1, 2, 3]),
- `new Uint8ClampedArray([1,2,3])`,
- ));
- it("Uint16Array", () =>
- assertStringify(new Uint16Array([1, 2, 3]), `new Uint16Array([1,2,3])`));
- it("Uint32Array", () =>
- assertStringify(new Uint32Array([1, 2, 3]), `new Uint32Array([1,2,3])`));
- it("Int8Array", () =>
- assertStringify(new Int8Array([1, 2, 3]), `new Int8Array([1,2,3])`));
- it("Int16Array", () =>
- assertStringify(new Int16Array([1, 2, 3]), `new Int16Array([1,2,3])`));
- it("Int32Array", () =>
- assertStringify(new Int32Array([1, 2, 3]), `new Int32Array([1,2,3])`));
- it("Float32Array", () =>
- assertStringify(new Float32Array([1, 2, 3]), `new Float32Array([1,2,3])`));
- it("Float64Array", () =>
- assertStringify(new Float64Array([1, 2, 3]), `new Float64Array([1,2,3])`));
-
- it("shared buffer, multiple views", () => {
- const buffer = new ArrayBuffer(32);
- const view1 = new Uint8Array(buffer);
- const view2 = new Uint16Array(buffer);
- const view3 = new Uint32Array(buffer);
-
- assertStringify(
- { view1, view2, view3 },
- `{view1:_.b=new Uint8Array(32),view2:new Uint16Array(_.a=_.b.buffer),view3:new Uint32Array(_.a)}`,
- );
+ describe("symbol", () => {
+ it("unknown", () => assertStringify({ s: Symbol() }, `{s:Symbol()}`));
+ it("unknown duplicated", () => {
+ const a = Symbol();
+ const b = Symbol();
+ assertStringify(
+ { a: [a, a], b: [b, b] },
+ `{a:[_.a=Symbol(),_.a],b:[_.b=Symbol(),_.b]}`,
+ );
+ });
+ it("for", () => assertStringify(Symbol.for("test"), `Symbol.for("test")`));
+ it("iterator", () => assertStringify(Symbol.iterator, `Symbol.iterator`));
+ it("asyncIterator", () =>
+ assertStringify(Symbol.asyncIterator, `Symbol.asyncIterator`));
+ it("hasInstance", () =>
+ assertStringify(Symbol.hasInstance, `Symbol.hasInstance`));
+ it("isConcatSpreadable", () =>
+ assertStringify(Symbol.isConcatSpreadable, `Symbol.isConcatSpreadable`));
+ it("match", () => assertStringify(Symbol.match, `Symbol.match`));
+ it("matchAll", () => assertStringify(Symbol.matchAll, `Symbol.matchAll`));
+ it("replace", () => assertStringify(Symbol.replace, `Symbol.replace`));
+ it("search", () => assertStringify(Symbol.search, `Symbol.search`));
+ it("species", () => assertStringify(Symbol.species, `Symbol.species`));
+ it("split", () => assertStringify(Symbol.split, `Symbol.split`));
+ it("toPrimitive", () =>
+ assertStringify(Symbol.toPrimitive, `Symbol.toPrimitive`));
+ it("toStringTag", () =>
+ assertStringify(Symbol.toStringTag, `Symbol.toStringTag`));
+ it("unscopables", () =>
+ assertStringify(Symbol.unscopables, `Symbol.unscopables`));
});
- it("shared buffer with content, multiple views", () => {
- const buffer = new ArrayBuffer(32);
- const view1 = new Uint8Array(buffer);
- const view2 = new Uint16Array(buffer);
- const view3 = new Uint32Array(buffer);
-
- view1[0] = 1;
- view2[1] = 2;
- view3[2] = 3;
-
- assertStringify(
- { view1, view2, view3 },
- `{view1:_.b=new Uint8Array([1,0,2,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]),view2:new Uint16Array(_.a=_.b.buffer),view3:new Uint32Array(_.a)}`,
- );
+ describe("weakmap", () => {
+ it("empty", () => assertStringify(new WeakMap(), `new WeakMap`));
+ it("values ignored", () =>
+ assertStringify(new WeakMap([[{}, 1]]), `new WeakMap`));
});
- it("shared buffer with content, multiple views, buffer first", () => {
- const buffer = new ArrayBuffer(32);
- const view1 = new Uint8Array(buffer);
- const view2 = new Uint16Array(buffer);
- const view3 = new Uint32Array(buffer);
-
- view1[0] = 1;
- view2[1] = 2;
- view3[2] = 3;
-
- assertStringify(
- { buffer, view1, view2, view3 },
- `{buffer:_.a=new Int8Array([1,0,2,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]).buffer,view1:new Uint8Array(_.a),view2:new Uint16Array(_.a),view3:new Uint32Array(_.a)}`,
- );
+ describe("weakset", () => {
+ it("empty", () => assertStringify(new WeakSet(), `new WeakSet`));
+ it("values ignored", () =>
+ assertStringify(new WeakSet([{}]), `new WeakSet`));
});
- it("shared buffer with content, multiple views, buffer last", () => {
- const buffer = new ArrayBuffer(32);
- const view1 = new Uint8Array(buffer);
- const view2 = new Uint16Array(buffer);
- const view3 = new Uint32Array(buffer);
-
- view1[0] = 1;
- view2[1] = 2;
- view3[2] = 3;
-
- assertStringify(
- { view1, view2, view3, buffer },
- `{view1:_.b=new Uint8Array([1,0,2,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]),view2:new Uint16Array(_.a=_.b.buffer),view3:new Uint32Array(_.a),buffer:_.a}`,
- );
+ describe("date", () => {
+ it("epoch", () =>
+ assertStringify(new Date(0), `new Date("1970-01-01T00:00:00.000Z")`));
+ it("now", () =>
+ assertStringify(new Date(), `new Date("${new Date().toISOString()}")`));
});
- it("shared empty array buffer, multiple views", () => {
- const buffer = new ArrayBuffer(0);
- const view1 = new Uint8Array(buffer);
- const view2 = new Uint16Array(buffer);
- const view3 = new Uint32Array(buffer);
+ describe("map", () => {
+ it("empty", () => assertStringify(new Map(), `new Map`));
+ it("nested", () =>
+ assertStringify(new Map([[1, new Map()]]), `new Map(_.a=[[1,new Map]])`));
+ it("circular key", () => {
+ const map = new Map();
+ map.set(map, 1);
+ assertStringify(
+ map,
+ `((m,i)=>(i[0][0]=m,i.forEach(i=>m.set(i[0],i[1])),m))(new Map,_.a=[[,1]])`,
+ );
+ });
+ it("circular value", () => {
+ const map = new Map();
+ map.set(1, map);
+ assertStringify(
+ map,
+ `((m,i)=>(i[0][1]=m,i.forEach(i=>m.set(i[0],i[1])),m))(new Map,_.a=[[1]])`,
+ );
+ });
+ it("circular key and value", () => {
+ const map = new Map();
+ map.set(map, map);
+ assertStringify(
+ map,
+ `((m,i)=>(i[0][0]=i[0][1]=m,i.forEach(i=>m.set(i[0],i[1])),m))(new Map,_.a=[[]])`,
+ );
+ });
+ it("circular mixed", () => {
+ const map = new Map([[1, 2]]);
+ map.set(map, map);
+ map.set(3, 4);
+ assertStringify(
+ map,
+ `((m,i)=>(i[1][0]=i[1][1]=m,i.forEach(i=>m.set(i[0],i[1])),m))(new Map,_.a=[[1,2],[],[3,4]])`,
+ );
+ });
+ it("circular nested", () => {
+ const map = new Map([[1, 2]]);
+ map.set(3, { nested: map });
- assertStringify(
- { view1, view2, view3 },
- `{view1:_.b=new Uint8Array,view2:new Uint16Array(_.a=_.b.buffer),view3:new Uint32Array(_.a)}`,
- );
+ assertStringify(map, `new Map(_.a=[[1,2],[3,_.c={}]]),_.c.nested=_.b`);
+ });
+ it("dedupe value and keys across flushes", () => {
+ const serializer = assertSerializer();
+ const objA = { a: 1 };
+ const objB = { b: 2 };
+ const map = new Map([[objA, objB]]);
+
+ serializer.assertStringify(map, `new Map(_.a=[[{a:1},{b:2}]])`);
+ serializer.assertStringify({ c: objA }, `{c:_.c=_.a[0][0]}`);
+ serializer.assertStringify({ d: objB }, `{d:_.e=_.a[0][1]}`);
+ serializer.assertStringify(
+ { e: map, f: objA, g: objB },
+ `{e:_.b,f:_.c,g:_.e}`,
+ );
+ });
});
- it("with byte offset", () => {
- const buffer = new ArrayBuffer(32);
- const view1 = new Uint8Array(buffer, 8);
- const view2 = new Uint16Array(buffer, 8);
- const view3 = new Uint32Array(buffer, 8);
+ describe("set", () => {
+ it("empty", () => assertStringify(new Set(), `new Set`));
+ it("nested", () =>
+ assertStringify(new Set([new Set(), 1]), `new Set(_.a=[new Set,1])`));
+ it("circular", () => {
+ const set = new Set();
+ set.add(set);
+ assertStringify(
+ set,
+ `((s,i)=>(i[0]=s,i.forEach(i=>s.add(i)),s))(new Set,_.a=[])`,
+ );
+ });
+ it("circular nested", () => {
+ const set = new Set([1]);
+ set.add({ nested: set });
- assertStringify(
- { view1, view2, view3 },
- `{view1:new Uint8Array(_.a=new ArrayBuffer(32),8),view2:new Uint16Array(_.a,8),view3:new Uint32Array(_.a,8)}`,
- );
+ assertStringify(set, `new Set(_.a=[1,_.c={}]),_.c.nested=_.b`);
+ });
+ it("dedupe values across flushes", () => {
+ const serializer = assertSerializer();
+ const objA = { a: 1 };
+ const objB = { b: 2 };
+ const set = new Set([objA, objB]);
+ serializer.assertStringify(set, `new Set(_.a=[{a:1},{b:2}])`);
+ serializer.assertStringify({ c: objA }, `{c:_.c=_.a[0]}`);
+ serializer.assertStringify({ d: objB }, `{d:_.e=_.a[1]}`);
+ serializer.assertStringify(
+ { e: set, f: objA, g: objB },
+ `{e:_.b,f:_.c,g:_.e}`,
+ );
+ });
});
- it("with byte offset and data", () => {
- const buffer = new ArrayBuffer(32);
- const view1 = new Uint8Array(buffer, 8);
- const view2 = new Uint16Array(buffer, 8);
- const view3 = new Uint32Array(buffer, 8);
+ describe("object", () => {
+ it("empty", () => assertStringify({}, `{}`));
+ it("nested", () => assertStringify({ a: { b: 1 } }, `{a:{b:1}}`));
+ it("dashed-keys", () => assertStringify({ "a-b": 1 }, `{"a-b":1}`));
+ it("invalid-keys", () =>
+ assertStringify({ "0": 1, "a:": 2, "[": 3 }, `{0:1,"a:":2,"[":3}`));
+ it("circular", () => {
+ const obj: any = { a: 1 };
+ obj.obj = obj;
+ assertStringify(obj, `{a:1},_.a.obj=_.a`);
+ });
+ it("circular nested", () => {
+ const obj: any = { a: 1 };
+ obj.b = { nested: obj };
+ assertStringify(obj, `{a:1,b:_.b={}},_.b.nested=_.a`);
+ });
+ it("circular object", () => {
+ const parent = {
+ name: "parent",
+ } as any;
- view1[0] = 1;
- view2[1] = 2;
- view3[2] = 3;
+ const child = {
+ parent,
+ };
- assertStringify(
- { view1, view2, view3 },
- `{view1:new Uint8Array(_.a=new Int8Array([0,0,0,0,0,0,0,0,1,0,2,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]).buffer,8),view2:new Uint16Array(_.a,8),view3:new Uint32Array(_.a,8)}`,
- );
+ parent.firstChild = child;
+ parent.children = [child];
+
+ assertStringify(
+ { parent },
+ `{parent:_.a={name:"parent",firstChild:_.b={},children:[_.b]}},_.b.parent=_.a`,
+ );
+ });
+ it("circular object combined assignments", () => {
+ const parent = {
+ name: "parent",
+ } as any;
+
+ const child = {
+ parentA: parent,
+ parentB: parent,
+ "parent-c": parent,
+ };
+
+ parent.children = [child];
+
+ assertStringify(
+ { parent },
+ `{parent:_.a={name:"parent",children:[_.b={}]}},_.b.parentA=_.b.parentB=_.b["parent-c"]=_.a`,
+ );
+ });
+
+ it("known objects", () => {
+ assertStringify(
+ [console, Math, JSON, globalThis],
+ `[console,Math,JSON,globalThis]`,
+ );
+ });
});
- // it("BigInt64Array", () =>
- // assertStringify(new BigInt64Array([1n, 2n, 3n]), `new BigInt64Array([1n,2n,3n])`));
- // it("BigUint64Array", () =>
- // assertStringify(new BigUint64Array([1n, 2n, 3n]), `new BigUint64Array([1n,2n,3n])`));
-});
+ describe("array", () => {
+ it("empty", () => assertStringify([], `[]`));
+ it("nested", () => assertStringify([1, [2]], `[1,[2]]`));
+ // eslint-disable-next-line no-sparse-arrays
+ it("sparse", () => assertStringify([, 1, 2], `[,1,2]`));
+ it("undefined", () => assertStringify([undefined, 1, 2], `[,1,2]`));
+ it("circular", () => {
+ const arr: any = [1];
+ arr.push(arr);
+ assertStringify(arr, `[1,],_.a[1]=_.a`);
+ });
+ it("circular nested", () => {
+ const arr: any = [1];
+ arr.push({ nested: arr });
+ assertStringify(arr, `[1,_.b={}],_.b.nested=_.a`);
+ });
+ it("circular combined assignments", () => {
+ const a: any = [];
+ const b: any = [a, 1, a];
+ a.push(b);
+ a.push(2);
+ a.push(b);
-describe("URL", () => {
- it("value", () =>
- assertStringify(
- new URL("https://example.com/?a=1&b=2"),
- `new URL("https://example.com/?a=1&b=2")`,
- ));
- it("duplicated", () => {
- const url = new URL("https://example.com");
- assertStringify(url, `new URL("https://example.com/")`);
- assertStringify([url, url], `[_.a=new URL("https://example.com/"),_.a]`);
- });
-});
+ assertStringify(a, `[_.b=[,1,],2,_.b],_.b[0]=_.b[2]=_.a`);
+ });
-describe("URLSearchParams", () => {
- it("empty", () =>
- assertStringify(new URLSearchParams(), `new URLSearchParams`));
- it("value", () =>
- assertStringify(
- new URLSearchParams("a=1&b=2"),
- `new URLSearchParams("a=1&b=2")`,
- ));
- it("duplicated", () => {
- const url = new URLSearchParams("a=1&b=2");
- assertStringify(url, `new URLSearchParams("a=1&b=2")`);
- assertStringify([url, url], `[_.a=new URLSearchParams("a=1&b=2"),_.a]`);
- });
-});
+ it("circular with empty", () => {
+ const a: any = [];
+ const b: any = [a, 1, a];
+ a.push(b);
+ a.push(2);
+ a.push(undefined);
-describe("Headers", () => {
- it("empty", () => assertStringify(new Headers(), `new Headers`));
- it("value", () =>
- assertStringify(
- new Headers({ a: "1", b: "2" }),
- `new Headers({a:"1",b:"2"})`,
- ));
- it("duplicated", () => {
- const headers = new Headers({ a: "1", b: "2" });
- assertStringify(headers, `new Headers({a:"1",b:"2"})`);
- assertStringify([headers, headers], `[_.a=new Headers({a:"1",b:"2"}),_.a]`);
+ assertStringify(a, `[_.b=[,1,],2,],_.b[0]=_.b[2]=_.a`);
+ });
});
-});
-describe("FormData", () => {
- it("empty", () => assertStringify(new FormData(), `new FormData`));
- it("value", () => {
- const formData = new FormData();
- formData.append("a", "1");
- formData.append("b", "2");
- assertStringify(
- formData,
- `((f,i)=>(f,i.forEach(i=>f.append(i[0],i[1])),f))(new FormData,[["a","1"],["b","2"]])`,
- );
- });
- it("duplicated", () => {
- const formData = new FormData();
- formData.append("a", "1");
- formData.append("b", "2");
- assertStringify(
- formData,
- `((f,i)=>(f,i.forEach(i=>f.append(i[0],i[1])),f))(new FormData,[["a","1"],["b","2"]])`,
- );
- assertStringify(
- [formData, formData],
- `[_.a=((f,i)=>(f,i.forEach(i=>f.append(i[0],i[1])),f))(new FormData,[["a","1"],["b","2"]]),_.a]`,
- );
- });
-});
+ describe("null prototype", () => {
+ it("empty", () => {
+ const obj = Object.create(null);
+ assertStringify(obj, `{__proto__:null}`);
+ });
-describe("generator", () => {
- it("empty", () => assertStringify((function* () {})(), `(function*(){})()`));
- it("single", () =>
- assertStringify(
- (function* () {
- yield 1;
- })(),
- `(function*(){yield 1})()`,
- ));
- it("multiple", () =>
- assertStringify(
- (function* () {
+ it("nested", () => {
+ const obj = Object.create(null);
+ obj.a = Object.create(null);
+ obj.a.b = 1;
+ assertStringify(obj, `{a:{b:1,__proto__:null},__proto__:null}`);
+ });
+ });
+
+ describe("misc", () => {
+ it("nested", () =>
+ assertStringify(
+ {
+ array: [
+ {
+ a: 1,
+ b: ["c"],
+ },
+ 2,
+ ],
+ },
+ `{array:[{a:1,b:["c"]},2]}`,
+ ));
+
+ it("shared", () => {
+ const registered = new Date(0);
+ const pattern = /test/;
+ const child = { name: "Henry" };
+ const children = [child];
+
+ const mother = {
+ name: "Jane",
+ registered,
+ pattern,
+ firstChild: child,
+ children,
+ };
+
+ const father = {
+ name: "Frank",
+ registered,
+ pattern,
+ firstChild: child,
+ children,
+ };
+
+ assertStringify(
+ {
+ mother,
+ father,
+ },
+ `{mother:{name:"Jane",registered:_.b=new Date("1970-01-01T00:00:00.000Z"),pattern:_.c=/test/,firstChild:_.a={name:"Henry"},children:_.d=[_.a]},father:{name:"Frank",registered:_.b,pattern:_.c,firstChild:_.a,children:_.d}}`,
+ );
+ });
+
+ it("unsupported constructor", () => {
+ class Thing {}
+ assertStringify({ thing: new Thing() }, `{}`);
+ });
+
+ it("unsupported prototype", () => {
+ const obj = Object.create({ x: 1 });
+ obj.y = 2;
+ assertStringify(obj, `{y:2}`);
+ });
+
+ it("unsupported prototype with nested", () => {
+ const obj = Object.create({ x: 1 });
+ obj.y = { z: 2 };
+ assertStringify(obj, `{y:{z:2}}`);
+ });
+
+ it("Symbol.iterator inline", () => {
+ const obj = {
+ x: 1,
+ *[Symbol.iterator]() {
+ yield 1;
+ yield 2;
+ yield 3;
+ },
+ };
+
+ assertStringify(
+ obj,
+ `{x:1,[Symbol.iterator]:(a=>()=>a.values())(_.a=[1,2,3])}`,
+ );
+ });
+
+ it("Symbol.iterator registered", () => {
+ const obj = {
+ y: 2,
+ [Symbol.iterator]: iterate,
+ };
+
+ function* iterate() {
yield 1;
yield 2;
yield 3;
- })(),
- `(function*(){yield 1;yield 2;yield 3})()`,
- ));
- it("nested", () =>
- assertStringify(
- (function* () {
- yield* (function* () {
- yield 1;
- })();
- })(),
- `(function*(){yield 1})()`,
- ));
+ }
- it("yield undefined", () => {
- const gen = (function* () {
- yield 1;
- yield undefined;
- yield 2;
- yield undefined;
- })();
- assertStringify(gen, `(function*(){yield 1;yield;yield 2;yield})()`);
+ register("iterate", iterate);
+
+ assertStringify(obj, `{y:2,[Symbol.iterator]:_._.iterate}`, {
+ _: { iterate },
+ });
+ });
});
-});
-describe("errors", () => {
- it("Error", () => assertStringify(new Error("test"), `new Error("test")`));
- it("EvalError", () =>
- assertStringify(new EvalError("test"), `new EvalError("test")`));
- it("RangeError", () =>
- assertStringify(new RangeError("test"), `new RangeError("test")`));
- it("ReferenceError", () =>
- assertStringify(new ReferenceError("test"), `new ReferenceError("test")`));
- it("SyntaxError", () =>
- assertStringify(new SyntaxError("test"), `new SyntaxError("test")`));
- it("TypeError", () =>
- assertStringify(new TypeError("test"), `new TypeError("test")`));
- it("URIError", () =>
- assertStringify(new URIError("test"), `new URIError("test")`));
+ describe("typed arrays", () => {
+ it("ArrayBuffer", () =>
+ assertStringify(new ArrayBuffer(32), `new ArrayBuffer(32)`));
+ it("Empty ArrayBuffer", () =>
+ assertStringify(new ArrayBuffer(0), `new ArrayBuffer`));
+ it("Uint8Array empty", () =>
+ assertStringify(new Uint8Array(), `new Uint8Array`));
+ it("Uint8Array", () =>
+ assertStringify(new Uint8Array([1, 2, 3]), `new Uint8Array([1,2,3])`));
+ it("Uint8ClampedArray", () =>
+ assertStringify(
+ new Uint8ClampedArray([1, 2, 3]),
+ `new Uint8ClampedArray([1,2,3])`,
+ ));
+ it("Uint16Array", () =>
+ assertStringify(new Uint16Array([1, 2, 3]), `new Uint16Array([1,2,3])`));
+ it("Uint32Array", () =>
+ assertStringify(new Uint32Array([1, 2, 3]), `new Uint32Array([1,2,3])`));
+ it("Int8Array", () =>
+ assertStringify(new Int8Array([1, 2, 3]), `new Int8Array([1,2,3])`));
+ it("Int16Array", () =>
+ assertStringify(new Int16Array([1, 2, 3]), `new Int16Array([1,2,3])`));
+ it("Int32Array", () =>
+ assertStringify(new Int32Array([1, 2, 3]), `new Int32Array([1,2,3])`));
+ it("Float32Array", () =>
+ assertStringify(
+ new Float32Array([1, 2, 3]),
+ `new Float32Array([1,2,3])`,
+ ));
+ it("Float64Array", () =>
+ assertStringify(
+ new Float64Array([1, 2, 3]),
+ `new Float64Array([1,2,3])`,
+ ));
- describe("AggregateError", () => {
+ it("shared buffer, multiple views", () => {
+ const buffer = new ArrayBuffer(32);
+ const view1 = new Uint8Array(buffer);
+ const view2 = new Uint16Array(buffer);
+ const view3 = new Uint32Array(buffer);
+
+ assertStringify(
+ { view1, view2, view3 },
+ `{view1:_.b=new Uint8Array(32),view2:new Uint16Array(_.a=_.b.buffer),view3:new Uint32Array(_.a)}`,
+ );
+ });
+
+ it("shared buffer with content, multiple views", () => {
+ const buffer = new ArrayBuffer(32);
+ const view1 = new Uint8Array(buffer);
+ const view2 = new Uint16Array(buffer);
+ const view3 = new Uint32Array(buffer);
+
+ view1[0] = 1;
+ view2[1] = 2;
+ view3[2] = 3;
+
+ assertStringify(
+ { view1, view2, view3 },
+ `{view1:_.b=new Uint8Array([1,0,2,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]),view2:new Uint16Array(_.a=_.b.buffer),view3:new Uint32Array(_.a)}`,
+ );
+ });
+
+ it("shared buffer with content, multiple views, buffer first", () => {
+ const buffer = new ArrayBuffer(32);
+ const view1 = new Uint8Array(buffer);
+ const view2 = new Uint16Array(buffer);
+ const view3 = new Uint32Array(buffer);
+
+ view1[0] = 1;
+ view2[1] = 2;
+ view3[2] = 3;
+
+ assertStringify(
+ { buffer, view1, view2, view3 },
+ `{buffer:_.a=new Int8Array([1,0,2,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]).buffer,view1:new Uint8Array(_.a),view2:new Uint16Array(_.a),view3:new Uint32Array(_.a)}`,
+ );
+ });
+
+ it("shared buffer with content, multiple views, buffer last", () => {
+ const buffer = new ArrayBuffer(32);
+ const view1 = new Uint8Array(buffer);
+ const view2 = new Uint16Array(buffer);
+ const view3 = new Uint32Array(buffer);
+
+ view1[0] = 1;
+ view2[1] = 2;
+ view3[2] = 3;
+
+ assertStringify(
+ { view1, view2, view3, buffer },
+ `{view1:_.b=new Uint8Array([1,0,2,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]),view2:new Uint16Array(_.a=_.b.buffer),view3:new Uint32Array(_.a),buffer:_.a}`,
+ );
+ });
+
+ it("shared empty array buffer, multiple views", () => {
+ const buffer = new ArrayBuffer(0);
+ const view1 = new Uint8Array(buffer);
+ const view2 = new Uint16Array(buffer);
+ const view3 = new Uint32Array(buffer);
+
+ assertStringify(
+ { view1, view2, view3 },
+ `{view1:_.b=new Uint8Array,view2:new Uint16Array(_.a=_.b.buffer),view3:new Uint32Array(_.a)}`,
+ );
+ });
+
+ it("with byte offset", () => {
+ const buffer = new ArrayBuffer(32);
+ const view1 = new Uint8Array(buffer, 8);
+ const view2 = new Uint16Array(buffer, 8);
+ const view3 = new Uint32Array(buffer, 8);
+
+ assertStringify(
+ { view1, view2, view3 },
+ `{view1:new Uint8Array(_.a=new ArrayBuffer(32),8),view2:new Uint16Array(_.a,8),view3:new Uint32Array(_.a,8)}`,
+ );
+ });
+
+ it("with byte offset and data", () => {
+ const buffer = new ArrayBuffer(32);
+ const view1 = new Uint8Array(buffer, 8);
+ const view2 = new Uint16Array(buffer, 8);
+ const view3 = new Uint32Array(buffer, 8);
+
+ view1[0] = 1;
+ view2[1] = 2;
+ view3[2] = 3;
+
+ assertStringify(
+ { view1, view2, view3 },
+ `{view1:new Uint8Array(_.a=new Int8Array([0,0,0,0,0,0,0,0,1,0,2,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]).buffer,8),view2:new Uint16Array(_.a,8),view3:new Uint32Array(_.a,8)}`,
+ );
+ });
+
+ // it("BigInt64Array", () =>
+ // assertStringify(new BigInt64Array([1n, 2n, 3n]), `new BigInt64Array([1n,2n,3n])`));
+ // it("BigUint64Array", () =>
+ // assertStringify(new BigUint64Array([1n, 2n, 3n]), `new BigUint64Array([1n,2n,3n])`));
+ });
+
+ describe("URL", () => {
+ it("value", () =>
+ assertStringify(
+ new URL("https://example.com/?a=1&b=2"),
+ `new URL("https://example.com/?a=1&b=2")`,
+ ));
+ it("duplicated", () => {
+ const url = new URL("https://example.com");
+ assertStringify(url, `new URL("https://example.com/")`);
+ assertStringify([url, url], `[_.a=new URL("https://example.com/"),_.a]`);
+ });
+ });
+
+ describe("URLSearchParams", () => {
it("empty", () =>
- assertStringify(new AggregateError([]), `new AggregateError([])`));
+ assertStringify(new URLSearchParams(), `new URLSearchParams`));
+ it("value", () =>
+ assertStringify(
+ new URLSearchParams("a=1&b=2"),
+ `new URLSearchParams("a=1&b=2")`,
+ ));
+ it("duplicated", () => {
+ const url = new URLSearchParams("a=1&b=2");
+ assertStringify(url, `new URLSearchParams("a=1&b=2")`);
+ assertStringify([url, url], `[_.a=new URLSearchParams("a=1&b=2"),_.a]`);
+ });
+ });
+
+ describe("Headers", () => {
+ it("empty", () => assertStringify(new Headers(), `new Headers`));
+ it("value", () =>
+ assertStringify(
+ new Headers({ a: "1", b: "2" }),
+ `new Headers({a:"1",b:"2"})`,
+ ));
+ it("duplicated", () => {
+ const headers = new Headers({ a: "1", b: "2" });
+ assertStringify(headers, `new Headers({a:"1",b:"2"})`);
+ assertStringify(
+ [headers, headers],
+ `[_.a=new Headers({a:"1",b:"2"}),_.a]`,
+ );
+ });
+ });
+
+ describe("FormData", () => {
+ it("empty", () => assertStringify(new FormData(), `new FormData`));
+ it("value", () => {
+ const formData = new FormData();
+ formData.append("a", "1");
+ formData.append("b", "2");
+ assertStringify(
+ formData,
+ `((f,i)=>(f,i.forEach(i=>f.append(i[0],i[1])),f))(new FormData,[["a","1"],["b","2"]])`,
+ );
+ });
+ it("duplicated", () => {
+ const formData = new FormData();
+ formData.append("a", "1");
+ formData.append("b", "2");
+ assertStringify(
+ formData,
+ `((f,i)=>(f,i.forEach(i=>f.append(i[0],i[1])),f))(new FormData,[["a","1"],["b","2"]])`,
+ );
+ assertStringify(
+ [formData, formData],
+ `[_.a=((f,i)=>(f,i.forEach(i=>f.append(i[0],i[1])),f))(new FormData,[["a","1"],["b","2"]]),_.a]`,
+ );
+ });
+ });
+
+ describe("generator", () => {
+ it("empty", () =>
+ assertStringify((function* () {})(), `(function*(){})()`));
it("single", () =>
assertStringify(
- new AggregateError(["test"]),
- `new AggregateError(["test"])`,
+ (function* () {
+ yield 1;
+ })(),
+ `(function*(){yield 1})()`,
));
it("multiple", () =>
assertStringify(
- new AggregateError(["test", "test2"]),
- `new AggregateError(["test","test2"])`,
+ (function* () {
+ yield 1;
+ yield 2;
+ yield 3;
+ })(),
+ `(function*(){yield 1;yield 2;yield 3})()`,
));
- it("duplicate", () => {
- const error = new Error("test");
+ it("nested", () =>
assertStringify(
- new AggregateError([error, error], "test"),
- `new AggregateError([_.a=new Error("test"),_.a],"test")`,
+ (function* () {
+ yield* (function* () {
+ yield 1;
+ })();
+ })(),
+ `(function*(){yield 1})()`,
+ ));
+
+ it("yield undefined", () => {
+ const gen = (function* () {
+ yield 1;
+ yield undefined;
+ yield 2;
+ yield undefined;
+ })();
+ assertStringify(gen, `(function*(){yield 1;yield;yield 2;yield})()`);
+ });
+ });
+
+ describe("errors", () => {
+ it("Error", () => assertStringify(new Error("test"), `new Error("test")`));
+ it("EvalError", () =>
+ assertStringify(new EvalError("test"), `new EvalError("test")`));
+ it("RangeError", () =>
+ assertStringify(new RangeError("test"), `new RangeError("test")`));
+ it("ReferenceError", () =>
+ assertStringify(
+ new ReferenceError("test"),
+ `new ReferenceError("test")`,
+ ));
+ it("SyntaxError", () =>
+ assertStringify(new SyntaxError("test"), `new SyntaxError("test")`));
+ it("TypeError", () =>
+ assertStringify(new TypeError("test"), `new TypeError("test")`));
+ it("URIError", () =>
+ assertStringify(new URIError("test"), `new URIError("test")`));
+
+ describe("AggregateError", () => {
+ it("empty", () =>
+ assertStringify(new AggregateError([]), `new AggregateError([])`));
+ it("single", () =>
+ assertStringify(
+ new AggregateError(["test"]),
+ `new AggregateError(["test"])`,
+ ));
+ it("multiple", () =>
+ assertStringify(
+ new AggregateError(["test", "test2"]),
+ `new AggregateError(["test","test2"])`,
+ ));
+ it("duplicate", () => {
+ const error = new Error("test");
+ assertStringify(
+ new AggregateError([error, error], "test"),
+ `new AggregateError([_.a=new Error("test"),_.a],"test")`,
+ );
+ });
+ it("errors array referenced", () => {
+ const agg = new AggregateError([new Error("test")], "test");
+ assertStringify(
+ { errors: agg.errors, agg },
+ `{errors:_.a=[new Error("test")],agg:new AggregateError(_.a,"test")}`,
+ );
+ });
+ });
+ });
+
+ describe("function", () => {
+ it("known functions", () => {
+ assertStringify(
+ [console.log, Object, Math.pow, JSON.stringify],
+ `[console.log,Object,Math.pow,JSON.stringify]`,
);
});
- it("errors array referenced", () => {
- const agg = new AggregateError([new Error("test")], "test");
- assertStringify(
- { errors: agg.errors, agg },
- `{errors:_.a=[new Error("test")],agg:new AggregateError(_.a,"test")}`,
- );
- });
- });
-});
-describe("function", () => {
- it("known functions", () => {
- assertStringify(
- [console.log, Object, Math.pow, JSON.stringify],
- `[console.log,Object,Math.pow,JSON.stringify]`,
- );
- });
-
- it("unknown functions", () => {
- const fn = () => 1;
- assertStringify([fn, "a"], `[,"a"]`);
- });
-});
-
-describe("registry", () => {
- it("reference object", () => {
- const obj = { a: 1 };
- register("obj", obj);
- assertStringify(obj, `_._.obj`, { _: { obj } });
- });
-
- it("reference function", () => {
- const fn = () => 1;
- register("fn", fn);
- assertStringify(fn, `_._.fn`, { _: { fn } });
- });
-
- it("reference non object key id", () => {
- const obj = { a: 1 };
- register("a-b-c", obj);
- assertStringify(obj, `_._["a-b-c"]`, { _: { "a-b-c": obj } });
- });
-
- it("scoped reference", () => {
- const builder = (s: typeof scope) => () => s.value;
- const scope = { value: 1 };
- const obj = { fn: builder(scope) };
- register("fn", obj.fn, scope);
- assertStringify(obj, `{fn:_._.fn(_.a={value:1})}`, { _: { fn: builder } });
- });
-
- it("circular scoped reference", () => {
- const scope = { value: 1 } as any;
- const builder =
- ({ value }: typeof scope) =>
- () =>
- value;
- scope.fn = builder(scope);
- register("fn", scope.fn, scope);
- assertStringify(scope, `{value:1},_.a.fn=_._.fn(_.a)`, {
- _: { fn: builder },
+ it("unknown functions", () => {
+ const fn = () => 1;
+ assertStringify([fn, "a"], `[,"a"]`);
});
});
- it("read after circular scoped reference", () => {
- const scope = { value: 1 } as any;
- const builder =
- ({ value }: typeof scope) =>
- () =>
- value;
- scope.fn = builder(scope);
- register("fn", scope.fn, scope);
- assertStringify(
- {
- scope,
- fn: scope.fn,
- },
- `{scope:_.a={value:1}},_.a.fn=_.b.fn=_._.fn(_.a)`,
- {
+ describe("registry", () => {
+ it("reference object", () => {
+ const obj = { a: 1 };
+ register("obj", obj);
+ assertStringify(obj, `_._.obj`, { _: { obj } });
+ });
+
+ it("reference function", () => {
+ const fn = () => 1;
+ register("fn", fn);
+ assertStringify(fn, `_._.fn`, { _: { fn } });
+ });
+
+ it("reference non object key id", () => {
+ const obj = { a: 1 };
+ register("a-b-c", obj);
+ assertStringify(obj, `_._["a-b-c"]`, { _: { "a-b-c": obj } });
+ });
+
+ it("scoped reference", () => {
+ const builder = (s: typeof scope) => () => s.value;
+ const scope = { value: 1 };
+ const obj = { fn: builder(scope) };
+ register("fn", obj.fn, scope);
+ assertStringify(obj, `{fn:_._.fn(_.a={value:1})}`, {
_: { fn: builder },
- },
- );
- });
-});
+ });
+ });
-describe("serializer", () => {
- it("multiple flushes", () => {
- const serializer = assertSerializer();
- serializer.assertStringify({ a: 1 }, `{a:1}`);
- serializer.assertStringify({ a: 2 }, `{a:2}`);
- });
+ it("circular scoped reference", () => {
+ const scope = { value: 1 } as any;
+ const builder =
+ ({ value }: typeof scope) =>
+ () =>
+ value;
+ scope.fn = builder(scope);
+ register("fn", scope.fn, scope);
+ assertStringify(scope, `{value:1},_.a.fn=_._.fn(_.a)`, {
+ _: { fn: builder },
+ });
+ });
- it("multiple flushes with shared references", () => {
- const serializer = assertSerializer();
- const obj = { a: 1 };
- serializer.assertStringify(obj, `{a:1}`);
- serializer.assertStringify(obj, `_.a`);
- });
-
- it("multiple flushes with shared references and nested", () => {
- const serializer = assertSerializer();
- const nested = { b: 1 };
- const obj = { a: nested };
- serializer.assertStringify(obj, `{a:{b:1}}`);
- serializer.assertStringify({ c: nested }, `{c:_.b=_.a.a}`);
- serializer.assertStringify({ d: nested }, `{d:_.b}`);
- });
-});
-
-describe("promise", () => {
- it("resolves", async () => {
- const serializer = assertSerializer();
- const obj = { x: 1 };
- const promise = Promise.resolve(obj);
- const [result] = await serializer.assertStringify(
- promise,
- `new Promise((f,r)=>_.a={f,r})`,
- `_.a.f(_.a={x:1})`,
- );
- assert.deepEqual(serializer.get("a"), obj);
- assert.deepEqual(await result, obj);
- });
-
- it("rejects", async () => {
- const serializer = assertSerializer();
- const error = new Error("test");
- const promise = Promise.reject(error);
- const [result] = await serializer.assertStringify(
- promise,
- `new Promise((f,r)=>_.a={f,r})`,
- `_.a.r(_.a=new Error("test"))`,
- );
- assert.deepEqual(serializer.get("a"), error);
- await assert.rejects(result, error);
- });
-});
-
-describe("async generator", () => {
- it("resolves", async () => {
- const serializer = assertSerializer();
- const yielded = [{ x: 1 }, { y: 2 }];
- const returned = { z: 3 };
- const [result] = await serializer.assertStringify(
- (async function* () {
- yield* yielded;
- return returned;
- })(),
- `(async function*(_,f,v,l,i,p=a=>l=new Promise((r,j)=>{f=_.r=r;_.j=j}),a=((_.f=v=>{f(v);a.push(p())}),[p()])){for(i of a)v=await i,i!=l&&(yield v);return v})(_.a={})`,
- `_.a.f(_.c={x:1})`,
- `_.a.f(_.d={y:2})`,
- `_.a.r(_.e={z:3})`,
- );
-
- assert.deepEqual(await consumeIterator(result), {
- yielded,
- returned,
- errored: undefined,
+ it("read after circular scoped reference", () => {
+ const scope = { value: 1 } as any;
+ const builder =
+ ({ value }: typeof scope) =>
+ () =>
+ value;
+ scope.fn = builder(scope);
+ register("fn", scope.fn, scope);
+ assertStringify(
+ {
+ scope,
+ fn: scope.fn,
+ },
+ `{scope:_.a={value:1}},_.a.fn=_.b.fn=_._.fn(_.a)`,
+ {
+ _: { fn: builder },
+ },
+ );
});
});
- it("rejects", async () => {
- const serializer = assertSerializer();
- const yielded = [{ x: 1 }, { y: 2 }];
- const errored = new Error("boom");
- const [result] = await serializer.assertStringify(
- (async function* () {
- yield* yielded;
- throw errored;
- })(),
- `(async function*(_,f,v,l,i,p=a=>l=new Promise((r,j)=>{f=_.r=r;_.j=j}),a=((_.f=v=>{f(v);a.push(p())}),[p()])){for(i of a)v=await i,i!=l&&(yield v);return v})(_.a={})`,
- `_.a.f(_.c={x:1})`,
- `_.a.f(_.d={y:2})`,
- `_.a.j(_.e=new Error("boom"))`,
- );
-
- assert.deepEqual(await consumeIterator(result), {
- yielded,
- errored,
- returned: undefined,
+ describe("serializer", () => {
+ it("multiple flushes", () => {
+ const serializer = assertSerializer();
+ serializer.assertStringify({ a: 1 }, `{a:1}`);
+ serializer.assertStringify({ a: 2 }, `{a:2}`);
});
- });
-});
-describe("readable stream", () => {
- it("resolves with sync flushes", async () => {
- const serializer = assertSerializer();
- const yielded = [{ x: 1 }, { y: 2 }];
- const stream = new ReadableStream({
- start(ctrl) {
- for (const value of yielded) {
- ctrl.enqueue(value);
- }
- ctrl.close();
- },
+ it("multiple flushes with shared references", () => {
+ const serializer = assertSerializer();
+ const obj = { a: 1 };
+ serializer.assertStringify(obj, `{a:1}`);
+ serializer.assertStringify(obj, `_.a`);
});
- const [result] = await serializer.assertStringify(
- stream,
- `new ReadableStream({start(c){(async(_,f,v,l,i,p=a=>l=new Promise((r,j)=>{f=_.r=r;_.j=j}),a=((_.f=v=>{f(v);a.push(p())}),[p()]))=>{for(i of a)v=await i,i==l?c.close():c.enqueue(v)})(_.a={}).catch(e=>c.error(e))}})`,
- `_.a.f(_.c={x:1});_.a.f(_.d={y:2})`,
- `_.a.r()`,
- );
- assert.deepEqual(await consumeReader(result.getReader()), {
- yielded,
- returned: undefined,
- errored: undefined,
+ it("multiple flushes with shared references and nested", () => {
+ const serializer = assertSerializer();
+ const nested = { b: 1 };
+ const obj = { a: nested };
+ serializer.assertStringify(obj, `{a:{b:1}}`);
+ serializer.assertStringify({ c: nested }, `{c:_.b=_.a.a}`);
+ serializer.assertStringify({ d: nested }, `{d:_.b}`);
});
});
- it("resolves with async flushes", async () => {
- const serializer = assertSerializer();
- const yielded = [{ x: 1 }, { y: 2 }];
- const stream = new ReadableStream({
- async start(ctrl) {
- for (const value of yielded) {
- ctrl.enqueue(value);
- await new Promise((r) => setTimeout(r, 0));
- }
- ctrl.close();
- },
+ describe("promise", () => {
+ it("resolves", async () => {
+ const serializer = assertSerializer();
+ const obj = { x: 1 };
+ const promise = Promise.resolve(obj);
+ const [result] = await serializer.assertStringify(
+ promise,
+ `new Promise((f,r)=>_.a={f,r})`,
+ `_.a.f(_.a={x:1})`,
+ );
+ assert.deepEqual(serializer.get("a"), obj);
+ assert.deepEqual(await result, obj);
});
- const [result] = await serializer.assertStringify(
- stream,
- `new ReadableStream({start(c){(async(_,f,v,l,i,p=a=>l=new Promise((r,j)=>{f=_.r=r;_.j=j}),a=((_.f=v=>{f(v);a.push(p())}),[p()]))=>{for(i of a)v=await i,i==l?c.close():c.enqueue(v)})(_.a={}).catch(e=>c.error(e))}})`,
- `_.a.f(_.c={x:1})`,
- `_.a.f(_.d={y:2})`,
- `_.a.r()`,
- );
- assert.deepEqual(await consumeReader(result.getReader()), {
- yielded,
- returned: undefined,
- errored: undefined,
+ it("rejects", async () => {
+ const serializer = assertSerializer();
+ const error = new Error("test");
+ const promise = Promise.reject(error);
+ const [result] = await serializer.assertStringify(
+ promise,
+ `new Promise((f,r)=>_.a={f,r})`,
+ `_.a.r(_.a=new Error("test"))`,
+ );
+ assert.deepEqual(serializer.get("a"), error);
+ await assert.rejects(result, error);
});
});
- it("rejects sync", async () => {
- const serializer = assertSerializer();
- const yielded = [{ x: 1 }, { y: 2 }];
- const errored = new Error("boom");
- const stream = new ReadableStream({
- start(ctrl) {
- for (const value of yielded) {
- ctrl.enqueue(value);
- }
- ctrl.error(errored);
- },
- });
- const [result] = await serializer.assertStringify(
- stream,
- `new ReadableStream({start(c){(async(_,f,v,l,i,p=a=>l=new Promise((r,j)=>{f=_.r=r;_.j=j}),a=((_.f=v=>{f(v);a.push(p())}),[p()]))=>{for(i of a)v=await i,i==l?c.close():c.enqueue(v)})(_.a={}).catch(e=>c.error(e))}})`,
- `_.a.j(_.c=new Error("boom"))`,
- );
+ describe("async generator", () => {
+ it("resolves", async () => {
+ const serializer = assertSerializer();
+ const yielded = [{ x: 1 }, { y: 2 }];
+ const returned = { z: 3 };
+ const [result] = await serializer.assertStringify(
+ (async function* () {
+ yield* yielded;
+ return returned;
+ })(),
+ `(async function*(_,f,v,l,i,p=a=>l=new Promise((r,j)=>{f=_.r=r;_.j=j}),a=((_.f=v=>{f(v);a.push(p())}),[p()])){for(i of a)v=await i,i!=l&&(yield v);return v})(_.a={})`,
+ `_.a.f(_.c={x:1})`,
+ `_.a.f(_.d={y:2})`,
+ `_.a.r(_.e={z:3})`,
+ );
- assert.deepEqual(await consumeReader(result.getReader()), {
- yielded: [],
- returned: undefined,
- errored,
+ assert.deepEqual(await consumeIterator(result), {
+ yielded,
+ returned,
+ errored: undefined,
+ });
+ });
+
+ it("rejects", async () => {
+ const serializer = assertSerializer();
+ const yielded = [{ x: 1 }, { y: 2 }];
+ const errored = new Error("boom");
+ const [result] = await serializer.assertStringify(
+ (async function* () {
+ yield* yielded;
+ throw errored;
+ })(),
+ `(async function*(_,f,v,l,i,p=a=>l=new Promise((r,j)=>{f=_.r=r;_.j=j}),a=((_.f=v=>{f(v);a.push(p())}),[p()])){for(i of a)v=await i,i!=l&&(yield v);return v})(_.a={})`,
+ `_.a.f(_.c={x:1})`,
+ `_.a.f(_.d={y:2})`,
+ `_.a.j(_.e=new Error("boom"))`,
+ );
+
+ assert.deepEqual(await consumeIterator(result), {
+ yielded,
+ errored,
+ returned: undefined,
+ });
});
});
- it("rejects async", async () => {
- const serializer = assertSerializer();
- const yielded = [{ x: 1 }];
- const errored = new Error("boom");
- const stream = new ReadableStream({
- async start(ctrl) {
- for (const value of yielded) {
- ctrl.enqueue(value);
- await new Promise((r) => setTimeout(r, 0));
- }
- ctrl.error(errored);
- },
- });
- const [result] = await serializer.assertStringify(
- stream,
- `new ReadableStream({start(c){(async(_,f,v,l,i,p=a=>l=new Promise((r,j)=>{f=_.r=r;_.j=j}),a=((_.f=v=>{f(v);a.push(p())}),[p()]))=>{for(i of a)v=await i,i==l?c.close():c.enqueue(v)})(_.a={}).catch(e=>c.error(e))}})`,
- `_.a.f(_.c={x:1})`,
- `_.a.j(_.d=new Error("boom"))`,
- );
-
- assert.deepEqual(await consumeReader(result.getReader()), {
- yielded,
- returned: undefined,
- errored,
- });
- });
-});
-
-describe("request", () => {
- it("url only", () =>
- assertStringify(
- new Request("https://ebay.com/"),
- `new Request("https://ebay.com/")`,
- ));
- it("method", () =>
- assertStringify(
- new Request("https://ebay.com/", { method: "POST" }),
- `new Request("https://ebay.com/",{method:"POST"})`,
- ));
-
- it("headers", () => {
- const req = new Request("https://ebay.com/", {
- headers: { "content-type": "text/plain" },
- method: "POST",
- });
- assertStringify(
- req,
- `new Request("https://ebay.com/",{headers:{"content-type":"text/plain"},method:"POST"})`,
- );
-
- assertStringify(
- { req: req, headers: req.headers },
- `{req:_.b=new Request("https://ebay.com/",{headers:{"content-type":"text/plain"},method:"POST"}),headers:_.a=_.b.headers}`,
- );
- });
-
- it("cache", () =>
- assertStringify(
- new Request("https://ebay.com/", { cache: "no-store" }),
- `new Request("https://ebay.com/",{cache:"no-store"})`,
- ));
-
- it("credentials", () =>
- assertStringify(
- new Request("https://ebay.com/", { credentials: "omit" }),
- `new Request("https://ebay.com/",{credentials:"omit"})`,
- ));
-
- it("integrity", () =>
- assertStringify(
- new Request("https://ebay.com/", { integrity: "sha-256" }),
- `new Request("https://ebay.com/",{integrity:"sha-256"})`,
- ));
-
- it("keepalive", () =>
- assertStringify(
- new Request("https://ebay.com/", { keepalive: true }),
- `new Request("https://ebay.com/",{keepalive:true})`,
- ));
-
- it("mode", () =>
- assertStringify(
- new Request("https://ebay.com/", { mode: "no-cors" }),
- `new Request("https://ebay.com/",{mode:"no-cors"})`,
- ));
-
- it("redirect", () =>
- assertStringify(
- new Request("https://ebay.com/", { redirect: "manual" }),
- `new Request("https://ebay.com/",{redirect:"manual"})`,
- ));
-
- it("referrer", () =>
- assertStringify(
- new Request("https://ebay.com/", { referrer: "https://google.com/" }),
- `new Request("https://ebay.com/",{referrer:"https://google.com/"})`,
- ));
-
- it("referrerPolicy", () =>
- assertStringify(
- new Request("https://ebay.com/", { referrerPolicy: "no-referrer" }),
- `new Request("https://ebay.com/",{referrerPolicy:"no-referrer"})`,
- ));
-
- it("body", async () => {
- const serializer = assertSerializer();
- const obj = { a: 1 };
- const request = new Request("https://ebay.com/", {
- method: "POST",
- duplex: "half",
- body: JSON.stringify(obj),
- } as any);
- const [result] = await serializer.assertStringify(
- request,
- `new Request("https://ebay.com/",{body:new ReadableStream({start(c){(async(_,f,v,l,i,p=a=>l=new Promise((r,j)=>{f=_.r=r;_.j=j}),a=((_.f=v=>{f(v);a.push(p())}),[p()]))=>{for(i of a)v=await i,i==l?c.close():c.enqueue(v)})(_.a={}).catch(e=>c.error(e))}}),duplex:"half",headers:{"content-type":"text/plain;charset=UTF-8"},method:"POST"})`,
- `_.a.f(_.c=new Uint8Array([123,34,97,34,58,49,125]))`,
- `_.a.r()`,
- );
-
- assert.deepEqual(await result.json(), obj);
- });
-});
-
-describe("response", () => {
- it("empty", () => assertStringify(new Response(), `new Response`));
- it("status", () =>
- assertStringify(
- new Response(null, { status: 301 }),
- `new Response(null,{status:301})`,
- ));
- it("statusText", () =>
- assertStringify(
- new Response(null, { statusText: "Moved Permanently" }),
- `new Response(null,{statusText:"Moved Permanently"})`,
- ));
- it("headers", () => {
- const res = new Response(null, { headers: { a: "1", b: "2" } });
- assertStringify(res, `new Response(null,{headers:{a:"1",b:"2"}})`);
-
- assertStringify(
- { res: res, headers: res.headers },
- `{res:_.b=new Response(null,{headers:{a:"1",b:"2"}}),headers:_.a=_.b.headers}`,
- );
- });
-
- it("buffer", async () => {
- const serializer = assertSerializer();
- const response = new Response(new Int8Array([116, 101, 115, 116]));
- const [result] = await serializer.assertStringify(
- response,
- `new Response(new ReadableStream({start(c){(async(_,f,v,l,i,p=a=>l=new Promise((r,j)=>{f=_.r=r;_.j=j}),a=((_.f=v=>{f(v);a.push(p())}),[p()]))=>{for(i of a)v=await i,i==l?c.close():c.enqueue(v)})(_.a={}).catch(e=>c.error(e))}}))`,
- `_.a.f(_.c=new Uint8Array([116,101,115,116]))`,
- `_.a.r()`,
- );
-
- assert.deepEqual(await consumeReader(result.body!.getReader()), {
- yielded: [new Uint8Array([116, 101, 115, 116])],
- errored: undefined,
- returned: undefined,
- });
- });
-
- it("ReadableStream string encoded", async () => {
- const serializer = assertSerializer();
- const encoder = new TextEncoder();
- const response = new Response(
- new ReadableStream({
- async start(ctrl) {
- ctrl.enqueue(encoder.encode("first"));
- await new Promise((r) => setTimeout(r, 0));
- ctrl.enqueue(encoder.encode("second"));
- await new Promise((r) => setTimeout(r, 0));
- ctrl.enqueue(encoder.encode("third"));
+ describe("readable stream", () => {
+ it("resolves with sync flushes", async () => {
+ const serializer = assertSerializer();
+ const yielded = [{ x: 1 }, { y: 2 }];
+ const stream = new ReadableStream({
+ start(ctrl) {
+ for (const value of yielded) {
+ ctrl.enqueue(value);
+ }
ctrl.close();
},
- }),
- );
+ });
+ const [result] = await serializer.assertStringify(
+ stream,
+ `new ReadableStream({start(c){(async(_,f,v,l,i,p=a=>l=new Promise((r,j)=>{f=_.r=r;_.j=j}),a=((_.f=v=>{f(v);a.push(p())}),[p()]))=>{for(i of a)v=await i,i==l?c.close():c.enqueue(v)})(_.a={}).catch(e=>c.error(e))}})`,
+ `_.a.f(_.c={x:1}),_.a.f(_.d={y:2})`,
+ `_.a.r()`,
+ );
- const [result] = await serializer.assertStringify(
- response,
- `new Response(new ReadableStream({start(c){(async(_,f,v,l,i,p=a=>l=new Promise((r,j)=>{f=_.r=r;_.j=j}),a=((_.f=v=>{f(v);a.push(p())}),[p()]))=>{for(i of a)v=await i,i==l?c.close():c.enqueue(v)})(_.a={}).catch(e=>c.error(e))}}))`,
- `_.a.f(_.c=new Uint8Array([102,105,114,115,116]))`,
- `_.a.f(_.d=new Uint8Array([115,101,99,111,110,100]))`,
- `_.a.f(_.e=new Uint8Array([116,104,105,114,100]));_.a.r()`,
- );
+ assert.deepEqual(await consumeReader(result.getReader()), {
+ yielded,
+ returned: undefined,
+ errored: undefined,
+ });
+ });
- assert.equal(await result.text(), "firstsecondthird");
+ it("resolves with async flushes", async () => {
+ const serializer = assertSerializer();
+ const yielded = [{ x: 1 }, { y: 2 }];
+ const stream = new ReadableStream({
+ async start(ctrl) {
+ for (const value of yielded) {
+ ctrl.enqueue(value);
+ await new Promise((r) => setTimeout(r, 0));
+ }
+ ctrl.close();
+ },
+ });
+ const [result] = await serializer.assertStringify(
+ stream,
+ `new ReadableStream({start(c){(async(_,f,v,l,i,p=a=>l=new Promise((r,j)=>{f=_.r=r;_.j=j}),a=((_.f=v=>{f(v);a.push(p())}),[p()]))=>{for(i of a)v=await i,i==l?c.close():c.enqueue(v)})(_.a={}).catch(e=>c.error(e))}})`,
+ `_.a.f(_.c={x:1})`,
+ `_.a.f(_.d={y:2})`,
+ `_.a.r()`,
+ );
+
+ assert.deepEqual(await consumeReader(result.getReader()), {
+ yielded,
+ returned: undefined,
+ errored: undefined,
+ });
+ });
+
+ it("rejects sync", async () => {
+ const serializer = assertSerializer();
+ const yielded = [{ x: 1 }, { y: 2 }];
+ const errored = new Error("boom");
+ const stream = new ReadableStream({
+ start(ctrl) {
+ for (const value of yielded) {
+ ctrl.enqueue(value);
+ }
+ ctrl.error(errored);
+ },
+ });
+ const [result] = await serializer.assertStringify(
+ stream,
+ `new ReadableStream({start(c){(async(_,f,v,l,i,p=a=>l=new Promise((r,j)=>{f=_.r=r;_.j=j}),a=((_.f=v=>{f(v);a.push(p())}),[p()]))=>{for(i of a)v=await i,i==l?c.close():c.enqueue(v)})(_.a={}).catch(e=>c.error(e))}})`,
+ `_.a.j(_.c=new Error("boom"))`,
+ );
+
+ assert.deepEqual(await consumeReader(result.getReader()), {
+ yielded: [],
+ returned: undefined,
+ errored,
+ });
+ });
+
+ it("rejects async", async () => {
+ const serializer = assertSerializer();
+ const yielded = [{ x: 1 }];
+ const errored = new Error("boom");
+ const stream = new ReadableStream({
+ async start(ctrl) {
+ for (const value of yielded) {
+ ctrl.enqueue(value);
+ await new Promise((r) => setTimeout(r, 0));
+ }
+ ctrl.error(errored);
+ },
+ });
+ const [result] = await serializer.assertStringify(
+ stream,
+ `new ReadableStream({start(c){(async(_,f,v,l,i,p=a=>l=new Promise((r,j)=>{f=_.r=r;_.j=j}),a=((_.f=v=>{f(v);a.push(p())}),[p()]))=>{for(i of a)v=await i,i==l?c.close():c.enqueue(v)})(_.a={}).catch(e=>c.error(e))}})`,
+ `_.a.f(_.c={x:1})`,
+ `_.a.j(_.d=new Error("boom"))`,
+ );
+
+ assert.deepEqual(await consumeReader(result.getReader()), {
+ yielded,
+ returned: undefined,
+ errored,
+ });
+ });
});
- it("json", async () => {
- const serializer = assertSerializer();
- const response = new Response(JSON.stringify({ a: 1 }), {
- headers: { "content-type": "application/json" },
- });
- const [result] = await serializer.assertStringify(
- response,
- `new Response(new ReadableStream({start(c){(async(_,f,v,l,i,p=a=>l=new Promise((r,j)=>{f=_.r=r;_.j=j}),a=((_.f=v=>{f(v);a.push(p())}),[p()]))=>{for(i of a)v=await i,i==l?c.close():c.enqueue(v)})(_.a={}).catch(e=>c.error(e))}}),{headers:{"content-type":"application/json"}})`,
- `_.a.f(_.c=new Uint8Array([123,34,97,34,58,49,125]))`,
- `_.a.r()`,
- );
+ describe("request", () => {
+ it("url only", () =>
+ assertStringify(
+ new Request("https://ebay.com/"),
+ `new Request("https://ebay.com/")`,
+ ));
+ it("method", () =>
+ assertStringify(
+ new Request("https://ebay.com/", { method: "POST" }),
+ `new Request("https://ebay.com/",{method:"POST"})`,
+ ));
- assert.deepEqual(await result.json(), { a: 1 });
+ it("headers", () => {
+ const req = new Request("https://ebay.com/", {
+ headers: { "content-type": "text/plain" },
+ method: "POST",
+ });
+ assertStringify(
+ req,
+ `new Request("https://ebay.com/",{headers:{"content-type":"text/plain"},method:"POST"})`,
+ );
+
+ assertStringify(
+ { req: req, headers: req.headers },
+ `{req:_.b=new Request("https://ebay.com/",{headers:{"content-type":"text/plain"},method:"POST"}),headers:_.a=_.b.headers}`,
+ );
+ });
+
+ it("cache", () =>
+ assertStringify(
+ new Request("https://ebay.com/", { cache: "no-store" }),
+ `new Request("https://ebay.com/",{cache:"no-store"})`,
+ ));
+
+ it("credentials", () =>
+ assertStringify(
+ new Request("https://ebay.com/", { credentials: "omit" }),
+ `new Request("https://ebay.com/",{credentials:"omit"})`,
+ ));
+
+ it("integrity", () =>
+ assertStringify(
+ new Request("https://ebay.com/", { integrity: "sha-256" }),
+ `new Request("https://ebay.com/",{integrity:"sha-256"})`,
+ ));
+
+ it("keepalive", () =>
+ assertStringify(
+ new Request("https://ebay.com/", { keepalive: true }),
+ `new Request("https://ebay.com/",{keepalive:true})`,
+ ));
+
+ it("mode", () =>
+ assertStringify(
+ new Request("https://ebay.com/", { mode: "no-cors" }),
+ `new Request("https://ebay.com/",{mode:"no-cors"})`,
+ ));
+
+ it("redirect", () =>
+ assertStringify(
+ new Request("https://ebay.com/", { redirect: "manual" }),
+ `new Request("https://ebay.com/",{redirect:"manual"})`,
+ ));
+
+ it("referrer", () =>
+ assertStringify(
+ new Request("https://ebay.com/", { referrer: "https://google.com/" }),
+ `new Request("https://ebay.com/",{referrer:"https://google.com/"})`,
+ ));
+
+ it("referrerPolicy", () =>
+ assertStringify(
+ new Request("https://ebay.com/", { referrerPolicy: "no-referrer" }),
+ `new Request("https://ebay.com/",{referrerPolicy:"no-referrer"})`,
+ ));
+
+ it("body", async () => {
+ const serializer = assertSerializer();
+ const obj = { a: 1 };
+ const request = new Request("https://ebay.com/", {
+ method: "POST",
+ duplex: "half",
+ body: JSON.stringify(obj),
+ } as any);
+ const [result] = await serializer.assertStringify(
+ request,
+ `new Request("https://ebay.com/",{body:new ReadableStream({start(c){(async(_,f,v,l,i,p=a=>l=new Promise((r,j)=>{f=_.r=r;_.j=j}),a=((_.f=v=>{f(v);a.push(p())}),[p()]))=>{for(i of a)v=await i,i==l?c.close():c.enqueue(v)})(_.a={}).catch(e=>c.error(e))}}),duplex:"half",headers:{"content-type":"text/plain;charset=UTF-8"},method:"POST"})`,
+ `_.a.f(_.c=new Uint8Array([123,34,97,34,58,49,125]))`,
+ `_.a.r()`,
+ );
+
+ assert.deepEqual(await result.json(), obj);
+ });
+ });
+
+ describe("response", () => {
+ it("empty", () => assertStringify(new Response(), `new Response`));
+ it("status", () =>
+ assertStringify(
+ new Response(null, { status: 301 }),
+ `new Response(null,{status:301})`,
+ ));
+ it("statusText", () =>
+ assertStringify(
+ new Response(null, { statusText: "Moved Permanently" }),
+ `new Response(null,{statusText:"Moved Permanently"})`,
+ ));
+ it("headers", () => {
+ const res = new Response(null, { headers: { a: "1", b: "2" } });
+ assertStringify(res, `new Response(null,{headers:{a:"1",b:"2"}})`);
+
+ assertStringify(
+ { res: res, headers: res.headers },
+ `{res:_.b=new Response(null,{headers:{a:"1",b:"2"}}),headers:_.a=_.b.headers}`,
+ );
+ });
+
+ it("buffer", async () => {
+ const serializer = assertSerializer();
+ const response = new Response(new Int8Array([116, 101, 115, 116]));
+ const [result] = await serializer.assertStringify(
+ response,
+ `new Response(new ReadableStream({start(c){(async(_,f,v,l,i,p=a=>l=new Promise((r,j)=>{f=_.r=r;_.j=j}),a=((_.f=v=>{f(v);a.push(p())}),[p()]))=>{for(i of a)v=await i,i==l?c.close():c.enqueue(v)})(_.a={}).catch(e=>c.error(e))}}))`,
+ `_.a.f(_.c=new Uint8Array([116,101,115,116]))`,
+ `_.a.r()`,
+ );
+
+ assert.deepEqual(await consumeReader(result.body!.getReader()), {
+ yielded: [new Uint8Array([116, 101, 115, 116])],
+ errored: undefined,
+ returned: undefined,
+ });
+ });
+
+ it("ReadableStream string encoded", async () => {
+ const serializer = assertSerializer();
+ const encoder = new TextEncoder();
+ const response = new Response(
+ new ReadableStream({
+ async start(ctrl) {
+ ctrl.enqueue(encoder.encode("first"));
+ await new Promise((r) => setTimeout(r, 0));
+ ctrl.enqueue(encoder.encode("second"));
+ await new Promise((r) => setTimeout(r, 0));
+ ctrl.enqueue(encoder.encode("third"));
+ ctrl.close();
+ },
+ }),
+ );
+
+ const [result] = await serializer.assertStringify(
+ response,
+ `new Response(new ReadableStream({start(c){(async(_,f,v,l,i,p=a=>l=new Promise((r,j)=>{f=_.r=r;_.j=j}),a=((_.f=v=>{f(v);a.push(p())}),[p()]))=>{for(i of a)v=await i,i==l?c.close():c.enqueue(v)})(_.a={}).catch(e=>c.error(e))}}))`,
+ `_.a.f(_.c=new Uint8Array([102,105,114,115,116]))`,
+ `_.a.f(_.d=new Uint8Array([115,101,99,111,110,100]))`,
+ `_.a.f(_.e=new Uint8Array([116,104,105,114,100])),_.a.r()`,
+ );
+
+ assert.equal(await result.text(), "firstsecondthird");
+ });
+
+ it("json", async () => {
+ const serializer = assertSerializer();
+ const response = new Response(JSON.stringify({ a: 1 }), {
+ headers: { "content-type": "application/json" },
+ });
+ const [result] = await serializer.assertStringify(
+ response,
+ `new Response(new ReadableStream({start(c){(async(_,f,v,l,i,p=a=>l=new Promise((r,j)=>{f=_.r=r;_.j=j}),a=((_.f=v=>{f(v);a.push(p())}),[p()]))=>{for(i of a)v=await i,i==l?c.close():c.enqueue(v)})(_.a={}).catch(e=>c.error(e))}}),{headers:{"content-type":"application/json"}})`,
+ `_.a.f(_.c=new Uint8Array([123,34,97,34,58,49,125]))`,
+ `_.a.r()`,
+ );
+
+ assert.deepEqual(await result.json(), { a: 1 });
+ });
});
});
@@ -1180,22 +1199,46 @@ function assertSerializer(ctx: Record = {}) {
first: string,
...flushes: string[]
): [T] | Promise<[T]> {
+ const createDeferred = () => {
+ let resolve!: () => void;
+ let reject!: (err: unknown) => void;
+ const promise = new Promise((res, rej) => {
+ resolve = res;
+ reject = rej;
+ });
+ return Object.assign(promise, { resolve, reject });
+ };
+
+ let promiseIndex = 0;
+ const promises: ReturnType[] = [];
+ const boundary = {
+ signal: {
+ aborted: false,
+ },
+ startAsync() {
+ promises.push(createDeferred());
+ },
+ endAsync() {
+ promises[promiseIndex++].resolve();
+ },
+ } as any as Boundary;
const result = assertSerializedIsActual(
val,
- serializer.stringify(val),
+ serializer.stringify(val, boundary),
first,
ctx,
);
if (flushes.length) {
return (async () => {
+ let promiseIndex = 0;
for (const flush of flushes) {
- const actual = await serializer.pending;
+ await promises[promiseIndex++];
+ const actual = serializer.stringify(undefined, boundary);
assert.equal(actual?.replace(/^_=>[({](.*?)[})]$/, "$1"), flush);
(0, eval)(actual)(ctx);
}
- assert.equal(serializer.pending, undefined);
return [result];
})();
} else {
diff --git a/packages/runtime-tags/src/common/compat-meta.ts b/packages/runtime-tags/src/common/compat-meta.ts
new file mode 100644
index 000000000..a30e35ff5
--- /dev/null
+++ b/packages/runtime-tags/src/common/compat-meta.ts
@@ -0,0 +1,3 @@
+const prefix = MARKO_DEBUG ? "$compat_" : "$C_";
+export const RENDERER_REGISTER_ID = prefix + (MARKO_DEBUG ? "renderer" : "r");
+export const SET_SCOPE_REGISTER_ID = prefix + (MARKO_DEBUG ? "setScope" : "s");
diff --git a/packages/runtime-tags/src/common/meta.ts b/packages/runtime-tags/src/common/meta.ts
new file mode 100644
index 000000000..a210c32ab
--- /dev/null
+++ b/packages/runtime-tags/src/common/meta.ts
@@ -0,0 +1,2 @@
+export const DEFAULT_RUNTIME_ID = "M";
+export const DEFAULT_RENDER_ID = "_";
diff --git a/packages/runtime-tags/src/common/types.ts b/packages/runtime-tags/src/common/types.ts
index ed8388e50..c7d663905 100644
--- a/packages/runtime-tags/src/common/types.ts
+++ b/packages/runtime-tags/src/common/types.ts
@@ -33,7 +33,6 @@ export enum ResumeSymbol {
PlaceholderStart = "",
PlaceholderEnd = "",
ReplacementId = "",
- VarResume = "$h",
VarReorderRuntime = "$r",
}
@@ -97,8 +96,19 @@ export enum WalkRangeSize {
}
export type Accessor = string | number;
-export type Input = Record;
-export type Context = Record;
+export interface $Global {
+ [x: PropertyKey]: unknown;
+ signal?: AbortSignal;
+ cspNonce?: string;
+ renderId?: string;
+ runtimeId?: string;
+}
+export interface Input {
+ [x: PropertyKey]: unknown;
+}
+export interface TemplateInput extends Input {
+ $global?: $Global;
+}
export interface Template {
_: unknown;
@@ -115,7 +125,7 @@ export interface TemplateInstance {
destroy(): void;
}
-export type RenderResult = Promise &
+export type RenderResult = PromiseLike &
AsyncIterable & {
toReadable(): ReadableStream;
};
diff --git a/packages/runtime-tags/src/dom.ts b/packages/runtime-tags/src/dom.ts
index b0c2da595..95094aee5 100644
--- a/packages/runtime-tags/src/dom.ts
+++ b/packages/runtime-tags/src/dom.ts
@@ -30,7 +30,6 @@ export {
registerRenderer,
registerBoundSignal,
registerSubscriber,
- scopeLookup,
} from "./dom/resume";
export {
diff --git a/packages/runtime-tags/src/dom/compat.ts b/packages/runtime-tags/src/dom/compat.ts
index ee1b8e963..32b753bd1 100644
--- a/packages/runtime-tags/src/dom/compat.ts
+++ b/packages/runtime-tags/src/dom/compat.ts
@@ -1,3 +1,8 @@
+import {
+ RENDERER_REGISTER_ID,
+ SET_SCOPE_REGISTER_ID,
+} from "../common/compat-meta";
+import type { Scope } from "../dom";
import { patchConditionals } from "./control-flow";
import { prepare, queueEffect, runEffects } from "./queue";
import {
@@ -5,13 +10,21 @@ import {
createScopeWithRenderer,
type Renderer,
} from "./renderer";
-import { getRegisteredWithScope, register, scopeLookup } from "./resume";
+import { getRegisteredWithScope, register } from "./resume";
import { CLEAN, DIRTY, MARK } from "./signals";
+const classIdToScope = new Map();
export const compat = {
- register,
patchConditionals,
queueEffect,
+ init() {
+ register(SET_SCOPE_REGISTER_ID, (scope: Scope & { m5c: string }) => {
+ classIdToScope.set(scope.m5c, scope);
+ });
+ },
+ registerRenderer(fn: any) {
+ register(RENDERER_REGISTER_ID, fn);
+ },
isOp(value: any) {
return value === MARK || value === CLEAN || value === DIRTY;
},
@@ -28,18 +41,24 @@ export const compat = {
runComponentEffects(this: any) {
runEffects(this.effects);
},
- resolveRenderer(renderer: any) {
- if (renderer && typeof renderer === "object") {
- if (Array.isArray(renderer)) {
- const [registerId, scopeId] = renderer;
- const scope = scopeLookup[scopeId];
- return getRegisteredWithScope(registerId, scope);
- }
-
- if (renderer.___clone) {
- return renderer;
- }
+ resolveRegistered(
+ value: any,
+ {
+ runtimeId,
+ componentIdPrefix,
+ }: { runtimeId: string; componentIdPrefix: string },
+ ) {
+ if (Array.isArray(value) && typeof value[0] === "string") {
+ return getRegisteredWithScope(
+ value[0],
+ value.length === 2 &&
+ (window as any)[runtimeId]?.[
+ componentIdPrefix === "s" ? "_" : componentIdPrefix
+ ]?.___scopeLookup[value[1]],
+ );
}
+
+ return value;
},
createRenderer(
setup: Renderer["___setup"],
@@ -50,19 +69,19 @@ export const compat = {
renderer.___clone = clone;
return renderer;
},
- render(
- isHydrate: boolean,
- out: any,
- component: any,
- renderer: Renderer,
- input: any,
- ) {
+ render(out: any, component: any, renderer: Renderer, input: any) {
+ let scope: Scope = component.scope;
+
+ if (!scope) {
+ scope = classIdToScope.get(component.id)!;
+ if (scope) {
+ component.scope = scope;
+ classIdToScope.delete(component.id);
+ }
+ }
+
const args = renderer.___args || noop;
let existing = false;
- let scope: any = isHydrate
- ? (component.scope =
- scopeLookup[(out.global.componentIdToScopeId as any)[component.id]])
- : component.scope;
component.effects = prepare(() => {
if (!scope) {
diff --git a/packages/runtime-tags/src/dom/resume.ts b/packages/runtime-tags/src/dom/resume.ts
index 7bad316c9..933f83675 100644
--- a/packages/runtime-tags/src/dom/resume.ts
+++ b/packages/runtime-tags/src/dom/resume.ts
@@ -3,10 +3,156 @@ import type { Renderer } from "./renderer";
import { bindRenderer } from "./scope";
import type { IntersectionSignal, SignalOp, ValueSignal } from "./signals";
+interface Renders {
+ (renderId: string): Render | RenderData;
+ [renderId: string]: Render | RenderData;
+}
+interface RenderData {
+ // RuntimeID + ResumeID
+ i: string;
+ // Marked nodes to visit
+ v: Comment[];
+ // Effect calls.
+ e?: (string | number)[];
+ // Scopes
+ s?: ((ctx: object) => Record)[];
+ // Indicates that the render is done.
+ d?: 1;
+ w(): void;
+}
type RegisteredFn = (scope: S) => void;
const registeredValues: Record = {};
-const doc = document;
+
+class Render implements RenderData {
+ declare i: string;
+ declare v: Comment[];
+ declare e?: (string | number)[];
+ declare s?: ((ctx: object) => Record)[];
+ private declare ___currentScopeId: number;
+ private declare ___data: RenderData;
+ private declare ___renders: Renders;
+ private declare ___runtimeId: string;
+ private declare ___renderId: string;
+ private ___scopeStack: number[] = [];
+ private ___scopeLookup: Record = {};
+ private ___serializeContext: Record = {
+ _: registeredValues,
+ };
+ constructor(renders: Renders, runtimeId: string, renderId: string) {
+ this.___renders = renders;
+ this.___runtimeId = runtimeId;
+ this.___renderId = renderId;
+ this.___data = renders[renderId] as RenderData;
+ this.___resume();
+ }
+ w() {
+ this.___data.w();
+ this.___resume();
+ }
+ ___resume() {
+ const data = this.___data;
+ const serializeContext = this.___serializeContext;
+ const scopeLookup = this.___scopeLookup;
+ const visits = data.v;
+
+ if (visits.length) {
+ const commentPrefix = data.i;
+ const commentPrefixLen = commentPrefix.length;
+ data.v = [];
+
+ for (const visit of visits) {
+ const commentText = visit.data!;
+ const token = commentText[commentPrefixLen];
+ const scopeId = parseInt(commentText.slice(commentPrefixLen + 1));
+ const scope = (scopeLookup[scopeId] ??= {} as Scope);
+ const data = commentText.slice(commentText.indexOf(" ") + 1);
+
+ if (token === ResumeSymbol.Node) {
+ scope[data] = visit.previousSibling;
+ } else if (token === ResumeSymbol.SectionStart) {
+ this.___scopeStack.push(this.___currentScopeId);
+ this.___currentScopeId = scopeId;
+ scope.___startNode = visit;
+ } else if (token === ResumeSymbol.SectionEnd) {
+ scope[data] = visit;
+ if (scopeId < this.___currentScopeId) {
+ const currScope = scopeLookup[this.___currentScopeId];
+ const currParent = visit.parentNode!;
+ const startNode = currScope.___startNode as Node;
+ if (currParent !== startNode.parentNode) {
+ currParent.prepend(startNode);
+ }
+ currScope.___endNode = visit.previousSibling!;
+ this.___currentScopeId = this.___scopeStack.pop()!;
+ }
+ } else if (token === ResumeSymbol.SectionSingleNodesEnd) {
+ scope[
+ MARKO_DEBUG ? data.slice(0, data.indexOf(" ")) : parseInt(data)
+ ] = visit;
+ // https://jsben.ch/dR7uk
+ const childScopeIds = JSON.parse(
+ "[" + data.slice(data.indexOf(" ") + 1) + "]",
+ );
+ let curNode: ChildNode = visit;
+ for (let i = childScopeIds.length - 1; i >= 0; i--) {
+ const childScope = (scopeLookup[childScopeIds[i]] ??= {} as Scope);
+ // TODO: consider whether the single node optimization
+ // should only apply to elements which means could
+ // use previousElementSibling instead of a while loop
+ while (
+ (curNode = curNode.previousSibling!).nodeType ===
+ 8 /* Node.COMMENT_NODE */
+ );
+ // TODO: consider only setting ___startNode?
+ childScope.___startNode = childScope.___endNode = curNode;
+ }
+ }
+ }
+ }
+
+ const serializedScopes = data.s;
+ if (serializedScopes) {
+ data.s = [];
+
+ for (const deserializeScopes of serializedScopes) {
+ const scopes = deserializeScopes(serializeContext);
+ let { $global } = scopeLookup;
+
+ if (!$global) {
+ scopeLookup.$global = $global = scopes.$ || {};
+ $global.runtimeId = this.___runtimeId;
+ $global.renderId = this.___renderId;
+ }
+
+ for (const scopeId in scopes) {
+ if (scopeId !== "$") {
+ const scope = scopes[scopeId];
+ const prevScope = scopeLookup[scopeId];
+ scope.$global = $global;
+ if (prevScope !== scope) {
+ scopeLookup[scopeId] = Object.assign(scope, prevScope) as Scope;
+ }
+ }
+ }
+ }
+ }
+
+ const effects = data.e;
+ if (effects) {
+ data.e = [];
+ for (let i = 0; i < effects.length; i += 2) {
+ (registeredValues[effects[i + 1] as string] as RegisteredFn)(
+ scopeLookup[effects[i] as number],
+ );
+ }
+ }
+
+ if (data.d) {
+ delete this.___renders[this.___renderId];
+ }
+ }
+}
export function register(id: string, obj: T): T {
registeredValues[id] = obj;
@@ -42,133 +188,47 @@ export function getRegisteredWithScope(id: string, scope?: Scope) {
return val;
}
-export const scopeLookup = {} as Record;
-
export function init(runtimeId = ResumeSymbol.DefaultRuntimeId) {
- const runtimeLength = runtimeId.length;
- const resumeVar = runtimeId + ResumeSymbol.VarResume;
- const initialHydration = (window as any)[resumeVar];
-
if (MARKO_DEBUG) {
if (!runtimeId.match(/^[_$a-z][_$a-z0-9]*$/i)) {
throw new Error(
`Invalid runtimeId: "${runtimeId}". The runtimeId must be a valid JavaScript identifier.`,
);
}
-
- if (initialHydration && !Array.isArray(initialHydration)) {
- throw new Error(
- "Marko tried to initialize multiple times. It could be that there are multiple instances of Marko running on the page.",
- );
- }
}
- const walker = doc.createTreeWalker(doc, 128 /** NodeFilter.SHOW_COMMENT */);
- let currentScopeId: number;
- let currentNode: Node & ChildNode;
- // const scopeLookup: Record = {};
- const getScope = (id: number) =>
- scopeLookup[id] ?? (scopeLookup[id] = {} as Scope);
- const stack: number[] = [];
- const fakeArray = { push: resume };
- const serializeContext: Record = { _: registeredValues };
+ const resumeRender = ((renderId: string) =>
+ (resumeRender[renderId] = renders![renderId] =
+ new Render(renders!, runtimeId, renderId))) as Renders;
+ let renders: Renders | undefined;
- if (initialHydration) {
- for (let i = 0; i < initialHydration.length; i += 2) {
- resume(initialHydration[i], initialHydration[i + 1]);
- }
+ if ((window as any)[runtimeId] as Renders | undefined) {
+ setRenders((window as any)[runtimeId] as Renders);
} else {
- (window as any)[resumeVar] = fakeArray;
+ Object.defineProperty(window, runtimeId, {
+ configurable: true,
+ set: setRenders,
+ });
}
- function resume(
- scopesFn: (ctx: typeof serializeContext) => Record | null,
- calls: Array,
- ) {
- // TODO: Can be refactored/removed when adding runtimeId and componentIdPrefix
- /**
- * Necessary for injecting content into an existing document (e.g. microframe)
- */
- if (doc.readyState !== "loading") {
- walker.currentNode = doc;
- }
-
- const scopes = scopesFn(serializeContext);
- if (scopes) {
- scopeLookup.$global ||= scopes.$global || {};
-
- /**
- * Loop over all the new hydration scopes and see if a previous walk
- * had to create a dummy scope to store Nodes of interest.
- * If so merge them and set/replace the scope in the scopeLookup.
- */
- for (const scopeIdAsString in scopes) {
- if (scopeIdAsString === "$global") continue;
- const scopeId = parseInt(scopeIdAsString);
- const scope = scopes[scopeId];
- const storedScope = scopeLookup[scopeId];
- scope.$global = scopes.$global;
- if (storedScope !== scope) {
- scopeLookup[scopeId] = Object.assign(scope, storedScope) as Scope;
- }
+ function setRenders(v: Renders) {
+ if (MARKO_DEBUG) {
+ if (renders) {
+ throw new Error(
+ "Marko tried to initialize multiple times. It could be that there are multiple instances of Marko running on the page.",
+ );
}
}
- while ((currentNode = walker.nextNode() as ChildNode)) {
- const nodeValue = currentNode.nodeValue!;
- if (nodeValue.startsWith(runtimeId)) {
- const token = nodeValue[runtimeLength];
- const scopeId = parseInt(nodeValue.slice(runtimeLength + 1));
- const scope = getScope(scopeId);
- const data = nodeValue.slice(nodeValue.indexOf(" ") + 1);
-
- if (token === ResumeSymbol.Node) {
- scope[data] = currentNode.previousSibling;
- } else if (token === ResumeSymbol.SectionStart) {
- stack.push(currentScopeId);
- currentScopeId = scopeId;
- scope.___startNode = currentNode;
- } else if (token === ResumeSymbol.SectionEnd) {
- scope[data] = currentNode;
- if (scopeId < currentScopeId) {
- const currScope = scopeLookup[currentScopeId];
- const currParent = currentNode.parentNode!;
- const startNode = currScope.___startNode as Node;
- if (currParent !== startNode.parentNode) {
- currParent.prepend(startNode);
- }
- currScope.___endNode = currentNode.previousSibling!;
- currentScopeId = stack.pop()!;
- }
- } else if (token === ResumeSymbol.SectionSingleNodesEnd) {
- scope[
- MARKO_DEBUG ? data.slice(0, data.indexOf(" ")) : parseInt(data)
- ] = currentNode;
- // https://jsben.ch/dR7uk
- const childScopeIds = JSON.parse(
- "[" + data.slice(data.indexOf(" ") + 1) + "]",
- );
- for (let i = childScopeIds.length - 1; i >= 0; i--) {
- const childScope = getScope(childScopeIds[i]);
- // TODO: consider whether the single node optimization
- // should only apply to elements which means could
- // use previousElementSibling instead of a while loop
- while (
- (currentNode = currentNode.previousSibling!).nodeType ===
- 8 /* Node.COMMENT_NODE */
- );
- // TODO: consider only setting ___startNode?
- childScope.___startNode = childScope.___endNode = currentNode;
- }
- }
- }
+ renders = v;
+ for (const renderId in v) {
+ resumeRender(renderId);
}
- for (let i = 0; i < calls.length; i += 2) {
- (registeredValues[calls[i + 1] as string] as RegisteredFn)(
- scopeLookup[calls[i] as number]!,
- );
- }
+ Object.defineProperty(window, runtimeId, {
+ configurable: true,
+ value: resumeRender,
+ });
}
}
diff --git a/packages/runtime-tags/src/dom/signals.ts b/packages/runtime-tags/src/dom/signals.ts
index e85246654..69c548ba8 100644
--- a/packages/runtime-tags/src/dom/signals.ts
+++ b/packages/runtime-tags/src/dom/signals.ts
@@ -129,7 +129,7 @@ export function intersection(
};
}
-const defaultGetOwnerScope = (scope: Scope) => scope._!;
+const defaultGetOwnerScope = (scope: Scope) => scope._ as Scope;
export function closure(
ownerValueAccessor: Accessor | ((scope: Scope) => Accessor),
@@ -289,9 +289,11 @@ export const inMany = (
}
};
-let tagId = 0;
-export function nextTagId() {
- return "c" + tagId++;
+const tagIdsByGlobal = new WeakMap();
+export function nextTagId({ $global }: Scope) {
+ const id = tagIdsByGlobal.get($global) || 0;
+ tagIdsByGlobal.set($global, id + 1);
+ return "c" + $global.runtimeId + $global.renderId + id.toString(36);
}
export function inChild(childAccessor: Accessor, signal: ValueSignal) {
diff --git a/packages/runtime-tags/src/dom/template.ts b/packages/runtime-tags/src/dom/template.ts
index 9b51b1d63..ca5f19178 100644
--- a/packages/runtime-tags/src/dom/template.ts
+++ b/packages/runtime-tags/src/dom/template.ts
@@ -1,3 +1,4 @@
+import { DEFAULT_RUNTIME_ID, DEFAULT_RENDER_ID } from "../common/meta";
import type { Template, Input, TemplateInstance, Scope } from "../common/types";
import { prepare, runEffects, runSync } from "./queue";
import { type Renderer, initRenderer } from "./renderer";
@@ -16,12 +17,26 @@ export class ClientTemplate implements Template {
}
mount(
- templateInput: Input & { $global?: Record } = {},
+ input: Input & { $global?: Record } = {},
reference: ParentNode & Node,
position?: InsertPosition,
): TemplateInstance {
let scope!: Scope, dom!: Node;
- const { $global = {}, ...input } = templateInput;
+ let { $global } = input;
+ if ($global) {
+ ({ $global, ...input } = input);
+ $global = {
+ runtimeId: DEFAULT_RUNTIME_ID,
+ renderId: DEFAULT_RENDER_ID,
+ ...$global,
+ };
+ } else {
+ $global = {
+ runtimeId: DEFAULT_RUNTIME_ID,
+ renderId: DEFAULT_RENDER_ID,
+ };
+ }
+
const args = this._.___args;
const effects = prepare(() => {
scope = createScope($global);
diff --git a/packages/runtime-tags/src/html.ts b/packages/runtime-tags/src/html.ts
index f74d2fec3..be9f267e1 100644
--- a/packages/runtime-tags/src/html.ts
+++ b/packages/runtime-tags/src/html.ts
@@ -6,12 +6,10 @@ export {
dynamicTagInput,
dynamicTagArgs,
createRenderer,
- patchDynamicTag,
} from "./html/dynamic-tag";
export {
write,
- maybeFlush,
fork,
tryPlaceholder,
tryCatch,
@@ -23,14 +21,13 @@ export {
markResumeScopeStart,
markResumeControlEnd,
markResumeControlSingleNodeEnd,
- createRenderFn,
peekNextScope,
ensureScopeWithId,
getScopeById,
getStreamData,
register,
- serializerRegister,
- getRegistryInfo,
} from "./html/writer";
export { createTemplate } from "./html/template";
+
+export { compat } from "./html/compat";
diff --git a/packages/runtime-tags/src/html/attrs.ts b/packages/runtime-tags/src/html/attrs.ts
index bb99bee6c..5ff45dc51 100644
--- a/packages/runtime-tags/src/html/attrs.ts
+++ b/packages/runtime-tags/src/html/attrs.ts
@@ -85,7 +85,7 @@ function attrAssignment(val: string) {
}
const unsafeAttrChars = /["'>\s]/g;
-function escapeAttrValue(str: string) {
+export function escapeAttrValue(str: string) {
if (unsafeAttrChars.test(str)) {
const c = str[unsafeAttrChars.lastIndex - 1];
unsafeAttrChars.lastIndex = 0;
diff --git a/packages/runtime-tags/src/html/compat.ts b/packages/runtime-tags/src/html/compat.ts
new file mode 100644
index 000000000..af4e8f0d2
--- /dev/null
+++ b/packages/runtime-tags/src/html/compat.ts
@@ -0,0 +1,112 @@
+import {
+ RENDERER_REGISTER_ID,
+ SET_SCOPE_REGISTER_ID,
+} from "../common/compat-meta";
+import { DEFAULT_RUNTIME_ID, DEFAULT_RENDER_ID } from "../common/meta";
+import type { Renderer, Scope } from "../common/types";
+import { patchDynamicTag } from "./dynamic-tag";
+import { getRegistered, register } from "./serializer";
+import {
+ Chunk,
+ Boundary,
+ State,
+ prepareChunk,
+ fork,
+ getChunk,
+ getScopeId,
+ nextScopeId,
+ peekNextScopeId,
+ write,
+ writeEffect,
+ writeScope,
+ writeScript,
+} from "./writer";
+
+const K_TAGS_API_STATE = Symbol();
+const COMPAT_REGISTRY = new WeakMap<
+ WeakKey,
+ [registryId: string, scopeId: unknown]
+>();
+
+export const compat = {
+ fork,
+ write,
+ writeScript,
+ nextScopeId,
+ patchDynamicTag,
+ writeSetScopeForComponent(m5c: string) {
+ const scopeId = nextScopeId();
+ writeScope(scopeId, { m5c });
+ writeEffect(scopeId, SET_SCOPE_REGISTER_ID);
+ },
+ toJSON(this: WeakKey) {
+ let compatRegistered = COMPAT_REGISTRY.get(this);
+ if (!compatRegistered) {
+ const registered = getRegistered(this);
+ if (registered) {
+ const scopeId = getScopeId(registered.scope as Scope);
+ if (scopeId !== undefined) {
+ writeScope(scopeId, {});
+ }
+ COMPAT_REGISTRY.set(
+ this,
+ (compatRegistered = [registered.id, scopeId]),
+ );
+ }
+ }
+
+ return compatRegistered;
+ },
+ render(
+ renderer: Renderer,
+ willRerender: boolean,
+ classAPIOut: any,
+ component: any,
+ input: any,
+ ) {
+ const $global = classAPIOut.global;
+ let state: State | undefined = ($global[K_TAGS_API_STATE] ||=
+ getChunk()?.boundary.state);
+ if (!state) {
+ $global.runtimeId ||= DEFAULT_RUNTIME_ID;
+ $global.renderId ||= $global.componentIdPrefix || DEFAULT_RENDER_ID;
+ $global[K_TAGS_API_STATE] = state = new State($global);
+ }
+
+ const boundary = new Boundary(state);
+ let head = new Chunk(boundary, null);
+ head.render(() => {
+ if (willRerender) {
+ const scopeId = peekNextScopeId();
+ writeScope(scopeId, { m5c: component.id });
+ writeEffect(scopeId, SET_SCOPE_REGISTER_ID);
+ }
+
+ renderer(input);
+ });
+
+ const asyncOut = classAPIOut.beginAsync();
+ (boundary.onNext = () => {
+ if (boundary.done) {
+ if (boundary.signal.aborted) {
+ asyncOut.error(boundary.signal.reason);
+ } else {
+ queueMicrotask(() => {
+ const { scripts, html } = (head = prepareChunk(head));
+ asyncOut.script(scripts);
+ asyncOut.write(html);
+ asyncOut.end();
+ head.html = head.scripts = "";
+ });
+ }
+ }
+ })();
+ },
+ registerRenderer(renderer: any, id: string) {
+ return register(
+ RENDERER_REGISTER_ID,
+ renderer,
+ register(id, () => {}),
+ );
+ },
+};
diff --git a/packages/runtime-tags/src/html/dynamic-tag.ts b/packages/runtime-tags/src/html/dynamic-tag.ts
index bec5205ee..86e35936b 100644
--- a/packages/runtime-tags/src/html/dynamic-tag.ts
+++ b/packages/runtime-tags/src/html/dynamic-tag.ts
@@ -99,7 +99,10 @@ export function dynamicTagArgs(
let getDynamicRenderer = (
tag: unknown | string | Renderer | RenderBodyObject | Template,
-) => (tag as Template)._ || (tag as RenderBodyObject).renderBody || tag;
+) =>
+ (tag as { _?: Renderer })._ ||
+ (tag as { renderBody?: Renderer }).renderBody ||
+ (tag as Renderer);
export let createRenderer = (fn: Renderer) => fn;
export function patchDynamicTag(
diff --git a/packages/runtime-tags/src/html/inlined-runtimes.ts b/packages/runtime-tags/src/html/inlined-runtimes.ts
new file mode 100644
index 000000000..6ffeab6eb
--- /dev/null
+++ b/packages/runtime-tags/src/html/inlined-runtimes.ts
@@ -0,0 +1,106 @@
+export const WALKER_RUNTIME_CODE = MARKO_DEBUG
+ ? `((runtimeId) =>
+(self[runtimeId] =
+ self[runtimeId] ||
+ ((renderId) => {
+ let id,
+ markers = {},
+ visits = [],
+ doc = document,
+ walker = doc.createTreeWalker(
+ doc,
+ 129,
+ ) /* NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_ELEMENT */,
+ op = (self[runtimeId][renderId] = {
+ i: (renderId = runtimeId + renderId),
+ d: doc,
+ l: markers,
+ v: visits,
+ x() {},
+ w(node) {
+ while ((node = walker.nextNode())) {
+ this.x(
+ (op =
+ (op = node.data) &&
+ !op.indexOf(renderId) &&
+ ((markers[(id = op.slice(prefix + 1))] = node), op[prefix])),
+ id,
+ node,
+ );
+
+ if (op > "#") {
+ visits.push(node);
+ }
+ }
+ },
+ }),
+ prefix = renderId.length;
+ })))`
+ : `(e=>self[e]=self[e]||(l=>{let t,d={},f=[],s=document,a=s.createTreeWalker(s,129),r=self[e][l]={i:l=e+l,d:s,l:d,v:f,x(){},w(e){for(;e=a.nextNode();)this.x(r=(r=e.data)&&!r.indexOf(l)&&(d[t=r.slice(x+1)]=e,r[x]),t,e),r>"#"&&f.push(e)}},x=l.length}))`;
+export const REORDER_RUNTIME_CODE = MARKO_DEBUG
+ ? `((runtime) => {
+let insertOne,
+ placeholder,
+ nextSibling,
+ previousSibling,
+ placeholders = {},
+ replace = (marker, container) => {
+ marker.replaceWith(...container.childNodes);
+ container.remove();
+ };
+runtime.d.head.append(
+ runtime.d.querySelector("style[" + runtime.i + "]") || "",
+);
+runtime.j = {};
+runtime.x = (op, id, node, start, placeholderCallback) => {
+ // "node" and "end" are all closed over and can't be repurposed. "start" is too but only in the new placeholder case
+
+ if (op == "#") {
+ (placeholders[id] = placeholder).i++;
+ } else if (node == nextSibling) {
+ insertOne();
+ }
+
+ if (node.tagName == "T" && (id = node.getAttribute(runtime.i))) {
+ start = runtime.l["^" + id];
+
+ if (start) {
+ placeholder = placeholders[id] = {
+ i: 0,
+ c(end = runtime.l[id] || previousSibling || node) {
+ while (end.parentNode !== start.parentNode) {
+ end = end.parentNode;
+ }
+
+ for (
+ ;
+ end != nextSibling;
+ (nextSibling = start.nextSibling).remove()
+ );
+ replace(start, node);
+ },
+ };
+ } else {
+ insertOne = () => {
+ previousSibling = node.previousSibling;
+ replace(runtime.l[id], node);
+ if (!--start.i) {
+ start.c();
+ }
+ };
+
+ // repurpose "start" to hold this placeholder
+ start = placeholder = placeholders[id];
+ nextSibling = node.nextElementSibling || insertOne();
+ }
+
+ // repurpose "op" for callbacks ...carefully
+ placeholderCallback = placeholder.c;
+ (op = runtime.j[id]) &&
+ (placeholder.c = () => placeholderCallback() + op());
+
+ if (node.attributes.c) placeholder.c();
+ }
+};
+})`
+ : `(e=>{let t,i,r,l,o={},a=(e,t)=>{e.replaceWith(...t.childNodes),t.remove()};e.d.head.append(e.d.querySelector("style["+e.i+"]")||""),e.j={},e.x=(d,n,c,p,b)=>{"#"==d?(o[n]=i).i++:c==r&&t(),"T"==c.tagName&&(n=c.getAttribute(e.i))&&((p=e.l["^"+n])?i=o[n]={i:0,c(t=e.l[n]||l||c){for(;t.parentNode!==p.parentNode;)t=t.parentNode;for(;t!=r;(r=p.nextSibling).remove());a(p,c)}}:(t=()=>{l=c.previousSibling,a(e.l[n],c),--p.i||p.c()},p=i=o[n],r=c.nextElementSibling||t()),b=i.c,(d=e.j[n])&&(i.c=()=>b()+d()),c.attributes.c&&i.c())}})`;
diff --git a/packages/runtime-tags/src/html/reorder-runtime.ts b/packages/runtime-tags/src/html/reorder-runtime.ts
deleted file mode 100644
index 172667333..000000000
--- a/packages/runtime-tags/src/html/reorder-runtime.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-/* tslint:disable */
-
-import type { CommentWalker } from "../common/types";
-
-export default function (
- id: string,
- doc: Document,
- walker: TreeWalker,
- node: Comment,
- replacementNode: Node,
- targetParent: ParentNode & Node,
- targetNode: Node | null | undefined,
- refNode: Node | null | undefined,
- nextNode: Node | null | undefined,
- runtimePrefix: string,
-) {
- runtimePrefix = "RUNTIME_ID$";
- id = runtimePrefix + id;
- doc = document;
- walker =
- (doc as any)[runtimePrefix + "w"] ||
- ((doc as any)[runtimePrefix + "w"] = doc.createTreeWalker(
- doc,
- 128 /** NodeFilter.SHOW_COMMENT */,
- ) as CommentWalker);
- while ((node = walker.nextNode() as Comment)) {
- if (node.data.indexOf(runtimePrefix) === 0) {
- (walker as any)[node.data] = node;
- }
- }
-
- replacementNode = doc.getElementById(id)!;
- targetNode = (walker as any)[id];
- targetParent = targetNode!.parentNode!;
-
- while ((refNode = replacementNode.firstChild)) {
- targetParent.insertBefore(refNode, targetNode!);
- }
-
- nextNode = replacementNode.parentNode!;
- nextNode.removeChild(replacementNode.nextSibling!);
- nextNode.removeChild(replacementNode);
-
- refNode = (walker as any)[id + "/"];
-
- while (
- targetNode &&
- ((nextNode = targetNode!.nextSibling),
- targetParent.removeChild(targetNode!) !== refNode)
- ) {
- targetNode = nextNode;
- }
-}
diff --git a/packages/runtime-tags/src/html/serializer.ts b/packages/runtime-tags/src/html/serializer.ts
index df3608947..f65996ca0 100644
--- a/packages/runtime-tags/src/html/serializer.ts
+++ b/packages/runtime-tags/src/html/serializer.ts
@@ -1,3 +1,5 @@
+import type { Boundary } from "./writer";
+
const { hasOwnProperty } = {};
const Generator = (function* () {})().constructor;
const AsyncGenerator = (async function* () {})().constructor;
@@ -15,11 +17,6 @@ type TypedArray =
| Float64Array;
const REGISTRY = new WeakMap();
-const REF_START_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$"; // Avoids chars that cannot start a property name and reserves _ for user refs.
-const REF_START_CHARS_LEN = REF_START_CHARS.length;
-const REF_CHARS =
- "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$_";
-const REF_CHARS_LEN = REF_CHARS.length;
const KNOWN_SYMBOLS = (() => {
const KNOWN_SYMBOLS = new Map();
for (const name of Object.getOwnPropertyNames(Symbol)) {
@@ -267,34 +264,11 @@ const KNOWN_OBJECTS = new Map