octane 0.2.13 → 0.2.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/constants.cjs +7 -7
- package/dist/cjs/dom-binding-claims.cjs +28 -0
- package/dist/cjs/dom-binding-handoff.cjs +2 -2
- package/dist/cjs/dom-bindings.cjs +105 -13
- package/dist/cjs/runtime.cjs +311 -55
- package/dist/cjs/server/streamed-signals.cjs +3 -2
- package/dist/cjs/signals/control-binding.cjs +33 -9
- package/dist/cjs/signals/control-handoff.cjs +39 -0
- package/dist/cjs/stream-protocol.cjs +6 -0
- package/dist/cjs/version.cjs +1 -1
- package/dist/compiler/compile.js +86 -24
- package/dist/compiler/dom-binding-program.js +30 -3
- package/dist/compiler/dom-bindings.js +115 -34
- package/dist/compiler/volar.js +58 -17
- package/dist/constants.d.ts +2 -2
- package/dist/constants.js +2 -2
- package/dist/dom-binding-claims.d.ts +1 -0
- package/dist/dom-binding-claims.js +4 -0
- package/dist/dom-binding-handoff.d.ts +6 -4
- package/dist/dom-binding-handoff.js +1 -1
- package/dist/dom-binding-program.d.ts +2 -1
- package/dist/dom-binding-program.js +16 -1
- package/dist/dom-bindings.d.ts +5 -2
- package/dist/dom-bindings.js +105 -13
- package/dist/runtime.d.ts +34 -3
- package/dist/runtime.js +314 -55
- package/dist/server/streamed-signals.js +3 -2
- package/dist/signals/control-binding.d.ts +9 -5
- package/dist/signals/control-binding.js +38 -6
- package/dist/signals/control-handoff.d.ts +26 -0
- package/dist/signals/control-handoff.js +12 -0
- package/dist/signals/index.d.ts +1 -0
- package/dist/stream-protocol.d.ts +4 -0
- package/dist/stream-protocol.js +4 -0
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/dom-bindings.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { normalizeClass } from "./class-names.js";
|
|
2
|
+
import { domBindingClaims } from "./dom-binding-claims.js";
|
|
2
3
|
import { sanitizeURL } from "./sanitize-url.js";
|
|
4
|
+
import { STREAM_SCRIPT_ATTR, SUSPENSE_SCRIPT_ATTR } from "./stream-protocol.js";
|
|
5
|
+
import { NATIVE_SIGNAL_SEED_ATTR } from "./signals/native-read-seeds.js";
|
|
6
|
+
import { BINDING_HANDOFF } from "./signals/control-handoff.js";
|
|
3
7
|
import { captureSignalOwner, currentSignalOwner } from "./signals/owner-context.js";
|
|
4
8
|
import {
|
|
5
9
|
NATIVE_TRANSITION_CONSUMER,
|
|
@@ -59,6 +63,11 @@ function __releaseBinding(node, name) {
|
|
|
59
63
|
const channels = claims.get(node);
|
|
60
64
|
channels.delete(name);
|
|
61
65
|
if (channels.size === 0) claims.delete(node);
|
|
66
|
+
const published = domBindingClaims.get(node);
|
|
67
|
+
if (published) {
|
|
68
|
+
published.delete(name);
|
|
69
|
+
if (published.size === 0) domBindingClaims.delete(node);
|
|
70
|
+
}
|
|
62
71
|
}
|
|
63
72
|
function resolveNodes(root, descriptor) {
|
|
64
73
|
if (root?.nodeType !== 1 || root.getAttribute("data-octane-bindings") !== descriptor.id || descriptor.nodes.length === 0) {
|
|
@@ -68,9 +77,9 @@ function resolveNodes(root, descriptor) {
|
|
|
68
77
|
const nodes = [];
|
|
69
78
|
const childIndices = [];
|
|
70
79
|
for (let i = 0; i < descriptor.nodes.length; i++) {
|
|
71
|
-
const [parent, tag, namespace, children] = descriptor.nodes[i];
|
|
80
|
+
const [parent, tag, namespace, children, , text] = descriptor.nodes[i];
|
|
72
81
|
const node = i === 0 ? parent === -1 ? root : void 0 : parent >= 0 && parent < i && descriptor.nodes[parent][3] !== null ? nodes[parent]?.children[childIndices[parent]++] : void 0;
|
|
73
|
-
if (node === void 0 || descriptor.nodes[i][4] === true || node.localName !== tag || node.namespaceURI !== namespaces[namespace] || children !== null && (node.childNodes.length !== children || node.children.length !== children)) {
|
|
82
|
+
if (node === void 0 || descriptor.nodes[i][4] === true || node.localName !== tag || node.namespaceURI !== namespaces[namespace] || (text ? node.childNodes.length > 1 || node.firstChild !== null && node.firstChild.nodeType !== 3 : children !== null && (node.childNodes.length !== children || node.children.length !== children))) {
|
|
74
83
|
throw new Error("DOM bindings cannot adopt a mismatched static element topology.");
|
|
75
84
|
}
|
|
76
85
|
nodes.push(node);
|
|
@@ -111,9 +120,9 @@ function resolveAddressedNodes(root, descriptor) {
|
|
|
111
120
|
}
|
|
112
121
|
const childCounts = new Array(descriptor.nodes.length).fill(0);
|
|
113
122
|
for (let i = 0; i < descriptor.nodes.length; i++) {
|
|
114
|
-
const [parent, tag, namespace, children, openChildren] = descriptor.nodes[i];
|
|
123
|
+
const [parent, tag, namespace, children, openChildren, text] = descriptor.nodes[i];
|
|
115
124
|
const node = nodes[i];
|
|
116
|
-
if (node === void 0 || node.localName !== tag || node.namespaceURI !== namespaces[namespace] || (i === 0 ? parent !== -1 || node !== root : parent < 0 || parent >= i || descriptor.nodes[parent][3] === null || node.parentElement !== nodes[parent] || descriptor.nodes[parent][4] !== true && nodes[parent].children[childCounts[parent]] !== node) || openChildren && children === null || children !== null && !openChildren && (node.childNodes.length !== children || node.children.length !== children)) {
|
|
125
|
+
if (node === void 0 || node.localName !== tag || node.namespaceURI !== namespaces[namespace] || (i === 0 ? parent !== -1 || node !== root : parent < 0 || parent >= i || descriptor.nodes[parent][3] === null || node.parentElement !== nodes[parent] || descriptor.nodes[parent][4] !== true && nodes[parent].children[childCounts[parent]] !== node) || openChildren && children === null || (text ? node.childNodes.length > 1 || node.firstChild !== null && node.firstChild.nodeType !== 3 : children !== null && !openChildren && (node.childNodes.length !== children || node.children.length !== children))) {
|
|
117
126
|
throw new Error("DOM bindings cannot adopt a mismatched addressed element topology.");
|
|
118
127
|
}
|
|
119
128
|
if (parent !== -1) childCounts[parent]++;
|
|
@@ -128,6 +137,11 @@ function resolveAddressedNodes(root, descriptor) {
|
|
|
128
137
|
function normalize(binding, value) {
|
|
129
138
|
const type = typeof value;
|
|
130
139
|
switch (binding[1]) {
|
|
140
|
+
case "text":
|
|
141
|
+
if (value == null || value === false) return "";
|
|
142
|
+
if (type !== "string" && type !== "number" && type !== "bigint" && type !== "boolean")
|
|
143
|
+
throw new TypeError("DOM binding text must be a synchronous scalar.");
|
|
144
|
+
return String(value);
|
|
131
145
|
case "styleObject":
|
|
132
146
|
return value;
|
|
133
147
|
case "class":
|
|
@@ -155,7 +169,11 @@ function normalize(binding, value) {
|
|
|
155
169
|
}
|
|
156
170
|
function write(node, binding, value) {
|
|
157
171
|
const name = binding[2];
|
|
158
|
-
if (binding[1] === "
|
|
172
|
+
if (binding[1] === "text") {
|
|
173
|
+
const text = node.firstChild;
|
|
174
|
+
if (text === null) node.appendChild(node.ownerDocument.createTextNode(value));
|
|
175
|
+
else if (text.nodeValue !== value) text.nodeValue = value;
|
|
176
|
+
} else if (binding[1] === "classToken") {
|
|
159
177
|
node.classList.toggle(name, value !== null);
|
|
160
178
|
} else if (binding[1] === "url") {
|
|
161
179
|
if (value === "" && !(name === "href" && node.localName === "a")) value = null;
|
|
@@ -254,16 +272,28 @@ function __adoptBindings(root, descriptor, source, options) {
|
|
|
254
272
|
let dirty = false;
|
|
255
273
|
let revision = 0;
|
|
256
274
|
let unsubscribe;
|
|
275
|
+
let publicationRetries;
|
|
276
|
+
let published;
|
|
257
277
|
const signal = options?.signal;
|
|
258
|
-
const dispose = () => {
|
|
259
|
-
if (disposed)
|
|
278
|
+
const dispose = (_disposal, publish, preservePresentation = false) => {
|
|
279
|
+
if (disposed) {
|
|
280
|
+
publish?.();
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
260
283
|
disposed = true;
|
|
284
|
+
publicationRetries?.clear();
|
|
261
285
|
signal?.removeEventListener("abort", dispose);
|
|
262
286
|
let failed = false;
|
|
263
287
|
let failure;
|
|
288
|
+
try {
|
|
289
|
+
publish?.();
|
|
290
|
+
} catch (error) {
|
|
291
|
+
failed = true;
|
|
292
|
+
failure = error;
|
|
293
|
+
}
|
|
264
294
|
for (const connection of signalConnections?.values() ?? []) {
|
|
265
295
|
try {
|
|
266
|
-
connection.dispose();
|
|
296
|
+
connection.dispose(preservePresentation);
|
|
267
297
|
} catch (error) {
|
|
268
298
|
if (!failed) {
|
|
269
299
|
failed = true;
|
|
@@ -273,7 +303,7 @@ function __adoptBindings(root, descriptor, source, options) {
|
|
|
273
303
|
}
|
|
274
304
|
for (const connection of projections ? new Set(projections.values()) : []) {
|
|
275
305
|
try {
|
|
276
|
-
connection.dispose();
|
|
306
|
+
connection.dispose(preservePresentation);
|
|
277
307
|
} catch (error) {
|
|
278
308
|
if (!failed) {
|
|
279
309
|
failed = true;
|
|
@@ -298,9 +328,9 @@ function __adoptBindings(root, descriptor, source, options) {
|
|
|
298
328
|
for (let i = 0; i < owned.length; i++) {
|
|
299
329
|
const [node, name] = owned[i];
|
|
300
330
|
try {
|
|
301
|
-
groups?.get(i)?.dispose();
|
|
302
|
-
styles?.get(i)?.dispose();
|
|
303
|
-
if (bindings[i][1] === "classToken" && previous[i] === "")
|
|
331
|
+
groups?.get(i)?.dispose(preservePresentation);
|
|
332
|
+
styles?.get(i)?.dispose(preservePresentation);
|
|
333
|
+
if (!preservePresentation && bindings[i][1] === "classToken" && previous[i] === "")
|
|
304
334
|
node.classList.remove(bindings[i][2]);
|
|
305
335
|
} catch (error) {
|
|
306
336
|
if (!failed) {
|
|
@@ -356,7 +386,21 @@ function __adoptBindings(root, descriptor, source, options) {
|
|
|
356
386
|
styles.set(i, style = __createBindingStyleRestoration(nodes[binding[0]], binding));
|
|
357
387
|
style.write(next[i]);
|
|
358
388
|
} else write(nodes[binding[0]], binding, next[i]);
|
|
359
|
-
if (!disposed)
|
|
389
|
+
if (!disposed) {
|
|
390
|
+
previous[i] = next[i];
|
|
391
|
+
if (binding[1] === "attr" || binding[1] === "boolean" || binding[1] === "aria" || binding[1] === "class" || binding[1] === "styleProperty" || binding[1] === "text") {
|
|
392
|
+
const [node, name] = owned[i];
|
|
393
|
+
let published2 = next[i];
|
|
394
|
+
if (binding[1] === "styleProperty") {
|
|
395
|
+
const style = node.style;
|
|
396
|
+
const value = style.getPropertyValue(binding[2]);
|
|
397
|
+
published2 = value === "" ? null : value + (style.getPropertyPriority(binding[2]) ? " !important" : "");
|
|
398
|
+
}
|
|
399
|
+
let claims2 = domBindingClaims.get(node);
|
|
400
|
+
if (claims2 === void 0) domBindingClaims.set(node, claims2 = /* @__PURE__ */ new Map());
|
|
401
|
+
claims2.set(name, published2);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
360
404
|
}
|
|
361
405
|
};
|
|
362
406
|
const drain = (preview = false) => {
|
|
@@ -571,6 +615,7 @@ function __adoptBindings(root, descriptor, source, options) {
|
|
|
571
615
|
writePrepared(next, indices, preparedControls, preparedGroups);
|
|
572
616
|
} finally {
|
|
573
617
|
busy = false;
|
|
618
|
+
published?.();
|
|
574
619
|
}
|
|
575
620
|
}
|
|
576
621
|
};
|
|
@@ -593,6 +638,7 @@ function __adoptBindings(root, descriptor, source, options) {
|
|
|
593
638
|
for (const index of previousUpdates ?? []) signalUpdates.add(index);
|
|
594
639
|
}
|
|
595
640
|
busy = false;
|
|
641
|
+
if (!preview) published?.();
|
|
596
642
|
}
|
|
597
643
|
};
|
|
598
644
|
const refresh = () => {
|
|
@@ -608,6 +654,52 @@ function __adoptBindings(root, descriptor, source, options) {
|
|
|
608
654
|
prepare: () => run(() => drain(true))
|
|
609
655
|
};
|
|
610
656
|
const handle = { refresh, dispose };
|
|
657
|
+
if (descriptor.handoff === "host") {
|
|
658
|
+
published = () => {
|
|
659
|
+
if (!publicationRetries?.size) return;
|
|
660
|
+
const pending = [...publicationRetries];
|
|
661
|
+
publicationRetries.clear();
|
|
662
|
+
for (const retry of pending)
|
|
663
|
+
queueMicrotask(() => {
|
|
664
|
+
if (!disposed) retry();
|
|
665
|
+
});
|
|
666
|
+
};
|
|
667
|
+
const host = root;
|
|
668
|
+
const document = host.ownerDocument;
|
|
669
|
+
const ancestry = [];
|
|
670
|
+
for (let node = host; node !== null; node = node.parentNode)
|
|
671
|
+
ancestry.push([node, node.parentNode]);
|
|
672
|
+
const sibling = (node, previous2) => {
|
|
673
|
+
let next = previous2 ? node.previousSibling : node.nextSibling;
|
|
674
|
+
while (next?.nodeType === 1 && next.localName === "script" && (next.hasAttribute(NATIVE_SIGNAL_SEED_ATTR) || next.hasAttribute(SUSPENSE_SCRIPT_ATTR) || next.hasAttribute(STREAM_SCRIPT_ATTR)))
|
|
675
|
+
next = previous2 ? next.previousSibling : next.nextSibling;
|
|
676
|
+
return next;
|
|
677
|
+
};
|
|
678
|
+
const previousSibling = sibling(host, true);
|
|
679
|
+
const nextSibling = sibling(host, false);
|
|
680
|
+
let handoff;
|
|
681
|
+
handle[BINDING_HANDOFF] = () => {
|
|
682
|
+
if (handoff !== void 0) return handoff;
|
|
683
|
+
return handoff = {
|
|
684
|
+
id: descriptor.id,
|
|
685
|
+
root: host,
|
|
686
|
+
anchor: host,
|
|
687
|
+
host: new Set(
|
|
688
|
+
bindings.map(
|
|
689
|
+
(binding) => binding[1] === "styleProperty" ? "style:" + binding[2] : binding[1].startsWith("style") ? "style" : binding[2]
|
|
690
|
+
)
|
|
691
|
+
),
|
|
692
|
+
revision: () => busy ? -1 : revision,
|
|
693
|
+
afterPublication: (callback) => {
|
|
694
|
+
(publicationRetries ??= /* @__PURE__ */ new Set()).add(callback);
|
|
695
|
+
return () => publicationRetries.delete(callback);
|
|
696
|
+
},
|
|
697
|
+
valid: () => host.parentNode !== null && host.ownerDocument === document && ancestry.every(([node, parent]) => node.parentNode === parent) && sibling(host, true) === previousSibling && sibling(host, false) === nextSibling && host.getAttribute("data-octane-bindings") === descriptor.id,
|
|
698
|
+
active: () => !disposed,
|
|
699
|
+
retire: (publish) => dispose(void 0, publish, true)
|
|
700
|
+
};
|
|
701
|
+
};
|
|
702
|
+
}
|
|
611
703
|
if (signal?.aborted) {
|
|
612
704
|
dispose();
|
|
613
705
|
return handle;
|
package/dist/runtime.d.ts
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import type { BindingHandle } from './dom-bindings.js';
|
|
16
16
|
import { type BindingHandoff } from './dom-binding-handoff.js';
|
|
17
|
+
import { type ControlHandoff, type SignalControlBinding } from './signals/control-handoff.js';
|
|
17
18
|
import { EXTERNAL_HYDRATION_PROMISE, HYDRATION_RANGE_BOUNDARY } from './constants.js';
|
|
18
19
|
import type { HydrateProps } from './hydration/types.js';
|
|
19
20
|
import type { IndependentHydrateActivator } from './hydration/independent-island.js';
|
|
@@ -23,7 +24,7 @@ import { SignalCandidateFrame } from './signals/transition-candidate.js';
|
|
|
23
24
|
import { setNativeAdoptionResolver } from './signals/read-protocol.js';
|
|
24
25
|
import { type NativeAdoptionState } from './signals/native-read-seeds.js';
|
|
25
26
|
export { documentSignalOwner, installStreamedSignalOwnerActivator, } from './signals/document-owner.js';
|
|
26
|
-
import { type SignalOwner, type SignalRendererOwnerIdentity } from './signals/types.js';
|
|
27
|
+
import { type SignalHandle, type SignalOwner, type SignalRendererOwnerIdentity } from './signals/types.js';
|
|
27
28
|
export { EXTERNAL_HYDRATION_PROMISE, HYDRATION_RANGE_BOUNDARY };
|
|
28
29
|
export { validateNativeReadWitness };
|
|
29
30
|
export type ComponentBody<P = any, E = any> = (props: P, scope: Scope, extra: E) => void;
|
|
@@ -526,6 +527,7 @@ interface RootRenderOwner {
|
|
|
526
527
|
/** Shared document/account data owner; distinct from this root's presentation owner. */
|
|
527
528
|
signalOwner?: SignalOwner;
|
|
528
529
|
bindingLeases?: Set<BindingHandoff>;
|
|
530
|
+
controlLeases?: Map<Element, ControlHandoff | undefined>;
|
|
529
531
|
preservePresentation?: boolean;
|
|
530
532
|
bindingContainer?: Node;
|
|
531
533
|
adopt?: (block: Block) => void;
|
|
@@ -1428,10 +1430,18 @@ interface PresentationHydrationFrame {
|
|
|
1428
1430
|
readToken: number;
|
|
1429
1431
|
witnessToken: number;
|
|
1430
1432
|
witnesses: Map<object, NativeReadWitness | null>;
|
|
1433
|
+
/** Successor native writers only; a host receipt never tears down children. */
|
|
1434
|
+
hostSuccessors?: Map<object, () => void>;
|
|
1435
|
+
controls?: Map<Element, {
|
|
1436
|
+
valid: (retired?: boolean) => boolean;
|
|
1437
|
+
binding: DirectSignalBinding;
|
|
1438
|
+
lease: ControlHandoff | undefined;
|
|
1439
|
+
stop?: () => void;
|
|
1440
|
+
}>;
|
|
1431
1441
|
completed: boolean;
|
|
1432
1442
|
lease: BindingHandoff;
|
|
1433
1443
|
revision?: number;
|
|
1434
|
-
/** Installed only for structural receipts
|
|
1444
|
+
/** Installed only for structural/control/host receipts, not ordinary fixed presentations. */
|
|
1435
1445
|
current?: typeof currentPresentation;
|
|
1436
1446
|
hydration: HydrationCapability | null;
|
|
1437
1447
|
}
|
|
@@ -1439,7 +1449,7 @@ interface PresentationHydrationFrame {
|
|
|
1439
1449
|
export declare function presentationFailure(error: unknown): never;
|
|
1440
1450
|
declare function currentPresentation(frame: PresentationHydrationFrame): boolean;
|
|
1441
1451
|
/** @internal Only compiler-proven native views enter this publication boundary. */
|
|
1442
|
-
export declare function beginPresentationHydration(scope: Scope, id: string, supported?: boolean, structural?: boolean, conditionalRest?: boolean): PresentationHydrationFrame | null;
|
|
1452
|
+
export declare function beginPresentationHydration(scope: Scope, id: string, supported?: boolean, structural?: boolean, conditionalRest?: boolean, host?: boolean): PresentationHydrationFrame | null;
|
|
1443
1453
|
/** @internal Restore the read frame even when preparation suspends or throws. */
|
|
1444
1454
|
export declare function endPresentationHydration(frame: PresentationHydrationFrame | null, completed?: boolean): void;
|
|
1445
1455
|
/** @internal Only authored host spreads retain generic host preparation. */
|
|
@@ -1465,7 +1475,26 @@ export declare function child<T extends Node>(node: T): Node | null;
|
|
|
1465
1475
|
*/
|
|
1466
1476
|
export declare function sibling(node: Node, n?: number): Node | null;
|
|
1467
1477
|
export declare function setText(node: Text, value: any): void;
|
|
1478
|
+
declare const DIRECT_SIGNAL_BINDING: unique symbol;
|
|
1479
|
+
type DirectSignalBindingKind = 'text' | 'textOnlyChild' | 'attribute' | 'value' | 'checked';
|
|
1468
1480
|
type DirectSignalAttributeKind = 'attr' | 'class' | 'booleanAttr' | 'ariaAttr' | 'stringData';
|
|
1481
|
+
interface DirectSignalBinding {
|
|
1482
|
+
readonly [DIRECT_SIGNAL_BINDING]: true;
|
|
1483
|
+
readonly scope: Scope;
|
|
1484
|
+
readonly kind: DirectSignalBindingKind;
|
|
1485
|
+
readonly target: Node;
|
|
1486
|
+
readonly site: string;
|
|
1487
|
+
readonly name?: string;
|
|
1488
|
+
readonly attributeKind?: DirectSignalAttributeKind;
|
|
1489
|
+
readonly handle: SignalHandle<unknown> | null;
|
|
1490
|
+
text?: Text;
|
|
1491
|
+
value: unknown;
|
|
1492
|
+
unsubscribe?: () => void;
|
|
1493
|
+
input?: EventListener;
|
|
1494
|
+
controlWriterCleanup?: () => void;
|
|
1495
|
+
pendingControl?: boolean;
|
|
1496
|
+
disposed: boolean;
|
|
1497
|
+
}
|
|
1469
1498
|
/** @internal Compiler target for a direct signal/scalar text binding. */
|
|
1470
1499
|
export declare function bindSignalText(scope: Scope, previous: unknown, position: Node, value: unknown, site: string, onlyChild?: boolean, seededText?: 1 | undefined, bindingMarker?: string, previousValue?: unknown): unknown;
|
|
1471
1500
|
/** @internal Compiler target for a direct signal/scalar attribute binding. */
|
|
@@ -2284,6 +2313,8 @@ export interface Root {
|
|
|
2284
2313
|
export interface RootOptions {
|
|
2285
2314
|
/** Adopt fixed native early bindings only when their matching hydration capture commits. */
|
|
2286
2315
|
bindingLeases?: readonly BindingHandle[];
|
|
2316
|
+
/** Offer an existing textarea value owner for accepted presentation hydration, never early disposal. */
|
|
2317
|
+
controlLeases?: readonly SignalControlBinding[];
|
|
2287
2318
|
/**
|
|
2288
2319
|
* Shared document/account owner for module signals. Roots borrow this owner;
|
|
2289
2320
|
* unmounting a presentation root never retires shared data state.
|