@zvndev/darkmown 2.12.3 → 2.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -0
- package/CHANGELOG.md +30 -0
- package/README.md +1 -0
- package/docs/spec-alignment.md +1 -0
- package/package.json +1 -1
- package/site/pages/docs/index.wd +1 -0
- package/site/pages/playground/index.js +52 -51
- package/src/builder.js +6 -1
- package/src/compiler/context.js +3 -0
- package/src/compiler/fetch.js +1 -0
- package/src/compiler/markdown.js +254 -18
- package/src/compiler/page.js +9 -0
- package/src/dev.js +17 -1
- package/src/runtime.js +4 -2
- package/src/runtime.min.js +1 -1
- package/src/runtime.min.js.map +1 -1
- package/src/statics.js +2 -1
- package/types/compiler/context.d.ts +11 -0
- package/types/compiler/markdown.d.ts +14 -0
package/src/builder.js
CHANGED
|
@@ -1343,7 +1343,12 @@ function match(pathname) {
|
|
|
1343
1343
|
|
|
1344
1344
|
export default {
|
|
1345
1345
|
async fetch(request, env, ctx) {
|
|
1346
|
-
|
|
1346
|
+
let matched;
|
|
1347
|
+
try {
|
|
1348
|
+
matched = match(new URL(request.url).pathname);
|
|
1349
|
+
} catch {
|
|
1350
|
+
return Response.json({ ok: false, error: "Malformed URL encoding" }, { status: 400 });
|
|
1351
|
+
}
|
|
1347
1352
|
if (!matched) return env.ASSETS.fetch(request);
|
|
1348
1353
|
try {
|
|
1349
1354
|
const response = await matched.handler(request, { params: matched.params, env });
|
package/src/compiler/context.js
CHANGED
|
@@ -64,6 +64,8 @@
|
|
|
64
64
|
* @property {Assets} assets
|
|
65
65
|
* @property {Map<string, unknown>} state Declared state keys → initial values.
|
|
66
66
|
* @property {Set<string>} stores Page-global store names (a subset of state keys).
|
|
67
|
+
* @property {{ name: string, url: string, at: string }[]} fetchPlaceholders Every
|
|
68
|
+
* `{ name }` a `:fetch` URL reads, checked once the whole page has declared its state.
|
|
67
69
|
* @property {Set<string>} fetchKeys Base state keys declared by `:fetch`, so
|
|
68
70
|
* bare `:if <key>_loading` / `:if <key>_error` regions get compile-time
|
|
69
71
|
* `role`/`aria-live` announcements.
|
|
@@ -320,6 +322,7 @@ export function createCompilation(reader) {
|
|
|
320
322
|
state: new Map(),
|
|
321
323
|
stores: new Set(),
|
|
322
324
|
fetchKeys: new Set(),
|
|
325
|
+
fetchPlaceholders: [],
|
|
323
326
|
warnings: [],
|
|
324
327
|
sectionCounter: 0,
|
|
325
328
|
headingSlugs: new Map(),
|
package/src/compiler/fetch.js
CHANGED
|
@@ -117,6 +117,7 @@ export function handleFetch(line, ctx, index) {
|
|
|
117
117
|
validatePath(dep[1], ctx, FETCH_USE);
|
|
118
118
|
const headKey = dep[1].split(".")[0];
|
|
119
119
|
if (!deps.includes(headKey)) deps.push(headKey);
|
|
120
|
+
ctx.comp.fetchPlaceholders.push({ name: headKey, url, at: at(ctx, index) });
|
|
120
121
|
}
|
|
121
122
|
|
|
122
123
|
// Auto-declare the lifecycle keys. `name` is declared via declareState
|
package/src/compiler/markdown.js
CHANGED
|
@@ -38,6 +38,8 @@ export { unsafeUrlValue };
|
|
|
38
38
|
// instances share it so `html: false` pages highlight too.
|
|
39
39
|
const md = new MarkdownIt({ html: false, highlight: highlightCode });
|
|
40
40
|
md.use(bindingPlugin);
|
|
41
|
+
md.use(unresolvedPlugin);
|
|
42
|
+
md.use(taskListPlugin);
|
|
41
43
|
md.use(attrsPlugin);
|
|
42
44
|
md.use(attrBindPlugin);
|
|
43
45
|
md.use(imageVariantsPlugin);
|
|
@@ -63,6 +65,8 @@ export function selectMd(meta) {
|
|
|
63
65
|
if (!mdRawHtml) {
|
|
64
66
|
mdRawHtml = new MarkdownIt({ html: true, highlight: highlightCode });
|
|
65
67
|
mdRawHtml.use(bindingPlugin);
|
|
68
|
+
mdRawHtml.use(unresolvedPlugin);
|
|
69
|
+
mdRawHtml.use(taskListPlugin);
|
|
66
70
|
mdRawHtml.use(attrsPlugin);
|
|
67
71
|
mdRawHtml.use(attrBindPlugin);
|
|
68
72
|
mdRawHtml.use(imageVariantsPlugin);
|
|
@@ -145,6 +149,61 @@ function warnUnbindable(ctx, index, expr, painted) {
|
|
|
145
149
|
);
|
|
146
150
|
}
|
|
147
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Names in reach of `head`, for the "did you mean" in {@link warnUnresolved}.
|
|
154
|
+
* @param {Ctx} ctx
|
|
155
|
+
* @returns {string[]}
|
|
156
|
+
*/
|
|
157
|
+
function knownNames(ctx) {
|
|
158
|
+
const names = new Set([...ctx.comp.state.keys()].map((key) => key.split(":").pop() ?? key));
|
|
159
|
+
/** @type {typeof ctx.scope | null} */
|
|
160
|
+
let scope = ctx.scope;
|
|
161
|
+
for (; scope; scope = scope.parent) {
|
|
162
|
+
for (const name of Object.keys(scope.vars)) names.add(name);
|
|
163
|
+
}
|
|
164
|
+
return [...names];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Levenshtein distance, stopping the moment it can no longer be 2 or less.
|
|
169
|
+
* @param {string} a
|
|
170
|
+
* @param {string} b
|
|
171
|
+
* @returns {number}
|
|
172
|
+
*/
|
|
173
|
+
function editDistance(a, b) {
|
|
174
|
+
if (Math.abs(a.length - b.length) > 2) return 3;
|
|
175
|
+
let row = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
176
|
+
for (let i = 1; i <= a.length; i++) {
|
|
177
|
+
const next = [i];
|
|
178
|
+
for (let j = 1; j <= b.length; j++) {
|
|
179
|
+
next[j] = Math.min(row[j] + 1, next[j - 1] + 1, row[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
180
|
+
}
|
|
181
|
+
row = next;
|
|
182
|
+
}
|
|
183
|
+
return row[b.length];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Warn once that `{ expr }` matched nothing, so it ships as the literal braces.
|
|
188
|
+
* A real page has no reason to show them, and `\{ name }` says it on purpose.
|
|
189
|
+
* @param {Ctx} ctx
|
|
190
|
+
* @param {number} index 0-based line index, for `file:line`.
|
|
191
|
+
* @param {string} expr
|
|
192
|
+
* @returns {void}
|
|
193
|
+
*/
|
|
194
|
+
function warnUnresolved(ctx, index, expr) {
|
|
195
|
+
const head = expr.split(/[.|\s]/)[0];
|
|
196
|
+
const near = knownNames(ctx)
|
|
197
|
+
.map((name) => /** @type {[string, number]} */ ([name, editDistance(head, name)]))
|
|
198
|
+
.filter(([, distance]) => distance <= 2)
|
|
199
|
+
.sort((a, b) => a[1] - b[1])[0];
|
|
200
|
+
const hint = near ? `Did you mean "${near[0]}"? ` : "";
|
|
201
|
+
const message =
|
|
202
|
+
`${ctx.file}:${lineOf(ctx, index)}: "{ ${expr} }" matches no :state, include argument or in-scope value, so it ships as literal text. ` +
|
|
203
|
+
`${hint}Declare it above this line, or write \\{ ${expr} } if the braces are meant to show.`;
|
|
204
|
+
if (!ctx.comp.warnings.includes(message)) ctx.comp.warnings.push(message);
|
|
205
|
+
}
|
|
206
|
+
|
|
148
207
|
// ---------------------------------------------------------------------------
|
|
149
208
|
// REACTIVE ATTRIBUTE BINDING (a link/image destination).
|
|
150
209
|
//
|
|
@@ -268,7 +327,10 @@ function resolveDestinationBindings(text, ctx, attrs, index) {
|
|
|
268
327
|
/\{\s*([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)\s*\}/g,
|
|
269
328
|
(/** @type {string} */ brace, /** @type {string} */ expr) => {
|
|
270
329
|
const found = resolveBinding(expr, ctx);
|
|
271
|
-
if (!found)
|
|
330
|
+
if (!found) {
|
|
331
|
+
warnUnresolved(ctx, index + i, expr);
|
|
332
|
+
return brace; // not in scope — leave the author's text alone
|
|
333
|
+
}
|
|
272
334
|
if (found.kind === "static") {
|
|
273
335
|
// The scheme test runs on the RAW value, before encoding, because
|
|
274
336
|
// that is the string the runtime's guard sees. Encoding first would
|
|
@@ -695,14 +757,118 @@ const URL_ATTRS = new Set([
|
|
|
695
757
|
"poster",
|
|
696
758
|
"cite",
|
|
697
759
|
"background",
|
|
698
|
-
"ping"
|
|
760
|
+
"ping",
|
|
761
|
+
"to",
|
|
762
|
+
"from",
|
|
763
|
+
"by"
|
|
699
764
|
]);
|
|
700
765
|
|
|
701
|
-
//
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
const
|
|
766
|
+
// `{ path }` plus an optional `| pipe:arg` chain, as the inline binding rule reads it.
|
|
767
|
+
const BINDING_BRACE = /\{\s*([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*\s*(?:\|[^}]*)?)\}/g;
|
|
768
|
+
|
|
769
|
+
/** @param {string | undefined} c */
|
|
770
|
+
const isHtmlSpace = (c) => c === " " || c === "\t" || c === "\n" || c === "\f" || c === "\r";
|
|
771
|
+
const TEXT_CONTEXT = /** @type {RawContext} */ ({ kind: "text" });
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* @typedef {{ kind: "text" }
|
|
775
|
+
* | { kind: "tag" }
|
|
776
|
+
* | { kind: "value", name: string, head: string, quoted: boolean }
|
|
777
|
+
* | { kind: "rawtext", tag: string, type: string }} RawContext
|
|
778
|
+
*/
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Where the end of a raw-HTML fragment sits, as the browser's tokenizer would
|
|
782
|
+
* read it: in text, inside a tag but between attributes, inside an attribute
|
|
783
|
+
* value (with the value so far), or inside a `<script>`/`<style>` body. It walks
|
|
784
|
+
* quoted values rather than pattern-matching back from the end, because a `>`
|
|
785
|
+
* inside `title=">"` does not close the tag.
|
|
786
|
+
* @param {string} html
|
|
787
|
+
* @returns {RawContext}
|
|
788
|
+
*/
|
|
789
|
+
function rawHtmlContext(html) {
|
|
790
|
+
const n = html.length;
|
|
791
|
+
let i = 0;
|
|
792
|
+
for (;;) {
|
|
793
|
+
const lt = html.indexOf("<", i);
|
|
794
|
+
if (lt < 0) return TEXT_CONTEXT;
|
|
795
|
+
const next = html[lt + 1];
|
|
796
|
+
// `<` then the value: whatever the value starts with decides the tag name.
|
|
797
|
+
if (next === undefined) return { kind: "tag" };
|
|
798
|
+
if (html.startsWith("<!--", lt)) {
|
|
799
|
+
const end = html.indexOf("-->", lt + 4);
|
|
800
|
+
if (end < 0) return TEXT_CONTEXT;
|
|
801
|
+
i = end + 3;
|
|
802
|
+
continue;
|
|
803
|
+
}
|
|
804
|
+
if (next === "/" && lt + 2 === n) return { kind: "tag" };
|
|
805
|
+
const closing = next === "/" && /[A-Za-z]/.test(html[lt + 2]);
|
|
806
|
+
if (!closing && !/[A-Za-z]/.test(next)) {
|
|
807
|
+
if (next !== "!" && next !== "?" && next !== "/") {
|
|
808
|
+
i = lt + 1;
|
|
809
|
+
continue;
|
|
810
|
+
}
|
|
811
|
+
// A bogus comment runs to the next `>`, and an escaped value cannot hold one.
|
|
812
|
+
const end = html.indexOf(">", lt + 2);
|
|
813
|
+
if (end < 0) return TEXT_CONTEXT;
|
|
814
|
+
i = end + 1;
|
|
815
|
+
continue;
|
|
816
|
+
}
|
|
817
|
+
let j = lt + (closing ? 2 : 1);
|
|
818
|
+
const nameStart = j;
|
|
819
|
+
while (j < n && !isHtmlSpace(html[j]) && html[j] !== "/" && html[j] !== ">") j++;
|
|
820
|
+
const tag = html.slice(nameStart, j).toLowerCase();
|
|
821
|
+
let type = "";
|
|
822
|
+
for (;;) {
|
|
823
|
+
while (j < n && (isHtmlSpace(html[j]) || html[j] === "/")) j++;
|
|
824
|
+
if (j >= n) return { kind: "tag" };
|
|
825
|
+
if (html[j] === ">") {
|
|
826
|
+
j++;
|
|
827
|
+
break;
|
|
828
|
+
}
|
|
829
|
+
const attrStart = j++;
|
|
830
|
+
while (j < n && !isHtmlSpace(html[j]) && !"/>=".includes(html[j])) j++;
|
|
831
|
+
const name = html.slice(attrStart, j).toLowerCase();
|
|
832
|
+
while (j < n && isHtmlSpace(html[j])) j++;
|
|
833
|
+
if (j >= n) return { kind: "tag" };
|
|
834
|
+
if (html[j] !== "=") continue;
|
|
835
|
+
j++;
|
|
836
|
+
while (j < n && isHtmlSpace(html[j])) j++;
|
|
837
|
+
if (j >= n) return { kind: "value", name, head: "", quoted: false };
|
|
838
|
+
let value;
|
|
839
|
+
const quote = html[j];
|
|
840
|
+
if (quote === '"' || quote === "'") {
|
|
841
|
+
const end = html.indexOf(quote, j + 1);
|
|
842
|
+
if (end < 0) return { kind: "value", name, head: html.slice(j + 1), quoted: true };
|
|
843
|
+
value = html.slice(j + 1, end);
|
|
844
|
+
j = end + 1;
|
|
845
|
+
} else {
|
|
846
|
+
const valueStart = j;
|
|
847
|
+
while (j < n && !isHtmlSpace(html[j]) && html[j] !== ">") j++;
|
|
848
|
+
value = html.slice(valueStart, j);
|
|
849
|
+
if (j >= n) return { kind: "value", name, head: value, quoted: false };
|
|
850
|
+
}
|
|
851
|
+
if (name === "type") type = value.trim().toLowerCase();
|
|
852
|
+
}
|
|
853
|
+
if (!closing && (tag === "script" || tag === "style")) {
|
|
854
|
+
const end = html.slice(j).search(new RegExp(`</${tag}[\\s/>]`, "i"));
|
|
855
|
+
if (end < 0) return { kind: "rawtext", tag, type };
|
|
856
|
+
i = j + end + 2;
|
|
857
|
+
continue;
|
|
858
|
+
}
|
|
859
|
+
i = j;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* A JSON `<script>` is data the browser never runs, and an escaped value cannot
|
|
865
|
+
* close it, so a binding there is safe. Every other script or style body is code.
|
|
866
|
+
* @param {string} type
|
|
867
|
+
* @returns {boolean}
|
|
868
|
+
*/
|
|
869
|
+
function isDataScript(type) {
|
|
870
|
+
return /^application\/(?:[\w.-]+\+)?json$/.test(type);
|
|
871
|
+
}
|
|
706
872
|
|
|
707
873
|
/**
|
|
708
874
|
* Warn that a value in URL position carries a scheme the browser would execute.
|
|
@@ -752,7 +918,7 @@ function fillRawHtml(html, ctx, index) {
|
|
|
752
918
|
// The capture mirrors the inline `wd_binding` rule: path plus an optional
|
|
753
919
|
// `| pipe:arg` chain. A pipe-only divergence here left `{ d | date }` literal
|
|
754
920
|
// in raw HTML while the same expression folded in prose.
|
|
755
|
-
const brace =
|
|
921
|
+
const brace = new RegExp(BINDING_BRACE);
|
|
756
922
|
let out = "";
|
|
757
923
|
let last = 0;
|
|
758
924
|
/** @type {RegExpExecArray | null} */
|
|
@@ -762,7 +928,10 @@ function fillRawHtml(html, ctx, index) {
|
|
|
762
928
|
last = hit.index + hit[0].length;
|
|
763
929
|
const expr = hit[1].trim();
|
|
764
930
|
const found = resolveBinding(expr, ctx);
|
|
931
|
+
const context = rawHtmlContext(out);
|
|
765
932
|
if (!found) {
|
|
933
|
+
// Inside a script or style, `{ x }` is usually the language's own braces.
|
|
934
|
+
if (context.kind !== "rawtext") warnUnresolved(ctx, index, expr);
|
|
766
935
|
out += hit[0];
|
|
767
936
|
continue;
|
|
768
937
|
}
|
|
@@ -775,33 +944,100 @@ function fillRawHtml(html, ctx, index) {
|
|
|
775
944
|
// Escaping only holds inside a quoted attribute or text. In a script-valued
|
|
776
945
|
// attribute the browser entity-decodes `'` back into a live quote, and
|
|
777
946
|
// in bare tag position (`<div { x }>`) the value IS markup.
|
|
778
|
-
|
|
779
|
-
const open = tag && OPEN_ATTR.exec(tag[0]);
|
|
780
|
-
const name = open ? open[1].toLowerCase() : "";
|
|
781
|
-
if (tag && !open) {
|
|
947
|
+
if (context.kind === "tag") {
|
|
782
948
|
warnUnsafeSlot(ctx, index, expr, "inside a tag but outside any attribute value");
|
|
783
949
|
continue;
|
|
784
950
|
}
|
|
951
|
+
if (context.kind === "rawtext") {
|
|
952
|
+
if (context.tag === "style" || !isDataScript(context.type)) {
|
|
953
|
+
warnUnsafeSlot(ctx, index, expr, `in a <${context.tag}> body`);
|
|
954
|
+
continue;
|
|
955
|
+
}
|
|
956
|
+
out += escapeHtml(found.text);
|
|
957
|
+
continue;
|
|
958
|
+
}
|
|
959
|
+
if (context.kind === "text") {
|
|
960
|
+
out += escapeHtml(found.text);
|
|
961
|
+
continue;
|
|
962
|
+
}
|
|
963
|
+
const { name, head, quoted } = context;
|
|
785
964
|
if (name.startsWith("on") || name === "srcdoc") {
|
|
786
965
|
warnUnsafeSlot(ctx, index, expr, `in the ${name}= attribute`);
|
|
787
966
|
continue;
|
|
788
967
|
}
|
|
789
|
-
|
|
790
|
-
|
|
968
|
+
// SVG `values` is a `;`-separated list, and any entry can animate a link's href.
|
|
969
|
+
const assembled = head + found.text;
|
|
970
|
+
const unsafe =
|
|
971
|
+
name === "values"
|
|
972
|
+
? assembled.split(";").some(unsafeUrlValue)
|
|
973
|
+
: URL_ATTRS.has(name) && unsafeUrlValue(assembled);
|
|
974
|
+
if (unsafe) {
|
|
791
975
|
warnUnsafeUrl(ctx, index, expr);
|
|
792
976
|
out = out.slice(0, out.length - head.length); // the head belongs to the refused value
|
|
793
977
|
continue;
|
|
794
978
|
}
|
|
795
979
|
// An unquoted value ends at whitespace, so `x onmouseover=…` would open a
|
|
796
980
|
// new attribute. Entities keep it one value and decode back to the text.
|
|
797
|
-
out +=
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
: escapeHtml(found.text);
|
|
981
|
+
out += quoted
|
|
982
|
+
? escapeHtml(found.text)
|
|
983
|
+
: escapeHtml(found.text).replace(/[\s=`]/g, (c) => `&#${c.charCodeAt(0)};`);
|
|
801
984
|
}
|
|
802
985
|
return out + html.slice(last);
|
|
803
986
|
}
|
|
804
987
|
|
|
988
|
+
/**
|
|
989
|
+
* markdown-it plugin (core rule): a `{ name }` still in a text token after the
|
|
990
|
+
* inline pass is one {@link bindingPlugin} could not resolve. Runs before
|
|
991
|
+
* `text_join`, which would merge an escaped `\{` back in and make it look unresolved.
|
|
992
|
+
* @param {MarkdownIt} mdInstance
|
|
993
|
+
* @returns {void}
|
|
994
|
+
*/
|
|
995
|
+
function unresolvedPlugin(mdInstance) {
|
|
996
|
+
mdInstance.core.ruler.after("inline", "wd_unresolved", (state) => {
|
|
997
|
+
const wd = state.env?.wd;
|
|
998
|
+
if (!wd) return false;
|
|
999
|
+
for (const block of state.tokens) {
|
|
1000
|
+
const line = wd.index + (block.map ? block.map[0] : 0);
|
|
1001
|
+
let offset = 0;
|
|
1002
|
+
for (const child of block.children ?? []) {
|
|
1003
|
+
if (child.type === "softbreak" || child.type === "hardbreak") offset++;
|
|
1004
|
+
if (child.type !== "text") continue;
|
|
1005
|
+
for (const hit of child.content.matchAll(BINDING_BRACE)) {
|
|
1006
|
+
warnUnresolved(wd.ctx, line + offset, hit[1].trim());
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
return false;
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
/**
|
|
1015
|
+
* markdown-it plugin (core rule): `- [ ] item` in a `.wd` renders a read-only
|
|
1016
|
+
* checkbox, so a checklist survives a markdown document becoming a `.wd`. `.md`
|
|
1017
|
+
* stays strict CommonMark: it renders with no `wd` env, and this rule is a no-op
|
|
1018
|
+
* without one. Static output, so a static page stays `runtime: false`. A checkbox
|
|
1019
|
+
* the reader can tick is `:checkbox`.
|
|
1020
|
+
* @param {MarkdownIt} mdInstance
|
|
1021
|
+
* @returns {void}
|
|
1022
|
+
*/
|
|
1023
|
+
function taskListPlugin(mdInstance) {
|
|
1024
|
+
mdInstance.core.ruler.after("wd_unresolved", "wd_task_list", (state) => {
|
|
1025
|
+
if (!state.env?.wd) return false;
|
|
1026
|
+
state.tokens.forEach((block, i) => {
|
|
1027
|
+
if (block.type !== "inline" || state.tokens[i - 2]?.type !== "list_item_open") return;
|
|
1028
|
+
const first = block.children?.[0];
|
|
1029
|
+
const box = first?.type === "text" ? /^\[([ xX])\](?=\s|$)\s*/.exec(first.content) : null;
|
|
1030
|
+
if (!first || !box) return;
|
|
1031
|
+
first.content = first.content.slice(box[0].length);
|
|
1032
|
+
const input = new state.Token("html_inline", "", 0);
|
|
1033
|
+
input.content = `<input type="checkbox" disabled${box[1] === " " ? "" : " checked"}> `;
|
|
1034
|
+
input.meta = { wdText: "" };
|
|
1035
|
+
block.children?.unshift(input);
|
|
1036
|
+
});
|
|
1037
|
+
return false;
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
|
|
805
1041
|
/**
|
|
806
1042
|
* markdown-it plugin: turns `{ name.path }` into an inline `html_inline` token
|
|
807
1043
|
* whose content comes from the env's `resolveBinding` callback.
|
package/src/compiler/page.js
CHANGED
|
@@ -489,6 +489,15 @@ export function compileDocument(
|
|
|
489
489
|
0,
|
|
490
490
|
null
|
|
491
491
|
);
|
|
492
|
+
// Checked after the whole page, not at the :fetch line: the runtime reads state
|
|
493
|
+
// when the request fires, so a :state further down still fills the placeholder.
|
|
494
|
+
for (const { name, url, at } of comp.fetchPlaceholders) {
|
|
495
|
+
if (!comp.state.has(name)) {
|
|
496
|
+
comp.warnings.push(
|
|
497
|
+
`${at}: :fetch "${url}" reads "{ ${name} }", but the page declares no :state ${name}, so the request never fires until something sets it. Declare it with :state ${name} = …, or set it from a colocated .js.`
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
492
501
|
// Shared skins (frontmatter `skins:`) join the page's asset set through the
|
|
493
502
|
// exact same pipeline as colocated skins (compile through compileSkin at emit,
|
|
494
503
|
// scoping honored), and the CSS cascade is then ordered so the page always
|
package/src/dev.js
CHANGED
|
@@ -51,7 +51,7 @@ export function refuseLocalRequest(req, { bindHost = "", allowedHosts = "" } = {
|
|
|
51
51
|
if (!host || !(isLoopbackName(host) || isIP(host) || host === bind || allowed.includes(host))) {
|
|
52
52
|
return `Host "${req.headers.host ?? ""}" is not allowed. Set DARKMOWN_ALLOWED_HOSTS to permit it.`;
|
|
53
53
|
}
|
|
54
|
-
if (!(req.url || "/")
|
|
54
|
+
if (!isApiPath(req.url || "/")) return null;
|
|
55
55
|
if (req.headers["sec-fetch-site"] === "cross-site") return "Cross-site request to /api/ refused.";
|
|
56
56
|
const origin = req.headers.origin;
|
|
57
57
|
if (origin !== undefined) {
|
|
@@ -66,6 +66,22 @@ export function refuseLocalRequest(req, { bindHost = "", allowedHosts = "" } = {
|
|
|
66
66
|
return null;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Whether a request reaches the `api/` runner: the same URL parse and the same
|
|
71
|
+
* test as `handleApiRequest`, so `/api`, `/api?x` and `/./api/x` are all guarded.
|
|
72
|
+
* A URL it cannot parse gets the stricter answer.
|
|
73
|
+
* @param {string} url
|
|
74
|
+
* @returns {boolean}
|
|
75
|
+
*/
|
|
76
|
+
function isApiPath(url) {
|
|
77
|
+
try {
|
|
78
|
+
const { pathname } = new URL(url, "http://localhost");
|
|
79
|
+
return pathname === "/api" || pathname.startsWith("/api/");
|
|
80
|
+
} catch {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
69
85
|
/**
|
|
70
86
|
* Serialize the dev server's rebuilds: at most ONE child build runs at a time.
|
|
71
87
|
* Every build read-modify-writes the dependency map (`dist/.wd-dev-deps.json`),
|
package/src/runtime.js
CHANGED
|
@@ -1090,8 +1090,10 @@ function startFetch(node) {
|
|
|
1090
1090
|
// `.`/`..` survive encodeURIComponent and the URL parser resolves them, so
|
|
1091
1091
|
// `?id=..` would retarget `/api/users/{ id }/x` (headers and body included)
|
|
1092
1092
|
// at `/api/x`. Treat them as missing, like an empty value.
|
|
1093
|
-
|
|
1094
|
-
|
|
1093
|
+
// String() first: `?id=[".."]` seeds an array, which stringifies to `..`.
|
|
1094
|
+
const text = String(v ?? "");
|
|
1095
|
+
if (text === "" || text === "." || text === "..") missing = true;
|
|
1096
|
+
return encodeURIComponent(text);
|
|
1095
1097
|
});
|
|
1096
1098
|
if (missing) return set("_loading", false);
|
|
1097
1099
|
|
package/src/runtime.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var a={},ft=new Set,nt=new Set,T=new Set,M={};function J(t,e){try{return e===void 0?localStorage.getItem(t):e===null?localStorage.removeItem(t):localStorage.setItem(t,e)}catch{return null}}var X=t=>t.replace(":",".");function mt(){for(let t of document.querySelectorAll("script[data-wd-state],script[data-wd-store]")){let e=t.getAttribute("data-wd-store"),n=JSON.parse(t.textContent||"null");for(let[r,o]of e?[[e,n]]:Object.entries(n||{})){if(r in a)continue;if(a[r]=structuredClone(M[r]=o),e?!t.hasAttribute("data-wd-store-ephemeral"):t.getAttribute("data-wd-persist")===r){(e?nt:ft).add(r);let c=(e?"wd:store:":"wd:")+r,i=J(c);i===null?e&&J(c,JSON.stringify(o??null)):F(i)===void 0?J(c,null):a[r]=F(i)}t.getAttribute("data-wd-url")===r&&(T.add(r),pt(!1,[r]))}}}function xt(){if(!T.size)return;let t=new URLSearchParams(location.search);for(let r of T){let o=a[r];JSON.stringify(o??null)===JSON.stringify(M[r]??null)?t.delete(X(r)):t.set(X(r),typeof o=="string"?o:JSON.stringify(o))}let e=t.toString(),n=location.pathname+(e?`?${e}`:"")+location.hash;if(n!==location.pathname+location.search+location.hash)try{history.replaceState(history.state,"",n)}catch{}}function pt(t,e){let n=new URLSearchParams(location.search);for(let r of e||T){let o=n.get(X(r)),s=M[r];o==null?t&&(a[r]=structuredClone(s)):a[r]=typeof s=="string"?o:F(o)??o}}function b(){for(let t of ft)J(`wd:${t}`,JSON.stringify(a[t]??null));for(let t of nt)J(`wd:store:${t}`,JSON.stringify(a[t]??null));xt()}mt();window.addEventListener("popstate",()=>{T.size&&(pt(!0),b(),g())});window.addEventListener("storage",t=>{let e=t.key&&t.key.startsWith("wd:store:")?t.key.slice(9):"";if(!nt.has(e)||t.newValue==null)return;let n=F(t.newValue);n!==void 0&&JSON.stringify(n)!==JSON.stringify(a[e])&&(a[e]=n,g())});var Y=t=>t==="__proto__"||t==="constructor"||t==="prototype";function f(t,e){if(!e)return t;let n=t;for(let r of e.split(".")){if(n==null||Y(r))return;n=n[r]}return n}function Ct(t,e,n){let r=e.split("."),o=r.pop()||"",s=t;for(let c of r){if(Y(c))return;(s[c]==null||typeof s[c]!="object")&&(s[c]={}),s=s[c]}Y(o)||(s[o]=n)}function Jt(t){return t==null||typeof t=="object"&&(Array.isArray(t)?t.length:Object.keys(t).length)===0}function F(t){try{return JSON.parse(t,(e,n)=>e==="toString"||e==="valueOf"?void 0:n)}catch{return}}var Tt=/[\u0000-\u0020()<>"'`\\\u007F]/g,$=(t,e)=>t.matches(e)?[t,...t.querySelectorAll(e)]:[...t.querySelectorAll(e)];function wt(t,e,n,r){if(!e)return;let o;try{o=JSON.parse(e)}catch{return}let s="",c="";for(let i=1;i<o.length;i++){let u=o[i];if(typeof u=="string"){s+=u,c+=u;continue}let l=String((u[0]==="s"?f(a[u[1]],u[2]):u[0]==="m"?r[u[1]]:f(n,u[1]))??"");c+=l,s+=l.replace(Tt,m=>`%${m.charCodeAt(0).toString(16).toUpperCase().padStart(2,"0")}`)}o[0]!=="href"&&o[0]!=="src"||t.setAttribute(o[0],/^(javascript|data|vbscript):/i.test(c.replace(/[\u0000-\u0020\u007F]+/g,""))?"":s)}var q=t=>{let e=typeof t=="number"?t:Number.parseFloat(t);return Number.isFinite(e)?e:0},k=t=>Array.isArray(t)?t:t==null?[]:[t],gt=(t,e)=>e!=null&&e!==""?t?.[e]:t,z=(t,e)=>t.map(n=>q(gt(n,e))),B=t=>{if(t instanceof Date)return t;if(typeof t=="number")return new Date(t);let e=String(t).trim();return/^-?\d+$/.test(e)?new Date(Number(e)):new Date(e)},W=(t,e,n)=>Number.isNaN(t.getTime())?String(n):new Intl.DateTimeFormat(void 0,e).format(t),H=t=>typeof t=="string"&&/^\d{4}-\d{2}-\d{2}$/.test(t.trim())?{timeZone:"UTC"}:void 0,E={money:(t,[e="USD",n])=>new Intl.NumberFormat(n||void 0,{style:"currency",currency:String(e||"USD")}).format(q(t)),number:(t,[e])=>new Intl.NumberFormat(void 0,e==null?{}:{minimumFractionDigits:+e,maximumFractionDigits:+e}).format(q(t)),percent:(t,[e=0])=>new Intl.NumberFormat(void 0,{style:"percent",minimumFractionDigits:+e,maximumFractionDigits:+e}).format(q(t)),round:(t,[e=0])=>{let n=10**+e;return Math.round(q(t)*n)/n},date:(t,[e="medium"])=>W(B(t),{dateStyle:e,...H(t)},t),time:(t,[e="short"])=>W(B(t),{timeStyle:e,...H(t)},t),datetime:(t,[e="medium",n="short"])=>W(B(t),{dateStyle:e,timeStyle:n,...H(t)},t),upper:t=>String(t??"").toUpperCase(),lower:t=>String(t??"").toLowerCase(),capitalize:t=>{let e=String(t??"");return e&&e[0].toUpperCase()+e.slice(1)},truncate:(t,[e=50,n="…"])=>{let r=String(t??"");return r.length>+e?r.slice(0,+e).trimEnd()+String(n):r},trim:t=>String(t??"").trim(),pluralize:(t,[e="",n])=>{let r=q(t);return`${new Intl.NumberFormat().format(r)} ${Math.abs(r)===1?String(e):n!=null?String(n):`${e}s`}`},default:(t,[e=""])=>t==null||t===""||Array.isArray(t)&&!t.length?String(e):t,sum:(t,[e])=>z(k(t),e).reduce((n,r)=>n+r,0),avg:(t,[e])=>{let n=k(t);return n.length?E.sum(n,[e])/n.length:0},min:(t,[e])=>{let n=k(t);return n.length?z(n,e).reduce((r,o)=>Math.min(r,o)):0},max:(t,[e])=>{let n=k(t);return n.length?z(n,e).reduce((r,o)=>Math.max(r,o)):0},count:t=>k(t).length,join:(t,[e=", ",n])=>k(t).map(r=>gt(r,n)).join(String(e))};function tt(t,e){let n=t.getAttribute("data-wd-fmt");if(!n)return e;let r;try{r=JSON.parse(n)}catch{return e}let o=e;for(let[s,c]of r)E[s]&&(o=E[s](o,c||[]));return o}var Ft=(t,e,n)=>E[t]?E[t](e,n==null?[]:[n]):void 0;async function rt(t,e){let n=await fetch(t,e),r=await n.text(),o=F(r);if(!n.ok){let s=o&&[o.error,o.message].find(i=>typeof i=="string"&&i),c=new Error(s||`HTTP ${n.status}`);throw c.status=n.status,c.body=o??null,c}return o===void 0?{status:n.status,body:r}:o}function x(t,e){a[t+"_error"]=e?e.message||String(e):null,a[t+"_error_body"]=(e&&e.body)??null}function yt(t){let e=t.__wdGen=(t.__wdGen||0)+1;return()=>e!==t.__wdGen}var _=!0;function P(t,e){let n=window.wd;n&&n.debug&&console.warn(`[wd] expression failed: ${t}`,e)}function Et(){for(let t of document.querySelectorAll("[data-wd-computed]")){let e=t.getAttribute("data-wd-computed-key")||"",n=t.getAttribute("data-wd-computed-expr")||"null";try{a[e]=ht(U(n),void 0)}catch(r){a[e]=void 0,P(n,r)}}}var It=(t,e)=>String(t??"").toLowerCase().includes(String(e??"").toLowerCase());function ht(t,e){let n=r=>{switch(r[0]){case"L":return r[1];case"S":return f(a[r[1]],r[2]??null);case"I":return f(e,r[1]??null);case"C":return It(n(r[1]),n(r[2]));case"A":return Ft(r[1],n(r[2]),r[3]);case"!":return!n(r[1]);case"u-":return-n(r[1]);case"&&":return n(r[1])&&n(r[2]);case"||":return n(r[1])||n(r[2]);case"==":return n(r[1])==n(r[2]);case"!=":return n(r[1])!=n(r[2]);case">":return n(r[1])>n(r[2]);case"<":return n(r[1])<n(r[2]);case">=":return n(r[1])>=n(r[2]);case"<=":return n(r[1])<=n(r[2]);case"+":return n(r[1])+n(r[2]);case"-":return n(r[1])-n(r[2]);case"*":return n(r[1])*n(r[2]);case"/":return n(r[1])/n(r[2]);case"%":return n(r[1])%n(r[2])}throw new Error(`wd: unknown op ${r[0]}`)};return n(t)}var it=new Map;function U(t){let e=it.get(t);if(e)return e;let n=JSON.parse(t);return it.set(t,n),n}function K(t,e){try{return!!ht(t,e)}catch(n){return P(t,n),!1}}function bt(t,e,n){if(!e)return;let r;try{r=JSON.parse(e)}catch{return}for(let[o,s]of r)t.classList.toggle(o,K(s,n))}function $t(t,e){let n=String(t&&typeof t=="object"?t.id??t.key??JSON.stringify(t):t).replace(/#/g,"##"),r=e.get(n)||0;return e.set(n,r+1),r?`${n}#${r}`:n}function St(t,e,n={}){let r=t.querySelectorAll("[data-wd-loop-item]"),o=s=>r.length&&s.closest("[data-wd-loop-item]");for(let s of t.querySelectorAll("[data-wd-each-if]")){if(o(s))continue;let c=s.getAttribute("data-wd-if-expr"),i=s.getAttribute("data-wd-meta"),u=c?K(U(c),e):i?n[i]:f(e,s.getAttribute("data-wd-path")),l=s.querySelector("[data-wd-each-if-out]");l&&(At(s,l,u,"wd-if"),St(l,e,n))}r=t.querySelectorAll("[data-wd-loop-item]");for(let s of $(t,"[data-wd-each]"))o(s)||(s.textContent=tt(s,f(e,s.getAttribute("data-wd-path")))??"");for(let s of t.querySelectorAll("[data-wd-each-meta]"))o(s)||(s.textContent=tt(s,n[s.getAttribute("data-wd-each-meta")||""])??"");for(let s of $(t,"[data-wd-each-class]"))o(s)||bt(s,s.getAttribute("data-wd-each-class"),e);for(let s of $(t,"[data-wd-each-attr]"))o(s)||wt(s,s.getAttribute("data-wd-each-attr"),e,n);for(let s of r)Ot(s,f(e,s.getAttribute("data-wd-loop-item")))}function at(t,e){let n=t.getAttribute(e);return n==null?null:n.startsWith("key:")?Number(a[n.slice(4)]??0):Number(n)}function vt(t,e){let n=e.getAttribute("data-wd-loop-sort");if(n!=null){let s=n.startsWith("key:"),c=s?String(a[n.slice(4)]??""):n;if(!s||c){let i=e.getAttribute("data-wd-loop-sort-dir")||"asc";i.startsWith("key:")&&(i=String(a[i.slice(4)]??"asc"));let u=i==="desc"?-1:1;t=t.map((l,m)=>({value:l,index:m})).sort((l,m)=>{let h=f(l.value,c||null),d=f(m.value,c||null),p=typeof h=="number"&&typeof d=="number"?h-d:String(h).localeCompare(String(d));return p?p*u:l.index-m.index}).map(l=>l.value)}}e.hasAttribute("data-wd-loop-reverse")&&t.reverse();let r=at(e,"data-wd-loop-offset");r&&(t=t.slice(r));let o=at(e,"data-wd-loop-limit");return o!=null&&(t=t.slice(0,o)),t}function At(t,e,n,r){let o=String(!!n);if(t.getAttribute("data-wd-if-active")===o)return!1;t.setAttribute("data-wd-if-active",o);let s=t.querySelector(`template[data-${r}-${o}]`);return v(e),e.innerHTML=s?.innerHTML||"",_=!0}function Nt(t){let e=t.getAttribute("data-wd-if-expr"),n=e?K(U(e),void 0):f(a[t.getAttribute("data-wd-if")||""],t.getAttribute("data-wd-path")),r=t.querySelector("[data-wd-if-out]");if(!(!r||!At(t,r,n,"wd")))for(let o of r.querySelectorAll("[data-wd-if]"))Nt(o)}function Ot(t,e){let n=t.querySelector("template[data-wd-loop-template]"),r=t.querySelector("[data-wd-loop-out]");if(!n||!r)return;let o=Array.isArray(e)?e.slice():[],s=t.getAttribute("data-wd-loop-where");if(s){let d=U(s);o=o.filter(p=>K(d,p))}o=vt(o,t);let c=t.querySelector("template[data-wd-loop-empty]");if(!o.length&&c){if(r.getAttribute("data-wd-empty")!=="1"){v(r),r.textContent="";for(let d of[...c.content.children])r.appendChild(d.cloneNode(!0));r.setAttribute("data-wd-empty","1"),_=!0}return}r.getAttribute("data-wd-empty")==="1"&&(v(r),r.textContent="",r.removeAttribute("data-wd-empty"));let i=new Map;for(let d of[...r.children])i.set(d.getAttribute("data-wd-loop-key")||"",d);let u=new Map,l=new Set,m=o.length,h=!1;for(let d=0;d<m;d++){let p=o[d],S=$t(p,u),w=i.get(S);if(!w||l.has(S)){if(w=n.content.firstElementChild?.cloneNode(!0),!w)continue;w.setAttribute("data-wd-loop-key",S),_=!0}l.add(S),St(w,p,{index:d,number:d+1,first:d===0,last:d===m-1,count:m}),w.__wdItem=p,(h||r.children[d]!==w)&&(h=!0,r.appendChild(w))}for(let[d,p]of i)l.has(d)||(v(p),p.remove())}var ct=0;function R(){for(let t=0;t<3;t++){_&&Pt(),_=!1,Et();for(let e of document.querySelectorAll("[data-wd-if]"))Nt(e);for(let e of document.querySelectorAll("[data-wd-class]"))bt(e,e.getAttribute("data-wd-class"),void 0);for(let e of document.querySelectorAll("[data-wd-loop]")){let n=e.getAttribute("data-wd-loop"),r=e.getAttribute("data-wd-loop-data"),o=n?n.indexOf("."):-1;Ot(e,n?o<0?a[n]:f(a[n.slice(0,o)],n.slice(o+1)):r?JSON.parse(r):[])}if(!_)break}_&&++ct<10?(window.wd?.debug&&console.warn("[wd] render did not settle; scheduling another pass"),g()):ct=0;for(let t of document.querySelectorAll("[data-wd-bind]"))t.textContent=tt(t,f(a[t.getAttribute("data-wd-bind")||""],t.getAttribute("data-wd-path")))??"";for(let t of document.querySelectorAll("[data-wd-attr]"))wt(t,t.getAttribute("data-wd-attr"),void 0,{});for(let t of document.querySelectorAll("[data-wd-bind-input]")){let e=a[t.getAttribute("data-wd-bind-input")||""]??"";t.type==="checkbox"?t.checked=!!e:t.type==="radio"?t.checked=t.value===String(e):document.activeElement!==t&&(t.value=e)}Dt(),Lt(),j.size&&Kt()}function Dt(){for(let t of document.querySelectorAll("[data-wd-theme]")){let e=a[t.getAttribute("data-wd-theme")||""],n=document.documentElement;e==null||e===""||e==="auto"?n.removeAttribute("data-theme"):n.setAttribute("data-theme",String(e))}}var D=[],V=0;function Lt(){if(!D.length)return;let t=!1;for(let e of D){let n=JSON.stringify(f(a,e.watch)??null);n!==e.last&&(e.last=n,_t(e.actions),t=!0)}if(!t){V=0;return}if(b(),++V>=10){V=0,console.warn("effect did not settle");return}R()}var jt=typeof requestAnimationFrame=="function"?requestAnimationFrame:queueMicrotask,Z=!1;function g(){Z||(Z=!0,jt(()=>{Z=!1,R()}))}document.addEventListener("input",t=>{let e=t.target?.closest("[data-wd-bind-input]");e&&(a[e.getAttribute("data-wd-bind-input")||""]=e.type==="range"?e.valueAsNumber:e.type==="checkbox"?e.checked:e.value,b(),g(),G())});function ut(t){if(!t)return null;let e=t.closest("[data-wd-loop-key]"),n=t.closest("[data-wd-loop]");return!e||!n?null:{srcKey:n.getAttribute("data-wd-loop"),item:e.__wdItem}}function et(t,e,n,r){let o=i=>Ct(a,n,i),s=f(a,n),c=()=>Array.isArray(s)?s:[];if(e==="inc"&&o(Number(s??0)+1),e==="dec"&&o(Number(s??0)-1),e==="add"&&o(Number(s??0)+Number(r)),e==="sub"&&o(Number(s??0)-Number(r)),e==="toggle"&&o(!s),e==="set"&&o(r),e==="append"&&o([...c(),r]),e==="prepend"&&o([r,...c()]),e==="member-toggle"&&o(c().includes(r)?c().filter(i=>i!==r):[...c(),r]),e==="remove-value"&&o(c().filter(i=>i!==r)),e==="clear"&&o(Array.isArray(s)?[]:{}),e==="merge"&&o({...s&&typeof s=="object"?s:{},...typeof r=="string"?f(a,r):r}),e==="delete"&&s&&typeof s=="object"&&(delete s[r],o(s)),e==="reset"&&o(structuredClone(f(M,n))),e==="refetch"){let i=document.querySelector(`[data-wd-fetch-key="${n}"]`);i&&L(i)}if(e==="remove"){let i=ut(t);i&&i.srcKey&&(a[i.srcKey]=(Array.isArray(a[i.srcKey])?a[i.srcKey]:[]).filter(u=>u!==i.item))}if(e==="append-row"){let i=ut(t);if(i&&i.item!==void 0){let u=i.item&&typeof i.item=="object"?structuredClone(i.item):i.item;o([...c(),u])}}}var _t=t=>{for(let e of t)et(null,e.op,e.target||"",structuredClone(e.value))};document.addEventListener("click",t=>{let e=t.target?.closest("[data-wd-action],[data-wd-actions]");if(!e)return;let n=e.getAttribute("data-wd-actions");if(n)for(let r of JSON.parse(n))et(e,r.op,r.target||"",r.value);else{let r=e.getAttribute("data-wd-value");et(e,e.getAttribute("data-wd-action")||"",e.getAttribute("data-wd-target")||"",r===null?void 0:JSON.parse(r))}b(),g(),G()});document.addEventListener("submit",t=>{let e=t.target?.closest("[data-wd-form]");if(!e)return;t.preventDefault();let n=e.getAttribute("data-wd-form")||"",r=e.getAttribute("action");if(!r){let i=new FormData(e),u=Object.fromEntries(i);for(let l of e.querySelectorAll("[data-wd-multi]")){let m=l.getAttribute("data-wd-multi");m&&(u[m]=i.getAll(m))}a[n]=u,b(),g();return}let o=yt(e);x(n),g();let s=new FormData(e);for(let i of e.querySelectorAll("[data-wd-bind-input]")){let u=i.getAttribute("data-wd-bind-input");u&&!i.getAttribute("name")&&!s.has(u)&&s.append(u,a[u]??"")}let c=e.querySelector("input[type=file]");rt(r,{method:e.getAttribute("method")||"post",headers:c?void 0:{"content-type":"application/x-www-form-urlencoded"},body:c?s:new URLSearchParams(s).toString()}).then(i=>{o()||(a[n]=i,x(n),b(),g())}).catch(i=>{o()||(x(n,i),g())})});function L(t){let e=yt(t),n=y=>t.getAttribute("data-wd-fetch-"+y)||"",r=n("key"),o=(y,A)=>{a[r+y]=A,g()},s=!1,c=n("url").replace(/\{\s*([\w$.]+)\s*\}/g,(y,A)=>{let N=f(a,A);return(N==null||N===""||N==="."||N==="..")&&(s=!0),encodeURIComponent(String(N??""))});if(s)return o("_loading",!1);let i=n("headers"),u=n("body"),l=n("refresh"),m=n("method")||"GET",h=Number(n("timeout")),d=Number(n("retry")),p=!1;x(r),o("_loading",!0);let S=y=>{x(r,y),o("_loading",!1)},w=()=>{let y={method:m};i&&(y.headers=a[i]||{}),u&&m!=="GET"&&(y.body=JSON.stringify(a[u]??null));let A=typeof AbortController=="function"?new AbortController:null;A&&(y.signal=A.signal);let N=h&&A?setTimeout(()=>A.abort(),h):0;rt(c,y).then(O=>{clearTimeout(N),!e()&&(a[r]=O,a[r+"_empty"]=Jt(O),x(r),o("_loading",!1))}).catch(O=>{if(clearTimeout(N),e())return;let st=O&&O.status===401;if(st&&l&&i&&!p)return p=!0,void Mt(l,i).then(qt=>{e()||(qt?w():S(O))});if(st&&p)return void S(O);if(d-- >0)return void setTimeout(w,200);S(O)})};w()}var Q=new Map;function Mt(t,e){let n=Q.get(t);if(n)return n;let r=rt(t,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(a[e]??{})}).then(o=>o&&typeof o=="object"?(a[e]=o,b(),!0):!1).catch(()=>!1).finally(()=>Q.delete(t));return Q.set(t,r),r}var ot=[],kt=t=>(t.getAttribute("data-wd-fetch-deps")||"").split(",").filter(Boolean).map(e=>JSON.stringify(f(a,e))).join("|"),dt=0,C=new Set;function G(){for(let t of ot){let e=kt(t);e&&e!==t.__wdSnap&&(t.__wdSnap=e,C.add(t))}C.size&&(clearTimeout(dt),dt=setTimeout(()=>{for(let t of C)L(t);C.clear()},150))}function v(t){for(let e of $(t,"[data-wd-fetch],[data-wd-effect],[data-wd-every]")){C.delete(e);for(let n of[ot,D,I])for(let r=n.length-1;r>=0;r--)(n[r].node||n[r])===e&&(n[r].t&&clearInterval(n[r].t),n.splice(r,1))}}function Pt(){mt();for(let t of document.querySelectorAll("[data-wd-fetch],[data-wd-effect],[data-wd-every]"))if(!t.__wdReg){if(t.__wdReg=1,t.hasAttribute("data-wd-effect")){let e=JSON.parse(t.textContent||"{}");D.push({node:t,watch:e.watch,actions:e.actions||[],last:JSON.stringify(f(a,e.watch)??null)})}if(t.hasAttribute("data-wd-every")&&(I.length||document.addEventListener("visibilitychange",()=>document.hidden?Ut():lt()),I.push({...JSON.parse(t.textContent||"{}"),node:t}),lt()),t.hasAttribute("data-wd-fetch"))if(ot.push(t),t.__wdSnap=kt(t),t.getAttribute("data-wd-fetch-when")==="visible"&&"IntersectionObserver"in window){let e=new IntersectionObserver(n=>{n.some(r=>r.isIntersecting)&&(e.disconnect(),L(t))});e.observe(t)}else L(t)}}var I=[];function lt(){if(!document.hidden)for(let t of I)t.t||!t.ms||!t.actions||(t.t=setInterval(()=>{_t(t.actions),b(),g(),G()},t.ms))}function Ut(){for(let t of I)clearInterval(t.t),t.t=0}var j=new Map;function Kt(){for(let[t,e]of j){let n=JSON.stringify(a[t]??null);if(n!==e.last){e.last=n;for(let r of e.cbs)try{r(a[t])}catch(o){P(`subscribe ${t}`,o)}}}}window.wd={state:a,debug:!1,get:t=>a[t],set:(t,e)=>{a[t]=e,b(),g(),G()},subscribe:(t,e)=>{let n=j.get(t);n||(n={cbs:new Set,last:JSON.stringify(a[t]??null)},j.set(t,n)),n.cbs.add(e);try{e(a[t])}catch(r){P(`subscribe ${t}`,r)}return()=>n.cbs.delete(e)},render:R};R();
|
|
1
|
+
var a={},ft=new Set,nt=new Set,J=new Set,M={};function C(t,e){try{return e===void 0?localStorage.getItem(t):e===null?localStorage.removeItem(t):localStorage.setItem(t,e)}catch{return null}}var X=t=>t.replace(":",".");function mt(){for(let t of document.querySelectorAll("script[data-wd-state],script[data-wd-store]")){let e=t.getAttribute("data-wd-store"),n=JSON.parse(t.textContent||"null");for(let[r,o]of e?[[e,n]]:Object.entries(n||{})){if(r in a)continue;if(a[r]=structuredClone(M[r]=o),e?!t.hasAttribute("data-wd-store-ephemeral"):t.getAttribute("data-wd-persist")===r){(e?nt:ft).add(r);let c=(e?"wd:store:":"wd:")+r,i=C(c);i===null?e&&C(c,JSON.stringify(o??null)):T(i)===void 0?C(c,null):a[r]=T(i)}t.getAttribute("data-wd-url")===r&&(J.add(r),pt(!1,[r]))}}}function qt(){if(!J.size)return;let t=new URLSearchParams(location.search);for(let r of J){let o=a[r];JSON.stringify(o??null)===JSON.stringify(M[r]??null)?t.delete(X(r)):t.set(X(r),typeof o=="string"?o:JSON.stringify(o))}let e=t.toString(),n=location.pathname+(e?`?${e}`:"")+location.hash;if(n!==location.pathname+location.search+location.hash)try{history.replaceState(history.state,"",n)}catch{}}function pt(t,e){let n=new URLSearchParams(location.search);for(let r of e||J){let o=n.get(X(r)),s=M[r];o==null?t&&(a[r]=structuredClone(s)):a[r]=typeof s=="string"?o:T(o)??o}}function S(){for(let t of ft)C(`wd:${t}`,JSON.stringify(a[t]??null));for(let t of nt)C(`wd:store:${t}`,JSON.stringify(a[t]??null));qt()}mt();window.addEventListener("popstate",()=>{J.size&&(pt(!0),S(),y())});window.addEventListener("storage",t=>{let e=t.key&&t.key.startsWith("wd:store:")?t.key.slice(9):"";if(!nt.has(e)||t.newValue==null)return;let n=T(t.newValue);n!==void 0&&JSON.stringify(n)!==JSON.stringify(a[e])&&(a[e]=n,y())});var Y=t=>t==="__proto__"||t==="constructor"||t==="prototype";function f(t,e){if(!e)return t;let n=t;for(let r of e.split(".")){if(n==null||Y(r))return;n=n[r]}return n}function Ct(t,e,n){let r=e.split("."),o=r.pop()||"",s=t;for(let c of r){if(Y(c))return;(s[c]==null||typeof s[c]!="object")&&(s[c]={}),s=s[c]}Y(o)||(s[o]=n)}function Jt(t){return t==null||typeof t=="object"&&(Array.isArray(t)?t.length:Object.keys(t).length)===0}function T(t){try{return JSON.parse(t,(e,n)=>e==="toString"||e==="valueOf"?void 0:n)}catch{return}}var Tt=/[\u0000-\u0020()<>"'`\\\u007F]/g,$=(t,e)=>t.matches(e)?[t,...t.querySelectorAll(e)]:[...t.querySelectorAll(e)];function wt(t,e,n,r){if(!e)return;let o;try{o=JSON.parse(e)}catch{return}let s="",c="";for(let i=1;i<o.length;i++){let u=o[i];if(typeof u=="string"){s+=u,c+=u;continue}let l=String((u[0]==="s"?f(a[u[1]],u[2]):u[0]==="m"?r[u[1]]:f(n,u[1]))??"");c+=l,s+=l.replace(Tt,m=>`%${m.charCodeAt(0).toString(16).toUpperCase().padStart(2,"0")}`)}o[0]!=="href"&&o[0]!=="src"||t.setAttribute(o[0],/^(javascript|data|vbscript):/i.test(c.replace(/[\u0000-\u0020\u007F]+/g,""))?"":s)}var k=t=>{let e=typeof t=="number"?t:Number.parseFloat(t);return Number.isFinite(e)?e:0},_=t=>Array.isArray(t)?t:t==null?[]:[t],gt=(t,e)=>e!=null&&e!==""?t?.[e]:t,z=(t,e)=>t.map(n=>k(gt(n,e))),B=t=>{if(t instanceof Date)return t;if(typeof t=="number")return new Date(t);let e=String(t).trim();return/^-?\d+$/.test(e)?new Date(Number(e)):new Date(e)},W=(t,e,n)=>Number.isNaN(t.getTime())?String(n):new Intl.DateTimeFormat(void 0,e).format(t),H=t=>typeof t=="string"&&/^\d{4}-\d{2}-\d{2}$/.test(t.trim())?{timeZone:"UTC"}:void 0,F={money:(t,[e="USD",n])=>new Intl.NumberFormat(n||void 0,{style:"currency",currency:String(e||"USD")}).format(k(t)),number:(t,[e])=>new Intl.NumberFormat(void 0,e==null?{}:{minimumFractionDigits:+e,maximumFractionDigits:+e}).format(k(t)),percent:(t,[e=0])=>new Intl.NumberFormat(void 0,{style:"percent",minimumFractionDigits:+e,maximumFractionDigits:+e}).format(k(t)),round:(t,[e=0])=>{let n=10**+e;return Math.round(k(t)*n)/n},date:(t,[e="medium"])=>W(B(t),{dateStyle:e,...H(t)},t),time:(t,[e="short"])=>W(B(t),{timeStyle:e,...H(t)},t),datetime:(t,[e="medium",n="short"])=>W(B(t),{dateStyle:e,timeStyle:n,...H(t)},t),upper:t=>String(t??"").toUpperCase(),lower:t=>String(t??"").toLowerCase(),capitalize:t=>{let e=String(t??"");return e&&e[0].toUpperCase()+e.slice(1)},truncate:(t,[e=50,n="…"])=>{let r=String(t??"");return r.length>+e?r.slice(0,+e).trimEnd()+String(n):r},trim:t=>String(t??"").trim(),pluralize:(t,[e="",n])=>{let r=k(t);return`${new Intl.NumberFormat().format(r)} ${Math.abs(r)===1?String(e):n!=null?String(n):`${e}s`}`},default:(t,[e=""])=>t==null||t===""||Array.isArray(t)&&!t.length?String(e):t,sum:(t,[e])=>z(_(t),e).reduce((n,r)=>n+r,0),avg:(t,[e])=>{let n=_(t);return n.length?F.sum(n,[e])/n.length:0},min:(t,[e])=>{let n=_(t);return n.length?z(n,e).reduce((r,o)=>Math.min(r,o)):0},max:(t,[e])=>{let n=_(t);return n.length?z(n,e).reduce((r,o)=>Math.max(r,o)):0},count:t=>_(t).length,join:(t,[e=", ",n])=>_(t).map(r=>gt(r,n)).join(String(e))};function tt(t,e){let n=t.getAttribute("data-wd-fmt");if(!n)return e;let r;try{r=JSON.parse(n)}catch{return e}let o=e;for(let[s,c]of r)F[s]&&(o=F[s](o,c||[]));return o}var Ft=(t,e,n)=>F[t]?F[t](e,n==null?[]:[n]):void 0;async function rt(t,e){let n=await fetch(t,e),r=await n.text(),o=T(r);if(!n.ok){let s=o&&[o.error,o.message].find(i=>typeof i=="string"&&i),c=new Error(s||`HTTP ${n.status}`);throw c.status=n.status,c.body=o??null,c}return o===void 0?{status:n.status,body:r}:o}function x(t,e){a[t+"_error"]=e?e.message||String(e):null,a[t+"_error_body"]=(e&&e.body)??null}function yt(t){let e=t.__wdGen=(t.__wdGen||0)+1;return()=>e!==t.__wdGen}var O=!0;function P(t,e){let n=window.wd;n&&n.debug&&console.warn(`[wd] expression failed: ${t}`,e)}function Et(){for(let t of document.querySelectorAll("[data-wd-computed]")){let e=t.getAttribute("data-wd-computed-key")||"",n=t.getAttribute("data-wd-computed-expr")||"null";try{a[e]=ht(U(n),void 0)}catch(r){a[e]=void 0,P(n,r)}}}var It=(t,e)=>String(t??"").toLowerCase().includes(String(e??"").toLowerCase());function ht(t,e){let n=r=>{switch(r[0]){case"L":return r[1];case"S":return f(a[r[1]],r[2]??null);case"I":return f(e,r[1]??null);case"C":return It(n(r[1]),n(r[2]));case"A":return Ft(r[1],n(r[2]),r[3]);case"!":return!n(r[1]);case"u-":return-n(r[1]);case"&&":return n(r[1])&&n(r[2]);case"||":return n(r[1])||n(r[2]);case"==":return n(r[1])==n(r[2]);case"!=":return n(r[1])!=n(r[2]);case">":return n(r[1])>n(r[2]);case"<":return n(r[1])<n(r[2]);case">=":return n(r[1])>=n(r[2]);case"<=":return n(r[1])<=n(r[2]);case"+":return n(r[1])+n(r[2]);case"-":return n(r[1])-n(r[2]);case"*":return n(r[1])*n(r[2]);case"/":return n(r[1])/n(r[2]);case"%":return n(r[1])%n(r[2])}throw new Error(`wd: unknown op ${r[0]}`)};return n(t)}var it=new Map;function U(t){let e=it.get(t);if(e)return e;let n=JSON.parse(t);return it.set(t,n),n}function K(t,e){try{return!!ht(t,e)}catch(n){return P(t,n),!1}}function bt(t,e,n){if(!e)return;let r;try{r=JSON.parse(e)}catch{return}for(let[o,s]of r)t.classList.toggle(o,K(s,n))}function $t(t,e){let n=String(t&&typeof t=="object"?t.id??t.key??JSON.stringify(t):t).replace(/#/g,"##"),r=e.get(n)||0;return e.set(n,r+1),r?`${n}#${r}`:n}function St(t,e,n={}){let r=t.querySelectorAll("[data-wd-loop-item]"),o=s=>r.length&&s.closest("[data-wd-loop-item]");for(let s of t.querySelectorAll("[data-wd-each-if]")){if(o(s))continue;let c=s.getAttribute("data-wd-if-expr"),i=s.getAttribute("data-wd-meta"),u=c?K(U(c),e):i?n[i]:f(e,s.getAttribute("data-wd-path")),l=s.querySelector("[data-wd-each-if-out]");l&&(At(s,l,u,"wd-if"),St(l,e,n))}r=t.querySelectorAll("[data-wd-loop-item]");for(let s of $(t,"[data-wd-each]"))o(s)||(s.textContent=tt(s,f(e,s.getAttribute("data-wd-path")))??"");for(let s of t.querySelectorAll("[data-wd-each-meta]"))o(s)||(s.textContent=tt(s,n[s.getAttribute("data-wd-each-meta")||""])??"");for(let s of $(t,"[data-wd-each-class]"))o(s)||bt(s,s.getAttribute("data-wd-each-class"),e);for(let s of $(t,"[data-wd-each-attr]"))o(s)||wt(s,s.getAttribute("data-wd-each-attr"),e,n);for(let s of r)Ot(s,f(e,s.getAttribute("data-wd-loop-item")))}function at(t,e){let n=t.getAttribute(e);return n==null?null:n.startsWith("key:")?Number(a[n.slice(4)]??0):Number(n)}function vt(t,e){let n=e.getAttribute("data-wd-loop-sort");if(n!=null){let s=n.startsWith("key:"),c=s?String(a[n.slice(4)]??""):n;if(!s||c){let i=e.getAttribute("data-wd-loop-sort-dir")||"asc";i.startsWith("key:")&&(i=String(a[i.slice(4)]??"asc"));let u=i==="desc"?-1:1;t=t.map((l,m)=>({value:l,index:m})).sort((l,m)=>{let b=f(l.value,c||null),d=f(m.value,c||null),p=typeof b=="number"&&typeof d=="number"?b-d:String(b).localeCompare(String(d));return p?p*u:l.index-m.index}).map(l=>l.value)}}e.hasAttribute("data-wd-loop-reverse")&&t.reverse();let r=at(e,"data-wd-loop-offset");r&&(t=t.slice(r));let o=at(e,"data-wd-loop-limit");return o!=null&&(t=t.slice(0,o)),t}function At(t,e,n,r){let o=String(!!n);if(t.getAttribute("data-wd-if-active")===o)return!1;t.setAttribute("data-wd-if-active",o);let s=t.querySelector(`template[data-${r}-${o}]`);return v(e),e.innerHTML=s?.innerHTML||"",O=!0}function Nt(t){let e=t.getAttribute("data-wd-if-expr"),n=e?K(U(e),void 0):f(a[t.getAttribute("data-wd-if")||""],t.getAttribute("data-wd-path")),r=t.querySelector("[data-wd-if-out]");if(!(!r||!At(t,r,n,"wd")))for(let o of r.querySelectorAll("[data-wd-if]"))Nt(o)}function Ot(t,e){let n=t.querySelector("template[data-wd-loop-template]"),r=t.querySelector("[data-wd-loop-out]");if(!n||!r)return;let o=Array.isArray(e)?e.slice():[],s=t.getAttribute("data-wd-loop-where");if(s){let d=U(s);o=o.filter(p=>K(d,p))}o=vt(o,t);let c=t.querySelector("template[data-wd-loop-empty]");if(!o.length&&c){if(r.getAttribute("data-wd-empty")!=="1"){v(r),r.textContent="";for(let d of[...c.content.children])r.appendChild(d.cloneNode(!0));r.setAttribute("data-wd-empty","1"),O=!0}return}r.getAttribute("data-wd-empty")==="1"&&(v(r),r.textContent="",r.removeAttribute("data-wd-empty"));let i=new Map;for(let d of[...r.children])i.set(d.getAttribute("data-wd-loop-key")||"",d);let u=new Map,l=new Set,m=o.length,b=!1;for(let d=0;d<m;d++){let p=o[d],A=$t(p,u),g=i.get(A);if(!g||l.has(A)){if(g=n.content.firstElementChild?.cloneNode(!0),!g)continue;g.setAttribute("data-wd-loop-key",A),O=!0}l.add(A),St(g,p,{index:d,number:d+1,first:d===0,last:d===m-1,count:m}),g.__wdItem=p,(b||r.children[d]!==g)&&(b=!0,r.appendChild(g))}for(let[d,p]of i)l.has(d)||(v(p),p.remove())}var ct=0;function R(){for(let t=0;t<3;t++){O&&Pt(),O=!1,Et();for(let e of document.querySelectorAll("[data-wd-if]"))Nt(e);for(let e of document.querySelectorAll("[data-wd-class]"))bt(e,e.getAttribute("data-wd-class"),void 0);for(let e of document.querySelectorAll("[data-wd-loop]")){let n=e.getAttribute("data-wd-loop"),r=e.getAttribute("data-wd-loop-data"),o=n?n.indexOf("."):-1;Ot(e,n?o<0?a[n]:f(a[n.slice(0,o)],n.slice(o+1)):r?JSON.parse(r):[])}if(!O)break}O&&++ct<10?(window.wd?.debug&&console.warn("[wd] render did not settle; scheduling another pass"),y()):ct=0;for(let t of document.querySelectorAll("[data-wd-bind]"))t.textContent=tt(t,f(a[t.getAttribute("data-wd-bind")||""],t.getAttribute("data-wd-path")))??"";for(let t of document.querySelectorAll("[data-wd-attr]"))wt(t,t.getAttribute("data-wd-attr"),void 0,{});for(let t of document.querySelectorAll("[data-wd-bind-input]")){let e=a[t.getAttribute("data-wd-bind-input")||""]??"";t.type==="checkbox"?t.checked=!!e:t.type==="radio"?t.checked=t.value===String(e):document.activeElement!==t&&(t.value=e)}Dt(),Lt(),j.size&&Kt()}function Dt(){for(let t of document.querySelectorAll("[data-wd-theme]")){let e=a[t.getAttribute("data-wd-theme")||""],n=document.documentElement;e==null||e===""||e==="auto"?n.removeAttribute("data-theme"):n.setAttribute("data-theme",String(e))}}var D=[],V=0;function Lt(){if(!D.length)return;let t=!1;for(let e of D){let n=JSON.stringify(f(a,e.watch)??null);n!==e.last&&(e.last=n,_t(e.actions),t=!0)}if(!t){V=0;return}if(S(),++V>=10){V=0,console.warn("effect did not settle");return}R()}var jt=typeof requestAnimationFrame=="function"?requestAnimationFrame:queueMicrotask,Z=!1;function y(){Z||(Z=!0,jt(()=>{Z=!1,R()}))}document.addEventListener("input",t=>{let e=t.target?.closest("[data-wd-bind-input]");e&&(a[e.getAttribute("data-wd-bind-input")||""]=e.type==="range"?e.valueAsNumber:e.type==="checkbox"?e.checked:e.value,S(),y(),G())});function ut(t){if(!t)return null;let e=t.closest("[data-wd-loop-key]"),n=t.closest("[data-wd-loop]");return!e||!n?null:{srcKey:n.getAttribute("data-wd-loop"),item:e.__wdItem}}function et(t,e,n,r){let o=i=>Ct(a,n,i),s=f(a,n),c=()=>Array.isArray(s)?s:[];if(e==="inc"&&o(Number(s??0)+1),e==="dec"&&o(Number(s??0)-1),e==="add"&&o(Number(s??0)+Number(r)),e==="sub"&&o(Number(s??0)-Number(r)),e==="toggle"&&o(!s),e==="set"&&o(r),e==="append"&&o([...c(),r]),e==="prepend"&&o([r,...c()]),e==="member-toggle"&&o(c().includes(r)?c().filter(i=>i!==r):[...c(),r]),e==="remove-value"&&o(c().filter(i=>i!==r)),e==="clear"&&o(Array.isArray(s)?[]:{}),e==="merge"&&o({...s&&typeof s=="object"?s:{},...typeof r=="string"?f(a,r):r}),e==="delete"&&s&&typeof s=="object"&&(delete s[r],o(s)),e==="reset"&&o(structuredClone(f(M,n))),e==="refetch"){let i=document.querySelector(`[data-wd-fetch-key="${n}"]`);i&&L(i)}if(e==="remove"){let i=ut(t);i&&i.srcKey&&(a[i.srcKey]=(Array.isArray(a[i.srcKey])?a[i.srcKey]:[]).filter(u=>u!==i.item))}if(e==="append-row"){let i=ut(t);if(i&&i.item!==void 0){let u=i.item&&typeof i.item=="object"?structuredClone(i.item):i.item;o([...c(),u])}}}var _t=t=>{for(let e of t)et(null,e.op,e.target||"",structuredClone(e.value))};document.addEventListener("click",t=>{let e=t.target?.closest("[data-wd-action],[data-wd-actions]");if(!e)return;let n=e.getAttribute("data-wd-actions");if(n)for(let r of JSON.parse(n))et(e,r.op,r.target||"",r.value);else{let r=e.getAttribute("data-wd-value");et(e,e.getAttribute("data-wd-action")||"",e.getAttribute("data-wd-target")||"",r===null?void 0:JSON.parse(r))}S(),y(),G()});document.addEventListener("submit",t=>{let e=t.target?.closest("[data-wd-form]");if(!e)return;t.preventDefault();let n=e.getAttribute("data-wd-form")||"",r=e.getAttribute("action");if(!r){let i=new FormData(e),u=Object.fromEntries(i);for(let l of e.querySelectorAll("[data-wd-multi]")){let m=l.getAttribute("data-wd-multi");m&&(u[m]=i.getAll(m))}a[n]=u,S(),y();return}let o=yt(e);x(n),y();let s=new FormData(e);for(let i of e.querySelectorAll("[data-wd-bind-input]")){let u=i.getAttribute("data-wd-bind-input");u&&!i.getAttribute("name")&&!s.has(u)&&s.append(u,a[u]??"")}let c=e.querySelector("input[type=file]");rt(r,{method:e.getAttribute("method")||"post",headers:c?void 0:{"content-type":"application/x-www-form-urlencoded"},body:c?s:new URLSearchParams(s).toString()}).then(i=>{o()||(a[n]=i,x(n),S(),y())}).catch(i=>{o()||(x(n,i),y())})});function L(t){let e=yt(t),n=h=>t.getAttribute("data-wd-fetch-"+h)||"",r=n("key"),o=(h,N)=>{a[r+h]=N,y()},s=!1,c=n("url").replace(/\{\s*([\w$.]+)\s*\}/g,(h,N)=>{let I=f(a,N),w=String(I??"");return(w===""||w==="."||w==="..")&&(s=!0),encodeURIComponent(w)});if(s)return o("_loading",!1);let i=n("headers"),u=n("body"),l=n("refresh"),m=n("method")||"GET",b=Number(n("timeout")),d=Number(n("retry")),p=!1;x(r),o("_loading",!0);let A=h=>{x(r,h),o("_loading",!1)},g=()=>{let h={method:m};i&&(h.headers=a[i]||{}),u&&m!=="GET"&&(h.body=JSON.stringify(a[u]??null));let N=typeof AbortController=="function"?new AbortController:null;N&&(h.signal=N.signal);let I=b&&N?setTimeout(()=>N.abort(),b):0;rt(c,h).then(w=>{clearTimeout(I),!e()&&(a[r]=w,a[r+"_empty"]=Jt(w),x(r),o("_loading",!1))}).catch(w=>{if(clearTimeout(I),e())return;let st=w&&w.status===401;if(st&&l&&i&&!p)return p=!0,void Mt(l,i).then(xt=>{e()||(xt?g():A(w))});if(st&&p)return void A(w);if(d-- >0)return void setTimeout(g,200);A(w)})};g()}var Q=new Map;function Mt(t,e){let n=Q.get(t);if(n)return n;let r=rt(t,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(a[e]??{})}).then(o=>o&&typeof o=="object"?(a[e]=o,S(),!0):!1).catch(()=>!1).finally(()=>Q.delete(t));return Q.set(t,r),r}var ot=[],kt=t=>(t.getAttribute("data-wd-fetch-deps")||"").split(",").filter(Boolean).map(e=>JSON.stringify(f(a,e))).join("|"),dt=0,q=new Set;function G(){for(let t of ot){let e=kt(t);e&&e!==t.__wdSnap&&(t.__wdSnap=e,q.add(t))}q.size&&(clearTimeout(dt),dt=setTimeout(()=>{for(let t of q)L(t);q.clear()},150))}function v(t){for(let e of $(t,"[data-wd-fetch],[data-wd-effect],[data-wd-every]")){q.delete(e);for(let n of[ot,D,E])for(let r=n.length-1;r>=0;r--)(n[r].node||n[r])===e&&(n[r].t&&clearInterval(n[r].t),n.splice(r,1))}}function Pt(){mt();for(let t of document.querySelectorAll("[data-wd-fetch],[data-wd-effect],[data-wd-every]"))if(!t.__wdReg){if(t.__wdReg=1,t.hasAttribute("data-wd-effect")){let e=JSON.parse(t.textContent||"{}");D.push({node:t,watch:e.watch,actions:e.actions||[],last:JSON.stringify(f(a,e.watch)??null)})}if(t.hasAttribute("data-wd-every")&&(E.length||document.addEventListener("visibilitychange",()=>document.hidden?Ut():lt()),E.push({...JSON.parse(t.textContent||"{}"),node:t}),lt()),t.hasAttribute("data-wd-fetch"))if(ot.push(t),t.__wdSnap=kt(t),t.getAttribute("data-wd-fetch-when")==="visible"&&"IntersectionObserver"in window){let e=new IntersectionObserver(n=>{n.some(r=>r.isIntersecting)&&(e.disconnect(),L(t))});e.observe(t)}else L(t)}}var E=[];function lt(){if(!document.hidden)for(let t of E)t.t||!t.ms||!t.actions||(t.t=setInterval(()=>{_t(t.actions),S(),y(),G()},t.ms))}function Ut(){for(let t of E)clearInterval(t.t),t.t=0}var j=new Map;function Kt(){for(let[t,e]of j){let n=JSON.stringify(a[t]??null);if(n!==e.last){e.last=n;for(let r of e.cbs)try{r(a[t])}catch(o){P(`subscribe ${t}`,o)}}}}window.wd={state:a,debug:!1,get:t=>a[t],set:(t,e)=>{a[t]=e,S(),y(),G()},subscribe:(t,e)=>{let n=j.get(t);n||(n={cbs:new Set,last:JSON.stringify(a[t]??null)},j.set(t,n)),n.cbs.add(e);try{e(a[t])}catch(r){P(`subscribe ${t}`,r)}return()=>n.cbs.delete(e)},render:R};R();
|
|
2
2
|
//# sourceMappingURL=runtime.js.map
|