pulse-js-framework 1.7.15 → 1.7.17
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 +43 -0
- package/cli/help.js +617 -0
- package/cli/index.js +102 -106
- package/cli/utils/file-utils.js +26 -4
- package/package.json +3 -2
- package/runtime/async.js +39 -0
- package/runtime/dom-element.js +107 -0
- package/runtime/index.js +70 -6
- package/runtime/pulse.js +40 -0
- package/runtime/ssr-async.js +229 -0
- package/runtime/ssr-hydrator.js +310 -0
- package/runtime/ssr-serializer.js +266 -0
- package/runtime/ssr.js +463 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pulse SSR Async Context - Async operation collection for SSR
|
|
3
|
+
*
|
|
4
|
+
* Collects and manages async operations during server-side rendering.
|
|
5
|
+
* Enables data prefetching before HTML generation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// SSR Async Context
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Context for collecting async operations during SSR.
|
|
14
|
+
* Tracks pending promises and caches resolved data for re-renders.
|
|
15
|
+
*/
|
|
16
|
+
export class SSRAsyncContext {
|
|
17
|
+
constructor() {
|
|
18
|
+
/** @type {Array<{key: any, promise: Promise}>} */
|
|
19
|
+
this.pending = [];
|
|
20
|
+
|
|
21
|
+
/** @type {Map<any, any>} */
|
|
22
|
+
this.resolved = new Map();
|
|
23
|
+
|
|
24
|
+
/** @type {Map<any, Error>} */
|
|
25
|
+
this.errors = new Map();
|
|
26
|
+
|
|
27
|
+
/** @type {boolean} */
|
|
28
|
+
this.collecting = true;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Register an async operation for collection.
|
|
33
|
+
* @param {any} key - Unique key for this operation (usually the async function)
|
|
34
|
+
* @param {Promise} promise - The promise to track
|
|
35
|
+
*/
|
|
36
|
+
register(key, promise) {
|
|
37
|
+
if (!this.collecting) return;
|
|
38
|
+
|
|
39
|
+
// Wrap promise to capture result
|
|
40
|
+
const tracked = promise
|
|
41
|
+
.then(data => {
|
|
42
|
+
this.resolved.set(key, data);
|
|
43
|
+
return data;
|
|
44
|
+
})
|
|
45
|
+
.catch(error => {
|
|
46
|
+
this.errors.set(key, error);
|
|
47
|
+
throw error;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
this.pending.push({ key, promise: tracked });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Check if a result is already cached.
|
|
55
|
+
* @param {any} key - Operation key
|
|
56
|
+
* @returns {boolean} True if result is cached
|
|
57
|
+
*/
|
|
58
|
+
has(key) {
|
|
59
|
+
return this.resolved.has(key);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get cached result for a key.
|
|
64
|
+
* @param {any} key - Operation key
|
|
65
|
+
* @returns {any} Cached result or undefined
|
|
66
|
+
*/
|
|
67
|
+
get(key) {
|
|
68
|
+
return this.resolved.get(key);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get error for a key (if the operation failed).
|
|
73
|
+
* @param {any} key - Operation key
|
|
74
|
+
* @returns {Error|undefined} Error or undefined
|
|
75
|
+
*/
|
|
76
|
+
getError(key) {
|
|
77
|
+
return this.errors.get(key);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Wait for all pending async operations to complete.
|
|
82
|
+
* @param {number} [timeout=5000] - Maximum wait time in ms
|
|
83
|
+
* @returns {Promise<void>}
|
|
84
|
+
* @throws {Error} If timeout is exceeded
|
|
85
|
+
*/
|
|
86
|
+
async waitAll(timeout = 5000) {
|
|
87
|
+
if (this.pending.length === 0) return;
|
|
88
|
+
|
|
89
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
90
|
+
setTimeout(() => {
|
|
91
|
+
reject(new Error(`[Pulse SSR] Async operations timed out after ${timeout}ms`));
|
|
92
|
+
}, timeout);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Wait for all promises, catching individual errors
|
|
96
|
+
const allSettled = Promise.all(
|
|
97
|
+
this.pending.map(p => p.promise.catch(() => null))
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
await Promise.race([allSettled, timeoutPromise]);
|
|
101
|
+
|
|
102
|
+
// Stop collecting after wait
|
|
103
|
+
this.collecting = false;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get the number of pending operations.
|
|
108
|
+
* @returns {number}
|
|
109
|
+
*/
|
|
110
|
+
get pendingCount() {
|
|
111
|
+
return this.pending.length;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get the number of resolved operations.
|
|
116
|
+
* @returns {number}
|
|
117
|
+
*/
|
|
118
|
+
get resolvedCount() {
|
|
119
|
+
return this.resolved.size;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get the number of failed operations.
|
|
124
|
+
* @returns {number}
|
|
125
|
+
*/
|
|
126
|
+
get errorCount() {
|
|
127
|
+
return this.errors.size;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get all resolved data as a plain object.
|
|
132
|
+
* @returns {Object} Map of key → value
|
|
133
|
+
*/
|
|
134
|
+
getAllResolved() {
|
|
135
|
+
const result = {};
|
|
136
|
+
for (const [key, value] of this.resolved) {
|
|
137
|
+
// Use string key if function, otherwise try to serialize
|
|
138
|
+
const keyStr = typeof key === 'function' ? key.name || 'anonymous' : String(key);
|
|
139
|
+
result[keyStr] = value;
|
|
140
|
+
}
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Reset the context for a new render pass.
|
|
146
|
+
*/
|
|
147
|
+
reset() {
|
|
148
|
+
this.pending = [];
|
|
149
|
+
this.resolved.clear();
|
|
150
|
+
this.errors.clear();
|
|
151
|
+
this.collecting = true;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ============================================================================
|
|
156
|
+
// Global SSR Async Context
|
|
157
|
+
// ============================================================================
|
|
158
|
+
|
|
159
|
+
/** @type {SSRAsyncContext|null} */
|
|
160
|
+
let ssrAsyncContext = null;
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Get the current SSR async context.
|
|
164
|
+
* Returns null if not in SSR mode.
|
|
165
|
+
* @returns {SSRAsyncContext|null}
|
|
166
|
+
*/
|
|
167
|
+
export function getSSRAsyncContext() {
|
|
168
|
+
return ssrAsyncContext;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Set the SSR async context.
|
|
173
|
+
* @param {SSRAsyncContext|null} ctx - Context to set, or null to clear
|
|
174
|
+
*/
|
|
175
|
+
export function setSSRAsyncContext(ctx) {
|
|
176
|
+
ssrAsyncContext = ctx;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Check if currently in SSR async collection mode.
|
|
181
|
+
* @returns {boolean}
|
|
182
|
+
*/
|
|
183
|
+
export function isCollectingAsync() {
|
|
184
|
+
return ssrAsyncContext !== null && ssrAsyncContext.collecting;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Register an async operation in the current SSR context.
|
|
189
|
+
* No-op if not in SSR mode.
|
|
190
|
+
* @param {any} key - Unique key for this operation
|
|
191
|
+
* @param {Promise} promise - The promise to track
|
|
192
|
+
*/
|
|
193
|
+
export function registerAsync(key, promise) {
|
|
194
|
+
if (ssrAsyncContext) {
|
|
195
|
+
ssrAsyncContext.register(key, promise);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Get cached async result from current SSR context.
|
|
201
|
+
* @param {any} key - Operation key
|
|
202
|
+
* @returns {any} Cached result or undefined
|
|
203
|
+
*/
|
|
204
|
+
export function getCachedAsync(key) {
|
|
205
|
+
return ssrAsyncContext?.get(key);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Check if an async result is cached in current SSR context.
|
|
210
|
+
* @param {any} key - Operation key
|
|
211
|
+
* @returns {boolean}
|
|
212
|
+
*/
|
|
213
|
+
export function hasCachedAsync(key) {
|
|
214
|
+
return ssrAsyncContext?.has(key) ?? false;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// ============================================================================
|
|
218
|
+
// Exports
|
|
219
|
+
// ============================================================================
|
|
220
|
+
|
|
221
|
+
export default {
|
|
222
|
+
SSRAsyncContext,
|
|
223
|
+
getSSRAsyncContext,
|
|
224
|
+
setSSRAsyncContext,
|
|
225
|
+
isCollectingAsync,
|
|
226
|
+
registerAsync,
|
|
227
|
+
getCachedAsync,
|
|
228
|
+
hasCachedAsync
|
|
229
|
+
};
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pulse SSR Hydrator - Client-side hydration utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities for hydrating server-rendered HTML by attaching
|
|
5
|
+
* event listeners and reactive bindings to existing DOM elements.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Hydration State
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
/** @type {boolean} */
|
|
13
|
+
let isHydrating = false;
|
|
14
|
+
|
|
15
|
+
/** @type {HydrationContext|null} */
|
|
16
|
+
let hydrationCtx = null;
|
|
17
|
+
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// Hydration Context
|
|
20
|
+
// ============================================================================
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @typedef {Object} HydrationContext
|
|
24
|
+
* @property {Element} root - Root container element
|
|
25
|
+
* @property {Node|null} cursor - Current position in DOM tree
|
|
26
|
+
* @property {Array<Function>} cleanups - Cleanup functions for disposal
|
|
27
|
+
* @property {Array<{element: Element, event: string, handler: Function}>} listeners - Attached event listeners
|
|
28
|
+
* @property {number} depth - Current nesting depth
|
|
29
|
+
* @property {boolean} mismatchWarned - Whether a mismatch warning was shown
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Create a new hydration context for a container element.
|
|
34
|
+
* @param {Element} root - Root container element
|
|
35
|
+
* @returns {HydrationContext}
|
|
36
|
+
*/
|
|
37
|
+
export function createHydrationContext(root) {
|
|
38
|
+
return {
|
|
39
|
+
root,
|
|
40
|
+
cursor: root.firstChild,
|
|
41
|
+
cleanups: [],
|
|
42
|
+
listeners: [],
|
|
43
|
+
depth: 0,
|
|
44
|
+
mismatchWarned: false
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ============================================================================
|
|
49
|
+
// Hydration Mode Control
|
|
50
|
+
// ============================================================================
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Enable or disable hydration mode.
|
|
54
|
+
* @param {boolean} enabled - Whether to enable hydration mode
|
|
55
|
+
* @param {HydrationContext|null} ctx - Hydration context (required when enabling)
|
|
56
|
+
*/
|
|
57
|
+
export function setHydrationMode(enabled, ctx = null) {
|
|
58
|
+
isHydrating = enabled;
|
|
59
|
+
hydrationCtx = enabled ? ctx : null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Check if currently in hydration mode.
|
|
64
|
+
* @returns {boolean}
|
|
65
|
+
*/
|
|
66
|
+
export function isHydratingMode() {
|
|
67
|
+
return isHydrating;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get the current hydration context.
|
|
72
|
+
* @returns {HydrationContext|null}
|
|
73
|
+
*/
|
|
74
|
+
export function getHydrationContext() {
|
|
75
|
+
return hydrationCtx;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ============================================================================
|
|
79
|
+
// DOM Cursor Navigation
|
|
80
|
+
// ============================================================================
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get the current node at the cursor position.
|
|
84
|
+
* @param {HydrationContext} ctx - Hydration context
|
|
85
|
+
* @returns {Node|null}
|
|
86
|
+
*/
|
|
87
|
+
export function getCurrentNode(ctx) {
|
|
88
|
+
return ctx.cursor;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Advance the cursor to the next sibling.
|
|
93
|
+
* @param {HydrationContext} ctx - Hydration context
|
|
94
|
+
*/
|
|
95
|
+
export function advanceCursor(ctx) {
|
|
96
|
+
if (ctx.cursor) {
|
|
97
|
+
ctx.cursor = ctx.cursor.nextSibling;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Enter a child scope (for nested elements).
|
|
103
|
+
* @param {HydrationContext} ctx - Hydration context
|
|
104
|
+
* @param {Element} element - Parent element to enter
|
|
105
|
+
*/
|
|
106
|
+
export function enterChild(ctx, element) {
|
|
107
|
+
ctx.cursor = element.firstChild;
|
|
108
|
+
ctx.depth++;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Exit a child scope and restore cursor to parent level.
|
|
113
|
+
* @param {HydrationContext} ctx - Hydration context
|
|
114
|
+
* @param {Element} element - Element we're exiting
|
|
115
|
+
*/
|
|
116
|
+
export function exitChild(ctx, element) {
|
|
117
|
+
ctx.cursor = element.nextSibling;
|
|
118
|
+
ctx.depth--;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Skip comment nodes (used as markers).
|
|
123
|
+
* @param {HydrationContext} ctx - Hydration context
|
|
124
|
+
*/
|
|
125
|
+
export function skipComments(ctx) {
|
|
126
|
+
while (ctx.cursor && ctx.cursor.nodeType === 8) {
|
|
127
|
+
ctx.cursor = ctx.cursor.nextSibling;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ============================================================================
|
|
132
|
+
// DOM Matching
|
|
133
|
+
// ============================================================================
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Check if an element matches expected tag and basic attributes.
|
|
137
|
+
* @param {Node} node - Node to check
|
|
138
|
+
* @param {string} expectedTag - Expected tag name (lowercase)
|
|
139
|
+
* @param {string} [expectedId] - Expected ID (optional)
|
|
140
|
+
* @param {string} [expectedClass] - Expected class (optional)
|
|
141
|
+
* @returns {boolean}
|
|
142
|
+
*/
|
|
143
|
+
export function matchesElement(node, expectedTag, expectedId, expectedClass) {
|
|
144
|
+
if (!node || node.nodeType !== 1) return false;
|
|
145
|
+
|
|
146
|
+
const tag = node.tagName?.toLowerCase();
|
|
147
|
+
if (tag !== expectedTag) return false;
|
|
148
|
+
|
|
149
|
+
if (expectedId && node.id !== expectedId) return false;
|
|
150
|
+
if (expectedClass && !node.classList?.contains(expectedClass)) return false;
|
|
151
|
+
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Log a hydration mismatch warning.
|
|
157
|
+
* @param {HydrationContext} ctx - Hydration context
|
|
158
|
+
* @param {string} expected - What was expected
|
|
159
|
+
* @param {Node|null} actual - What was found
|
|
160
|
+
*/
|
|
161
|
+
export function warnMismatch(ctx, expected, actual) {
|
|
162
|
+
if (ctx.mismatchWarned) return;
|
|
163
|
+
|
|
164
|
+
const actualDesc = actual
|
|
165
|
+
? `<${actual.tagName?.toLowerCase() || actual.nodeName}>`
|
|
166
|
+
: 'null';
|
|
167
|
+
|
|
168
|
+
console.warn(
|
|
169
|
+
`[Pulse Hydration] Mismatch at depth ${ctx.depth}: ` +
|
|
170
|
+
`expected ${expected}, found ${actualDesc}. ` +
|
|
171
|
+
`This may cause hydration errors.`
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
ctx.mismatchWarned = true;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ============================================================================
|
|
178
|
+
// Event Listener Management
|
|
179
|
+
// ============================================================================
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Register an event listener during hydration.
|
|
183
|
+
* @param {HydrationContext} ctx - Hydration context
|
|
184
|
+
* @param {Element} element - Target element
|
|
185
|
+
* @param {string} event - Event name
|
|
186
|
+
* @param {Function} handler - Event handler
|
|
187
|
+
* @param {Object} [options] - Event listener options
|
|
188
|
+
*/
|
|
189
|
+
export function registerListener(ctx, element, event, handler, options) {
|
|
190
|
+
element.addEventListener(event, handler, options);
|
|
191
|
+
ctx.listeners.push({ element, event, handler, options });
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Register a cleanup function.
|
|
196
|
+
* @param {HydrationContext} ctx - Hydration context
|
|
197
|
+
* @param {Function} cleanup - Cleanup function
|
|
198
|
+
*/
|
|
199
|
+
export function registerCleanup(ctx, cleanup) {
|
|
200
|
+
ctx.cleanups.push(cleanup);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ============================================================================
|
|
204
|
+
// Hydration Disposal
|
|
205
|
+
// ============================================================================
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Dispose of all hydration resources.
|
|
209
|
+
* Removes event listeners and runs cleanup functions.
|
|
210
|
+
* @param {HydrationContext} ctx - Hydration context
|
|
211
|
+
*/
|
|
212
|
+
export function disposeHydration(ctx) {
|
|
213
|
+
// Remove all event listeners
|
|
214
|
+
for (const { element, event, handler, options } of ctx.listeners) {
|
|
215
|
+
element.removeEventListener(event, handler, options);
|
|
216
|
+
}
|
|
217
|
+
ctx.listeners = [];
|
|
218
|
+
|
|
219
|
+
// Run cleanup functions
|
|
220
|
+
for (const cleanup of ctx.cleanups) {
|
|
221
|
+
try {
|
|
222
|
+
cleanup();
|
|
223
|
+
} catch (e) {
|
|
224
|
+
console.error('[Pulse Hydration] Cleanup error:', e);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
ctx.cleanups = [];
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ============================================================================
|
|
231
|
+
// Hydration Helpers
|
|
232
|
+
// ============================================================================
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Find the next element matching a tag within the current scope.
|
|
236
|
+
* Useful for recovering from mismatches.
|
|
237
|
+
* @param {HydrationContext} ctx - Hydration context
|
|
238
|
+
* @param {string} tag - Tag name to find
|
|
239
|
+
* @returns {Element|null}
|
|
240
|
+
*/
|
|
241
|
+
export function findNextElement(ctx, tag) {
|
|
242
|
+
let node = ctx.cursor;
|
|
243
|
+
while (node) {
|
|
244
|
+
if (node.nodeType === 1 && node.tagName?.toLowerCase() === tag) {
|
|
245
|
+
return node;
|
|
246
|
+
}
|
|
247
|
+
node = node.nextSibling;
|
|
248
|
+
}
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Count remaining elements in the current scope.
|
|
254
|
+
* Useful for debugging hydration issues.
|
|
255
|
+
* @param {HydrationContext} ctx - Hydration context
|
|
256
|
+
* @returns {number}
|
|
257
|
+
*/
|
|
258
|
+
export function countRemaining(ctx) {
|
|
259
|
+
let count = 0;
|
|
260
|
+
let node = ctx.cursor;
|
|
261
|
+
while (node) {
|
|
262
|
+
if (node.nodeType === 1) count++;
|
|
263
|
+
node = node.nextSibling;
|
|
264
|
+
}
|
|
265
|
+
return count;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Check if hydration is complete (no more nodes to process).
|
|
270
|
+
* @param {HydrationContext} ctx - Hydration context
|
|
271
|
+
* @returns {boolean}
|
|
272
|
+
*/
|
|
273
|
+
export function isHydrationComplete(ctx) {
|
|
274
|
+
return ctx.cursor === null && ctx.depth === 0;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// ============================================================================
|
|
278
|
+
// Exports
|
|
279
|
+
// ============================================================================
|
|
280
|
+
|
|
281
|
+
export default {
|
|
282
|
+
// Mode control
|
|
283
|
+
setHydrationMode,
|
|
284
|
+
isHydratingMode,
|
|
285
|
+
getHydrationContext,
|
|
286
|
+
|
|
287
|
+
// Context
|
|
288
|
+
createHydrationContext,
|
|
289
|
+
|
|
290
|
+
// Navigation
|
|
291
|
+
getCurrentNode,
|
|
292
|
+
advanceCursor,
|
|
293
|
+
enterChild,
|
|
294
|
+
exitChild,
|
|
295
|
+
skipComments,
|
|
296
|
+
|
|
297
|
+
// Matching
|
|
298
|
+
matchesElement,
|
|
299
|
+
warnMismatch,
|
|
300
|
+
findNextElement,
|
|
301
|
+
|
|
302
|
+
// Resources
|
|
303
|
+
registerListener,
|
|
304
|
+
registerCleanup,
|
|
305
|
+
disposeHydration,
|
|
306
|
+
|
|
307
|
+
// Helpers
|
|
308
|
+
countRemaining,
|
|
309
|
+
isHydrationComplete
|
|
310
|
+
};
|