@bleedingdev/modern-js-runtime-extensions 3.8.3-ultramodern.2
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/LICENSE +21 -0
- package/dist/cjs/helmetCompat.js +419 -0
- package/dist/cjs/html.js +45 -0
- package/dist/cjs/index.js +83 -0
- package/dist/cjs/navigationWarmup.js +59 -0
- package/dist/cjs/node.js +143 -0
- package/dist/cjs/rendererHead.js +263 -0
- package/dist/cjs/rscHtmlStream.js +128 -0
- package/dist/cjs/safeFailure.js +125 -0
- package/dist/cjs/shellStream.js +76 -0
- package/dist/esm/helmetCompat.mjs +357 -0
- package/dist/esm/html.mjs +4 -0
- package/dist/esm/index.mjs +4 -0
- package/dist/esm/navigationWarmup.mjs +21 -0
- package/dist/esm/node.mjs +61 -0
- package/dist/esm/rendererHead.mjs +162 -0
- package/dist/esm/rscHtmlStream.mjs +86 -0
- package/dist/esm/safeFailure.mjs +78 -0
- package/dist/esm/shellStream.mjs +38 -0
- package/dist/esm-node/helmetCompat.mjs +358 -0
- package/dist/esm-node/html.mjs +5 -0
- package/dist/esm-node/index.mjs +5 -0
- package/dist/esm-node/navigationWarmup.mjs +22 -0
- package/dist/esm-node/node.mjs +62 -0
- package/dist/esm-node/rendererHead.mjs +163 -0
- package/dist/esm-node/rscHtmlStream.mjs +87 -0
- package/dist/esm-node/safeFailure.mjs +79 -0
- package/dist/esm-node/shellStream.mjs +39 -0
- package/dist/types/helmetCompat.d.ts +75 -0
- package/dist/types/html.d.ts +2 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/navigationWarmup.d.ts +8 -0
- package/dist/types/node.d.ts +11 -0
- package/dist/types/rendererHead.d.ts +17 -0
- package/dist/types/rscHtmlStream.d.ts +4 -0
- package/dist/types/safeFailure.d.ts +17 -0
- package/dist/types/shellStream.d.ts +1 -0
- package/package.json +90 -0
- package/rslib.config.mts +4 -0
- package/rstest.config.mts +7 -0
- package/src/helmetCompat.ts +628 -0
- package/src/html.ts +11 -0
- package/src/index.ts +4 -0
- package/src/navigationWarmup.ts +39 -0
- package/src/node.ts +93 -0
- package/src/rendererHead.ts +236 -0
- package/src/rscHtmlStream.ts +142 -0
- package/src/safeFailure.ts +137 -0
- package/src/shellStream.ts +53 -0
- package/tests/rendererHead.test.ts +299 -0
- package/tests/rscHtmlStream.test.ts +153 -0
- package/tests/safeFailure.test.ts +154 -0
- package/tests/shellStream.test.ts +85 -0
- package/tsconfig.json +10 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
export * from "./helmetCompat.mjs";
|
|
3
|
+
const MARKER_ATTRIBUTE = 'data-modern-helmet';
|
|
4
|
+
const MARKER_PREFIX = `<template ${MARKER_ATTRIBUTE}="`;
|
|
5
|
+
const MARKER_SUFFIX = '"></template>';
|
|
6
|
+
const RECORD_ID_WIDTH = 12;
|
|
7
|
+
const MARKER_LENGTH = MARKER_PREFIX.length + 1 + 36 + RECORD_ID_WIDTH + MARKER_SUFFIX.length;
|
|
8
|
+
const HEAD_STATE = Symbol('modern.rendererHead');
|
|
9
|
+
const getState = (context)=>{
|
|
10
|
+
const headContext = context;
|
|
11
|
+
let state = headContext[HEAD_STATE];
|
|
12
|
+
if (void 0 === state) {
|
|
13
|
+
state = {
|
|
14
|
+
publishedRecords: []
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(headContext, HEAD_STATE, {
|
|
17
|
+
value: state
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return state;
|
|
21
|
+
};
|
|
22
|
+
const formatId = (value)=>value.toString(36).padStart(RECORD_ID_WIDTH, '0');
|
|
23
|
+
const createToken = (transaction)=>{
|
|
24
|
+
const token = `h${transaction.nonce}${formatId(transaction.nextRecordId)}`;
|
|
25
|
+
transaction.nextRecordId += 1;
|
|
26
|
+
return token;
|
|
27
|
+
};
|
|
28
|
+
const beginHeadRender = (context)=>{
|
|
29
|
+
const state = getState(context);
|
|
30
|
+
state.transaction = {
|
|
31
|
+
recordsByToken: new Map(),
|
|
32
|
+
committedRecords: [],
|
|
33
|
+
committedTokens: new Set(),
|
|
34
|
+
nextRecordId: 0,
|
|
35
|
+
nonce: globalThis.crypto.randomUUID(),
|
|
36
|
+
previousRecords: state.publishedRecords,
|
|
37
|
+
sealed: false
|
|
38
|
+
};
|
|
39
|
+
state.publish?.([]);
|
|
40
|
+
};
|
|
41
|
+
const collectHeadRecord = (context, createRecord, publish)=>{
|
|
42
|
+
const state = getState(context);
|
|
43
|
+
const transaction = state.transaction;
|
|
44
|
+
if (void 0 === transaction) return;
|
|
45
|
+
const hadPublisher = void 0 !== state.publish;
|
|
46
|
+
state.publish = (records)=>publish(records);
|
|
47
|
+
if (!hadPublisher) state.publish([]);
|
|
48
|
+
if (transaction.sealed) return null;
|
|
49
|
+
const token = createToken(transaction);
|
|
50
|
+
transaction.recordsByToken.set(token, createRecord());
|
|
51
|
+
return {
|
|
52
|
+
[MARKER_ATTRIBUTE]: token
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
const collectHeadState = (context, createRecord, derive, target)=>collectHeadRecord(context, createRecord, (records)=>{
|
|
56
|
+
target.helmet = derive(records);
|
|
57
|
+
});
|
|
58
|
+
const consumeMarkers = (context, html)=>{
|
|
59
|
+
const transaction = getState(context).transaction;
|
|
60
|
+
if (void 0 === transaction) return html;
|
|
61
|
+
let cursor = 0;
|
|
62
|
+
let stripped = '';
|
|
63
|
+
while(cursor < html.length){
|
|
64
|
+
const start = html.indexOf(MARKER_PREFIX, cursor);
|
|
65
|
+
if (-1 === start) {
|
|
66
|
+
stripped += html.slice(cursor);
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
const tokenStart = start + MARKER_PREFIX.length;
|
|
70
|
+
const end = html.indexOf(MARKER_SUFFIX, tokenStart);
|
|
71
|
+
if (-1 === end) {
|
|
72
|
+
stripped += html.slice(cursor);
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
const token = html.slice(tokenStart, end);
|
|
76
|
+
if (transaction.recordsByToken.has(token)) {
|
|
77
|
+
stripped += html.slice(cursor, start);
|
|
78
|
+
if (!transaction.sealed && !transaction.committedTokens.has(token)) {
|
|
79
|
+
transaction.committedTokens.add(token);
|
|
80
|
+
transaction.committedRecords.push(transaction.recordsByToken.get(token));
|
|
81
|
+
}
|
|
82
|
+
} else stripped += html.slice(cursor, end + MARKER_SUFFIX.length);
|
|
83
|
+
cursor = end + MARKER_SUFFIX.length;
|
|
84
|
+
}
|
|
85
|
+
return stripped;
|
|
86
|
+
};
|
|
87
|
+
const rendererHead_publish = (context)=>{
|
|
88
|
+
const state = getState(context);
|
|
89
|
+
const records = state.transaction?.committedRecords ?? [];
|
|
90
|
+
state.publishedRecords = records;
|
|
91
|
+
state.publish?.(records);
|
|
92
|
+
};
|
|
93
|
+
const publishHeadRender = (context)=>{
|
|
94
|
+
const transaction = getState(context).transaction;
|
|
95
|
+
if (void 0 !== transaction) {
|
|
96
|
+
transaction.sealed = true;
|
|
97
|
+
rendererHead_publish(context);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
const finishHeadRender = (context)=>{
|
|
101
|
+
const state = getState(context);
|
|
102
|
+
if (void 0 !== state.transaction) {
|
|
103
|
+
if (!state.transaction.sealed) rendererHead_publish(context);
|
|
104
|
+
state.transaction = void 0;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
const completeHeadRender = (context, html)=>{
|
|
108
|
+
const stripped = consumeMarkers(context, html);
|
|
109
|
+
finishHeadRender(context);
|
|
110
|
+
return stripped;
|
|
111
|
+
};
|
|
112
|
+
const abortHeadRender = (context)=>{
|
|
113
|
+
const state = getState(context);
|
|
114
|
+
const transaction = state.transaction;
|
|
115
|
+
if (void 0 !== transaction) {
|
|
116
|
+
state.publishedRecords = transaction.previousRecords;
|
|
117
|
+
state.publish?.(transaction.previousRecords);
|
|
118
|
+
state.transaction = void 0;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
const findCarryStart = (input)=>{
|
|
122
|
+
const windowStart = Math.max(0, input.length - MARKER_LENGTH + 1);
|
|
123
|
+
const markerStart = input.lastIndexOf(MARKER_PREFIX);
|
|
124
|
+
if (markerStart >= windowStart && input.length - markerStart < MARKER_LENGTH) return markerStart;
|
|
125
|
+
const maxPrefixLength = Math.min(MARKER_PREFIX.length - 1, input.length);
|
|
126
|
+
for(let length = maxPrefixLength; length > 0; length -= 1)if (MARKER_PREFIX.startsWith(input.slice(-length))) return input.length - length;
|
|
127
|
+
return input.length;
|
|
128
|
+
};
|
|
129
|
+
const createHeadChunkProcessor = (context)=>{
|
|
130
|
+
let carry = '';
|
|
131
|
+
return {
|
|
132
|
+
push (chunk) {
|
|
133
|
+
const input = carry + chunk;
|
|
134
|
+
const carryStart = findCarryStart(input);
|
|
135
|
+
const output = consumeMarkers(context, input.slice(0, carryStart));
|
|
136
|
+
carry = input.slice(carryStart);
|
|
137
|
+
return output;
|
|
138
|
+
},
|
|
139
|
+
finish (chunk = '') {
|
|
140
|
+
const output = completeHeadRender(context, carry + chunk);
|
|
141
|
+
carry = '';
|
|
142
|
+
return output;
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
};
|
|
146
|
+
const createWebHeadMarkerStripper = (context)=>{
|
|
147
|
+
const decoder = new TextDecoder();
|
|
148
|
+
const encoder = new TextEncoder();
|
|
149
|
+
const processor = createHeadChunkProcessor(context);
|
|
150
|
+
return new TransformStream({
|
|
151
|
+
transform (chunk, controller) {
|
|
152
|
+
const output = processor.push(decoder.decode(chunk, {
|
|
153
|
+
stream: true
|
|
154
|
+
}));
|
|
155
|
+
if (output) controller.enqueue(encoder.encode(output));
|
|
156
|
+
},
|
|
157
|
+
flush (controller) {
|
|
158
|
+
const output = processor.finish(decoder.decode());
|
|
159
|
+
if (output) controller.enqueue(encoder.encode(output));
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
export { abortHeadRender, beginHeadRender, collectHeadRecord, collectHeadState, completeHeadRender, createHeadChunkProcessor, createWebHeadMarkerStripper, finishHeadRender, publishHeadRender };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
const encoder = new TextEncoder();
|
|
3
|
+
const closingTagsPattern = /<\/body>\s*<\/html>\s*$/i;
|
|
4
|
+
const closingTagsPrefixPattern = /(?:<\/body>\s*(?:<\/html>\s*|<\/h(?:t(?:m(?:l>?)?)?)?|<\/?|<)?|<\/b(?:o(?:d(?:y>?)?)?)?|<\/?|<[^>]*)$/i;
|
|
5
|
+
const RSC_HTML_CLOSING_TAIL_LIMIT = 8192;
|
|
6
|
+
function injectRSCPayload(rscStream, { injectClosingTags = true } = {}) {
|
|
7
|
+
const decoder = new TextDecoder();
|
|
8
|
+
let retainedTail = '';
|
|
9
|
+
let committedClosingBody = false;
|
|
10
|
+
let committedHtmlClose = false;
|
|
11
|
+
let committedProbe = '';
|
|
12
|
+
let flightPromise;
|
|
13
|
+
const startFlight = (controller)=>{
|
|
14
|
+
if (!flightPromise) {
|
|
15
|
+
flightPromise = writeRSCStream(rscStream, controller);
|
|
16
|
+
flightPromise.catch((error)=>controller.error(error));
|
|
17
|
+
}
|
|
18
|
+
return flightPromise;
|
|
19
|
+
};
|
|
20
|
+
const enqueueHtml = (value, controller)=>{
|
|
21
|
+
if (value) controller.enqueue(encoder.encode(value));
|
|
22
|
+
};
|
|
23
|
+
const processHtml = async (value, controller)=>{
|
|
24
|
+
if (!value) return;
|
|
25
|
+
if (committedClosingBody) {
|
|
26
|
+
const probe = committedProbe + value;
|
|
27
|
+
committedHtmlClose ||= /<\/html>/i.test(probe);
|
|
28
|
+
committedProbe = probe.slice(-6);
|
|
29
|
+
enqueueHtml(value, controller);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const html = retainedTail + value;
|
|
33
|
+
retainedTail = '';
|
|
34
|
+
const suffix = html.match(closingTagsPrefixPattern)?.[0] ?? '';
|
|
35
|
+
const prefix = suffix ? html.slice(0, -suffix.length) : html;
|
|
36
|
+
enqueueHtml(prefix, controller);
|
|
37
|
+
if (!suffix) return void startFlight(controller);
|
|
38
|
+
if (suffix.length <= RSC_HTML_CLOSING_TAIL_LIMIT) {
|
|
39
|
+
retainedTail = suffix;
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
await startFlight(controller);
|
|
43
|
+
if (/<\/body>/i.test(suffix)) {
|
|
44
|
+
committedClosingBody = true;
|
|
45
|
+
committedHtmlClose = /<\/html>/i.test(suffix);
|
|
46
|
+
committedProbe = suffix.slice(-6);
|
|
47
|
+
}
|
|
48
|
+
enqueueHtml(suffix, controller);
|
|
49
|
+
};
|
|
50
|
+
return new TransformStream({
|
|
51
|
+
async transform (chunk, controller) {
|
|
52
|
+
await processHtml(decoder.decode(chunk, {
|
|
53
|
+
stream: true
|
|
54
|
+
}), controller);
|
|
55
|
+
},
|
|
56
|
+
async flush (controller) {
|
|
57
|
+
await processHtml(decoder.decode(), controller);
|
|
58
|
+
await startFlight(controller);
|
|
59
|
+
const retainedClosingTags = closingTagsPattern.test(retainedTail);
|
|
60
|
+
enqueueHtml(retainedTail, controller);
|
|
61
|
+
if (!injectClosingTags) return;
|
|
62
|
+
if (committedClosingBody) {
|
|
63
|
+
if (!committedHtmlClose) enqueueHtml('</html>', controller);
|
|
64
|
+
} else if (!retainedClosingTags) enqueueHtml('</body></html>', controller);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
async function writeRSCStream(rscStream, controller) {
|
|
69
|
+
const decoder = new TextDecoder('utf-8', {
|
|
70
|
+
fatal: true
|
|
71
|
+
});
|
|
72
|
+
for await (const chunk of rscStream)try {
|
|
73
|
+
writeChunk(JSON.stringify(decoder.decode(chunk, {
|
|
74
|
+
stream: true
|
|
75
|
+
})), controller);
|
|
76
|
+
} catch {
|
|
77
|
+
const base64 = JSON.stringify(btoa(String.fromCodePoint(...chunk)));
|
|
78
|
+
writeChunk(`Uint8Array.from(atob(${base64}), m => m.codePointAt(0))`, controller);
|
|
79
|
+
}
|
|
80
|
+
const remaining = decoder.decode();
|
|
81
|
+
if (remaining) writeChunk(JSON.stringify(remaining), controller);
|
|
82
|
+
}
|
|
83
|
+
function writeChunk(chunk, controller) {
|
|
84
|
+
const script = `(self.__FLIGHT_DATA||=[]).push(${chunk})`;
|
|
85
|
+
controller.enqueue(encoder.encode(`<script>${script.replace(/<!--/g, '<\\!--').replace(/<\/(script)/gi, '</\\$1')}</script>`));
|
|
86
|
+
}
|
|
87
|
+
export { RSC_HTML_CLOSING_TAIL_LIMIT, injectRSCPayload };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
const SAFE_FAILURE_MESSAGES = {
|
|
3
|
+
500: 'Internal Server Error',
|
|
4
|
+
503: 'Service Unavailable'
|
|
5
|
+
};
|
|
6
|
+
const SAFE_FAILURE_CODES = {
|
|
7
|
+
500: 'INTERNAL_SERVER_ERROR',
|
|
8
|
+
503: 'SERVICE_UNAVAILABLE'
|
|
9
|
+
};
|
|
10
|
+
const readErrorProperty = (error, key)=>{
|
|
11
|
+
if ('object' != typeof error || null === error) return;
|
|
12
|
+
try {
|
|
13
|
+
return error[key];
|
|
14
|
+
} catch {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const normalizeFailureStatus = (value)=>'number' == typeof value && Number.isInteger(value) && value >= 400 && value <= 599 ? value : void 0;
|
|
19
|
+
const normalizeDelaySeconds = (value)=>{
|
|
20
|
+
if ('number' == typeof value) {
|
|
21
|
+
const seconds = Math.ceil(value);
|
|
22
|
+
return Number.isFinite(value) && value >= 0 && Number.isSafeInteger(seconds) ? String(seconds) : void 0;
|
|
23
|
+
}
|
|
24
|
+
if ('string' == typeof value) {
|
|
25
|
+
const trimmed = value.trim();
|
|
26
|
+
if (!/^\d+$/u.test(trimmed)) return;
|
|
27
|
+
const seconds = Number(trimmed);
|
|
28
|
+
return Number.isSafeInteger(seconds) ? String(seconds) : void 0;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const normalizeHttpDate = (value)=>{
|
|
32
|
+
try {
|
|
33
|
+
if (value instanceof Date) {
|
|
34
|
+
const timestamp = Date.prototype.getTime.call(value);
|
|
35
|
+
return Number.isFinite(timestamp) ? new Date(timestamp).toUTCString() : void 0;
|
|
36
|
+
}
|
|
37
|
+
if ('string' == typeof value) {
|
|
38
|
+
const trimmed = value.trim();
|
|
39
|
+
const timestamp = Date.parse(trimmed);
|
|
40
|
+
if (Number.isFinite(timestamp)) {
|
|
41
|
+
const canonical = new Date(timestamp).toUTCString();
|
|
42
|
+
return canonical === trimmed ? canonical : void 0;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
} catch {}
|
|
46
|
+
};
|
|
47
|
+
const normalizeRetryAfter = (value)=>normalizeDelaySeconds(value) ?? normalizeHttpDate(value);
|
|
48
|
+
const getSafeFailureStatus = (error)=>normalizeFailureStatus(readErrorProperty(error, 'status')) ?? normalizeFailureStatus(readErrorProperty(error, 'statusCode')) ?? 500;
|
|
49
|
+
const createSafeFailureHttpResult = (error)=>{
|
|
50
|
+
const status = getSafeFailureStatus(error);
|
|
51
|
+
const retryAfterMs = readErrorProperty(error, 'retryAfterMs');
|
|
52
|
+
const retryAfter = 503 === status ? normalizeRetryAfter(readErrorProperty(error, 'retryAfter')) ?? normalizeRetryAfter(readErrorProperty(error, 'retryAfterSeconds')) ?? normalizeRetryAfter('number' == typeof retryAfterMs ? retryAfterMs / 1000 : void 0) : void 0;
|
|
53
|
+
return {
|
|
54
|
+
status,
|
|
55
|
+
body: {
|
|
56
|
+
success: false,
|
|
57
|
+
error: {
|
|
58
|
+
code: SAFE_FAILURE_CODES[status] ?? 'REQUEST_FAILED',
|
|
59
|
+
message: SAFE_FAILURE_MESSAGES[status] ?? 'Request failed',
|
|
60
|
+
status
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
headers: {
|
|
64
|
+
'content-type': 'application/json; charset=utf-8',
|
|
65
|
+
...void 0 === retryAfter ? {} : {
|
|
66
|
+
'Retry-After': retryAfter
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
const createSafeJsonFailureResponse = (error)=>{
|
|
72
|
+
const result = createSafeFailureHttpResult(error);
|
|
73
|
+
return new Response(JSON.stringify(result.body), {
|
|
74
|
+
status: result.status,
|
|
75
|
+
headers: result.headers
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
const createSafeFailureResponse = createSafeJsonFailureResponse;
|
|
79
|
+
export { createSafeFailureHttpResult, createSafeFailureResponse, createSafeJsonFailureResponse, getSafeFailureStatus };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
import { abortHeadRender, createHeadChunkProcessor } from "./rendererHead.mjs";
|
|
3
|
+
const MISSING_SHELL_MARKER_ERROR = 'React SSR stream ended before the shell marker';
|
|
4
|
+
const createConservingWebShellStream = (source, context, marker, onMissingMarker)=>{
|
|
5
|
+
const decoder = new TextDecoder();
|
|
6
|
+
const encoder = new TextEncoder();
|
|
7
|
+
const headProcessor = createHeadChunkProcessor(context);
|
|
8
|
+
let markerCarry = '';
|
|
9
|
+
let markerFound = false;
|
|
10
|
+
const observeMarker = (chunk)=>{
|
|
11
|
+
if (markerFound) return;
|
|
12
|
+
const candidate = markerCarry + chunk;
|
|
13
|
+
markerFound = candidate.includes(marker);
|
|
14
|
+
markerCarry = markerFound ? '' : candidate.slice(-(marker.length - 1));
|
|
15
|
+
};
|
|
16
|
+
return source.pipeThrough(new TransformStream({
|
|
17
|
+
transform (chunk, controller) {
|
|
18
|
+
const decoded = decoder.decode(chunk, {
|
|
19
|
+
stream: true
|
|
20
|
+
});
|
|
21
|
+
observeMarker(decoded);
|
|
22
|
+
const output = headProcessor.push(decoded);
|
|
23
|
+
if (output) controller.enqueue(encoder.encode(output));
|
|
24
|
+
},
|
|
25
|
+
flush (controller) {
|
|
26
|
+
const decoded = decoder.decode();
|
|
27
|
+
observeMarker(decoded);
|
|
28
|
+
if (!markerFound) {
|
|
29
|
+
abortHeadRender(context);
|
|
30
|
+
const error = new Error(MISSING_SHELL_MARKER_ERROR);
|
|
31
|
+
onMissingMarker?.(error);
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
const output = headProcessor.finish(decoded);
|
|
35
|
+
if (output) controller.enqueue(encoder.encode(output));
|
|
36
|
+
}
|
|
37
|
+
}));
|
|
38
|
+
};
|
|
39
|
+
export { createConservingWebShellStream };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
type HeadTag = Record<string, unknown>;
|
|
2
|
+
type PriorityTagName = 'link' | 'meta' | 'script';
|
|
3
|
+
type ElementFactory<Element> = {
|
|
4
|
+
createElement: (...args: never[]) => Element;
|
|
5
|
+
};
|
|
6
|
+
type ReactAdapter<Element, Child> = ElementFactory<Element> & {
|
|
7
|
+
Children: {
|
|
8
|
+
forEach: (children: Child | undefined, visit: (child: unknown) => void) => void;
|
|
9
|
+
toArray: (children: Child | undefined) => unknown[];
|
|
10
|
+
};
|
|
11
|
+
Fragment: unknown;
|
|
12
|
+
isValidElement: (value: unknown) => boolean;
|
|
13
|
+
};
|
|
14
|
+
type HeadDatum<Element> = {
|
|
15
|
+
toComponent: () => Element[];
|
|
16
|
+
toString: () => string;
|
|
17
|
+
};
|
|
18
|
+
type HeadAttributeDatum = {
|
|
19
|
+
toComponent: () => Record<string, unknown>;
|
|
20
|
+
toString: () => string;
|
|
21
|
+
};
|
|
22
|
+
type MarkerProps = Record<'data-modern-helmet', string>;
|
|
23
|
+
export type HelmetCompatProps<Child = unknown> = {
|
|
24
|
+
base?: object;
|
|
25
|
+
bodyAttributes?: object;
|
|
26
|
+
children?: Child;
|
|
27
|
+
defaultTitle?: string;
|
|
28
|
+
htmlAttributes?: object;
|
|
29
|
+
link?: object[];
|
|
30
|
+
meta?: object[];
|
|
31
|
+
noscript?: object[];
|
|
32
|
+
prioritizeSeoTags?: boolean;
|
|
33
|
+
script?: object[];
|
|
34
|
+
style?: object[];
|
|
35
|
+
title?: unknown;
|
|
36
|
+
titleAttributes?: object;
|
|
37
|
+
titleTemplate?: string;
|
|
38
|
+
};
|
|
39
|
+
export type HelmetCompatRecord = {
|
|
40
|
+
base: HeadTag[];
|
|
41
|
+
bodyAttributes?: HeadTag;
|
|
42
|
+
defaultTitle?: string;
|
|
43
|
+
htmlAttributes?: HeadTag;
|
|
44
|
+
link: HeadTag[];
|
|
45
|
+
meta: HeadTag[];
|
|
46
|
+
noscript: HeadTag[];
|
|
47
|
+
prioritizeSeoTags: boolean;
|
|
48
|
+
script: HeadTag[];
|
|
49
|
+
style: HeadTag[];
|
|
50
|
+
title?: string;
|
|
51
|
+
titleAttributes: HeadTag;
|
|
52
|
+
titleTemplate?: string;
|
|
53
|
+
};
|
|
54
|
+
export type HelmetCompatState<Element> = {
|
|
55
|
+
base: HeadDatum<Element>;
|
|
56
|
+
bodyAttributes: HeadAttributeDatum;
|
|
57
|
+
htmlAttributes: HeadAttributeDatum;
|
|
58
|
+
link: HeadDatum<Element>;
|
|
59
|
+
meta: HeadDatum<Element>;
|
|
60
|
+
noscript: HeadDatum<Element>;
|
|
61
|
+
priority: HeadDatum<Element>;
|
|
62
|
+
script: HeadDatum<Element>;
|
|
63
|
+
style: HeadDatum<Element>;
|
|
64
|
+
title: HeadDatum<Element>;
|
|
65
|
+
};
|
|
66
|
+
export declare const renderHeadMarker: <Element>(react: ElementFactory<Element>, marker: MarkerProps | null | undefined) => Element | null;
|
|
67
|
+
export declare const markPriorityHeadRecord: (record: object, prioritize: boolean | undefined) => void;
|
|
68
|
+
export declare const isPriorityHeadTag: (tag: object) => boolean;
|
|
69
|
+
export declare const headTagsToComponents: <Element>(react: ElementFactory<Element>, tagName: string, tags: HeadTag[]) => Element[];
|
|
70
|
+
export declare const headTitleToComponent: <Element>(react: ElementFactory<Element>, title: string | undefined, attributes: HeadTag) => Element[];
|
|
71
|
+
export declare const createPriorityHeadDatum: <Element, RecordType>(react: ElementFactory<Element>, records: RecordType[], dedupe: (tagName: PriorityTagName, records: RecordType[]) => HeadTag[]) => HeadDatum<Element>;
|
|
72
|
+
export declare const createHelmetRecord: <Element, Child>(reactValue: ReactAdapter<Element, Child>, props: HelmetCompatProps<Child>) => HelmetCompatRecord;
|
|
73
|
+
export declare const deriveHelmetServerState: <Element>(react: ElementFactory<Element>, records: HelmetCompatRecord[]) => HelmetCompatState<Element>;
|
|
74
|
+
export declare const collectImmediateHelmetState: <Element>(react: ElementFactory<Element>, context: object, record: HelmetCompatRecord) => HelmetCompatState<Element>;
|
|
75
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
type ChunkLoaderIdentity = object;
|
|
2
|
+
/**
|
|
3
|
+
* Namespaces route warmups by the identities that give a chunk id meaning.
|
|
4
|
+
* Weak maps ensure a retired app context or bundler runtime is not retained by
|
|
5
|
+
* the shared prefetch cache. The public path is the final, value-based scope.
|
|
6
|
+
*/
|
|
7
|
+
export declare function getNavigationWarmupCacheKey(runtimeContext: object, chunkLoader: ChunkLoaderIdentity, publicPath: string, warmupKey: string): string;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Transform } from 'node:stream';
|
|
2
|
+
export * from './rendererHead';
|
|
3
|
+
export declare const createNodeHeadMarkerStripper: (context: object, terminalMarker?: string) => Transform;
|
|
4
|
+
export declare const createOnceErrorReporter: (onError?: (error: unknown) => void) => ((error: unknown) => void);
|
|
5
|
+
export declare const pipeNodeHeadStream: (options: {
|
|
6
|
+
source: NodeJS.ReadWriteStream;
|
|
7
|
+
destination: NodeJS.ReadWriteStream;
|
|
8
|
+
context: object;
|
|
9
|
+
terminalMarker?: string;
|
|
10
|
+
onError?: (error: Error) => void;
|
|
11
|
+
}) => void;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
declare const MARKER_ATTRIBUTE = "data-modern-helmet";
|
|
2
|
+
export type RendererHeadMarkerProps = Record<typeof MARKER_ATTRIBUTE, string>;
|
|
3
|
+
export * from './helmetCompat';
|
|
4
|
+
export declare const beginHeadRender: (context: object) => void;
|
|
5
|
+
export declare const collectHeadRecord: <RecordType>(context: object, createRecord: () => RecordType, publish: (records: RecordType[]) => void) => RendererHeadMarkerProps | null | undefined;
|
|
6
|
+
export declare const collectHeadState: <RecordType, StateType>(context: object, createRecord: () => RecordType, derive: (records: RecordType[]) => StateType, target: {
|
|
7
|
+
helmet?: StateType;
|
|
8
|
+
}) => RendererHeadMarkerProps | null | undefined;
|
|
9
|
+
export declare const publishHeadRender: (context: object) => void;
|
|
10
|
+
export declare const finishHeadRender: (context: object) => void;
|
|
11
|
+
export declare const completeHeadRender: (context: object, html: string) => string;
|
|
12
|
+
export declare const abortHeadRender: (context: object) => void;
|
|
13
|
+
export declare const createHeadChunkProcessor: (context: object) => {
|
|
14
|
+
push(chunk: string): string;
|
|
15
|
+
finish(chunk?: string): string;
|
|
16
|
+
};
|
|
17
|
+
export declare const createWebHeadMarkerStripper: (context: object) => TransformStream<Uint8Array, Uint8Array>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type SafeFailureEnvelope = {
|
|
2
|
+
success: false;
|
|
3
|
+
error: {
|
|
4
|
+
code: string;
|
|
5
|
+
message: string;
|
|
6
|
+
status: number;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
export type SafeFailureHttpResult = {
|
|
10
|
+
status: number;
|
|
11
|
+
body: SafeFailureEnvelope;
|
|
12
|
+
headers: Record<string, string>;
|
|
13
|
+
};
|
|
14
|
+
export declare const getSafeFailureStatus: (error: unknown) => number;
|
|
15
|
+
export declare const createSafeFailureHttpResult: (error: unknown) => SafeFailureHttpResult;
|
|
16
|
+
export declare const createSafeJsonFailureResponse: (error: unknown) => Response;
|
|
17
|
+
export declare const createSafeFailureResponse: typeof createSafeJsonFailureResponse;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const createConservingWebShellStream: (source: ReadableStream<Uint8Array>, context: object, marker: string, onMissingMarker?: (error: Error) => void) => ReadableStream<Uint8Array>;
|
package/package.json
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bleedingdev/modern-js-runtime-extensions",
|
|
3
|
+
"description": "Fork-owned UltraModern.js runtime transaction, streaming, and safe failure primitives.",
|
|
4
|
+
"homepage": "https://github.com/BleedingDev/ultramodern.js#readme",
|
|
5
|
+
"bugs": {
|
|
6
|
+
"url": "https://github.com/BleedingDev/ultramodern.js/issues"
|
|
7
|
+
},
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/BleedingDev/ultramodern.js.git",
|
|
11
|
+
"directory": "packages/runtime/runtime-extensions"
|
|
12
|
+
},
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"version": "3.8.3-ultramodern.2",
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=26.7.0"
|
|
17
|
+
},
|
|
18
|
+
"types": "./dist/types/index.d.ts",
|
|
19
|
+
"main": "./dist/cjs/index.js",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/types/index.d.ts",
|
|
23
|
+
"node": {
|
|
24
|
+
"import": "./dist/esm-node/index.mjs",
|
|
25
|
+
"require": "./dist/cjs/index.js"
|
|
26
|
+
},
|
|
27
|
+
"default": "./dist/esm/index.mjs"
|
|
28
|
+
},
|
|
29
|
+
"./node": {
|
|
30
|
+
"types": "./dist/types/node.d.ts",
|
|
31
|
+
"node": {
|
|
32
|
+
"import": "./dist/esm-node/node.mjs",
|
|
33
|
+
"require": "./dist/cjs/node.js"
|
|
34
|
+
},
|
|
35
|
+
"default": "./dist/esm-node/node.mjs"
|
|
36
|
+
},
|
|
37
|
+
"./rsc-html-stream": {
|
|
38
|
+
"types": "./dist/types/rscHtmlStream.d.ts",
|
|
39
|
+
"node": {
|
|
40
|
+
"import": "./dist/esm-node/rscHtmlStream.mjs",
|
|
41
|
+
"require": "./dist/cjs/rscHtmlStream.js"
|
|
42
|
+
},
|
|
43
|
+
"default": "./dist/esm-node/rscHtmlStream.mjs"
|
|
44
|
+
},
|
|
45
|
+
"./safe-failure": {
|
|
46
|
+
"types": "./dist/types/safeFailure.d.ts",
|
|
47
|
+
"node": {
|
|
48
|
+
"import": "./dist/esm-node/safeFailure.mjs",
|
|
49
|
+
"require": "./dist/cjs/safeFailure.js"
|
|
50
|
+
},
|
|
51
|
+
"default": "./dist/esm/safeFailure.mjs"
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"typesVersions": {
|
|
55
|
+
"*": {
|
|
56
|
+
".": [
|
|
57
|
+
"./dist/types/index.d.ts",
|
|
58
|
+
"./src/index.ts"
|
|
59
|
+
],
|
|
60
|
+
"node": [
|
|
61
|
+
"./dist/types/node.d.ts",
|
|
62
|
+
"./src/node.ts"
|
|
63
|
+
],
|
|
64
|
+
"rsc-html-stream": [
|
|
65
|
+
"./dist/types/rscHtmlStream.d.ts",
|
|
66
|
+
"./src/rscHtmlStream.ts"
|
|
67
|
+
],
|
|
68
|
+
"safe-failure": [
|
|
69
|
+
"./dist/types/safeFailure.d.ts",
|
|
70
|
+
"./src/safeFailure.ts"
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"devDependencies": {
|
|
75
|
+
"@rslib/core": "0.23.2",
|
|
76
|
+
"@scripts/rstest-config": "2.66.0",
|
|
77
|
+
"@types/node": "^26.2.0",
|
|
78
|
+
"@typescript/native-preview": "7.0.0-dev.20260707.2",
|
|
79
|
+
"typescript": "^7.0.2"
|
|
80
|
+
},
|
|
81
|
+
"sideEffects": false,
|
|
82
|
+
"publishConfig": {
|
|
83
|
+
"access": "public"
|
|
84
|
+
},
|
|
85
|
+
"scripts": {
|
|
86
|
+
"dev": "rslib build --watch",
|
|
87
|
+
"build": "rslib build",
|
|
88
|
+
"test": "rstest"
|
|
89
|
+
}
|
|
90
|
+
}
|
package/rslib.config.mts
ADDED