@bleedingdev/modern-js-render 3.8.3-ultramodern.1 → 3.8.3-ultramodern.3

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.
@@ -1,6 +1,8 @@
1
+ import { injectRSCPayload } from "@modern-js/runtime-extensions/rsc-html-stream";
1
2
  const encoder = new TextEncoder();
2
3
  const closingTagsPattern = /<\/body>\s*<\/html>\s*$/i;
3
- function injectRSCPayload(rscStream, { injectClosingTags = true }) {
4
+ function server_injectRSCPayload(rscStream, { injectClosingTags = true }) {
5
+ if (injectClosingTags) return injectRSCPayload(rscStream);
4
6
  const decoder = new TextDecoder();
5
7
  let resolveFlightDataPromise;
6
8
  const flightDataPromise = new Promise((resolve)=>resolveFlightDataPromise = resolve);
@@ -107,4 +109,4 @@ function injectCSS(cssFiles, { injectClosingTags = true } = {}) {
107
109
  }
108
110
  });
109
111
  }
110
- export { injectCSS, injectRSCPayload };
112
+ export { injectCSS, server_injectRSCPayload as injectRSCPayload };
package/dist/esm/rsc.mjs CHANGED
@@ -1,7 +1,9 @@
1
+ import { injectRSCPayload } from "@modern-js/runtime-extensions/rsc-html-stream";
1
2
  import { decodeReply as server_node_decodeReply, loadServerAction as server_node_loadServerAction, registerClientReference, registerServerReference, renderToReadableStream } from "react-server-dom-rspack/server.node";
2
3
  const server_encoder = new TextEncoder();
3
4
  const closingTagsPattern = /<\/body>\s*<\/html>\s*$/i;
4
- function injectRSCPayload(rscStream, { injectClosingTags = true }) {
5
+ function server_injectRSCPayload(rscStream, { injectClosingTags = true }) {
6
+ if (injectClosingTags) return injectRSCPayload(rscStream);
5
7
  const decoder = new TextDecoder();
6
8
  let resolveFlightDataPromise;
7
9
  const flightDataPromise = new Promise((resolve)=>resolveFlightDataPromise = resolve);
@@ -123,7 +125,7 @@ const createRenderCSRWithRSC = (renderRsc)=>async (options)=>{
123
125
  });
124
126
  const stream = htmlStream.pipeThrough(injectCSS(cssFiles, {
125
127
  injectClosingTags: false
126
- })).pipeThrough(injectRSCPayload(rscPayloadStream, {
128
+ })).pipeThrough(server_injectRSCPayload(rscPayloadStream, {
127
129
  injectClosingTags: true
128
130
  }));
129
131
  return new Response(stream, {
@@ -133,12 +135,49 @@ const createRenderCSRWithRSC = (renderRsc)=>async (options)=>{
133
135
  })
134
136
  });
135
137
  };
138
+ const MAX_ACTION_BODY_BYTES = 1048576;
139
+ const readBoundedBody = async (req)=>{
140
+ const reader = req.body?.getReader();
141
+ if (!reader) return new Uint8Array();
142
+ const bytes = new Uint8Array(MAX_ACTION_BODY_BYTES);
143
+ let size = 0;
144
+ try {
145
+ while(true){
146
+ const { done, value } = await reader.read();
147
+ if (done) return bytes.slice(0, size);
148
+ if (value.byteLength > MAX_ACTION_BODY_BYTES - size) {
149
+ reader.cancel().catch(()=>{});
150
+ return null;
151
+ }
152
+ bytes.set(value, size);
153
+ size += value.byteLength;
154
+ }
155
+ } finally{
156
+ reader.releaseLock();
157
+ }
158
+ };
136
159
  const createHandleAction = ({ decodeReply, loadServerAction, renderRsc })=>async (req)=>{
160
+ if ('POST' !== req.method) return new Response('Method not allowed', {
161
+ status: 405,
162
+ headers: {
163
+ Allow: 'POST'
164
+ }
165
+ });
137
166
  try {
138
167
  const serverReference = req.headers.get('x-rsc-action');
139
168
  if (!serverReference) return new Response('Cannot find server reference', {
140
169
  status: 404
141
170
  });
171
+ const declaredLength = req.headers.get('content-length');
172
+ if (null !== declaredLength) {
173
+ const parsedLength = Number(declaredLength);
174
+ if (!/^\d+$/.test(declaredLength) || !Number.isSafeInteger(parsedLength)) return new Response('Invalid server action request', {
175
+ status: 400
176
+ });
177
+ if (parsedLength > MAX_ACTION_BODY_BYTES) return new Response('Server action payload too large', {
178
+ status: 413
179
+ });
180
+ }
142
181
  const action = loadServerAction(serverReference);
143
182
  if ('function' != typeof action) {
144
183
  console.error('[RSC] Invalid action: server reference is not a function, serverReference:', serverReference);
@@ -149,11 +188,21 @@ const createHandleAction = ({ decodeReply, loadServerAction, renderRsc })=>async
149
188
  const contentType = req.headers.get('content-type');
150
189
  let args;
151
190
  try {
191
+ const body = await readBoundedBody(req);
192
+ if (null === body) return new Response('Server action payload too large', {
193
+ status: 413
194
+ });
152
195
  if (contentType?.includes('multipart/form-data')) {
153
- const formData = await req.formData();
196
+ const formData = await new Response(body, {
197
+ headers: {
198
+ 'Content-Type': contentType
199
+ }
200
+ }).formData();
154
201
  args = await decodeReply(formData);
155
202
  } else {
156
- const text = await req.text();
203
+ const text = new TextDecoder('utf-8', {
204
+ fatal: true
205
+ }).decode(body);
157
206
  args = await decodeReply(text);
158
207
  }
159
208
  } catch (error) {
@@ -1,7 +1,9 @@
1
1
  import { decodeReply as server_edge_decodeReply, loadServerAction as server_edge_loadServerAction, registerClientReference, registerServerReference, renderToReadableStream } from "react-server-dom-rspack/server.edge";
2
+ import { injectRSCPayload } from "@modern-js/runtime-extensions/rsc-html-stream";
2
3
  const server_encoder = new TextEncoder();
3
4
  const closingTagsPattern = /<\/body>\s*<\/html>\s*$/i;
4
- function injectRSCPayload(rscStream, { injectClosingTags = true }) {
5
+ function server_injectRSCPayload(rscStream, { injectClosingTags = true }) {
6
+ if (injectClosingTags) return injectRSCPayload(rscStream);
5
7
  const decoder = new TextDecoder();
6
8
  let resolveFlightDataPromise;
7
9
  const flightDataPromise = new Promise((resolve)=>resolveFlightDataPromise = resolve);
@@ -123,7 +125,7 @@ const createRenderCSRWithRSC = (renderRsc)=>async (options)=>{
123
125
  });
124
126
  const stream = htmlStream.pipeThrough(injectCSS(cssFiles, {
125
127
  injectClosingTags: false
126
- })).pipeThrough(injectRSCPayload(rscPayloadStream, {
128
+ })).pipeThrough(server_injectRSCPayload(rscPayloadStream, {
127
129
  injectClosingTags: true
128
130
  }));
129
131
  return new Response(stream, {
@@ -133,12 +135,49 @@ const createRenderCSRWithRSC = (renderRsc)=>async (options)=>{
133
135
  })
134
136
  });
135
137
  };
138
+ const MAX_ACTION_BODY_BYTES = 1048576;
139
+ const readBoundedBody = async (req)=>{
140
+ const reader = req.body?.getReader();
141
+ if (!reader) return new Uint8Array();
142
+ const bytes = new Uint8Array(MAX_ACTION_BODY_BYTES);
143
+ let size = 0;
144
+ try {
145
+ while(true){
146
+ const { done, value } = await reader.read();
147
+ if (done) return bytes.slice(0, size);
148
+ if (value.byteLength > MAX_ACTION_BODY_BYTES - size) {
149
+ reader.cancel().catch(()=>{});
150
+ return null;
151
+ }
152
+ bytes.set(value, size);
153
+ size += value.byteLength;
154
+ }
155
+ } finally{
156
+ reader.releaseLock();
157
+ }
158
+ };
136
159
  const createHandleAction = ({ decodeReply, loadServerAction, renderRsc })=>async (req)=>{
160
+ if ('POST' !== req.method) return new Response('Method not allowed', {
161
+ status: 405,
162
+ headers: {
163
+ Allow: 'POST'
164
+ }
165
+ });
137
166
  try {
138
167
  const serverReference = req.headers.get('x-rsc-action');
139
168
  if (!serverReference) return new Response('Cannot find server reference', {
140
169
  status: 404
141
170
  });
171
+ const declaredLength = req.headers.get('content-length');
172
+ if (null !== declaredLength) {
173
+ const parsedLength = Number(declaredLength);
174
+ if (!/^\d+$/.test(declaredLength) || !Number.isSafeInteger(parsedLength)) return new Response('Invalid server action request', {
175
+ status: 400
176
+ });
177
+ if (parsedLength > MAX_ACTION_BODY_BYTES) return new Response('Server action payload too large', {
178
+ status: 413
179
+ });
180
+ }
142
181
  const action = loadServerAction(serverReference);
143
182
  if ('function' != typeof action) {
144
183
  console.error('[RSC] Invalid action: server reference is not a function, serverReference:', serverReference);
@@ -149,11 +188,21 @@ const createHandleAction = ({ decodeReply, loadServerAction, renderRsc })=>async
149
188
  const contentType = req.headers.get('content-type');
150
189
  let args;
151
190
  try {
191
+ const body = await readBoundedBody(req);
192
+ if (null === body) return new Response('Server action payload too large', {
193
+ status: 413
194
+ });
152
195
  if (contentType?.includes('multipart/form-data')) {
153
- const formData = await req.formData();
196
+ const formData = await new Response(body, {
197
+ headers: {
198
+ 'Content-Type': contentType
199
+ }
200
+ }).formData();
154
201
  args = await decodeReply(formData);
155
202
  } else {
156
- const text = await req.text();
203
+ const text = new TextDecoder('utf-8', {
204
+ fatal: true
205
+ }).decode(body);
157
206
  args = await decodeReply(text);
158
207
  }
159
208
  } catch (error) {
package/package.json CHANGED
@@ -17,18 +17,19 @@
17
17
  "modern",
18
18
  "modern.js"
19
19
  ],
20
- "version": "3.8.3-ultramodern.1",
20
+ "version": "3.8.3-ultramodern.3",
21
21
  "files": [
22
22
  "dist",
23
23
  "hook.d.ts"
24
24
  ],
25
25
  "dependencies": {
26
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.8.3-ultramodern.1",
27
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.8.3-ultramodern.1",
26
+ "@modern-js/runtime-extensions": "npm:@bleedingdev/modern-js-runtime-extensions@3.8.3-ultramodern.3",
27
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.8.3-ultramodern.3",
28
+ "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.8.3-ultramodern.3",
28
29
  "@swc/helpers": "^0.5.23"
29
30
  },
30
31
  "devDependencies": {
31
- "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.8.3-ultramodern.1",
32
+ "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.8.3-ultramodern.3",
32
33
  "@rslib/core": "0.23.2",
33
34
  "@scripts/build": "2.66.0",
34
35
  "@scripts/rstest-config": "2.66.0",
@@ -91,7 +92,7 @@
91
92
  },
92
93
  "scripts": {
93
94
  "dev": "rslib build --watch",
94
- "build": "rslib build && pnpm -w tsgo:dts \"$PWD\"",
95
+ "build": "rslib build",
95
96
  "test": "tsgo --noEmit -p tsconfig.json && rstest --passWithNoTests"
96
97
  }
97
98
  }