@fictjs/ssr 0.22.0 → 0.24.0
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/experimental.cjs +3 -780
- package/dist/experimental.d.cts +2 -164
- package/dist/experimental.d.ts +2 -164
- package/dist/experimental.js +2 -7
- package/dist/index.cjs +8 -1026
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -17
- package/dist/render-core-BCYvLfHF.d.cts +166 -0
- package/dist/render-core-BCYvLfHF.d.cts.map +1 -0
- package/dist/render-core-BCYvLfHF.d.ts +166 -0
- package/dist/render-core-BCYvLfHF.d.ts.map +1 -0
- package/dist/render-core-BLLUhkYy.js +969 -0
- package/dist/render-core-BLLUhkYy.js.map +1 -0
- package/dist/render-core-CWudV9yi.cjs +1008 -0
- package/dist/stream-runtime.cjs +13 -34
- package/dist/stream-runtime.d.cts +8 -6
- package/dist/stream-runtime.d.cts.map +1 -0
- package/dist/stream-runtime.d.ts +8 -6
- package/dist/stream-runtime.d.ts.map +1 -0
- package/dist/stream-runtime.js +15 -8
- package/dist/stream-runtime.js.map +1 -0
- package/package.json +6 -6
- package/dist/chunk-DI4PFT6R.js +0 -11
- package/dist/chunk-LW7ALN63.js +0 -1129
package/dist/index.cjs
CHANGED
|
@@ -1,1026 +1,8 @@
|
|
|
1
|
-
"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// src/index.ts
|
|
21
|
-
var index_exports = {};
|
|
22
|
-
__export(index_exports, {
|
|
23
|
-
createSSRDocument: () => createSSRDocument,
|
|
24
|
-
renderToDocument: () => renderToDocument,
|
|
25
|
-
renderToPipeableStream: () => renderToPipeableStream,
|
|
26
|
-
renderToStream: () => renderToStream,
|
|
27
|
-
renderToString: () => renderToString,
|
|
28
|
-
renderToStringAsync: () => renderToStringAsync
|
|
29
|
-
});
|
|
30
|
-
module.exports = __toCommonJS(index_exports);
|
|
31
|
-
|
|
32
|
-
// src/render-core.ts
|
|
33
|
-
var import_runtime = require("@fictjs/runtime");
|
|
34
|
-
var import_internal2 = require("@fictjs/runtime/internal");
|
|
35
|
-
var import_linkedom = require("linkedom");
|
|
36
|
-
|
|
37
|
-
// src/globals.ts
|
|
38
|
-
var import_internal = require("@fictjs/runtime/internal");
|
|
39
|
-
function installGlobals(window, document) {
|
|
40
|
-
const win = window;
|
|
41
|
-
const required = {
|
|
42
|
-
window: win,
|
|
43
|
-
document,
|
|
44
|
-
self: win,
|
|
45
|
-
Node: win.Node,
|
|
46
|
-
Element: win.Element,
|
|
47
|
-
HTMLElement: win.HTMLElement,
|
|
48
|
-
SVGElement: win.SVGElement,
|
|
49
|
-
Document: win.Document,
|
|
50
|
-
DocumentFragment: win.DocumentFragment,
|
|
51
|
-
Text: win.Text,
|
|
52
|
-
Comment: win.Comment
|
|
53
|
-
};
|
|
54
|
-
const optional = {
|
|
55
|
-
Range: win.Range,
|
|
56
|
-
Event: win.Event,
|
|
57
|
-
CustomEvent: win.CustomEvent,
|
|
58
|
-
MutationObserver: win.MutationObserver,
|
|
59
|
-
DOMParser: win.DOMParser,
|
|
60
|
-
getComputedStyle: win.getComputedStyle?.bind(win)
|
|
61
|
-
};
|
|
62
|
-
const missing = Object.entries(required).filter(([, value]) => value === void 0).map(([key]) => key);
|
|
63
|
-
if (missing.length) {
|
|
64
|
-
throw new Error(`[fict/ssr] Missing DOM globals: ${missing.join(", ")}`);
|
|
65
|
-
}
|
|
66
|
-
const globals = { ...required, ...optional };
|
|
67
|
-
const keys = Object.keys(globals);
|
|
68
|
-
const snapshot = captureGlobals(keys);
|
|
69
|
-
for (const key of keys) {
|
|
70
|
-
const value = globals[key];
|
|
71
|
-
if (value !== void 0) {
|
|
72
|
-
;
|
|
73
|
-
globalThis[key] = value;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return () => restoreGlobals(snapshot);
|
|
77
|
-
}
|
|
78
|
-
function installManifest(manifest) {
|
|
79
|
-
if (!manifest) return () => {
|
|
80
|
-
};
|
|
81
|
-
let resolved;
|
|
82
|
-
if (typeof manifest === "string") {
|
|
83
|
-
const raw = readTextFileFromPath(manifest);
|
|
84
|
-
resolved = JSON.parse(raw);
|
|
85
|
-
} else {
|
|
86
|
-
resolved = manifest;
|
|
87
|
-
}
|
|
88
|
-
const session = (0, import_internal.__fictGetCurrentSSRSession)();
|
|
89
|
-
if (session) {
|
|
90
|
-
const previous = session.manifest;
|
|
91
|
-
session.manifest = resolved;
|
|
92
|
-
return () => {
|
|
93
|
-
session.manifest = previous;
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
const key = "__FICT_MANIFEST__";
|
|
97
|
-
const snapshot = {
|
|
98
|
-
exists: Object.prototype.hasOwnProperty.call(globalThis, key),
|
|
99
|
-
value: globalThis[key]
|
|
100
|
-
};
|
|
101
|
-
globalThis[key] = resolved;
|
|
102
|
-
return () => {
|
|
103
|
-
if (snapshot.exists) {
|
|
104
|
-
;
|
|
105
|
-
globalThis[key] = snapshot.value;
|
|
106
|
-
} else {
|
|
107
|
-
delete globalThis[key];
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
function captureGlobals(keys) {
|
|
112
|
-
const snapshot = [];
|
|
113
|
-
for (const key of keys) {
|
|
114
|
-
const exists = Object.prototype.hasOwnProperty.call(globalThis, key);
|
|
115
|
-
const value = globalThis[key];
|
|
116
|
-
snapshot.push({ key, exists, value });
|
|
117
|
-
}
|
|
118
|
-
return snapshot;
|
|
119
|
-
}
|
|
120
|
-
function restoreGlobals(snapshot) {
|
|
121
|
-
for (const entry of snapshot) {
|
|
122
|
-
if (entry.exists) {
|
|
123
|
-
;
|
|
124
|
-
globalThis[entry.key] = entry.value;
|
|
125
|
-
} else {
|
|
126
|
-
delete globalThis[entry.key];
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
function readTextFileFromPath(path) {
|
|
131
|
-
const g = globalThis;
|
|
132
|
-
const deno = g.Deno;
|
|
133
|
-
if (deno && typeof deno.readTextFileSync === "function") {
|
|
134
|
-
return deno.readTextFileSync(path);
|
|
135
|
-
}
|
|
136
|
-
const nodeRequire = getNodeRequire();
|
|
137
|
-
if (nodeRequire) {
|
|
138
|
-
const fs = nodeRequire("node:fs");
|
|
139
|
-
return fs.readFileSync(path, "utf8");
|
|
140
|
-
}
|
|
141
|
-
throw new Error(
|
|
142
|
-
"[fict/ssr] `manifest` as file path is only supported when Deno.readTextFileSync or CommonJS require is available. Pass a manifest object in Node ESM or edge runtimes."
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
function getNodeRequire() {
|
|
146
|
-
const g = globalThis;
|
|
147
|
-
const direct = g.require;
|
|
148
|
-
if (typeof direct === "function") {
|
|
149
|
-
return direct;
|
|
150
|
-
}
|
|
151
|
-
try {
|
|
152
|
-
return Function('return typeof require === "function" ? require : null')();
|
|
153
|
-
} catch {
|
|
154
|
-
return null;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// src/stream-bridge.ts
|
|
159
|
-
function createPipeBridge() {
|
|
160
|
-
const nodeBridge = createNodePipeBridge();
|
|
161
|
-
if (nodeBridge) return nodeBridge;
|
|
162
|
-
const targets = /* @__PURE__ */ new Set();
|
|
163
|
-
const buffer = [];
|
|
164
|
-
let state = "open";
|
|
165
|
-
let abortReason = null;
|
|
166
|
-
let sinkErrorHandler;
|
|
167
|
-
const safeWrite = (target, chunk) => {
|
|
168
|
-
try {
|
|
169
|
-
const ready = target.write(chunk);
|
|
170
|
-
if (ready === false) {
|
|
171
|
-
return new Promise((resolve) => {
|
|
172
|
-
const withOnce = target;
|
|
173
|
-
if (typeof withOnce.once === "function") {
|
|
174
|
-
withOnce.once("drain", resolve);
|
|
175
|
-
} else {
|
|
176
|
-
resolve();
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
} catch (error) {
|
|
181
|
-
sinkErrorHandler?.(error);
|
|
182
|
-
}
|
|
183
|
-
return void 0;
|
|
184
|
-
};
|
|
185
|
-
const safeEnd = (target) => {
|
|
186
|
-
try {
|
|
187
|
-
target.end();
|
|
188
|
-
} catch {
|
|
189
|
-
}
|
|
190
|
-
};
|
|
191
|
-
const safeDestroy = (target, reason) => {
|
|
192
|
-
const withDestroy = target;
|
|
193
|
-
if (typeof withDestroy.destroy === "function") {
|
|
194
|
-
try {
|
|
195
|
-
withDestroy.destroy(reason);
|
|
196
|
-
} catch {
|
|
197
|
-
}
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
safeEnd(target);
|
|
201
|
-
};
|
|
202
|
-
return {
|
|
203
|
-
pipe(writable, options) {
|
|
204
|
-
targets.add(writable);
|
|
205
|
-
if (options?.onError) {
|
|
206
|
-
sinkErrorHandler = options.onError;
|
|
207
|
-
if (typeof writable.on === "function") {
|
|
208
|
-
writable.on("error", options.onError);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
if (buffer.length > 0) {
|
|
212
|
-
for (const chunk of buffer) {
|
|
213
|
-
safeWrite(writable, chunk);
|
|
214
|
-
}
|
|
215
|
-
buffer.length = 0;
|
|
216
|
-
}
|
|
217
|
-
if (state === "closed") {
|
|
218
|
-
safeEnd(writable);
|
|
219
|
-
} else if (state === "aborted") {
|
|
220
|
-
safeDestroy(writable, abortReason ?? new Error("Stream aborted"));
|
|
221
|
-
}
|
|
222
|
-
},
|
|
223
|
-
write(chunk) {
|
|
224
|
-
if (state !== "open") return;
|
|
225
|
-
if (targets.size === 0) {
|
|
226
|
-
buffer.push(chunk);
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
const pending = [];
|
|
230
|
-
for (const target of targets) {
|
|
231
|
-
const result = safeWrite(target, chunk);
|
|
232
|
-
if (result) pending.push(result);
|
|
233
|
-
}
|
|
234
|
-
return pending.length > 0 ? Promise.all(pending).then(() => void 0) : void 0;
|
|
235
|
-
},
|
|
236
|
-
close() {
|
|
237
|
-
if (state !== "open") return;
|
|
238
|
-
state = "closed";
|
|
239
|
-
for (const target of targets) {
|
|
240
|
-
safeEnd(target);
|
|
241
|
-
}
|
|
242
|
-
if (targets.size > 0) {
|
|
243
|
-
buffer.length = 0;
|
|
244
|
-
}
|
|
245
|
-
},
|
|
246
|
-
abort(reason) {
|
|
247
|
-
if (state !== "open") return;
|
|
248
|
-
state = "aborted";
|
|
249
|
-
abortReason = reason instanceof Error ? reason : new Error("Stream aborted");
|
|
250
|
-
for (const target of targets) {
|
|
251
|
-
safeDestroy(target, abortReason);
|
|
252
|
-
}
|
|
253
|
-
buffer.length = 0;
|
|
254
|
-
}
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
function createNodePipeBridge() {
|
|
258
|
-
const nodeRequire = getNodeRequire2();
|
|
259
|
-
if (!nodeRequire) return null;
|
|
260
|
-
try {
|
|
261
|
-
const streamModule = nodeRequire("node:stream");
|
|
262
|
-
if (!streamModule.PassThrough) return null;
|
|
263
|
-
const passThrough = new streamModule.PassThrough();
|
|
264
|
-
const buffer = [];
|
|
265
|
-
let piped = false;
|
|
266
|
-
let state = "open";
|
|
267
|
-
let abortReason = null;
|
|
268
|
-
const pendingDrains = /* @__PURE__ */ new Set();
|
|
269
|
-
const flushDrains = () => {
|
|
270
|
-
for (const resolve of pendingDrains) resolve();
|
|
271
|
-
pendingDrains.clear();
|
|
272
|
-
};
|
|
273
|
-
const writeToPassThrough = (chunk) => {
|
|
274
|
-
if (passThrough.write(chunk) === false) {
|
|
275
|
-
return new Promise((resolve) => {
|
|
276
|
-
const settle = () => {
|
|
277
|
-
pendingDrains.delete(settle);
|
|
278
|
-
resolve();
|
|
279
|
-
};
|
|
280
|
-
pendingDrains.add(settle);
|
|
281
|
-
const withOnce = passThrough;
|
|
282
|
-
if (typeof withOnce.once === "function") {
|
|
283
|
-
withOnce.once("drain", settle);
|
|
284
|
-
} else {
|
|
285
|
-
settle();
|
|
286
|
-
}
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
return void 0;
|
|
290
|
-
};
|
|
291
|
-
const flushBuffer = () => {
|
|
292
|
-
if (buffer.length === 0) return void 0;
|
|
293
|
-
const pending = [];
|
|
294
|
-
for (const chunk of buffer) {
|
|
295
|
-
const result = writeToPassThrough(chunk);
|
|
296
|
-
if (result) pending.push(result);
|
|
297
|
-
}
|
|
298
|
-
buffer.length = 0;
|
|
299
|
-
return pending.length > 0 ? Promise.all(pending).then(() => void 0) : void 0;
|
|
300
|
-
};
|
|
301
|
-
const destroyPassThrough = (error) => {
|
|
302
|
-
if (typeof passThrough.destroy === "function") {
|
|
303
|
-
passThrough.destroy(error);
|
|
304
|
-
} else {
|
|
305
|
-
passThrough.end();
|
|
306
|
-
}
|
|
307
|
-
};
|
|
308
|
-
return {
|
|
309
|
-
pipe(writable, options) {
|
|
310
|
-
piped = true;
|
|
311
|
-
passThrough.pipe(writable);
|
|
312
|
-
const onError = options?.onError;
|
|
313
|
-
if (onError && typeof writable.on === "function") {
|
|
314
|
-
writable.on("error", (err) => {
|
|
315
|
-
flushDrains();
|
|
316
|
-
onError(err);
|
|
317
|
-
});
|
|
318
|
-
}
|
|
319
|
-
if (state === "aborted") {
|
|
320
|
-
destroyPassThrough(abortReason ?? new Error("Stream aborted"));
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
const flushed = flushBuffer();
|
|
324
|
-
if (state === "closed") {
|
|
325
|
-
if (flushed) {
|
|
326
|
-
void flushed.then(() => passThrough.end());
|
|
327
|
-
} else {
|
|
328
|
-
passThrough.end();
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
},
|
|
332
|
-
write(chunk) {
|
|
333
|
-
if (state !== "open") return;
|
|
334
|
-
if (!piped) {
|
|
335
|
-
buffer.push(chunk);
|
|
336
|
-
return void 0;
|
|
337
|
-
}
|
|
338
|
-
return writeToPassThrough(chunk);
|
|
339
|
-
},
|
|
340
|
-
close() {
|
|
341
|
-
if (state !== "open") return;
|
|
342
|
-
state = "closed";
|
|
343
|
-
if (!piped) return;
|
|
344
|
-
passThrough.end();
|
|
345
|
-
},
|
|
346
|
-
abort(reason) {
|
|
347
|
-
if (state !== "open") return;
|
|
348
|
-
state = "aborted";
|
|
349
|
-
abortReason = reason instanceof Error ? reason : new Error("Stream aborted");
|
|
350
|
-
buffer.length = 0;
|
|
351
|
-
flushDrains();
|
|
352
|
-
if (piped) {
|
|
353
|
-
destroyPassThrough(abortReason);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
};
|
|
357
|
-
} catch {
|
|
358
|
-
return null;
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
function getNodeRequire2() {
|
|
362
|
-
const g = globalThis;
|
|
363
|
-
const direct = g.require;
|
|
364
|
-
if (typeof direct === "function") {
|
|
365
|
-
return direct;
|
|
366
|
-
}
|
|
367
|
-
try {
|
|
368
|
-
return Function('return typeof require === "function" ? require : null')();
|
|
369
|
-
} catch {
|
|
370
|
-
return null;
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// src/stream-runtime.ts
|
|
375
|
-
function createStreamRuntimeCode(options = {}) {
|
|
376
|
-
const observerMode = options.observerMode ?? true;
|
|
377
|
-
return `(function(){if(window.__FICT_STREAM)return;var cache=new Map();function find(id){var hit=cache.get(id);if(hit)return hit;var start=null,end=null;var w=document.createTreeWalker(document,NodeFilter.SHOW_COMMENT);while(w.nextNode()){var n=w.currentNode;var d=n.data;if(d==="fict:suspense-start:"+id)start=n;else if(d==="fict:suspense-end:"+id)end=n;if(start&&end)break;}if(start&&end){hit={start:start,end:end};cache.set(id,hit);}return hit;}function apply(id){var tpl=document.querySelector('template[data-fict-suspense="' + id + '"]');if(!tpl)return;var b=find(id);if(!b)return;var node=b.start.nextSibling;while(node&&node!==b.end){var next=node.nextSibling;node.parentNode&&node.parentNode.removeChild(node);node=next;}b.end.parentNode&&b.end.parentNode.insertBefore(tpl.content,b.end);tpl.parentNode&&tpl.parentNode.removeChild(tpl);}window.__FICT_STREAM={apply:apply};` + (observerMode ? 'function scan(root){var list=(root&&root.querySelectorAll?root:document).querySelectorAll("template[data-fict-suspense]");for(var i=0;i<list.length;i++){apply(list[i].getAttribute("data-fict-suspense"));}}if(typeof MutationObserver==="function"){new MutationObserver(function(muts){for(var i=0;i<muts.length;i++){for(var j=0;j<muts[i].addedNodes.length;j++){var n=muts[i].addedNodes[j];if(n.nodeType===1){if(n.matches&&n.matches("template[data-fict-suspense]"))apply(n.getAttribute("data-fict-suspense"));scan(n);}}}}).observe(document.documentElement||document,{childList:true,subtree:true});}if(document.readyState==="loading"){document.addEventListener("DOMContentLoaded",function(){scan(document);},{once:true});}else{scan(document);}' : "") + "})();";
|
|
378
|
-
}
|
|
379
|
-
var FICT_STREAM_RUNTIME_CODE = createStreamRuntimeCode({ observerMode: true });
|
|
380
|
-
|
|
381
|
-
// src/render-core.ts
|
|
382
|
-
var DEFAULT_HTML = "<!doctype html><html><head></head><body></body></html>";
|
|
383
|
-
function createSSRDocument(html = DEFAULT_HTML) {
|
|
384
|
-
const window = (0, import_linkedom.parseHTML)(html);
|
|
385
|
-
const document = window.document;
|
|
386
|
-
if (!window || !document) {
|
|
387
|
-
throw new Error("[fict/ssr] Failed to create DOM. Missing window or document.");
|
|
388
|
-
}
|
|
389
|
-
return { window, document };
|
|
390
|
-
}
|
|
391
|
-
function renderToDocument(view, options = {}) {
|
|
392
|
-
const session = (0, import_internal2.__fictCreateSSRSession)();
|
|
393
|
-
return (0, import_internal2.__fictRunWithSSRSession)(session, () => renderToDocumentInSession(view, options));
|
|
394
|
-
}
|
|
395
|
-
function renderToDocumentInSession(view, options) {
|
|
396
|
-
const includeSnapshot = options.includeSnapshot !== false;
|
|
397
|
-
(0, import_internal2.__fictEnableSSR)();
|
|
398
|
-
let dom;
|
|
399
|
-
let restoreGlobals2 = () => {
|
|
400
|
-
};
|
|
401
|
-
let restoreManifest = () => {
|
|
402
|
-
};
|
|
403
|
-
let container;
|
|
404
|
-
let teardown = () => {
|
|
405
|
-
};
|
|
406
|
-
try {
|
|
407
|
-
dom = resolveDom(options);
|
|
408
|
-
const { document, window } = dom;
|
|
409
|
-
const shouldExpose = options.exposeGlobals === true;
|
|
410
|
-
restoreGlobals2 = shouldExpose ? installGlobals(window, document) : () => {
|
|
411
|
-
};
|
|
412
|
-
restoreManifest = installManifest(options.manifest);
|
|
413
|
-
container = resolveContainer(document, options);
|
|
414
|
-
teardown = (0, import_runtime.render)(view, container);
|
|
415
|
-
if (includeSnapshot) {
|
|
416
|
-
const state = (0, import_internal2.__fictSerializeSSRState)();
|
|
417
|
-
injectSnapshot(document, container, state, options);
|
|
418
|
-
}
|
|
419
|
-
} catch (error) {
|
|
420
|
-
(0, import_internal2.__fictDisableSSR)();
|
|
421
|
-
restoreGlobals2();
|
|
422
|
-
restoreManifest();
|
|
423
|
-
throw error;
|
|
424
|
-
}
|
|
425
|
-
(0, import_internal2.__fictDisableSSR)();
|
|
426
|
-
const html = serializeOutput(dom.document, container, options);
|
|
427
|
-
const dispose = () => {
|
|
428
|
-
try {
|
|
429
|
-
teardown();
|
|
430
|
-
} finally {
|
|
431
|
-
restoreGlobals2();
|
|
432
|
-
restoreManifest();
|
|
433
|
-
}
|
|
434
|
-
};
|
|
435
|
-
return { html, document: dom.document, window: dom.window, container, dispose };
|
|
436
|
-
}
|
|
437
|
-
function renderToString(view, options = {}) {
|
|
438
|
-
const result = renderToDocument(view, options);
|
|
439
|
-
const html = result.html;
|
|
440
|
-
result.dispose();
|
|
441
|
-
return html;
|
|
442
|
-
}
|
|
443
|
-
async function renderToStringAsync(view, options = {}) {
|
|
444
|
-
return renderToString(view, options);
|
|
445
|
-
}
|
|
446
|
-
function renderToStream(view, options = {}) {
|
|
447
|
-
const encoder = new TextEncoder();
|
|
448
|
-
let controller = null;
|
|
449
|
-
let abortRender = null;
|
|
450
|
-
const readyResolvers = [];
|
|
451
|
-
const drainBackpressure = () => {
|
|
452
|
-
while (readyResolvers.length > 0) {
|
|
453
|
-
readyResolvers.shift()?.();
|
|
454
|
-
}
|
|
455
|
-
};
|
|
456
|
-
const resolveBackpressure = () => {
|
|
457
|
-
if (!controller || (controller.desiredSize ?? 1) <= 0) return;
|
|
458
|
-
drainBackpressure();
|
|
459
|
-
};
|
|
460
|
-
const closeController = () => {
|
|
461
|
-
abortRender = null;
|
|
462
|
-
drainBackpressure();
|
|
463
|
-
if (!controller) return;
|
|
464
|
-
try {
|
|
465
|
-
controller.close();
|
|
466
|
-
} finally {
|
|
467
|
-
controller = null;
|
|
468
|
-
}
|
|
469
|
-
};
|
|
470
|
-
const errorController = (reason) => {
|
|
471
|
-
abortRender = null;
|
|
472
|
-
drainBackpressure();
|
|
473
|
-
if (!controller) return;
|
|
474
|
-
try {
|
|
475
|
-
controller.error(reason);
|
|
476
|
-
} finally {
|
|
477
|
-
controller = null;
|
|
478
|
-
}
|
|
479
|
-
};
|
|
480
|
-
const stream = new ReadableStream({
|
|
481
|
-
start(ctrl) {
|
|
482
|
-
controller = ctrl;
|
|
483
|
-
const started = startStreamingRender(view, options, {
|
|
484
|
-
write(chunk) {
|
|
485
|
-
if (!controller) return;
|
|
486
|
-
controller.enqueue(encoder.encode(chunk));
|
|
487
|
-
if ((controller.desiredSize ?? 1) <= 0) {
|
|
488
|
-
return new Promise((resolve) => {
|
|
489
|
-
readyResolvers.push(resolve);
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
|
-
return void 0;
|
|
493
|
-
},
|
|
494
|
-
close() {
|
|
495
|
-
closeController();
|
|
496
|
-
},
|
|
497
|
-
abort(reason) {
|
|
498
|
-
errorController(reason);
|
|
499
|
-
}
|
|
500
|
-
});
|
|
501
|
-
abortRender = started.abort;
|
|
502
|
-
started.shellReady.catch(() => void 0);
|
|
503
|
-
started.allReady.catch(() => void 0);
|
|
504
|
-
},
|
|
505
|
-
pull() {
|
|
506
|
-
resolveBackpressure();
|
|
507
|
-
},
|
|
508
|
-
cancel(reason) {
|
|
509
|
-
const abort = abortRender;
|
|
510
|
-
controller = null;
|
|
511
|
-
drainBackpressure();
|
|
512
|
-
abort?.(reason ?? new Error("Stream canceled"));
|
|
513
|
-
}
|
|
514
|
-
});
|
|
515
|
-
return stream;
|
|
516
|
-
}
|
|
517
|
-
function renderToPipeableStream(view, options = {}) {
|
|
518
|
-
const bridge = createPipeBridge();
|
|
519
|
-
const { shellReady, allReady, abort } = startStreamingRender(view, options, {
|
|
520
|
-
write(chunk) {
|
|
521
|
-
return bridge.write(chunk);
|
|
522
|
-
},
|
|
523
|
-
close() {
|
|
524
|
-
bridge.close();
|
|
525
|
-
},
|
|
526
|
-
abort(reason) {
|
|
527
|
-
bridge.abort(reason);
|
|
528
|
-
}
|
|
529
|
-
});
|
|
530
|
-
return {
|
|
531
|
-
pipe(writable) {
|
|
532
|
-
bridge.pipe(writable, { onError: abort });
|
|
533
|
-
},
|
|
534
|
-
abort,
|
|
535
|
-
shellReady,
|
|
536
|
-
allReady
|
|
537
|
-
};
|
|
538
|
-
}
|
|
539
|
-
function resolveDom(options) {
|
|
540
|
-
if (options.dom) {
|
|
541
|
-
return options.dom;
|
|
542
|
-
}
|
|
543
|
-
if (options.document && options.window) {
|
|
544
|
-
return { document: options.document, window: options.window };
|
|
545
|
-
}
|
|
546
|
-
if (options.document) {
|
|
547
|
-
const window = options.window ?? options.document.defaultView ?? options.document.defaultView ?? void 0;
|
|
548
|
-
if (!window) {
|
|
549
|
-
throw new Error(
|
|
550
|
-
"[fict/ssr] A window is required when providing a document without defaultView."
|
|
551
|
-
);
|
|
552
|
-
}
|
|
553
|
-
return { document: options.document, window };
|
|
554
|
-
}
|
|
555
|
-
if (options.window) {
|
|
556
|
-
return { document: options.window.document, window: options.window };
|
|
557
|
-
}
|
|
558
|
-
return createSSRDocument(options.html);
|
|
559
|
-
}
|
|
560
|
-
function isPromiseLike(value) {
|
|
561
|
-
return typeof value === "object" && value !== null && typeof value.then === "function";
|
|
562
|
-
}
|
|
563
|
-
function startStreamingRender(view, options, writer, control = {}) {
|
|
564
|
-
const session = (0, import_internal2.__fictCreateSSRSession)();
|
|
565
|
-
return (0, import_internal2.__fictRunWithSSRSession)(
|
|
566
|
-
session,
|
|
567
|
-
() => startStreamingRenderInSession(session, view, options, writer, control)
|
|
568
|
-
);
|
|
569
|
-
}
|
|
570
|
-
function startStreamingRenderInSession(session, view, options, writer, control = {}) {
|
|
571
|
-
const runInSession = (fn) => (0, import_internal2.__fictRunWithSSRSession)(session, fn);
|
|
572
|
-
const resolvedOptions = {
|
|
573
|
-
...options,
|
|
574
|
-
// Streaming requires a real document; default to fullDocument when unspecified.
|
|
575
|
-
fullDocument: options.fullDocument ?? true
|
|
576
|
-
};
|
|
577
|
-
let resolveShell;
|
|
578
|
-
let rejectShell;
|
|
579
|
-
let resolveAll;
|
|
580
|
-
let rejectAll;
|
|
581
|
-
let shellSettled = false;
|
|
582
|
-
const shellReady = new Promise((res, rej) => {
|
|
583
|
-
resolveShell = () => {
|
|
584
|
-
if (shellSettled) return;
|
|
585
|
-
shellSettled = true;
|
|
586
|
-
res();
|
|
587
|
-
};
|
|
588
|
-
rejectShell = (err) => {
|
|
589
|
-
if (shellSettled) return;
|
|
590
|
-
shellSettled = true;
|
|
591
|
-
rej(err);
|
|
592
|
-
};
|
|
593
|
-
});
|
|
594
|
-
const allReady = new Promise((res, rej) => {
|
|
595
|
-
resolveAll = res;
|
|
596
|
-
rejectAll = rej;
|
|
597
|
-
});
|
|
598
|
-
let dom = null;
|
|
599
|
-
let restoreGlobals2 = () => {
|
|
600
|
-
};
|
|
601
|
-
let restoreManifest = () => {
|
|
602
|
-
};
|
|
603
|
-
let teardown = () => {
|
|
604
|
-
};
|
|
605
|
-
let container = null;
|
|
606
|
-
let closed = false;
|
|
607
|
-
let tailHtml = "";
|
|
608
|
-
let wroteShell = false;
|
|
609
|
-
let shellCarriesTail = false;
|
|
610
|
-
let writeChain = null;
|
|
611
|
-
let writeFailed = false;
|
|
612
|
-
let removeAbortListener = () => {
|
|
613
|
-
};
|
|
614
|
-
const mode = options.mode ?? "shell";
|
|
615
|
-
const includeSnapshot = options.includeSnapshot !== false;
|
|
616
|
-
const sentScopes = /* @__PURE__ */ new Set();
|
|
617
|
-
const boundaryMap = /* @__PURE__ */ new Map();
|
|
618
|
-
let boundaryId = 0;
|
|
619
|
-
let pendingCount = 0;
|
|
620
|
-
const handleWriteError = (error) => {
|
|
621
|
-
if (writeFailed) return;
|
|
622
|
-
writeFailed = true;
|
|
623
|
-
options.onError?.(error);
|
|
624
|
-
writer.abort(error);
|
|
625
|
-
cleanup();
|
|
626
|
-
rejectShell(error);
|
|
627
|
-
rejectAll(error);
|
|
628
|
-
};
|
|
629
|
-
const enqueueWrite = (chunk) => {
|
|
630
|
-
if (writeFailed) return;
|
|
631
|
-
const trackWrite = (promise) => {
|
|
632
|
-
const tracked = promise.then(
|
|
633
|
-
() => {
|
|
634
|
-
if (writeChain === tracked) {
|
|
635
|
-
writeChain = null;
|
|
636
|
-
}
|
|
637
|
-
},
|
|
638
|
-
(error) => {
|
|
639
|
-
if (writeChain === tracked) {
|
|
640
|
-
writeChain = null;
|
|
641
|
-
}
|
|
642
|
-
handleWriteError(error);
|
|
643
|
-
}
|
|
644
|
-
);
|
|
645
|
-
writeChain = tracked;
|
|
646
|
-
};
|
|
647
|
-
const writeAsync = () => Promise.resolve(writer.write(chunk)).then(() => void 0);
|
|
648
|
-
if (writeChain) {
|
|
649
|
-
trackWrite(writeChain.then(writeAsync));
|
|
650
|
-
return;
|
|
651
|
-
}
|
|
652
|
-
try {
|
|
653
|
-
const result = writer.write(chunk);
|
|
654
|
-
if (isPromiseLike(result)) {
|
|
655
|
-
trackWrite(
|
|
656
|
-
result.then(
|
|
657
|
-
() => void 0,
|
|
658
|
-
(error) => {
|
|
659
|
-
throw error;
|
|
660
|
-
}
|
|
661
|
-
)
|
|
662
|
-
);
|
|
663
|
-
}
|
|
664
|
-
} catch (error) {
|
|
665
|
-
handleWriteError(error);
|
|
666
|
-
}
|
|
667
|
-
};
|
|
668
|
-
const afterWrites = (fn) => {
|
|
669
|
-
const pending = writeChain;
|
|
670
|
-
if (!pending) {
|
|
671
|
-
if (!writeFailed) {
|
|
672
|
-
fn();
|
|
673
|
-
}
|
|
674
|
-
return;
|
|
675
|
-
}
|
|
676
|
-
void pending.then(() => {
|
|
677
|
-
if (!writeFailed) {
|
|
678
|
-
fn();
|
|
679
|
-
}
|
|
680
|
-
});
|
|
681
|
-
};
|
|
682
|
-
const markShellReady = () => {
|
|
683
|
-
afterWrites(() => {
|
|
684
|
-
control.onShellFlushed?.();
|
|
685
|
-
resolveShell();
|
|
686
|
-
options.onShellReady?.();
|
|
687
|
-
});
|
|
688
|
-
};
|
|
689
|
-
const writeSnapshotForScopes = (scopeIds) => {
|
|
690
|
-
runInSession(() => {
|
|
691
|
-
if (!includeSnapshot || scopeIds.length === 0) return;
|
|
692
|
-
const registry = (0, import_internal2.__fictGetScopeRegistry)();
|
|
693
|
-
const pending = scopeIds.filter((id) => registry.has(id) && !sentScopes.has(id));
|
|
694
|
-
if (pending.length === 0) return;
|
|
695
|
-
const snapshot = (0, import_internal2.__fictSerializeSSRStateForScopes)(pending);
|
|
696
|
-
const ids = Object.keys(snapshot.scopes);
|
|
697
|
-
if (ids.length === 0) return;
|
|
698
|
-
const chunk = buildIncrementalSnapshotChunk(snapshot, resolvedOptions);
|
|
699
|
-
if (chunk) {
|
|
700
|
-
enqueueWrite(chunk);
|
|
701
|
-
}
|
|
702
|
-
for (const id of ids) {
|
|
703
|
-
sentScopes.add(id);
|
|
704
|
-
}
|
|
705
|
-
});
|
|
706
|
-
};
|
|
707
|
-
const writeSnapshotForBoundary = (boundary) => {
|
|
708
|
-
runInSession(() => {
|
|
709
|
-
const scopes = (0, import_internal2.__fictGetScopesForBoundary)(boundary);
|
|
710
|
-
writeSnapshotForScopes(scopes);
|
|
711
|
-
});
|
|
712
|
-
};
|
|
713
|
-
const writeRemainingSnapshots = () => {
|
|
714
|
-
runInSession(() => {
|
|
715
|
-
const scopes = Array.from((0, import_internal2.__fictGetScopeRegistry)().keys());
|
|
716
|
-
writeSnapshotForScopes(scopes);
|
|
717
|
-
});
|
|
718
|
-
};
|
|
719
|
-
const cleanup = () => {
|
|
720
|
-
removeAbortListener();
|
|
721
|
-
removeAbortListener = () => {
|
|
722
|
-
};
|
|
723
|
-
runInSession(() => {
|
|
724
|
-
(0, import_internal2.__fictSetSSRStreamHooks)(null);
|
|
725
|
-
(0, import_internal2.__fictDisableSSR)();
|
|
726
|
-
});
|
|
727
|
-
try {
|
|
728
|
-
teardown();
|
|
729
|
-
} catch {
|
|
730
|
-
} finally {
|
|
731
|
-
restoreGlobals2();
|
|
732
|
-
restoreManifest();
|
|
733
|
-
}
|
|
734
|
-
};
|
|
735
|
-
const finalize = () => {
|
|
736
|
-
if (closed) return;
|
|
737
|
-
closed = true;
|
|
738
|
-
if (mode === "all" && dom && container && !wroteShell) {
|
|
739
|
-
if (includeSnapshot) {
|
|
740
|
-
const snapshot = (0, import_internal2.__fictSerializeSSRState)();
|
|
741
|
-
injectSnapshot(dom.document, container, snapshot, resolvedOptions);
|
|
742
|
-
}
|
|
743
|
-
const fullHtml = serializeOutput(dom.document, container, resolvedOptions);
|
|
744
|
-
enqueueWrite(fullHtml);
|
|
745
|
-
afterWrites(() => {
|
|
746
|
-
writer.close();
|
|
747
|
-
cleanup();
|
|
748
|
-
resolveShell();
|
|
749
|
-
resolveAll();
|
|
750
|
-
options.onShellReady?.();
|
|
751
|
-
options.onAllReady?.();
|
|
752
|
-
});
|
|
753
|
-
return;
|
|
754
|
-
}
|
|
755
|
-
writeRemainingSnapshots();
|
|
756
|
-
if (tailHtml) {
|
|
757
|
-
enqueueWrite(tailHtml);
|
|
758
|
-
}
|
|
759
|
-
afterWrites(() => {
|
|
760
|
-
writer.close();
|
|
761
|
-
cleanup();
|
|
762
|
-
resolveAll();
|
|
763
|
-
options.onAllReady?.();
|
|
764
|
-
});
|
|
765
|
-
};
|
|
766
|
-
const maybeFinalize = () => {
|
|
767
|
-
if (pendingCount === 0) {
|
|
768
|
-
finalize();
|
|
769
|
-
}
|
|
770
|
-
};
|
|
771
|
-
const hooks = {
|
|
772
|
-
registerBoundary(start, end) {
|
|
773
|
-
const id = `s${++boundaryId}`;
|
|
774
|
-
boundaryMap.set(id, { start, end, pending: false });
|
|
775
|
-
return id;
|
|
776
|
-
},
|
|
777
|
-
boundaryPending(id) {
|
|
778
|
-
const entry = boundaryMap.get(id);
|
|
779
|
-
if (!entry || entry.pending) return;
|
|
780
|
-
entry.pending = true;
|
|
781
|
-
pendingCount++;
|
|
782
|
-
},
|
|
783
|
-
boundaryResolved(id) {
|
|
784
|
-
const entry = boundaryMap.get(id);
|
|
785
|
-
if (!entry) return;
|
|
786
|
-
if (entry.pending) {
|
|
787
|
-
entry.pending = false;
|
|
788
|
-
pendingCount = Math.max(0, pendingCount - 1);
|
|
789
|
-
}
|
|
790
|
-
if (mode === "shell") {
|
|
791
|
-
writeSnapshotForBoundary(id);
|
|
792
|
-
if (dom) {
|
|
793
|
-
const html = serializeBetween(dom.document, entry.start, entry.end);
|
|
794
|
-
enqueueWrite(buildPatchChunk(id, html, resolvedOptions));
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
maybeFinalize();
|
|
798
|
-
},
|
|
799
|
-
onError(err) {
|
|
800
|
-
options.onError?.(err);
|
|
801
|
-
abort(err);
|
|
802
|
-
}
|
|
803
|
-
};
|
|
804
|
-
const abort = (reason) => {
|
|
805
|
-
const abortReason = reason ?? new Error("Stream aborted");
|
|
806
|
-
if (!closed) {
|
|
807
|
-
closed = true;
|
|
808
|
-
writeFailed = true;
|
|
809
|
-
writer.abort(reason);
|
|
810
|
-
cleanup();
|
|
811
|
-
}
|
|
812
|
-
rejectShell(abortReason);
|
|
813
|
-
rejectAll(abortReason);
|
|
814
|
-
};
|
|
815
|
-
if (options.signal) {
|
|
816
|
-
if (options.signal.aborted) {
|
|
817
|
-
abort(options.signal.reason);
|
|
818
|
-
return { shellReady, allReady, abort };
|
|
819
|
-
} else {
|
|
820
|
-
const onAbort = () => abort(options.signal?.reason);
|
|
821
|
-
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
822
|
-
removeAbortListener = () => options.signal?.removeEventListener("abort", onAbort);
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
try {
|
|
826
|
-
(0, import_internal2.__fictEnableSSR)();
|
|
827
|
-
(0, import_internal2.__fictSetSSRStreamHooks)(hooks);
|
|
828
|
-
dom = resolveDom(resolvedOptions);
|
|
829
|
-
restoreGlobals2 = resolvedOptions.exposeGlobals === true ? installGlobals(dom.window, dom.document) : () => {
|
|
830
|
-
};
|
|
831
|
-
restoreManifest = installManifest(resolvedOptions.manifest);
|
|
832
|
-
container = resolveContainer(dom.document, resolvedOptions);
|
|
833
|
-
teardown = (0, import_runtime.render)(view, container);
|
|
834
|
-
if (mode === "all") {
|
|
835
|
-
if (pendingCount === 0) {
|
|
836
|
-
finalize();
|
|
837
|
-
}
|
|
838
|
-
return { shellReady, allReady, abort };
|
|
839
|
-
}
|
|
840
|
-
const shellHtml = serializeOutput(dom.document, container, resolvedOptions);
|
|
841
|
-
const streamRuntime = boundaryMap.size > 0 ? buildStreamRuntimeScript(resolvedOptions) : "";
|
|
842
|
-
if (resolvedOptions.fullDocument) {
|
|
843
|
-
const split = splitDocumentHtml(shellHtml);
|
|
844
|
-
if (!split) {
|
|
845
|
-
throw new Error("[fict/ssr] Failed to locate </body> for streaming output.");
|
|
846
|
-
}
|
|
847
|
-
if (control.includeTailInShell) {
|
|
848
|
-
enqueueWrite(split.head + streamRuntime);
|
|
849
|
-
tailHtml = split.tail;
|
|
850
|
-
shellCarriesTail = true;
|
|
851
|
-
} else {
|
|
852
|
-
enqueueWrite(split.head + streamRuntime);
|
|
853
|
-
tailHtml = split.tail;
|
|
854
|
-
}
|
|
855
|
-
} else {
|
|
856
|
-
enqueueWrite(shellHtml + streamRuntime);
|
|
857
|
-
}
|
|
858
|
-
wroteShell = true;
|
|
859
|
-
writeSnapshotForScopes(Array.from((0, import_internal2.__fictGetScopeRegistry)().keys()));
|
|
860
|
-
if (shellCarriesTail && tailHtml) {
|
|
861
|
-
enqueueWrite(tailHtml);
|
|
862
|
-
tailHtml = "";
|
|
863
|
-
shellCarriesTail = false;
|
|
864
|
-
}
|
|
865
|
-
markShellReady();
|
|
866
|
-
maybeFinalize();
|
|
867
|
-
} catch (err) {
|
|
868
|
-
options.onError?.(err);
|
|
869
|
-
abort(err);
|
|
870
|
-
}
|
|
871
|
-
return { shellReady, allReady, abort };
|
|
872
|
-
}
|
|
873
|
-
function resolveContainer(document, options) {
|
|
874
|
-
if (options.container) {
|
|
875
|
-
if (options.container.ownerDocument && options.container.ownerDocument !== document) {
|
|
876
|
-
throw new Error("[fict/ssr] Provided container belongs to a different document.");
|
|
877
|
-
}
|
|
878
|
-
return options.container;
|
|
879
|
-
}
|
|
880
|
-
const tag = options.containerTag ?? "div";
|
|
881
|
-
const container = document.createElement(tag);
|
|
882
|
-
if (options.containerId) {
|
|
883
|
-
container.setAttribute("id", options.containerId);
|
|
884
|
-
}
|
|
885
|
-
if (options.containerAttributes) {
|
|
886
|
-
for (const [name, value] of Object.entries(options.containerAttributes)) {
|
|
887
|
-
if (value === null || value === void 0 || value === false) continue;
|
|
888
|
-
container.setAttribute(name, value === true ? "" : String(value));
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
if (document.body) {
|
|
892
|
-
document.body.appendChild(container);
|
|
893
|
-
}
|
|
894
|
-
return container;
|
|
895
|
-
}
|
|
896
|
-
function buildStreamRuntimeScript(options) {
|
|
897
|
-
const nonce = renderNonceAttribute(options);
|
|
898
|
-
if (options.streamRuntime === "external") {
|
|
899
|
-
if (!options.streamRuntimeSrc) {
|
|
900
|
-
throw new Error('[fict/ssr] streamRuntimeSrc is required when streamRuntime is "external".');
|
|
901
|
-
}
|
|
902
|
-
return `<script${nonce} src="${escapeAttribute(options.streamRuntimeSrc)}" data-fict-stream-runtime data-fict-stream-observer></script>`;
|
|
903
|
-
}
|
|
904
|
-
return `<script${nonce}>${createStreamRuntimeCode({
|
|
905
|
-
observerMode: resolveStreamPatchMode(options) === "observer"
|
|
906
|
-
})}</script>`;
|
|
907
|
-
}
|
|
908
|
-
function buildPatchChunk(id, html, options) {
|
|
909
|
-
const template = `<template data-fict-suspense="${escapeAttribute(id)}">${html}</template>`;
|
|
910
|
-
if (resolveStreamPatchMode(options) === "observer") {
|
|
911
|
-
return template;
|
|
912
|
-
}
|
|
913
|
-
return `${template}<script${renderNonceAttribute(options)}>__FICT_STREAM.apply("${escapeScriptString(id)}")</script>`;
|
|
914
|
-
}
|
|
915
|
-
function resolveStreamPatchMode(options) {
|
|
916
|
-
if (options.streamPatchMode) return options.streamPatchMode;
|
|
917
|
-
return options.streamRuntime === "external" ? "observer" : "inline";
|
|
918
|
-
}
|
|
919
|
-
function serializeBetween(document, start, end) {
|
|
920
|
-
const wrapper = document.createElement("div");
|
|
921
|
-
let node = start.nextSibling;
|
|
922
|
-
while (node && node !== end) {
|
|
923
|
-
wrapper.appendChild(node.cloneNode(true));
|
|
924
|
-
node = node.nextSibling;
|
|
925
|
-
}
|
|
926
|
-
return wrapper.innerHTML;
|
|
927
|
-
}
|
|
928
|
-
function splitDocumentHtml(html) {
|
|
929
|
-
const lower = html.toLowerCase();
|
|
930
|
-
const idx = lower.lastIndexOf("</body>");
|
|
931
|
-
if (idx === -1) return null;
|
|
932
|
-
return { head: html.slice(0, idx), tail: html.slice(idx) };
|
|
933
|
-
}
|
|
934
|
-
function buildIncrementalSnapshotChunk(state, options) {
|
|
935
|
-
const json = serializeSnapshotForScript(state);
|
|
936
|
-
const nonce = renderNonceAttribute(options);
|
|
937
|
-
if (options.snapshotTarget === "head") {
|
|
938
|
-
const jsonLiteral = JSON.stringify(json);
|
|
939
|
-
const setNonce = options.scriptNonce !== void 0 ? `s.setAttribute('nonce',${serializeScriptStringLiteral(options.scriptNonce)});` : "";
|
|
940
|
-
return `<script${nonce}>(function(){var s=document.createElement('script');s.type='application/json';s.setAttribute('data-fict-snapshot','');${setNonce}s.textContent=${jsonLiteral};(document.head||document.documentElement).appendChild(s);}())</script>`;
|
|
941
|
-
}
|
|
942
|
-
return `<script${nonce} type="application/json" data-fict-snapshot>${json}</script>`;
|
|
943
|
-
}
|
|
944
|
-
function serializeOutput(document, container, options) {
|
|
945
|
-
if (options.fullDocument) {
|
|
946
|
-
const doctype = serializeDoctype(document, options.doctype);
|
|
947
|
-
const html = document.documentElement ? document.documentElement.outerHTML : container.outerHTML;
|
|
948
|
-
return doctype ? `${doctype}${html}` : html;
|
|
949
|
-
}
|
|
950
|
-
if (options.includeContainer) {
|
|
951
|
-
return container.outerHTML;
|
|
952
|
-
}
|
|
953
|
-
return container.innerHTML;
|
|
954
|
-
}
|
|
955
|
-
function injectSnapshot(document, container, state, options) {
|
|
956
|
-
const script = document.createElement("script");
|
|
957
|
-
script.type = "application/json";
|
|
958
|
-
script.id = options.snapshotScriptId ?? "__FICT_SNAPSHOT__";
|
|
959
|
-
if (options.scriptNonce !== void 0) {
|
|
960
|
-
script.setAttribute("nonce", options.scriptNonce);
|
|
961
|
-
}
|
|
962
|
-
script.textContent = serializeSnapshotForScript(state);
|
|
963
|
-
if (options.fullDocument) {
|
|
964
|
-
if (options.snapshotTarget === "head" && document.head) {
|
|
965
|
-
document.head.appendChild(script);
|
|
966
|
-
return;
|
|
967
|
-
}
|
|
968
|
-
if (document.body) {
|
|
969
|
-
document.body.appendChild(script);
|
|
970
|
-
return;
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
const target = options.snapshotTarget ?? "container";
|
|
974
|
-
if (target === "body" && document.body) {
|
|
975
|
-
document.body.appendChild(script);
|
|
976
|
-
return;
|
|
977
|
-
}
|
|
978
|
-
if (target === "head" && document.head) {
|
|
979
|
-
document.head.appendChild(script);
|
|
980
|
-
return;
|
|
981
|
-
}
|
|
982
|
-
container.appendChild(script);
|
|
983
|
-
}
|
|
984
|
-
function serializeSnapshotForScript(state) {
|
|
985
|
-
return JSON.stringify(state).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
986
|
-
}
|
|
987
|
-
function serializeScriptStringLiteral(value) {
|
|
988
|
-
return JSON.stringify(value).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
989
|
-
}
|
|
990
|
-
function renderNonceAttribute(options) {
|
|
991
|
-
return options.scriptNonce === void 0 ? "" : ` nonce="${escapeAttribute(options.scriptNonce)}"`;
|
|
992
|
-
}
|
|
993
|
-
function escapeAttribute(value) {
|
|
994
|
-
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
995
|
-
}
|
|
996
|
-
function escapeScriptString(value) {
|
|
997
|
-
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
998
|
-
}
|
|
999
|
-
function serializeDoctype(document, override) {
|
|
1000
|
-
if (override === null) return "";
|
|
1001
|
-
if (override !== void 0) return override;
|
|
1002
|
-
const doctype = document.doctype;
|
|
1003
|
-
if (!doctype) return "";
|
|
1004
|
-
const name = doctype.name || "html";
|
|
1005
|
-
const publicId = doctype.publicId;
|
|
1006
|
-
const systemId = doctype.systemId;
|
|
1007
|
-
let id = "";
|
|
1008
|
-
if (publicId) {
|
|
1009
|
-
id = ` PUBLIC "${publicId}"`;
|
|
1010
|
-
if (systemId) {
|
|
1011
|
-
id += ` "${systemId}"`;
|
|
1012
|
-
}
|
|
1013
|
-
} else if (systemId) {
|
|
1014
|
-
id = ` SYSTEM "${systemId}"`;
|
|
1015
|
-
}
|
|
1016
|
-
return `<!DOCTYPE ${name}${id}>`;
|
|
1017
|
-
}
|
|
1018
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
1019
|
-
0 && (module.exports = {
|
|
1020
|
-
createSSRDocument,
|
|
1021
|
-
renderToDocument,
|
|
1022
|
-
renderToPipeableStream,
|
|
1023
|
-
renderToStream,
|
|
1024
|
-
renderToString,
|
|
1025
|
-
renderToStringAsync
|
|
1026
|
-
});
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_render_core = require("./render-core-CWudV9yi.cjs");
|
|
3
|
+
exports.createSSRDocument = require_render_core.createSSRDocument;
|
|
4
|
+
exports.renderToDocument = require_render_core.renderToDocument;
|
|
5
|
+
exports.renderToPipeableStream = require_render_core.renderToPipeableStream;
|
|
6
|
+
exports.renderToStream = require_render_core.renderToStream;
|
|
7
|
+
exports.renderToString = require_render_core.renderToString;
|
|
8
|
+
exports.renderToStringAsync = require_render_core.renderToStringAsync;
|