elysia 2.0.0-exp.46 → 2.0.0-exp.49

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.
Files changed (118) hide show
  1. package/dist/adapter/bun/index.js +16 -5
  2. package/dist/adapter/bun/index.mjs +17 -6
  3. package/dist/adapter/index.d.ts +1 -1
  4. package/dist/adapter/index.js +1 -3
  5. package/dist/adapter/index.mjs +1 -3
  6. package/dist/adapter/response-ownership.d.ts +5 -0
  7. package/dist/adapter/response-ownership.js +13 -0
  8. package/dist/adapter/response-ownership.mjs +10 -0
  9. package/dist/adapter/utils.d.ts +4 -4
  10. package/dist/adapter/utils.js +57 -17
  11. package/dist/adapter/utils.mjs +58 -18
  12. package/dist/adapter/web-standard/handler.d.ts +3 -3
  13. package/dist/adapter/web-standard/handler.js +39 -43
  14. package/dist/adapter/web-standard/handler.mjs +39 -43
  15. package/dist/base.d.ts +27 -2
  16. package/dist/base.js +191 -59
  17. package/dist/base.mjs +191 -59
  18. package/dist/compile/analysis-cache.d.ts +6 -0
  19. package/dist/compile/analysis-cache.js +14 -0
  20. package/dist/compile/analysis-cache.mjs +13 -0
  21. package/dist/compile/aot-capture.js +15 -1
  22. package/dist/compile/aot-capture.mjs +15 -1
  23. package/dist/compile/aot.d.ts +5 -0
  24. package/dist/compile/aot.js +8 -0
  25. package/dist/compile/aot.mjs +8 -0
  26. package/dist/compile/handler/descriptor.d.ts +3 -3
  27. package/dist/compile/handler/descriptor.js +4 -3
  28. package/dist/compile/handler/descriptor.mjs +5 -4
  29. package/dist/compile/handler/index.d.ts +6 -3
  30. package/dist/compile/handler/index.js +11 -43
  31. package/dist/compile/handler/index.mjs +11 -43
  32. package/dist/compile/handler/jit.d.ts +1 -1
  33. package/dist/compile/handler/jit.js +26 -18
  34. package/dist/compile/handler/jit.mjs +27 -19
  35. package/dist/compile/handler/params.js +1 -0
  36. package/dist/compile/handler/params.mjs +2 -1
  37. package/dist/compile/handler/reconstruct.d.ts +2 -2
  38. package/dist/compile/handler/reconstruct.js +12 -3
  39. package/dist/compile/handler/reconstruct.mjs +13 -4
  40. package/dist/compile/handler/utils.js +1 -1
  41. package/dist/compile/handler/utils.mjs +1 -1
  42. package/dist/compile/jit-probe.d.ts +0 -2
  43. package/dist/compile/jit-probe.js +1 -3
  44. package/dist/compile/jit-probe.mjs +1 -3
  45. package/dist/compile/lexer.d.ts +1 -1
  46. package/dist/cookie/crypto.d.ts +4 -1
  47. package/dist/cookie/crypto.js +26 -4
  48. package/dist/cookie/crypto.mjs +24 -5
  49. package/dist/cookie/utils.d.ts +10 -3
  50. package/dist/cookie/utils.js +30 -5
  51. package/dist/cookie/utils.mjs +30 -6
  52. package/dist/generation.d.ts +11 -1
  53. package/dist/generation.js +17 -1
  54. package/dist/generation.mjs +13 -1
  55. package/dist/handler/fetch.js +13 -11
  56. package/dist/handler/fetch.mjs +13 -11
  57. package/dist/index.d.ts +5 -4
  58. package/dist/index.js +3 -0
  59. package/dist/index.mjs +3 -2
  60. package/dist/package.js +1 -1
  61. package/dist/package.mjs +1 -1
  62. package/dist/plugin/aot/core.d.ts +23 -4
  63. package/dist/plugin/aot/core.js +22 -26
  64. package/dist/plugin/aot/core.mjs +21 -25
  65. package/dist/plugin/aot/rspack.js +1 -2
  66. package/dist/plugin/aot/rspack.mjs +2 -3
  67. package/dist/plugin/aot/source.d.ts +3 -33
  68. package/dist/plugin/aot/source.js +29 -31
  69. package/dist/plugin/aot/source.mjs +30 -31
  70. package/dist/plugin/aot/treeshake.d.ts +2 -8
  71. package/dist/plugin/aot/treeshake.js +3 -3
  72. package/dist/plugin/aot/treeshake.mjs +3 -3
  73. package/dist/plugin/trace.d.ts +10 -0
  74. package/dist/plugin/trace.js +39 -0
  75. package/dist/plugin/trace.mjs +36 -0
  76. package/dist/plugin/websocket.d.ts +6 -0
  77. package/dist/plugin/websocket.js +61 -0
  78. package/dist/plugin/websocket.mjs +60 -0
  79. package/dist/route-table.d.ts +1 -2
  80. package/dist/route-table.js +2 -6
  81. package/dist/route-table.mjs +2 -6
  82. package/dist/sucrose.d.ts +1 -7
  83. package/dist/sucrose.js +444 -231
  84. package/dist/sucrose.mjs +445 -229
  85. package/dist/trace.d.ts +6 -1
  86. package/dist/type/bridge-live.d.ts +4 -3
  87. package/dist/type/bridge-live.js +2 -0
  88. package/dist/type/bridge-live.mjs +2 -1
  89. package/dist/type/bridge.d.ts +4 -3
  90. package/dist/type/bridge.js +2 -0
  91. package/dist/type/bridge.mjs +2 -1
  92. package/dist/type/constants.d.ts +1 -1
  93. package/dist/type/exports.d.ts +1 -1
  94. package/dist/type/index.d.ts +1 -1
  95. package/dist/type/validator/index.d.ts +2 -2
  96. package/dist/type/validator/validator-cache.js +7 -3
  97. package/dist/type/validator/validator-cache.mjs +7 -3
  98. package/dist/types.d.ts +1 -31
  99. package/dist/utils.d.ts +12 -1
  100. package/dist/utils.js +20 -0
  101. package/dist/utils.mjs +18 -1
  102. package/dist/validator/index.d.ts +1 -1
  103. package/dist/validator/route.d.ts +1 -1
  104. package/dist/ws/context.d.ts +1 -1
  105. package/dist/ws/route.d.ts +4 -2
  106. package/dist/ws/route.js +37 -3
  107. package/dist/ws/route.mjs +36 -4
  108. package/dist/ws/types.d.ts +46 -2
  109. package/package.json +9 -10
  110. package/dist/compile/plan/emit.d.ts +0 -18
  111. package/dist/compile/plan/emit.js +0 -434
  112. package/dist/compile/plan/emit.mjs +0 -433
  113. package/dist/compile/plan/plan.d.ts +0 -53
  114. package/dist/compile/plan/plan.js +0 -96
  115. package/dist/compile/plan/plan.mjs +0 -94
  116. package/dist/experimental/resume.d.ts +0 -11
  117. package/dist/experimental/resume.js +0 -12
  118. package/dist/experimental/resume.mjs +0 -11
@@ -6,7 +6,6 @@ const require_adapter_web_standard_index = require('../web-standard/index.js');
6
6
  const require_generation = require('../../generation.js');
7
7
  const require_compile_handler_index = require('../../compile/handler/index.js');
8
8
  const require_route_table = require('../../route-table.js');
9
- const require_ws_route = require('../../ws/route.js');
10
9
 
11
10
  //#region src/adapter/bun/index.ts
12
11
  const nativeStaticMethods = new Set([
@@ -29,6 +28,16 @@ function collectStaticRoutes(app) {
29
28
  if (!table || !length) return;
30
29
  const methods = table.method;
31
30
  const paths = table.path;
31
+ const handlers = table.handler;
32
+ let hasCandidate = false;
33
+ for (let i = 0; i < length; i++) {
34
+ const h = handlers[i];
35
+ if (typeof h === "function" || h instanceof Error || h instanceof Promise) continue;
36
+ if (!nativeStaticMethods.has(methods[i])) continue;
37
+ hasCandidate = true;
38
+ break;
39
+ }
40
+ if (!hasCandidate) return;
32
41
  const ready = require_utils.nullObject();
33
42
  const strictPath = frozenRoot["~config"]?.strictPath === true;
34
43
  const seen = /* @__PURE__ */ new Map();
@@ -58,6 +67,8 @@ function collectStaticRoutes(app) {
58
67
  const path = paths[i];
59
68
  if (seen.get(method + " " + path) !== i) continue;
60
69
  if (!nativeStaticMethods.has(method)) continue;
70
+ const h = handlers[i];
71
+ if (typeof h === "function" || h instanceof Error || h instanceof Promise) continue;
61
72
  const value = require_compile_handler_index.buildNativeStaticResponse(require_route_table.routeRow(table, i), app);
62
73
  if (!value) continue;
63
74
  add(method, path, value);
@@ -98,11 +109,11 @@ const BunAdapter = require_adapter_index.createAdapter({
98
109
  } catch (error) {
99
110
  console.warn("[Elysia] Native static promotion was skipped:", error);
100
111
  }
101
- const hasWs = app["~hasWS"];
102
112
  let websocket;
103
- if (hasWs) {
104
- const defaultConfig = require_generation.frozenRootOf(app)["~config"]?.websocket;
105
- websocket = defaultConfig ? Object.assign(require_ws_route.buildGlobalWSHandler(), defaultConfig) : require_ws_route.buildGlobalWSHandler();
113
+ if (app["~hasWS"]) {
114
+ const resolved = require_generation.resolvedWsOf(app);
115
+ if (!resolved) throw new Error("[Elysia] internal: WebSocket routes are present but no capability provider was resolved.");
116
+ websocket = resolved.config ? Object.assign(resolved.provider.buildGlobalWSHandler(), resolved.config) : resolved.provider.buildGlobalWSHandler();
106
117
  }
107
118
  return {
108
119
  fetch,
@@ -2,10 +2,9 @@ import { createAdapter } from "../index.mjs";
2
2
  import { isDynamicRegex, needEncodeRegex } from "../../constants.mjs";
3
3
  import { flattenChain, getLoosePath, nullObject } from "../../utils.mjs";
4
4
  import { WebStandardAdapter } from "../web-standard/index.mjs";
5
- import { frozenRootOf } from "../../generation.mjs";
5
+ import { frozenRootOf, resolvedWsOf } from "../../generation.mjs";
6
6
  import { buildNativeStaticResponse } from "../../compile/handler/index.mjs";
7
7
  import { routeRow } from "../../route-table.mjs";
8
- import { buildGlobalWSHandler } from "../../ws/route.mjs";
9
8
 
10
9
  //#region src/adapter/bun/index.ts
11
10
  const nativeStaticMethods = new Set([
@@ -28,6 +27,16 @@ function collectStaticRoutes(app) {
28
27
  if (!table || !length) return;
29
28
  const methods = table.method;
30
29
  const paths = table.path;
30
+ const handlers = table.handler;
31
+ let hasCandidate = false;
32
+ for (let i = 0; i < length; i++) {
33
+ const h = handlers[i];
34
+ if (typeof h === "function" || h instanceof Error || h instanceof Promise) continue;
35
+ if (!nativeStaticMethods.has(methods[i])) continue;
36
+ hasCandidate = true;
37
+ break;
38
+ }
39
+ if (!hasCandidate) return;
31
40
  const ready = nullObject();
32
41
  const strictPath = frozenRoot["~config"]?.strictPath === true;
33
42
  const seen = /* @__PURE__ */ new Map();
@@ -57,6 +66,8 @@ function collectStaticRoutes(app) {
57
66
  const path = paths[i];
58
67
  if (seen.get(method + " " + path) !== i) continue;
59
68
  if (!nativeStaticMethods.has(method)) continue;
69
+ const h = handlers[i];
70
+ if (typeof h === "function" || h instanceof Error || h instanceof Promise) continue;
60
71
  const value = buildNativeStaticResponse(routeRow(table, i), app);
61
72
  if (!value) continue;
62
73
  add(method, path, value);
@@ -97,11 +108,11 @@ const BunAdapter = createAdapter({
97
108
  } catch (error) {
98
109
  console.warn("[Elysia] Native static promotion was skipped:", error);
99
110
  }
100
- const hasWs = app["~hasWS"];
101
111
  let websocket;
102
- if (hasWs) {
103
- const defaultConfig = frozenRootOf(app)["~config"]?.websocket;
104
- websocket = defaultConfig ? Object.assign(buildGlobalWSHandler(), defaultConfig) : buildGlobalWSHandler();
112
+ if (app["~hasWS"]) {
113
+ const resolved = resolvedWsOf(app);
114
+ if (!resolved) throw new Error("[Elysia] internal: WebSocket routes are present but no capability provider was resolved.");
115
+ websocket = resolved.config ? Object.assign(resolved.provider.buildGlobalWSHandler(), resolved.config) : resolved.provider.buildGlobalWSHandler();
105
116
  }
106
117
  return {
107
118
  fetch,
@@ -1,7 +1,7 @@
1
1
  import { ElysiaAdapterOptions } from "./types.js";
2
2
 
3
3
  //#region src/adapter/index.d.ts
4
- declare function createAdapter<const T extends ElysiaAdapterOptions>(adapter: ElysiaAdapterOptions): T;
4
+ declare const createAdapter: (adapter: ElysiaAdapterOptions) => ElysiaAdapterOptions;
5
5
  type ElysiaAdapter = ReturnType<typeof createAdapter>;
6
6
  //#endregion
7
7
  export { ElysiaAdapter, type ElysiaAdapterOptions, createAdapter };
@@ -1,9 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
 
3
3
  //#region src/adapter/index.ts
4
- function createAdapter(adapter) {
5
- return adapter;
6
- }
4
+ const createAdapter = (adapter) => adapter;
7
5
 
8
6
  //#endregion
9
7
  exports.createAdapter = createAdapter;
@@ -1,7 +1,5 @@
1
1
  //#region src/adapter/index.ts
2
- function createAdapter(adapter) {
3
- return adapter;
4
- }
2
+ const createAdapter = (adapter) => adapter;
5
3
 
6
4
  //#endregion
7
5
  export { createAdapter };
@@ -0,0 +1,5 @@
1
+ //#region src/adapter/response-ownership.d.ts
2
+ declare function borrow<const T extends Response>(response: T): T;
3
+ declare const isBorrowedResponse: (response: Response) => boolean;
4
+ //#endregion
5
+ export { borrow, isBorrowedResponse };
@@ -0,0 +1,13 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+
3
+ //#region src/adapter/response-ownership.ts
4
+ const borrowedResponses = /* @__PURE__ */ new WeakSet();
5
+ function borrow(response) {
6
+ borrowedResponses.add(response);
7
+ return response;
8
+ }
9
+ const isBorrowedResponse = (response) => borrowedResponses.has(response);
10
+
11
+ //#endregion
12
+ exports.borrow = borrow;
13
+ exports.isBorrowedResponse = isBorrowedResponse;
@@ -0,0 +1,10 @@
1
+ //#region src/adapter/response-ownership.ts
2
+ const borrowedResponses = /* @__PURE__ */ new WeakSet();
3
+ function borrow(response) {
4
+ borrowedResponses.add(response);
5
+ return response;
6
+ }
7
+ const isBorrowedResponse = (response) => borrowedResponses.has(response);
8
+
9
+ //#endregion
10
+ export { borrow, isBorrowedResponse };
@@ -17,16 +17,16 @@ declare function responseToSetHeaders(response: Response, set?: Context['set']):
17
17
  status: number | "Continue" | "Switching Protocols" | "Processing" | "Early Hints" | "OK" | "Created" | "Accepted" | "Non-Authoritative Information" | "No Content" | "Reset Content" | "Partial Content" | "Multi-Status" | "Already Reported" | "Multiple Choices" | "Moved Permanently" | "Found" | "See Other" | "Not Modified" | "Temporary Redirect" | "Permanent Redirect" | "Bad Request" | "Unauthorized" | "Payment Required" | "Forbidden" | "Not Found" | "Method Not Allowed" | "Not Acceptable" | "Proxy Authentication Required" | "Request Timeout" | "Conflict" | "Gone" | "Length Required" | "Precondition Failed" | "Payload Too Large" | "URI Too Long" | "Unsupported Media Type" | "Range Not Satisfiable" | "Expectation Failed" | "I'm a teapot" | "Enhance Your Calm" | "Misdirected Request" | "Unprocessable Content" | "Locked" | "Failed Dependency" | "Too Early" | "Upgrade Required" | "Precondition Required" | "Too Many Requests" | "Request Header Fields Too Large" | "Unavailable For Legal Reasons" | "Internal Server Error" | "Not Implemented" | "Bad Gateway" | "Service Unavailable" | "Gateway Timeout" | "HTTP Version Not Supported" | "Variant Also Negotiates" | "Insufficient Storage" | "Loop Detected" | "Not Extended" | "Network Authentication Required";
18
18
  };
19
19
  interface CreateHandlerParameter {
20
- mapResponse(response: unknown, set: Context['set'], request?: Request): Response;
21
- mapCompactResponse(response: unknown, request?: Request): Response;
20
+ mapResponse(response: unknown, set: Context['set'], request?: Request, owned?: boolean): Response;
21
+ mapCompactResponse(response: unknown, request?: Request, owned?: boolean): Response;
22
22
  }
23
23
  declare function createStreamHandler({
24
24
  mapResponse,
25
25
  mapCompactResponse
26
- }: CreateHandlerParameter): (generator: Generator | AsyncGenerator | ReadableStream, set?: Context["set"], request?: Request, skipFormat?: boolean) => Promise<Response>;
26
+ }: CreateHandlerParameter): (generator: Generator | AsyncGenerator | ReadableStream, set?: Context["set"], request?: Request, skipFormat?: boolean, owned?: boolean) => Promise<Response>;
27
27
  declare function streamResponse(response: Response): AsyncGenerator<any, void, any>;
28
28
  declare function handleSet(set: Context['set']): void;
29
- declare function createResponseHandler(handler: CreateHandlerParameter): (response: Response, set?: Context["set"], request?: Request) => any;
29
+ declare function createResponseHandler(handler: CreateHandlerParameter): (response: Response, set?: Context["set"], request?: Request, owned?: boolean) => any;
30
30
  /**
31
31
  * Split async source into `branches` independent iterators
32
32
  *
@@ -4,6 +4,7 @@ const require_constants = require('../constants.js');
4
4
  const require_utils = require('../utils.js');
5
5
  const require_adapter_skip_clone = require('./skip-clone.js');
6
6
  const require_cookie_serialize = require('../cookie/serialize.js');
7
+ const require_adapter_response_ownership = require('./response-ownership.js');
7
8
  const require_adapter_default_headers = require('./default-headers.js');
8
9
 
9
10
  //#region src/adapter/utils.ts
@@ -143,29 +144,49 @@ function enqueueBinaryChunk(controller, chunk) {
143
144
  return false;
144
145
  }
145
146
  function createStreamHandler({ mapResponse, mapCompactResponse }) {
146
- return async (generator, set, request, skipFormat) => {
147
- let init = generator.next?.();
147
+ return async (generator, set, request, skipFormat, owned = false) => {
148
+ if (require_utils.isByteStream(generator)) {
149
+ if (generator.locked) throw new TypeError("Cannot transfer a locked or consumed byte stream");
150
+ if (set) {
151
+ handleSet(set);
152
+ const headers = materializeSetHeaders(set);
153
+ if (headers instanceof Headers) {
154
+ if (!headers.has("content-type")) headers.set("content-type", "application/octet-stream");
155
+ } else if (!headers["content-type"]) headers["content-type"] = "application/octet-stream";
156
+ return new Response(generator, set);
157
+ }
158
+ return new Response(generator, { headers: { "content-type": "application/octet-stream" } });
159
+ }
160
+ const typedSSE = !skipFormat && "sse" in generator && generator.sse === true;
161
+ let init = typedSSE ? void 0 : generator.next?.();
148
162
  if (set) handleSet(set);
149
163
  if (init instanceof Promise) init = await init;
150
164
  if (init?.value instanceof ReadableStream) generator = init.value;
151
165
  else if (init && (typeof init?.done === "undefined" || init?.done)) {
152
- if (set) return mapResponse(init.value, set, request);
153
- return mapCompactResponse(init.value, request);
166
+ if (set) return mapResponse(init.value, set, request, owned);
167
+ return mapCompactResponse(init.value, request, owned);
154
168
  }
155
- const isSSE = !skipFormat && (init?.value?.sse ?? generator?.sse ?? (set?.headers instanceof Headers ? set.headers.get("content-type")?.startsWith("text/event-stream") : set?.headers["content-type"]?.startsWith("text/event-stream")));
169
+ const isSSE = typedSSE || !skipFormat && (init?.value?.sse ?? generator?.sse ?? (set?.headers instanceof Headers ? set.headers.get("content-type")?.startsWith("text/event-stream") : set?.headers["content-type"]?.startsWith("text/event-stream")));
156
170
  const format = isSSE ? sseFormat : identityFormat;
157
171
  const contentType = isSSE ? "text/event-stream" : init?.value && typeof init?.value === "object" ? ArrayBuffer.isView(init.value) ? "application/octet-stream" : "application/json" : "text/plain";
158
172
  if (set) materializeSetHeaders(set);
159
173
  const headers = set?.headers;
160
174
  if (headers instanceof Headers) {
161
- if (!headers.has("transfer-encoding")) headers.set("transfer-encoding", "chunked");
175
+ if (!typedSSE && !headers.has("transfer-encoding")) headers.set("transfer-encoding", "chunked");
162
176
  if (!headers.has("content-type")) headers.set("content-type", contentType);
163
177
  if (!headers.has("cache-control")) headers.set("cache-control", "no-cache");
164
178
  } else if (headers) {
165
- if (!headers["transfer-encoding"]) headers["transfer-encoding"] = "chunked";
179
+ if (!typedSSE && !headers["transfer-encoding"]) headers["transfer-encoding"] = "chunked";
166
180
  if (!headers["content-type"]) headers["content-type"] = contentType;
167
181
  if (!headers["cache-control"]) headers["cache-control"] = "no-cache";
168
- } else set = {
182
+ } else if (typedSSE) set = {
183
+ status: 200,
184
+ headers: {
185
+ "content-type": contentType,
186
+ "cache-control": "no-cache"
187
+ }
188
+ };
189
+ else set = {
169
190
  status: 200,
170
191
  headers: {
171
192
  "content-type": contentType,
@@ -250,7 +271,7 @@ function createStreamHandler({ mapResponse, mapCompactResponse }) {
250
271
  cleanupAbort();
251
272
  safeReturn();
252
273
  }
253
- }), set);
274
+ }, typedSSE ? { highWaterMark: 0 } : void 0), set);
254
275
  };
255
276
  }
256
277
  async function* streamResponse(response) {
@@ -310,7 +331,7 @@ function mergeStatus(responseStatus, setStatus) {
310
331
  if (responseStatus === 200) return setStatus;
311
332
  return responseStatus;
312
333
  }
313
- function cancelPropagatingBody(clonedBody, orphanedBranch) {
334
+ function cancelPropagatingBody(clonedBody, orphanedBranch, preserveOrphan = false) {
314
335
  const reader = clonedBody.getReader();
315
336
  return new ReadableStream({
316
337
  async pull(controller) {
@@ -319,23 +340,42 @@ function cancelPropagatingBody(clonedBody, orphanedBranch) {
319
340
  else controller.enqueue(value);
320
341
  },
321
342
  cancel(reason) {
343
+ if (preserveOrphan) {
344
+ reader.cancel(reason).catch(() => {});
345
+ return;
346
+ }
322
347
  orphanedBranch.cancel(reason);
323
348
  return reader.cancel(reason);
324
349
  }
325
350
  });
326
351
  }
352
+ const warnedStreamBorrows = /* @__PURE__ */ new WeakSet();
327
353
  function createResponseHandler(handler) {
328
354
  const handleStream = createStreamHandler(handler);
329
- return (response, set, request) => {
355
+ return (response, set, request, owned = false) => {
356
+ if (require_adapter_skip_clone.skipClone.has(response) && !response.bodyUsed) {
357
+ require_adapter_skip_clone.skipClone.delete(response);
358
+ owned = true;
359
+ }
360
+ const explicitlyBorrowed = require_adapter_response_ownership.isBorrowedResponse(response);
361
+ const borrowed = !owned || explicitlyBorrowed;
362
+ const mustClone = owned && explicitlyBorrowed;
363
+ if (!borrowed && response.bodyUsed) throw new TypeError("Cannot reuse a consumed Response across requests — a returned Response is request-owned; call borrow(response) to mark it reusable");
330
364
  if (set) {
331
365
  const status = mergeStatus(response.status, set.status);
332
- if ((status === void 0 || status === response.status) && !set.cookie && !require_utils.isNotEmpty(set.headers)) return response;
333
- }
366
+ const statusUnchanged = status === void 0 || status === response.status;
367
+ if (!mustClone && statusUnchanged && !set.cookie && !require_utils.isNotEmpty(set.headers)) return response;
368
+ } else if (!mustClone) return response;
369
+ if (!borrowed && response.body?.locked) throw new TypeError("Cannot patch a Response whose body is locked — release the reader, or call borrow(response) if it must be reused");
334
370
  let body = response.body;
335
- if (require_adapter_skip_clone.skipClone.has(response) && !response.bodyUsed) require_adapter_skip_clone.skipClone.delete(response);
336
- else {
371
+ if (borrowed) {
372
+ const bodyBefore = body;
337
373
  const cloned = response.clone();
338
- body = cloned.body && response.body ? cancelPropagatingBody(cloned.body, response.body) : cloned.body;
374
+ if (explicitlyBorrowed && bodyBefore && response.body !== bodyBefore && !warnedStreamBorrows.has(response)) {
375
+ warnedStreamBorrows.add(response);
376
+ console.warn("[elysia] borrow() was called on a stream-backed Response — its entire body will stay buffered in memory for as long as the borrowed object lives, and every reuse adds a tee layer. Borrow only buffered Responses (string / ArrayBuffer / Blob bodies), or construct a fresh Response per request.");
377
+ }
378
+ body = cloned.body && response.body ? cancelPropagatingBody(cloned.body, response.body, explicitlyBorrowed) : cloned.body;
339
379
  }
340
380
  const newResponse = new Response(body, set ? {
341
381
  headers: mergeHeaders(response.headers, set.headers),
@@ -346,7 +386,7 @@ function createResponseHandler(handler) {
346
386
  status: response.status,
347
387
  statusText: response.statusText
348
388
  });
349
- if (!newResponse.headers.has("content-length") && newResponse.headers.get("transfer-encoding") === "chunked") return handleStream(streamResponse(newResponse), responseToSetHeaders(newResponse, set), request, true);
389
+ if (borrowed && !newResponse.headers.has("content-length") && newResponse.headers.get("transfer-encoding") === "chunked") return handleStream(streamResponse(newResponse), responseToSetHeaders(newResponse, set), request, true);
350
390
  return newResponse;
351
391
  };
352
392
  }
@@ -1,8 +1,9 @@
1
1
  import { hasHeaderShorthand, isBun } from "../universal/constants.mjs";
2
2
  import { StatusMap } from "../constants.mjs";
3
- import { isNotEmpty, nullObject } from "../utils.mjs";
3
+ import { isByteStream, isNotEmpty, nullObject } from "../utils.mjs";
4
4
  import { skipClone } from "./skip-clone.mjs";
5
5
  import { serializeCookie } from "../cookie/serialize.mjs";
6
+ import { isBorrowedResponse } from "./response-ownership.mjs";
6
7
  import { defaultHeaders } from "./default-headers.mjs";
7
8
 
8
9
  //#region src/adapter/utils.ts
@@ -142,29 +143,49 @@ function enqueueBinaryChunk(controller, chunk) {
142
143
  return false;
143
144
  }
144
145
  function createStreamHandler({ mapResponse, mapCompactResponse }) {
145
- return async (generator, set, request, skipFormat) => {
146
- let init = generator.next?.();
146
+ return async (generator, set, request, skipFormat, owned = false) => {
147
+ if (isByteStream(generator)) {
148
+ if (generator.locked) throw new TypeError("Cannot transfer a locked or consumed byte stream");
149
+ if (set) {
150
+ handleSet(set);
151
+ const headers = materializeSetHeaders(set);
152
+ if (headers instanceof Headers) {
153
+ if (!headers.has("content-type")) headers.set("content-type", "application/octet-stream");
154
+ } else if (!headers["content-type"]) headers["content-type"] = "application/octet-stream";
155
+ return new Response(generator, set);
156
+ }
157
+ return new Response(generator, { headers: { "content-type": "application/octet-stream" } });
158
+ }
159
+ const typedSSE = !skipFormat && "sse" in generator && generator.sse === true;
160
+ let init = typedSSE ? void 0 : generator.next?.();
147
161
  if (set) handleSet(set);
148
162
  if (init instanceof Promise) init = await init;
149
163
  if (init?.value instanceof ReadableStream) generator = init.value;
150
164
  else if (init && (typeof init?.done === "undefined" || init?.done)) {
151
- if (set) return mapResponse(init.value, set, request);
152
- return mapCompactResponse(init.value, request);
165
+ if (set) return mapResponse(init.value, set, request, owned);
166
+ return mapCompactResponse(init.value, request, owned);
153
167
  }
154
- const isSSE = !skipFormat && (init?.value?.sse ?? generator?.sse ?? (set?.headers instanceof Headers ? set.headers.get("content-type")?.startsWith("text/event-stream") : set?.headers["content-type"]?.startsWith("text/event-stream")));
168
+ const isSSE = typedSSE || !skipFormat && (init?.value?.sse ?? generator?.sse ?? (set?.headers instanceof Headers ? set.headers.get("content-type")?.startsWith("text/event-stream") : set?.headers["content-type"]?.startsWith("text/event-stream")));
155
169
  const format = isSSE ? sseFormat : identityFormat;
156
170
  const contentType = isSSE ? "text/event-stream" : init?.value && typeof init?.value === "object" ? ArrayBuffer.isView(init.value) ? "application/octet-stream" : "application/json" : "text/plain";
157
171
  if (set) materializeSetHeaders(set);
158
172
  const headers = set?.headers;
159
173
  if (headers instanceof Headers) {
160
- if (!headers.has("transfer-encoding")) headers.set("transfer-encoding", "chunked");
174
+ if (!typedSSE && !headers.has("transfer-encoding")) headers.set("transfer-encoding", "chunked");
161
175
  if (!headers.has("content-type")) headers.set("content-type", contentType);
162
176
  if (!headers.has("cache-control")) headers.set("cache-control", "no-cache");
163
177
  } else if (headers) {
164
- if (!headers["transfer-encoding"]) headers["transfer-encoding"] = "chunked";
178
+ if (!typedSSE && !headers["transfer-encoding"]) headers["transfer-encoding"] = "chunked";
165
179
  if (!headers["content-type"]) headers["content-type"] = contentType;
166
180
  if (!headers["cache-control"]) headers["cache-control"] = "no-cache";
167
- } else set = {
181
+ } else if (typedSSE) set = {
182
+ status: 200,
183
+ headers: {
184
+ "content-type": contentType,
185
+ "cache-control": "no-cache"
186
+ }
187
+ };
188
+ else set = {
168
189
  status: 200,
169
190
  headers: {
170
191
  "content-type": contentType,
@@ -249,7 +270,7 @@ function createStreamHandler({ mapResponse, mapCompactResponse }) {
249
270
  cleanupAbort();
250
271
  safeReturn();
251
272
  }
252
- }), set);
273
+ }, typedSSE ? { highWaterMark: 0 } : void 0), set);
253
274
  };
254
275
  }
255
276
  async function* streamResponse(response) {
@@ -309,7 +330,7 @@ function mergeStatus(responseStatus, setStatus) {
309
330
  if (responseStatus === 200) return setStatus;
310
331
  return responseStatus;
311
332
  }
312
- function cancelPropagatingBody(clonedBody, orphanedBranch) {
333
+ function cancelPropagatingBody(clonedBody, orphanedBranch, preserveOrphan = false) {
313
334
  const reader = clonedBody.getReader();
314
335
  return new ReadableStream({
315
336
  async pull(controller) {
@@ -318,23 +339,42 @@ function cancelPropagatingBody(clonedBody, orphanedBranch) {
318
339
  else controller.enqueue(value);
319
340
  },
320
341
  cancel(reason) {
342
+ if (preserveOrphan) {
343
+ reader.cancel(reason).catch(() => {});
344
+ return;
345
+ }
321
346
  orphanedBranch.cancel(reason);
322
347
  return reader.cancel(reason);
323
348
  }
324
349
  });
325
350
  }
351
+ const warnedStreamBorrows = /* @__PURE__ */ new WeakSet();
326
352
  function createResponseHandler(handler) {
327
353
  const handleStream = createStreamHandler(handler);
328
- return (response, set, request) => {
354
+ return (response, set, request, owned = false) => {
355
+ if (skipClone.has(response) && !response.bodyUsed) {
356
+ skipClone.delete(response);
357
+ owned = true;
358
+ }
359
+ const explicitlyBorrowed = isBorrowedResponse(response);
360
+ const borrowed = !owned || explicitlyBorrowed;
361
+ const mustClone = owned && explicitlyBorrowed;
362
+ if (!borrowed && response.bodyUsed) throw new TypeError("Cannot reuse a consumed Response across requests — a returned Response is request-owned; call borrow(response) to mark it reusable");
329
363
  if (set) {
330
364
  const status = mergeStatus(response.status, set.status);
331
- if ((status === void 0 || status === response.status) && !set.cookie && !isNotEmpty(set.headers)) return response;
332
- }
365
+ const statusUnchanged = status === void 0 || status === response.status;
366
+ if (!mustClone && statusUnchanged && !set.cookie && !isNotEmpty(set.headers)) return response;
367
+ } else if (!mustClone) return response;
368
+ if (!borrowed && response.body?.locked) throw new TypeError("Cannot patch a Response whose body is locked — release the reader, or call borrow(response) if it must be reused");
333
369
  let body = response.body;
334
- if (skipClone.has(response) && !response.bodyUsed) skipClone.delete(response);
335
- else {
370
+ if (borrowed) {
371
+ const bodyBefore = body;
336
372
  const cloned = response.clone();
337
- body = cloned.body && response.body ? cancelPropagatingBody(cloned.body, response.body) : cloned.body;
373
+ if (explicitlyBorrowed && bodyBefore && response.body !== bodyBefore && !warnedStreamBorrows.has(response)) {
374
+ warnedStreamBorrows.add(response);
375
+ console.warn("[elysia] borrow() was called on a stream-backed Response — its entire body will stay buffered in memory for as long as the borrowed object lives, and every reuse adds a tee layer. Borrow only buffered Responses (string / ArrayBuffer / Blob bodies), or construct a fresh Response per request.");
376
+ }
377
+ body = cloned.body && response.body ? cancelPropagatingBody(cloned.body, response.body, explicitlyBorrowed) : cloned.body;
338
378
  }
339
379
  const newResponse = new Response(body, set ? {
340
380
  headers: mergeHeaders(response.headers, set.headers),
@@ -345,7 +385,7 @@ function createResponseHandler(handler) {
345
385
  status: response.status,
346
386
  statusText: response.statusText
347
387
  });
348
- if (!newResponse.headers.has("content-length") && newResponse.headers.get("transfer-encoding") === "chunked") return handleStream(streamResponse(newResponse), responseToSetHeaders(newResponse, set), request, true);
388
+ if (borrowed && !newResponse.headers.has("content-length") && newResponse.headers.get("transfer-encoding") === "chunked") return handleStream(streamResponse(newResponse), responseToSetHeaders(newResponse, set), request, true);
349
389
  return newResponse;
350
390
  };
351
391
  }
@@ -2,10 +2,10 @@ import { MaybePromise } from "../../types.js";
2
2
  import { Context } from "../../context.js";
3
3
 
4
4
  //#region src/adapter/web-standard/handler.d.ts
5
- declare function mapResponse(response: unknown, set: Context['set'], request?: Request): Response;
6
- declare function mapCompactResponse(response: unknown, request?: Request): Response;
5
+ declare function mapResponse(response: unknown, set: Context['set'], request?: Request, owned?: boolean): Response;
6
+ declare function mapCompactResponse(response: unknown, request?: Request, owned?: boolean): Response;
7
7
  declare function errorToResponse(error: Error & {
8
8
  toResponse?(): MaybePromise<Response>;
9
- }, set?: Context['set']): any;
9
+ }, set?: Context['set'], request?: Request, owned?: boolean): any;
10
10
  //#endregion
11
11
  export { errorToResponse, mapCompactResponse, mapResponse };