octane 0.2.11 → 0.2.13
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/README.md +5 -0
- package/dist/behavior-root.d.ts +13 -3
- package/dist/behavior-root.js +28 -5
- package/dist/cjs/behavior-root.cjs +32 -6
- package/dist/cjs/class-names.cjs +49 -0
- package/dist/cjs/constants.cjs +9 -0
- package/dist/cjs/context-epoch.cjs +38 -0
- package/dist/cjs/css.cjs +6 -27
- package/dist/cjs/data-encoding.cjs +160 -0
- package/dist/cjs/dom-binding-handoff.cjs +93 -0
- package/dist/cjs/dom-binding-protocol.cjs +115 -0
- package/dist/cjs/dom-binding-styles.cjs +292 -0
- package/dist/cjs/dom-bindings.cjs +700 -0
- package/dist/cjs/dom-focused-move.cjs +66 -0
- package/dist/cjs/dom-stage.cjs +896 -0
- package/dist/cjs/dom-tables.cjs +4 -94
- package/dist/cjs/error-codes.client.generated.cjs +33 -0
- package/dist/cjs/error-codes.server.generated.cjs +24 -0
- package/dist/cjs/hydration/control-capture.cjs +244 -0
- package/dist/cjs/hydration/event-capture.cjs +125 -43
- package/dist/cjs/hydration/interaction-config.cjs +50 -2
- package/dist/cjs/hydration-markers.cjs +37 -0
- package/dist/cjs/independent-hydration-protocol.cjs +106 -0
- package/dist/cjs/index.cjs +9 -0
- package/dist/cjs/runtime.cjs +6397 -1683
- package/dist/cjs/runtime.server.cjs +1465 -213
- package/dist/cjs/server/early-signals.cjs +45 -0
- package/dist/cjs/server/index.cjs +47 -0
- package/dist/cjs/server/rpc-batch.cjs +149 -0
- package/dist/cjs/server/rpc-stream.cjs +146 -0
- package/dist/cjs/server/rpc.cjs +34 -15
- package/dist/cjs/server/signal-query-observation.cjs +158 -0
- package/dist/cjs/server/streamed-region.cjs +52 -0
- package/dist/cjs/server/streamed-signals.cjs +512 -0
- package/dist/cjs/server-call.cjs +103 -0
- package/dist/cjs/server-rpc-batch-client.cjs +141 -0
- package/dist/cjs/server-rpc-client.cjs +94 -27
- package/dist/cjs/server-rpc-protocol.cjs +141 -0
- package/dist/cjs/server-rpc-stream-client.cjs +118 -0
- package/dist/cjs/signals/actions.cjs +474 -0
- package/dist/cjs/signals/client.cjs +4 -1
- package/dist/cjs/signals/computations.cjs +460 -0
- package/dist/cjs/signals/control-binding.cjs +368 -0
- package/dist/cjs/signals/derived-facade.cjs +50 -0
- package/dist/cjs/signals/document-owner.cjs +71 -0
- package/dist/cjs/signals/early-values.cjs +155 -0
- package/dist/cjs/signals/encoding.cjs +6 -95
- package/dist/cjs/signals/engine.cjs +276 -12
- package/dist/cjs/signals/errors.cjs +18 -0
- package/dist/cjs/signals/facade.cjs +477 -0
- package/dist/cjs/signals/graph.cjs +166 -20
- package/dist/cjs/signals/handle-protocol.cjs +36 -0
- package/dist/cjs/signals/index.cjs +81 -2
- package/dist/cjs/signals/native-read-client.cjs +10 -0
- package/dist/cjs/signals/owner-context.cjs +112 -0
- package/dist/cjs/signals/query-attempt-observer.cjs +68 -0
- package/dist/cjs/signals/query-facade.cjs +72 -0
- package/dist/cjs/signals/read-protocol.cjs +54 -0
- package/dist/cjs/signals/requests.cjs +287 -14
- package/dist/cjs/signals/scalar-computations.cjs +76 -0
- package/dist/cjs/signals/server.cjs +4 -1
- package/dist/cjs/signals/transition-candidate.cjs +567 -0
- package/dist/cjs/signals/transition-state.cjs +106 -0
- package/dist/cjs/signals/types.cjs +50 -0
- package/dist/cjs/stream-protocol.cjs +3 -0
- package/dist/cjs/streamed-signals-protocol.cjs +159 -0
- package/dist/cjs/style-values.cjs +122 -0
- package/dist/cjs/trusted-html.cjs +30 -0
- package/dist/cjs/version.cjs +1 -1
- package/dist/class-names.d.ts +5 -0
- package/dist/class-names.js +25 -0
- package/dist/compiler/bundler.js +51 -22
- package/dist/compiler/compile.js +2534 -581
- package/dist/compiler/dom-binding-program.js +935 -0
- package/dist/compiler/dom-binding-request.js +57 -0
- package/dist/compiler/dom-bindings.js +2245 -0
- package/dist/compiler/hook-deps.js +759 -10
- package/dist/compiler/hydrate-boundaries.js +358 -15
- package/dist/compiler/index.d.ts +39 -2
- package/dist/compiler/index.js +1 -0
- package/dist/compiler/native-attribute-reads.js +127 -0
- package/dist/compiler/native-change-diagnostics.js +32 -38
- package/dist/compiler/native-read-diagnostics.js +69 -8
- package/dist/compiler/native-read-types.js +80 -34
- package/dist/compiler/parser.node.js +1 -1
- package/dist/compiler/renderer-boundaries.js +40 -0
- package/dist/compiler/server-context.js +118 -0
- package/dist/compiler/signal-attempt-reads.js +479 -0
- package/dist/compiler/signal-declarations.js +557 -0
- package/dist/compiler/signal-start-reads.js +413 -0
- package/dist/compiler/slot-hooks.d.ts +3 -1
- package/dist/compiler/slot-hooks.js +168 -34
- package/dist/compiler/strong-auto-memo.js +90 -0
- package/dist/compiler/strong-html.js +143 -0
- package/dist/compiler/strong-mode.js +764 -66
- package/dist/compiler/strong-template-policy.js +307 -0
- package/dist/compiler/template-ir.js +200 -0
- package/dist/compiler/typescript.d.ts +2 -0
- package/dist/compiler/typescript.js +2 -0
- package/dist/compiler/vite.d.ts +3 -1
- package/dist/compiler/vite.js +122 -4
- package/dist/compiler/volar.LICENSES.txt +1 -1
- package/dist/compiler/volar.d.ts +6 -1
- package/dist/compiler/volar.js +750 -1433
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +10 -0
- package/dist/context-epoch.d.ts +2 -0
- package/dist/context-epoch.js +13 -0
- package/dist/css.d.ts +2 -5
- package/dist/css.js +2 -23
- package/dist/data-encoding.d.ts +9 -0
- package/dist/data-encoding.js +134 -0
- package/dist/dom-binding-classes.d.ts +15 -0
- package/dist/dom-binding-classes.js +129 -0
- package/dist/dom-binding-controls.d.ts +2 -0
- package/dist/dom-binding-controls.js +6 -0
- package/dist/dom-binding-handoff.d.ts +55 -0
- package/dist/dom-binding-handoff.js +62 -0
- package/dist/dom-binding-program.d.ts +300 -0
- package/dist/dom-binding-program.js +1625 -0
- package/dist/dom-binding-projections.d.ts +15 -0
- package/dist/dom-binding-projections.js +187 -0
- package/dist/dom-binding-protocol.d.ts +13 -0
- package/dist/dom-binding-protocol.js +88 -0
- package/dist/dom-binding-signals.d.ts +21 -0
- package/dist/dom-binding-signals.js +109 -0
- package/dist/dom-binding-styles.d.ts +17 -0
- package/dist/dom-binding-styles.js +272 -0
- package/dist/dom-bindings.d.ts +89 -0
- package/dist/dom-bindings.js +670 -0
- package/dist/dom-focused-move.d.ts +6 -0
- package/dist/dom-focused-move.js +42 -0
- package/dist/dom-stage.d.ts +52 -0
- package/dist/dom-stage.js +872 -0
- package/dist/dom-tables.d.ts +1 -3
- package/dist/dom-tables.js +1 -91
- package/dist/error-codes.client.generated.d.ts +9 -0
- package/dist/error-codes.client.generated.js +33 -0
- package/dist/error-codes.server.generated.d.ts +6 -0
- package/dist/error-codes.server.generated.js +24 -0
- package/dist/hydration/control-capture.d.ts +62 -0
- package/dist/hydration/control-capture.js +223 -0
- package/dist/hydration/document-lifecycle.d.ts +23 -0
- package/dist/hydration/document-lifecycle.js +80 -0
- package/dist/hydration/event-capture.d.ts +20 -1
- package/dist/hydration/event-capture.js +128 -42
- package/dist/hydration/independent-island.d.ts +45 -0
- package/dist/hydration/independent-island.js +233 -0
- package/dist/hydration/index.d.ts +8 -1
- package/dist/hydration/index.js +55 -1
- package/dist/hydration/interaction-config.d.ts +5 -0
- package/dist/hydration/interaction-config.js +44 -1
- package/dist/hydration/stream-delivery.d.ts +32 -0
- package/dist/hydration/stream-delivery.js +333 -0
- package/dist/hydration/stream-receiver.d.ts +26 -0
- package/dist/hydration/stream-receiver.js +204 -0
- package/dist/hydration/stream-result-receiver.d.ts +70 -0
- package/dist/hydration/stream-result-receiver.js +246 -0
- package/dist/hydration/streamed-signals.d.ts +44 -0
- package/dist/hydration/streamed-signals.js +186 -0
- package/dist/hydration/types.d.ts +6 -0
- package/dist/hydration-markers.d.ts +7 -0
- package/dist/hydration-markers.js +10 -0
- package/dist/independent-hydration-protocol.d.ts +31 -0
- package/dist/independent-hydration-protocol.js +80 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +6 -0
- package/dist/internal/signal-read.d.ts +1 -0
- package/dist/internal/signal-read.js +4 -0
- package/dist/jsx-runtime-strong.d.ts +33 -0
- package/dist/jsx-runtime.d.ts +58 -2
- package/dist/public-types.d.ts +15 -1
- package/dist/runtime.d.ts +297 -23
- package/dist/runtime.js +6405 -1679
- package/dist/runtime.server.d.ts +110 -27
- package/dist/runtime.server.js +1458 -213
- package/dist/server/early-signals.d.ts +14 -0
- package/dist/server/early-signals.js +34 -0
- package/dist/server/index.d.ts +11 -2
- package/dist/server/index.js +50 -1
- package/dist/server/rpc-batch.d.ts +17 -0
- package/dist/server/rpc-batch.js +129 -0
- package/dist/server/rpc-stream.d.ts +9 -0
- package/dist/server/rpc-stream.js +125 -0
- package/dist/server/rpc.d.ts +5 -1
- package/dist/server/rpc.js +37 -13
- package/dist/server/signal-query-observation.d.ts +3 -0
- package/dist/server/signal-query-observation.js +134 -0
- package/dist/server/streamed-region.d.ts +15 -0
- package/dist/server/streamed-region.js +30 -0
- package/dist/server/streamed-signals.d.ts +45 -0
- package/dist/server/streamed-signals.js +488 -0
- package/dist/server-call.d.ts +41 -0
- package/dist/server-call.js +75 -0
- package/dist/server-rpc-batch-client.d.ts +7 -0
- package/dist/server-rpc-batch-client.js +119 -0
- package/dist/server-rpc-client.d.ts +3 -1
- package/dist/server-rpc-client.js +99 -27
- package/dist/server-rpc-protocol.d.ts +47 -0
- package/dist/server-rpc-protocol.js +109 -0
- package/dist/server-rpc-stream-client.d.ts +5 -0
- package/dist/server-rpc-stream-client.js +98 -0
- package/dist/signals/actions.d.ts +12 -0
- package/dist/signals/actions.js +466 -0
- package/dist/signals/client.d.ts +2 -0
- package/dist/signals/client.js +1 -0
- package/dist/signals/computations.d.ts +37 -0
- package/dist/signals/computations.js +455 -0
- package/dist/signals/control-binding.d.ts +31 -0
- package/dist/signals/control-binding.js +353 -0
- package/dist/signals/derived-facade.d.ts +3 -0
- package/dist/signals/derived-facade.js +25 -0
- package/dist/signals/document-owner.d.ts +10 -0
- package/dist/signals/document-owner.js +43 -0
- package/dist/signals/early-values.d.ts +24 -0
- package/dist/signals/early-values.js +124 -0
- package/dist/signals/encoding.d.ts +1 -3
- package/dist/signals/encoding.js +3 -93
- package/dist/signals/engine.d.ts +53 -4
- package/dist/signals/engine.js +275 -16
- package/dist/signals/errors.d.ts +7 -0
- package/dist/signals/errors.js +16 -0
- package/dist/signals/facade.d.ts +74 -0
- package/dist/signals/facade.js +452 -0
- package/dist/signals/graph.d.ts +52 -6
- package/dist/signals/graph.js +174 -19
- package/dist/signals/handle-protocol.d.ts +4 -0
- package/dist/signals/handle-protocol.js +11 -0
- package/dist/signals/index.d.ts +12 -3
- package/dist/signals/index.js +71 -2
- package/dist/signals/native-read-client.d.ts +3 -0
- package/dist/signals/native-read-client.js +10 -0
- package/dist/signals/owner-context.d.ts +22 -0
- package/dist/signals/owner-context.js +77 -0
- package/dist/signals/query-attempt-observer.d.ts +49 -0
- package/dist/signals/query-attempt-observer.js +41 -0
- package/dist/signals/query-facade.d.ts +3 -0
- package/dist/signals/query-facade.js +49 -0
- package/dist/signals/read-protocol.d.ts +37 -2
- package/dist/signals/read-protocol.js +44 -0
- package/dist/signals/requests.d.ts +38 -5
- package/dist/signals/requests.js +298 -15
- package/dist/signals/scalar-computations.d.ts +2 -0
- package/dist/signals/scalar-computations.js +60 -0
- package/dist/signals/server.d.ts +2 -0
- package/dist/signals/server.js +1 -0
- package/dist/signals/transition-candidate.d.ts +71 -0
- package/dist/signals/transition-candidate.js +557 -0
- package/dist/signals/transition-state.d.ts +51 -0
- package/dist/signals/transition-state.js +70 -0
- package/dist/signals/types.d.ts +95 -5
- package/dist/signals/types.js +20 -0
- package/dist/stream-protocol.d.ts +0 -5
- package/dist/stream-protocol.js +3 -0
- package/dist/streamed-signals-protocol.d.ts +60 -0
- package/dist/streamed-signals-protocol.js +130 -0
- package/dist/style-values.d.ts +3 -0
- package/dist/style-values.js +96 -0
- package/dist/trusted-html.d.ts +17 -0
- package/dist/trusted-html.js +6 -0
- package/dist/universal-core.d.ts +6 -0
- package/dist/universal-core.js +214 -202
- package/dist/version.js +1 -1
- package/package.json +48 -5
|
@@ -1,4 +1,42 @@
|
|
|
1
1
|
const HYDRATE_INTERACTION_EVENTS_ATTR = "data-octane-hydrate-interaction-events";
|
|
2
|
+
const HYDRATE_SELECTION_ATTR = "data-octane-hydrate-selection";
|
|
3
|
+
const EARLY_HYDRATION_INTENTS_KEY = "__octaneEarlyHydrationIntents";
|
|
4
|
+
const EARLY_HYDRATION_INTENTS_LIMIT = 256;
|
|
5
|
+
const HYDRATE_SUPPORTED_INTERACTION_EVENTS = [
|
|
6
|
+
"auxclick",
|
|
7
|
+
"beforeinput",
|
|
8
|
+
"click",
|
|
9
|
+
"compositionend",
|
|
10
|
+
"compositionstart",
|
|
11
|
+
"compositionupdate",
|
|
12
|
+
"contextmenu",
|
|
13
|
+
"dblclick",
|
|
14
|
+
"focusin",
|
|
15
|
+
"input",
|
|
16
|
+
"keydown",
|
|
17
|
+
"keyup",
|
|
18
|
+
"mousedown",
|
|
19
|
+
"mouseenter",
|
|
20
|
+
"mouseover",
|
|
21
|
+
"mouseup",
|
|
22
|
+
"pointerdown",
|
|
23
|
+
"pointerenter",
|
|
24
|
+
"pointerover",
|
|
25
|
+
"pointerup",
|
|
26
|
+
"touchend",
|
|
27
|
+
"touchstart"
|
|
28
|
+
];
|
|
29
|
+
const HYDRATE_NATIVE_DEFAULT_INTERACTION_EVENTS = [
|
|
30
|
+
"beforeinput",
|
|
31
|
+
"compositionend",
|
|
32
|
+
"compositionstart",
|
|
33
|
+
"compositionupdate",
|
|
34
|
+
"input",
|
|
35
|
+
"mousedown",
|
|
36
|
+
"pointerdown",
|
|
37
|
+
"touchend",
|
|
38
|
+
"touchstart"
|
|
39
|
+
];
|
|
2
40
|
const HYDRATE_DEFAULT_INTERACTION_EVENTS = [
|
|
3
41
|
"pointerenter",
|
|
4
42
|
"focusin",
|
|
@@ -13,6 +51,11 @@ const HYDRATE_DEFAULT_INTERACTION_EVENTS = [
|
|
|
13
51
|
"click"
|
|
14
52
|
];
|
|
15
53
|
export {
|
|
54
|
+
EARLY_HYDRATION_INTENTS_KEY,
|
|
55
|
+
EARLY_HYDRATION_INTENTS_LIMIT,
|
|
16
56
|
HYDRATE_DEFAULT_INTERACTION_EVENTS,
|
|
17
|
-
HYDRATE_INTERACTION_EVENTS_ATTR
|
|
57
|
+
HYDRATE_INTERACTION_EVENTS_ATTR,
|
|
58
|
+
HYDRATE_NATIVE_DEFAULT_INTERACTION_EVENTS,
|
|
59
|
+
HYDRATE_SELECTION_ATTR,
|
|
60
|
+
HYDRATE_SUPPORTED_INTERACTION_EVENTS
|
|
18
61
|
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type StreamedResultReceiver } from './stream-result-receiver.js';
|
|
2
|
+
type StreamedDeliveryReceiver = Pick<StreamedResultReceiver, 'receive' | 'failSelection'>;
|
|
3
|
+
/** Stable realm slot used by CSP-nonced server frame calls. */
|
|
4
|
+
export declare const STREAMED_RENDERER_RECEIVER = "__octaneStreamedRenderer";
|
|
5
|
+
export interface StreamedRendererGlobal {
|
|
6
|
+
receive(frame: unknown): void;
|
|
7
|
+
}
|
|
8
|
+
export interface StreamedRendererDeliveryOptions {
|
|
9
|
+
readonly maxFrameBytes?: number;
|
|
10
|
+
/** Includes style/composition waits; also caps unfinished inline result channels. */
|
|
11
|
+
readonly maxPendingFrames?: number;
|
|
12
|
+
readonly maxPendingBytes?: number;
|
|
13
|
+
/** Maximum queue/placement and inline result lifetime; readers also bound the response. */
|
|
14
|
+
readonly timeoutMs?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface StreamedRendererReadOptions extends StreamedRendererDeliveryOptions {
|
|
17
|
+
readonly maxTotalBytes?: number;
|
|
18
|
+
readonly signal?: AbortSignal;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Install the pre-module frame entrypoint. A host calls this before any
|
|
22
|
+
* renderer-owned frame script; replacing a receiver already owned by another
|
|
23
|
+
* document is an error rather than a silent authority transfer.
|
|
24
|
+
*/
|
|
25
|
+
export declare function installStreamedRendererGlobal(receiver: StreamedDeliveryReceiver, target?: Record<string, unknown>, options?: StreamedRendererDeliveryOptions): () => void;
|
|
26
|
+
/**
|
|
27
|
+
* Incrementally consume the same newline-delimited frame protocol used by the
|
|
28
|
+
* initial document stream. A bounded cross-channel window permits independent
|
|
29
|
+
* progress; a full window applies backpressure before accepting another frame.
|
|
30
|
+
*/
|
|
31
|
+
export declare function readStreamedRendererResponse(response: Response, receiver: StreamedDeliveryReceiver, options?: StreamedRendererReadOptions): Promise<void>;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isStreamedRendererFrame,
|
|
3
|
+
streamFrameIdentityKey
|
|
4
|
+
} from "../streamed-signals-protocol.js";
|
|
5
|
+
import {
|
|
6
|
+
StreamedReceiverError
|
|
7
|
+
} from "./stream-result-receiver.js";
|
|
8
|
+
const STREAMED_RENDERER_RECEIVER = "__octaneStreamedRenderer";
|
|
9
|
+
const DEFAULT_MAX_FRAME_BYTES = 4 * 1024 * 1024;
|
|
10
|
+
const DEFAULT_MAX_TOTAL_BYTES = 64 * 1024 * 1024;
|
|
11
|
+
const DEFAULT_PENDING_BYTES = 16 * 1024 * 1024;
|
|
12
|
+
const DEFAULT_PENDING_FRAMES = 64;
|
|
13
|
+
const DEFAULT_TIMEOUT_MS = 3e4;
|
|
14
|
+
function positiveLimit(value, fallback) {
|
|
15
|
+
const limit = value ?? fallback;
|
|
16
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) {
|
|
17
|
+
throw new RangeError("Streamed renderer response limits must be positive safe integers.");
|
|
18
|
+
}
|
|
19
|
+
return limit;
|
|
20
|
+
}
|
|
21
|
+
function createDelivery(receiver, options, delivered) {
|
|
22
|
+
const maxFrameBytes = positiveLimit(options.maxFrameBytes, DEFAULT_MAX_FRAME_BYTES);
|
|
23
|
+
const maxPendingBytes = positiveLimit(options.maxPendingBytes, DEFAULT_PENDING_BYTES);
|
|
24
|
+
const maxPendingFrames = positiveLimit(options.maxPendingFrames, DEFAULT_PENDING_FRAMES);
|
|
25
|
+
const timeoutMs = positiveLimit(options.timeoutMs, DEFAULT_TIMEOUT_MS);
|
|
26
|
+
const tails = /* @__PURE__ */ new Map();
|
|
27
|
+
const pending = /* @__PURE__ */ new Set();
|
|
28
|
+
const aborters = /* @__PURE__ */ new Set();
|
|
29
|
+
const waiters = /* @__PURE__ */ new Set();
|
|
30
|
+
let pendingBytes = 0;
|
|
31
|
+
let closed;
|
|
32
|
+
const notify = () => {
|
|
33
|
+
for (const wake of waiters) wake();
|
|
34
|
+
waiters.clear();
|
|
35
|
+
};
|
|
36
|
+
function validateSize(bytes) {
|
|
37
|
+
if (bytes > maxFrameBytes || bytes > maxPendingBytes)
|
|
38
|
+
throw new StreamedReceiverError(
|
|
39
|
+
"overflow",
|
|
40
|
+
"A streamed renderer frame exceeded its byte budget."
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
async room(bytes) {
|
|
45
|
+
validateSize(bytes);
|
|
46
|
+
while (pending.size >= maxPendingFrames || pendingBytes + bytes > maxPendingBytes) {
|
|
47
|
+
if (closed !== void 0) throw closed;
|
|
48
|
+
await new Promise((resolve) => waiters.add(resolve));
|
|
49
|
+
}
|
|
50
|
+
if (closed !== void 0) throw closed;
|
|
51
|
+
},
|
|
52
|
+
enqueue(frame, bytes) {
|
|
53
|
+
if (closed !== void 0) return Promise.reject(closed);
|
|
54
|
+
try {
|
|
55
|
+
validateSize(bytes);
|
|
56
|
+
if (pending.size >= maxPendingFrames || pendingBytes + bytes > maxPendingBytes)
|
|
57
|
+
throw new StreamedReceiverError(
|
|
58
|
+
"overflow",
|
|
59
|
+
"Streamed renderer delivery exceeded its pending budget."
|
|
60
|
+
);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
receiver.failSelection(frame.identity, error);
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
const key = JSON.stringify([streamFrameIdentityKey(frame.identity), frame.channel]);
|
|
66
|
+
const previous = tails.get(key) ?? Promise.resolve();
|
|
67
|
+
let canceled;
|
|
68
|
+
let abort;
|
|
69
|
+
const cancellation = new Promise((_, reject) => {
|
|
70
|
+
abort = (error) => {
|
|
71
|
+
canceled = error;
|
|
72
|
+
try {
|
|
73
|
+
receiver.failSelection(frame.identity, error);
|
|
74
|
+
} catch {
|
|
75
|
+
}
|
|
76
|
+
reject(error);
|
|
77
|
+
};
|
|
78
|
+
aborters.add(abort);
|
|
79
|
+
});
|
|
80
|
+
const timer = setTimeout(
|
|
81
|
+
() => abort(new StreamedReceiverError("timeout", "Streamed renderer delivery timed out.")),
|
|
82
|
+
timeoutMs
|
|
83
|
+
);
|
|
84
|
+
const work = previous.catch(() => {
|
|
85
|
+
}).then(() => {
|
|
86
|
+
if (canceled !== void 0) throw canceled;
|
|
87
|
+
return receiver.receive(frame);
|
|
88
|
+
});
|
|
89
|
+
const delivery = Promise.race([work, cancellation]).then((disposition) => {
|
|
90
|
+
if (canceled !== void 0) throw canceled;
|
|
91
|
+
delivered?.(frame, disposition);
|
|
92
|
+
}).catch((cause) => {
|
|
93
|
+
const error = cause instanceof StreamedReceiverError ? cause : new StreamedReceiverError("protocol", "Streamed renderer delivery failed.");
|
|
94
|
+
try {
|
|
95
|
+
receiver.failSelection(frame.identity, error);
|
|
96
|
+
} catch {
|
|
97
|
+
}
|
|
98
|
+
throw error;
|
|
99
|
+
}).finally(() => {
|
|
100
|
+
clearTimeout(timer);
|
|
101
|
+
aborters.delete(abort);
|
|
102
|
+
pending.delete(delivery);
|
|
103
|
+
pendingBytes -= bytes;
|
|
104
|
+
if (tails.get(key) === delivery) tails.delete(key);
|
|
105
|
+
notify();
|
|
106
|
+
});
|
|
107
|
+
pending.add(delivery);
|
|
108
|
+
pendingBytes += bytes;
|
|
109
|
+
tails.set(key, delivery);
|
|
110
|
+
void delivery.catch(() => {
|
|
111
|
+
});
|
|
112
|
+
return delivery;
|
|
113
|
+
},
|
|
114
|
+
async drain() {
|
|
115
|
+
while (pending.size !== 0) await Promise.allSettled([...pending]);
|
|
116
|
+
},
|
|
117
|
+
close(error) {
|
|
118
|
+
if (closed !== void 0) return;
|
|
119
|
+
closed = error;
|
|
120
|
+
for (const abort of aborters) abort(error);
|
|
121
|
+
notify();
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function installStreamedRendererGlobal(receiver, target = globalThis, options = {}) {
|
|
126
|
+
const previous = Object.getOwnPropertyDescriptor(target, STREAMED_RENDERER_RECEIVER);
|
|
127
|
+
const early = previous?.value;
|
|
128
|
+
if (previous !== void 0 && (early === null || typeof early !== "object" || early.version !== 1 || !Array.isArray(early.frames) || typeof early.receive !== "function")) {
|
|
129
|
+
throw new Error("An Octane streamed renderer receiver is already installed in this realm.");
|
|
130
|
+
}
|
|
131
|
+
if (early?.overflow === true) {
|
|
132
|
+
throw new Error("The pre-module streamed renderer mailbox overflowed.");
|
|
133
|
+
}
|
|
134
|
+
const maxOpenResults = positiveLimit(options.maxPendingFrames, DEFAULT_PENDING_FRAMES);
|
|
135
|
+
const resultTimeoutMs = positiveLimit(options.timeoutMs, DEFAULT_TIMEOUT_MS);
|
|
136
|
+
const openResults = /* @__PURE__ */ new Map();
|
|
137
|
+
function forgetResult(identity) {
|
|
138
|
+
const key = streamFrameIdentityKey(identity);
|
|
139
|
+
const result = openResults.get(key);
|
|
140
|
+
if (result === void 0) return;
|
|
141
|
+
clearTimeout(result.timer);
|
|
142
|
+
openResults.delete(key);
|
|
143
|
+
}
|
|
144
|
+
const delivery = createDelivery(receiver, options, (frame, disposition) => {
|
|
145
|
+
if (disposition === "stale" || frame.channel !== "result") return;
|
|
146
|
+
if (frame.kind === "complete" || frame.kind === "error") {
|
|
147
|
+
forgetResult(frame.identity);
|
|
148
|
+
} else if (frame.kind === "open") {
|
|
149
|
+
if (openResults.size >= maxOpenResults)
|
|
150
|
+
throw new StreamedReceiverError(
|
|
151
|
+
"overflow",
|
|
152
|
+
"Streamed renderer exceeded its open result budget."
|
|
153
|
+
);
|
|
154
|
+
const timer = setTimeout(() => {
|
|
155
|
+
forgetResult(frame.identity);
|
|
156
|
+
try {
|
|
157
|
+
receiver.failSelection(
|
|
158
|
+
frame.identity,
|
|
159
|
+
new StreamedReceiverError("timeout", "Streamed renderer result timed out.")
|
|
160
|
+
);
|
|
161
|
+
} catch {
|
|
162
|
+
}
|
|
163
|
+
}, resultTimeoutMs);
|
|
164
|
+
openResults.set(streamFrameIdentityKey(frame.identity), { identity: frame.identity, timer });
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
let removed = false;
|
|
168
|
+
const entrypoint = Object.freeze({
|
|
169
|
+
receive(frame) {
|
|
170
|
+
if (removed) return;
|
|
171
|
+
if (!isStreamedRendererFrame(frame)) {
|
|
172
|
+
void receiver.receive(frame).catch(() => {
|
|
173
|
+
});
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
void delivery.enqueue(frame, new TextEncoder().encode(JSON.stringify(frame)).byteLength).catch(() => forgetResult(frame.identity));
|
|
178
|
+
} catch {
|
|
179
|
+
forgetResult(frame.identity);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
Object.defineProperty(target, STREAMED_RENDERER_RECEIVER, {
|
|
184
|
+
value: entrypoint,
|
|
185
|
+
configurable: true
|
|
186
|
+
});
|
|
187
|
+
if (early !== void 0) {
|
|
188
|
+
for (const frame of early.frames) entrypoint.receive(frame);
|
|
189
|
+
early.frames.length = 0;
|
|
190
|
+
}
|
|
191
|
+
return () => {
|
|
192
|
+
removed = true;
|
|
193
|
+
const error = new StreamedReceiverError(
|
|
194
|
+
"terminal",
|
|
195
|
+
"Streamed renderer entrypoint was removed."
|
|
196
|
+
);
|
|
197
|
+
delivery.close(error);
|
|
198
|
+
for (const { identity, timer } of openResults.values()) {
|
|
199
|
+
clearTimeout(timer);
|
|
200
|
+
try {
|
|
201
|
+
receiver.failSelection(identity, error);
|
|
202
|
+
} catch {
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
openResults.clear();
|
|
206
|
+
if (target[STREAMED_RENDERER_RECEIVER] !== entrypoint) return;
|
|
207
|
+
delete target[STREAMED_RENDERER_RECEIVER];
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
async function readStreamedRendererResponse(response, receiver, options = {}) {
|
|
211
|
+
if (response.body === null) throw new Error("The streamed renderer response has no body.");
|
|
212
|
+
const maxFrameBytes = positiveLimit(options.maxFrameBytes, DEFAULT_MAX_FRAME_BYTES);
|
|
213
|
+
const maxTotalBytes = positiveLimit(options.maxTotalBytes, DEFAULT_MAX_TOTAL_BYTES);
|
|
214
|
+
const timeoutMs = positiveLimit(options.timeoutMs, DEFAULT_TIMEOUT_MS);
|
|
215
|
+
const reader = response.body.getReader();
|
|
216
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
217
|
+
const openResults = /* @__PURE__ */ new Map();
|
|
218
|
+
const delivery = createDelivery(receiver, options, (frame, disposition) => {
|
|
219
|
+
if (disposition === "stale" || frame.channel !== "result") return;
|
|
220
|
+
const key = streamFrameIdentityKey(frame.identity);
|
|
221
|
+
if (frame.kind === "open") openResults.set(key, frame.identity);
|
|
222
|
+
else if (frame.kind === "complete" || frame.kind === "error") openResults.delete(key);
|
|
223
|
+
});
|
|
224
|
+
let frameParts = [];
|
|
225
|
+
let frameBytes = 0;
|
|
226
|
+
let totalBytes = 0;
|
|
227
|
+
let finished = false;
|
|
228
|
+
let failure;
|
|
229
|
+
function failOpenResults(error) {
|
|
230
|
+
let failed = false;
|
|
231
|
+
for (const identity of openResults.values())
|
|
232
|
+
failed = receiver.failSelection(identity, error) || failed;
|
|
233
|
+
openResults.clear();
|
|
234
|
+
return failed;
|
|
235
|
+
}
|
|
236
|
+
const acceptLine = async () => {
|
|
237
|
+
if (frameBytes === 0) return;
|
|
238
|
+
const bytes = frameParts.length === 1 ? frameParts[0] : (() => {
|
|
239
|
+
const joined = new Uint8Array(frameBytes);
|
|
240
|
+
let offset = 0;
|
|
241
|
+
for (const part of frameParts) {
|
|
242
|
+
joined.set(part, offset);
|
|
243
|
+
offset += part.byteLength;
|
|
244
|
+
}
|
|
245
|
+
return joined;
|
|
246
|
+
})();
|
|
247
|
+
const frame = JSON.parse(decoder.decode(bytes));
|
|
248
|
+
if (!isStreamedRendererFrame(frame))
|
|
249
|
+
throw new StreamedReceiverError("protocol", "Malformed streamed renderer frame.");
|
|
250
|
+
await delivery.room(frameBytes);
|
|
251
|
+
void delivery.enqueue(frame, frameBytes).catch((error) => {
|
|
252
|
+
failure ??= error;
|
|
253
|
+
});
|
|
254
|
+
};
|
|
255
|
+
const abort = () => {
|
|
256
|
+
failure ??= new StreamedReceiverError("terminal", "Streamed renderer response was canceled.");
|
|
257
|
+
delivery.close(failure);
|
|
258
|
+
failOpenResults(failure);
|
|
259
|
+
void reader.cancel(options.signal?.reason).catch(() => {
|
|
260
|
+
});
|
|
261
|
+
};
|
|
262
|
+
const timer = setTimeout(() => {
|
|
263
|
+
failure ??= new StreamedReceiverError("timeout", "Streamed renderer response timed out.");
|
|
264
|
+
abort();
|
|
265
|
+
}, timeoutMs);
|
|
266
|
+
options.signal?.addEventListener("abort", abort, { once: true });
|
|
267
|
+
if (options.signal?.aborted) abort();
|
|
268
|
+
try {
|
|
269
|
+
for (; ; ) {
|
|
270
|
+
if (failure !== void 0 && options.signal?.aborted) throw failure;
|
|
271
|
+
options.signal?.throwIfAborted();
|
|
272
|
+
const next = await reader.read();
|
|
273
|
+
if (next.done) break;
|
|
274
|
+
totalBytes += next.value.byteLength;
|
|
275
|
+
if (totalBytes > maxTotalBytes) {
|
|
276
|
+
throw new Error("The streamed renderer response exceeded its total byte budget.");
|
|
277
|
+
}
|
|
278
|
+
let start = 0;
|
|
279
|
+
for (let index = 0; index < next.value.byteLength; index++) {
|
|
280
|
+
if (next.value[index] !== 10) continue;
|
|
281
|
+
const part = next.value.subarray(start, index);
|
|
282
|
+
if (part.byteLength > 0) frameParts.push(part);
|
|
283
|
+
frameBytes += part.byteLength;
|
|
284
|
+
if (frameBytes > maxFrameBytes) {
|
|
285
|
+
throw new Error("A streamed renderer frame exceeded its byte budget.");
|
|
286
|
+
}
|
|
287
|
+
await acceptLine();
|
|
288
|
+
frameParts = [];
|
|
289
|
+
frameBytes = 0;
|
|
290
|
+
start = index + 1;
|
|
291
|
+
}
|
|
292
|
+
const tail = next.value.subarray(start);
|
|
293
|
+
if (tail.byteLength > 0) frameParts.push(tail);
|
|
294
|
+
frameBytes += tail.byteLength;
|
|
295
|
+
if (frameBytes > maxFrameBytes) {
|
|
296
|
+
throw new Error("A streamed renderer frame exceeded its byte budget.");
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
if (frameBytes !== 0) throw new Error("The streamed renderer response ended mid-frame.");
|
|
300
|
+
await delivery.drain();
|
|
301
|
+
const terminalError = new StreamedReceiverError(
|
|
302
|
+
"terminal",
|
|
303
|
+
"The streamed renderer response ended before a result terminal."
|
|
304
|
+
);
|
|
305
|
+
if (failOpenResults(terminalError)) failure ??= terminalError;
|
|
306
|
+
if (failure !== void 0) throw failure;
|
|
307
|
+
finished = true;
|
|
308
|
+
} catch (cause) {
|
|
309
|
+
const error = cause instanceof StreamedReceiverError ? cause : new StreamedReceiverError(
|
|
310
|
+
"protocol",
|
|
311
|
+
cause instanceof Error ? cause.message : "Streamed renderer response failed."
|
|
312
|
+
);
|
|
313
|
+
delivery.close(error);
|
|
314
|
+
failOpenResults(error);
|
|
315
|
+
throw error;
|
|
316
|
+
} finally {
|
|
317
|
+
clearTimeout(timer);
|
|
318
|
+
options.signal?.removeEventListener("abort", abort);
|
|
319
|
+
if (!finished) {
|
|
320
|
+
try {
|
|
321
|
+
void reader.cancel().catch(() => {
|
|
322
|
+
});
|
|
323
|
+
} catch {
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
reader.releaseLock();
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
export {
|
|
330
|
+
STREAMED_RENDERER_RECEIVER,
|
|
331
|
+
installStreamedRendererGlobal,
|
|
332
|
+
readStreamedRendererResponse
|
|
333
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type StreamedRegionPlacementFrame, type StreamFrameIdentity } from '../streamed-signals-protocol.js';
|
|
2
|
+
import type { ScopeSeed } from '../signals/types.js';
|
|
3
|
+
import { type StreamedResultReceiver, type StreamedResultReceiverOptions as StreamedRegionReceiverOptions } from './stream-result-receiver.js';
|
|
4
|
+
export { StreamedReceiverError, type StreamedFrameDisposition, type StreamedResultReceiverOptions as StreamedRegionReceiverOptions, type StreamedResultConsumer, } from './stream-result-receiver.js';
|
|
5
|
+
export interface HistoricalFrameLease {
|
|
6
|
+
release(): void;
|
|
7
|
+
}
|
|
8
|
+
export interface StreamedRegionRegistration {
|
|
9
|
+
readonly identity: StreamFrameIdentity;
|
|
10
|
+
readonly start: Comment;
|
|
11
|
+
readonly end: Comment;
|
|
12
|
+
readonly contentRevision: number;
|
|
13
|
+
/** Re-read immediately before commit; active renderer ownership forbids HTML placement. */
|
|
14
|
+
readonly isActive: () => boolean;
|
|
15
|
+
/** Resolve compiler-proven styles. Completion means every identity is ready to paint. */
|
|
16
|
+
readonly loadStyles: (styles: readonly string[]) => void | Promise<void>;
|
|
17
|
+
/** Acquire the exact historical values that produced this candidate range. */
|
|
18
|
+
readonly adoptHistoricalFrame: (frame: ScopeSeed) => HistoricalFrameLease;
|
|
19
|
+
/** Renderer-specific stable-ID delta application; generic DOM morph/append is forbidden. */
|
|
20
|
+
readonly applyDelta?: (frame: StreamedRegionPlacementFrame) => void;
|
|
21
|
+
}
|
|
22
|
+
export interface StreamedRegionReceiver extends StreamedResultReceiver {
|
|
23
|
+
registerRegion(registration: StreamedRegionRegistration): () => void;
|
|
24
|
+
}
|
|
25
|
+
/** Accept streamed results and place registered renderer-owned HTML ranges. */
|
|
26
|
+
export declare function createStreamedRegionReceiver(options: StreamedRegionReceiverOptions): StreamedRegionReceiver;
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { HYDRATE_INPUT_ATTR } from "../hydration-markers.js";
|
|
2
|
+
import { rendererRangeClose } from "../stream-protocol.js";
|
|
3
|
+
import {
|
|
4
|
+
sameStreamFrameIdentity
|
|
5
|
+
} from "../streamed-signals-protocol.js";
|
|
6
|
+
import { snapshotHydrationControl } from "./control-capture.js";
|
|
7
|
+
import {
|
|
8
|
+
createStreamedResultReceiverState,
|
|
9
|
+
StreamedReceiverError
|
|
10
|
+
} from "./stream-result-receiver.js";
|
|
11
|
+
import {
|
|
12
|
+
StreamedReceiverError as StreamedReceiverError2
|
|
13
|
+
} from "./stream-result-receiver.js";
|
|
14
|
+
function rangeNodes(start, end) {
|
|
15
|
+
if (start.parentNode === null || start.parentNode !== end.parentNode) {
|
|
16
|
+
throw new StreamedReceiverError("placement", "The streamed region range is detached.");
|
|
17
|
+
}
|
|
18
|
+
const nodes = [];
|
|
19
|
+
for (let node = start.nextSibling; node !== null && node !== end; node = node.nextSibling) {
|
|
20
|
+
nodes.push(node);
|
|
21
|
+
}
|
|
22
|
+
if (end.previousSibling !== start && nodes.length === 0) {
|
|
23
|
+
throw new StreamedReceiverError("placement", "The streamed region range is malformed.");
|
|
24
|
+
}
|
|
25
|
+
return nodes;
|
|
26
|
+
}
|
|
27
|
+
function keyedControls(nodes) {
|
|
28
|
+
const controls = /* @__PURE__ */ new Map();
|
|
29
|
+
const visit = (element) => {
|
|
30
|
+
const key = element.getAttribute(HYDRATE_INPUT_ATTR);
|
|
31
|
+
if (key !== null) {
|
|
32
|
+
if (controls.has(key)) {
|
|
33
|
+
throw new StreamedReceiverError("placement", `Duplicate streamed control key "${key}".`);
|
|
34
|
+
}
|
|
35
|
+
controls.set(key, element);
|
|
36
|
+
}
|
|
37
|
+
for (const child of element.children) visit(child);
|
|
38
|
+
};
|
|
39
|
+
for (const node of nodes) if (node.nodeType === 1) visit(node);
|
|
40
|
+
return controls;
|
|
41
|
+
}
|
|
42
|
+
function compatibleControl(current, incoming) {
|
|
43
|
+
return current.localName === incoming.localName && current.namespaceURI === incoming.namespaceURI && (current.localName !== "input" || current.type === incoming.type);
|
|
44
|
+
}
|
|
45
|
+
function restoreControl(control) {
|
|
46
|
+
const { element, snapshot } = control;
|
|
47
|
+
if (!snapshot.focused || !element.isConnected) return;
|
|
48
|
+
try {
|
|
49
|
+
element.focus({ preventScroll: true });
|
|
50
|
+
} catch {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (snapshot.composing || snapshot.selectionStart === null || snapshot.selectionEnd === null)
|
|
54
|
+
return;
|
|
55
|
+
try {
|
|
56
|
+
element.setSelectionRange(
|
|
57
|
+
snapshot.selectionStart,
|
|
58
|
+
snapshot.selectionEnd,
|
|
59
|
+
snapshot.selectionDirection ?? void 0
|
|
60
|
+
);
|
|
61
|
+
} catch {
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function placeFullRegion(registration, html) {
|
|
65
|
+
const { start, end } = registration;
|
|
66
|
+
const oldNodes = rangeNodes(start, end);
|
|
67
|
+
const oldControls = keyedControls(oldNodes);
|
|
68
|
+
const snapshots = /* @__PURE__ */ new Map();
|
|
69
|
+
for (const [key, element] of oldControls) {
|
|
70
|
+
const snapshot = snapshotHydrationControl(element);
|
|
71
|
+
if (snapshot !== null) snapshots.set(key, { element, snapshot });
|
|
72
|
+
}
|
|
73
|
+
const template = start.ownerDocument.createElement("template");
|
|
74
|
+
template.innerHTML = html;
|
|
75
|
+
const incomingOpen = template.content.firstChild;
|
|
76
|
+
const incomingClose = rendererRangeClose(incomingOpen);
|
|
77
|
+
if (incomingOpen === null || incomingClose === null || incomingClose !== template.content.lastChild) {
|
|
78
|
+
throw new StreamedReceiverError(
|
|
79
|
+
"placement",
|
|
80
|
+
"A full streamed region must be one balanced range."
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
const incomingNodes = [];
|
|
84
|
+
for (let node = incomingOpen.nextSibling; node !== incomingClose; node = node.nextSibling) {
|
|
85
|
+
incomingNodes.push(node);
|
|
86
|
+
}
|
|
87
|
+
const incomingControls = keyedControls(incomingNodes);
|
|
88
|
+
for (const [key, preserved] of snapshots) {
|
|
89
|
+
const replacement = incomingControls.get(key);
|
|
90
|
+
if ((preserved.snapshot.editRevision > 0 || preserved.snapshot.focused || preserved.snapshot.composing) && (replacement === void 0 || !compatibleControl(preserved.element, replacement))) {
|
|
91
|
+
throw new StreamedReceiverError(
|
|
92
|
+
"placement",
|
|
93
|
+
`Streamed HTML cannot preserve live control "${key}".`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const parent = end.parentNode;
|
|
98
|
+
for (const node of incomingNodes) parent.insertBefore(node, end);
|
|
99
|
+
const preservedElements = /* @__PURE__ */ new Set();
|
|
100
|
+
for (const [key, replacement] of incomingControls) {
|
|
101
|
+
const current = oldControls.get(key);
|
|
102
|
+
if (current === void 0 || !compatibleControl(current, replacement)) continue;
|
|
103
|
+
replacement.parentNode.replaceChild(current, replacement);
|
|
104
|
+
preservedElements.add(current);
|
|
105
|
+
}
|
|
106
|
+
for (const node of oldNodes) {
|
|
107
|
+
if (node.nodeType === 1 && preservedElements.has(node)) continue;
|
|
108
|
+
node.parentNode?.removeChild(node);
|
|
109
|
+
}
|
|
110
|
+
for (const preserved of snapshots.values()) restoreControl(preserved);
|
|
111
|
+
}
|
|
112
|
+
async function commitPlacement(state, frame, isCurrent) {
|
|
113
|
+
const registration = state.region;
|
|
114
|
+
if (!isCurrent(state, frame.identity) || registration === void 0 || registration.isActive() || !sameStreamFrameIdentity(state.identity, frame.identity) || frame.contentRevision <= state.contentRevision || frame.mode === "delta" && frame.baseRevision !== state.contentRevision) {
|
|
115
|
+
return "stale";
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
await registration.loadStyles(frame.styles);
|
|
119
|
+
} catch {
|
|
120
|
+
throw new StreamedReceiverError("styles", "Required streamed region styles are unavailable.");
|
|
121
|
+
}
|
|
122
|
+
if (!isCurrent(state, frame.identity) || state.region !== registration || registration.isActive() || !sameStreamFrameIdentity(state.identity, frame.identity) || frame.contentRevision <= state.contentRevision || frame.mode === "delta" && frame.baseRevision !== state.contentRevision) {
|
|
123
|
+
return "stale";
|
|
124
|
+
}
|
|
125
|
+
for (const element of keyedControls(rangeNodes(registration.start, registration.end)).values()) {
|
|
126
|
+
if (!snapshotHydrationControl(element)?.composing) continue;
|
|
127
|
+
state.pendingPlacement?.cancel();
|
|
128
|
+
return new Promise((resolve, reject) => {
|
|
129
|
+
const cleanup = () => {
|
|
130
|
+
element.removeEventListener("compositionend", resume);
|
|
131
|
+
if (state.pendingPlacement === pending) state.pendingPlacement = void 0;
|
|
132
|
+
};
|
|
133
|
+
const pending = {
|
|
134
|
+
cancel() {
|
|
135
|
+
cleanup();
|
|
136
|
+
resolve("stale");
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
const resume = () => {
|
|
140
|
+
cleanup();
|
|
141
|
+
void commitPlacement(state, frame, isCurrent).then(resolve, reject);
|
|
142
|
+
};
|
|
143
|
+
state.pendingPlacement = pending;
|
|
144
|
+
element.addEventListener("compositionend", resume, { once: true });
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
let lease;
|
|
148
|
+
try {
|
|
149
|
+
lease = registration.adoptHistoricalFrame(frame.historicalFrame);
|
|
150
|
+
} catch {
|
|
151
|
+
throw new StreamedReceiverError("historical-frame", "Historical read-frame adoption failed.");
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
if (frame.mode === "delta") {
|
|
155
|
+
if (registration.applyDelta === void 0) {
|
|
156
|
+
throw new StreamedReceiverError(
|
|
157
|
+
"placement",
|
|
158
|
+
"This region does not support renderer deltas."
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
registration.applyDelta(frame);
|
|
162
|
+
} else {
|
|
163
|
+
placeFullRegion(registration, frame.html);
|
|
164
|
+
}
|
|
165
|
+
} catch (error) {
|
|
166
|
+
lease.release();
|
|
167
|
+
throw error;
|
|
168
|
+
}
|
|
169
|
+
state.historical?.release();
|
|
170
|
+
state.historical = lease;
|
|
171
|
+
state.contentRevision = frame.contentRevision;
|
|
172
|
+
return "accepted";
|
|
173
|
+
}
|
|
174
|
+
function createStreamedRegionReceiver(options) {
|
|
175
|
+
const { receiver, current } = createStreamedResultReceiverState(options, commitPlacement);
|
|
176
|
+
const registerRegion = (registration) => {
|
|
177
|
+
const state = current(registration.identity);
|
|
178
|
+
if (state === void 0) {
|
|
179
|
+
throw new StreamedReceiverError("identity", "Register the exact current selection first.");
|
|
180
|
+
}
|
|
181
|
+
if (registration.start.parentNode !== registration.end.parentNode) {
|
|
182
|
+
throw new StreamedReceiverError(
|
|
183
|
+
"placement",
|
|
184
|
+
"The streamed region markers do not share a parent."
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
if (!Number.isSafeInteger(registration.contentRevision) || registration.contentRevision < 0) {
|
|
188
|
+
throw new StreamedReceiverError(
|
|
189
|
+
"identity",
|
|
190
|
+
"Streamed region content revisions must be nonnegative safe integers."
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
state.region = registration;
|
|
194
|
+
state.contentRevision = registration.contentRevision;
|
|
195
|
+
return () => {
|
|
196
|
+
if (state.region === registration) state.region = void 0;
|
|
197
|
+
};
|
|
198
|
+
};
|
|
199
|
+
return { ...receiver, registerRegion };
|
|
200
|
+
}
|
|
201
|
+
export {
|
|
202
|
+
StreamedReceiverError2 as StreamedReceiverError,
|
|
203
|
+
createStreamedRegionReceiver
|
|
204
|
+
};
|