@askrjs/askr 0.4.1 → 0.4.3
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/CHANGELOG.md +39 -0
- package/dist/benchmark.js +3 -3
- package/dist/boot/index.js +1 -1
- package/dist/{boot-C5HnQZio.js → boot-DpsfNWHm.js} +37 -3
- package/dist/foundations/interactions/index.js +1 -1
- package/dist/{interactions-BAa2xuJ-.js → interactions-B_wfsDQg.js} +4 -0
- package/dist/{render-sync-CUxvoVab.js → render-sync-Qz-9Hcqw.js} +159 -17
- package/dist/router/index.js +1 -1
- package/dist/ssg/index.js +1 -1
- package/dist/ssr/index.js +2 -2
- package/dist/{ssr-9LOx_bEu.js → ssr-DueDLmhp.js} +1 -1
- package/dist/testing/index.js +1 -1
- package/dist/{verify-hydration-D_cyn1OX.js → verify-hydration-Jh5rnm_w.js} +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.3 — 2026-10-02
|
|
4
|
+
|
|
5
|
+
### Breaking changes
|
|
6
|
+
|
|
7
|
+
- None.
|
|
8
|
+
|
|
9
|
+
### Deprecations
|
|
10
|
+
|
|
11
|
+
- None.
|
|
12
|
+
|
|
13
|
+
### Additions
|
|
14
|
+
|
|
15
|
+
- None.
|
|
16
|
+
|
|
17
|
+
### Fixes
|
|
18
|
+
|
|
19
|
+
- Retain each queued input or textarea edit and checkbox/radio change during
|
|
20
|
+
hydration, so replayed handlers and a following submit observe the captured
|
|
21
|
+
values even when hydration writes the initial control state. File input
|
|
22
|
+
values remain untouched.
|
|
23
|
+
- Preserve native printable typing, Backspace, and Delete in editable text
|
|
24
|
+
controls before hydration. Enter, modified shortcuts, and activation retain
|
|
25
|
+
their existing cancellation; immediately activated deferred key handlers
|
|
26
|
+
can still cancel the native edit.
|
|
27
|
+
- Discard events whose target is replaced during deferred activation and
|
|
28
|
+
release root capture listeners when the final deferred boundary completes.
|
|
29
|
+
|
|
30
|
+
## 0.4.2 — 2026-10-01
|
|
31
|
+
|
|
32
|
+
- fix(foundations): honor previously cancelled click and keyboard events in
|
|
33
|
+
`pressable()` and `rovingFocus()` while preserving disabled propagation
|
|
34
|
+
suppression and native keyboard activation timing.
|
|
35
|
+
- fix(ssr): support private sibling component opt-in to render descendants once
|
|
36
|
+
before ancestor attributes, preserving legacy attribute order and output for
|
|
37
|
+
ordinary hosts, scoped lifetimes, namespaces, portals, and streaming transport.
|
|
38
|
+
- fix(ssr): finalize private component reference metadata after the rendered
|
|
39
|
+
root and its portals, preserving ordinary attribute evaluation and error
|
|
40
|
+
boundaries without repeating components or caller ID functions.
|
|
41
|
+
|
|
3
42
|
## 0.4.1 — 2026-10-01
|
|
4
43
|
|
|
5
44
|
- fix(data): honor defined-query `skipInitialFetch` and `initialData` options,
|
package/dist/benchmark.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { y as flushSync } from "./instance-DL-YKrj6.js";
|
|
2
2
|
import { a as state, i as selector } from "./state-i9D4EtY6.js";
|
|
3
3
|
import { i as jsxs, n as jsx } from "./jsx-runtime-C4idqHY0.js";
|
|
4
|
-
import { r as createIsland } from "./boot-
|
|
4
|
+
import { r as createIsland } from "./boot-DpsfNWHm.js";
|
|
5
5
|
import { n as For } from "./control-hr3B0QaD.js";
|
|
6
6
|
//#region src/bench/components/benchmark-row.tsx
|
|
7
7
|
function BenchmarkRow({ item, isSelected, onSelect, onRemove }) {
|
|
@@ -58,8 +58,8 @@ function BenchmarkTable({ rows, isSelected, onSelect, onRemove }) {
|
|
|
58
58
|
}
|
|
59
59
|
const benchmarkMetadata = {
|
|
60
60
|
packageName: "@askrjs/askr",
|
|
61
|
-
packageVersion: "0.4.
|
|
62
|
-
buildLabel: "0.4.
|
|
61
|
+
packageVersion: "0.4.3",
|
|
62
|
+
buildLabel: "0.4.3-local"
|
|
63
63
|
};
|
|
64
64
|
function getBenchmarkMetadata() {
|
|
65
65
|
return { ...benchmarkMetadata };
|
package/dist/boot/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as cleanupApp, i as createIslands, n as createSPA, o as hasApp, r as createIsland, t as hydrateSPA } from "../boot-
|
|
1
|
+
import { a as cleanupApp, i as createIslands, n as createSPA, o as hasApp, r as createIsland, t as hydrateSPA } from "../boot-DpsfNWHm.js";
|
|
2
2
|
export { cleanupApp, createIsland, createIslands, createSPA, hasApp, hydrateSPA };
|
|
@@ -3773,6 +3773,33 @@ function cloneInteractionEvent(event) {
|
|
|
3773
3773
|
});
|
|
3774
3774
|
}
|
|
3775
3775
|
}
|
|
3776
|
+
function captureControlState(event, target) {
|
|
3777
|
+
if (event.type !== "input" && event.type !== "change") return void 0;
|
|
3778
|
+
if (target instanceof HTMLInputElement) {
|
|
3779
|
+
if (target.type === "checkbox" || target.type === "radio") return { checked: target.checked };
|
|
3780
|
+
if (target.type !== "file") return { value: target.value };
|
|
3781
|
+
}
|
|
3782
|
+
if (target instanceof HTMLTextAreaElement) return { value: target.value };
|
|
3783
|
+
}
|
|
3784
|
+
function restoreControlState(interaction) {
|
|
3785
|
+
const { target, controlState } = interaction;
|
|
3786
|
+
if (!controlState) return;
|
|
3787
|
+
if ("checked" in controlState) {
|
|
3788
|
+
if (target instanceof HTMLInputElement && (target.type === "checkbox" || target.type === "radio")) target.checked = controlState.checked;
|
|
3789
|
+
} else if (target instanceof HTMLTextAreaElement || target instanceof HTMLInputElement && target.type !== "file") target.value = controlState.value;
|
|
3790
|
+
}
|
|
3791
|
+
function allowsNativeTextInput(event, target) {
|
|
3792
|
+
if (!(event instanceof KeyboardEvent) || event.type !== "keydown" || event.key.length !== 1 && event.key !== "Backspace" && event.key !== "Delete" || event.ctrlKey || event.metaKey || event.altKey) return false;
|
|
3793
|
+
return (target instanceof HTMLTextAreaElement || target instanceof HTMLInputElement && [
|
|
3794
|
+
"text",
|
|
3795
|
+
"search",
|
|
3796
|
+
"email",
|
|
3797
|
+
"url",
|
|
3798
|
+
"tel",
|
|
3799
|
+
"password",
|
|
3800
|
+
"number"
|
|
3801
|
+
].includes(target.type)) && !target.readOnly && !target.disabled;
|
|
3802
|
+
}
|
|
3776
3803
|
/**
|
|
3777
3804
|
* Capture discrete interactions before hydrated handlers exist, then replay
|
|
3778
3805
|
* them after the listener transaction commits. Deferred boundaries remain
|
|
@@ -3825,8 +3852,13 @@ function beginHydrationInteractionReplay(root, activateBoundary, permanentSkipSe
|
|
|
3825
3852
|
deferredBoundaries.delete(boundary);
|
|
3826
3853
|
notifyIfDeferredBoundariesDrained();
|
|
3827
3854
|
}
|
|
3855
|
+
if (!root.contains(interaction.target)) {
|
|
3856
|
+
releaseIfFinished();
|
|
3857
|
+
return;
|
|
3858
|
+
}
|
|
3828
3859
|
replaying = true;
|
|
3829
3860
|
try {
|
|
3861
|
+
restoreControlState(interaction);
|
|
3830
3862
|
interaction.target.dispatchEvent(interaction.event);
|
|
3831
3863
|
} finally {
|
|
3832
3864
|
replaying = false;
|
|
@@ -3839,17 +3871,19 @@ function beginHydrationInteractionReplay(root, activateBoundary, permanentSkipSe
|
|
|
3839
3871
|
if (!target || isPermanentlySkipped(target)) return;
|
|
3840
3872
|
const boundary = findDeferredBoundary(target);
|
|
3841
3873
|
if (!hydratingRoot && !boundary) return;
|
|
3842
|
-
event.preventDefault();
|
|
3874
|
+
if (!allowsNativeTextInput(event, target)) event.preventDefault();
|
|
3843
3875
|
event.stopImmediatePropagation();
|
|
3844
3876
|
const interaction = {
|
|
3845
3877
|
target,
|
|
3846
|
-
event: cloneInteractionEvent(event)
|
|
3878
|
+
event: cloneInteractionEvent(event),
|
|
3879
|
+
controlState: captureControlState(event, target)
|
|
3847
3880
|
};
|
|
3848
3881
|
if (hydratingRoot) {
|
|
3849
3882
|
queued.push(interaction);
|
|
3850
3883
|
return;
|
|
3851
3884
|
}
|
|
3852
3885
|
replayInteraction(interaction);
|
|
3886
|
+
if (interaction.event.defaultPrevented) event.preventDefault();
|
|
3853
3887
|
}
|
|
3854
3888
|
const installedTypes = [];
|
|
3855
3889
|
try {
|
|
@@ -3966,7 +4000,7 @@ async function hydrateSPA(config) {
|
|
|
3966
4000
|
onCommit: captureClientMarkup
|
|
3967
4001
|
});
|
|
3968
4002
|
if (shouldVerifyHydrationMarkup(config)) {
|
|
3969
|
-
const { captureServerHydrationMarkup, verifyClientHydrationMarkup, verifyHydrationSyncForUrl } = await import("./verify-hydration-
|
|
4003
|
+
const { captureServerHydrationMarkup, verifyClientHydrationMarkup, verifyHydrationSyncForUrl } = await import("./verify-hydration-Jh5rnm_w.js");
|
|
3970
4004
|
if (!verifyHydrationSyncForUrl({
|
|
3971
4005
|
root: rootElement,
|
|
3972
4006
|
url: currentUrl,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as focusable, i as hoverable, n as mergeInteractionProps, o as dismissable, r as rovingFocus, s as pressable, t as applyInteractionPolicy } from "../../interactions-
|
|
1
|
+
import { a as focusable, i as hoverable, n as mergeInteractionProps, o as dismissable, r as rovingFocus, s as pressable, t as applyInteractionPolicy } from "../../interactions-B_wfsDQg.js";
|
|
2
2
|
export { applyInteractionPolicy, dismissable, focusable, hoverable, mergeInteractionProps, pressable, rovingFocus };
|
|
@@ -8,6 +8,7 @@ function pressable({ disabled, onPress, isNativeButton = false }) {
|
|
|
8
8
|
e.stopPropagation?.();
|
|
9
9
|
return;
|
|
10
10
|
}
|
|
11
|
+
if (e.defaultPrevented) return;
|
|
11
12
|
onPress?.(e);
|
|
12
13
|
} };
|
|
13
14
|
if (isNativeButton) {
|
|
@@ -25,6 +26,7 @@ function pressable({ disabled, onPress, isNativeButton = false }) {
|
|
|
25
26
|
e.stopPropagation?.();
|
|
26
27
|
return;
|
|
27
28
|
}
|
|
29
|
+
if (e.defaultPrevented) return;
|
|
28
30
|
if (e.key === "Enter") {
|
|
29
31
|
e.preventDefault?.();
|
|
30
32
|
onPress?.(e);
|
|
@@ -38,6 +40,7 @@ function pressable({ disabled, onPress, isNativeButton = false }) {
|
|
|
38
40
|
e.stopPropagation?.();
|
|
39
41
|
return;
|
|
40
42
|
}
|
|
43
|
+
if (e.defaultPrevented) return;
|
|
41
44
|
if (e.key === " ") {
|
|
42
45
|
e.preventDefault?.();
|
|
43
46
|
onPress?.(e);
|
|
@@ -118,6 +121,7 @@ function rovingFocus(options) {
|
|
|
118
121
|
}
|
|
119
122
|
}
|
|
120
123
|
function handleKeyDown(e) {
|
|
124
|
+
if (e.defaultPrevented) return;
|
|
121
125
|
const { key } = e;
|
|
122
126
|
let direction;
|
|
123
127
|
if (orientation === "horizontal" || orientation === "both") {
|
|
@@ -473,6 +473,16 @@ var StreamSink = class {
|
|
|
473
473
|
//#region src/ssr/attrs.ts
|
|
474
474
|
const ESCAPED_ATTR_VALUE_CACHE_LIMIT = 512;
|
|
475
475
|
const escapedAttrValueCache = /* @__PURE__ */ new Map();
|
|
476
|
+
function readReferenceAttributeCell(cell) {
|
|
477
|
+
const descriptor = cell && Object.getOwnPropertyDescriptor(cell, "value");
|
|
478
|
+
if (!descriptor || !("value" in descriptor) || descriptor.value !== void 0 && typeof descriptor.value !== "string") throw new TypeError("SSR reference attribute cells require a string or undefined data value.");
|
|
479
|
+
return descriptor.value;
|
|
480
|
+
}
|
|
481
|
+
function renderReferenceAttributeCell(name, cell, sink) {
|
|
482
|
+
const value = readReferenceAttributeCell(cell);
|
|
483
|
+
if (value === void 0) return;
|
|
484
|
+
sink.write(" " + name + "=\"" + getEscapedAttrValue(value) + "\"");
|
|
485
|
+
}
|
|
476
486
|
function isEventHandler(key) {
|
|
477
487
|
return key.length >= 2 && key.slice(0, 2).toLowerCase() === "on";
|
|
478
488
|
}
|
|
@@ -489,13 +499,13 @@ function resolvePropValue(value) {
|
|
|
489
499
|
* element whose attributes are inspected before they are written. Returns
|
|
490
500
|
* `props` itself when nothing needs reading.
|
|
491
501
|
*/
|
|
492
|
-
function resolveReactiveAttributeProps(props) {
|
|
502
|
+
function resolveReactiveAttributeProps(props, referenceCells) {
|
|
493
503
|
if (!props || typeof props !== "object") return props;
|
|
494
504
|
let resolved = null;
|
|
495
505
|
const propsObj = props;
|
|
496
506
|
for (const key in propsObj) {
|
|
497
507
|
const value = propsObj[key];
|
|
498
|
-
if (typeof value !== "function" || isSkippedProp(key) || isEventHandler(key)) continue;
|
|
508
|
+
if (typeof value !== "function" || isSkippedProp(key) || isEventHandler(key) || referenceCells && Object.prototype.hasOwnProperty.call(referenceCells, key)) continue;
|
|
499
509
|
resolved ??= { ...propsObj };
|
|
500
510
|
resolved[key] = resolvePropValue(value);
|
|
501
511
|
}
|
|
@@ -522,7 +532,7 @@ function getEscapedAttrValue(value) {
|
|
|
522
532
|
* object values on custom elements) have no markup and are left out; the
|
|
523
533
|
* client applies them when it hydrates.
|
|
524
534
|
*/
|
|
525
|
-
function renderAttrsDirect(props, sink, tagName = "") {
|
|
535
|
+
function renderAttrsDirect(props, sink, tagName = "", referenceCells) {
|
|
526
536
|
if (!props || typeof props !== "object") return;
|
|
527
537
|
const customElement = isCustomElementName(tagName);
|
|
528
538
|
const propsObj = props;
|
|
@@ -530,6 +540,14 @@ function renderAttrsDirect(props, sink, tagName = "") {
|
|
|
530
540
|
if (isSkippedProp(key) || key === "dangerouslySetInnerHTML") continue;
|
|
531
541
|
if (isEventHandler(key)) continue;
|
|
532
542
|
if (key.charCodeAt(0) === 95) continue;
|
|
543
|
+
if (referenceCells && Object.prototype.hasOwnProperty.call(referenceCells, key)) {
|
|
544
|
+
if (key !== "aria-labelledby" && key !== "aria-describedby" && key !== "aria-controls") throw new TypeError("SSR reference attribute cells only support derived ARIA references.");
|
|
545
|
+
const cell = referenceCells[key];
|
|
546
|
+
readReferenceAttributeCell(cell);
|
|
547
|
+
if (sink.writeReferenceAttribute) sink.writeReferenceAttribute(key, cell);
|
|
548
|
+
else renderReferenceAttributeCell(key, cell, sink);
|
|
549
|
+
continue;
|
|
550
|
+
}
|
|
533
551
|
const value = resolvePropValue(propsObj[key]);
|
|
534
552
|
if (isPropertyOnlyProp(tagName, key, value)) continue;
|
|
535
553
|
if (value !== null && typeof value === "object" && key.startsWith("attr:")) continue;
|
|
@@ -690,6 +708,9 @@ function stopRenderPhase() {
|
|
|
690
708
|
* render contains preloaded resource slots. Portal hosts write tokens that
|
|
691
709
|
* are replaced with their final content once the render root completes.
|
|
692
710
|
*/
|
|
711
|
+
const CHILDREN_BEFORE_ATTRS = Symbol.for("askr.ssr.children-before-attrs");
|
|
712
|
+
const ATTRIBUTE_ROOT = Symbol.for("askr.ssr.attribute-root");
|
|
713
|
+
const ATTRIBUTE_CELLS = Symbol.for("askr.ssr.attribute-cells");
|
|
693
714
|
/**
|
|
694
715
|
* Collects writes so they can be published or dropped. An ErrorBoundary's
|
|
695
716
|
* subtree renders into one first, so markup from a subtree that then throws
|
|
@@ -701,24 +722,38 @@ var BufferedSink = class {
|
|
|
701
722
|
}
|
|
702
723
|
write(html) {
|
|
703
724
|
if (html) this.operations.push({
|
|
704
|
-
|
|
725
|
+
kind: "text",
|
|
705
726
|
text: html
|
|
706
727
|
});
|
|
707
728
|
}
|
|
708
729
|
writePortalHost(token) {
|
|
709
730
|
this.operations.push({
|
|
710
|
-
|
|
731
|
+
kind: "portal",
|
|
711
732
|
text: token
|
|
712
733
|
});
|
|
713
734
|
}
|
|
735
|
+
writeReferenceAttribute(name, cell) {
|
|
736
|
+
this.operations.push({
|
|
737
|
+
kind: "reference",
|
|
738
|
+
name,
|
|
739
|
+
cell
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
beginAttributeRoot() {
|
|
743
|
+
this.operations.push({ kind: "root" });
|
|
744
|
+
}
|
|
714
745
|
/** The buffered markup, without portal host tokens. */
|
|
715
746
|
html() {
|
|
716
747
|
let html = "";
|
|
717
|
-
for (const operation of this.operations) if (
|
|
748
|
+
for (const operation of this.operations) if (operation.kind === "text") html += operation.text;
|
|
718
749
|
return html;
|
|
719
750
|
}
|
|
720
751
|
publishTo(sink) {
|
|
721
|
-
for (const operation of this.operations) if (operation.
|
|
752
|
+
for (const operation of this.operations) if (operation.kind === "root") sink.beginAttributeRoot?.();
|
|
753
|
+
else if (operation.kind === "reference") {
|
|
754
|
+
if (sink.writeReferenceAttribute) sink.writeReferenceAttribute(operation.name, operation.cell);
|
|
755
|
+
else sink.write(referenceAttributeToken(operation.name, operation.cell));
|
|
756
|
+
} else if (operation.kind === "portal" && sink.writePortalHost) sink.writePortalHost(operation.text);
|
|
722
757
|
else sink.write(operation.text);
|
|
723
758
|
}
|
|
724
759
|
};
|
|
@@ -736,8 +771,14 @@ var PortalSink = class {
|
|
|
736
771
|
writePortalHost(token) {
|
|
737
772
|
(this.buffered ??= []).push(token);
|
|
738
773
|
}
|
|
774
|
+
beginAttributeRoot() {
|
|
775
|
+
this.buffered ??= [];
|
|
776
|
+
}
|
|
777
|
+
writeReferenceAttribute(name, cell) {
|
|
778
|
+
(this.buffered ??= []).push(referenceAttributeToken(name, cell));
|
|
779
|
+
}
|
|
739
780
|
flush(ctx) {
|
|
740
|
-
if (this.buffered) this.sink.write(resolvePortals(this.buffered.join(""), ctx));
|
|
781
|
+
if (this.buffered) this.sink.write(resolveReferenceAttributes(resolvePortals(this.buffered.join(""), ctx)));
|
|
741
782
|
}
|
|
742
783
|
};
|
|
743
784
|
let current = null;
|
|
@@ -777,11 +818,18 @@ function componentOutput(value) {
|
|
|
777
818
|
}
|
|
778
819
|
function renderComponent(fn, props, sink) {
|
|
779
820
|
const render = state();
|
|
821
|
+
const destination = sink;
|
|
822
|
+
const attributeRoot = props[ATTRIBUTE_ROOT] === true ? new BufferedSink() : null;
|
|
823
|
+
if (attributeRoot) {
|
|
824
|
+
attributeRoot.beginAttributeRoot();
|
|
825
|
+
sink = attributeRoot;
|
|
826
|
+
}
|
|
780
827
|
let owner = render.owner;
|
|
781
828
|
for (;;) {
|
|
782
829
|
const instance = new ComponentInstance(owner, fn, props);
|
|
783
830
|
instance.server = true;
|
|
784
831
|
instance.serverContext = render.ctx;
|
|
832
|
+
if (attributeRoot) render.attributeRoots.add(instance);
|
|
785
833
|
const output = runComponent(instance);
|
|
786
834
|
const resources = getCurrentRenderData()?.resources;
|
|
787
835
|
if (instance.serverResourceKeys.length && resources && Object.keys(resources).some((key) => /^r:\d+$/.test(key))) sink.write(`<!--askr-resource:${instance.serverResourceKeys.join(",")}-->`);
|
|
@@ -795,6 +843,7 @@ function renderComponent(fn, props, sink) {
|
|
|
795
843
|
}
|
|
796
844
|
if (!instance.boundary) {
|
|
797
845
|
withOwner(instance, () => renderValue(componentOutput(output), sink));
|
|
846
|
+
attributeRoot?.publishTo(destination);
|
|
798
847
|
return;
|
|
799
848
|
}
|
|
800
849
|
const buffer = new BufferedSink();
|
|
@@ -812,9 +861,11 @@ function renderComponent(fn, props, sink) {
|
|
|
812
861
|
} finally {
|
|
813
862
|
reportBoundaryCleanupErrors(cleanupErrors);
|
|
814
863
|
}
|
|
864
|
+
attributeRoot?.publishTo(destination);
|
|
815
865
|
return;
|
|
816
866
|
}
|
|
817
867
|
buffer.publishTo(sink);
|
|
868
|
+
attributeRoot?.publishTo(destination);
|
|
818
869
|
return;
|
|
819
870
|
}
|
|
820
871
|
}
|
|
@@ -906,19 +957,35 @@ function renderChild(child, sink) {
|
|
|
906
957
|
function assertElementName(name) {
|
|
907
958
|
if (!/^[A-Za-z][A-Za-z0-9._:-]*$/.test(name)) throw new TypeError(`Invalid SSR element name: ${JSON.stringify(name)}`);
|
|
908
959
|
}
|
|
960
|
+
/** Resolve only inputs needed to choose the child's parsing/selection context. */
|
|
961
|
+
function resolveEarlyContextProps(props, names) {
|
|
962
|
+
const descriptors = Object.getOwnPropertyDescriptors(props);
|
|
963
|
+
for (const name of names) {
|
|
964
|
+
if (!(name in props)) continue;
|
|
965
|
+
const value = props[name];
|
|
966
|
+
descriptors[name] = {
|
|
967
|
+
value: typeof value === "function" ? untrack(() => readValue(value)) : value,
|
|
968
|
+
enumerable: descriptors[name]?.enumerable ?? true,
|
|
969
|
+
configurable: true,
|
|
970
|
+
writable: true
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
return Object.create(Object.getPrototypeOf(props), descriptors);
|
|
974
|
+
}
|
|
909
975
|
function renderElement(tag, props, sink) {
|
|
910
976
|
assertElementName(tag);
|
|
911
977
|
if (VOID_ELEMENTS.has(tag)) {
|
|
912
978
|
sink.write("<" + tag);
|
|
913
|
-
|
|
979
|
+
renderHostAttrs(props, sink, tag);
|
|
914
980
|
sink.write(" />");
|
|
915
981
|
return;
|
|
916
982
|
}
|
|
917
983
|
const render = state();
|
|
984
|
+
const childrenBeforeAttrs = props[CHILDREN_BEFORE_ATTRS] === true;
|
|
918
985
|
const parentNamespace = render.namespace;
|
|
919
986
|
const lower = tag.toLowerCase();
|
|
920
987
|
const namespace = getElementNamespace(parentNamespace, lower);
|
|
921
|
-
let elementProps = lower === "annotation-xml" || namespace === "html" && (lower === "select" || lower === "option" && parentNamespace === "select") ? resolveReactiveAttributeProps(props) ?? props : props;
|
|
988
|
+
let elementProps = lower === "annotation-xml" || namespace === "html" && (lower === "select" || lower === "option" && parentNamespace === "select") ? childrenBeforeAttrs ? resolveEarlyContextProps(props, lower === "annotation-xml" ? ["encoding"] : lower === "select" ? ["value", "multiple"] : ["value"]) : resolveReactiveAttributeProps(props, referenceCellsFor(props)) ?? props : props;
|
|
922
989
|
const selection = render.selectSelections[render.selectSelections.length - 1];
|
|
923
990
|
let renderedChildren = null;
|
|
924
991
|
if (lower === "option" && parentNamespace === "select" && selection) {
|
|
@@ -933,14 +1000,27 @@ function renderElement(tag, props, sink) {
|
|
|
933
1000
|
} else value = String(own);
|
|
934
1001
|
const selected = selection.values.has(value) && (selection.multiple || !selection.matched);
|
|
935
1002
|
if (selected) selection.matched = true;
|
|
936
|
-
|
|
1003
|
+
if (childrenBeforeAttrs) {
|
|
1004
|
+
const descriptors = Object.getOwnPropertyDescriptors(elementProps);
|
|
1005
|
+
descriptors.selected = {
|
|
1006
|
+
value: selected,
|
|
1007
|
+
enumerable: true,
|
|
1008
|
+
configurable: true,
|
|
1009
|
+
writable: true
|
|
1010
|
+
};
|
|
1011
|
+
elementProps = Object.create(Object.getPrototypeOf(elementProps), descriptors);
|
|
1012
|
+
} else elementProps = {
|
|
937
1013
|
...elementProps,
|
|
938
1014
|
selected
|
|
939
1015
|
};
|
|
940
1016
|
}
|
|
941
1017
|
if (renderedChildren) {
|
|
1018
|
+
if (childrenBeforeAttrs) {
|
|
1019
|
+
writeBufferedElement(tag, elementProps, null, sink, renderedChildren);
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
942
1022
|
sink.write("<" + tag);
|
|
943
|
-
|
|
1023
|
+
renderHostAttrs(elementProps, sink, tag);
|
|
944
1024
|
sink.write(">");
|
|
945
1025
|
renderedChildren.publishTo(sink);
|
|
946
1026
|
sink.write("</" + tag + ">");
|
|
@@ -959,7 +1039,7 @@ function renderElement(tag, props, sink) {
|
|
|
959
1039
|
}
|
|
960
1040
|
const rawText = getRawTextElementInContext(parentNamespace, namespace, lower);
|
|
961
1041
|
try {
|
|
962
|
-
withNamespace(getChildNamespace(parentNamespace, namespace, lower, elementProps), () => writeElement(tag, elementProps, rawText, sink));
|
|
1042
|
+
withNamespace(getChildNamespace(parentNamespace, namespace, lower, elementProps), () => writeElement(tag, elementProps, rawText, sink, childrenBeforeAttrs));
|
|
963
1043
|
} finally {
|
|
964
1044
|
if (nextSelection) render.selectSelections.pop();
|
|
965
1045
|
}
|
|
@@ -1045,9 +1125,24 @@ function optionTextValue(html) {
|
|
|
1045
1125
|
}
|
|
1046
1126
|
return text;
|
|
1047
1127
|
}
|
|
1048
|
-
function
|
|
1128
|
+
function writeBufferedElement(tag, props, rawText, sink, children) {
|
|
1129
|
+
const opening = new BufferedSink();
|
|
1130
|
+
opening.write("<" + tag);
|
|
1131
|
+
renderHostAttrs(props, opening, rawText === null ? tag : void 0);
|
|
1132
|
+
opening.write(">");
|
|
1133
|
+
opening.publishTo(sink);
|
|
1134
|
+
children.publishTo(sink);
|
|
1135
|
+
sink.write("</" + tag + ">");
|
|
1136
|
+
}
|
|
1137
|
+
function writeElement(tag, props, rawText, sink, childrenBeforeAttrs) {
|
|
1138
|
+
if (childrenBeforeAttrs) {
|
|
1139
|
+
const children = new BufferedSink();
|
|
1140
|
+
writeContent(props, rawText, children);
|
|
1141
|
+
writeBufferedElement(tag, props, rawText, sink, children);
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1049
1144
|
sink.write("<" + tag);
|
|
1050
|
-
|
|
1145
|
+
renderHostAttrs(props, sink, rawText === null ? tag : void 0);
|
|
1051
1146
|
sink.write(">");
|
|
1052
1147
|
writeContent(props, rawText, sink);
|
|
1053
1148
|
sink.write("</" + tag + ">");
|
|
@@ -1124,6 +1219,49 @@ function flattenText(value, element) {
|
|
|
1124
1219
|
visit(value);
|
|
1125
1220
|
return out;
|
|
1126
1221
|
}
|
|
1222
|
+
function referenceCellsFor(props) {
|
|
1223
|
+
const cells = props[ATTRIBUTE_CELLS];
|
|
1224
|
+
if (!cells || typeof cells !== "object") return void 0;
|
|
1225
|
+
const render = state();
|
|
1226
|
+
for (let owner = render.owner; owner; owner = owner.parent) if (render.attributeRoots.has(owner)) return cells;
|
|
1227
|
+
}
|
|
1228
|
+
function renderHostAttrs(props, sink, tag) {
|
|
1229
|
+
const cells = referenceCellsFor(props);
|
|
1230
|
+
if (!cells) {
|
|
1231
|
+
renderAttrsDirect(props, sink, tag);
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1234
|
+
renderAttrsDirect(props, {
|
|
1235
|
+
write: (html) => sink.write(html),
|
|
1236
|
+
writeReferenceAttribute: (name, cell) => {
|
|
1237
|
+
if (sink.writeReferenceAttribute) sink.writeReferenceAttribute(name, cell);
|
|
1238
|
+
else sink.write(referenceAttributeToken(name, cell));
|
|
1239
|
+
}
|
|
1240
|
+
}, tag, cells);
|
|
1241
|
+
}
|
|
1242
|
+
function referenceAttributeToken(name, cell) {
|
|
1243
|
+
const render = state();
|
|
1244
|
+
readReferenceAttributeCell(cell);
|
|
1245
|
+
if (render.referenceAttributeNonce === null) {
|
|
1246
|
+
const nonce = globalThis.crypto.getRandomValues(/* @__PURE__ */ new Uint32Array(4));
|
|
1247
|
+
render.referenceAttributeNonce = Array.from(nonce, (part) => part.toString(16).padStart(8, "0")).join("");
|
|
1248
|
+
}
|
|
1249
|
+
const token = `<!--askr-attribute:${render.referenceAttributeNonce}:${render.nextReferenceAttribute++}-->`;
|
|
1250
|
+
render.referenceAttributes.set(token, {
|
|
1251
|
+
name,
|
|
1252
|
+
cell
|
|
1253
|
+
});
|
|
1254
|
+
return token;
|
|
1255
|
+
}
|
|
1256
|
+
function resolveReferenceAttributes(html) {
|
|
1257
|
+
let resolved = html;
|
|
1258
|
+
for (const [token, { name, cell }] of state().referenceAttributes) {
|
|
1259
|
+
const sink = new StringSink();
|
|
1260
|
+
renderReferenceAttributeCell(name, cell, sink);
|
|
1261
|
+
resolved = resolved.replace(token, () => sink.toString());
|
|
1262
|
+
}
|
|
1263
|
+
return resolved;
|
|
1264
|
+
}
|
|
1127
1265
|
function renderToString(value, namespace) {
|
|
1128
1266
|
const sink = new StringSink();
|
|
1129
1267
|
withNamespace(namespace, () => renderValue(value, sink));
|
|
@@ -1159,7 +1297,11 @@ function withServerRender(ctx, fn) {
|
|
|
1159
1297
|
owner,
|
|
1160
1298
|
namespace: "html",
|
|
1161
1299
|
portalNamespaces: /* @__PURE__ */ new Map(),
|
|
1162
|
-
selectSelections: []
|
|
1300
|
+
selectSelections: [],
|
|
1301
|
+
attributeRoots: /* @__PURE__ */ new WeakSet(),
|
|
1302
|
+
referenceAttributes: /* @__PURE__ */ new Map(),
|
|
1303
|
+
referenceAttributeNonce: null,
|
|
1304
|
+
nextReferenceAttribute: 0
|
|
1163
1305
|
};
|
|
1164
1306
|
let result;
|
|
1165
1307
|
try {
|
|
@@ -1227,7 +1369,7 @@ function renderToStringSync(component, props, options) {
|
|
|
1227
1369
|
const sink = new StringSink();
|
|
1228
1370
|
renderValue(rootElement(component, props ?? {}, nonce), sink);
|
|
1229
1371
|
sink.end();
|
|
1230
|
-
const html = resolvePortals(sink.toString(), ctx);
|
|
1372
|
+
const html = resolveReferenceAttributes(resolvePortals(sink.toString(), ctx));
|
|
1231
1373
|
options?.onContext?.(ctx);
|
|
1232
1374
|
return html + serializeHydrationRenderData(ctx.hydrationData ?? void 0, ctx.dataRuntime);
|
|
1233
1375
|
});
|
package/dist/router/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { n as createRouteRegistry } from "../manifest-BNupb86f.js";
|
|
|
6
6
|
import { a as group, c as route, i as fallback, n as resolveDeferredValues, o as index, r as routeData, s as page, t as Resolve } from "../deferred-nGZglpj6.js";
|
|
7
7
|
import { a as isSafeHref, r as assertPathHrefStaysOnOrigin } from "../url-C9xMpMMv.js";
|
|
8
8
|
import { o as mergeProps } from "../compose-ref-ccRU59BM.js";
|
|
9
|
-
import { t as applyInteractionPolicy } from "../interactions-
|
|
9
|
+
import { t as applyInteractionPolicy } from "../interactions-B_wfsDQg.js";
|
|
10
10
|
//#region src/router/route-query.ts
|
|
11
11
|
function getRouteQueryHistoryMode(options) {
|
|
12
12
|
if (options.history) return options.history;
|
package/dist/ssg/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { ct as encodePathSegment, mt as prepareRouteHydrationData, nt as getRend
|
|
|
2
2
|
import { n as createRouteRegistry } from "../manifest-BNupb86f.js";
|
|
3
3
|
import { c as route, n as resolveDeferredValues } from "../deferred-nGZglpj6.js";
|
|
4
4
|
import { n as renderDocument } from "../ssr-DPfdcq0e.js";
|
|
5
|
-
import { n as renderToString } from "../ssr-
|
|
5
|
+
import { n as renderToString } from "../ssr-DueDLmhp.js";
|
|
6
6
|
import * as fs$1 from "node:fs/promises";
|
|
7
7
|
import * as os from "node:os";
|
|
8
8
|
import * as pathModule from "node:path";
|
package/dist/ssr/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { T as SSRDataMissingError } from "../render-context-DGHnEveC.js";
|
|
2
|
-
import { a as SSRAccessDecisionError, i as renderRouteRequestToString, n as renderToString, r as renderRouteRequest, t as renderToStream } from "../ssr-
|
|
3
|
-
import { c as getRenderContext, d as escapeHtml, l as withRenderContext, n as renderToStringSync, s as createRenderContext, u as withRenderContextAsync } from "../render-sync-
|
|
2
|
+
import { a as SSRAccessDecisionError, i as renderRouteRequestToString, n as renderToString, r as renderRouteRequest, t as renderToStream } from "../ssr-DueDLmhp.js";
|
|
3
|
+
import { c as getRenderContext, d as escapeHtml, l as withRenderContext, n as renderToStringSync, s as createRenderContext, u as withRenderContextAsync } from "../render-sync-Qz-9Hcqw.js";
|
|
4
4
|
export { SSRAccessDecisionError, SSRDataMissingError, createRenderContext, escapeHtml, getRenderContext, renderRouteRequest, renderRouteRequestToString, renderToStream, renderToString, renderToStringSync, withRenderContext, withRenderContextAsync };
|
|
@@ -5,7 +5,7 @@ import { c as withTelemetry, i as dehydrateDataRuntime, t as createQueryPrefetch
|
|
|
5
5
|
import { C as removeRouteBasePath, S as normalizeRouteBasePath, _ as getMatchingRouteRecord, a as resolveRouteRequest, n as getRouteRenderContext } from "./resolution-CF1BqHLm.js";
|
|
6
6
|
import { t as createDataRuntime } from "./data-runtime-i-RD0Ffm.js";
|
|
7
7
|
import { n as renderDocument } from "./ssr-DPfdcq0e.js";
|
|
8
|
-
import { a as serializeHydrationRenderData, i as StringSink, n as renderToStringSync, o as withHydratedAuth, r as StreamSink, s as createRenderContext, t as renderSSRRouteAppToSink, u as withRenderContextAsync } from "./render-sync-
|
|
8
|
+
import { a as serializeHydrationRenderData, i as StringSink, n as renderToStringSync, o as withHydratedAuth, r as StreamSink, s as createRenderContext, t as renderSSRRouteAppToSink, u as withRenderContextAsync } from "./render-sync-Qz-9Hcqw.js";
|
|
9
9
|
//#region src/ssr/errors.ts
|
|
10
10
|
/**
|
|
11
11
|
* Thrown by synchronous route SSR (`renderToString()`/`renderToStream()`) when
|
package/dist/testing/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { y as flushSync } from "../instance-DL-YKrj6.js";
|
|
2
2
|
import { n as createSource, s as notify, u as readSource } from "../hooks-kjLigwDJ.js";
|
|
3
3
|
import { v as matchRoute } from "../resolution-CF1BqHLm.js";
|
|
4
|
-
import { a as cleanupApp, n as createSPA, r as createIsland } from "../boot-
|
|
4
|
+
import { a as cleanupApp, n as createSPA, r as createIsland } from "../boot-DpsfNWHm.js";
|
|
5
5
|
import { h as addInvalidationListener, t as createDataRuntime, u as resolveDataRuntimeState } from "../data-runtime-i-RD0Ffm.js";
|
|
6
6
|
import { a as loadingQueryState, i as freshQueryState, o as refreshingQueryState, r as errorQueryState, s as staleQueryState } from "../data-BsnOUpQ6.js";
|
|
7
7
|
import { i as normalizeAsyncDataError } from "../shared-O2ZnmYAf.js";
|
|
@@ -2,7 +2,7 @@ import { r as validateCspNonce } from "./csp-nonce-D-XM_uuJ.js";
|
|
|
2
2
|
import { g as withHydrationVerificationRender, v as getHydrationRenderUrl } from "./render-context-DGHnEveC.js";
|
|
3
3
|
import { C as removeRouteBasePath, S as normalizeRouteBasePath, f as currentAuth, n as getRouteRenderContext, y as resolveRouteFromRoutes } from "./resolution-CF1BqHLm.js";
|
|
4
4
|
import { t as SSR_RENDER_DATA_ATTR } from "./ssr-DPfdcq0e.js";
|
|
5
|
-
import { i as StringSink, s as createRenderContext, t as renderSSRRouteAppToSink } from "./render-sync-
|
|
5
|
+
import { i as StringSink, s as createRenderContext, t as renderSSRRouteAppToSink } from "./render-sync-Qz-9Hcqw.js";
|
|
6
6
|
//#region src/ssr/render-resolved.ts
|
|
7
7
|
function sameRouteParams(left, right) {
|
|
8
8
|
if (!left || !right) return !left && !right;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@askrjs/askr",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "A TypeScript UI runtime with getter-based state, fine-grained bindings, transactional commits with rollback, and one route graph for SPA, SSR, and SSG.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"askr",
|