@dom-expressions/runtime 0.50.0-next.19 → 0.50.0-next.21
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 +21 -0
- package/package.json +6 -6
- package/src/serializer.d.ts +96 -2
- package/src/serializer.js +122 -28
- package/src/server.d.ts +4 -3
- package/src/server.js +15 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# dom-expressions
|
|
2
2
|
|
|
3
|
+
## 0.50.0-next.21
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 8e54049: Expose reusable Seroval serializer primitives from the runtime's serializer module. `createSerializer(options)` builds a streaming serializer preconfigured with the default web plugin set (custom plugins compose ahead of the defaults and can shadow them) and the ~ES2017 feature policy, targeting a caller-provided `globalIdentifier`. The SSR-specific configuration (pinned `_$HY.r` global) moves to `createHydrationSerializer(options)`, which `renderToString`/`renderToStream` now use internally — hydration output is unchanged. `DEFAULT_WEB_PLUGINS` and `resolveSerializerPlugins(customPlugins)` are also exported, the module's type declarations are corrected to match the implementation (they previously declared a nonexistent default export), and the render options' `plugins` typing is tightened from `any[]` to `SerializerPlugin[]`.
|
|
8
|
+
|
|
9
|
+
The module also gains an isomorphic JSON codec for RPC-style transports (e.g. server functions): `serializeJSON(value, { onParse, onDone, onError, ... })` streams a value as `SerovalNode` chunks (async values continue streaming as they resolve), and `createJSONDeserializer(options)` returns its decoding counterpart with cross-chunk reference state. Both share the web plugin resolution, and default to a transport-hardened policy (RegExp disabled, parse depth capped at 64) that can be overridden per peer. Wire framing of chunks is intentionally left to the transport. This is groundwork for sharing one serialization configuration between SSR hydration and server function transports.
|
|
10
|
+
|
|
11
|
+
- e717d06: Fix streamed fragment swaps leaving fallback content behind when the fallback contains unrelated comment markers.
|
|
12
|
+
- 2c4ab6b: Raise the seroval / seroval-plugins peer dependency floor from `~1.5.0` to `~1.5.4`: seroval 1.5.3 and earlier are affected by a security issue fixed in 1.5.4.
|
|
13
|
+
|
|
14
|
+
## 0.50.0-next.20
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- 9d5a90b: Queue streamed fragment activations whose `pl-*` marker is not yet in the live DOM instead of silently dropping them.
|
|
19
|
+
|
|
20
|
+
A fragment's marker can sit inside a flushed-but-unactivated ancestor `<template>` (a slot held by a reveal group). Template content is inert, so `document.getElementById` cannot see the marker and `$df`/`$dfl` previously returned `0` and lost the swap permanently — the fallback stayed stuck even though the content template had streamed. Today this window is masked because the server enrolls nested boundaries into the ancestor reveal group, but fixing that enrollment (solidjs/solid#2871, solidjs/solid#2872) makes nested boundaries activate independently, exposing the drop.
|
|
21
|
+
|
|
22
|
+
`$df` and `$dfl` now queue marker misses (`_$HY.dq` / `_$HY.dlq`) and a new `$dfd` drains both queues after every successful swap or fallback materialization — the only events that can bring queued markers into the live document. Content swaps drain before fallbacks so a settled fragment wins over its own pending fallback, and drains cascade through arbitrarily nested held levels. An activation whose content template is already consumed remains a plain no-op and is never queued.
|
|
23
|
+
|
|
3
24
|
## 0.50.0-next.19
|
|
4
25
|
|
|
5
26
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dom-expressions/runtime",
|
|
3
3
|
"description": "A Fine-Grained Runtime for Performant DOM Rendering",
|
|
4
|
-
"version": "0.50.0-next.
|
|
4
|
+
"version": "0.50.0-next.21",
|
|
5
5
|
"author": "Ryan Carniato",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
},
|
|
20
20
|
"peerDependencies": {
|
|
21
21
|
"csstype": "^3.0",
|
|
22
|
-
"seroval": "~1.5.
|
|
23
|
-
"seroval-plugins": "~1.5.
|
|
22
|
+
"seroval": "~1.5.4",
|
|
23
|
+
"seroval-plugins": "~1.5.4"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"csstype": "^3.1",
|
|
27
|
-
"seroval": "~1.5.
|
|
28
|
-
"seroval-plugins": "~1.5.
|
|
29
|
-
"@dom-expressions/babel-plugin-jsx": "0.50.0-next.
|
|
27
|
+
"seroval": "~1.5.5",
|
|
28
|
+
"seroval-plugins": "~1.5.5",
|
|
29
|
+
"@dom-expressions/babel-plugin-jsx": "0.50.0-next.21"
|
|
30
30
|
},
|
|
31
31
|
"scripts": {
|
|
32
32
|
"test": "jest --no-cache",
|
package/src/serializer.d.ts
CHANGED
|
@@ -1,3 +1,97 @@
|
|
|
1
|
-
import { Serializer } from "seroval";
|
|
1
|
+
import { Plugin, Serializer, SerovalNode } from "seroval";
|
|
2
2
|
|
|
3
|
-
export
|
|
3
|
+
export type { SerovalNode };
|
|
4
|
+
|
|
5
|
+
/** A Seroval plugin usable with the web serializers. */
|
|
6
|
+
export type SerializerPlugin = Plugin<any, any>;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Baseline plugin set for serializing web-platform values (AbortSignal,
|
|
10
|
+
* Event, FormData, Headers, ReadableStream, Request, Response, URL, ...).
|
|
11
|
+
*/
|
|
12
|
+
export const DEFAULT_WEB_PLUGINS: readonly SerializerPlugin[];
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Composes custom plugins with `DEFAULT_WEB_PLUGINS`. Custom plugins come
|
|
16
|
+
* first so they can shadow a default for values both would match. Returns a
|
|
17
|
+
* fresh array; the defaults are never mutated.
|
|
18
|
+
*/
|
|
19
|
+
export function resolveSerializerPlugins(customPlugins?: SerializerPlugin[]): SerializerPlugin[];
|
|
20
|
+
|
|
21
|
+
export interface WebSerializerOptions {
|
|
22
|
+
/** Name of the global object the emitted scripts write resolved values into. */
|
|
23
|
+
globalIdentifier: string;
|
|
24
|
+
scopeId?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Seroval feature bitflags to exclude from output. Defaults to disabling
|
|
27
|
+
* post-ES2017 features (AggregateError, BigInt typed arrays).
|
|
28
|
+
*/
|
|
29
|
+
disabledFeatures?: number;
|
|
30
|
+
/** Extra plugins, composed ahead of `DEFAULT_WEB_PLUGINS`. */
|
|
31
|
+
plugins?: SerializerPlugin[];
|
|
32
|
+
onData: (result: string) => void;
|
|
33
|
+
onError?: (error: unknown) => void;
|
|
34
|
+
onDone?: () => void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Creates a streaming Seroval serializer preconfigured with the web plugin
|
|
39
|
+
* set and the default feature policy.
|
|
40
|
+
*/
|
|
41
|
+
export function createSerializer(options: WebSerializerOptions): Serializer;
|
|
42
|
+
|
|
43
|
+
export type HydrationSerializerOptions = Omit<
|
|
44
|
+
WebSerializerOptions,
|
|
45
|
+
"globalIdentifier" | "disabledFeatures"
|
|
46
|
+
>;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Serializer for SSR hydration output. Pins the hydration global (`_$HY.r`)
|
|
50
|
+
* and feature policy — only the wiring options (callbacks, scope, extra
|
|
51
|
+
* plugins) are configurable.
|
|
52
|
+
*/
|
|
53
|
+
export function createHydrationSerializer(options: HydrationSerializerOptions): Serializer;
|
|
54
|
+
|
|
55
|
+
/** Returns the cross-reference bootstrap script for a render scope. */
|
|
56
|
+
export function getLocalHeaderScript(id?: string): string;
|
|
57
|
+
|
|
58
|
+
// ---- JSON codec (server function transports) ----
|
|
59
|
+
|
|
60
|
+
export interface JSONCodecOptions {
|
|
61
|
+
/** Extra plugins, composed ahead of `DEFAULT_WEB_PLUGINS`. Must match on both peers. */
|
|
62
|
+
plugins?: SerializerPlugin[];
|
|
63
|
+
/**
|
|
64
|
+
* Seroval feature bitflags to exclude. Defaults to disabling `RegExp`
|
|
65
|
+
* (payloads may come from an untrusted peer). Must match on both peers.
|
|
66
|
+
*/
|
|
67
|
+
disabledFeatures?: number;
|
|
68
|
+
/** Maximum parse/deserialize depth. Defaults to 64. Must match on both peers. */
|
|
69
|
+
depthLimit?: number;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface JSONSerializeOptions extends JSONCodecOptions {
|
|
73
|
+
/**
|
|
74
|
+
* Receives each serialized node; `initial` is true for the first chunk
|
|
75
|
+
* (the source value itself). Async values produce additional chunks as
|
|
76
|
+
* they resolve.
|
|
77
|
+
*/
|
|
78
|
+
onParse: (node: SerovalNode, initial: boolean) => void;
|
|
79
|
+
onError?: (error: unknown) => void;
|
|
80
|
+
/** Fires once all async values have settled. */
|
|
81
|
+
onDone?: () => void;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Serializes `value` as SerovalNode chunks delivered through `onParse`.
|
|
86
|
+
* Wire framing of the nodes is the transport's concern. Returns a cancel
|
|
87
|
+
* function that aborts pending async serialization.
|
|
88
|
+
*/
|
|
89
|
+
export function serializeJSON(value: unknown, options: JSONSerializeOptions): () => void;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Creates the decoding counterpart of `serializeJSON`. Cross-references
|
|
93
|
+
* between chunks resolve through state shared across calls, so all chunks
|
|
94
|
+
* from one stream must go through the same deserializer instance. The first
|
|
95
|
+
* chunk's return value is the decoded source value.
|
|
96
|
+
*/
|
|
97
|
+
export function createJSONDeserializer(options?: JSONCodecOptions): <T>(node: SerovalNode) => T;
|
package/src/serializer.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Feature,
|
|
3
|
+
Serializer,
|
|
4
|
+
fromCrossJSON,
|
|
5
|
+
getCrossReferenceHeader,
|
|
6
|
+
toCrossJSONStream
|
|
7
|
+
} from "seroval";
|
|
2
8
|
import {
|
|
3
9
|
AbortSignalPlugin,
|
|
4
10
|
CustomEventPlugin,
|
|
@@ -13,36 +19,69 @@ import {
|
|
|
13
19
|
URLSearchParamsPlugin
|
|
14
20
|
} from "seroval-plugins/web";
|
|
15
21
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
22
|
+
// Features excluded from emitted scripts so output stays runnable on ~ES2017
|
|
23
|
+
// targets (AggregateError is ES2021, BigInt typed arrays are ES2020).
|
|
24
|
+
const DEFAULT_DISABLED_FEATURES = Feature.AggregateError | Feature.BigIntTypedArray;
|
|
25
|
+
|
|
26
|
+
// Part of the hydration wire protocol since the streaming serializer landed
|
|
27
|
+
// (#275): the bootstrap from `generateHydrationScript` creates it and the
|
|
28
|
+
// client runtime reads resolved values out of it. Terse on purpose — it ships
|
|
29
|
+
// in every SSR payload.
|
|
30
|
+
const HYDRATION_GLOBAL = "_$HY.r";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Baseline plugin set for serializing web-platform values. Shared by the
|
|
34
|
+
* hydration serializer and any consumer building its own serializer (e.g.
|
|
35
|
+
* server function transports).
|
|
36
|
+
*/
|
|
37
|
+
export const DEFAULT_WEB_PLUGINS = Object.freeze([
|
|
38
|
+
AbortSignalPlugin,
|
|
39
|
+
// BlobPlugin,
|
|
40
|
+
CustomEventPlugin,
|
|
41
|
+
DOMExceptionPlugin,
|
|
42
|
+
EventPlugin,
|
|
43
|
+
// FilePlugin,
|
|
44
|
+
FormDataPlugin,
|
|
45
|
+
HeadersPlugin,
|
|
46
|
+
ReadableStreamPlugin,
|
|
47
|
+
RequestPlugin,
|
|
48
|
+
ResponsePlugin,
|
|
49
|
+
URLSearchParamsPlugin,
|
|
50
|
+
URLPlugin
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Composes user plugins with the default web set. Custom plugins come first
|
|
55
|
+
* so they can shadow a default for values both would match.
|
|
56
|
+
*/
|
|
57
|
+
export function resolveSerializerPlugins(customPlugins) {
|
|
58
|
+
return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
|
|
59
|
+
}
|
|
40
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Creates a streaming Seroval serializer preconfigured with the web plugin
|
|
63
|
+
* set and the default feature policy. `globalIdentifier` is required and
|
|
64
|
+
* names the object the emitted scripts write resolved values into.
|
|
65
|
+
*/
|
|
66
|
+
export function createSerializer(options) {
|
|
41
67
|
return new Serializer({
|
|
68
|
+
...options,
|
|
69
|
+
plugins: resolveSerializerPlugins(options.plugins),
|
|
70
|
+
disabledFeatures:
|
|
71
|
+
options.disabledFeatures === undefined ? DEFAULT_DISABLED_FEATURES : options.disabledFeatures
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Serializer for SSR hydration output. Pins the hydration global (`_$HY.r`)
|
|
77
|
+
* and feature policy — only the wiring options (callbacks, scope, extra
|
|
78
|
+
* plugins) are configurable.
|
|
79
|
+
*/
|
|
80
|
+
export function createHydrationSerializer({ onData, onDone, scopeId, onError, plugins }) {
|
|
81
|
+
return createSerializer({
|
|
42
82
|
scopeId,
|
|
43
|
-
plugins
|
|
44
|
-
globalIdentifier:
|
|
45
|
-
disabledFeatures: ES2017FLAG,
|
|
83
|
+
plugins,
|
|
84
|
+
globalIdentifier: HYDRATION_GLOBAL,
|
|
46
85
|
onData,
|
|
47
86
|
onDone,
|
|
48
87
|
onError
|
|
@@ -52,3 +91,58 @@ export function createSerializer({ onData, onDone, scopeId, onError, plugins: cu
|
|
|
52
91
|
export function getLocalHeaderScript(id) {
|
|
53
92
|
return getCrossReferenceHeader(id) + ";";
|
|
54
93
|
}
|
|
94
|
+
|
|
95
|
+
// ---- JSON codec (server function transports) ----
|
|
96
|
+
//
|
|
97
|
+
// Unlike hydration output (executable JS targeting a global), the JSON codec
|
|
98
|
+
// streams SerovalNode values that a peer decodes without eval. Framing the
|
|
99
|
+
// nodes on the wire (chunk delimiting, HTTP plumbing) is the transport's
|
|
100
|
+
// concern; this layer only guarantees both sides agree on plugins and
|
|
101
|
+
// feature policy.
|
|
102
|
+
|
|
103
|
+
// Codec payloads may come from an untrusted peer, so the defaults protect
|
|
104
|
+
// the decoding side: RegExp is disabled (ReDoS via deserialized patterns)
|
|
105
|
+
// and parse depth is capped well below Seroval's own limit.
|
|
106
|
+
const JSON_CODEC_DISABLED_FEATURES = Feature.RegExp;
|
|
107
|
+
const JSON_CODEC_DEPTH_LIMIT = 64;
|
|
108
|
+
|
|
109
|
+
// Single source of truth for codec defaults — encode and decode must agree
|
|
110
|
+
// on plugins and feature policy or payloads won't roundtrip.
|
|
111
|
+
function resolveCodecOptions({ plugins, disabledFeatures, depthLimit } = {}) {
|
|
112
|
+
return {
|
|
113
|
+
plugins: resolveSerializerPlugins(plugins),
|
|
114
|
+
disabledFeatures:
|
|
115
|
+
disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
|
|
116
|
+
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Serializes `value` as SerovalNode chunks delivered through
|
|
122
|
+
* `onParse(node, initial)`. Async values (promises, streams) produce
|
|
123
|
+
* additional chunks as they resolve; `onDone` fires when everything has
|
|
124
|
+
* settled. Returns a cancel function that aborts any pending async
|
|
125
|
+
* serialization.
|
|
126
|
+
*/
|
|
127
|
+
export function serializeJSON(value, { onParse, onDone, onError, ...codecOptions }) {
|
|
128
|
+
return toCrossJSONStream(value, {
|
|
129
|
+
onParse,
|
|
130
|
+
onDone,
|
|
131
|
+
onError,
|
|
132
|
+
...resolveCodecOptions(codecOptions)
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Creates the decoding counterpart of `serializeJSON`. The returned function
|
|
138
|
+
* deserializes one SerovalNode chunk at a time; cross-references between
|
|
139
|
+
* chunks resolve through a map shared across calls, so all chunks from one
|
|
140
|
+
* stream must go through the same deserializer instance.
|
|
141
|
+
*/
|
|
142
|
+
export function createJSONDeserializer(options) {
|
|
143
|
+
const refs = new Map();
|
|
144
|
+
const resolved = resolveCodecOptions(options);
|
|
145
|
+
return function deserializeJSONChunk(node) {
|
|
146
|
+
return fromCrossJSON(node, { refs, ...resolved });
|
|
147
|
+
};
|
|
148
|
+
}
|
package/src/server.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { JSX } from "./jsx.js";
|
|
2
|
+
import { SerializerPlugin } from "./serializer.js";
|
|
2
3
|
export const DOMWithState: Record<string, Record<string, 1 | 2>>;
|
|
3
4
|
export const ChildProperties: Set<string>;
|
|
4
5
|
export const DelegatedEvents: Set<string>;
|
|
@@ -62,7 +63,7 @@ export function renderToString<T>(
|
|
|
62
63
|
nonce?: string;
|
|
63
64
|
renderId?: string;
|
|
64
65
|
noScripts?: boolean;
|
|
65
|
-
plugins?:
|
|
66
|
+
plugins?: SerializerPlugin[];
|
|
66
67
|
manifest?: AssetManifest | AssetResolver | AssetResolverFn;
|
|
67
68
|
onError?: (err: any) => void;
|
|
68
69
|
}
|
|
@@ -75,7 +76,7 @@ export function renderToStringAsync<T>(
|
|
|
75
76
|
nonce?: string;
|
|
76
77
|
renderId?: string;
|
|
77
78
|
noScripts?: boolean;
|
|
78
|
-
plugins?:
|
|
79
|
+
plugins?: SerializerPlugin[];
|
|
79
80
|
manifest?: AssetManifest | AssetResolver | AssetResolverFn;
|
|
80
81
|
onError?: (err: any) => void;
|
|
81
82
|
}
|
|
@@ -86,7 +87,7 @@ export function renderToStream<T>(
|
|
|
86
87
|
nonce?: string;
|
|
87
88
|
renderId?: string;
|
|
88
89
|
noScripts?: boolean;
|
|
89
|
-
plugins?:
|
|
90
|
+
plugins?: SerializerPlugin[];
|
|
90
91
|
manifest?: AssetManifest | AssetResolver | AssetResolverFn;
|
|
91
92
|
onCompleteShell?: (info: { write: (v: string) => void }) => void;
|
|
92
93
|
onCompleteAll?: (info: { write: (v: string) => void }) => void;
|
package/src/server.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ChildProperties } from "./constants";
|
|
2
2
|
import { sharedConfig, root, ssrHandleError, getOwner, runWithOwner } from "rxcore";
|
|
3
|
-
import {
|
|
3
|
+
import { createHydrationSerializer, getLocalHeaderScript } from "./serializer";
|
|
4
4
|
|
|
5
5
|
// `mergeProps` comes from the framework like the client/universal entries —
|
|
6
6
|
// prop-merge semantics (function sources, precedence) belong to the reactive
|
|
@@ -178,19 +178,29 @@ function applyAssetTracking(context, tracking, manifest) {
|
|
|
178
178
|
const VOID_ELEMENTS =
|
|
179
179
|
/^(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)$/i;
|
|
180
180
|
// Fragment replacement helpers emitted into stream task scripts:
|
|
181
|
-
// - $df(id): swap template payload into the `pl-*` marker range.
|
|
181
|
+
// - $df(id): swap template payload into the `pl-*` marker range. A marker that
|
|
182
|
+
// isn't in the live DOM yet (it sits inside a flushed-but-unactivated ancestor
|
|
183
|
+
// template held by a reveal group) queues the id in `_$HY.dq` for retry instead
|
|
184
|
+
// of dropping the swap. A missing content template means the swap already ran —
|
|
185
|
+
// that stays a plain no-op and is never queued.
|
|
182
186
|
// - $dfl(id): materialize fallback from `pl-*` template content without resolving.
|
|
187
|
+
// Marker misses queue in `_$HY.dlq`, same reasoning as $df.
|
|
183
188
|
// - $dflj(ids): materialize fallback content for every id in the list.
|
|
189
|
+
// - $dfd(): drain both retry queues. Runs after every successful swap or fallback
|
|
190
|
+
// materialization — the only events that can bring queued markers into the live
|
|
191
|
+
// document. Content swaps ($df) drain before fallbacks ($dfl) so a settled
|
|
192
|
+
// fragment wins over its own pending fallback. Each pass snapshots the queue,
|
|
193
|
+
// so still-inert entries simply re-queue and wait for the next swap.
|
|
184
194
|
// - $dfs(id, count, defer): register pending stylesheet count for fragment `id`.
|
|
185
195
|
// - $dfc(id): style completion callback; reveals when the fragment/group is unblocked.
|
|
186
196
|
// - $dfg(id): group-style gate check; reveals a waiting group once all style counts hit zero.
|
|
187
197
|
// - $dfj(ids): reveal a group in registration order, waiting if any member still has pending styles.
|
|
188
|
-
const REPLACE_SCRIPT = `function $df(e,n,o,t){if(!(n=document.getElementById(e))
|
|
198
|
+
const REPLACE_SCRIPT = `function $df(e,n,o,t){if(!(n=document.getElementById(e)))return 0;if(!(o=document.getElementById("pl-"+e)))return(_$HY.dq=_$HY.dq||{})[e]=1,0;for(;o&&(8!==o.nodeType||o.nodeValue!=="pl-"+e);)t=o.nextSibling,o.remove(),o=t;_$HY.done?o.remove():o.replaceWith(n.content),n.remove(),_$HY.fe(e),$dfd();return 1}function $dfl(e,o,n){if(!(o=document.getElementById("pl-"+e)))return(_$HY.dlq=_$HY.dlq||{})[e]=1,0;if(o._$fl)return 1;for(n=o.nextSibling;n;){if(8===n.nodeType&&n.nodeValue==="pl-"+e){o.parentNode&&o.parentNode.insertBefore(o.content.cloneNode(!0),n),o._$fl=1,$dfd();return 1}n=n.nextSibling}return 0}function $dflj(e,i){for(i=0;i<e.length;i++)$dfl(e[i])}function $dfd(e,i){if(e=_$HY.dq){_$HY.dq=0;for(i in e)$df(i)}if(e=_$HY.dlq){_$HY.dlq=0;for(i in e)$dfl(i)}}function $dfs(e,c,d){(_$HY.sc=_$HY.sc||{})[e]=c,d&&((_$HY.sd=_$HY.sd||{})[e]=1)}function $dfg(e,g,i,k){if(!(g=_$HY.sg&&_$HY.sg[e]))return;for(i=0;i<g.length;i++)if(_$HY.sc&&_$HY.sc[g[i]]>0)return;for(i=0;i<g.length;i++)k=g[i],delete _$HY.sg[k],$df(k)}function $dfc(e){if(--_$HY.sc[e]<=0){delete _$HY.sc[e],_$HY.sg&&_$HY.sg[e]?$dfg(e):!(_$HY.sd&&_$HY.sd[e])&&$df(e);_$HY.sd&&delete _$HY.sd[e]}}function $dfj(e,i,n){for(i=0;i<e.length;i++)if(_$HY.sc&&_$HY.sc[e[i]]>0){for(n=0;n<e.length;n++)(_$HY.sg=_$HY.sg||{})[e[n]]=e;return}for(i=0;i<e.length;i++)$df(e[i])}`;
|
|
189
199
|
|
|
190
200
|
export function renderToString(code, options = {}) {
|
|
191
201
|
const { renderId = "", nonce, noScripts, manifest } = options;
|
|
192
202
|
let scripts = "";
|
|
193
|
-
const serializer =
|
|
203
|
+
const serializer = createHydrationSerializer({
|
|
194
204
|
scopeId: renderId,
|
|
195
205
|
plugins: options.plugins,
|
|
196
206
|
onData(script) {
|
|
@@ -289,7 +299,7 @@ export function renderToStream(code, options = {}) {
|
|
|
289
299
|
completed = true;
|
|
290
300
|
if (firstFlushed) dispose();
|
|
291
301
|
};
|
|
292
|
-
const serializer =
|
|
302
|
+
const serializer = createHydrationSerializer({
|
|
293
303
|
scopeId: options.renderId,
|
|
294
304
|
plugins: options.plugins,
|
|
295
305
|
onData: pushTask,
|