elysia 1.4.29 → 1.4.31

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/SECURITY.md ADDED
@@ -0,0 +1,27 @@
1
+ # Security Policy
2
+
3
+ **Do not open a public issue for security vulnerabilities.**
4
+
5
+ Please report privately via [GitHub Security Advisories](https://github.com/elysiajs/elysia/security/advisories/new).
6
+
7
+ Please include:
8
+
9
+ - A description of the vulnerability and its impact
10
+ - A minimal reproduction (an `Elysia` app + the request that triggers it)
11
+ - The affected version(s)
12
+
13
+ You should receive an initial response within 7 days. Once confirmed, a fix will be developed privately and released with a coordinated advisory. Credit is given to reporters unless anonymity is requested.
14
+
15
+ The advisory will be published publicly once patch is published after ~7 days and the issue will be closed. If you have any questions, please contact maintainers eg. [saltyaom@gmail.com](mailto:saltyaom@gmail.com).
16
+
17
+ ## Scope
18
+
19
+ In scope:
20
+
21
+ - The `elysia` package (this repository)
22
+
23
+ Out of scope:
24
+
25
+ - Official plugins and ecosystem packages (report to their own repositories under [elysiajs](https://github.com/elysiajs))
26
+ - Vulnerabilities in Bun or other runtimes
27
+ - Issues requiring a malicious or misconfigured application (e.g. developer-controlled schemas or handlers), unless they cross a trust boundary for end-user input
@@ -19,7 +19,11 @@ __export(compose_exports, {
19
19
  });
20
20
  module.exports = __toCommonJS(compose_exports);
21
21
  var import_handler = require('./handler.js'), import_sucrose = require('../../sucrose.js'), import_compose = require('../../compose.js'), import_utils = require('../../utils.js'), import_error = require('../../error.js'), import_trace = require('../../trace.js');
22
- const allocateIf = (value, condition) => condition ? value : "", createContext = (app, route, inference, isInline = !1) => {
22
+ const allocateIf = (value, condition) => condition ? value : "", preserveEncodedSlashes = (params) => {
23
+ for (const key of Object.keys(params))
24
+ params[key].includes("/") && (params[key] = params[key].replaceAll("/", "%2F"));
25
+ return params;
26
+ }, createContext = (app, route, inference, isInline = !1) => {
23
27
  let fnLiteral = "";
24
28
  const defaultHeaders = app.setHeaders, hasTrace = !!app.event.trace?.length;
25
29
  hasTrace && (fnLiteral += `const id=randomId()
@@ -31,8 +35,8 @@ const allocateIf = (value, condition) => condition ? value : "", createContext =
31
35
  needsQuery && (fnLiteral += getQi);
32
36
  const getPath = inference.path ? isDynamic ? "get path(){" + (needsQuery ? "" : getQi) + `if(qi===-1)return u.substring(s)
33
37
  return u.substring(s,qi)
34
- },` : `path:'${route.path}',` : "";
35
- fnLiteral += allocateIf("const c=", !isInline) + "{request,store," + allocateIf("qi,", needsQuery) + allocateIf("params:request.params,", isDynamic) + getPath + allocateIf(
38
+ },` : "path:route," : "";
39
+ fnLiteral += allocateIf("const c=", !isInline) + "{request,store," + allocateIf("qi,", needsQuery) + allocateIf("params:preserveEncodedSlashes(request.params),", isDynamic) + getPath + allocateIf(
36
40
  "url:request.url,",
37
41
  hasTrace || inference.url || needsQuery
38
42
  ) + "redirect,status,set:{headers:" + ((0, import_utils.isNotEmpty)(defaultHeaders) ? "Object.assign({},app.setHeaders)" : "Object.create(null)") + ",status:200}", inference.server && (fnLiteral += ",get server(){return app.getServer()}"), hasTrace && (fnLiteral += ",[ELYSIA_REQUEST_ID]:id");
@@ -57,7 +61,7 @@ return u.substring(s,qi)
57
61
  },
58
62
  inference
59
63
  );
60
- let fnLiteral = "const handler=data.handler,app=data.app,store=data.store,decorator=data.decorator,redirect=data.redirect,route=data.route,mapEarlyResponse=data.mapEarlyResponse," + allocateIf("randomId=data.randomId,", hasTrace) + allocateIf("ELYSIA_REQUEST_ID=data.ELYSIA_REQUEST_ID,", hasTrace) + allocateIf("ELYSIA_TRACE=data.ELYSIA_TRACE,", hasTrace) + allocateIf("trace=data.trace,", hasTrace) + allocateIf("hoc=data.hoc,", hasHoc) + `status=data.status
64
+ let fnLiteral = "const handler=data.handler,app=data.app,store=data.store,decorator=data.decorator,preserveEncodedSlashes=data.preserveEncodedSlashes,redirect=data.redirect,route=data.route,mapEarlyResponse=data.mapEarlyResponse," + allocateIf("randomId=data.randomId,", hasTrace) + allocateIf("ELYSIA_REQUEST_ID=data.ELYSIA_REQUEST_ID,", hasTrace) + allocateIf("ELYSIA_TRACE=data.ELYSIA_TRACE,", hasTrace) + allocateIf("trace=data.trace,", hasTrace) + allocateIf("hoc=data.hoc,", hasHoc) + `status=data.status
61
65
  `;
62
66
  app.event.request?.length && (fnLiteral += `const onRequest=app.event.request.map(x=>x.fn)
63
67
  `), fnLiteral += `${app.event.request?.find(import_compose.isAsync) ? "async" : ""} function map(request){`;
@@ -76,6 +80,7 @@ return u.substring(s,qi)
76
80
  hoc: app.extender.higherOrderFunctions.map((x) => x.fn),
77
81
  store: app.store,
78
82
  decorator: app.decorator,
83
+ preserveEncodedSlashes,
79
84
  route: route.path,
80
85
  randomId: hasTrace ? import_utils.randomId : void 0,
81
86
  ELYSIA_TRACE: hasTrace ? import_trace.ELYSIA_TRACE : void 0,
@@ -4,7 +4,11 @@ import { createHoc, createOnRequestHandler, isAsync } from "../../compose.mjs";
4
4
  import { randomId, ELYSIA_REQUEST_ID, redirect, isNotEmpty } from "../../utils.mjs";
5
5
  import { status } from "../../error.mjs";
6
6
  import { ELYSIA_TRACE } from "../../trace.mjs";
7
- const allocateIf = (value, condition) => condition ? value : "", createContext = (app, route, inference, isInline = !1) => {
7
+ const allocateIf = (value, condition) => condition ? value : "", preserveEncodedSlashes = (params) => {
8
+ for (const key of Object.keys(params))
9
+ params[key].includes("/") && (params[key] = params[key].replaceAll("/", "%2F"));
10
+ return params;
11
+ }, createContext = (app, route, inference, isInline = !1) => {
8
12
  let fnLiteral = "";
9
13
  const defaultHeaders = app.setHeaders, hasTrace = !!app.event.trace?.length;
10
14
  hasTrace && (fnLiteral += `const id=randomId()
@@ -16,8 +20,8 @@ const allocateIf = (value, condition) => condition ? value : "", createContext =
16
20
  needsQuery && (fnLiteral += getQi);
17
21
  const getPath = inference.path ? isDynamic ? "get path(){" + (needsQuery ? "" : getQi) + `if(qi===-1)return u.substring(s)
18
22
  return u.substring(s,qi)
19
- },` : `path:'${route.path}',` : "";
20
- fnLiteral += allocateIf("const c=", !isInline) + "{request,store," + allocateIf("qi,", needsQuery) + allocateIf("params:request.params,", isDynamic) + getPath + allocateIf(
23
+ },` : "path:route," : "";
24
+ fnLiteral += allocateIf("const c=", !isInline) + "{request,store," + allocateIf("qi,", needsQuery) + allocateIf("params:preserveEncodedSlashes(request.params),", isDynamic) + getPath + allocateIf(
21
25
  "url:request.url,",
22
26
  hasTrace || inference.url || needsQuery
23
27
  ) + "redirect,status,set:{headers:" + (isNotEmpty(defaultHeaders) ? "Object.assign({},app.setHeaders)" : "Object.create(null)") + ",status:200}", inference.server && (fnLiteral += ",get server(){return app.getServer()}"), hasTrace && (fnLiteral += ",[ELYSIA_REQUEST_ID]:id");
@@ -42,7 +46,7 @@ return u.substring(s,qi)
42
46
  },
43
47
  inference
44
48
  );
45
- let fnLiteral = "const handler=data.handler,app=data.app,store=data.store,decorator=data.decorator,redirect=data.redirect,route=data.route,mapEarlyResponse=data.mapEarlyResponse," + allocateIf("randomId=data.randomId,", hasTrace) + allocateIf("ELYSIA_REQUEST_ID=data.ELYSIA_REQUEST_ID,", hasTrace) + allocateIf("ELYSIA_TRACE=data.ELYSIA_TRACE,", hasTrace) + allocateIf("trace=data.trace,", hasTrace) + allocateIf("hoc=data.hoc,", hasHoc) + `status=data.status
49
+ let fnLiteral = "const handler=data.handler,app=data.app,store=data.store,decorator=data.decorator,preserveEncodedSlashes=data.preserveEncodedSlashes,redirect=data.redirect,route=data.route,mapEarlyResponse=data.mapEarlyResponse," + allocateIf("randomId=data.randomId,", hasTrace) + allocateIf("ELYSIA_REQUEST_ID=data.ELYSIA_REQUEST_ID,", hasTrace) + allocateIf("ELYSIA_TRACE=data.ELYSIA_TRACE,", hasTrace) + allocateIf("trace=data.trace,", hasTrace) + allocateIf("hoc=data.hoc,", hasHoc) + `status=data.status
46
50
  `;
47
51
  app.event.request?.length && (fnLiteral += `const onRequest=app.event.request.map(x=>x.fn)
48
52
  `), fnLiteral += `${app.event.request?.find(isAsync) ? "async" : ""} function map(request){`;
@@ -61,6 +65,7 @@ return u.substring(s,qi)
61
65
  hoc: app.extender.higherOrderFunctions.map((x) => x.fn),
62
66
  store: app.store,
63
67
  decorator: app.decorator,
68
+ preserveEncodedSlashes,
64
69
  route: route.path,
65
70
  randomId: hasTrace ? randomId : void 0,
66
71
  ELYSIA_TRACE: hasTrace ? ELYSIA_TRACE : void 0,
@@ -19,8 +19,24 @@ __export(handler_native_exports, {
19
19
  });
20
20
  module.exports = __toCommonJS(handler_native_exports);
21
21
  var import_index = require('./index.js'), import_handler = require('./handler.js');
22
- const createNativeStaticHandler = (handle, hooks, set) => {
23
- if (typeof handle == "function" || handle instanceof Blob) return;
22
+ const isEmptyPipelineHook = (hooks) => {
23
+ for (const key in hooks) {
24
+ switch (key) {
25
+ case "detail":
26
+ case "tags":
27
+ case "start":
28
+ case "stop":
29
+ case "error":
30
+ case "afterResponse":
31
+ continue;
32
+ }
33
+ const value = hooks[key];
34
+ if (value !== void 0 && value !== !1 && (!Array.isArray(value) || value.length))
35
+ return !1;
36
+ }
37
+ return !0;
38
+ }, createNativeStaticHandler = (handle, hooks, set) => {
39
+ if (typeof handle == "function" || handle instanceof Blob || !isEmptyPipelineHook(hooks)) return;
24
40
  if ((0, import_index.isHTMLBundle)(handle)) return () => handle;
25
41
  const response = (0, import_handler.mapResponse)(
26
42
  handle instanceof Response ? handle.clone() : handle instanceof Promise ? handle.then(
@@ -30,11 +46,10 @@ const createNativeStaticHandler = (handle, hooks, set) => {
30
46
  headers: {}
31
47
  }
32
48
  );
33
- if (!hooks.parse?.length && !hooks.transform?.length && !hooks.beforeHandle?.length && !hooks.afterHandle?.length)
34
- return response instanceof Promise ? response.then((response2) => {
35
- if (response2)
36
- return response2.clone();
37
- }) : () => response.clone();
49
+ return response instanceof Promise ? response.then((response2) => {
50
+ if (response2)
51
+ return response2.clone();
52
+ }) : () => response.clone();
38
53
  };
39
54
  // Annotate the CommonJS export names for ESM import in node:
40
55
  0 && (module.exports = {
@@ -1,7 +1,23 @@
1
1
  import { isHTMLBundle } from "./index.mjs";
2
2
  import { mapResponse } from "./handler.mjs";
3
- const createNativeStaticHandler = (handle, hooks, set) => {
4
- if (typeof handle == "function" || handle instanceof Blob) return;
3
+ const isEmptyPipelineHook = (hooks) => {
4
+ for (const key in hooks) {
5
+ switch (key) {
6
+ case "detail":
7
+ case "tags":
8
+ case "start":
9
+ case "stop":
10
+ case "error":
11
+ case "afterResponse":
12
+ continue;
13
+ }
14
+ const value = hooks[key];
15
+ if (value !== void 0 && value !== !1 && (!Array.isArray(value) || value.length))
16
+ return !1;
17
+ }
18
+ return !0;
19
+ }, createNativeStaticHandler = (handle, hooks, set) => {
20
+ if (typeof handle == "function" || handle instanceof Blob || !isEmptyPipelineHook(hooks)) return;
5
21
  if (isHTMLBundle(handle)) return () => handle;
6
22
  const response = mapResponse(
7
23
  handle instanceof Response ? handle.clone() : handle instanceof Promise ? handle.then(
@@ -11,11 +27,10 @@ const createNativeStaticHandler = (handle, hooks, set) => {
11
27
  headers: {}
12
28
  }
13
29
  );
14
- if (!hooks.parse?.length && !hooks.transform?.length && !hooks.beforeHandle?.length && !hooks.afterHandle?.length)
15
- return response instanceof Promise ? response.then((response2) => {
16
- if (response2)
17
- return response2.clone();
18
- }) : () => response.clone();
30
+ return response instanceof Promise ? response.then((response2) => {
31
+ if (response2)
32
+ return response2.clone();
33
+ }) : () => response.clone();
19
34
  };
20
35
  export {
21
36
  createNativeStaticHandler
@@ -123,6 +123,9 @@ for(const [k,v] of c.request.headers.entries())c.headers[k]=v
123
123
  }
124
124
  const createStaticRoute = (iterator, { withAsync = !1 } = {}) => {
125
125
  const staticRoutes = {}, ops = [];
126
+ if (app.event.request?.length || app.event.trace?.length || // @ts-expect-error private
127
+ app.extender.higherOrderFunctions.length)
128
+ return withAsync ? Promise.resolve(staticRoutes) : staticRoutes;
126
129
  for (let [path, route] of Object.entries(iterator))
127
130
  if (path = encodeURI(path), import_utils2.supportPerMethodInlineHandler) {
128
131
  if (!route) continue;
@@ -235,7 +238,10 @@ for(const [k,v] of c.request.headers.entries())c.headers[k]=v
235
238
  // @ts-expect-error private property
236
239
  models: app.definitions.type,
237
240
  normalize: app.config.normalize
238
- }), validateMessage = messageValidator ? messageValidator.provider === "standard" ? (data) => messageValidator.schema["~standard"].validate(data).issues : (data) => messageValidator.Check(data) === !1 : void 0, responseValidator = (0, import_schema.getSchemaValidator)(response, {
241
+ }), validateMessage = messageValidator ? messageValidator.provider === "standard" ? (data) => messageValidator.schema["~standard"].validate(data) : (data) => ({
242
+ issues: messageValidator.Check(data) === !1 || void 0,
243
+ value: data
244
+ }) : void 0, responseValidator = (0, import_schema.getSchemaValidator)(response, {
239
245
  // @ts-expect-error private property
240
246
  modules: app.definitions.typebox,
241
247
  // @ts-expect-error private property
@@ -310,19 +316,22 @@ for(const [k,v] of c.request.headers.entries())c.headers[k]=v
310
316
  }
311
317
  },
312
318
  message: async (ws, _message) => {
313
- const message = await parseMessage(ws, _message);
314
- if (validateMessage && validateMessage(message)) {
315
- const validationError = new import_error.ValidationError(
316
- "message",
317
- messageValidator,
318
- message
319
- );
320
- return hasCustomErrorHandlers ? handleErrors(ws, validationError) : void ws.send(
321
- validationError.message
322
- );
323
- }
324
319
  try {
325
- await handleResponse(
320
+ let message = await parseMessage(ws, _message);
321
+ const validation = validateMessage ? await validateMessage(message) : void 0;
322
+ if (validation?.issues) {
323
+ const validationError = new import_error.ValidationError(
324
+ "message",
325
+ messageValidator,
326
+ message,
327
+ !1,
328
+ messageValidator?.provider === "standard" ? validation.issues : void 0
329
+ );
330
+ return hasCustomErrorHandlers ? handleErrors(ws, validationError) : void ws.send(
331
+ validationError.message
332
+ );
333
+ }
334
+ validation && (message = validation.value), await handleResponse(
326
335
  ws,
327
336
  options.message?.(
328
337
  new import_ws.ElysiaWS(
@@ -126,6 +126,9 @@ for(const [k,v] of c.request.headers.entries())c.headers[k]=v
126
126
  }
127
127
  const createStaticRoute = (iterator, { withAsync = !1 } = {}) => {
128
128
  const staticRoutes = {}, ops = [];
129
+ if (app.event.request?.length || app.event.trace?.length || // @ts-expect-error private
130
+ app.extender.higherOrderFunctions.length)
131
+ return withAsync ? Promise.resolve(staticRoutes) : staticRoutes;
129
132
  for (let [path, route] of Object.entries(iterator))
130
133
  if (path = encodeURI(path), supportPerMethodInlineHandler) {
131
134
  if (!route) continue;
@@ -238,7 +241,10 @@ for(const [k,v] of c.request.headers.entries())c.headers[k]=v
238
241
  // @ts-expect-error private property
239
242
  models: app.definitions.type,
240
243
  normalize: app.config.normalize
241
- }), validateMessage = messageValidator ? messageValidator.provider === "standard" ? (data) => messageValidator.schema["~standard"].validate(data).issues : (data) => messageValidator.Check(data) === !1 : void 0, responseValidator = getSchemaValidator(response, {
244
+ }), validateMessage = messageValidator ? messageValidator.provider === "standard" ? (data) => messageValidator.schema["~standard"].validate(data) : (data) => ({
245
+ issues: messageValidator.Check(data) === !1 || void 0,
246
+ value: data
247
+ }) : void 0, responseValidator = getSchemaValidator(response, {
242
248
  // @ts-expect-error private property
243
249
  modules: app.definitions.typebox,
244
250
  // @ts-expect-error private property
@@ -313,19 +319,22 @@ for(const [k,v] of c.request.headers.entries())c.headers[k]=v
313
319
  }
314
320
  },
315
321
  message: async (ws, _message) => {
316
- const message = await parseMessage(ws, _message);
317
- if (validateMessage && validateMessage(message)) {
318
- const validationError = new ValidationError(
319
- "message",
320
- messageValidator,
321
- message
322
- );
323
- return hasCustomErrorHandlers ? handleErrors(ws, validationError) : void ws.send(
324
- validationError.message
325
- );
326
- }
327
322
  try {
328
- await handleResponse(
323
+ let message = await parseMessage(ws, _message);
324
+ const validation = validateMessage ? await validateMessage(message) : void 0;
325
+ if (validation?.issues) {
326
+ const validationError = new ValidationError(
327
+ "message",
328
+ messageValidator,
329
+ message,
330
+ !1,
331
+ messageValidator?.provider === "standard" ? validation.issues : void 0
332
+ );
333
+ return hasCustomErrorHandlers ? handleErrors(ws, validationError) : void ws.send(
334
+ validationError.message
335
+ );
336
+ }
337
+ validation && (message = validation.value), await handleResponse(
329
338
  ws,
330
339
  options.message?.(
331
340
  new ElysiaWS(
@@ -285,25 +285,70 @@ const createResponseHandler = (handler) => {
285
285
  };
286
286
  };
287
287
  async function tee(source, branches = 2) {
288
- const buffer = [];
289
- let done = !1, waiting = [];
290
- (async () => {
291
- for await (const value of source)
292
- buffer.push(value), waiting.forEach((w) => w.resolve()), waiting = [];
293
- done = !0, waiting.forEach((w) => w.resolve());
294
- })();
295
- async function* makeIterator() {
296
- let i = 0;
297
- for (; ; )
298
- if (i < buffer.length)
299
- yield buffer[i++];
300
- else {
301
- if (done)
302
- return;
303
- await new Promise((resolve) => waiting.push({ resolve }));
288
+ const state = Array.from({ length: branches }, () => ({
289
+ active: !0,
290
+ consumed: !0
291
+ }));
292
+ if (!state.length) return [];
293
+ const iterator = source[Symbol.asyncIterator]?.() ?? source[Symbol.iterator]();
294
+ let active = state.length, current, hasValue = !1, remaining = 0, done = !1, failed = !1, sourceError, pulling, waiting = [];
295
+ const wake = () => {
296
+ const pending = waiting;
297
+ waiting = [];
298
+ for (const resolve of pending) resolve();
299
+ }, wait = () => new Promise((resolve) => waiting.push(resolve)), pull = () => {
300
+ pulling ??= Promise.resolve().then(() => iterator.next()).then((result) => {
301
+ if (result.done || !active || done) {
302
+ done = !0;
303
+ return;
304
304
  }
305
- }
306
- return Array.from({ length: branches }, makeIterator);
305
+ current = result.value, hasValue = !0, remaining = active;
306
+ for (const branch of state)
307
+ branch.active && (branch.consumed = !1);
308
+ }).catch((error) => {
309
+ !active || done || (failed = done = !0, sourceError = error);
310
+ }).finally(() => {
311
+ pulling = void 0, wake();
312
+ });
313
+ }, stop = () => {
314
+ if (!done) {
315
+ done = !0, wake();
316
+ try {
317
+ Promise.resolve(iterator.return?.()).catch(() => {
318
+ });
319
+ } catch {
320
+ }
321
+ }
322
+ }, close = (index) => {
323
+ const branch = state[index];
324
+ return branch.active ? (branch.active = !1, active--, hasValue && !branch.consumed && --remaining === 0 && (hasValue = !1, wake()), !0) : !1;
325
+ }, next = async (index) => {
326
+ const branch = state[index];
327
+ for (; branch.active; ) {
328
+ if (failed)
329
+ throw close(index), sourceError;
330
+ if (hasValue && !branch.consumed) {
331
+ branch.consumed = !0;
332
+ const result = { done: !1, value: current };
333
+ return --remaining === 0 && (hasValue = !1, wake()), result;
334
+ }
335
+ if (done) {
336
+ close(index);
337
+ break;
338
+ }
339
+ hasValue || pull(), await wait();
340
+ }
341
+ return { done: !0, value: void 0 };
342
+ };
343
+ return state.map((_, index) => ({
344
+ next: () => next(index),
345
+ async return(finalValue) {
346
+ return close(index) && (index === 0 || !active) && stop(), { done: !0, value: finalValue };
347
+ },
348
+ [Symbol.asyncIterator]() {
349
+ return this;
350
+ }
351
+ }));
307
352
  }
308
353
  // Annotate the CommonJS export names for ESM import in node:
309
354
  0 && (module.exports = {
@@ -258,25 +258,70 @@ const createResponseHandler = (handler) => {
258
258
  };
259
259
  };
260
260
  async function tee(source, branches = 2) {
261
- const buffer = [];
262
- let done = !1, waiting = [];
263
- (async () => {
264
- for await (const value of source)
265
- buffer.push(value), waiting.forEach((w) => w.resolve()), waiting = [];
266
- done = !0, waiting.forEach((w) => w.resolve());
267
- })();
268
- async function* makeIterator() {
269
- let i = 0;
270
- for (; ; )
271
- if (i < buffer.length)
272
- yield buffer[i++];
273
- else {
274
- if (done)
275
- return;
276
- await new Promise((resolve) => waiting.push({ resolve }));
261
+ const state = Array.from({ length: branches }, () => ({
262
+ active: !0,
263
+ consumed: !0
264
+ }));
265
+ if (!state.length) return [];
266
+ const iterator = source[Symbol.asyncIterator]?.() ?? source[Symbol.iterator]();
267
+ let active = state.length, current, hasValue = !1, remaining = 0, done = !1, failed = !1, sourceError, pulling, waiting = [];
268
+ const wake = () => {
269
+ const pending = waiting;
270
+ waiting = [];
271
+ for (const resolve of pending) resolve();
272
+ }, wait = () => new Promise((resolve) => waiting.push(resolve)), pull = () => {
273
+ pulling ??= Promise.resolve().then(() => iterator.next()).then((result) => {
274
+ if (result.done || !active || done) {
275
+ done = !0;
276
+ return;
277
277
  }
278
- }
279
- return Array.from({ length: branches }, makeIterator);
278
+ current = result.value, hasValue = !0, remaining = active;
279
+ for (const branch of state)
280
+ branch.active && (branch.consumed = !1);
281
+ }).catch((error) => {
282
+ !active || done || (failed = done = !0, sourceError = error);
283
+ }).finally(() => {
284
+ pulling = void 0, wake();
285
+ });
286
+ }, stop = () => {
287
+ if (!done) {
288
+ done = !0, wake();
289
+ try {
290
+ Promise.resolve(iterator.return?.()).catch(() => {
291
+ });
292
+ } catch {
293
+ }
294
+ }
295
+ }, close = (index) => {
296
+ const branch = state[index];
297
+ return branch.active ? (branch.active = !1, active--, hasValue && !branch.consumed && --remaining === 0 && (hasValue = !1, wake()), !0) : !1;
298
+ }, next = async (index) => {
299
+ const branch = state[index];
300
+ for (; branch.active; ) {
301
+ if (failed)
302
+ throw close(index), sourceError;
303
+ if (hasValue && !branch.consumed) {
304
+ branch.consumed = !0;
305
+ const result = { done: !1, value: current };
306
+ return --remaining === 0 && (hasValue = !1, wake()), result;
307
+ }
308
+ if (done) {
309
+ close(index);
310
+ break;
311
+ }
312
+ hasValue || pull(), await wait();
313
+ }
314
+ return { done: !0, value: void 0 };
315
+ };
316
+ return state.map((_, index) => ({
317
+ next: () => next(index),
318
+ async return(finalValue) {
319
+ return close(index) && (index === 0 || !active) && stop(), { done: !0, value: finalValue };
320
+ },
321
+ [Symbol.asyncIterator]() {
322
+ return this;
323
+ }
324
+ }));
280
325
  }
281
326
  export {
282
327
  createResponseHandler,
@@ -54,62 +54,10 @@ for(const [k,v] of c.request.headers.entries())c.headers[k]=v
54
54
  `;
55
55
  },
56
56
  formData(isOptional) {
57
- let fnLiteral = `
58
- c.body={}
59
- `;
57
+ let fnLiteral = "";
60
58
  return isOptional ? fnLiteral += "let form;try{form=await c.request.formData()}catch{}" : fnLiteral += `const form=await c.request.formData()
61
- `, fnLiteral + `const dangerousKeys=new Set(['__proto__','constructor','prototype'])
62
- const isDangerousKey=(k)=>{if(dangerousKeys.has(k))return true;const m=k.match(/^(.+)\\[(\\d+)\\]$/);return m?dangerousKeys.has(m[1]):false}
63
- const parseArrayKey=(k)=>{const m=k.match(/^(.+)\\[(\\d+)\\]$/);return m?{name:m[1],index:parseInt(m[2],10)}:null}
64
- const grouped=new Map()
65
- form.forEach((v,k)=>{const l=grouped.get(k);if(l)l.push(v);else grouped.set(k,[v])})
66
- for(const [key,value] of grouped){if(c.body[key])continue
67
- let finalValue
68
- if(value.length===1){
69
- const sv=value[0]
70
- if(typeof sv==='string'&&(sv.charCodeAt(0)===123||sv.charCodeAt(0)===91)){
71
- try{
72
- const p=JSON.parse(sv)
73
- if(p&&typeof p==='object')finalValue=p
74
- }catch{}
75
- }
76
- if(finalValue===undefined)finalValue=sv
77
- }else finalValue=value
78
- if(Array.isArray(finalValue)){
79
- const stringValue=finalValue.find((entry)=>typeof entry==='string')
80
- const files=typeof File==='undefined'?[]:finalValue.filter((entry)=>entry instanceof File)
81
- if(stringValue&&files.length&&stringValue.charCodeAt(0)===123){
82
- try{
83
- const parsed=JSON.parse(stringValue)
84
- if(parsed&&typeof parsed==='object'&&!Array.isArray(parsed)){
85
- if(!('file' in parsed)&&files.length===1)parsed.file=files[0]
86
- else if(!('files' in parsed)&&files.length>1)parsed.files=files
87
- finalValue=parsed
88
- }
89
- }catch{}
90
- }
91
- }
92
- if(key.includes('.')||key.includes('[')){const keys=key.split('.')
93
- const lastKey=keys.pop()
94
- if(isDangerousKey(lastKey)||keys.some(isDangerousKey))continue
95
- let current=c.body
96
- for(const k of keys){const arrayInfo=parseArrayKey(k)
97
- if(arrayInfo){if(!Array.isArray(current[arrayInfo.name]))current[arrayInfo.name]=[]
98
- const existing=current[arrayInfo.name][arrayInfo.index]
99
- const isFile=typeof File!=='undefined'&&existing instanceof File
100
- if(!existing||typeof existing!=='object'||Array.isArray(existing)||isFile){
101
- let parsed
102
- if(typeof existing==='string'&&existing.charCodeAt(0)===123){
103
- try{parsed=JSON.parse(existing)
104
- if(!parsed||typeof parsed!=='object'||Array.isArray(parsed))parsed=undefined}catch{}
105
- }
106
- current[arrayInfo.name][arrayInfo.index]=parsed||{}
107
- }
108
- current=current[arrayInfo.name][arrayInfo.index]}else{if(!current[k]||typeof current[k]!=='object')current[k]={}
109
- current=current[k]}}
110
- const arrayInfo=parseArrayKey(lastKey)
111
- if(arrayInfo){if(!Array.isArray(current[arrayInfo.name]))current[arrayInfo.name]=[]
112
- current[arrayInfo.name][arrayInfo.index]=finalValue}else{current[lastKey]=finalValue}}else c.body[key]=finalValue}`;
59
+ `, fnLiteral + `c.body=parseFormData(form)
60
+ `;
113
61
  }
114
62
  }
115
63
  },
@@ -39,62 +39,10 @@ for(const [k,v] of c.request.headers.entries())c.headers[k]=v
39
39
  `;
40
40
  },
41
41
  formData(isOptional) {
42
- let fnLiteral = `
43
- c.body={}
44
- `;
42
+ let fnLiteral = "";
45
43
  return isOptional ? fnLiteral += "let form;try{form=await c.request.formData()}catch{}" : fnLiteral += `const form=await c.request.formData()
46
- `, fnLiteral + `const dangerousKeys=new Set(['__proto__','constructor','prototype'])
47
- const isDangerousKey=(k)=>{if(dangerousKeys.has(k))return true;const m=k.match(/^(.+)\\[(\\d+)\\]$/);return m?dangerousKeys.has(m[1]):false}
48
- const parseArrayKey=(k)=>{const m=k.match(/^(.+)\\[(\\d+)\\]$/);return m?{name:m[1],index:parseInt(m[2],10)}:null}
49
- const grouped=new Map()
50
- form.forEach((v,k)=>{const l=grouped.get(k);if(l)l.push(v);else grouped.set(k,[v])})
51
- for(const [key,value] of grouped){if(c.body[key])continue
52
- let finalValue
53
- if(value.length===1){
54
- const sv=value[0]
55
- if(typeof sv==='string'&&(sv.charCodeAt(0)===123||sv.charCodeAt(0)===91)){
56
- try{
57
- const p=JSON.parse(sv)
58
- if(p&&typeof p==='object')finalValue=p
59
- }catch{}
60
- }
61
- if(finalValue===undefined)finalValue=sv
62
- }else finalValue=value
63
- if(Array.isArray(finalValue)){
64
- const stringValue=finalValue.find((entry)=>typeof entry==='string')
65
- const files=typeof File==='undefined'?[]:finalValue.filter((entry)=>entry instanceof File)
66
- if(stringValue&&files.length&&stringValue.charCodeAt(0)===123){
67
- try{
68
- const parsed=JSON.parse(stringValue)
69
- if(parsed&&typeof parsed==='object'&&!Array.isArray(parsed)){
70
- if(!('file' in parsed)&&files.length===1)parsed.file=files[0]
71
- else if(!('files' in parsed)&&files.length>1)parsed.files=files
72
- finalValue=parsed
73
- }
74
- }catch{}
75
- }
76
- }
77
- if(key.includes('.')||key.includes('[')){const keys=key.split('.')
78
- const lastKey=keys.pop()
79
- if(isDangerousKey(lastKey)||keys.some(isDangerousKey))continue
80
- let current=c.body
81
- for(const k of keys){const arrayInfo=parseArrayKey(k)
82
- if(arrayInfo){if(!Array.isArray(current[arrayInfo.name]))current[arrayInfo.name]=[]
83
- const existing=current[arrayInfo.name][arrayInfo.index]
84
- const isFile=typeof File!=='undefined'&&existing instanceof File
85
- if(!existing||typeof existing!=='object'||Array.isArray(existing)||isFile){
86
- let parsed
87
- if(typeof existing==='string'&&existing.charCodeAt(0)===123){
88
- try{parsed=JSON.parse(existing)
89
- if(!parsed||typeof parsed!=='object'||Array.isArray(parsed))parsed=undefined}catch{}
90
- }
91
- current[arrayInfo.name][arrayInfo.index]=parsed||{}
92
- }
93
- current=current[arrayInfo.name][arrayInfo.index]}else{if(!current[k]||typeof current[k]!=='object')current[k]={}
94
- current=current[k]}}
95
- const arrayInfo=parseArrayKey(lastKey)
96
- if(arrayInfo){if(!Array.isArray(current[arrayInfo.name]))current[arrayInfo.name]=[]
97
- current[arrayInfo.name][arrayInfo.index]=finalValue}else{current[lastKey]=finalValue}}else c.body[key]=finalValue}`;
44
+ `, fnLiteral + `c.body=parseFormData(form)
45
+ `;
98
46
  }
99
47
  }
100
48
  },